feat: add title for before and after hooks (#26523)

This commit is contained in:
Richard Blažo 2023-08-21 18:50:22 +02:00 committed by GitHub
parent 2f6148bcd1
commit bcc30bc71e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 372 additions and 34 deletions

View File

@ -137,7 +137,7 @@ test.describe('Playwright homepage', () => {
1. Each Playwright Test file has explicit import of the `test` and `expect` functions 1. Each Playwright Test file has explicit import of the `test` and `expect` functions
1. Test function is marked with `async` 1. Test function is marked with `async`
1. Playwright Test is given a `page` as one of its parameters. This is one of the many [useful fixtures](./api/class-fixtures) in Playwright Test. 1. Playwright Test is given a `page` as one of its parameters. This is one of the many [useful fixtures](./api/class-fixtures) in Playwright Test.
Playwright Test creates an isolated [Page] object for each test. However, if you'd like to reuse a single [Page] object between multiple tests, you can create your own in [`method: Test.beforeAll`] and close it in [`method: Test.afterAll`]. Playwright Test creates an isolated [Page] object for each test. However, if you'd like to reuse a single [Page] object between multiple tests, you can create your own in [`method: Test.beforeAll#1`] and close it in [`method: Test.afterAll#1`].
1. Locator creation with [`method: Page.locator`] is one of the few methods that is sync. 1. Locator creation with [`method: Page.locator`] is one of the few methods that is sync.
1. Use [assertions](./test-assertions) to verify the state instead of `page.$eval()`. 1. Use [assertions](./test-assertions) to verify the state instead of `page.$eval()`.

View File

@ -44,7 +44,7 @@ Test function that takes one or two arguments: an object with fixtures and optio
## method: Test.afterAll ## method: Test.afterAll#1
* since: v1.10 * since: v1.10
Declares an `afterAll` hook that is executed once per worker after all tests. Declares an `afterAll` hook that is executed once per worker after all tests.
@ -64,15 +64,42 @@ test.afterAll(async () => {
}); });
``` ```
### param: Test.afterAll.hookFunction ### param: Test.afterAll#1.hookFunction
* since: v1.10 * since: v1.10
- `hookFunction` <[function]\([Fixtures], [TestInfo]\)> - `hookFunction` <[function]\([Fixtures], [TestInfo]\)>
Hook function that takes one or two arguments: an object with worker fixtures and optional [TestInfo]. Hook function that takes one or two arguments: an object with worker fixtures and optional [TestInfo].
## method: Test.afterAll#2
* since: v1.38
## method: Test.afterEach Declares an `afterAll` hook with a title that is executed once per worker after all tests.
**Usage**
```js
test.afterAll('Teardown', async () => {
console.log('Done with tests');
// ...
});
```
### param: Test.afterAll#2.title
* since: v1.38
- `title` <[string]>
Hook title.
### param: Test.afterAll#2.hookFunction
* since: v1.38
- `hookFunction` <[function]\([Fixtures], [TestInfo]\)>
Hook function that takes one or two arguments: an object with worker fixtures and optional [TestInfo].
## method: Test.afterEach#1
* since: v1.10 * since: v1.10
Declares an `afterEach` hook that is executed after each test. Declares an `afterEach` hook that is executed after each test.
@ -100,14 +127,50 @@ test('my test', async ({ page }) => {
}); });
``` ```
### param: Test.afterEach.hookFunction ### param: Test.afterEach#1.hookFunction
* since: v1.10 * since: v1.10
- `hookFunction` <[function]\([Fixtures], [TestInfo]\)> - `hookFunction` <[function]\([Fixtures], [TestInfo]\)>
Hook function that takes one or two arguments: an object with fixtures and optional [TestInfo]. Hook function that takes one or two arguments: an object with fixtures and optional [TestInfo].
## method: Test.beforeAll ## method: Test.afterEach#2
* since: v1.38
Declares an `afterEach` hook with a title that is executed after each test.
**Usage**
```js title="example.spec.ts"
import { test, expect } from '@playwright/test';
test.afterEach('Status check', async ({ page }, testInfo) => {
console.log(`Finished ${testInfo.title} with status ${testInfo.status}`);
if (testInfo.status !== testInfo.expectedStatus)
console.log(`Did not run as expected, ended up at ${page.url()}`);
});
test('my test', async ({ page }) => {
// ...
});
```
### param: Test.afterEach#2.title
* since: v1.38
- `title` <[string]>
Hook title.
### param: Test.afterEach#2.hookFunction
* since: v1.38
- `hookFunction` <[function]\([Fixtures], [TestInfo]\)>
Hook function that takes one or two arguments: an object with fixtures and optional [TestInfo].
## method: Test.beforeAll#1
* since: v1.10 * since: v1.10
Declares a `beforeAll` hook that is executed once per worker process before all tests. Declares a `beforeAll` hook that is executed once per worker process before all tests.
@ -118,7 +181,7 @@ When called in the scope of a test file, runs before all tests in the file. When
Note that worker process is restarted on test failures, and `beforeAll` hook runs again in the new worker. Learn more about [workers and failures](../test-retries.md). Note that worker process is restarted on test failures, and `beforeAll` hook runs again in the new worker. Learn more about [workers and failures](../test-retries.md).
You can use [`method: Test.afterAll`] to teardown any resources set up in `beforeAll`. You can use [`method: Test.afterAll#1`] to teardown any resources set up in `beforeAll`.
**Usage** **Usage**
@ -138,15 +201,47 @@ test('my test', async ({ page }) => {
}); });
``` ```
### param: Test.beforeAll.hookFunction ### param: Test.beforeAll#1.hookFunction
* since: v1.10 * since: v1.10
- `hookFunction` <[function]\([Fixtures], [TestInfo]\)> - `hookFunction` <[function]\([Fixtures], [TestInfo]\)>
Hook function that takes one or two arguments: an object with worker fixtures and optional [TestInfo]. Hook function that takes one or two arguments: an object with worker fixtures and optional [TestInfo].
## method: Test.beforeAll#2
* since: v1.38
## method: Test.beforeEach Declares a `beforeAll` hook with a title that is executed once per worker process before all tests.
**Usage**
```js title="example.spec.ts"
import { test, expect } from '@playwright/test';
test.beforeAll('Setup', async () => {
console.log('Before tests');
});
test('my test', async ({ page }) => {
// ...
});
```
### param: Test.beforeAll#2.title
* since: v1.38
- `title` <[string]>
Hook title.
### param: Test.beforeAll#2.hookFunction
* since: v1.38
- `hookFunction` <[function]\([Fixtures], [TestInfo]\)>
Hook function that takes one or two arguments: an object with worker fixtures and optional [TestInfo].
## method: Test.beforeEach#1
* since: v1.10 * since: v1.10
Declares a `beforeEach` hook that is executed before each test. Declares a `beforeEach` hook that is executed before each test.
@ -157,7 +252,7 @@ When called in the scope of a test file, runs before each test in the file. When
You can access all the same [Fixtures] as the test function itself, and also the [TestInfo] object that gives a lot of useful information. For example, you can navigate the page before starting the test. You can access all the same [Fixtures] as the test function itself, and also the [TestInfo] object that gives a lot of useful information. For example, you can navigate the page before starting the test.
You can use [`method: Test.afterEach`] to teardown any resources set up in `beforeEach`. You can use [`method: Test.afterEach#1`] to teardown any resources set up in `beforeEach`.
**Usage** **Usage**
@ -174,13 +269,45 @@ test('my test', async ({ page }) => {
}); });
``` ```
### param: Test.beforeEach.hookFunction ### param: Test.beforeEach#1.hookFunction
* since: v1.10 * since: v1.10
- `hookFunction` <[function]\([Fixtures], [TestInfo]\)> - `hookFunction` <[function]\([Fixtures], [TestInfo]\)>
Hook function that takes one or two arguments: an object with fixtures and optional [TestInfo]. Hook function that takes one or two arguments: an object with fixtures and optional [TestInfo].
## method: Test.beforeEach#2
* since: v1.38
Declares a `beforeEach` hook with a title that is executed before each test.
**Usage**
```js title="example.spec.ts"
import { test, expect } from '@playwright/test';
test.beforeEach('Open start URL', async ({ page }, testInfo) => {
console.log(`Running ${testInfo.title}`);
await page.goto('https://my.start.url/');
});
test('my test', async ({ page }) => {
expect(page.url()).toBe('https://my.start.url/');
});
```
### param: Test.beforeEach#2.title
* since: v1.38
- `title` <[string]>
Hook title.
### param: Test.beforeEach#2.hookFunction
* since: v1.38
- `hookFunction` <[function]\([Fixtures], [TestInfo]\)>
Hook function that takes one or two arguments: an object with fixtures and optional [TestInfo].
## method: Test.describe#1 ## method: Test.describe#1
@ -1057,7 +1184,7 @@ test('skip in WebKit', async ({ page, browserName }) => {
}); });
``` ```
Skip from [`method: Test.beforeEach`] hook: Skip from [`method: Test.beforeEach#1`] hook:
```js ```js
import { test, expect } from '@playwright/test'; import { test, expect } from '@playwright/test';

