Resource library

QA How-To

How to Use Playwright global setup (2026)

Learn Playwright global setup in 2026 with project dependencies, authentication state, API seeding, teardown, CI guidance, and runnable TypeScript patterns.

25 min read | 3,715 words

TL;DR

For current Playwright Test suites, create a setup project and add it to each browser project's dependencies. This approach is recommended because setup appears in reports, supports fixtures and traces, and can have a teardown project; use the globalSetup config option only for narrow process-level preparation.

Key Takeaways

  • Use a setup project with project dependencies for most new Playwright Test suites.
  • Keep setup idempotent because retries, shards, and separate CI jobs can execute it again.
  • Store reusable browser authentication with context.storageState and keep the file out of version control.
  • Model cleanup with a teardown project when setup creates shared external resources.
  • Use the globalSetup config option only when its simpler process-level hook fits the requirement.
  • Pass durable setup output through files, APIs, or external state instead of mutable worker memory.
  • Make setup observable with named tests, assertions, traces, and precise failure messages.

Playwright global setup lets a test run prepare authentication, seed required data, validate an environment, or provision a shared prerequisite before dependent tests execute. In 2026, the recommended Playwright Test design is a setup project connected through project dependencies because it receives fixtures, trace recording, retries, configuration, and report visibility.

The older globalSetup configuration option is still supported. It exports one function that runs before the suite, but it does not behave like a normal test project. Browser launching is manual, Playwright fixtures are unavailable, and setup work does not appear as a regular test in the HTML report.

This guide shows both approaches with TypeScript and gives a practical decision model for authentication, data seeding, teardown, CI, and debugging.

TL;DR

Requirement Best approach
Reuse authenticated browser state Setup project plus storageState
Seed data with the request fixture Setup project
Capture setup trace and report steps Setup project
Retry setup as a normal test Setup project
Run cleanup after dependent projects Setup project with teardown
Set a process environment variable before tests globalSetup config option
Execute a small Node-only prerequisite Either, based on reporting needs

Minimal recommended configuration:

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  use: {
    baseURL: 'https://example.test',
  },
  projects: [
    {
      name: 'setup',
      testMatch: /global\.setup\.ts/,
    },
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
      dependencies: ['setup'],
    },
  ],
});

The setup project runs first. If it fails, dependent projects do not run.

1. What Playwright Global Setup Means

Global setup is work that must finish before a selected group of tests can start. Typical examples include signing in once and saving cookies, creating a tenant, seeding reference data, verifying a service health endpoint, or provisioning a temporary resource.

"Global" does not mean one execution across every machine in a distributed pipeline. Each Playwright command is its own runner invocation. If a matrix launches three jobs, each job can execute its own setup. If a suite is sharded, each shard is another invocation. Design setup so repeated execution is safe.

Playwright has two supported models:

  1. A normal test project that other projects list in dependencies.
  2. A function referenced by the top-level globalSetup configuration property.

Project dependencies are the preferred model for most suites. A setup project uses test(), expect(), fixtures, hooks, retries, attachments, and traces. It is visible in reports and can connect to cleanup through a teardown project.

The globalSetup function runs once before tests in that Playwright process. It receives FullConfig. It can return a function that acts as teardown, or a separate globalTeardown path can be configured. It does not receive the page, browser, request, or other Playwright Test fixtures.

Do not place ordinary per-test preparation in either global mechanism. Each test should own state that must be isolated. Use fixtures or beforeEach for users, carts, feature flags, and anything a parallel test could mutate. Global setup is for genuinely shared prerequisites or reusable read-only state.

2. Choose Project Dependencies or the globalSetup Config Option

The choice affects visibility and maintenance more than syntax.

Capability Project dependency globalSetup option
Appears in HTML report Yes No
Uses Playwright Test fixtures Yes No
Trace recording Yes No
Automatic browser fixture Yes No
Standard retries and timeout Yes No
Inherits use configuration Yes No
Can have project teardown Yes Separate hook or returned function
Can set process.env for later tests Do not rely on it across workers Yes
Recommended for new suites Yes Only for a fitting narrow case

Choose a setup project for browser authentication, API seeding through the request fixture, traceable environment checks, or preparation that benefits from assertions. This is also the clearest approach when different browser or role projects depend on different setup work.

