其他定位器
简介
请查看主要的定位器指南以获取最常用和推荐的定位器。
除了推荐的定位器(如 Page.GetByRole() 和 Page.GetByText())外,Playwright 还支持本指南中介绍的各种其他定位器。
CSS 定位符
我们建议优先使用用户可见的定位器(如文本或无障碍角色),而不是使用与实现细节耦合、且可能随页面更改而失效的 CSS 选择器。
Playwright 可以通过 CSS 选择器定位元素。
await page.Locator("css=button").ClickAsync();
Playwright 通过两种方式增强了标准 CSS 选择器
- CSS 选择器穿透开放的 Shadow 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>
await page.Locator(":has-text(\"Playwright\")").ClickAsync();
// Correct, only matches the <article> element
await page.Locator("article:has-text(\"Playwright\")").ClickAsync(); -
#nav-bar :text("Home")-:text()伪类匹配包含指定文本的最小元素。匹配不区分大小写,修剪空白并搜索子字符串。例如,这将在
#nav-bar元素中查找包含文本“Home”的元素await page.Locator("#nav-bar :text('Home')").ClickAsync(); -
#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>
-
这将找到两个按钮并抛出严格性 (strictness) 违规错误
await page.Locator("button").ClickAsync(); -
这将只找到第二个按钮,因为它可见,然后点击它。
await page.Locator("button:visible").ClickAsync();
CSS:包含其他元素的元素
:has() 伪类是一个 CSS 伪类。如果作为参数传递的选择器相对于给定元素的 :scope 至少匹配一个元素,它就会返回该元素。
以下代码片段返回包含 <div class=promo> 的 <article> 元素的文本内容。
await page.Locator("article:has(div.promo)").TextContentAsync();
CSS:匹配满足其中一个条件的元素
逗号分隔的 CSS 选择器列表将匹配所有可以由该列表中某个选择器选择的元素。
// Clicks a <button> that has either a "Log in" or "Sign in" text.
await page.Locator("button:has-text(\"Log in\"), button:has-text(\"Sign in\")").ClickAsync();
: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".
await page.Locator("input:right-of(:text(\"Username\"))").FillAsync("value");
// Click a button near the promo card.
await page.Locator("button:near(.promo-card)").ClickAsync();
// Click the radio input in the list closest to the "Label 3".
await page.Locator("[type=radio]:left-of(:text(\"Label 3\"))").First.ClickAsync();
所有布局伪类都支持可选的最大像素距离作为最后一个参数。例如 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
await page.Locator(":nth-match(:text('Buy'), 3)").ClickAsync();
:nth-match() 也可用于在使用 Locator.WaitForAsync() 时等待直到出现指定数量的元素。
// Wait until all three buttons are visible
await page.Locator(":nth-match(:text('Buy'), 3)").WaitForAsync();
与 :nth-child() 不同,元素不必是兄弟节点,它们可以位于页面上的任何位置。在上面的代码片段中,所有三个按钮都匹配 :text("Buy") 选择器,而 :nth-match() 选择第三个按钮。
第 N 个元素定位符
您可以使用 nth= 定位符将查询缩小到第 n 个匹配项,传递一个从零开始的索引。
// Click first button
await page.Locator("button").Locator("nth=0").ClickAsync();
// Click last button
await page.Locator("button").Locator("nth=-1").ClickAsync();
父元素定位符
当你需要定位某个元素的父元素时,大多数情况下你应该使用 Locator.Filter() 并通过子元素定位器进行过滤。例如,考虑以下 DOM 结构
<li><label>Hello</label></li>
<li><label>World</label></li>
如果你想定位文本为 "Hello" 的标签的父级 <li>,使用 Locator.Filter() 是最好的方法
var child = page.GetByText("Hello");
var parent = page.GetByRole(AriaRole.Listitem).Filter(new () { Has = child });
或者,如果你无法为父元素找到合适的定位器,请使用 xpath=..。请注意,这种方法不太可靠,因为 DOM 结构的任何更改都会破坏你的测试。尽可能优先使用 Locator.Filter()。
var parent = page.GetByText("Hello").Locator("xpath=..");
XPath 定位符
我们建议优先使用用户可见的定位器(如文本或无障碍角色),而不是使用与实现细节耦合、且容易随页面更改而失效的 XPath。
XPath 定位符等同于调用 Document.evaluate。
await page.Locator("xpath=//button").ClickAsync();
任何以 // 或 .. 开头的选择器字符串都被认为是 xpath 选择器。例如,Playwright 将 '//html/body' 转换为 'xpath=//html/body'。
XPath 不会穿透 Shadow DOM。
XPath 联合
管道运算符(|)可用于在 XPath 中指定多个选择器。它将匹配所有可以通过该列表中某个选择器选择的元素。
// Waits for either confirmation dialog or load spinner.
await page.Locator("//span[contains(@class, 'spinner__loading')]|//div[@id='confirmation']").WaitForAsync();
标签到表单控件的重定向
我们建议按标签文本定位,而不是依赖标签到控件的重定向。
Playwright 中的目标输入操作会自动区分标签和控件,因此您可以将标签作为目标,对关联的控件执行操作。
例如,考虑以下 DOM 结构:<label for="password">Password:</label><input id="password" type="password">。你可以使用 Page.GetByText() 按其“Password”文本定位该标签。然而,以下操作将在输入框而不是标签上执行
- Locator.ClickAsync() 将点击标签并自动聚焦到输入框;
- Locator.FillAsync() 将填充输入框;
- Locator.InputValueAsync() 将返回输入框的值;
- Locator.SelectTextAsync() 将选中文本输入框中的文本;
- Locator.SetInputFilesAsync() 将为
type=file的输入框设置文件; - Locator.SelectOptionAsync() 将从下拉选择框中选择一个选项。
// Fill the input by targeting the label.
await page.GetByText("Password").FillAsync("secret");
然而,其他方法将指向标签本身,例如 Expect(Locator).ToHaveTextAsync() 将断言标签的文本内容,而不是输入框的。
// Fill the input by targeting the label.
await Expect(Page.Locator("label")).ToHaveTextAsync("Password");
传统文本定位符
我们建议改用现代的文本定位器。
传统文本定位符匹配包含传递文本的元素。
await page.Locator("text=Log in").ClickAsync();
传统文本定位符有几种变体
-
text=Log in- 默认匹配不区分大小写,修剪空白并搜索子字符串。例如,text=Log匹配<button>Log in</button>。await page.Locator("text=Log in").ClickAsync(); -
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"。await page.Locator("text='Log in'").ClickAsync(); -
/Log\s*in/i- body 可以是包装在/符号中的 类似 JavaScript 的正则表达式。例如,text=/Log\s*in/i匹配<button>Login</button>和<button>log IN</button>。await page.Locator("text=/Log\\s*in/i").ClickAsync();
以引号(" 或 ')开头和结尾的字符串选择器被认为是传统文本定位符。例如,"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 支持使用特定属性选择元素的简写形式。目前,仅支持以下属性
iddata-testiddata-test-iddata-test
// Fill an input with the id "username"
await page.Locator("id=username").FillAsync("value");
// Click an element with data-test-id "submit"
await page.Locator("data-test-id=submit").ClickAsync();
属性选择器不是 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 元素。