跳到主要内容

选择器

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选择器模式查询元素,例如:

selectors/example.js
loading...

链接文本

要获取包含特定文本的锚元素,请使用以等号(=)开头的文本进行查询。

例如:

selectors/example.html
loading...

你可以通过以下方式查询这个元素:

selectors/example.js
loading...

部分链接文本

要查找可见文本部分匹配你的搜索值的锚元素,可以在查询字符串前使用*=(例如*=driver)进行查询。

你也可以通过以下方式查询上例中的元素:

selectors/example.js
loading...

注意: 你不能在一个选择器中混合使用多种选择器策略。使用多个链式元素查询来达到相同的目标,例如:

const elem = await $('header h1*=Welcome') // 这样不行!!!
// 改用
const elem = await $('header').$('*=driver')

包含特定文本的元素

同样的技术也可以应用于元素。另外,还可以使用.=.*=在查询中进行不区分大小写的匹配。

例如,这里是查询文本为"Welcome to my Page"的一级标题:

selectors/example.html
loading...

你可以通过以下方式查询该元素:

selectors/example.js
loading...

或使用查询部分文本:

selectors/example.js
loading...

对于idclass名称也是一样的:

selectors/example.html
loading...

你可以通过以下方式查询该元素:

selectors/example.js
loading...

注意: 你不能在一个选择器中混合使用多种选择器策略。使用多个链式元素查询来达到相同的目标,例如:

const elem = await $('header h1*=Welcome') // 这样不行!!!
// 改用
const elem = await $('header').$('h1*=Welcome')

标签名称

要查询具有特定标签名称的元素,使用<tag><tag />

selectors/example.html
loading...

你可以通过以下方式查询该元素:

selectors/example.js
loading...

名称属性

要查询具有特定名称属性的元素,你可以使用普通的CSS3选择器或从JSONWireProtocol提供的名称策略,方法是将类似[name="some-name"]作为选择器参数传递:

selectors/example.html
loading...
selectors/example.js
loading...

注意: 这种选择器策略已被弃用,只在由JSONWireProtocol协议运行的旧浏览器或使用Appium时才有效。

xPath

也可以通过特定的xPath查询元素。

xPath选择器的格式如//body/div[6]/div[1]/span[1]

selectors/xpath.html
loading...

你可以通过以下方式查询第二个段落:

selectors/example.js
loading...

你可以使用xPath在DOM树中上下遍历:

selectors/example.js
loading...

无障碍名称选择器

通过无障碍名称查询元素。无障碍名称是当元素获得焦点时屏幕阅读器宣布的内容。无障碍名称的值可以是视觉内容或隐藏的文本替代内容。

信息

你可以在我们的发布博客文章中了解更多关于这个选择器的信息

通过aria-label获取

selectors/aria.html
loading...
selectors/example.js
loading...

通过aria-labelledby获取

selectors/aria.html
loading...
selectors/example.js
loading...

通过内容获取

selectors/aria.html
loading...
selectors/example.js
loading...

通过标题获取

selectors/aria.html
loading...
selectors/example.js
loading...

通过alt属性获取

selectors/aria.html
loading...
selectors/example.js
loading...

ARIA - 角色属性

要基于ARIA角色查询元素,你可以直接指定元素的角色,如[role=button]作为选择器参数:

selectors/aria.html
loading...
selectors/example.js
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结构:

selectors/js.html
loading...

你可以按如下方式查询#elem的兄弟元素:

selectors/example.js
loading...

深层选择器

注意

从WebdriverIO的v9版本开始,不再需要这种特殊的选择器,因为WebdriverIO会自动穿透Shadow DOM。建议通过移除前面的>>>来迁移此选择器。

许多前端应用程序严重依赖于shadow DOM元素。在没有变通方法的情况下,技术上不可能查询shadow DOM内的元素。shadow$shadow$$曾是这样的变通方法,但有其局限性。使用深层选择器,你现在可以使用通用查询命令查询任何shadow DOM内的所有元素。

假设我们有一个具有以下结构的应用程序:

Chrome示例

使用此选择器,你可以查询嵌套在另一个shadow DOM中的<button />元素,例如:

selectors/example.js
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()

Appium图像选择器

使用-image定位策略,可以向Appium发送表示你想访问的元素的图像文件。

