ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

关于Playwright定位问题:完整的定位心法

2026/8/10 10:30:08 拓冰建站 浏览量
关于Playwright定位问题:完整的定位心法 一、属性选择器揭秘中括号[ ]叫属性选择器是 CSS 标准里极其强大的定位方式。写法标签名[属性名属性值]比如button[title删除] —— 找带有 title 属性且值完全等于删除的按钮。 input[placeholder*名称] —— * 表示包含只要 placeholder 里有名称二字就匹配。 div[class^ant-] —— ^ 表示以...开头匹配 class 以 ant- 开头的 div。 span[data-testid$-submit] —— $ 表示以...结尾。中括号里还能写多个条件且的关系button[title删除][disabled] /* title为删除且带disabled属性 */ input[typetext][nameusername] /* 多个属性同时匹配 */这个技巧专治那些没有role、没有文字、只有图标和图标的title的元素。二、has:和filter——从一堆元素里筛出你的目标Playwright 提供了两种“过滤”方式作用都是在已匹配的集合里进一步缩小范围。1.filter()—— 按条件过滤属性、文本、子元素都可以// 1) 过滤出文本包含“保存”的按钮 page.locator(button).filter({ hasText: 保存 }) // 2) 过滤出内部包含某个子元素的父元素has: 参数是另一个locator page.locator(tr).filter({ has: page.locator(td, { hasText: 张三 }) }) // 3) 过滤出当前可见的、不带 disabled 属性的 page.locator(button).filter({ hasNot: page.locator([disabled]) })关键理解filter()是在你已经选中的一批元素上按“包含什么文本 / 包含什么子元素 / 不包含什么子元素”再筛一遍。2.has和hasText—— 直接在定位器内部过滤推荐Playwright 的locator()支持直接传入has和hasText选项和filter效果类似但写法更紧凑// 直接找“内部包含td为张三”的tr page.locator(tr, { has: page.getByText(张三) }) // 找文本包含“删除”的按钮 page.locator(button, { hasText: 删除 }) // 找 class 为 card 的 div且内部有一个 span 的文本是“详情” page.locator(div.card, { has: page.locator(span, { hasText: 详情 }) })区别记忆法locator(A, { has: locatorB }) 选那些内部含有 B 的 A。 locator(A).filter({ has: locatorB }) 和上面一样但可以链式再继续过滤。 hasText: xxx 是文本内容包含 xxx不用再套一层 locator常用于按钮、链接。三、父子兄弟关系的写法——拿到一个元素后怎样找到它身边的人Playwright 提供了locator的相对定位方法。假设你已拿到某个元素const cell page.getByRole(cell, { name: 张三 });找爸爸父元素const row cell.locator(..); // CSS 的上级选择器 const row2 cell.locator(xpath..); // 同样效果用 XPath找爷爷祖先const grandpa cell.locator(../..); // 上两级找兄弟前面一个 / 后面一个const nextSibling cell.locator( *); // CSS 相邻兄弟选择器紧挨着的下一个 const prevSibling cell.locator(xpathpreceding-sibling::*[1]); // XPath 前一个兄弟找儿子后代元素cell.locator(button); // cell 下的所有 button子孙 cell.locator( button); // cell 的**直接儿子** button最常用的套路先找到唯一行再在行里找按钮。const row page.locator(tr, { has: page.getByText(张三) }); await row.locator(button[title编辑]).click();四、Playwright 定位武器库——除了 CSS还有更多方法方式例子适合场景CSS 选择器page.locator(.ant-btn-primary)通用快XPathpage.locator(//button[text()保存])文本精确匹配、路径复杂时文本page.getByText(提交)直接按可见文字找角色page.getByRole(button, { name: 保存 })最优选择符合无障碍标签文本page.getByLabel(用户名)表单输入框占位文本page.getByPlaceholder(请输入)input 占位符替代文本page.getByAltText(logo)图片 alt标题page.getByTitle(删除)等同于[title删除]测试 IDpage.getByTestId(submit-btn)自定义的稳定标记推荐优先级Role Label Text TestId Attribute CSS XPath五、定位器的最佳实践规范写出三年后还能绿的代码1. 不要依赖自动生成的 classAnt Design 的css-1a2b3c4d、.ant-btn-primary这类随时会变一升级全趴。2. 不要用nth(1)做唯一性保障页面多一个隐藏元素就会错位。如果你不得不用索引请确保父容器已经唯一比如row.locator(td).nth(2)是安全的因为行已经唯一定位了。3. 用data-testid给关键元素加“身份证”在组件里给敏感按钮加上data-testidcreate-module测试里直接用page.getByTestId(create-module)这是前端和测试最稳定的契约。4. 凡是要等待的都用waitFor()或断言可见性await expect(submitBtn).toBeVisible({ timeout: 10000 }); await submitBtn.click();而不是直接click再 catch。5. 封装“查找行”的方法在页面对象里写一个findRow(username)方法其他地方复用避免每个方法都重复拼定位器。六、实战串联从一团乱麻到稳如磐石需求在表格中点击“张三”那行的“重置密码”并处理可能出现的确认弹窗。最佳写法全流程// 1. 先确保表格加载完 await this.waitForTableLoaded(); // 2. 唯一定位行通过两个 cell 的精确文本 const targetRow this.page.locator(tr.ant-table-row, { has: this.page.getByRole(cell, { name: 张三, exact: true }) }).filter({ has: this.page.getByRole(cell, { name: zhangsan, exact: true }) }); // 3. 在行内找“重置密码”按钮假设按钮用 title 区分 const resetBtn targetRow.locator(button[title重置密码]); await expect(resetBtn).toBeVisible(); // 4. 点击前把鼠标移走防止 tooltip await this.page.mouse.move(0, 0); await resetBtn.dispatchEvent(click); // 5. 等待确认弹窗 const confirmDialog this.page.getByRole(dialog); await expect(confirmDialog).toBeVisible(); await confirmDialog.getByRole(button, { name: 确定 }).click();为什么稳行用两个 cell 互锁绝无可能找到第二行。按钮用title属性跟布局、顺序无关。移动鼠标 dispatchEvent从根源消灭 tooltip 遮挡。确认弹窗用roledialog不依赖任何 class。七、检验唯一性的工具箱在写定位器的过程中你可以随时在测试里验证// 方法1打印匹配数量 console.log(await page.locator(button[title删除]).count()); // 方法2断言唯一 await expect(page.locator(button[title删除])).toHaveCount(1); // 方法3用 page.pause() 打开 Inspector在浏览器里实时调试 await page.pause(); // 方法4用 .first() 暂时锁定第一个临时方案不推荐长期用 const btn page.locator(button[title删除]).first();八、高阶组合技一个locator走天下// 1. 找“包含特定子元素的元素”的直接儿子 page.locator(ul.menu li, { has: page.getByText(设置) }).locator( a); // 2. 多个 filter 链式串联 page.locator(div.card) .filter({ hasText: 环境配置 }) .filter({ has: page.locator(button[data-statusactive]) }) .locator(button, { hasText: 详情 }); // 3. 结合正则文本可以使用正则 page.getByText(/创建.*模块/); page.locator(button, { hasText: /保存|确认/ });掌握了这些你就不再是“找元素”而是用规则计算元素。DOM 再怎么变只要业务含义不变你的定位就不会变。把这些方法印在脑子里它们就是你以后和 Flaky Tests 对线时最硬的底气。