跳至主要内容

其他定位符

引言

注意

请查看主要的定位符指南,了解最常用和推荐的定位符。

除了推荐的定位符,如 Page.getByRole()Page.getByText(),Playwright 还支持本指南中描述的各种其他定位符。

CSS 定位符

注意

我们建议优先使用用户可见的定位符,如文本或可访问角色,而不是使用依赖于实现细节且页面变更时可能失效的 CSS 定位符。

Playwright 可以通过 CSS 选择器定位元素。

page.locator("css=button").click();

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

注意

文本匹配总是会标准化空白字符。例如,它会将多个空格变成一个,将换行符变成空格,并忽略开头和结尾的空白字符。

注意

类型为 buttonsubmit 的输入元素按其 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")),很可能你不会找到你想要的输入框,而是文本和目标输入框之间的某个空元素。

布局伪类使用边界客户端矩形(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>,或者一个已滚出屏幕且当前不可见的元素。

// 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”的 label 的父元素 <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 仅适用于未压缩(unminified)的应用构建。

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 仅适用于未压缩(unminified)的应用构建。

XPath 定位符

警告

我们建议优先使用用户可见的定位符,如文本或可访问角色,而不是使用依赖于实现细节且页面变更时容易失效的 XPath 定位符。

XPath 定位符等同于调用 Document.evaluate

page.locator("xpath=//button").click();
注意

任何以 //.. 开头的选择器字符串都被认为是 XPath 选择器。例如,Playwright 将 '//html/body' 转换为 'xpath=//html/body'

注意

XPath 不穿透 Shadow Roots。

XPath 联合

在 XPath 中,可以使用管道运算符 (|) 来指定多个选择器。它将匹配列表中任一选择器可选择的所有元素。

// Waits for either confirmation dialog or load spinner.
page.locator("//span[contains(@class, 'spinner__loading')]|//div[@id='confirmation']").waitFor();

Label 到表单控件的重定向

警告

我们建议通过标签文本定位,而不是依赖于标签到控件的重定向。

Playwright 中的目标输入操作会自动区分标签和控件,因此您可以定位标签以对其关联的控件执行操作。

例如,考虑以下 DOM 结构:<label for="password">Password:</label><input id="password" type="password">。您可以使用 Page.getByText() 通过其“Password”文本定位该标签。但是,以下操作将在输入框而不是标签上执行

// 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"

注意

匹配总是标准化空白字符。例如,它将多个空格变成一个,将换行符变成空格,并忽略开头和结尾的空白字符。

注意

类型为 buttonsubmit 的输入元素按其 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 元素。