View File

@ -2,7 +2,7 @@
* since: v1.10 * since: v1.10
* langs: js * langs: js
`TestInfo` contains information about currently running test. It is available to test functions, [`method: Test.beforeEach`], [`method: Test.afterEach`], [`method: Test.beforeAll`] and [`method: Test.afterAll`] hooks, and test-scoped fixtures. `TestInfo` provides utilities to control test execution: attach files, update test timeout, determine which test is currently running and whether it was retried, etc. `TestInfo` contains information about currently running test. It is available to test functions, [`method: Test.beforeEach#1`], [`method: Test.afterEach#1`], [`method: Test.beforeAll#1`] and [`method: Test.afterAll#1`] hooks, and test-scoped fixtures. `TestInfo` provides utilities to control test execution: attach files, update test timeout, determine which test is currently running and whether it was retried, etc.
```js ```js
import { test, expect } from '@playwright/test'; import { test, expect } from '@playwright/test';
@ -115,7 +115,7 @@ Processed configuration from the [configuration file](../test-configuration.md).
* since: v1.10 * since: v1.10
- type: <[int]> - type: <[int]>
The number of milliseconds the test took to finish. Always zero before the test finishes, either successfully or not. Can be used in [`method: Test.afterEach`] hook. The number of milliseconds the test took to finish. Always zero before the test finishes, either successfully or not. Can be used in [`method: Test.afterEach#1`] hook.
## property: TestInfo.error ## property: TestInfo.error
@ -403,7 +403,7 @@ Suffix used to differentiate snapshots between multiple test configurations. For
* since: v1.10 * since: v1.10
- type: ?<[TestStatus]<"passed"|"failed"|"timedOut"|"skipped"|"interrupted">> - type: ?<[TestStatus]<"passed"|"failed"|"timedOut"|"skipped"|"interrupted">>
Actual status for the currently running test. Available after the test has finished in [`method: Test.afterEach`] hook and fixtures. Actual status for the currently running test. Available after the test has finished in [`method: Test.afterEach#1`] hook and fixtures.
Status is usually compared with the [`property: TestInfo.expectedStatus`]: Status is usually compared with the [`property: TestInfo.expectedStatus`]:

