PRACTICAL GUIDE / playwright timeout configuration

Budget Playwright Timeouts Across Tests, Fixtures, Actions, and Assertions

Budget Playwright timeouts across tests, fixtures, actions, navigation, and assertions so failures identify the stalled layer instead of merely waiting longer.

By The Testing AcademyUpdated July 11, 20269 min read
All field guides
In this guide13 sections
  1. Map the Clocks Before Changing Them
  2. Start with a Suite Runtime Target
  3. Give the Test One Coherent Outer Budget
  4. Isolate Slow Fixture Provisioning
  5. Keep Action Timeouts Short and Diagnostic
  6. Separate Navigation from Post-Navigation Readiness
  7. Set Assertion Windows by State Class
  8. Account for Hooks and Teardown
  9. Diagnose the Expired Layer
  10. Avoid Timeout Anti-Patterns
  11. Review the Tradeoffs
  12. Operational Checklist
  13. Conclusion: Make the Expired Clock Name the Problem

What you will learn

  • Map the Clocks Before Changing Them
  • Start with a Suite Runtime Target
  • Give the Test One Coherent Outer Budget
  • Isolate Slow Fixture Provisioning

A timeout is a resource budget and a diagnostic boundary. It should tell the team whether provisioning stalled, a button never became actionable, navigation did not begin, or an expected business state failed to appear. Raising every Playwright timeout to the same large number destroys that information and makes genuine hangs expensive.

Build the policy from the outside inward. Set a cap for the complete run, a normal budget for one test, explicit exceptions for slow fixtures, shorter limits for browser operations, and an assertion window based on the state being observed. Every inner clock must fit inside the work that owns it.

Map the Clocks Before Changing Them

The official Playwright timeout documentation separates test, assertion, global, action, navigation, hook, and fixture timeouts. Read the error and call log to identify which clock expired. A test timeout message and an expect.toHaveText timeout are different failures even when they occur on the same line.

The normal test budget includes the test function, fixture setup, and beforeEach hooks. Fixture teardown and afterEach work receive a separate teardown allowance based on the test timeout after the test function finishes. beforeAll and afterAll have their own hook budgets. Assertion timeout is independent, but it cannot keep a test alive after the outer test budget is exhausted.

Animated field map

A Layered Playwright Timeout Budget

The suite target narrows into test, fixture, action, navigation, and assertion clocks that identify the layer which stopped progressing.

  1. 01 / suite runtime

    Suite Runtime Target

    Cap the complete CI run so systemic failure cannot consume unlimited resources.

  2. 02 / test budget

    Test Budget

    Bound one scenario together with its setup fixtures and beforeEach hooks.

  3. 03 / fixture budget

    Fixture Budget

    Isolate legitimately slow provisioning from ordinary scenario execution.

  4. 04 / operation budgets

    Action and Assertion

    Limit browser operations and state polling with purpose-specific deadlines.

  5. 05 / timeout diagnosis

    Timeout Diagnosis

    Use the expired layer, trace, and call log to locate lost progress.

Start with a Suite Runtime Target

globalTimeout protects the CI system when many workers hang, infrastructure is unavailable, or retries multiply a broad failure. Set it from the job's service-level target and leave room for report upload and teardown. It is a circuit breaker, not a performance objective for individual tests.

The following numbers illustrate a policy shape, not universal recommendations. The correct values come from your product's normal and exceptional lifecycles.

TypeScript
import { defineConfig } from "@playwright/test";

export default defineConfig({
  globalTimeout: process.env.CI ? 45 * 60_000 : undefined,
  timeout: 45_000,
  expect: {
    timeout: 7_000,
  },
  use: {
    actionTimeout: 10_000,
    navigationTimeout: 20_000,
    trace: "on-first-retry",
  },
});

If most tests need the maximum, the baseline is not describing normal behavior. Measure which phase consumes time before increasing it.

Give the Test One Coherent Outer Budget

The test budget should cover normal setup and the complete scenario with enough margin for assertion retries. It should not be calculated as the sum of every declared inner timeout, because operations usually finish early. It should still be large enough that a legitimate final assertion is not cut off by setup consuming nearly all available time.

Use test.setTimeout for a named exception whose workflow is intentionally longer. Use test.slow when the whole test category deserves the framework's slow classification. Put the reason next to the override and review exceptions periodically.

TypeScript
import { expect, test } from "@playwright/test";

