跳至主要内容

选择器

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') // doesn't work!!!
// use instead
const elem = await $('header').$('*=driver')

具有特定文本的元素

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

例如,以下是如何查询文本为“欢迎来到我的页面”的 1 级标题:

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') // doesn't work!!!
// use instead
const elem = await $('header').$('h1*=Welcome')

标签名

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

selectors/example.html
loading...

您可以通过调用以下命令查询此元素:

selectors/example.js
loading...

名称属性

对于查询具有特定名称属性的元素,您可以使用普通的 CSS3 选择器,或者通过传递类似于[name="some-name"]的内容作为选择器参数,使用JSONWireProtocol中提供的名称策略。

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 属性

定位器策略“id”在 WebDriver 协议中不受支持,应改为使用 CSS 或 XPath 选择器策略来查找使用 ID 的元素。

但是,某些驱动程序(例如Appium You.i Engine Driver)可能仍然支持此选择器。

当前支持的 ID 选择器语法为

//css locator
const button = await $('#someid')
//xpath locator
const button = await $('//*[@id="someid"]')
//id strategy
// Note: works only in Appium or similar frameworks which supports locator strategy "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 Example

使用此选择器,您可以查询嵌套在另一个 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 框架来查找元素。

此 JavaScriptAPI具有访问视图及其上所有内容的方法。

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 元素的唯一标识符。这样做的好处是在本地化或任何可能更改文本的过程中都不会更改。此外,如果功能相同的元素具有相同的辅助功能 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 example
await $('UIATextField').click()
// Android example
await $('android.widget.DatePicker').click()
// Youi.tv example
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 VirtualDOM中选择组件,并返回单个 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 和/或状态来过滤选择。为此,您需要为 props 传递第二个参数,或为状态传递第三个参数到浏览器命令。

// 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'))

如果您想选择具有nameWebdriverIO的 prop 的MyComponent实例,您可以执行如下命令:

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

如果您想根据状态过滤选择,则browser命令将如下所示:

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

处理React.Fragment

当使用react$命令选择 React片段时,WebdriverIO 将返回该组件的第一个子节点作为组件的节点。如果您使用react$$,您将收到一个数组,其中包含片段内与选择器匹配的所有 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') // returns the WebdriverIO Element for the first <div />
await browser.react$$('MyComponent') // returns the WebdriverIO Elements for the array [<div />, <div />]

注意:如果您有多个MyComponent实例,并且您使用react$$来选择这些片段组件,则将返回一个包含所有节点的一维数组。换句话说,如果您有 3 个<MyComponent />实例,则将返回一个包含 6 个 WebdriverIO 元素的数组。

自定义选择器策略

如果您的应用程序需要一种特定的方法来获取元素,您可以自己定义一个自定义选择器策略,可以使用custom$custom$$。为此,请在测试开始时(例如在before钩子中)注册一次您的策略。

queryElements/customStrategy.js
loading...

给定以下 HTML 代码片段:

queryElements/example.html
loading...

然后通过调用以下命令使用它:

queryElements/customStrategy.js
loading...

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

欢迎!我如何帮助您?

WebdriverIO AI Copilot