View File

@ -161,7 +161,7 @@ It is usually better to make your tests isolated, so they can be efficiently run
## Reuse single page between tests ## Reuse single page between tests
Playwright Test creates an isolated [Page] object for each test. However, if you'd like to reuse a single [Page] object between multiple tests, you can create your own in [`method: Test.beforeAll`] and close it in [`method: Test.afterAll`]. Playwright Test creates an isolated [Page] object for each test. However, if you'd like to reuse a single [Page] object between multiple tests, you can create your own in [`method: Test.beforeAll#1`] and close it in [`method: Test.afterAll#1`].
```js tab=js-js title="example.spec.js" ```js tab=js-js title="example.spec.js"
// @ts-check // @ts-check

View File

@ -45,7 +45,7 @@ export class Suite extends Base implements SuitePrivate {
parent?: Suite; parent?: Suite;
_use: FixturesWithLocation[] = []; _use: FixturesWithLocation[] = [];
_entries: (Suite | TestCase)[] = []; _entries: (Suite | TestCase)[] = [];
_hooks: { type: 'beforeEach' | 'afterEach' | 'beforeAll' | 'afterAll', fn: Function, location: Location }[] = []; _hooks: { type: 'beforeEach' | 'afterEach' | 'beforeAll' | 'afterAll', fn: Function, title: string, location: Location }[] = [];
_timeout: number | undefined; _timeout: number | undefined;
_retries: number | undefined; _retries: number | undefined;
_staticAnnotations: Annotation[] = []; _staticAnnotations: Annotation[] = [];
@ -187,7 +187,7 @@ export class Suite extends Base implements SuitePrivate {
staticAnnotations: this._staticAnnotations.slice(), staticAnnotations: this._staticAnnotations.slice(),
modifiers: this._modifiers.slice(), modifiers: this._modifiers.slice(),
parallelMode: this._parallelMode, parallelMode: this._parallelMode,
hooks: this._hooks.map(h => ({ type: h.type, location: h.location })), hooks: this._hooks.map(h => ({ type: h.type, location: h.location, title: h.title })),
fileId: this._fileId, fileId: this._fileId,
}; };
} }
@ -202,7 +202,7 @@ export class Suite extends Base implements SuitePrivate {
suite._staticAnnotations = data.staticAnnotations; suite._staticAnnotations = data.staticAnnotations;
suite._modifiers = data.modifiers; suite._modifiers = data.modifiers;
suite._parallelMode = data.parallelMode; suite._parallelMode = data.parallelMode;
suite._hooks = data.hooks.map((h: any) => ({ type: h.type, location: h.location, fn: () => { } })); suite._hooks = data.hooks.map((h: any) => ({ type: h.type, location: h.location, title: h.title, fn: () => { } }));
suite._fileId = data.fileId; suite._fileId = data.fileId;
return suite; return suite;
} }