test("a quarterly export becomes downloadable", async ({ page }) => {
  test.setTimeout(90_000);

  await page.goto("/reports/quarterly");
  await page.getByRole("button", { name: "Generate export" }).click();
  await expect(page.getByRole("status"))
    .toHaveText("Export ready", { timeout: 60_000 });
  await expect(page.getByRole("link", { name: "Download CSV" }))
    .toBeEnabled();
});

The long assertion expresses the known asynchronous boundary. It is more informative than extending every click and navigation in the suite.

Isolate Slow Fixture Provisioning

By default, a test-scoped fixture consumes the test's setup time. A slow worker fixture can use an independent timeout so account leasing or environment provisioning does not force every test to inherit a large outer budget. The fixture must still expose progress through logs or attachments; a separate timeout should not become a silent waiting room.

TypeScript
import { test as base } from "@playwright/test";

type WorkerFixtures = {
  seededTenant: { tenantId: string; cleanupToken: string };
};

export const test = base.extend<{}, WorkerFixtures>({
  seededTenant: [async ({ request }, use) => {
    const response = await request.post("/test-support/tenants", {
      data: { plan: "enterprise", sampleOrders: 25 },
    });
    if (!response.ok()) {
      throw new Error(`Tenant provisioning failed with ${response.status()}`);
    }

    const tenant = await response.json();
    await use(tenant);
    await request.delete(`/test-support/tenants/${tenant.tenantId}`, {
      headers: { "x-cleanup-token": tenant.cleanupToken },
    });
  }, { scope: "worker", timeout: 75_000 }],
});

Provisioning data should be bounded and deterministic. If setup time varies wildly, investigate service capacity, account contention, or an unobservable job rather than repeatedly increasing the fixture limit.

Keep Action Timeouts Short and Diagnostic

Actions already wait for their required actionability checks. An action timeout answers how long a click, fill, or similar browser operation may wait for one target to become usable. A long action timeout can conceal an overlay, wrong locator, or disabled control.

Use a local override when one interaction has a documented reason to wait longer, and pair it with an assertion for the precondition whenever possible.

TypeScript
const submit = page.getByRole("button", { name: "Submit order" });

await expect(page.getByTestId("price-validation"))
  .toHaveText("Validated", { timeout: 15_000 });
await submit.click({ timeout: 5_000 });
await expect(page.getByRole("heading", { name: "Order confirmed" }))
  .toBeVisible();

The validation state owns the longer wait. The click remains a short browser action whose failure points to interactability.

Separate Navigation from Post-Navigation Readiness

Navigation timeout controls operations such as page.goto. It does not prove that a background report, websocket subscription, or dashboard calculation is complete. After navigation reaches its configured completion, assert the product state separately.

If navigation itself is slow, inspect DNS, server response, redirects, and resource loading in the trace. If navigation succeeds but the page remains in a loading state, the assertion or domain polling boundary owns the wait. Raising navigation timeout will not repair a stalled client request that starts afterward.

Set Assertion Windows by State Class

Most visible UI state should settle quickly. Jobs, exports, asynchronous indexing, and replicated data may have a longer documented window. Configure a normal assertion timeout and override only the matcher that observes the slower state.

Avoid chains of sequential long assertions that can each consume their full limit. Assert the primary readiness signal first, then use normal limits for dependent UI. For a group of values that become consistent together, one locator assertion or a bounded polling block may communicate the boundary more accurately than five independent long waits.

Account for Hooks and Teardown

An expensive beforeEach reduces the time left for the test. Move reusable, isolated provisioning to a worker fixture only when tests can safely share it. Keep per-test data reset visible and fast. beforeAll has a separate timeout, but shared mutable setup can make parallel and retry behavior harder to reason about.

Teardown must be idempotent and should fail with its own resource identity. A test timeout followed by another long cleanup failure produces confusing reports. Use server-side lease expiration or cleanup jobs for resources that must survive a terminated worker, because no in-process timeout policy can guarantee teardown after a hard crash.

Diagnose the Expired Layer

When a test timeout fires, inspect how much time setup and hooks consumed before assuming the final line is slow. When an action timeout fires, read the actionability call log for visibility, stability, event interception, enabled state, and locator resolution. When an assertion expires, compare the received value history and verify the observed signal is the correct one.

