PAYATHON 2026

如何使用 Playwright 定位 Vaadin 登录表单元素?

数据小陈

结论

优先使用 Playwright 的标签或角色定位器,例如 getByLabel() 和 getByRole()。你给出的 XPath 也有语法问题:id 是属性,在 XPath 中应写作 @id。修正为 //*[@id='vaadinLoginUsername'] 后,仍无法穿过 Shadow DOM。Vaadin 组件的内部元素如果位于开放的 Shadow DOM 中,可以使用 Playwright 支持穿透 Shadow DOM 的 CSS 定位器或可访问性定位器。

推荐写法:按标签和角色定位

如果页面上的输入框和按钮有可访问名称,可以这样写:

import { test as setup, expect } from '@playwright/test';
import path from 'path';

const authFile = path.join(__dirname, '../playwright/auth.setup.ts');

setup('authenticate', async ({ page }) => {
  await page.goto('https://aservice.somewhere/mypage-ui/login');

  await page.getByLabel('Username').fill('_famname');
  await page.getByLabel('Password').fill('_passw');
  await page.getByRole('button', { name: 'Log in' }).click();

  await page.waitForURL('https://aservice.somewhere/mypage-ui/home');
  await page.context().storageState({ path: authFile });
});

Username、Password 和 Log in 必须与页面上的标签和可访问名称一致。如果页面使用其他语言或自定义文案,请替换为浏览器中实际显示的名称。

如果标签定位不到

先用浏览器开发者工具检查 Vaadin 组件的实际 DOM,确认输入框的标签、name 属性和组件结构。如果输入框位于开放的 Shadow DOM 中,可以尝试 CSS 定位器:

const loginForm = page.locator('vaadin-login-form');

await loginForm.locator('input[name="username"]').fill('_famname');
await loginForm.locator('input[name="password"]').fill('_passw');
await loginForm.getByRole('button', { name: 'Log in' }).click();

Playwright 的 CSS 定位器可以穿透开放的 Shadow DOM。组件名、属性和按钮名称应以页面的实际 DOM 为准。如果 Vaadin 组件结构不同,或应用采用了自定义实现,请相应调整选择器。

为什么原来的定位器会等待

原代码中的选择器:

xpath=//*[id='vaadinLoginUsername']

把属性写成了 id,正确写法是 @id。此外,XPath 定位器无法穿透 Shadow DOM,而 Vaadin 常把组件内部控件放在 Shadow DOM 中。因此,定位器找不到目标时,fill() 会根据 Playwright 的自动等待机制持续等待,直到超时,看起来就像调试器卡住了。

可以在操作前检查定位结果,区分选择器错误和页面加载问题:

const username = page.getByLabel('Username');
console.log(await username.count());

如果结果为 0,请核对页面上的标签,或检查组件内部 DOM。如果页面使用封闭的 Shadow DOM,或者控件没有可访问标签,就需要根据组件提供的接口或应用代码调整测试方式。

备注:内容仅供参考。