Перейти к основному содержимому

Фикстуры

Введение

Playwright Test основан на концепции тестовых фикстур. Тестовые фикстуры используются для установления окружения для каждого теста, предоставляя тесту всё необходимое и ничего лишнего. Тестовые фикстуры изолированы между тестами. С помощью фикстур вы можете группировать тесты на основе их смысла, а не их общей настройки.

Встроенные фикстуры

Вы уже использовали тестовые фикстуры в вашем первом тесте.

import { test, expect } from '@playwright/test';

test('basic test', async ({ page }) => {
await page.goto('https://playwright.dev/');

await expect(page).toHaveTitle(/Playwright/);
});

Аргумент { page } говорит Playwright Test настроить фикстуру page и передать её в вашу тестовую функцию.

Вот список предопределённых фикстур, которые вы, вероятно, будете использовать чаще всего:

ФикстураТипОписание
pagePageИзолированная страница для этого запуска теста.
contextBrowserContextИзолированный контекст для этого запуска теста. Фикстура page также принадлежит этому контексту. Узнайте, как настроить контекст.
browserBrowserБраузеры переиспользуются между тестами, чтобы оптимизировать ресурсы. Узнайте, как настроить браузеры.
browserNamestringИмя браузера, в котором сейчас выполняется тест: chromium, firefox или webkit.
requestAPIRequestContextИзолированный экземпляр APIRequestContext для этого запуска теста.

Без фикстур

Вот чем обычно отличается настройка тестового окружения в традиционном стиле тестов и в стиле на основе фикстур.

TodoPage — это класс, который помогает нам взаимодействовать со страницей «todo list» веб‑приложения, следуя паттерну Page Object Model. Внутри он использует Playwright page.

Нажмите, чтобы развернуть код для TodoPage
todo-page.ts
import type { Page, Locator } from '@playwright/test';

export class TodoPage {
private readonly inputBox: Locator;
private readonly todoItems: Locator;

constructor(public readonly page: Page) {
this.inputBox = this.page.locator('input.new-todo');
this.todoItems = this.page.getByTestId('todo-item');
}

async goto() {
await this.page.goto('https://demo.playwright.dev/todomvc/');
}

async addToDo(text: string) {
await this.inputBox.fill(text);
await this.inputBox.press('Enter');
}

async remove(text: string) {
const todo = this.todoItems.filter({ hasText: text });
await todo.hover();
await todo.getByLabel('Delete').click();
}

async removeAll() {
while ((await this.todoItems.count()) > 0) {
await this.todoItems.first().hover();
await this.todoItems.getByLabel('Delete').first().click();
}
}
}
todo.spec.ts
const { test } = require('@playwright/test');
const { TodoPage } = require('./todo-page');

test.describe('todo tests', () => {
let todoPage;

test.beforeEach(async ({ page }) => {
todoPage = new TodoPage(page);
await todoPage.goto();
await todoPage.addToDo('item1');
await todoPage.addToDo('item2');
});

test.afterEach(async () => {
await todoPage.removeAll();
});

test('should add an item', async () => {
await todoPage.addToDo('my item');
// ...
});

test('should remove an item', async () => {
await todoPage.remove('item1');
// ...
});
});

С фикстурами

У фикстур есть ряд преимуществ по сравнению с before/after хуками:

  • Фикстуры инкапсулируют setup и teardown в одном месте, поэтому их проще писать. Поэтому если у вас есть after-хук, который очищает то, что было создано в before-хуке, подумайте о том, чтобы превратить их в фикстуру.
  • Фикстуры переиспользуемы между файлами тестов: вы можете определить их один раз и использовать во всех тестах. Так работает встроенная фикстура Playwright page. Поэтому если у вас есть вспомогательная функция, которая используется в нескольких тестах, подумайте о том, чтобы превратить её в фикстуру.
  • Фикстуры по требованию: вы можете определить сколько угодно фикстур, а Playwright Test настроит только те, которые нужны вашему тесту, и ничего лишнего.
  • Фикстуры компонуемы: они могут зависеть друг от друга, обеспечивая сложное поведение.
  • Фикстуры гибкие. Тесты могут использовать любую комбинацию фикстур, чтобы точно настроить окружение под свои нужды, не влияя на другие тесты.
  • Фикстуры упрощают группировку. Вам больше не нужно оборачивать тесты в describe, который настраивает окружение, и вы можете группировать тесты по их смыслу.