Choose the globalSetup option when you need a small runner-level Node task, want to set a process environment variable for the same invocation, or maintain an existing suite where conversion adds no current value. Launching a browser manually in globalSetup is valid, but you must apply launch and context options yourself.

Neither approach is a substitute for a deployment pipeline. Database migrations, application rollout, and production-grade infrastructure provisioning usually belong before the test command. Playwright setup can verify or create test-specific prerequisites, but it should not quietly own the entire environment.

For focused recipes beyond this lifecycle tutorial, see Playwright global setup examples.

3. Configure a Setup Project Correctly

Install Playwright Test and its browsers in a test repository:

npm init playwright@latest
npx playwright install

Create a configuration where global use options apply to setup and browser projects:

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  retries: process.env.CI ? 2 : 0,
  use: {
    baseURL: process.env.BASE_URL ?? 'http://127.0.0.1:3000',
    trace: 'on-first-retry',
  },
  projects: [
    {
      name: 'setup',
      testMatch: /global\.setup\.ts/,
    },
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
      dependencies: ['setup'],
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
      dependencies: ['setup'],
    },
  ],
});

Then add a real setup test:

// tests/global.setup.ts
import { test as setup, expect } from '@playwright/test';

setup('environment is ready', async ({ request }) => {
  const response = await request.get('/api/health');

  expect(response.ok()).toBeTruthy();
  expect(await response.json()).toMatchObject({
    status: 'ready',
  });
});

The setup file name must match testMatch. Keep setup patterns narrow so normal specifications do not accidentally join the setup project. Likewise, ensure ordinary project patterns do not select setup files. Separate folders such as tests/setup can make boundaries even clearer.

Run the suite normally:

npx playwright test

Selecting chromium also selects its dependency. The CLI flag --no-deps intentionally skips dependencies and teardown projects, so use it only when you understand that required preparation will not run.

Project configuration is matched by project name, not array position. Give projects stable, descriptive names because those names appear in reports, command-line filters, dependencies, and teardown relationships.

Treat the project graph as executable architecture. A reviewer should be able to identify each prerequisite, its consumers, and its failure effect from playwright.config.ts without opening a shell wrapper.

4. Authenticate Once and Reuse storageState

Authentication is the most common Playwright global setup use case. The setup test signs in, saves browser context state, and dependent projects load that file into fresh contexts.

// tests/auth.setup.ts
import { mkdir } from 'node:fs/promises';
import path from 'node:path';
import { test as setup, expect } from '@playwright/test';

const authFile = path.join(process.cwd(), 'playwright/.auth/user.json');

setup('authenticate standard user', async ({ page }) => {
  await mkdir(path.dirname(authFile), { recursive: true });

  await page.goto('/login');
  await page.getByLabel('Email').fill(process.env.E2E_USER_EMAIL ?? '');
  await page.getByLabel('Password').fill(process.env.E2E_USER_PASSWORD ?? '');
  await page.getByRole('button', { name: 'Sign in' }).click();

  await expect(page).toHaveURL(/\/dashboard$/);
  await expect(page.getByRole('heading', { name: 'Dashboard' }))
    .toBeVisible();

  await page.context().storageState({ path: authFile });
});

Reference that state from dependent projects:

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  use: {
    baseURL: process.env.BASE_URL ?? 'http://127.0.0.1:3000',
  },
  projects: [
    {
      name: 'auth setup',
      testMatch: /auth\.setup\.ts/,
    },
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
        storageState: 'playwright/.auth/user.json',
      },
      dependencies: ['auth setup'],
    },
  ],
});

Add playwright/.auth to .gitignore. The state file can contain cookies or local storage values that impersonate the account. Treat it as a secret artifact and generate it in the target environment.

Wait for an authenticated URL or page signal before saving. Some applications set cookies through redirects or asynchronous calls. Saving immediately after click can persist incomplete state.

Shared authentication is appropriate when tests only read or isolate their server-side data. If parallel tests change the same account preferences, cart, permissions, or resources, use one account per worker or create users through an API fixture. Typing Playwright fixtures explains the per-test and per-worker alternatives.

Plan for expiration as well as creation. Local state may survive between commands, while clean CI workspaces regenerate it each time. A failed authenticated smoke check should direct engineers to rerun setup, not encourage manual cookie editing.

