Фикстуры
Введение
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 — это класс, который помогает нам взаимодействовать со страницей «todo list» веб‑приложения, следуя паттерну Page Object Model. Внутри он использует Playwright page.
Нажмите, чтобы развернуть код для 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 хуками:
- Фикстуры инкапсулируют setup и teardown в одном месте, поэтому их проще писать. Поэтому если у вас есть after-хук, который очищает то, что было создано в before-хуке, подумайте о том, чтобы превратить их в фикстуру.
- Фикстуры переиспользуемы между файлами тестов: вы можете определить их один раз и использовать во всех тестах. Так работает встроенная фикстура Playwright
page. Поэтому если у вас есть вспомогательная функция, которая используется в нескольких тестах, подумайте о том, чтобы превратить её в фикстуру. - Фикстуры по требованию: вы можете определить сколько угодно фикстур, а 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-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
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,
});
Сбросить опцию
Вы можете сбросить опцию к значению, заданному в файле конфигурации, установив её в undefined. Рассмотрим следующий конфиг, который задаёт baseURL:
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
baseURL: 'https://playwright.dev',
},
});
Теперь вы можете настроить baseURL для файла, а также отключить это для одного конкретного теста.
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) нотации фикстур.
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освобождаются после каждого теста, а фикстуры со scopeworkerосвобождаются только при завершении 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 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() (если он есть). Если вы хотите объявить хуки, которые выполняются перед/после каждого теста глобально, вы можете объявить их как автоматические фикстуры следующим образом:
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, но вам не нужно переопределять их в каждом файле.