Нажмите, чтобы развернуть код для TodoPage
todo-page.ts
import type { Page, Locator } from '@playwright/test';

export class TodoPage {
private readonly inputBox: Locator;
private readonly todoItems: Locator;

constructor(public readonly page: Page) {
this.inputBox = this.page.locator('input.new-todo');
this.todoItems = this.page.getByTestId('todo-item');
}

async goto() {
await this.page.goto('https://demo.playwright.dev/todomvc/');
}

async addToDo(text: string) {
await this.inputBox.fill(text);
await this.inputBox.press('Enter');
}

async remove(text: string) {
const todo = this.todoItems.filter({ hasText: text });
await todo.hover();
await todo.getByLabel('Delete').click();
}

async removeAll() {
while ((await this.todoItems.count()) > 0) {
await this.todoItems.first().hover();
await this.todoItems.getByLabel('Delete').first().click();
}
}
}
example.spec.ts
import { test as base } from '@playwright/test';
import { TodoPage } from './todo-page';

// Расширяем базовый тест, предоставляя фикстуру "todoPage".
const test = base.extend<{ todoPage: TodoPage }>({
todoPage: async ({ page }, use) => {
const todoPage = new TodoPage(page);
await todoPage.goto();
await todoPage.addToDo('item1');
await todoPage.addToDo('item2');
await use(todoPage);
await todoPage.removeAll();
},
});

test('should add an item', async ({ todoPage }) => {
await todoPage.addToDo('my item');
// ...
});

test('should remove an item', async ({ todoPage }) => {
await todoPage.remove('item1');
// ...
});

Создание фикстуры

Чтобы создать свою собственную фикстуру, используйте test.extend() для создания нового объекта test, который будет её включать.

Ниже мы создаём две фикстуры todoPage и settingsPage, которые следуют паттерну Page Object Model.

Нажмите, чтобы развернуть код для TodoPage и SettingsPage
todo-page.ts
import type { Page, Locator } from '@playwright/test';

export class TodoPage {
private readonly inputBox: Locator;
private readonly todoItems: Locator;

constructor(public readonly page: Page) {
this.inputBox = this.page.locator('input.new-todo');
this.todoItems = this.page.getByTestId('todo-item');
}

async goto() {
await this.page.goto('https://demo.playwright.dev/todomvc/');
}

async addToDo(text: string) {
await this.inputBox.fill(text);
await this.inputBox.press('Enter');
}

async remove(text: string) {
const todo = this.todoItems.filter({ hasText: text });
await todo.hover();
await todo.getByLabel('Delete').click();
}

async removeAll() {
while ((await this.todoItems.count()) > 0) {
await this.todoItems.first().hover();
await this.todoItems.getByLabel('Delete').first().click();
}
}
}

SettingsPage аналогичен:

settings-page.ts
import type { Page } from '@playwright/test';

export class SettingsPage {
constructor(public readonly page: Page) {
}

async switchToDarkMode() {
// ...
}
}
my-test.ts
import { test as base } from '@playwright/test';
import { TodoPage } from './todo-page';
import { SettingsPage } from './settings-page';

// Объявляем типы ваших фикстур.
type MyFixtures = {
todoPage: TodoPage;
settingsPage: SettingsPage;
};

// Расширяем базовый тест, предоставляя "todoPage" и "settingsPage".
// Этот новый "test" может быть использован в нескольких тестовых файлах, и каждый из них получит фикстуры.
export const test = base.extend<MyFixtures>({
todoPage: async ({ page }, use) => {
// Настраиваем фикстуру.
const todoPage = new TodoPage(page);
await todoPage.goto();
await todoPage.addToDo('item1');
await todoPage.addToDo('item2');

// Используем значение фикстуры в тесте.
await use(todoPage);

// Очищаем фикстуру.
await todoPage.removeAll();
},

settingsPage: async ({ page }, use) => {
await use(new SettingsPage(page));
},
});
export { expect } from '@playwright/test';
примечание