5. Seed Test Data Through the request Fixture

A setup project can use APIRequestContext through the built-in request fixture. This is cleaner and faster than driving the browser through administrative screens when the requirement is simply to create prerequisite data.

// tests/data.setup.ts
import { test as setup, expect } from '@playwright/test';

setup('seed reference catalog', async ({ request }) => {
  const response = await request.post('/api/test-support/catalog', {
    data: {
      catalogKey: 'e2e-default',
      products: [
        { sku: 'KB-100', name: 'Mechanical Keyboard', price: 99 },
        { sku: 'MS-200', name: 'Wireless Mouse', price: 49 },
      ],
    },
    headers: {
      authorization: 'Bearer ' + (process.env.E2E_SEED_TOKEN ?? ''),
    },
  });

  expect(response.ok()).toBeTruthy();
  expect(await response.json()).toMatchObject({
    catalogKey: 'e2e-default',
  });
});

Make the endpoint idempotent. An upsert keyed by e2e-default is safer than blind insertion. Retries, repeated local runs, shards, and canceled jobs should not create uncontrolled duplicates.

Avoid writing mutable IDs into process variables from a setup test. Setup and dependent tests can run in different worker processes. Better options include:

  • Use deterministic keys such as catalogKey.
  • Write nonsecret output to a known JSON file and read it in tests.
  • Query the created resource by a unique run ID.
  • Store shared state in a test-support service.
  • Move per-test entities into fixtures.

A single shared mutable record is a common source of parallel failures. Seed reference data globally only when tests can treat it as read-only. Create orders, drafts, and other mutable business objects per test.

Keep credentials in CI secrets and send them only to controlled test-support endpoints. Do not expose production administration tokens to a browser page or commit them in the setup file.

6. Add a Teardown Project for Shared Resources

When setup creates an external resource that must be deleted, connect a teardown project. Playwright runs it after all projects that depend on the setup have finished.

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  projects: [
    {
      name: 'tenant setup',
      testMatch: /tenant\.setup\.ts/,
      teardown: 'tenant cleanup',
    },
    {
      name: 'tenant cleanup',
      testMatch: /tenant\.teardown\.ts/,
    },
    {
      name: 'e2e',
      testMatch: /.*\.spec\.ts/,
      dependencies: ['tenant setup'],
    },
  ],
});

The cleanup test can delete a deterministic tenant:

// tests/tenant.teardown.ts
import { test as teardown, expect } from '@playwright/test';

teardown('remove E2E tenant', async ({ request }) => {
  const response = await request.delete('/api/test-support/tenants/e2e-main', {
    headers: {
      authorization: 'Bearer ' + (process.env.E2E_SEED_TOKEN ?? ''),
    },
  });

  expect([204, 404]).toContain(response.status());
});

Accepting 404 makes cleanup idempotent when the resource was already removed. Decide whether a cleanup failure should fail the pipeline. For expensive or security-sensitive resources, it usually should. For diagnostic business data that must survive a failed run, automatic deletion may be counterproductive.

Teardown should not erase evidence before reporters upload traces, screenshots, or logs. Keep Playwright artifacts in the output directory and clean only external business data owned by the run.

A teardown project is better than an afterAll in an arbitrary test file because its lifecycle is linked to the setup project and all dependents. Per-test cleanup still belongs in fixtures or hooks close to the test that created the data.

Record resource ownership during creation. Cleanup that searches by a broad name prefix can remove another developer's tenant or another shard's data, while exact IDs make the deletion auditable and safe.

7. Use the globalSetup Config Function When It Fits

The configuration hook remains a current API. Add a path to the top-level configuration:

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  globalSetup: require.resolve('./tests/global-setup'),
  use: {
    baseURL: process.env.BASE_URL ?? 'http://127.0.0.1:3000',
  },
});

Export one function that accepts FullConfig:

// tests/global-setup.ts
import type { FullConfig } from '@playwright/test';

export default async function globalSetup(
  config: FullConfig,
): Promise<void> {
  const baseURL = config.projects[0]?.use.baseURL;

  if (typeof baseURL !== 'string') {
    throw new Error('A string baseURL is required for E2E tests');
  }

  const response = await fetch(new URL('/api/health', baseURL));

  if (!response.ok) {
    throw new Error(
      'Environment health check failed with HTTP ' + response.status,
    );
  }

  const body = await response.json() as { status?: string };

  if (body.status !== 'ready') {
    throw new Error('Environment is not ready');
  }
}

