Skip to main content

定位器 (Locators)

Locator 是 Playwright 自动等待和重试能力的核心部分。简而言之,定位器代表了一种随时在页面上查找元素的方法。

Quick Guide

这些是推荐的内置定位器。

page.get_by_label("User Name").fill("John")

page.get_by_label("Password").fill("secret-password")

page.get_by_role("button", name="Sign in").click()

expect(page.get_by_text("Welcome, John!")).to_be_visible()

每次将定位器用于某些操作时,都会在页面中定位最新的 DOM 元素。因此,在下面的代码片段中,底层 DOM 元素将被定位两次,在每次操作之前。这意味着如果 DOM 在调用之间由于重新渲染而发生变化,则将使用与定位器对应的新元素。

locator = page.get_by_text("Submit")
locator.hover()
locator.click()

Strictness

定位器是严格的。这意味着如果多个元素匹配给定的选择器,则所有暗示某些目标 DOM 元素的定位器操作都将抛出异常。例如,如果 DOM 中有多个按钮,则以下调用将抛出异常:

page.get_by_role("button").click()

另一方面,Playwright 了解您何时执行多元素操作,因此当定位器解析为多个元素时,以下调用可以完美运行。

page.get_by_role("button").count()

您可以通过 locator.firstlocator.lastlocator.nth(index) 告诉 Playwright 当多个元素匹配时使用哪个元素,从而显式选择退出严格性检查。这些方法 不推荐,因为当您的页面更改时,Playwright 可能会点击您不打算点击的元素。相反,请遵循以下最佳实践来创建一个唯一标识目标元素的定位器。

Locating elements

Playwright 提供了多种内置方法来创建定位器。为了使测试具有弹性,我们建议优先考虑面向用户的属性和显式契约,并为它们提供专用方法,例如 page.get_by_text(text, **kwargs)。使用 代码生成器 生成定位器,然后根据需要进行编辑通常很方便。

page.get_by_text("Log in").click()

如果您绝对必须使用 CSS 或 XPath 定位器,可以使用 page.locator(selector, **kwargs) 创建一个定位器,该定位器接受描述如何在页面中查找元素的 选择器

请注意,所有创建定位器的方法,例如 page.get_by_label(text, **kwargs),也可在 LocatorFrameLocator 类上使用,因此您可以链接它们并迭代地缩小定位器范围。

locator = page.frame_locator("my-frame").get_by_text("Submit")
locator.click()

Locate based on accessible attributes

page.get_by_role(role, **kwargs) 定位器反映了用户和辅助技术如何感知页面,例如某个元素是按钮还是复选框。按角色定位时,通常还应传递可访问名称,以便定位器精确定位确切的元素。

page.get_by_role("button", name=re.compile("submit", re.IGNORECASE)).click()

page.get_by_role("checkbox", checked=True, name="Check me").check()

角色定位器遵循 ARIA 角色ARIA 属性可访问名称 的 W3C 规范。

请注意,角色定位器 不能替代 可访问性审核和一致性测试,而是提供有关 ARIA 指南的早期反馈。

Locate by label text

大多数表单控件通常具有专用标签,可以方便地用于与表单交互。在这种情况下,您可以使用 page.get_by_label(text, **kwargs) 通过其关联标签定位控件。

例如,考虑以下 DOM 结构。

<label for="password">Password:</label><input type="password" id="password">

您可以在通过标签文本定位输入后填写它:

page.get_by_label("Password").fill("secret")

Locate by placeholder text

输入可能具有占位符属性,以向用户提示应输入什么值。您可以使用 page.get_by_placeholder(text, **kwargs) 定位此类输入。

例如,考虑以下 DOM 结构。

 <input id="email" name="email" type="email" placeholder="name@example.com">

您可以在通过占位符文本定位输入后填写它:

page.get_by_placeholder("name@example.com").fill("playwright@microsoft.com")

Locate by text

查找元素的最简单方法是查找它包含的文本。使用 page.get_by_text(text, **kwargs) 时,您可以按子字符串、精确字符串或正则表达式进行匹配。

page.get_by_text("Log in").click()
page.get_by_text("Log in", exact=True).click()
page.get_by_text(re.compile("Log in", re.IGNORECASE)).click()

您还可以在以其他方式定位时 按文本过滤,例如在列表中查找特定项目。

page.get_by_test_id("product-item").filter(has_text="Playwright Book").click()
note

按文本匹配总是规范化空白,即使是精确匹配也是如此。例如,它将多个空格转换为一个,将换行符转换为空格,并忽略前导和尾随空白。

Locate by alt text

所有图像都应具有描述图像的 alt 属性。您可以使用 page.get_by_alt_text(text, **kwargs) 根据替代文本定位图像。

例如,考虑以下 DOM 结构。

<img alt="playwright logo" src="/playwright-logo.png" />

您可以在通过替代文本定位图像后点击它:

page.get_by_alt_text("playwright logo").click()

Locate by title