Имена пользовательских фикстур должны начинаться с буквы или подчёркивания и могут содержать только буквы, цифры и подчёркивания.

Использование фикстуры

Просто укажите фикстуру в аргументах вашей тестовой функции — раннер тестов сам обо всём позаботится. Фикстуры также доступны в хуках и других фикстурах. Если вы используете TypeScript, фикстуры будут типобезопасными.

Ниже мы используем фикстуры todoPage и settingsPage, которые определили выше.

import { test, expect } from './my-test';

test.beforeEach(async ({ settingsPage }) => {
await settingsPage.switchToDarkMode();
});

test('basic test', async ({ todoPage, page }) => {
await todoPage.addToDo('something nice');
await expect(page.getByTestId('todo-title')).toContainText(['something nice']);
});

Переопределение фикстур

Помимо создания собственных фикстур, вы также можете переопределять существующие фикстуры под свои нужды. Рассмотрим пример, который переопределяет фикстуру page, автоматически переходя на baseURL:

import { test as base } from '@playwright/test';

export const test = base.extend({
page: async ({ baseURL, page }, use) => {
await page.goto(baseURL);
await use(page);
},
});

Обратите внимание, что в этом примере фикстура page может зависеть от других встроенных фикстур, таких как testOptions.baseURL. Теперь мы можем настроить baseURL в файле конфигурации или локально в тестовом файле с помощью test.use().

example.spec.ts

test.use({ baseURL: 'https://playwright.dev' });

Фикстуры также можно переопределять так, что базовая фикстура полностью заменяется чем‑то другим. Например, мы могли бы переопределить фикстуру testOptions.storageState, чтобы подставлять свои данные.

import { test as base } from '@playwright/test';

export const test = base.extend({
storageState: async ({}, use) => {
const cookie = await getAuthCookie();
await use({ cookies: [cookie] });
},
});

Фикстуры с областью действия "worker"

Playwright Test использует worker-процессы для запуска файлов тестов. Подобно тому, как тестовые фикстуры настраиваются для каждого отдельного запуска теста, worker-фикстуры настраиваются для каждого worker-процесса. Именно там можно поднимать сервисы, запускать серверы и т. п. Playwright Test будет переиспользовать worker-процесс для максимально возможного числа файлов тестов — при условии, что их worker-фикстуры совпадают, а значит окружения идентичны.

Ниже мы создадим фикстуру account, которая будет общей для всех тестов в одном и том же worker, и переопределим фикстуру page, чтобы для каждого теста выполнять вход в этот аккаунт. Чтобы генерировать уникальные аккаунты, мы используем workerInfo.workerIndex, доступный любому тесту или фикстуре. Обратите внимание на «кортежный» синтаксис для worker-фикстуры: нужно передать {scope: 'worker'}, чтобы раннер настраивал эту фикстуру один раз на worker.

my-test.ts
import { test as base } from '@playwright/test';

type Account = {
username: string;
password: string;
};

// Обратите внимание, что мы передаём типы фикстур worker в качестве второго параметра шаблона.
export const test = base.extend<{}, { account: Account }>({
account: [async ({ browser }, use, workerInfo) => {
// Уникальное имя пользователя.
const username = 'user' + workerInfo.workerIndex;
const password = 'verysecure';

// Создаём аккаунт с помощью Playwright.
const page = await browser.newPage();
await page.goto('/signup');
await page.getByLabel('User Name').fill(username);
await page.getByLabel('Password').fill(password);
await page.getByText('Sign up').click();
// Убедитесь, что всё в порядке.
await expect(page.getByTestId('result')).toHaveText('Success');
// Не забудьте очистить.
await page.close();

// Используем значение аккаунта.
await use({ username, password });
}, { scope: 'worker' }],

page: async ({ page, account }, use) => {
// Входим с нашим аккаунтом.
const { username, password } = account;
await page.goto('/signin');
await page.getByLabel('User Name').fill(username);
await page.getByLabel('Password').fill(password);
await page.getByText('Sign in').click();
await expect(page.getByTestId('userinfo')).toHaveText(username);

// Используем страницу с входом в тесте.
await use(page);
},
});
export { expect } from '@playwright/test';

