globalSetup выполняется один раз до того как поднимаются любые браузерные воркеры, в основном процессе. Это значит что передавать JavaScript-объекты в тесты нельзя: данные шарятся через файлы на диске или через process.env. Самое частое применение: залогиниться один раз и сохранить результат через storageState, чтобы каждый тест в сьюте стартовал авторизованным без повторения флоу входа. Это руководство покрывает паттерн с auth-состоянием, несколько ролей пользователей с отдельными файлами хранения, наполнение базы данных, глобальный teardown и условный паттерн который пропускает повторный вход если свежий auth-файл уже существует.

Что такое глобальный setup

Playwright выполняет функцию globalSetup один раз до запуска всего тест-сьюта, и функцию globalTeardown один раз после завершения всего. Они работают в основном процессе, до того как поднимаются браузерные воркеры.

Глобальный setup нужен для:

  • Аутентификации и сохранения storageState (самое частое применение)
  • Наполнения тестовой базы данных базовыми данными
  • Запуска мок-сервера или тестового сервера
  • Генерации файлов тест-данных

Базовая конфигурация

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

export default defineConfig({
  globalSetup: require.resolve('./global-setup'),
  globalTeardown: require.resolve('./global-teardown'),
  use: {
    baseURL: 'http://localhost:3000',
  },
});

Самое частое применение: сохранение auth-состояния

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

async function globalSetup(config: FullConfig) {
  const { baseURL } = config.projects[0].use;
  const browser = await chromium.launch();
  const page = await browser.newPage();

  await page.goto(`${baseURL}/login`);
  await page.getByLabel('Email').fill(process.env.TEST_USER_EMAIL!);
  await page.getByLabel('Password').fill(process.env.TEST_USER_PASSWORD!);
  await page.getByRole('button', { name: 'Log in' }).click();
  await page.waitForURL(`${baseURL}/dashboard`);

  // сохраняем куки + localStorage для переиспользования
  await page.context().storageState({ path: 'playwright/.auth/user.json' });
  await browser.close();
}

export default globalSetup;

// playwright.config.ts — используем сохранённый auth во всех тестах
use: {
  storageState: 'playwright/.auth/user.json',
},

Каждый тест теперь стартует уже авторизованным. Никаких шагов входа. Никаких настроек сессии в каждом тесте. Логин происходит один раз.

Несколько ролей пользователей

Для приложений с ролями администратора и обычного пользователя:

// global-setup.ts
async function globalSetup(config: FullConfig) {
  const { baseURL } = config.projects[0].use;
  const browser = await chromium.launch();

  // сохраняем auth администратора
  const adminPage = await browser.newPage();
  await loginAs(adminPage, `${baseURL}`, process.env.ADMIN_EMAIL!, process.env.ADMIN_PASSWORD!);
  await adminPage.context().storageState({ path: 'playwright/.auth/admin.json' });
  await adminPage.close();

  // сохраняем auth обычного пользователя
  const userPage = await browser.newPage();
  await loginAs(userPage, `${baseURL}`, process.env.USER_EMAIL!, process.env.USER_PASSWORD!);
  await userPage.context().storageState({ path: 'playwright/.auth/user.json' });
  await userPage.close();

  await browser.close();
}

async function loginAs(page: Page, baseURL: string, email: string, password: string) {
  await page.goto(`${baseURL}/login`);
  await page.getByLabel('Email').fill(email);
  await page.getByLabel('Password').fill(password);
  await page.getByRole('button', { name: 'Log in' }).click();
  await page.waitForURL(`${baseURL}/dashboard`);
}

Разные auth-состояния используются в разных проектах:

// playwright.config.ts
projects: [
  {
    name: 'admin-tests',
    use: { storageState: 'playwright/.auth/admin.json' },
    testMatch: '**/admin/**/*.spec.ts',
  },
  {
    name: 'user-tests',
    use: { storageState: 'playwright/.auth/user.json' },
    testMatch: '**/user/**/*.spec.ts',
  },
],

Наполнение базы данных в глобальном setup

// global-setup.ts
import { Pool } from 'pg';

async function globalSetup() {
  const pool = new Pool({ connectionString: process.env.DATABASE_URL });

  // наполняем базовыми тест-данными один раз
  await pool.query(`
    INSERT INTO users (email, password_hash, role)
    VALUES ('testuser@example.com', $1, 'user')
    ON CONFLICT (email) DO NOTHING
  `, [hashedPassword]);

  await pool.end();
}

Наполнение БД в глобальном setup подходит для статических базовых данных. Для тест-специфичных данных которые нужно изолировать и очищать, используй фикстуры: они запускаются для каждого теста и сами убирают за собой.

Глобальный teardown

// global-teardown.ts
import { Pool } from 'pg';

async function globalTeardown() {
  // очищаем тест-данные после всего сьюта
  const pool = new Pool({ connectionString: process.env.DATABASE_URL });
  await pool.query("DELETE FROM users WHERE email LIKE '%@test.example.com'");
  await pool.end();

  // останавливаем серверы запущенные в globalSetup
  // (если хранил ссылку в process или global)
}

export default globalTeardown;

Передача данных между глобальным setup и тестами

Глобальный setup работает в отдельном процессе от тестов. Передать JavaScript-объекты напрямую нельзя. Варианты:

Файлы: сохраняй данные на диск в глобальном setup, читай в тестах.

// global-setup.ts
import fs from 'fs';

async function globalSetup() {
  const testData = { userId: '123', orderId: '456' };
  fs.writeFileSync('playwright/.test-data.json', JSON.stringify(testData));
}

// в тестах
import testData from '../playwright/.test-data.json';
test('uses seeded data', async ({ page }) => {
  await page.goto(`/orders/${testData.orderId}`);
});

Переменные окружения: задавай через process.env.KEY = value в глобальном setup. Доступны в воркер-процессах тестов. Подходят для простых значений: ID, токены.

Условный запуск глобального setup

Пропускай глобальный setup при запуске отдельных тестов локально:

// global-setup.ts
async function globalSetup() {
  // пропускаем если auth-файл свежий
  const authFile = 'playwright/.auth/user.json';
  if (fs.existsSync(authFile)) {
    const stats = fs.statSync(authFile);
    const ageMinutes = (Date.now() - stats.mtimeMs) / 60000;
    if (ageMinutes < 30) {
      console.log('Auth state is recent, skipping login...');
      return;
    }
  }

  // иначе логинимся и сохраняем
  // ...
}

Повторные локальные запуски становятся быстрее: шаг логина выполняется только когда сохранённый auth отсутствует или устарел.

→ See also: Авторизация в Playwright через storageState (без логина в каждом тесте) | Фикстуры Playwright: от встроенных до кастомных | Изоляция тестов: почему каждый тест Playwright должен быть stateless