使用 page.get_by_title(text, **kwargs) 定位具有匹配 title 属性的元素。

例如,考虑以下 DOM 结构。

<span title='Issues count'>25 issues</span>

您可以在通过标题文本定位后检查问题计数:

expect(page.get_by_title("Issues count")).to_have_text("25 issues")

Define explicit contract and use a data-testid attribute

面向用户的属性(如文本或可访问名称)可能会随时间而变化。在这种情况下,定义显式测试 ID 并使用 page.get_by_test_id(test_id) 查询它们很方便。

<button data-testid="directions">Itinéraire</button>
page.get_by_test_id("directions").click()

默认情况下,page.get_by_test_id(test_id) 将根据 data-testid 属性定位元素,但您可以在测试配置中配置它或调用 selectors.set_test_id_attribute(attribute_name)

Locate in a subtree

您可以链接创建定位器的方法,例如 page.get_by_text(text, **kwargs)locator.get_by_role(role, **kwargs),以将搜索范围缩小到页面的特定部分。

例如,考虑以下 DOM 结构:

<div data-testid='product-card'>
<span>Product 1</span>
<button>Buy</button>
</div>
<div data-testid='product-card'>
<span>Product 2</span>
<button>Buy</button>
</div>

例如,我们可以首先找到包含文本 "Product 2" 的产品卡,然后单击此特定产品卡中的按钮。

product = page.get_by_test_id("product-card").filter(has_text="Product 2")

product.get_by_text("Buy").click()

Locate by CSS or XPath selector

Playwright 支持 CSS 和 XPath 选择器,如果您省略 css=xpath= 前缀,则会自动检测它们。为此使用 page.locator(selector, **kwargs)

page.locator("css=button").click()
page.locator("xpath=//button").click()

page.locator("button").click()
page.locator("//button").click()

XPath 和 CSS 选择器可以绑定到 DOM 结构或实现。当 DOM 结构更改时,这些选择器可能会中断。下面的长 CSS 或 XPath 链是导致测试不稳定的 不良实践 的示例:

page.locator("#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input").click()

page.locator("//*[@id="tsf"]/div[2]/div[1]/div[1]/div/div[2]/input").click()

相反,尝试想出一个接近用户感知页面的定位器,或者 定义显式测试契约

Locate elements that contain other elements

Filter by text

定位器可以选择按文本过滤。它将在元素内部的某个位置(可能在后代元素中)不区分大小写地搜索特定字符串。您也可以传递正则表达式。

page.get_by_test_id("product-card").filter(has_text="Product 3").click()
page.get_by_test_id("product-card").filter(has_text=re.compile("Product 3")).click()

Filter by another locator

定位器支持一个选项,仅选择具有匹配另一个定位器的后代的元素。

page.get_by_role("section").filter(has=page.get_by_test_id("subscribe-button"))

请注意,内部定位器是从外部定位器开始匹配的,而不是从文档根目录开始。

Augment an existing locator

您可以使用 locator.filter(**kwargs) 方法按文本或另一个定位器过滤现有定位器,可能多次链接它。

row_locator = page.locator("tr")
# ...
row_locator
.filter(has_text="text in column 1")
.filter(has=page.get_by_role("button", name="column 2 button"))
.screenshot()

Locate elements in Shadow DOM

Playwright 中的所有定位器 默认情况下 都适用于 Shadow DOM 中的元素。例外情况包括:

考虑以下带有自定义 Web 组件的示例:

<x-details role=button aria-expanded=true aria-controls=inner-details>
<div>Title</div>
#shadow-root
<div id=inner-details>Details</div>
</x-details>

您可以像根本不存在 shadow root 一样进行定位。

  • Click <div>Details</div>

    page.get_by_text("Details").click()
  • Click <x-details>

    page.locator("x-details", has_text="Details" ).click()
  • Ensure that <x-details> contains text "Details"

    expect(page.locator("x-details")).to_contain_text("Details")

Lists

您还可以使用定位器来处理元素列表。

# Locate elements, this locator points to a list.
rows = page.get_by_role("listitem")

# Pattern 1: use locator methods to calculate text on the whole list.
texts = rows.all_text_contents()

# Pattern 2: do something with each element in the list.
count = rows.count()
for i in range(count):
print(rows.nth(i).text_content())

# Pattern 3: resolve locator to elements on page and map them to their text content.
# Note: the code inside evaluateAll runs in page, you can call any DOM apis there.
texts = rows.evaluate_all("list => list.map(element => element.textContent)")

Picking specific element from a list

如果您有一个相同元素的列表,并且区分它们的唯一方法是顺序,则可以使用 locator.firstlocator.lastlocator.nth(index) 从列表中选择特定元素。

例如,要点击产品列表中的第三项:

page.get_by_test_id("product-card").nth(3).click()

但是,请谨慎使用这些方法。很多时候,页面可能会发生变化,定位器将指向与您预期的完全不同的元素。相反,尝试想出一个可以通过 严格性标准 的唯一定位器。