View File

@ -134,11 +134,16 @@ export class TestTypeImpl {
setCurrentlyLoadingFileSuite(suite); setCurrentlyLoadingFileSuite(suite);
} }
private _hook(name: 'beforeEach' | 'afterEach' | 'beforeAll' | 'afterAll', location: Location, fn: Function) { private _hook(name: 'beforeEach' | 'afterEach' | 'beforeAll' | 'afterAll', location: Location, title: string | Function, fn?: Function) {
const suite = this._currentSuite(location, `test.${name}()`); const suite = this._currentSuite(location, `test.${name}()`);
if (!suite) if (!suite)
return; return;
suite._hooks.push({ type: name, fn, location }); if (typeof title === 'function') {
fn = title;
title = `${name} hook`;
}
suite._hooks.push({ type: name, fn: fn!, title, location });
} }
private _configure(location: Location, options: { mode?: 'default' | 'parallel' | 'serial', retries?: number, timeout?: number }) { private _configure(location: Location, options: { mode?: 'default' | 'parallel' | 'serial', retries?: number, timeout?: number }) {

View File

@ -528,7 +528,7 @@ export class WorkerMain extends ProcessRunner {
testInfo._timeoutManager.setCurrentRunnable({ type: 'beforeAll', location: hook.location, slot: timeSlot }); testInfo._timeoutManager.setCurrentRunnable({ type: 'beforeAll', location: hook.location, slot: timeSlot });
await testInfo._runAsStep({ await testInfo._runAsStep({
category: 'hook', category: 'hook',
title: `${hook.type} hook`, title: `${hook.title}`,
location: hook.location, location: hook.location,
}, async () => { }, async () => {
try { try {
@ -564,7 +564,7 @@ export class WorkerMain extends ProcessRunner {
testInfo._timeoutManager.setCurrentRunnable({ type: 'afterAll', location: hook.location, slot: timeSlot }); testInfo._timeoutManager.setCurrentRunnable({ type: 'afterAll', location: hook.location, slot: timeSlot });
await testInfo._runAsStep({ await testInfo._runAsStep({
category: 'hook', category: 'hook',
title: `${hook.type} hook`, title: `${hook.title}`,
location: hook.location, location: hook.location,
}, async () => { }, async () => {
try { try {
@ -590,7 +590,7 @@ export class WorkerMain extends ProcessRunner {
testInfo._timeoutManager.setCurrentRunnable({ type, location: hook.location, slot: timeSlot }); testInfo._timeoutManager.setCurrentRunnable({ type, location: hook.location, slot: timeSlot });
await testInfo._runAsStep({ await testInfo._runAsStep({
category: 'hook', category: 'hook',
title: `${hook.type} hook`, title: `${hook.title}`,
location: hook.location, location: hook.location,
}, () => this._fixtureRunner.resolveParametersAndRunFunction(hook.fn, testInfo, 'test')); }, () => this._fixtureRunner.resolveParametersAndRunFunction(hook.fn, testInfo, 'test'));
} catch (e) { } catch (e) {

View File

@ -1900,10 +1900,10 @@ export interface WorkerInfo {
/** /**
* `TestInfo` contains information about currently running test. It is available to test functions, * `TestInfo` contains information about currently running test. It is available to test functions,
* [test.beforeEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-before-each), * [test.beforeEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-before-each-1),
* [test.afterEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-each), * [test.afterEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-each-1),
* [test.beforeAll(hookFunction)](https://playwright.dev/docs/api/class-test#test-before-all) and * [test.beforeAll(hookFunction)](https://playwright.dev/docs/api/class-test#test-before-all-1) and
* [test.afterAll(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-all) hooks, and test-scoped * [test.afterAll(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-all-1) hooks, and test-scoped
* fixtures. `TestInfo` provides utilities to control test execution: attach files, update test timeout, determine * fixtures. `TestInfo` provides utilities to control test execution: attach files, update test timeout, determine
* which test is currently running and whether it was retried, etc. * which test is currently running and whether it was retried, etc.
* *
@ -2150,7 +2150,7 @@ export interface TestInfo {
/** /**
* The number of milliseconds the test took to finish. Always zero before the test finishes, either successfully or * The number of milliseconds the test took to finish. Always zero before the test finishes, either successfully or
* not. Can be used in [test.afterEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-each) * not. Can be used in [test.afterEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-each-1)
* hook. * hook.
*/ */
duration: number; duration: number;
@ -2274,7 +2274,7 @@ export interface TestInfo {
/** /**
* Actual status for the currently running test. Available after the test has finished in * Actual status for the currently running test. Available after the test has finished in
* [test.afterEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-each) hook and fixtures. * [test.afterEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-each-1) hook and fixtures.
* *
* Status is usually compared with the * Status is usually compared with the
* [testInfo.expectedStatus](https://playwright.dev/docs/api/class-testinfo#test-info-expected-status): * [testInfo.expectedStatus](https://playwright.dev/docs/api/class-testinfo#test-info-expected-status):
@ -2746,7 +2746,7 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
* }); * });
* ``` * ```
* *
* Skip from [test.beforeEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-before-each) hook: * Skip from [test.beforeEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-before-each-1) hook:
* *
* ```js * ```js
* import { test, expect } from '@playwright/test'; * import { test, expect } from '@playwright/test';
@ -3071,8 +3071,8 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
* You can access all the same {@link Fixtures} as the test function itself, and also the {@link TestInfo} object that * You can access all the same {@link Fixtures} as the test function itself, and also the {@link TestInfo} object that
* gives a lot of useful information. For example, you can navigate the page before starting the test. * gives a lot of useful information. For example, you can navigate the page before starting the test.
* *
* You can use [test.afterEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-each) to teardown * You can use [test.afterEach(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-each-1) to
* any resources set up in `beforeEach`. * teardown any resources set up in `beforeEach`.
* *
* **Usage** * **Usage**
* *
@ -3093,6 +3093,29 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
* @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. * @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}.
*/ */
beforeEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; beforeEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
/**
* Declares a `beforeEach` hook with a title that is executed before each test.
*
* **Usage**
*
* ```js
* // example.spec.ts
* import { test, expect } from '@playwright/test';
*
* test.beforeEach('Open start URL', async ({ page }, testInfo) => {
* console.log(`Running ${testInfo.title}`);
* await page.goto('https://my.start.url/');
* });
*
* test('my test', async ({ page }) => {
* expect(page.url()).toBe('https://my.start.url/');
* });
* ```
*
* @param title Hook title.
* @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}.
*/
beforeEach(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
/** /**
* Declares an `afterEach` hook that is executed after each test. * Declares an `afterEach` hook that is executed after each test.
* *
@ -3126,6 +3149,31 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
* @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}. * @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}.
*/ */
afterEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; afterEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
/**
* Declares an `afterEach` hook with a title that is executed after each test.
*
* **Usage**
*
* ```js
* // example.spec.ts
* import { test, expect } from '@playwright/test';
*
* test.afterEach('Status check', async ({ page }, testInfo) => {
* console.log(`Finished ${testInfo.title} with status ${testInfo.status}`);
*
* if (testInfo.status !== testInfo.expectedStatus)
* console.log(`Did not run as expected, ended up at ${page.url()}`);
* });
*
* test('my test', async ({ page }) => {
* // ...
* });
* ```
*
* @param title Hook title.
* @param hookFunction Hook function that takes one or two arguments: an object with fixtures and optional {@link TestInfo}.
*/
afterEach(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
/** /**
* Declares a `beforeAll` hook that is executed once per worker process before all tests. * Declares a `beforeAll` hook that is executed once per worker process before all tests.
* *
@ -3138,7 +3186,7 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
* Note that worker process is restarted on test failures, and `beforeAll` hook runs again in the new worker. Learn * Note that worker process is restarted on test failures, and `beforeAll` hook runs again in the new worker. Learn
* more about [workers and failures](https://playwright.dev/docs/test-retries). * more about [workers and failures](https://playwright.dev/docs/test-retries).
* *
* You can use [test.afterAll(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-all) to teardown * You can use [test.afterAll(hookFunction)](https://playwright.dev/docs/api/class-test#test-after-all-1) to teardown
* any resources set up in `beforeAll`. * any resources set up in `beforeAll`.
* *
* **Usage** * **Usage**
@ -3163,6 +3211,28 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
* @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional {@link TestInfo}. * @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional {@link TestInfo}.
*/ */
beforeAll(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; beforeAll(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
/**
* Declares a `beforeAll` hook with a title that is executed once per worker process before all tests.
*
* **Usage**
*
* ```js
* // example.spec.ts
* import { test, expect } from '@playwright/test';
*
* test.beforeAll('Setup', async () => {
* console.log('Before tests');
* });
*
* test('my test', async ({ page }) => {
* // ...
* });
* ```
*
* @param title Hook title.
* @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional {@link TestInfo}.
*/
beforeAll(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
/** /**
* Declares an `afterAll` hook that is executed once per worker after all tests. * Declares an `afterAll` hook that is executed once per worker after all tests.
* *
@ -3187,6 +3257,22 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
* @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional {@link TestInfo}. * @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional {@link TestInfo}.
*/ */
afterAll(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; afterAll(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
/**
* Declares an `afterAll` hook with a title that is executed once per worker after all tests.
*
* **Usage**
*
* ```js
* test.afterAll('Teardown', async () => {
* console.log('Done with tests');
* // ...
* });
* ```
*
* @param title Hook title.
* @param hookFunction Hook function that takes one or two arguments: an object with worker fixtures and optional {@link TestInfo}.
*/
afterAll(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
/** /**
* Specifies options or fixtures to use in a single test file or a * Specifies options or fixtures to use in a single test file or a
* [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) group. Most useful to * [test.describe(title, callback)](https://playwright.dev/docs/api/class-test#test-describe-1) group. Most useful to

View File

@ -2063,6 +2063,122 @@ for (const useIntermediateMergeReport of [false, true] as const) {
await expect(page.getByText('failed title')).not.toBeVisible(); await expect(page.getByText('failed title')).not.toBeVisible();
await expect(page.getByText('passes title')).toBeVisible(); await expect(page.getByText('passes title')).toBeVisible();
}); });
test('should properly display beforeEach with and without title', async ({ runInlineTest, showReport, page }) => {
const result = await runInlineTest({
'a.test.js': `
const { test, expect } = require('@playwright/test');
test.beforeEach('titled hook', () => {
console.log('titled hook');
});
test.beforeEach(() => {
console.log('anonymous hook');
});
test('titles', async ({}) => {
expect(1).toBe(1);
});
`,
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
await showReport();
await page.click('text=titles');
await page.click('text=Before Hooks');
await expect(page.locator('.tree-item:has-text("Before Hooks") .tree-item')).toContainText([
/titled hook/,
/beforeEach hook/,
]);
});
test('should properly display beforeAll with and without title', async ({ runInlineTest, showReport, page }) => {
const result = await runInlineTest({
'a.test.js': `
const { test, expect } = require('@playwright/test');
test.beforeAll('titled hook', () => {
console.log('titled hook');
});
test.beforeAll(() => {
console.log('anonymous hook');
});
test('titles', async ({}) => {
expect(1).toBe(1);
});
`,
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
await showReport();
await page.click('text=titles');
await page.click('text=Before Hooks');
await expect(page.locator('.tree-item:has-text("Before Hooks") .tree-item')).toContainText([
/titled hook/,
/beforeAll hook/,
]);
});
test('should properly display afterEach with and without title', async ({ runInlineTest, showReport, page }) => {
const result = await runInlineTest({
'a.test.js': `
const { test, expect } = require('@playwright/test');
test.afterEach('titled hook', () => {
console.log('titled hook');
});
test.afterEach(() => {
console.log('anonymous hook');
});
test('titles', async ({}) => {
expect(1).toBe(1);
});
`,
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
await showReport();
await page.click('text=titles');
await page.click('text=After Hooks');
await expect(page.locator('.tree-item:has-text("After Hooks") .tree-item')).toContainText([
/titled hook/,
/afterEach hook/,
]);
});
test('should properly display afterAll with and without title', async ({ runInlineTest, showReport, page }) => {
const result = await runInlineTest({
'a.test.js': `
const { test, expect } = require('@playwright/test');
test.afterAll('titled hook', () => {
console.log('titled hook');
});
test.afterAll(() => {
console.log('anonymous hook');
});
test('titles', async ({}) => {
expect(1).toBe(1);
});
`,
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
await showReport();
await page.click('text=titles');
await page.click('text=After Hooks');
await expect(page.locator('.tree-item:has-text("After Hooks") .tree-item')).toContainText([
/titled hook/,
/afterAll hook/,
]);
});
}); });
} }

View File

@ -149,9 +149,13 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
slow(callback: (args: TestArgs & WorkerArgs) => boolean, description?: string): void; slow(callback: (args: TestArgs & WorkerArgs) => boolean, description?: string): void;
setTimeout(timeout: number): void; setTimeout(timeout: number): void;
beforeEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; beforeEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
beforeEach(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
afterEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; afterEach(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
afterEach(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
beforeAll(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; beforeAll(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
beforeAll(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
afterAll(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void; afterAll(inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
afterAll(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
use(fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs>): void; use(fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs>): void;
step<T>(title: string, body: () => T | Promise<T>): Promise<T>; step<T>(title: string, body: () => T | Promise<T>): Promise<T>;
expect: Expect; expect: Expect;