You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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事件,确认页面是否完成跳转
  • 修正成功文本的定位方式:

    • 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')
    • 对其他表单字段也添加断言,确保填充的值正确
  • 调整等待策略:

    • 提交后先等网络空闲:await page.waitForLoadState('networkidle'),再等待文本可见
    • 延长超时时间到15-20秒,给页面足够的加载/渲染时间
    • 先定位到提示文本所在的容器元素,再检查文本,比如:await expect(page.locator('.success-container').getByText('Signup successful')).toBeVisible()

内容的提问来源于stack exchange,提问作者Sibtain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 21:57:01