Автоматические фикстуры

Автоматические фикстуры настраиваются для каждого теста/worker, даже если тест не указывает их напрямую. Чтобы создать автоматическую фикстуру, используйте синтаксис кортежа и передайте { auto: true }.

Вот пример фикстуры, которая автоматически прикрепляет отладочные логи, когда тест падает, чтобы позже можно было посмотреть их в репортере. Обратите внимание, как она использует объект TestInfo, доступный в каждом тесте/фикстуре, чтобы получить метаданные о выполняемом тесте.

my-test.ts
import debug from 'debug';
import fs from 'fs';
import { test as base } from '@playwright/test';

export const test = base.extend<{ saveLogs: void }>({
saveLogs: [async ({}, use, testInfo) => {
// Сбор логов во время теста.
const logs = [];
debug.log = (...args) => logs.push(args.map(String).join(''));
debug.enable('myserver');

await use();

// После теста мы можем проверить, прошёл ли тест или нет.
if (testInfo.status !== testInfo.expectedStatus) {
// API outputPath() гарантирует уникальное имя файла.
const logFile = testInfo.outputPath('logs.txt');
await fs.promises.writeFile(logFile, logs.join('\n'), 'utf8');
testInfo.attachments.push({ name: 'logs', contentType: 'text/plain', path: logFile });
}
}, { auto: true }],
});
export { expect } from '@playwright/test';

Таймаут фикстуры

По умолчанию фикстура наследует значение таймаута теста. Однако для медленных фикстур, особенно worker-scoped, удобно иметь отдельный таймаут. Так вы можете держать общий таймаут теста небольшим и дать медленной фикстуре больше времени.

import { test as base, expect } from '@playwright/test';

const test = base.extend<{ slowFixture: string }>({
slowFixture: [async ({}, use) => {
// ... выполняем медленную операцию ...
await use('hello');
}, { timeout: 60000 }]
});

test('example test', async ({ slowFixture }) => {
// ...
});

Фикстуры-опции

Playwright Test поддерживает запуск нескольких тестовых проектов, которые можно настраивать отдельно. Вы можете использовать фикстуры-«опции» (option fixtures), чтобы сделать параметры конфигурации декларативными и типобезопасными. Подробнее см. параметризация тестов.

Ниже мы создадим опцию defaultItem в дополнение к фикстуре todoPage из других примеров. Эта опция будет задаваться в файле конфигурации. Обратите внимание на кортежный синтаксис и аргумент { option: true }.

Нажмите, чтобы развернуть код для TodoPage
todo-page.ts
import type { Page, Locator } from '@playwright/test';

export class TodoPage {
private readonly inputBox: Locator;
private readonly todoItems: Locator;

constructor(public readonly page: Page) {
this.inputBox = this.page.locator('input.new-todo');
this.todoItems = this.page.getByTestId('todo-item');
}

async goto() {
await this.page.goto('https://demo.playwright.dev/todomvc/');
}

async addToDo(text: string) {
await this.inputBox.fill(text);
await this.inputBox.press('Enter');
}

async remove(text: string) {
const todo = this.todoItems.filter({ hasText: text });
await todo.hover();
await todo.getByLabel('Delete').click();
}

async removeAll() {
while ((await this.todoItems.count()) > 0) {
await this.todoItems.first().hover();
await this.todoItems.getByLabel('Delete').first().click();
}
}
}
my-test.ts
import { test as base } from '@playwright/test';
import { TodoPage } from './todo-page';

