# Playwright Waiting · PW-08

Playwright automatically waits for elements to be ready before acting on them. This document describes when Playwright tests need to wait explicitly, and how to wait for something specific rather than for a fixed time or a page load state.

## Standard Metadata

- Area: Playwright
- Owner: Technical Leadership group
- Last reviewed: 2026-10-07
- Next review due from: 2026-12-01
- Enforcement: ESLint / code review
- Status: Active

## Playwright tests should wait for a specific response or element, not a page load state · PW-08.1 · **SHOULD** · **TEST**

Avoid generic waits such as `waitForTimeout()` or `waitForLoadState()`, and avoid using `waitForURL()` as a signal that the page is ready. They either wait longer than needed or not long enough, which makes tests slow or flaky.

Instead, wait for the specific network response the next step depends on. When the next step acts on an element, no wait is usually needed at all, because of Playwright's [built-in actionability checks](#playwright-tests-must-not-duplicate-built-in-actionability-checks).

❌ Waits for all network activity to stop, whatever the page is doing:

```ts
public async searchForCustomer(customerName: string) {
  await this.searchInput().fill(customerName);
  await this.page.waitForLoadState('networkidle');
}
```

✔️ Waits for the search request that this function triggers:

```ts
public async searchForCustomer(customerName: string) {
  const customersResponse = this.page.waitForResponse((response) =>
    response.url().includes('/customers') && response.status() === 200);

  await this.searchInput().fill(customerName);
  await customersResponse;
}
```

## Playwright tests must not duplicate built-in actionability checks · PW-08.2 · **MUST** · **TEST**

Before every action, Playwright performs [actionability checks](https://playwright.dev/docs/actionability) and waits for them to pass. For example:

- `.click()` waits for the element to be visible, stable, enabled and able to receive events
- `.fill()` waits for the input to be visible, enabled and editable
- `.check()` and `.setChecked()` wait for the element to be visible, stable, enabled and able to receive events

Do not add `.waitFor()`, `toBeVisible()` or `toBeEnabled()` immediately before an action on the same element, because the action already performs these checks.

❌ Duplicates the check that `click()` already makes:

```ts
await expect(customersPage.addButton()).toBeVisible();
await customersPage.addButton().click();
```

✔️ Let the action wait for the element:

```ts
await customersPage.addButton().click();
```

**NOTE**:
This only applies to checks made before an action. Assertions such as `toBeVisible()` are still expected when the element's state is the outcome under test.

## Playwright tests must not override the default `waitUntil` option · PW-08.3 · **MUST** · **TEST**

Navigation functions such as `goto()` and `reload()` already wait for the page's `load` event by default. Do not pass a different `waitUntil` value, such as `'networkidle'`, to wait for the page to be ready. Instead, [wait for a specific response or element](#playwright-tests-should-wait-for-a-specific-response-or-element-not-a-page-load-state).

❌ Waits for all network activity to stop:

```ts
await this.page.goto('/customers', { waitUntil: 'networkidle' });
```

✔️ Uses the default:

```ts
await this.page.goto('/customers');
```

**EXCEPTION**:
A different `waitUntil` value may be used only when the default cannot work for that page. Add a comment where it is used explaining why:

```ts
// The dashboard holds a long-lived connection open, which delays the load event, so only wait for navigation to start
await this.page.goto('/dashboard', { waitUntil: 'commit' });
```