A navigation timeout points toward request and lifecycle evidence. A fixture timeout points toward provisioning or teardown. A global timeout accompanied by many similar failures points toward shared infrastructure or a retry storm. Preserve the trace, fixture logs, and service health evidence needed to make those distinctions.

Avoid Timeout Anti-Patterns

Hard sleeps spend the full delay even when the state is ready and still fail when it is slower. Global multiplication makes every defect expensive. Setting timeout: 0 removes a useful boundary and leaves the outer clock to produce a less specific failure. Copying one large number into test, action, navigation, and assertion settings prevents the expired clock from naming the stalled layer.

Retries are not additional wait strategy. They rerun a test in a fresh worker after failure. If a deterministic wait is undersized, fix that boundary; if the signal is wrong, a retry only changes timing around the same defect.

Review the Tradeoffs

Short budgets surface regressions quickly but can create false failures when an operation has a legitimate tail. Long budgets tolerate expected variability but increase feedback time and mask lost progress. Per-operation overrides improve diagnosis while adding policy complexity.

Choose values from observed healthy runs and documented product deadlines, then review outliers rather than tuning toward the slowest incident. Separate local developer ergonomics from CI resource protection where appropriate, but keep behavior close enough that failures reproduce in both places.

Operational Checklist

  • Set a complete-run cap that leaves time for reporting and cleanup.
  • Define one normal test timeout for the common workflow.
  • Track every test-level exception and its reason.
  • Give slow worker provisioning its own fixture timeout.
  • Keep action limits below the test budget and inspect actionability failures.
  • Treat navigation completion and application readiness as different states.
  • Override only the assertion that owns a known slow domain boundary.
  • Include hook and fixture setup time when reviewing test consumption.
  • Preserve traces and service logs for each timeout class.
  • Remove hard waits and unlimited clocks from release-gating tests.

Conclusion: Make the Expired Clock Name the Problem

Inventory the suite's clocks, assign each asynchronous boundary to one owner, and set the outer budgets before the inner ones. Keep ordinary actions and assertions tight, isolate legitimate fixture and job delays, and investigate every broad override. A timeout policy is working when a failure immediately narrows the search to provisioning, browser interaction, navigation, or product state.

// LIVE COURSE / THE TESTING ACADEMY

Playwright Automation Mastery

Go beyond Selenium. Master Playwright with JS/TS in 90 days.

From the instructor behind this guide.

Playwright jobs are growing 8x faster than Selenium. 90 days / 75+ live hrs / Tue-Thu-Sat 7 AM IST.

Code PROMODE / 10% offJoin the batch

The Testing Academy editorial desk

Practical QA guidance built around test evidence, production tradeoffs, and interview-ready explanations.

Published July 11, 2026 / Reviewed July 11, 2026

PRIMARY REFERENCES

Verify the details at the source

QABattle guides are practical explanations. Product behavior, standards, and APIs can change, so use these primary references for the canonical details.

  1. 01
    Playwright documentation

    Microsoft

    Canonical API, locator, fixture, browser, and test-runner behavior.

  2. 02
    Playwright best practices

    Microsoft

    Official guidance for resilient tests, isolation, and user-facing locators.

  3. 03
    WebDriver standard

    W3C

    The browser automation protocol specification used by major automation stacks.

FAQ / QUICK ANSWERS

Questions testers ask

What is the difference between Playwright test and assertion timeouts?

The test timeout bounds the test body, fixture setup, and beforeEach work, while an auto-retrying assertion has its own polling deadline. The test budget remains the outer limit even when an individual assertion allows more time.

Do Playwright fixtures need a separate timeout?

Most test-scoped fixtures can share the test budget. Give a fixture its own timeout when setup has a distinct, legitimately slower lifecycle, especially for worker-scoped provisioning, while keeping ordinary tests on a smaller budget.

How do actionTimeout and navigationTimeout differ?

Action timeout limits operations such as locator clicks, while navigation timeout limits navigation operations such as page.goto. Both are low-level controls and should remain below the outer test budget.

Should I increase the global timeout to fix a slow test?

No. Global timeout bounds the whole run and protects CI resources; it does not diagnose one scenario. Classify the stalled test, fixture, action, navigation, or assertion and change only the justified layer.

When should I call test.slow?

Use it for a test category that is intentionally slower by design and document why. Do not use it as a blanket response to intermittent delays, because it multiplies the test timeout without fixing the missing readiness signal.