This example is Node-only and does not need Playwright fixtures. It reads configuration explicitly, validates assumptions, and throws a useful error.

If you launch chromium inside globalSetup, configuration such as use.headless, testIdAttribute, and context options is not automatically applied. Import chromium, launch it, create a context, and close the browser yourself. This manual ownership is one reason project dependencies are generally better for browser authentication.

globalSetup may return an async function for teardown. A separate globalTeardown file is also supported. Choose one style and keep ownership obvious.

Remember that the configuration file itself can be evaluated more than once. Keep configuration values stable and avoid provisioning resources while the config module loads. Perform side effects only inside the exported setup function, validate inputs before the first mutation, and close every manually created client even when an intermediate step throws.

8. Pass Data From Setup Without Hidden Coupling

A globalSetup function can assign process.env values for tests in the same Playwright runner process:

import type { FullConfig } from '@playwright/test';

export default async function globalSetup(
  _config: FullConfig,
): Promise<void> {
  const response = await fetch('https://example.test/api/test-session', {
    method: 'POST',
    headers: {
      authorization: 'Bearer ' + (process.env.E2E_SEED_TOKEN ?? ''),
    },
  });

  if (!response.ok) {
    throw new Error('Could not create test session');
  }

  const data = await response.json() as { sessionId: string };
  process.env.E2E_SESSION_ID = data.sessionId;
}

A test can read process.env.E2E_SESSION_ID. This behavior belongs to the process-level globalSetup hook, not a setup project test running in a worker.

Environment mutation is convenient but invisible to TypeScript's type system and test dependencies. Validate required values in one helper:

export function requiredEnv(name: string): string {
  const value = process.env[name];

  if (!value) {
    throw new Error('Missing required environment variable: ' + name);
  }

  return value;
}

For nonsecret structured data, a JSON manifest can be clearer. Write it atomically during setup, use a run-specific path, and make consumers fail with a precise message if it is missing. Do not have several shards overwrite one shared local file on a network volume.

For secrets, prefer the CI secret store and short-lived credentials. Never attach authentication state, tokens, or environment dumps to a public report. Setup observability must not become credential leakage.

The cleanest data flow often uses deterministic resource names. Tests can query e2e-catalog rather than receiving an opaque generated ID from global setup.

9. Understand Filtering, Projects, Retries, and CI

Project dependencies participate in normal project selection. When selected primary tests belong to a project with dependencies, Playwright runs the dependencies first. If a dependency fails, dependent tests are not run. The --no-deps flag bypasses this behavior, including teardown projects.

Multiple dependencies can run before a project. Independent dependencies may run in parallel, so do not assume an order unless you model it through another dependency. If browser tests require both authentication and database setup, either make both idempotent and independent or make auth depend on database setup when there is a real prerequisite.

Setup tests can use standard retries. This is useful for a transient health check, but retries must not multiply data. Every create operation needs a deterministic key, upsert semantics, or rollback.

A minimal CI step is ordinary Playwright execution after secrets and the target URL have been configured by the pipeline:

- name: Run Playwright tests
  run: npx playwright test

The setup project runs as part of this command. A browser matrix with separate commands repeats setup in each job. That can be correct for isolated environments. If setup is costly, provision once in an earlier pipeline step and pass a durable, secure reference to each job, but keep browser state local when it is job-specific.

For pipeline design, GitHub Actions for Playwright covers browser installation, artifacts, and CI execution.

10. Debug Playwright Global Setup Failures

Project dependency failures appear as failed setup tests, which is their major operational advantage. Give each setup responsibility a descriptive test name and assert important responses.

Use focused commands:

npx playwright test --project=setup
npx playwright test --project=chromium
npx playwright test --project=setup --trace=on

The second command includes dependencies unless --no-deps is passed. Open the HTML report or trace to inspect requests, screenshots, console logs, and actions.

