Playwright "locator.waitFor: Timeout exceeded" waiting for a selector in CI
Playwright auto-waited for the locator to satisfy its condition (visible, attached, enabled) and the element never got there in time. The call log shows exactly what it waited on, which usually points at a selector that is wrong in the CI build or content that loads slower.
What this error means
A step fails with "locator.waitFor: Timeout 30000ms exceeded" plus a call log like "waiting for locator('#submit') to be visible". The page rendered but not the expected element.
Error: locator.click: Timeout 30000ms exceeded.
Call log:
- waiting for getByRole('button', { name: 'Submit' })
- locator resolved to hidden <button disabled>...</button>Common causes
The element stays hidden, disabled, or detached
The locator resolves but the element never becomes actionable (still hidden or disabled), so the auto-wait runs out the clock.
A selector that does not match the CI-rendered DOM
A text or role selector that depends on data, locale, or feature flags differs in the CI build, so nothing ever matches.
How to fix it
Assert the state you actually expect
Wait for the precise condition (visible, enabled) with a web-first assertion so the failure message is specific.
await expect(page.getByRole('button', { name: 'Submit' }))
.toBeEnabled();Use stable selectors that exist in CI
Prefer data-testid or role locators that do not depend on environment-specific text or feature flags.
await page.getByTestId('submit-order').click();How to prevent it
- Prefer role and
data-testidlocators over brittle CSS or text matches. - Assert the actionable state (visible/enabled) instead of clicking blind.
- Seed deterministic data so the CI DOM matches what the selector expects.