Playwright无头模式测试失败,有头模式正常求排查方案
问题:Playwright无头模式下注册成功提示超时找不到
我写了一个Playwright测试用例,流程是打开注册页面、填写表单字段、用测试模式过reCAPTCHA,最后提交表单跳转到成功页。这个用例在有头(UI)模式下跑完全正常,但无头模式里一直报超时错误,说找不到成功提示文本。我试过加等待、延长超时时间,都没用。下面是测试代码和无头模式的报错信息,求排查思路。
测试代码
const formInputs = [{ name: 'name', type: 'input', value: "Tester Doe" }, { name: 'email', value: `test.${Date.now().toString(24)}@stakater.com`, type: "input" }, { name: 'phone', type: 'input', value: "111111111" }, { name: 'companyName', type: 'input', value: `Tester${Date.now().toString(24)}` }, { name: 'number', type: "input", value: "4242424242424242", stripe: true }, { name: 'expiry', type: 'input', value: "1250", stripe: true }, { name: "cvc", type: 'input', value: "123", stripe: true }, { name: "country", type: 'select', value: "Sweden", stripe: true }] test('should sign up user', async ({ page, context }) => { await page.goto(`${process.env.APP_BASE_URL}/#/signup`); // Wait for the Payment Element iframe to be available const stripeIframe = await page.waitForSelector("iframe"); const stripeFrame = await stripeIframe.contentFrame(); // fill input fields for (const input of formInputs) { if (input.stripe) { if (input.type === "select") { await stripeFrame.locator(`${input.type}[name="${input.name}"]`).selectOption(input.value) } else { await stripeFrame.locator(`${input.type}[name="${input.name}"]`).fill(input.value) } } else { await page.locator(`${input.type}[name="${input.name}"]`).fill(input.value); } } // Wait for the Recaptcha Element iframe to be available const recaptchaIframe = await page.waitForSelector("[title=reCAPTCHA]"); const recaptchaFrame = await recaptchaIframe.contentFrame(); // solve captcha expect(recaptchaFrame).toBeTruthy(); console.log('reCAPTCHA iframe found'); await recaptchaFrame.locator('#recaptcha-anchor').click(); // await page.waitForLoadState('domcontentloaded'); console.log('Clicked on reCAPTCHA checkbox'); // Wait for the reCAPTCHA to be solved await expect(recaptchaFrame.locator('.recaptcha-checkbox-checked')).toBeVisible({ timeout: 10000 }); console.log('reCAPTCHA solved'); await page.locator('button[type="submit"]').click() // await page.waitForLoadState('networkidle'); // tried this but didn't work await expect(page.getByText(`Signup successful, an email confirmation have been sent to`)).toBeVisible({ timeout: 10000 }) });
无头模式报错信息
Error: Timed out 10000ms waiting for expect(locator).toBeVisible() Locator: getByText('Signup successful, an email confirmation have been sent to') Expected: visible Received: <element(s) not found> Call log: - expect.toBeVisible with timeout 10000ms - waiting for getByText('Signup successful, an email confirmation have been sent to') 198 | await page.locator('button[type="submit"]').click() 199 | // await page.waitForLoadState('networkidle'); > 200 | await expect(page.getByText(`Signup successful, an email confirmation have been sent to`)).toBeVisible({ timeout: 10000 }) | ^
排查思路
确认reCAPTCHA在无头模式下是否真的验证通过:虽然代码里判断了
.recaptcha-checkbox-checked可见,但无头模式下reCAPTCHA可能触发额外验证步骤(比如图片验证),测试模式未必生效。可以在提交前给页面/recaptcha区域截图,或者打印页面上的表单错误提示,确认是否因为验证没通过导致提交失败。处理表单提交后的页面状态变化:
- 你的页面是SPA(URL带
/#/),无头模式下路由跳转可能延迟,试试用page.waitForURL等待成功页的URL,比如:await page.waitForURL('**/#/signup-success')(替换成实际成功页路径) - 监听注册接口的响应,用
page.waitForResponse(response => response.url().includes('/api/signup') && response.ok()),确认接口返回成功,再去查找提示文本 - 检查
framenavigated事件,确认页面是否完成跳转
- 你的页面是SPA(URL带
修正成功文本的定位方式:
getByText默认精确匹配,可能实际文本有细微差异(比如语法错误:应该是has been sent而不是have been sent),改成模糊匹配:page.getByText('Signup successful', { exact: false })- 确认成功文本不在iframe内,要是在iframe里,需要先切换到对应frame再查找
统一无头与有头模式的浏览器配置:
- 手动设置视口大小,避免无头模式默认小视口导致元素被隐藏:
await page.setViewportSize({ width: 1280, height: 720 }) - 设置和有头模式一致的用户代理,防止网站针对无头浏览器做限制:
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36')
- 手动设置视口大小,避免无头模式默认小视口导致元素被隐藏:
验证表单字段填充的正确性:
- 无头模式下Stripe iframe的字段可能没填充成功,比如国家选择,
<select>的value可能是SE(瑞典的代码)而不是显示文本Sweden,加断言确认:await expect(stripeFrame.locator('select[name="country"]')).toHaveValue('SE') - 对其他表单字段也添加断言,确保填充的值正确
- 无头模式下Stripe iframe的字段可能没填充成功,比如国家选择,
调整等待策略:
- 提交后先等网络空闲:
await page.waitForLoadState('networkidle'),再等待文本可见 - 延长超时时间到15-20秒,给页面足够的加载/渲染时间
- 先定位到提示文本所在的容器元素,再检查文本,比如:
await expect(page.locator('.success-container').getByText('Signup successful')).toBeVisible()
- 提交后先等网络空闲:
内容的提问来源于stack exchange,提问作者Sibtain
相关产品推荐
相关产品推荐

