选择器
WebDriver 协议提供了几种选择器策略来查询元素。WebdriverIO将它们简化以保持选择元素的简单性。请注意,尽管查询元素的命令被称为$
和$$
,但它们与jQuery或Sizzle选择器引擎没有任何关系。
虽然有很多不同的选择器可用,但只有少数几个提供了一种有弹性的方式来找到正确的元素。例如,给定以下按钮:
<button
id="main"
class="btn btn-large"
name="submission"
role="button"
data-testid="submit"
>
Submit
</button>
我们__推荐__和__不推荐__以下选择器:
选择器 | 推荐 | 备注 |
---|---|---|
$('button') | 🚨 永不 | 最差 - 太通用,没有上下文。 |
$('.btn.btn-large') | 🚨 永不 | 差。与样式耦合。极易变化。 |
$('#main') | ⚠️ 谨慎使用 | 较好。但仍与样式或JS事件监听器耦合。 |
$(() => document.queryElement('button')) | ⚠️ 谨慎使用 | 有效查询,但编写复杂。 |
$('button[name="submission"]') | ⚠️ 谨慎使用 | 与具有HTML语义的name 属性耦合。 |
$('button[data-testid="submit"]') | ✅ 良好 | 需要额外属性,与a11y无关。 |
$('aria/Submit') 或 $('button=Submit') | ✅ 始终 | 最佳。类似于用户与页面交互的方式。建议使用前端的翻译文件,这样当翻译更新时测试永远不会失败 |
CSS查询选择器
如果没有特别说明,WebdriverIO将使用CSS选择器模式查询元素,例如:
loading...
链接文本
要获取包含特定文本的锚元素,请使用以等号(=
)开头的文本进行查询。
例如:
loading...
你可以通过以下方式查询这个元素:
loading...
部分链接文本
要查找可见文本部分匹配你的搜索值的锚元素,可以在查询字符串前使用*=
(例如*=driver
)进行查询。
你也可以通过以下方式查询上例中的元素:
loading...
注意: 你不能在一个选择器中混合使用多种选择器策略。使用多个链式元素查询来达到相同的目标,例如:
const elem = await $('header h1*=Welcome') // 这样不行!!!
// 改用
const elem = await $('header').$('*=driver')
包含特定文本的元素
同样的技术也可以应用于元素。另外,还可以使用.=
或.*=
在查询中进行不区分大小写的匹配。
例如,这里是查询文本为"Welcome to my Page"的一级标题:
loading...
你可以通过以下方式查询该元素:
loading...
或使用查询部分文本:
loading...
对于id
和class
名称也是一样的:
loading...
你可以通过以下方式查询该元素:
loading...
注意: 你不能在一个选择器中混合使用多种选择器策略。使用多个链式元素查询来达到相同的目标,例如:
const elem = await $('header h1*=Welcome') // 这样不行!!!
// 改用
const elem = await $('header').$('h1*=Welcome')
标签名称
要查询具有特定标签名称的元素,使用<tag>
或<tag />
。
loading...
你可以通过以下方式查询该元素:
loading...
名称属性
要查询具有特定名称属性的元素,你可以使用普通的CSS3选择器或从JSONWireProtocol提供的名称策略,方法是将类似[name="some-name"]作为选择器参数传递:
loading...
loading...
注意: 这种选择器策略已被弃用,只在由JSONWireProtocol协议运行的旧浏览器或使用Appium时才有效。
xPath
也可以通过特定的xPath查询元素。
xPath选择器的格式如//body/div[6]/div[1]/span[1]
。
loading...
你可以通过以下方式查询第二个段落:
loading...
你可以使用xPath在DOM树中上下遍历:
loading...
无障碍名称选择器
通过无障碍名称查询元素。无障碍名称是当元素获得焦点时屏幕阅读器宣布的内容。无障碍名称的值可以是视觉内容或隐藏的文本替代内容。
你可以在我们的发布博客文章中了解更多关于这个选择器的信息
通过aria-label
获取
loading...
loading...
通过aria-labelledby
获取
loading...
loading...
通过内容获取
loading...
loading...
通过标题获取
loading...
loading...
通过alt
属性获取
loading...
loading...
ARIA - 角色属性
要基于ARIA角色查询元素,你可以直接指定元素的角色,如[role=button]
作为选择器参数:
loading...
loading...
ID属性
WebDriver协议不支持"id"定位策略,应该使用CSS或xPath选择器策略来查找使用ID的元素。
然而,一些驱动程序(例如Appium You.i Engine Driver)可能仍然支持此选择器。
当前支持的ID选择器语法为:
//css定位器
const button = await $('#someid')
//xpath定位器
const button = await $('//*[@id="someid"]')
//id策略
// 注意:仅在Appium或类似支持"ID"定位策略的框架中有效
const button = await $('id=resource-id/iosname')
JS函数
你还可以使用JavaScript函数通过Web原生API获取元素。当然,你只能在Web上下文中执行此操作(例如,browser
或移动设备中的Web上下文)。
给定以下HTML结构:
loading...
你可以按如下方式查询#elem
的兄弟元素:
loading...
深层选择器
从WebdriverIO的v9
版本开始,不再需要这种特殊的选择器,因为WebdriverIO会自动穿透Shadow DOM。建议通过移除前面的>>>
来迁移此选择器。
许多前端应用程序严重依赖于shadow DOM元素。在没有变通方法的情况下,技术上不可能查询shadow DOM内的元素。shadow$
和shadow$$
曾是这样的变通方法,但有其局限性。使用深层选择器,你现在可以使用通用查询命令查询任何shadow DOM内的所有元素。
假设我们有一个具有以下结构的应用程序:
使用此选择器,你可以查询嵌套在另一个shadow DOM中的<button />
元素,例如:
loading...
移动选择器
对于混合移动测试,重要的是自动化服务器在执行命令之前处于正确的上下文中。对于自动化手势,驱动程序理想情况下应设置为本机上下文。但要从DOM中选择元素,驱动程序需要设置为平台的webview上下文。只有这样,才能使用上面提到的方法。
对于原生移动测试,无需在上下文之间切换,因为你必须使用移动策略并直接使用底层设备自动化技术。当测试需要对查找元素进行精细控制时,这特别有用。
Android UiAutomator
Android的UI Automator框架提供了多种查找元素的方法。你可以使用UI Automator API,特别是UiSelector类来定位元素。在Appium中,你 将Java代码作为字符串发送到服务器,服务器在应用程序环境中执行它,并返回元素。
const selector = 'new UiSelector().text("Cancel").className("android.widget.Button")'
const button = await $(`android=${selector}`)
await button.click()
Android DataMatcher和ViewMatcher(仅限Espresso)
Android的DataMatcher策略提供了一种通过Data Matcher查找元素的方法
const menuItem = await $({
"name": "hasEntry",
"args": ["title", "ViewTitle"]
})
await menuItem.click()
类似地,View Matcher
const menuItem = await $({
"name": "hasEntry",
"args": ["title", "ViewTitle"],
"class": "androidx.test.espresso.matcher.ViewMatchers"
})
await menuItem.click()
Android View Tag(仅限Espresso)
视图标签策略提供了一种通过元素的标签查找元素的便捷方法。
const elem = await $('-android viewtag:tag_identifier')
await elem.click()
iOS UIAutomation
在自动化iOS应用程序时,可以使用Apple的UI Automation框架查找元素。
这个JavaScript API有方法可以访问视图及其上的所有内容。
const selector = 'UIATarget.localTarget().frontMostApp().mainWindow().buttons()[0]'
const button = await $(`ios=${selector}`)
await button.click()
你还可以在Appium中的iOS UI Automation中使用谓词搜索进一步优化元素选择。详情参见此处。
iOS XCUITest谓词字符串和类链
使用iOS 10及以上版本(使用XCUITest
驱动程序),你可以使用谓词字符串:
const selector = `type == 'XCUIElementTypeSwitch' && name CONTAINS 'Allow'`
const switch = await $(`-ios predicate string:${selector}`)
await switch.click()
以及类链:
const selector = '**/XCUIElementTypeCell[`name BEGINSWITH "D"`]/**/XCUIElementTypeButton'
const button = await $(`-ios class chain:${selector}`)
await button.click()
无障碍ID
accessibility id
定位策略旨在读取UI元素的唯一标识符。这样做的好处是在本地化或任何可能改变文本的其他过程中不会改变。此外,如果功能相同的元素具有相同的accessibility id,它还可以帮助创建跨平台测试。
- 对于iOS,这是Apple在此处布局的
accessibility identifier
。 - 对于Android,
accessibility id
映射到元素的content-description
,如此处所述。
对于两个平台,通过accessibility id
获取元素(或多个元素)通常是最好的方法。这也是优于已弃用的name
策略的首选方式。
const elem = await $('~my_accessibility_identifier')
await elem.click()
类名
class name
策略 是表示当前视图上UI元素的string
。
- 对于iOS,它是UIAutomation类的完整名称,将以
UIA-
开头,例如文本字段的UIATextField
。完整参考可在此处找到。 - 对于Android,它是UI Automator 类的完全限定名称,例如文本字段的
android.widget.EditText
。完整参考可在此处找到。 - 对于Youi.tv,它是Youi.tv类的完整名称,将以
CYI-
开头,例如推按钮元素的CYIPushButtonView
。完整参考可在You.i Engine Driver的GitHub页面找到
// iOS示例
await $('UIATextField').click()
// Android示例
await $('android.widget.DatePicker').click()
// Youi.tv示例
await $('CYIPushButtonView').click()
链式选择器
如果你想在查询中更具体,可以链接选择器,直到找到正确的元素。如果在实际命令之前调用element
,WebdriverIO将从该元素开始查询。
例如,如果你有一个DOM结构如下:
<div class="row">
<div class="entry">
<label>Product A</label>
<button>Add to cart</button>
<button>More Information</button>
</div>
<div class="entry">
<label>Product B</label>
<button>Add to cart</button>
<button>More Information</button>
</div>
<div class="entry">
<label>Product C</label>
<button>Add to cart</button>
<button>More Information</button>
</div>
</div>
如果你想将产品B添加到购物车,仅使用CSS选择器会很困难。
使用选择器链接,就容易多了。只需逐步缩小所需元素的范围:
await $('.row .entry:nth-child(2)').$('button*=Add').click()