Фикстуры
Введение
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
и предоставить её вашей тестовой функции.
Вот список предопределённых фикстур, которые вы, вероятно, будете использовать чаще всего:
Фикстура | Тип | Описание |
---|---|---|
page | [Page] | Изолированная страница для этого теста. |
context | [BrowserContext] | Изолированный контекст для этого теста. Фикстура page также принадлежит этому контексту. Узнайте, как настроить контекст. |
browser | [Browser] | Браузеры разделяются между тестами для оптимизации ресурсов. Узнайте, как настроить браузер. |
browserName | [string] | Имя браузера, в котором в данный момент выполняется тест. Либо chromium , firefox или webkit . |
request | [APIRequestContext] | Изолированный экземпляр APIRequestContext для этого теста. |
Без фикстур
Вот как типичная настройка тестового окружения отличается между традиционным стилем тестирования и основанным на фикстурах.
TodoPage
— это класс, который помогает взаимодействовать со страницей "списка дел" веб-приложения, следуя паттерну Page Object Model. Он использует page
Playwright внутри.
Нажмите, чтобы развернуть код для TodoPage
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();
}
}
}
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:
- Фикстуры инкапсулируют настройку и завершение в одном месте, что упрощает написание. Поэтому, если у вас есть хук after, который завершает то, что было создано в хуке before, рассмотрите возможность превращения их в фикстуру.
- Фикстуры повторно используемы между тестовыми файлами - вы можете определить их один раз и использовать во всех ваших тестах. Так работает встроенная фикстура
page
в Playwright. Поэтому, если у вас есть вспомогательная функция, которая используется в нескольких тестах, рассмотрите возможность превращения её в фикстуру. - Фикстуры по требованию - вы можете определить столько фикстур, сколько хотите, и Playwright Test настроит только те, которые нужны вашему тесту, и ничего больше.
- Фикстуры компонуемы - они могут зависеть друг от друга для предоставления сложного поведения.
- Фикстуры гибкие. Тесты могут использовать любые комбинации фикстур для точной настройки окружения, которое им нужно, не влияя на другие тесты.
- Фикстуры упрощают группировку. Вам больше не нужно оборачивать тесты в
describe
, которые настраивают окружение, и вы можете свободно группировать ваши тесты по их смыслу.
Нажмите, чтобы развернуть код для TodoPage
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();
}
}
}
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
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 аналогичен:
import type { Page } from '@playwright/test';
export class SettingsPage {
constructor(public readonly page: Page) {
}
async switchToDarkMode() {
// ...
}
}
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().
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.
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], который доступен в каждом тесте/фикстуре для получения метаданных о выполняемом тесте.
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, удобно иметь отдельный таймаут. Таким образом, вы можете держать общий таймаут теста небольшим и дать медленной фикстуре больше времени.
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 поддерживает выполнение нескольких тестовых проектов, которые могут быть настроены отдельно. Вы можете использовать "опционные" фикстуры, чтобы сделать ваши параметры конфигурации декларативными и проверяемыми на типы. Узнайте больше о параметризации тестов.
Ниже мы создадим опцию defaultItem
в дополнение к фикстуре todoPage
из других примеров. Эта опция будет установлена в файле конфигурации. Обратите внимание на синтаксис кортежа и аргумент { option: true }
.
Нажмите, чтобы развернуть код для TodoPage
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();
}
}
}
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
в файле конфигурации.
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,
});
Порядок выполнения
Каждая фикстура имеет фазу настройки и завершения, разделённые вызовом await use()
в фикстуре. Настройка выполняется перед использованием фикстуры тестом/хуком, а завершение выполняется, когда фикстура больше не будет использоваться тестом/хуком.
Фикстуры следуют этим правилам для определения порядка выполнения:
- Когда фикстура A зависит от фикстуры B: B всегда настраивается перед A и завершается после A.
- Неавтоматические фикстуры выполняются лениво, только когда тест/хук нуждается в них.
- Фикстуры с областью действия "test" завершаются после каждого теста, в то время как фикстуры с областью действия "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
нет.
Объединение пользовательских фикстур из нескольких модулей
Вы можете объединять тестовые фикстуры из нескольких файлов или модулей:
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);
import { test } from './fixtures';
test('passes', async ({ database, page, a11y }) => {
// используйте фикстуры database и a11y.
});
Фикстуры-коробки
Обычно пользовательские фикстуры отображаются как отдельные шаги в UI-режиме, Trace Viewer и различных тестовых отчётах. Они также появляются в сообщениях об ошибках от тестового раннера. Для часто используемых фикстур это может означать много шума. Вы можете остановить отображение шагов фикстур в UI, "упаковав" их.
import { test as base } from '@playwright/test';
export const test = base.extend({
helperFixture: [async ({}, use, testInfo) => {
// ...
}, { box: true }],
});
Это полезно для неинтересных вспомогательных фикстур. Например, автоматическая фикстура, которая настраивает некоторые общие данные, может быть безопасно скрыта из тестового отчёта.
Пользовательское название фикстуры
Вместо обычного имени фикстуры вы можете дать фикстурам пользовательское название, которое будет отображаться в тестовых отчётах и сообщениях об ошибках.
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() (если он есть). Если вы хотите объявить хуки, которые выполняются перед/после каждого теста глобально, вы можете объявить их как автоматические фикстуры следующим образом:
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 }], // автоматически запускается для каждого теста.
});
А затем импортируйте фикстуры во всех ваших тестах:
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'
следующим образом:
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.
});
А затем импортируйте фикстуры во всех ваших тестах:
import { test } from './fixtures';
import { expect } from '@playwright/test';
test('basic', async ({ }) => {
// ...
});
Обратите внимание, что фикстуры всё равно будут выполняться один раз на процесс worker, но вам не нужно переопределять их в каждом файле.