其他定位器
介绍
查看主要的 定位器指南 以了解最常用和推荐的定位器。
除了推荐的定位器,如 Page.getByRole() 和 Page.getByText(),Playwright 还支持本指南中描述的各种其他定位器。
CSS 定位器
我们建议优先考虑 用户可见定位器,如文本或可访问角色,而不是使用与实现相关的 CSS,因为这种 CSS 在页面更改时可能会失效。
Playwright 可以通过 CSS 选择器定位元素。
page.locator("css=button").click();
Playwright 以两种方式扩展标准 CSS 选择器
- CSS 选择器穿透开放的影子 DOM。
- Playwright 添加了自定义伪类,如
:visible
、:has-text()
、:has()
、:is()
、:nth-match()
等。
CSS:按文本匹配
Playwright 包含一些 CSS 伪类,用于按其文本内容匹配元素。
-
article:has-text("Playwright")
-:has-text()
匹配包含指定文本的任何元素,可能在子元素或后代元素中。匹配不区分大小写,修剪空格并搜索子字符串。例如,
article:has-text("Playwright")
匹配<article><div>Playwright</div></article>
。请注意,
:has-text()
应该与其他 CSS 说明符一起使用,否则它将匹配所有包含指定文本的元素,包括<body>
。// Wrong, will match many elements including <body>
page.locator(":has-text(\"Playwright\")").click();
// Correct, only matches the <article> element
page.locator("article:has-text(\"Playwright\")").click(); -
#nav-bar :text("Home")
-:text()
伪类匹配包含指定文本的最小元素。匹配不区分大小写,修剪空格并搜索子字符串。例如,这将找到
#nav-bar
元素内部的某个位置包含文本“Home”的元素page.locator("#nav-bar :text('Home')").click();
-
#nav-bar :text-is("Home")
-:text-is()
伪类匹配具有精确文本的最小元素。精确匹配区分大小写,修剪空格并搜索完整字符串。例如,
:text-is("Log")
不匹配<button>Log in</button>
,因为<button>
包含单个文本节点"Log in"
,它不等于"Log"
。但是,:text-is("Log")
匹配<button> Log <span>in</span></button>
,因为<button>
包含文本节点" Log "
。类似地,
:text-is("Download")
将不会匹配<button>download</button>
,因为它区分大小写。
-
#nav-bar :text-matches("reg?ex", "i")
-:text-matches()
伪类匹配文本内容与 类似 JavaScript 的正则表达式 相匹配的最小元素。例如,
:text-matches("Log\s*in", "i")
匹配<button>Login</button>
和<button>log IN</button>
。
文本匹配始终规范化空格。例如,它将多个空格变成一个空格,将换行符变成空格,并忽略前导和尾随空格。
类型为 button
和 submit
的输入元素通过其 value
而不是文本内容进行匹配。例如,:text("Log in")
匹配 <input type=button value="Log in">
。
CSS:仅匹配可见元素
Playwright 在 CSS 选择器中支持 :visible
伪类。例如,css=button
匹配页面上的所有按钮,而 css=button:visible
仅匹配可见按钮。这对于区分非常相似但可见性不同的元素非常有用。
考虑一个页面,其中有两个按钮,第一个不可见,第二个可见。
<button style='display: none'>Invisible</button>
<button>Visible</button>
-
这将找到这两个按钮并抛出一个 严格性 违规错误
page.locator("button").click();
-
这将只找到第二个按钮,因为它可见,然后单击它。
page.locator("button:visible").click();
CSS:包含其他元素的元素
:has()
伪类是一个 实验性 CSS 伪类。如果作为参数传递的任何选择器相对于给定元素的 :scope
至少匹配一个元素,则它将返回一个元素。
以下代码段返回包含 <div class=promo>
的 <article>
元素的文本内容。
page.locator("article:has(div.promo)").textContent();
CSS:匹配其中一个条件的元素
用逗号分隔的 CSS 选择器列表将匹配所有可以通过该列表中一个选择器进行选择的元素。
// Clicks a <button> that has either a "Log in" or "Sign in" text.
page.locator("button:has-text(\"Log in\"), button:has-text(\"Sign in\")").click();
:is()
伪类是一个 实验性 CSS 伪类,它可能有助于在元素上指定额外的条件列表。
CSS:根据布局匹配元素
根据布局进行匹配可能会产生意外结果。例如,当布局更改一个像素时,可能会匹配不同的元素。
有时,当目标元素缺乏独特特征时,很难想到一个好的选择器来定位目标元素。在这种情况下,使用 Playwright 布局 CSS 伪类可能会有所帮助。这些可以与常规 CSS 结合使用,以精确定位多个选择器之一。
例如,input:right-of(:text("Password"))
匹配位于文本“Password”右侧的输入字段 - 这在页面有多个难以区分的输入字段时很有用。
请注意,布局伪类除了其他东西,例如 input
之外,很有用。如果您单独使用布局伪类,例如 :right-of(:text("Password"))
,您很可能不会获得您要查找的输入,而是位于文本和目标输入之间的某个空元素。
布局伪类使用 边界客户端矩形 来计算元素的距离和相对位置。
:right-of(div > button)
- 匹配位于匹配内部选择器的任何元素右侧的元素,位于任何垂直位置。:left-of(div > button)
- 匹配位于匹配内部选择器的任何元素左侧的元素,位于任何垂直位置。:above(div > button)
- 匹配位于匹配内部选择器的任何元素上方的元素,位于任何水平位置。:below(div > button)
- 匹配位于匹配内部选择器的任何元素下方的元素,位于任何水平位置。:near(div > button)
- 匹配位于匹配内部选择器的任何元素附近(在 50 个 CSS 像素内)的元素。
请注意,结果匹配将按其到锚元素的距离排序,因此您可以使用 Locator.first() 选择最靠近的元素。这只有在您有一个类似元素列表时才有用,其中最靠近的显然是正确的元素。但是,在其他情况下使用 Locator.first() 很可能不会按预期工作 - 它不会定位您要查找的元素,而会定位一些其他元素,这些元素碰巧是最靠近的元素,例如随机的空 <div>
,或者一个已滚动出且当前不可见的元素。
// Fill an input to the right of "Username".
page.locator("input:right-of(:text(\"Username\"))").fill("value");
// Click a button near the promo card.
page.locator("button:near(.promo-card)").click();
// Click the radio input in the list closest to the "Label 3".
page.locator("[type=radio]:left-of(:text(\"Label 3\"))").first().click();
所有布局伪类都支持可选的最大像素距离作为最后一个参数。例如 button:near(:text("Username"), 120)
匹配距离包含文本“Username”的元素最多 120 个 CSS 像素的按钮。
CSS:从查询结果中选择第 N 个匹配项
通常可以通过某些属性或文本内容来区分元素,这对于页面更改更具弹性。
有时页面包含许多类似的元素,很难选择一个特定的元素。例如
<section> <button>Buy</button> </section>
<article><div> <button>Buy</button> </div></article>
<div><div> <button>Buy</button> </div></div>
在这种情况下,:nth-match(:text("Buy"), 3)
将从上面的代码段中选择第三个按钮。请注意,索引从 1 开始。
// Click the third "Buy" button
page.locator(":nth-match(:text('Buy'), 3)").click();
:nth-match()
也可用于使用 Locator.waitFor() 等待指定数量的元素出现。
// Wait until all three buttons are visible
page.locator(":nth-match(:text('Buy'), 3)").waitFor();
与 :nth-child()
不同,元素不必是兄弟姐妹,它们可以在页面的任何位置。在上面的代码段中,所有三个按钮都匹配 :text("Buy")
选择器,而 :nth-match()
选择了第三个按钮。
第 N 个元素定位器
您可以使用 nth=
定位器,传递一个以零为基数的索引,将查询缩小到第 N 个匹配项。
// Click first button
page.locator("button").locator("nth=0").click();
// Click last button
page.locator("button").locator("nth=-1").click();
父元素定位器
当您需要定位某个其他元素的父元素时,大多数情况下,您应该通过子定位器使用 Locator.filter()。例如,考虑以下 DOM 结构
<li><label>Hello</label></li>
<li><label>World</label></li>
如果你想定位标签文本为“Hello”的父级<li>
元素,使用Locator.filter()是最合适的方法。
Locator child = page.getByText("Hello");
Locator parent = page.getByRole(AriaRole.LISTITEM).filter(new Locator.FilterOptions().setHas(child));
另外,如果你无法找到父级元素的合适定位器,可以使用xpath=..
。需要注意的是,这种方法并不稳定,因为 DOM 结构的任何变化都会导致测试失败。如果可能,建议优先使用Locator.filter()。
Locator parent = page.getByText("Hello").locator("xpath=..");
React 定位器
React 定位器处于实验阶段,以_
为前缀。该功能在未来可能会发生变化。
React 定位器允许根据组件名称和属性值查找元素。语法与CSS 属性选择器非常相似,并支持所有 CSS 属性选择器操作符。
在 React 定位器中,组件名称使用驼峰命名法(CamelCase)表示。
page.locator("_react=BookItem").click();
更多示例
- 根据组件匹配:
_react=BookItem
- 根据组件和精确的属性值匹配,区分大小写:
_react=BookItem[author = "Steven King"]
- 只根据属性值匹配,不区分大小写:
_react=[author = "steven king" i]
- 根据组件和真值属性值匹配:
_react=MyButton[enabled]
- 根据组件和布尔值匹配:
_react=MyButton[enabled = false]
- 根据属性值子串匹配:
_react=[author *= "King"]
- 根据组件和多个属性匹配:
_react=BookItem[author *= "king" i][year = 1990]
- 根据嵌套属性值匹配:
_react=[some.nested.value = 12]
- 根据组件和属性值前缀匹配:
_react=BookItem[author ^= "Steven"]
- 根据组件和属性值后缀匹配:
_react=BookItem[author $= "Steven"]
- 根据组件和键匹配:
_react=BookItem[key = '2']
- 根据属性值正则表达式匹配:
_react=[author = /Steven(\\s+King)?/i]
要在树中查找 React 元素名称,请使用React DevTools。
React 定位器支持 React 15 及更高版本。
React 定位器以及React DevTools仅适用于未压缩的应用程序构建。
Vue 定位器
Vue 定位器处于实验阶段,以_
为前缀。该功能在未来可能会发生变化。
Vue 定位器允许根据组件名称和属性值查找元素。语法与CSS 属性选择器非常相似,并支持所有 CSS 属性选择器操作符。
在 Vue 定位器中,组件名称使用短横线命名法(kebab-case)表示。
page.locator("_vue=book-item").click();
更多示例
- 根据组件匹配:
_vue=book-item
- 根据组件和精确的属性值匹配,区分大小写:
_vue=book-item[author = "Steven King"]
- 只根据属性值匹配,不区分大小写:
_vue=[author = "steven king" i]
- 根据组件和真值属性值匹配:
_vue=my-button[enabled]
- 根据组件和布尔值匹配:
_vue=my-button[enabled = false]
- 根据属性值子串匹配:
_vue=[author *= "King"]
- 根据组件和多个属性匹配:
_vue=book-item[author *= "king" i][year = 1990]
- 根据嵌套属性值匹配:
_vue=[some.nested.value = 12]
- 根据组件和属性值前缀匹配:
_vue=book-item[author ^= "Steven"]
- 根据组件和属性值后缀匹配:
_vue=book-item[author $= "Steven"]
- 根据属性值正则表达式匹配:
_vue=[author = /Steven(\\s+King)?/i]
要在树中查找 Vue 元素名称,请使用Vue DevTools。
Vue 定位器支持 Vue2 及更高版本。
Vue 定位器以及Vue DevTools仅适用于未压缩的应用程序构建。
XPath 定位器
我们建议优先使用用户可见的定位器,例如文本或可访问角色,而不是使用与实现绑定的 XPath,因为 XPath 容易在页面更改时失效。
XPath 定位器等效于调用Document.evaluate
。
page.locator("xpath=//button").click();
所有以//
或..
开头的选择器字符串都被认为是 XPath 选择器。例如,Playwright 将'//html/body'
转换为'xpath=//html/body'
。
XPath 不会穿透影子 DOM。
XPath 并集
可以使用管道符号 (|
) 在 XPath 中指定多个选择器。它将匹配所有可以通过列表中某个选择器选定的元素。
// Waits for either confirmation dialog or load spinner.
page.locator("//span[contains(@class, 'spinner__loading')]|//div[@id='confirmation']").waitFor();
标签到表单控件的重新定位
我们建议使用根据标签文本定位,而不是依赖标签到控件的重新定位。
Playwright 中的目标输入操作会自动区分标签和控件,因此您可以定位标签,对关联的控件执行操作。
例如,考虑以下 DOM 结构:<label for="password">Password:</label><input id="password" type="password">
。您可以使用Page.getByText(),根据其“Password”文本定位标签。但是,以下操作将作用于输入,而不是标签
- Locator.click()将点击标签并自动将输入框聚焦;
- Locator.fill()将填充输入框;
- Locator.inputValue()将返回输入框的值;
- Locator.selectText()将选择输入框中的文本;
- Locator.setInputFiles()将为类型为
type=file
的输入框设置文件; - Locator.selectOption()将从下拉框中选择一个选项。
// Fill the input by targeting the label.
page.getByText("Password").fill("secret");
但是,其他方法将定位标签本身,例如assertThat(locator).hasText()将断言标签的文本内容,而不是输入框的内容。
// Fill the input by targeting the label.
assertThat(page.locator("label")).hasText("Password");
传统文本定位器
我们建议使用现代文本定位器。
传统文本定位器匹配包含传入文本的元素。
page.locator("text=Log in").click();
传统文本定位器有几种变体
-
text=Log in
- 默认匹配不区分大小写,去除空格,并搜索子字符串。例如,text=Log
匹配<button>Log in</button>
。page.locator("text=Log in").click();
-
text="Log in"
- 可以使用单引号或双引号转义文本内容,以在去除空格后搜索具有精确内容的文本节点。例如,
text="Log"
不会匹配<button>Log in</button>
,因为<button>
包含一个文本节点"Log in"
,它不等于"Log"
。但是,text="Log"
匹配<button> Log <span>in</span></button>
,因为<button>
包含一个文本节点" Log "
。这种精确模式意味着区分大小写匹配,因此text="Download"
将不会匹配<button>download</button>
。引号括起来的文本内容遵循通常的转义规则,例如,使用
\"
转义双引号括起来的字符串中的双引号:text="foo\"bar"
。page.locator("text='Log in'").click();
-
/Log\s*in/i
- 文本内容可以是类似 JavaScript 的正则表达式,用/
符号括起来。例如,text=/Log\s*in/i
匹配<button>Login</button>
和<button>log IN</button>
。page.locator("text=/Log\\s*in/i").click();
以引号("
或'
)开头和结尾的字符串选择器被认为是传统文本定位器。例如,"Log in"
在内部被转换为text="Log in"
。
匹配始终会规范化空格。例如,它将多个空格转换为一个空格,将换行符转换为空格,并忽略开头和结尾的空格。
类型为button
和submit
的输入元素通过其value
属性进行匹配,而不是文本内容。例如,text=Log in
匹配<input type=button value="Log in">
。
id、data-testid、data-test-id、data-test 选择器
我们建议使用根据测试 ID 定位。
Playwright 支持使用某些属性选择元素的简写。目前,仅支持以下属性
id
data-testid
data-test-id
data-test
// Fill an input with the id "username"
page.locator("id=username").fill("value");
// Click an element with data-test-id "submit"
page.locator("data-test-id=submit").click();
属性选择器不是 CSS 选择器,因此不支持任何 CSS 特定的内容,例如:enabled
。要使用更多功能,请使用正确的css选择器,例如css=[data-test="login"]:enabled
。
链接选择器
我们建议使用链接定位器。
定义为engine=body
或简写形式的选择器可以与>>
标记组合,例如selector1 >> selector2 >> selectors3
。链接选择器时,下一个选择器将相对于上一个选择器的结果进行查询。
例如,
css=article >> css=.bar > .baz >> css=span[attr=value]
等效于
document
.querySelector('article')
.querySelector('.bar > .baz')
.querySelector('span[attr=value]');
如果选择器需要在文本内容中包含>>
,则应将其在字符串中转义,以避免与链接分隔符混淆,例如text="some >> text"
。
中间匹配
我们建议使用根据另一个定位器过滤,以定位包含其他元素的元素。
默认情况下,链接选择器解析为最后一个选择器查询的元素。选择器可以以*
为前缀,以捕获中间选择器查询的元素。
例如,css=article >> text=Hello
捕获文本为Hello
的元素,而*css=article >> text=Hello
(注意*
)捕获包含文本为Hello
的元素的article
元素。