支持的文件格式:jpg,png,gif,bmp,svg

完整参考可在此处找到

const elem = await $('./file/path/of/image/test.jpg')
await elem.click()

注意:Appium使用此选择器的方式是内部制作(应用程序)截图,并使用提供的图像选择器验证元素是否可以在该(应用程序)截图中找到。

请注意,Appium可能会调整所拍摄的(应用程序)截图的大小,使其符合你的(应用程序)屏幕的CSS大小(这会在iPhone上发生,也会在具有视网膜显示器的Mac机器上发生,因为DPR大于1)。这将导致无法找到匹配项,因为提供的图像选择器可能是从原始截图中获取的。 你可以通过更新Appium服务器设置来解决这个问题,请参阅Appium文档了解设置,以及此评论获取详细解释。

React选择器

WebdriverIO提供了一种基于组件名称选择React组件的方法。为此,你可以选择使用两个命令:react$react$$

这些命令允许你从React虚拟DOM中选择组件,并返回单个WebdriverIO元素或元素数组(取决于使用的函数)。

注意:命令react$react$$在功能上相似,只是react$$会将所有匹配的实例作为WebdriverIO元素数组返回,而react$将返回找到的第一个实例。

基本示例

// index.jsx
import React from 'react'
import ReactDOM from 'react-dom'

function MyComponent() {
return (
<div>
MyComponent
</div>
)
}

function App() {
return (<MyComponent />)
}

ReactDOM.render(<App />, document.querySelector('#root'))

在上面的代码中,应用程序内部有一个简单的MyComponent实例,React将其渲染在id为root的HTML元素中。

使用browser.react$命令,你可以选择MyComponent的实例:

const myCmp = await browser.react$('MyComponent')

现在你已经将WebdriverIO元素存储在myCmp变量中,你可以针对它执行元素命令。

过滤组件

WebdriverIO内部使用的库允许你通过组件的props和/或state来过滤选择。为此,你需要为props传递第二个参数和/或为state传递第三个参数给浏览器命令。

// index.jsx
import React from 'react'
import ReactDOM from 'react-dom'

function MyComponent(props) {
return (
<div>
Hello { props.name || 'World' }!
</div>
)
}

function App() {
return (
<div>
<MyComponent name="WebdriverIO" />
<MyComponent />
</div>
)
}

ReactDOM.render(<App />, document.querySelector('#root'))

如果你想选择具有prop nameWebdriverIOMyComponent实例,可以执行如下命令:

const myCmp = await browser.react$('MyComponent', {
props: { name: 'WebdriverIO' }
})

如果你想通过state过滤选择,browser命令看起来类似于:

const myCmp = await browser.react$('MyComponent', {
state: { myState: 'some value' }
})

处理React.Fragment

使用react$命令选择React fragments时,WebdriverIO将返回该组件的第一个子元素作为组件节点。如果使用react$$,你将收到一个包含与选择器匹配的fragments内所有HTML节点的数组。

// index.jsx
import React from 'react'
import ReactDOM from 'react-dom'

function MyComponent() {
return (
<React.Fragment>
<div>
MyComponent
</div>
<div>
MyComponent
</div>
</React.Fragment>
)
}

function App() {
return (<MyComponent />)
}

ReactDOM.render(<App />, document.querySelector('#root'))

给定上面的示例,命令的工作方式如下:

await browser.react$('MyComponent') // 返回第一个<div />的WebdriverIO元素
await browser.react$$('MyComponent') // 返回数组[<div />, <div />]的WebdriverIO元素

注意: 如果你有多个MyComponent实例,并且使用react$$选择这些fragment组件,你将得到一个一维数组,包含所有节点。换句话说,如果你有3个<MyComponent />实例,你将得到一个包含六个WebdriverIO元素的数组。

自定义选择器策略

如果你的应用程序需要特定的方式来获取元素,你可以自定义一个选择器策略,然后使用custom$custom$$。为此,在测试开始时注册你的策略,例如在before钩子中:

queryElements/customStrategy.js
loading...

给定以下HTML片段:

queryElements/example.html
loading...

然后通过调用以下方式使用它:

queryElements/customStrategy.js
loading...

注意: 这只在可以运行execute命令的Web环境中有效。

Welcome! How can I help?

WebdriverIO AI Copilot