Playwright "expect(received).toHaveText(expected)" race in CI
A toHaveText assertion saw the wrong (often empty or placeholder) text because the content had not finished rendering when it was read. Web-first assertions auto-retry up to the expect timeout, so when this fails it usually means the timeout was too short for the slower CI machine or the text never becomes what you assert.
What this error means
The diff shows the expected string against an empty or stale "Received string", and the test is flaky: green locally, red in CI, green on retry.
Error: expect(received).toHaveText(expected)
Expected string: "Welcome, Ada"
Received string: ""
Call log:
- expect.toHaveText with timeout 5000ms
- waiting for getByTestId('greeting')Common causes
The text had not rendered when asserted
Async data populates the element after first paint; on a slower CI runner the 5s expect timeout can elapse before the text appears.
A non-retrying assertion on a snapshot value
Reading textContent() into a variable and asserting that with expect(value) does not auto-retry, so it captures the pre-render state.
How to fix it
Use the auto-retrying web-first assertion
Assert on the locator directly so Playwright polls until the text matches or the timeout elapses.
await expect(page.getByTestId('greeting'))
.toHaveText('Welcome, Ada');Raise the expect timeout for CI
Give web-first assertions more time so genuinely slow renders are not misreported as failures.
expect: { timeout: 10_000 },How to prevent it
- Assert on locators, never on captured
textContent()snapshots. - Size the
expecttimeout for the slowest CI runner. - Wait for the network/data state the text depends on before asserting.