A reliable test waits for a meaningful condition, not an estimated number of seconds. Good waits handle normal variation without hiding real performance problems.
Auto-waiting
Playwright waits before actions until the locator resolves to one element and the element is visible, stable, enabled and able to receive events as required.
await page.getByRole('button', { name: 'Pay' }).click();
await expect(page.getByText('Payment complete')).toBeVisible();
The assertion retries. A plain immediate check does not.
Explicit waits
Use an explicit wait when the next step depends on an event that auto-waiting cannot express.
| Need | Example |
|---|---|
| Navigation | await page.waitForURL('**/dashboard') |
| API response | await page.waitForResponse(r => r.url().includes('/save') && r.ok()) |
| Download | const download = await page.waitForEvent('download') |
| Element state | await expect(locator).toBeEnabled() |
const responsePromise = page.waitForResponse(
r => r.url().includes('/orders') && r.status() === 201
);
await page.getByRole('button', { name: 'Submit' }).click();
await responsePromise;
Polling
Poll when checking an external system or an eventually consistent result.
await expect.poll(async () => {
const response = await request.get('/jobs/123');
return (await response.json()).status;
}, { timeout: 30_000 }).toBe('complete');
Poll a specific condition at a sensible interval. Stop when it passes or reaches a clear timeout.
Timeouts
- Test timeout: maximum time for the whole test.
- Expect timeout: maximum retry time for an assertion.
- Action timeout: optional limit for an individual action.
- Navigation timeout: optional limit for navigation.
export default defineConfig({
timeout: 30_000,
expect: { timeout: 5_000 }
});
Set limits from realistic service expectations. A very long global timeout makes genuine hangs slow to diagnose.
Why fixed sleeps cause flaky tests
// Avoid
await page.waitForTimeout(5000);
// Prefer a user-visible outcome
await expect(page.getByRole('status')).toHaveText('Saved');
- If the app is fast, the sleep wastes time.
- If the app is slower, the test still fails.
- The test does not explain what it expected.
- Large suites multiply the wasted delay.