Common diagnostic questions are:

  • Did testMatch select the intended setup file?
  • Does the setup project inherit the needed baseURL and headers?
  • Are credentials present in the current shell or CI job?
  • Did login reach the final authenticated state before storageState was saved?
  • Is the output directory writable?
  • Is seed logic idempotent after a retry?
  • Does the dependent project point to the exact storage state path?
  • Is a matrix job using a different workspace or container?

For globalSetup config functions, errors appear outside normal test reporting. Throw concise messages with response status and prerequisite name, but never include passwords, cookies, authorization headers, or full secret payloads.

Enable debug logging selectively if needed:

DEBUG=pw:api npx playwright test

Treat logs as sensitive because API and browser diagnostics can expose URLs or values. Reproduce with the smallest setup responsibility and remove temporary secret-adjacent logging before committing.

11. Design Maintainable Setup Architecture

Good global setup has a narrow purpose, explicit inputs, observable output, and safe repetition. Split independent responsibilities into named setup tests or projects instead of one long script that authenticates five roles, migrates a database, seeds hundreds of records, and starts services.

A scalable project graph might contain:

  • environment check, required by all E2E projects
  • standard-user auth, required by standard-user tests
  • admin auth, required by administration tests
  • catalog seed, required by commerce tests
  • tenant cleanup, linked only to tenant setup

Do not make every browser project depend on every setup project. Unnecessary dependencies slow focused runs and expand the failure blast radius.

Keep browser state files role-specific and obvious, such as playwright/.auth/admin.json and playwright/.auth/user.json. Avoid sharing one Page or BrowserContext across tests. storageState initializes new isolated contexts; it does not reuse a live browser page.

Document ownership. State who provides accounts, which endpoint accepts seeding, how cleanup works, whether a run can target production, and how local developers obtain safe credentials. Enforce environment guards on destructive test-support endpoints.

Review setup when suite parallelism changes. A design that worked with one worker can fail with shards because it assumed unique mutable state. The correct long-term fix is usually better data isolation, not serial execution.

Interview Questions and Answers

Q: What is the recommended way to implement Playwright global setup?

Use a setup project and list it in the dependencies of projects that require it. The setup runs as normal Playwright tests, so it supports fixtures, traces, retries, assertions, and report visibility. This is usually more maintainable than the globalSetup config function.

Q: What happens when a setup dependency fails?

Projects that depend on the failed setup are not run. The setup failure appears in the report as a test failure. This prevents misleading downstream failures caused by a missing prerequisite.

Q: When would you still use the globalSetup config option?

I would use it for a small process-level Node task, a compatible existing suite, or a requirement to set process.env before tests in that invocation. I would not choose it for fixture-heavy browser authentication because browser and context management are manual.

Q: How do you share login across tests safely?

Authenticate in a setup project, wait for a definitive signed-in state, and save context.storageState to a gitignored file. Dependent projects load that state into fresh contexts. Tests must still isolate server-side data if they modify the same account.

Q: Why must global setup be idempotent?

Retries, shards, separate matrix jobs, and repeated local commands can execute setup more than once. Blind inserts or fixed unique resources create collisions and leaks. I use deterministic keys, upserts, or run-specific ownership plus cleanup.

Q: How do you pass values from setup to tests?

For globalSetup, process.env can pass a value in the runner process. For setup projects, I use storageState, deterministic external resources, or an explicit file because workers do not share mutable memory reliably. I validate all required inputs at the consumer boundary.

Q: How is teardown modeled with project dependencies?

The setup project names a teardown project through its teardown property. Playwright runs that teardown after dependent projects finish. Cleanup should be idempotent and should delete only resources owned by the run.

Q: What belongs in a fixture instead of global setup?

Mutable records, users, carts, orders, and other data that each parallel test changes belong in per-test or per-worker fixtures. Global setup is best for shared prerequisites and reusable read-only state. Isolation is more important than avoiding a few setup calls.

Common Mistakes

  • Using global setup for data that each parallel test mutates.
  • Treating "global" as one execution across all CI machines and shards.
  • Saving storageState before the final authentication redirect or cookie update.
  • Committing authentication state files or credentials.
  • Using a setup project to set process.env and expecting every worker to inherit the mutation.
  • Launching a browser in globalSetup without closing it.
  • Assuming use options automatically apply to a manually launched globalSetup browser.
  • Creating duplicate seed data on retries.
  • Making all projects depend on unrelated setup responsibilities.
  • Hiding setup behind beforeAll in an arbitrary specification.
  • Cleaning resources that belong to another run.
  • Deleting traces or screenshots during business-data teardown.
  • Running with --no-deps and forgetting that required setup was skipped.
  • Logging tokens, cookies, or full environment objects during diagnosis.
  • Moving deployment and migration ownership into an opaque test hook.

