更多内容见草稿: https://wkmcyz.notion.site/Appium-H5-c9c287855ef74ef0ae5d8f819da3923f
本文章主要介绍在 Android 平台上使用 appium 对 app 内的 webView 进行自动化操作上的一些知识,包括一些配置和可以进行的操作等。
需要读者:
- 熟悉如何使用 appium 进行 native app 的自动化操作
自动化操作 App 内的 webview 的前置要求
注意: 这是针对某个 app 内的 webview 进行自动化操作;而不是使用 chrome 等浏览器应用里的 webview 进行操作。
- 本地有和手机系统的 webview 版本一致的 chromedriver 。
- app 的 webView 已经开启了 debug 模式。
这两者的具体操作方法为:
- 如何获取 chromedriver?
- 命令行里执行
adb shell dumpsys package com.google.android.webview | grep versionName
可以获取当前设备的 webview 版本(有些时候会获得多个值,可以每个版本都试一下)。 或者在 “开发者选项” 里也可以查看到系统的 webView 版本。 - 从 https://registry.npmmirror.com/binary.html?path=chromedriver/ 这个地址下载对应版本的 chromedriver。
- 如何开启 app 的 webview 的 debug 模式?
修改 app 代码实现,详情请搜索setWebContentsDebuggingEnabled
在 appium 端,增加 desired_capabilities 信息:
# python
options.chromedriver_executable = '/tmp/chromedriver'
options.chrome_options = {
# 待测试的 app package
"androidPackage": YOUR_APP_PACKAGE,
}
# 省略其他的 driver capabilities 信息。
# 打开 app 的 webView 页面后,查看当前的 context。
print(driver.contexts)
# 切换到 webView 的 context
driver.switch_to.context("WEBVIEW_XXXX")
appium driver 可以执行的功能。
从 appium 调用 js
def run_js(driver,js_code):
driver.execute_script(js_code)
run_js(driver, "console.log('123')")
获取 js 的 console 日志
logs = driver.get_log('browser')
for l in logs:
print("log:", l)
# log: {'level': 'INFO', 'message': 'x.js 4:236286 "123"', 'source': 'console-api', 'timestamp': 1681386146296}
# log: {'level': 'INFO', 'message': 'x.js 4:236286 "123"', 'source': 'console-api', 'timestamp': 1681386146355}
获取页面信息
driver.page_source
寻找元素
- 使用 xpath 寻找
略过。
- 使用 css_selector 寻找。
如何使用 CSS Selector 获取元素
要使用 CSS 选择器获取元素,首先确保您处于 WebView 上下文,因为 CSS 选择器仅在 WebView 中可用。然后,使用 **`find_element_by_css_selector()`** 或 **`find_elements_by_css_selector()`** 方法查找元素。以下是一些使用 CSS 选择器查找元素的示例:
1. **通过 ID 获取元素**:
```
pythonCopy code
element = driver.find_element_by_css_selector("#element_id")
```
2. **通过类名获取元素**:
```
pythonCopy code
elements = driver.find_elements_by_css_selector(".element_class")
```
3. **通过标签名获取元素**:
```
pythonCopy code
elements = driver.find_elements_by_css_selector("tag_name")
```
4. **通过属性获取元素**:
```
pythonCopy code
element = driver.find_element_by_css_selector("tag_name[attribute='value']")
```
5. **通过子元素获取元素**:
```
pythonCopy code
element = driver.find_element_by_css_selector("parent_tag > child_tag")
```
6. **通过兄弟元素获取元素**:
```
pythonCopy code
element = driver.find_element_by_css_selector("sibling_tag1 ~ sibling_tag2")
```
这些仅是使用 CSS 选择器查找元素的一些基本示例。CSS 选择器提供了许多其他方法和组合,可以更具体地选择元素。在使用 CSS 选择器时,请确保您熟悉 CSS 选择器语法。
如何使用 CSS Selector 获取元素
通过属性获取元素是指使用 CSS 选择器根据元素的属性(如 id
、class
、name
等)来查找页面上的元素。以下是一些基于属性的 CSS 选择器示例:
-
通过特定属性获取元素:
查找具有特定属性的元素,无论属性值是什么。例如,查找具有
data-custom-attribute
属性的所有元素:elements = driver.find_elements_by_css_selector("[data-custom-attribute]")
-
通过属性值获取元素:
查找具有特定属性值的元素。例如,查找
name
属性值为username
的所有元素:elements = driver.find_elements_by_css_selector("[name='username']")
-
通过部分属性值获取元素:
查找属性值包含指定子串的元素。例如,查找
class
属性值包含container
的所有元素:elements = driver.find_elements_by_css_selector("[class*='container']")
-
通过属性值前缀获取元素:
查找属性值以特定前缀开头的元素。例如,查找
href
属性值以https://
开头的所有链接:elements = driver.find_elements_by_css_selector("a[href^='https://']")
-
通过属性值后缀获取元素:
查找属性值以特定后缀结尾的元素。例如,查找
src
属性值以.jpg
结尾的所有图片:elements = driver.find_elements_by_css_selector("img[src$='.jpg']")
这些示例展示了如何使用不同的属性选择器来查找元素。可以组合这些选择器以满足更复杂的选择需求。请注意,这些选择器仅适用于 WebView 上下文,因为它们是基于 CSS 选择器的。在原生应用上下文中,请使用其他定位策略。
获取元素位置
-
通过上面的方法获取到目标元素以后,可以使用
element.rect
获取到其在 webView 里的宽高和位置坐标。
webView 使用逻辑像素,跟物理像素有倍数差距。int(driver.execute_script("return window.devicePixelRatio;"))
def get_device_pixel_ratio(driver) -> int: device_pixel_ratio = int(driver.execute_script("return window.devicePixelRatio;")) return device_pixel_ratio
-
webView 是可滑动的,获得的 x/y 需要考虑当前的滑动值。
-
获取 element 在屏幕上的坐标 (没考虑 statusbar)
def get_device_pixel_ratio(driver) -> float: device_pixel_ratio = float(driver.execute_script("return window.devicePixelRatio;")) return device_pixel_ratio def get_scroll(driver) -> Tuple[float, float]: scroll_x = float(driver.execute_script("return window.pageXOffset;")) scroll_y = float(driver.execute_script("return window.pageYOffset;")) return scroll_x, scroll_y def get_element_asb_location(driver, element) -> Tuple[float, float, float, float]: webview_elment_rect = element.rect webview_elment_height = webview_elment_rect['height'] webview_elment_width = webview_elment_rect['width'] webview_elment_x = webview_elment_rect['x'] webview_elment_y = webview_elment_rect['y'] print(webview_elment_rect) device_pixel_ratio = get_device_pixel_ratio(driver) scroll_x, scroll_y = get_scroll(driver) print(scroll_x, scroll_y) return ( webview_elment_height * device_pixel_ratio, webview_elment_width * device_pixel_ratio, (webview_elment_x - scroll_x) * device_pixel_ratio, (webview_elment_y - scroll_y) * device_pixel_ratio, )