除了像 page.getByRole() 和 page.getByText() 这样的推荐定位器之外,Playwright 还支持本指南中描述的各种其它定位器。
CSS 定位器
Section titled “CSS 定位器”Playwright 可以通过 CSS 选择器定位元素。
await page.locator('css=button').click();Playwright 通过两种方式增强标准 CSS 选择器:
- CSS 选择器会穿透开放的 Shadow DOM。
- Playwright 添加了自定义伪类,例如
:visible、:has-text()、:has()、:is()、:nth-match()等。
CSS:按文本匹配
Section titled “CSS:按文本匹配”Playwright 包含许多 CSS 伪类,用于按文本内容匹配元素。
article:has-text("Playwright")-:has-text()会匹配内部某处包含指定文本的任何元素,该文本可能位于子元素或后代元素中。匹配不区分大小写,会修剪空白,并搜索子字符串。
例如,article:has-text("Playwright") 会匹配 <article><div>Playwright</div></article>。
请注意,:has-text() 应该与其它 CSS 说明符一起使用,否则它会匹配所有包含指定文本的元素,包括 <body>。
// 错误,会匹配包括 <body> 在内的许多元素await page.locator(':has-text("Playwright")').click();// 正确,只匹配 <article> 元素await page.locator('article:has-text("Playwright")').click();#nav-bar :text("Home")-:text()伪类会匹配包含指定文本的最小元素。匹配不区分大小写,会修剪空白,并搜索子字符串。
例如,这将查找 #nav-bar 元素内部某处带有文本 “Home” 的元素:
await 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>。
CSS:只匹配可见元素
Section titled “CSS:只匹配可见元素”Playwright 在 CSS 选择器中支持 :visible 伪类。例如,css=button 会匹配页面上的所有按钮,而 css=button:visible 只匹配可见按钮。这对于区分非常相似但可见性不同的元素很有用。
考虑一个包含两个按钮的页面,第一个不可见,第二个可见。
<button style='display: none'>Invisible</button><button>Visible</button>- 这将找到两个按钮,并抛出严格性违规错误:
await page.locator('button').click();- 这将只找到第二个按钮,因为它是可见的,然后点击它。
await page.locator('button:visible').click();CSS:包含其它元素的元素
Section titled “CSS:包含其它元素的元素”:has() 伪类是一个 CSS 伪类。如果相对于给定元素的 :scope,作为参数传入的任何选择器至少匹配到一个元素,它就会返回该元素。
下面的代码片段返回内部包含 <div class=promo> 的 <article> 元素的文本内容。
await page.locator('article:has(div.promo)').textContent();CSS:匹配多个条件之一的元素
Section titled “CSS:匹配多个条件之一的元素”用逗号分隔的 CSS 选择器列表会匹配可由该列表中任一选择器选中的所有元素。
// 点击带有 “Log in” 或 “Sign in” 文本的 <button>。await page.locator('button:has-text("Log in"), button:has-text("Sign in")').click();:is() 伪类是一个 CSS 伪类,在为元素指定一组额外条件时可能很有用。
CSS:基于布局匹配元素
Section titled “CSS:基于布局匹配元素”布局选择器已弃用,将来可能会被移除。基于布局的匹配可能会产生意外结果。例如,当布局变化一个像素时,可能会匹配到不同的元素。
我们建议优先使用用户可见定位器。
有时,当目标元素缺少明显特征时,很难为它想出一个好的选择器。在这种情况下,使用 Playwright 的布局 CSS 伪类可能会有所帮助。它们可以与常规 CSS 结合使用,从多个选项中精确定位其中一个。
例如,input:right-of(:text("Password")) 会匹配位于文本 “Password” 右侧的输入框——当页面上有多个难以互相区分的输入框时很有用。
请注意,布局伪类通常要和其它内容一起使用,例如 input。如果只单独使用布局伪类,比如 :right-of(:text("Password")),你很可能得到的不是你要找的输入框,而是文本和目标输入框之间的某个空元素。
布局伪类使用 bounding client rect 来计算元素的距离和相对位置。
:right-of(div > button)- 匹配位于任何匹配内部选择器元素右侧的元素,不限制垂直位置。:left-of(div > button)- 匹配位于任何匹配内部选择器元素左侧的元素,不限制垂直位置。:above(div > button)- 匹配位于任何匹配内部选择器元素上方的元素,不限制水平位置。:below(div > button)- 匹配位于任何匹配内部选择器元素下方的元素,不限制水平位置。:near(div > button)- 匹配靠近(50 个 CSS 像素以内)任何匹配内部选择器元素的元素。
请注意,结果匹配项会按它们到锚点元素的距离排序,因此你可以使用 locator.first() 来选择最近的一个。只有在你有一组类似元素,并且最近的那个显然就是正确元素时,这才有用。
但是,在其它情况下使用 locator.first() 很可能不会按预期工作——它不会定位到你正在寻找的元素,而是定位到某个刚好最近的其它元素,例如随机的空 <div>,或者一个已滚出视口、当前不可见的元素。
// 填充 “Username” 右侧的输入框。await page.locator('input:right-of(:text("Username"))').fill('value');
// 点击促销卡片附近的按钮。await page.locator('button:near(.promo-card)').click();
// 点击列表中最接近 “Label 3” 的单选输入框。await page.locator('[type=radio]:left-of(:text("Label 3"))').first().click();所有布局伪类都支持将可选的最大像素距离作为最后一个参数。例如,button:near(:text("Username"), 120) 会匹配距离文本为 “Username” 的元素最多 120 个 CSS 像素的按钮。
CSS:从查询结果中选择第 n 个匹配项
Section titled “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 开始。
// 点击第三个 “Buy” 按钮await page.locator(':nth-match(:text("Buy"), 3)').click();:nth-match() 也适合与 locator.waitFor() 一起使用,等待指定数量的元素出现。
// 等到三个按钮都可见await page.locator(':nth-match(:text("Buy"), 3)').waitFor();第 n 个元素定位器
Section titled “第 n 个元素定位器”你可以使用 nth= 定位器并传入从 0 开始的索引,将查询缩小到第 n 个匹配项。
// 点击第一个按钮await page.locator('button').locator('nth=0').click();
// 点击最后一个按钮await page.locator('button').locator('nth=-1').click();父元素定位器
Section titled “父元素定位器”当你需要定位某个元素的父元素时,大多数情况下应该通过子定位器使用 locator.filter()。例如,考虑以下 DOM 结构:
<li><label>Hello</label></li><li><label>World</label></li>如果你想定位文本为 "Hello" 的 label 的父 <li>,使用 locator.filter() 最合适:
const child = page.getByText('Hello');const parent = page.getByRole('listitem').filter({ has: child });或者,如果你无法为父元素找到合适的定位器,可以使用 xpath=..。请注意,这种方法不那么可靠,因为 DOM 结构的任何变化都会破坏你的测试。尽可能优先使用 locator.filter()。
const parent = page.getByText('Hello').locator('xpath=..');XPath 定位器
Section titled “XPath 定位器”我们建议优先使用像文本或可访问角色这样的用户可见定位器,而不是使用与实现绑定、并容易在页面变化时失效的 XPath。
XPath 定位器等价于调用 Document.evaluate。
await page.locator('xpath=//button').click();XPath 并集
Section titled “XPath 并集”管道操作符(|)可用于在 XPath 中指定多个选择器。它会匹配可由该列表中任一选择器选中的所有元素。
// 等待确认对话框或加载 spinner 中的任意一个。await page.locator( `//span[contains(@class, 'spinner__loading')]|//div[@id='confirmation']`).waitFor();标签到表单控件的重新定位
Section titled “标签到表单控件的重新定位”我们建议按标签文本定位,而不是依赖标签到控件的重新定位。
Playwright 中的目标输入操作会自动区分标签和控件,因此你可以定位标签来对关联控件执行操作。
例如,考虑以下 DOM 结构:<label for="password">Password:</label><input id="password" type="password">。你可以使用 page.getByText() 通过 “Password” 文本定位标签。但是,以下操作会在 input 上执行,而不是在 label 上执行:
locator.click()会点击标签,并自动聚焦输入字段;locator.fill()会填充输入字段;locator.inputValue()会返回输入字段的值;locator.selectText()会选择输入字段中的文本;locator.setInputFiles()会为type=file的输入字段设置文件;locator.selectOption()会从 select 框中选择一个选项。
// 通过定位标签来填充输入框。await page.getByText('Password').fill('secret');但是,其它方法会定位标签本身,例如 expect(locator).toHaveText() 会断言标签的文本内容,而不是输入字段的文本内容。
// 断言标签文本。await expect(page.locator('label')).toHaveText('Password');旧版文本定位器
Section titled “旧版文本定位器”我们建议使用现代的文本定位器。
旧版文本定位器会匹配包含传入文本的元素。
await page.locator('text=Log in').click();旧版文本定位器有几种变体:
text=Log in- 默认匹配不区分大小写,会修剪空白并搜索子字符串。例如,text=Log会匹配<button>Log in</button>。
await 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"。
await page.locator('text="Log in"').click();/Log\s*in/i- 主体可以是包裹在/符号中的类似 JavaScript 的正则表达式。例如,text=/Log\s*in/i会匹配<button>Login</button>和<button>log IN</button>。
await page.locator('text=/Log\\s*in/i').click();id、data-testid、data-test-id、data-test 选择器
Section titled “id、data-testid、data-test-id、data-test 选择器”我们建议按测试 id 定位。
Playwright 支持使用某些属性来选择元素的简写形式。目前仅支持以下属性:
iddata-testiddata-test-iddata-test
// 填充 id 为 “username” 的输入框await page.locator('id=username').fill('value');// 点击 data-test-id 为 “submit” 的元素await page.locator('data-test-id=submit').click();我们建议改用链式定位器。
定义为 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 元素。