Conclusion

Use a project dependency for most Playwright global setup in 2026. It gives preparation the same fixtures, assertions, tracing, retry behavior, and reporting as other tests, and it can connect to a dedicated teardown project. Keep the globalSetup config function for process-level work that genuinely benefits from its simpler lifecycle.

Start by classifying your prerequisite as shared read-only state, reusable authentication, or per-test mutable data. Put only the first two in setup, make every operation idempotent, protect generated credentials, and let isolated fixtures own the rest.

Interview Questions and Answers

What is the recommended Playwright global setup design?

I use a setup project and connect required projects through dependencies. It behaves like normal Playwright tests, so I get fixtures, assertions, retries, traces, and report visibility. If setup fails, dependent projects do not run.

How does the globalSetup config function differ from a setup project?

The function runs at runner level and receives FullConfig, but it has no Playwright Test fixtures or normal test reporting. Browser management and use options are manual. A setup project integrates with the full test-runner lifecycle.

How would you implement shared authentication?

I sign in through a setup project, wait for a reliable authenticated signal, and save context.storageState to a protected file. Dependent projects load that file into fresh browser contexts. I keep server-side data isolated if tests mutate the account.

Why should setup code be idempotent?

Setup can repeat because of retries, shards, CI matrices, or another command. Idempotent upserts, deterministic names, and safe cleanup prevent duplicate records and resource leaks. This also makes local reruns predictable.

Can a setup project pass an environment variable to test workers?

I do not rely on worker memory mutation from a setup test. I pass durable output through storageState, a file, or external state with deterministic keys. The process-level globalSetup function can set process.env for the runner invocation.

How do you design teardown for global resources?

I link a dedicated teardown project from the setup project's teardown property. Cleanup is idempotent, scoped to resources owned by the run, and does not delete report artifacts. I decide explicitly whether cleanup failure should fail CI.

What data should not be created globally?

Records that tests mutate, such as carts, orders, drafts, and account preferences, should not be shared globally. I create them in per-test or per-worker fixtures. Global setup remains focused on shared prerequisites and read-only reference data.

How do you debug setup failures in CI?

I run the setup project alone, inspect its trace and report, verify testMatch, configuration, secrets, and output paths, then check idempotency. I use precise error messages without logging credentials. I also reproduce the exact CI project selection locally.

Frequently Asked Questions

What is Playwright global setup?

It is preparation that runs before selected Playwright tests, such as authentication, health checks, or reference-data seeding. Playwright supports setup projects with dependencies and a top-level globalSetup function.

Should I use globalSetup or project dependencies in Playwright?

Use project dependencies for most new suites because they support fixtures, traces, retries, configuration, and report visibility. Use globalSetup for a narrow process-level hook when those test-runner features are unnecessary.

Does Playwright global setup run once?

A globalSetup function runs once per Playwright runner invocation. A setup project runs when selected projects depend on it. Separate CI jobs, shards, or commands can each run setup, so the work must be idempotent.

How do I reuse login from Playwright global setup?

Authenticate in a setup project, wait for the final signed-in state, and save context.storageState to a gitignored file. Set that file as storageState in dependent browser projects.

Can Playwright global setup use the page fixture?

A setup project can use page and other Playwright Test fixtures. The globalSetup config function cannot receive them, so any browser must be imported, launched, configured, and closed manually.

How do I run global teardown in Playwright?

With projects, set the setup project's teardown property to the cleanup project name. With the globalSetup function, return an async cleanup function or configure a separate globalTeardown file.

Why does my setup run multiple times in CI?

Each matrix job, shard, or separate Playwright command is an independent runner invocation. Design seed and provisioning operations to be repeatable or use unique run ownership plus cleanup.

Should test data be created in global setup?

Only shared, read-only reference data is a strong fit. Mutable business records should usually be created per test or per worker so parallel tests do not compete for the same state.

Related Guides