Playwright test fails in headless mode, works in headed mode

I have a playwright test that opens up a signup page, fills up some fields, solves recaptcha (test mode) and then submits the form which redirects it to a success page.

The same test run fine in headed / UI mode but in case of headless, it gives error which doesn’t make sense. I have read that headless tests run faster than headed but I have tried adding wait and also increasing the timeout as well but that didn’t make any difference. Here is my test:


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 })

 });

The error that comes in headless mode is below:

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 })
      |                                                                                                ^

Does anyone have any pointers as to what might be the issue?