定位断言
Day 07 - Advanced Locators: Filtering and Chaining
今日目标
- 掌握 locator 组合能力。
- 能处理列表、表格、重复元素。
学习时间安排(60–120 分钟)
| 时间 | 模块 | 做什么 |
|---|---|---|
| 0-20 分钟 | 核心概念与词汇 | 读概念表,重点理解 filter、nth、链式定位的语义。 |
| 20-45 分钟 | 官方文档阅读 | 读 Locators 的 filtering 和 lists 部分。 |
| 45-75 分钟 | 实操练习 | 在列表页完成 5 个定位任务。 |
| 75-105 分钟 | 示例代码改写 | 把 nth/长链式写法改写成容器 + filter 的写法。 |
| 105-120 分钟 | 复盘与作业 | 完成链式定位示例表,完成今日问题。 |
核心概念与词汇
| English | 中文 | 场景用法 |
|---|---|---|
| chaining | 链式定位 | Use it when you narrow a search from a container to its children. |
filter() | 过滤 | Use it when you keep only elements matching text, another locator, or a predicate. |
hasText | 按包含文本过滤 | Use it when you filter a list by the text it contains. |
has | 按子元素过滤 | Use it when you keep rows that contain a specific status or button. |
nth() | 按序号取元素 | Use it when order itself is the business rule, otherwise avoid it. |
first() / last() | 第一个/最后一个 | Use it for the first or last item of a collection. |
count() | 元素数量 | Use it when you assert how many items a list renders. |
| strict mode violation | 严格模式冲突 | Use it when multiple elements match and Playwright refuses to act. |
| list / row | 列表 / 行 | Use it when you locate a repeated UI unit like a product card or table row. |
| container | 容器 | Use it when you first locate the region that groups related elements. |
学习材料
- 必读:[Locators - Lists](https://playwright.dev/docs/locators#lists)
- 必读:[Locators - Filtering](https://playwright.dev/docs/locators#filtering-locators)
- 选读:[Other locators](https://playwright.dev/docs/other-locators)
重点理解
常用写法:
page.getByRole('listitem').filter({ hasText: 'Product 1' })
page.getByRole('row').filter({ has: page.getByText('Pending') })
page.locator('.item').nth(0)
page.locator('.item').first()
page.locator('.item').last()
注意:
- 优先让 locator 唯一。
- 不要随意使用
.nth(),除非顺序本身就是业务规则。 - 使用
filter({ hasText })缩小范围。 - 对表格、卡片、列表先定位容器,再定位内部元素。
实操步骤
找一个列表页面,例如商品列表、文章列表、用户表格:
- 定位包含指定文本的行。
- 在该行内点击按钮。
- 断言行状态变化。
- 统计列表数量。
- 验证某个 item 可见。
示例代码
import { test, expect } from '@playwright/test';
test('approve a pending order', async ({ page }) => {
await page.goto('/orders');
// 先定位容器行,再在行内操作,避免 nth
const row = page.getByRole('row').filter({ hasText: 'Order #42' });
await row.getByRole('button', { name: 'Approve' }).click();
// 断言行状态变化
await expect(row).toContainText('Approved');
await expect(page.getByRole('row').filter({ hasText: 'Pending' })).toHaveCount(2);
});
常见坑
- 列表元素文本相同就用
nth(0),需求一变顺序就 flaky。 filter({ hasText })写不完整文本导致匹配到多条。- 在
getByRole('row')上直接用toHaveCount把表头也数进去。 - 链式定位写成长 CSS 的等价物(
.panel > .list > li:nth-child(2)),失去语义。 - 用
count()手动循环遍历元素,而不是用 filter + 断言。
今日产出
- 一个列表/表格测试。
- 一份 “locator 链式定位示例表”。
今日问题
filter({ hasText })解决什么问题?.nth()的风险是什么?- 如何定位某一行里的按钮?
- locator 链式定位和长 CSS 选择器有什么区别?
- 多个元素匹配时 Playwright 为什么可能报错?
复盘要点
- “先容器、后内部”是列表类页面的万能套路:找到唯一容器 → filter 缩小 → 在容器内操作。
- nth 是最后的武器而不是首选:能用 filter 解决的顺序问题就不要用 nth。
- 严格模式报错不是 bug,是 Playwright 在提醒你 locator 不够唯一。
AI 时代扩展:AI 辅助 Playwright 测试
新增概念
| English | 中文 |
|---|---|
| element disambiguation | 元素消歧 |
| collection filtering | 集合过滤 |
| locator chain review | 定位链审查 |
适用场景
让 AI 把你写的 nth() 代码重构成 filter 写法,或让 AI 判断一条 locator 链在什么情况下会失效。列表类页面的消歧问题很适合让 AI 给多种方案。
可复用表达 / 提示词
这个页面有 10 个订单行,我想定位「包含 Order #42 的行内的 Approve 按钮」,请给出 2 种写法并比较稳定性。
下面这条 locator 链有哪些失效场景?page.locator('.list > li').nth(2).getByRole('button')
追问加练
- AI 给的 filter 方案你如何确认它只匹配一行?
- 顺序是业务规则时(如最新订单),nth 是否就安全了?
- 让 AI 重构定位代码,你最需要它保留什么?
今日作业
- 完成“链式定位示例表”:至少 5 组场景(列表行内按钮、卡片内文本、表格状态过滤等)。
- 找一个真实列表页写 3 条测试,其中至少 1 条使用容器 + filter。
- 让 AI 审查你的测试,找出可以用 filter 替代 nth 的地方。