// Объявляем ваши опции для проверки типов вашей конфигурации.
export type MyOptions = {
defaultItem: string;
};
type MyFixtures = {
todoPage: TodoPage;
};

// Указываем как опции, так и типы фикстур.
export const test = base.extend<MyOptions & MyFixtures>({
// Определяем опцию и предоставляем значение по умолчанию.
// Мы можем позже переопределить её в конфигурации.
defaultItem: ['Something nice', { option: true }],

// Наша фикстура "todoPage" зависит от опции.
todoPage: async ({ page, defaultItem }, use) => {
const todoPage = new TodoPage(page);
await todoPage.goto();
await todoPage.addToDo(defaultItem);
await use(todoPage);
await todoPage.removeAll();
},
});
export { expect } from '@playwright/test';

Теперь мы можем использовать фикстуру todoPage как обычно и задавать опцию defaultItem в файле конфигурации.

playwright.config.ts
import { defineConfig } from '@playwright/test';
import type { MyOptions } from './my-test';

export default defineConfig<MyOptions>({
projects: [
{
name: 'shopping',
use: { defaultItem: 'Buy milk' },
},
{
name: 'wellbeing',
use: { defaultItem: 'Exercise!' },
},
]
});

Массив как значение опции

Если значение вашей опции является массивом, например [{ name: 'Alice' }, { name: 'Bob' }], вам нужно будет обернуть его в дополнительный массив при предоставлении значения. Это лучше всего иллюстрируется примером.

type Person = { name: string };
const test = base.extend<{ persons: Person[] }>({
// Объявляем опцию, значение по умолчанию - пустой массив.
persons: [[], { option: true }],
});

// Значение опции - массив персон.
const actualPersons = [{ name: 'Alice' }, { name: 'Bob' }];
test.use({
// ПРАВИЛЬНО: Оберните значение в массив и передайте область.
persons: [actualPersons, { scope: 'test' }],
});

test.use({
// НЕПРАВИЛЬНО: передача значения массива напрямую не сработает.
persons: actualPersons,
});

Сбросить опцию

Вы можете сбросить опцию к значению, заданному в файле конфигурации, установив её в undefined. Рассмотрим следующий конфиг, который задаёт baseURL:

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

export default defineConfig({
use: {
baseURL: 'https://playwright.dev',
},
});

Теперь вы можете настроить baseURL для файла, а также отключить это для одного конкретного теста.

intro.spec.ts
import { test } from '@playwright/test';

// Настроить baseURL для этого файла.
test.use({ baseURL: 'https://playwright.dev/docs/intro' });

test('check intro contents', async ({ page }) => {
// Этот тест будет использовать baseURL "https://playwright.dev/docs/intro", заданный выше.
});

test.describe(() => {
// Сбросить значение к заданному в конфиге.
test.use({ baseURL: undefined });

test('can navigate to intro from the home page', async ({ page }) => {
// Этот тест будет использовать baseURL "https://playwright.dev", заданный в конфиге.
});
});

Если вы хотите полностью сбросить значение до undefined, используйте длинную форму (long-form) нотации фикстур.

intro.spec.ts
import { test } from '@playwright/test';

// Полностью убрать baseURL для этого файла.
test.use({
baseURL: [async ({}, use) => use(undefined), { scope: 'test' }],
});

test('no base url', async ({ page }) => {
// У этого теста не будет baseURL.
});

Порядок выполнения

У каждой фикстуры есть этапы setup и teardown — до и после вызова await use() внутри фикстуры. Setup выполняется до запуска теста/хука, которому она нужна, а teardown выполняется, когда фикстура больше не используется тестом/хуком.

Фикстуры следуют таким правилам, определяя порядок выполнения:

  • Если фикстура A зависит от фикстуры B, то B всегда настраивается (setup) перед A и освобождается (teardown) после A.
  • Неавтоматические фикстуры выполняются лениво — только когда они нужны тесту/хуку.
  • Фикстуры со scope test освобождаются после каждого теста, а фикстуры со scope worker освобождаются только при завершении worker-процесса, выполняющего тесты.

Рассмотрим следующий пример:

import { test as base } from '@playwright/test';

const test = base.extend<{
testFixture: string,
autoTestFixture: string,
unusedFixture: string,
}, {
workerFixture: string,
autoWorkerFixture: string,
}>({
workerFixture: [async ({ browser }) => {
// workerFixture настройка...
await use('workerFixture');
// workerFixture завершение...
}, { scope: 'worker' }],

autoWorkerFixture: [async ({ browser }) => {
// autoWorkerFixture настройка...
await use('autoWorkerFixture');
// autoWorkerFixture завершение...
}, { scope: 'worker', auto: true }],

testFixture: [async ({ page, workerFixture }) => {
// testFixture настройка...
await use('testFixture');
// testFixture завершение...
}, { scope: 'test' }],

autoTestFixture: [async () => {
// autoTestFixture настройка...
await use('autoTestFixture');
// autoTestFixture завершение...
}, { scope: 'test', auto: true }],

unusedFixture: [async ({ page }) => {
// unusedFixture настройка...
await use('unusedFixture');
// unusedFixture завершение...
}, { scope: 'test' }],
});

test.beforeAll(async () => { /* ... */ });
test.beforeEach(async ({ page }) => { /* ... */ });
test('first test', async ({ page }) => { /* ... */ });
test('second test', async ({ testFixture }) => { /* ... */ });
test.afterEach(async () => { /* ... */ });
test.afterAll(async () => { /* ... */ });

Обычно, если все тесты проходят и ошибки не выбрасываются, порядок выполнения следующий.

  • настройка worker и секция beforeAll:
    • настройка browser, потому что он требуется для autoWorkerFixture.
    • настройка autoWorkerFixture, потому что автоматические фикстуры worker всегда настраиваются перед чем-либо ещё.
    • выполняется beforeAll.
  • секция first test:
    • настройка autoTestFixture, потому что автоматические фикстуры теста всегда настраиваются перед тестом и хуками beforeEach.
    • настройка page, потому что он требуется в хуке beforeEach.
    • выполняется beforeEach.
    • выполняется first test.
    • выполняется afterEach.
    • завершение page, потому что это фикстура с областью действия "test" и должна быть завершена после завершения теста.
    • завершение autoTestFixture, потому что это фикстура с областью действия "test" и должна быть завершена после завершения теста.
  • секция second test:
    • настройка autoTestFixture, потому что автоматические фикстуры теста всегда настраиваются перед тестом и хуками beforeEach.
    • настройка page, потому что он требуется в хуке beforeEach.
    • выполняется beforeEach.
    • настройка workerFixture, потому что он требуется для testFixture, который требуется для second test.
    • настройка testFixture, потому что он требуется для second test.
    • выполняется second test.
    • выполняется afterEach.
    • завершение testFixture, потому что это фикстура с областью действия "test" и должна быть завершена после завершения теста.
    • завершение page, потому что это фикстура с областью действия "test" и должна быть завершена после завершения теста.
    • завершение autoTestFixture, потому что это фикстура с областью действия "test" и должна быть завершена после завершения теста.
  • секция afterAll и завершение worker:
    • выполняется afterAll.
    • завершение workerFixture, потому что это фикстура с областью действия "worker" и должна быть завершена один раз в конце.
    • завершение autoWorkerFixture, потому что это фикстура с областью действия "worker" и должна быть завершена один раз в конце.
    • завершение browser, потому что это фикстура с областью действия "worker" и должна быть завершена один раз в конце.

Несколько наблюдений:

  • page и autoTestFixture настраиваются и завершаются для каждого теста, как фикстуры с областью действия "test".
  • unusedFixture никогда не настраивается, потому что он не используется ни одним тестом/хуком.
  • testFixture зависит от workerFixture и вызывает его настройку.
  • workerFixture лениво настраивается перед вторым тестом, но завершается один раз при завершении worker, как фикстура с областью действия "worker".
  • autoWorkerFixture настраивается для хука beforeAll, но autoTestFixture нет.

Объединение пользовательских фикстур из нескольких модулей

