IKivan-kozenko -aqa
Try yourself as a QA tester
All topics·Intermediate·13 / 27

Global setup and teardown

There are two ways to run code once before all tests: project dependencies (recommended) and globalSetup in config. I always use project dependencies — it shows in the HTML report, records traces, and supports fixtures. The main use case: log in once, save storageState, every test starts already authenticated.

When I need global setup

The most common reason I use global setup: authentication. Logging in before every test is slow — on a suite of 200 tests it adds 200 login flows. Instead I log in once in global setup, save the browser storage state (cookies + localStorage) to a file, and every test starts already authenticated by loading that file.

Other use cases: seeding a test database, creating fixtures in an API, setting environment variables that tests read.

Two approaches — and which to pick

Project dependencies appear in the HTML report and support fixtures; globalSetup is invisible to the report and cannot use fixtures

Option 1: Project dependencies — I create a special 'setup' project in playwright.config.ts and list it as a dependency of my main test projects. The setup project runs first, then the tests run. Option 2: globalSetup config option — I point globalSetup in the config to a file that exports a function. That function runs once before all tests. I always use Option 1. Here's why:

| Feature | Project dependencies | globalSetup | |---|---|---| | Visible in HTML report | ✅ As separate project | ❌ Not shown | | Trace recording | ✅ Full trace | ❌ Not supported | | Playwright fixtures | ✅ Supported | ❌ Not supported | | Browser via fixture | ✅ { browser } fixture | ❌ Manual browserType.launch() |

Project dependencies — login once pattern

This is the pattern I use for authentication in every project. The 'setup' project runs the login script, saves storageState to a file. The 'chromium' project depends on 'setup', so setup always runs first, and the chromium tests load the saved state.

ts
// playwright.config.ts
export default defineConfig({
  testDir: './tests',
  projects: [
    {
      name: 'setup',
      testMatch: /global\.setup\.ts/,
      teardown: 'cleanup',      // опційно — виконається після всіх тестів
    },
    {
      name: 'cleanup',
      testMatch: /global\.teardown\.ts/,
    },
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
        storageState: 'playwright/.auth/user.json',  // завантажити збережений логін
      },
      dependencies: ['setup'],   // ← setup виконається першим
    },
  ],
})
ts
// tests/global.setup.ts
import { test as setup, expect } from '@playwright/test'
import path from 'path'

const authFile = 'playwright/.auth/user.json'

setup('authenticate', async ({ page }) => {
  await page.goto('/login')
  await page.getByLabel('Email').fill('admin@example.com')
  await page.getByLabel('Password').fill('secret')
  await page.getByRole('button', { name: 'Sign in' }).click()
  await expect(page).toHaveURL('/dashboard')

  // Зберегти cookies і localStorage у файл
  await page.context().storageState({ path: authFile })
})
ts
// tests/global.teardown.ts
import { test as teardown } from '@playwright/test'

teardown('cleanup after tests', async ({ request }) => {
  // Наприклад: видалити тестові дані з API
  await request.delete('/api/test-data')
})

Option 2: globalSetup config (when fixtures don't matter)

If I only need to set environment variables or call an API without browser interaction, I use globalSetup in the config. It's simpler for non-browser work but doesn't get traces or HTML report visibility.

ts
// playwright.config.ts
export default defineConfig({
  globalSetup: require.resolve('./global-setup'),
  globalTeardown: require.resolve('./global-teardown'),
})
ts
// global-setup.ts
import type { FullConfig } from '@playwright/test'

async function globalSetup(config: FullConfig) {
  // Передати дані в тести через process.env
  process.env.API_TOKEN = await fetchTestToken()
  process.env.BASE_URL = config.projects[0].use.baseURL!
}

export default globalSetup
ts
// Тест читає змінні встановлені в globalSetup
test('uses api token', async ({ request }) => {
  const response = await request.get('/api/orders', {
    headers: { Authorization: `Bearer ${process.env.API_TOKEN}` }
  })
  expect(response.ok()).toBeTruthy()
})

Test filtering with dependencies

When I filter tests with --grep or run a specific file, Playwright still runs the setup project first if the selected tests depend on it. This is the expected behavior — I can't run tests that need auth without the auth setup.

To skip dependencies (for debugging or when I know the state is already set up): --no-deps. This runs only the directly selected tests without any dependent projects.

bash
# Запустити конкретний тест без виконання setup-проєкту
npx playwright test tests/orders.spec.ts --no-deps

# Фільтрувати за назвою — setup все одно виконається
npx playwright test --grep "create order"