公司的 App 是 Hybrid 混合应用,95% 页面由 html5 实现,webview 没有使用原生的 webview,是使用的名字叫 crosswalk 的 webview。我现在的问题是在 html5 页面打印 webview 打印出来只有 NATIVE_APP,无法切换到 webview。
开发在代码里面有加 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { WebView.setWebContentsDebuggingEnabled(true);}
可以在谷歌浏览器里面输入 chrome://inspect/#devices 进行调试,但是在在 Appium 打印出来就只有 NATIVE_APP
联系了 Android 开发,他也不懂 Appium,根据我给他的信息他搜索了一下,找到一篇帖子(链接如下):
https://github.com/appium/appium/issues/4597#issuecomment-164540456
里面说要改 android-hybrid.js 这个文件。我找到这个文件之后做了如下修改 (标识部分为修改区域):
修改之后打印出来 webview 还是 NATIVE_APP,请教一下是什么原因?
调试代码如下:
# -*- coding:utf-8 -*-
import os
import unittest
from appium import webdriver
from time import sleep
desird_caps={}
desird_caps['platformName'] = 'Android'
desird_caps['deviceName'] = '76P4C15909025320'
desird_caps['udid'] = '76P4C15909025320'
desird_caps['appPackage'] = 'com.my.shop'
desird_caps['appActivity'] = 'com.my.shop.activity.SplashActivity'
desird_caps['platformVersion'] = '5.1.1'
desird_caps['automationName'] = 'crosswalk'
driver = webdriver.Remote('http://localhost:4723/wd/hub',desird_caps)
def webview():
contexts=driver.contexts
for cotext in contexts:
print cotext
def login():
driver.find_element_by_id('com.my.shop:id/tv_login').click()
driver.find_element_by_id('com.my.shop:id/et_phone_number').send_keys(‘180****5829’)
driver.find_element_by_id('com.my.shop:id/et_password').send_keys(‘123456’)
driver.find_element_by_id('com.my.shop:id/tv_login').click()
sleep(5)
def entershop():
driver.find_elements_by_class_name('android.view.View')[6].click()
sleep(5)
login()
entershop()
webview()
居然已经有解决方案了。
还是通过 class 写一个 list 来定位吧,或者要原生的 h5 页面,h5 页面查到元素之后再放到 app 中操作,嵌套 h5 的话元素一般是不会变的。
crosswalk 和原生 webview 使用的 socket 不一样。appium 根据原生 webview 的规则去找会找不到是正常的。
我觉得你理解错了那位作者说的意思。他给的解决方案是直接用他自己的 docker 里面的 appium(已经添加了对 crosswalk 的支持):https://github.com/hamsterksu/appium-xwalk
具体修改是这样的:
appium/lib/devices/android/android.js
文件中appium/lib/devices/android/android-hybrid.js
/opt/xwalkdriver/
目录中--chromedriver-executable=/opt/xwalkdriver/xwalkdriver64_xwalk_15
简单来说,在 server 端添加了能识别 crosswalk 的 webdriver 使用的 xwalkOptions
的相关代码,并把 chromedriver 执行文件改为 crosswalk 的。
但我觉得其实不全,缺少了识别 crosswalk 进程的相关变更(获取 context 时需要用到)。也许这部分内容他在源镜像里已经修改了。
PS:不知道你的 appium-crosswalkdriver
这个库是哪里找到的?我在你给的链接里没找到。。。
1.5.3 亲测!
你已经安装好了 appium,无论是 GUI 还是 npm 的。
从这里下载 https://github.com/appium/appium/files/320185/webview_helpers_cordova_suppport.patch.zip,解压。
然后进入到 appium/node_modules/appium-android-driver/ 这个目录下,进行补丁:
patch -p1 lib/webview-helpers.js /path/to/patch/file/webview_helpers_cordova_suppport.patch
打好补丁后,重新把 appium-android-driver build 一下:
npm install
从 https://github.com/piotrekkmt/chromedriver-appium/blob/master/chromedriver 下载打过补丁的 chromedriver。
将这个目录下 appium/node_modules/appium-android-driver/node_modules/appium-chromedriver/chromedriver/mac/
下的 chromedriver 替换成打过补丁的。
import os
import unittest
from appium import webdriver
from time import sleep
pkg_name = 'com.xxx.bbb'
desird_caps={}
desird_caps['platformName'] = 'Android'
desird_caps['deviceName'] = 'Samsung'
desird_caps['udid'] = '2bcf97ef'
desird_caps['appPackage'] = pkg_name
desird_caps['appActivity'] = ‘com.xxx.bbb.activity.SplashActivity'
desird_caps['platformVersion'] = '5.1.1'
ANDROID_DEVICE_SOCKET = pkg_name + "_devtools_remote"
desird_caps['androidDeviceSocket'] = ANDROID_DEVICE_SOCKET
chromeOptions = {}
chromeOptions['androidDeviceSocket'] = ANDROID_DEVICE_SOCKET
desird_caps['chromeOptions'] = chromeOptions;
driver = webdriver.Remote('http://localhost:4723/wd/hub',desird_caps)
运行过程中会返回一个没有名字的 webview:
[debug] [ADB] Getting connected devices...
[debug] [ADB] 1 device(s) connected
[debug] [ADB] Running /Applications/android-sdk/platform-tools/adb with args: ["-P",5037,"-s","2bcf97ef","shell","cat","/proc/net/unix"]
[debug] [AndroidDriver] Available contexts: ["NATIVE_APP","WEBVIEW_"]
[MJSONWP] Responding to client with driver.getContexts() result: ["NATIVE_APP","WEBVIEW_"]
[HTTP] <-- GET /wd/hub/session/077ca253-cd31-41da-8ce1-7c2bf0cbb107/contexts 200 80 ms - 97
[BaseDriver] Shutting down because we waited 60 seconds for a command
#4 楼 @lihuazhang 赞~
另外发个帖吧,现在这方面的需求应该不少。
#5 楼 @chenhengjie123 让楼主在 check 一遍吧
之前也一直是用 hamsterksu 的 docker 来解决 crosswalk 的问题,不过替换的方案只适用 1.5 以下的 appium,重构后的 appium 不知道怎么改。windows 下也只能用 docker 来跑,楼主的这个只能适合 mac 环境么 还是其他的环境也适合?
#7 楼 @wang04170 只有 mac 有 1.5.3 版本的 Appium,Android 最高只到 1.4.16,mac 上的 android 环境没装,正在研究@chenhengjie 给的 windows 解决方案,你解决过这样的问题?能留个 Q 么
#3 楼 @chenhengjie123 不清楚 appium 的结构,原理。改的也就不对
#3 楼 @chenhengjie123 hamsterksu 的 docker 我有在 windows 和 linux 上都试过是可以的,只不过它是把 webview 的识别替换成了 xwalk 的了,context 的获取还是用 webview 的 api 来获取,然后获取到的 webview 的 context 的格式也还是'WEBVIEW_XXXX',swich 过去后是实际上是在操作 xwalk 的,只不过它的 dokcer 是 1.4.16 的版本。 想问下@lihuazhang的那个打过补丁的 chromedriver 只能在 mac 版本使用么 。。。
我原来用的 xwalkdriver,只提供了 linux 的,自己编了个 windows 版本,替代 chromedriver,然后按照上面说的改下 appium 的代码就可以用了
—— 来自 TesterHome 官方 安卓客户端
#3 楼 @chenhengjie123 webview 和 crosswalk 的识别方式不一样,新的 appium 好像可通过启动参数设置,我当时是直接修改代码的
—— 来自 TesterHome 官方 安卓客户端
非常感谢@lihuazhang 。小菜鸟遇到疑难杂症的心情是崩溃的,第一次遇到这个问题是两个月前了。
遇到 Hybrid 应用怎么做:
1.先确定开发在测试版本的包里面是否有加调试代码 (一般都有加):if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { WebView.setWebContentsDebuggingEnabled(true);}
如何确定有加,连接好电脑打开应用并停留在 H5 页面,在 Chrome 浏览器中输入 chrome://inspect,按回车会显示设备名字,并且会显示安装包名。点 inspect 就会在 Chrome 浏览器显示 App 的页面,就可以在浏览器上定位元素了。
注意:这一步 Chrome 浏览器要 ***,不然打不开
2.写几条简单的操作步骤进入到 html5 页面,打印 webview: print driver.contexts,如果除了 NATIVE_APP 还有一个的话那说明能切换 webview。切换 webview:driver.switch_to.context('打印出来 webview 名字'),
这样,切换 webview 之后,对第一步中找到元素的 xpth,css 进行定位操作。。
我也遇到了这样的问题,不过,我用了 uiautomatorviewer 直接看元素的 id classname 等 ,没有去取 webview.!
自己弄个 android demo ,让一个 webview 显示的是手机百度页面,
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Set;
import org.junit.Test;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.AndroidDriver;
@SuppressWarnings({ "rawtypes", "unused" })
public class Appium {
@Test
public DesiredCapabilities chacha() {
final DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(CapabilityType.BROWSER_NAME, "");
capabilities.setCapability("deviceName", "emulator-5554");
capabilities.setCapability("platformVersion", "5.1.1");
capabilities.setCapability("platformName", "Android");
capabilities.setCapability("appPackage", "vipdd.club.chacha");
capabilities.setCapability("appActivity", "vipdd.club.chacha.MainActivity");
return capabilities;
}
public static void main(String[] args) throws InterruptedException {
Appium appium = new Appium();
// appium.chacha();
// appium.iulicai();
try {
AppiumDriver<?> driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), appium.chacha()
);
Set<String> contextNames = driver.getContextHandles();
for (String contextName : contextNames) {
System.out.println(contextName);
}
//Thread.sleep(20000);
//点击OK 显示出手机百度页面
driver.findElementById("vipdd.club.chacha:id/btnOK").click();
driver.findElementsByClassName("android.widget.EditText").get(1).sendKeys("appium");
driver.findElementsByClassName("android.widget.Button").get(1).click();
// 根据 content-desc 查找元素
//driver.findElementByAccessibilityId("我的账户").click();
//driver.findElementByAccessibilityId("请输入手机号码").sendKeys("abc");
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
也不知道是不是这样
#2 楼 @leizi0715 我切换到 webview 后,定位到 html5 元素放到 app 中操作,为什么他不执行呢,也不报错。就是没有反应,android 版本 4.4.4.
1
LZ 现在问题解决了么,我也遇到这个问题。
ANDROID_DEVICE_SOCKET = pkg_name + "_devtools_remote"
desird_caps['androidDeviceSocket'] = ANDROID_DEVICE_SOCKET
chromeOptions = {}
chromeOptions['androidDeviceSocket'] = ANDROID_DEVICE_SOCKET
请问这一部分用 java 怎么写?原谅我是 Python 菜鸟
#4 楼 @Lihuazhang 我按照你的方法是可以看到 WEBVIEW,但是切换到 webview 进行 findElement 的时候却一直报 SessionNotFoundException:no such session 的错误。
我按这个步骤已经配置完成,Chromedriver 也下了 crosswlak 补丁的这一版本。上下文也打印出来,但是当我切换 webview 的时候报错,是因为这个 chromedriver 的补丁版本不对吗 ??
提示