Вы можете объединять тестовые фикстуры из нескольких файлов или модулей:

fixtures.ts
import { mergeTests } from '@playwright/test';
import { test as dbTest } from 'database-test-utils';
import { test as a11yTest } from 'a11y-test-utils';

export const test = mergeTests(dbTest, a11yTest);
test.spec.ts
import { test } from './fixtures';

test('passes', async ({ database, page, a11y }) => {
// используйте фикстуры database и a11y.
});

Фикстуры-коробки

Обычно пользовательские фикстуры отображаются как отдельные шаги в UI mode, Trace Viewer и разных тестовых отчётах. Они также появляются в сообщениях об ошибках раннера. Для часто используемых фикстур это может создавать много «шума». Можно скрыть шаги фикстур в UI, «упаковав» (boxing) фикстуру.

import { test as base } from '@playwright/test';

export const test = base.extend({
helperFixture: [async ({}, use, testInfo) => {
// ...
}, { box: true }],
});

Это полезно для неинтересных вспомогательных фикстур. Например, автоматическая фикстура, которая настраивает некоторые общие данные, может быть безопасно скрыта из тестового отчёта.

Также можно пометить фикстуру как box: 'self', чтобы скрыть только эту конкретную фикстуру, но при этом включить все шаги внутри фикстуры в отчёт о тесте.

Пользовательский заголовок фикстуры

Вместо обычного имени фикстуры вы можете дать фикстурам пользовательское название, которое будет отображаться в тестовых отчётах и сообщениях об ошибках.

import { test as base } from '@playwright/test';

export const test = base.extend({
innerFixture: [async ({}, use, testInfo) => {
// ...
}, { title: 'my fixture' }],
});

Добавление глобальных хуков beforeEach/afterEach

Хуки test.beforeEach() и test.afterEach() выполняются перед/после каждого теста, объявленного в том же файле и в том же блоке test.describe() (если он есть). Если вы хотите объявить хуки, которые выполняются перед/после каждого теста глобально, вы можете объявить их как автоматические фикстуры следующим образом:

fixtures.ts
import { test as base } from '@playwright/test';

export const test = base.extend<{ forEachTest: void }>({
forEachTest: [async ({ page }, use) => {
// Этот код выполняется перед каждым тестом.
await page.goto('http://localhost:8000');
await use();
// Этот код выполняется после каждого теста.
console.log('Last URL:', page.url());
}, { auto: true }], // автоматически запускается для каждого теста.
});

А затем импортируйте фикстуры во всех ваших тестах:

mytest.spec.ts
import { test } from './fixtures';
import { expect } from '@playwright/test';

test('basic', async ({ page }) => {
expect(page).toHaveURL('http://localhost:8000');
await page.goto('https://playwright.dev');
});

Добавление глобальных хуков beforeAll/afterAll

Хуки test.beforeAll() и test.afterAll() выполняются перед/после всех тестов, объявленных в том же файле и в том же блоке test.describe() (если он есть), один раз на процесс worker. Если вы хотите объявить хуки, которые выполняются перед/после всех тестов в каждом файле, вы можете объявить их как автоматические фикстуры с scope: 'worker' следующим образом:

fixtures.ts
import { test as base } from '@playwright/test';

export const test = base.extend<{}, { forEachWorker: void }>({
forEachWorker: [async ({}, use) => {
// Этот код выполняется перед всеми тестами в процессе worker.
console.log(`Starting test worker ${test.info().workerIndex}`);
await use();
// Этот код выполняется после всех тестов в процессе worker.
console.log(`Stopping test worker ${test.info().workerIndex}`);
}, { scope: 'worker', auto: true }], // автоматически запускается для каждого worker.
});

А затем импортируйте фикстуры во всех ваших тестах:

mytest.spec.ts
import { test } from './fixtures';
import { expect } from '@playwright/test';

test('basic', async ({ }) => {
// ...
});

Обратите внимание, что фикстуры всё равно будут выполняться один раз на процесс worker, но вам не нужно переопределять их в каждом файле.