mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
docs: improve test.step documentation (#27535)
This commit is contained in:
parent
9edb811434
commit
d11380e911
@ -1331,7 +1331,7 @@ Optional description that will be reflected in a test report.
|
||||
* since: v1.10
|
||||
- returns: <[any]>
|
||||
|
||||
Declares a test step.
|
||||
Declares a test step that is shown in the report.
|
||||
|
||||
**Usage**
|
||||
|
||||
@ -1342,6 +1342,14 @@ test('test', async ({ page }) => {
|
||||
await test.step('Log in', async () => {
|
||||
// ...
|
||||
});
|
||||
|
||||
await test.step('Outer step', async () => {
|
||||
// ...
|
||||
// You can nest steps inside each other.
|
||||
await test.step('Inner step', async () => {
|
||||
// ...
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
@ -1361,6 +1369,118 @@ test('test', async ({ page }) => {
|
||||
});
|
||||
```
|
||||
|
||||
**Decorator**
|
||||
|
||||
You can use TypeScript method decorators to turn a method into a step.
|
||||
Each call to the decorated method will show up as a step in the report.
|
||||
|
||||
```js
|
||||
function step(target: Function, context: ClassMethodDecoratorContext) {
|
||||
return function replacementMethod(...args: any) {
|
||||
const name = this.constructor.name + '.' + (context.name as string);
|
||||
return test.step(name, async () => {
|
||||
return await target.call(this, ...args);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
class LoginPage {
|
||||
constructor(readonly page: Page) {}
|
||||
|
||||
@step
|
||||
async login() {
|
||||
const account = { username: 'Alice', password: 's3cr3t' };
|
||||
await this.page.getByLabel('Username or email address').fill(account.username);
|
||||
await this.page.getByLabel('Password').fill(account.password);
|
||||
await this.page.getByRole('button', { name: 'Sign in' }).click();
|
||||
await expect(this.page.getByRole('button', { name: 'View profile and more' })).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
test('example', async ({ page }) => {
|
||||
const loginPage = new LoginPage(page);
|
||||
await loginPage.login();
|
||||
});
|
||||
```
|
||||
|
||||
**Boxing**
|
||||
|
||||
When something inside a step fails, you would usually see the error pointing to the exact action that failed. For example, consider the following login step:
|
||||
|
||||
```js
|
||||
async function login(page) {
|
||||
await test.step('login', async () => {
|
||||
const account = { username: 'Alice', password: 's3cr3t' };
|
||||
await page.getByLabel('Username or email address').fill(account.username);
|
||||
await page.getByLabel('Password').fill(account.password);
|
||||
await page.getByRole('button', { name: 'Sign in' }).click();
|
||||
await expect(page.getByRole('button', { name: 'View profile and more' })).toBeVisible();
|
||||
});
|
||||
}
|
||||
|
||||
test('example', async ({ page }) => {
|
||||
await page.goto('https://github.com/login');
|
||||
await login(page);
|
||||
});
|
||||
```
|
||||
|
||||
```txt
|
||||
Error: Timed out 5000ms waiting for expect(locator).toBeVisible()
|
||||
... error details omitted ...
|
||||
|
||||
8 | await page.getByRole('button', { name: 'Sign in' }).click();
|
||||
> 9 | await expect(page.getByRole('button', { name: 'View profile and more' })).toBeVisible();
|
||||
| ^
|
||||
10 | });
|
||||
```
|
||||
|
||||
As we see above, the test may fail with an error pointing inside the step. If you would like the error to highlight the "login" step instead of its internals, use the `box` option. An error inside a boxed step points to the step call site.
|
||||
|
||||
```js
|
||||
async function login(page) {
|
||||
await test.step('login', async () => {
|
||||
// ...
|
||||
}, { box: true }); // Note the "box" option here.
|
||||
}
|
||||
```
|
||||
|
||||
```txt
|
||||
Error: Timed out 5000ms waiting for expect(locator).toBeVisible()
|
||||
... error details omitted ...
|
||||
|
||||
14 | await page.goto('https://github.com/login');
|
||||
> 15 | await login(page);
|
||||
| ^
|
||||
16 | });
|
||||
```
|
||||
|
||||
You can also create a TypeScript decorator for a boxed step, similar to a regular step decorator above:
|
||||
|
||||
```js
|
||||
function boxedStep(target: Function, context: ClassMethodDecoratorContext) {
|
||||
return function replacementMethod(...args: any) {
|
||||
const name = this.constructor.name + '.' + (context.name as string);
|
||||
return test.step(name, async () => {
|
||||
return await target.call(this, ...args);
|
||||
}, { box: true }); // Note the "box" option here.
|
||||
};
|
||||
}
|
||||
|
||||
class LoginPage {
|
||||
constructor(readonly page: Page) {}
|
||||
|
||||
@boxedStep
|
||||
async login() {
|
||||
// ....
|
||||
}
|
||||
}
|
||||
|
||||
test('example', async ({ page }) => {
|
||||
const loginPage = new LoginPage(page);
|
||||
await loginPage.login(); // <-- Error will be reported on this line.
|
||||
});
|
||||
```
|
||||
|
||||
### param: Test.step.title
|
||||
* since: v1.10
|
||||
- `title` <[string]>
|
||||
@ -1378,46 +1498,7 @@ Step body.
|
||||
* since: v1.39
|
||||
- `box` <boolean>
|
||||
|
||||
Whether to box the step in the report. Defaults to `false`. When the step is boxed, errors thrown from the step internals point to the step call site.
|
||||
|
||||
```js
|
||||
const assertGoodPage = async page => {
|
||||
await test.step('assertGoodPage', async () => {
|
||||
await expect(page.getByText('does-not-exist')).toBeVisible();
|
||||
}, { box: true });
|
||||
};
|
||||
|
||||
test('box', async ({ page }) => {
|
||||
await assertGoodPage(page); // <-- Errors will be reported on this line.
|
||||
});
|
||||
```
|
||||
|
||||
You can also use TypeScript method decorators to annotate method as a boxed step:
|
||||
|
||||
```js
|
||||
function boxedStep(target: Function, context: ClassMethodDecoratorContext) {
|
||||
return function replacementMethod(...args: any) {
|
||||
const name = this.constructor.name + '.' + (context.name as string);
|
||||
return test.step(name, async () => {
|
||||
return await target.call(this, ...args);
|
||||
}, { box: true });
|
||||
};
|
||||
}
|
||||
|
||||
class Pom {
|
||||
constructor(readonly page: Page) {}
|
||||
|
||||
@boxedStep
|
||||
async assertGoodPage() {
|
||||
await expect(this.page.getByText('does-not-exist')).toBeVisible({ timeout: 1 });
|
||||
}
|
||||
}
|
||||
|
||||
test('box', async ({ page }) => {
|
||||
const pom = new Pom(page);
|
||||
await pom.assertGoodPage(); // <-- Error will be reported on this line.
|
||||
});
|
||||
```
|
||||
Whether to box the step in the report. Defaults to `false`. When the step is boxed, errors thrown from the step internals point to the step call site. See below for more details.
|
||||
|
||||
## method: Test.use
|
||||
* since: v1.10
|
||||
|
||||
125
packages/playwright/types/test.d.ts
vendored
125
packages/playwright/types/test.d.ts
vendored
@ -3318,7 +3318,7 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
|
||||
*/
|
||||
use(fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs>): void;
|
||||
/**
|
||||
* Declares a test step.
|
||||
* Declares a test step that is shown in the report.
|
||||
*
|
||||
* **Usage**
|
||||
*
|
||||
@ -3329,6 +3329,14 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
|
||||
* await test.step('Log in', async () => {
|
||||
* // ...
|
||||
* });
|
||||
*
|
||||
* await test.step('Outer step', async () => {
|
||||
* // ...
|
||||
* // You can nest steps inside each other.
|
||||
* await test.step('Inner step', async () => {
|
||||
* // ...
|
||||
* });
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
@ -3348,6 +3356,121 @@ export interface TestType<TestArgs extends KeyValue, WorkerArgs extends KeyValue
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* **Decorator**
|
||||
*
|
||||
* You can use TypeScript method decorators to turn a method into a step. Each call to the decorated method will show
|
||||
* up as a step in the report.
|
||||
*
|
||||
* ```js
|
||||
* function step(target: Function, context: ClassMethodDecoratorContext) {
|
||||
* return function replacementMethod(...args: any) {
|
||||
* const name = this.constructor.name + '.' + (context.name as string);
|
||||
* return test.step(name, async () => {
|
||||
* return await target.call(this, ...args);
|
||||
* });
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* class LoginPage {
|
||||
* constructor(readonly page: Page) {}
|
||||
*
|
||||
* @step
|
||||
* async login() {
|
||||
* const account = { username: 'Alice', password: 's3cr3t' };
|
||||
* await this.page.getByLabel('Username or email address').fill(account.username);
|
||||
* await this.page.getByLabel('Password').fill(account.password);
|
||||
* await this.page.getByRole('button', { name: 'Sign in' }).click();
|
||||
* await expect(this.page.getByRole('button', { name: 'View profile and more' })).toBeVisible();
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* test('example', async ({ page }) => {
|
||||
* const loginPage = new LoginPage(page);
|
||||
* await loginPage.login();
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* **Boxing**
|
||||
*
|
||||
* When something inside a step fails, you would usually see the error pointing to the exact action that failed. For
|
||||
* example, consider the following login step:
|
||||
*
|
||||
* ```js
|
||||
* async function login(page) {
|
||||
* await test.step('login', async () => {
|
||||
* const account = { username: 'Alice', password: 's3cr3t' };
|
||||
* await page.getByLabel('Username or email address').fill(account.username);
|
||||
* await page.getByLabel('Password').fill(account.password);
|
||||
* await page.getByRole('button', { name: 'Sign in' }).click();
|
||||
* await expect(page.getByRole('button', { name: 'View profile and more' })).toBeVisible();
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* test('example', async ({ page }) => {
|
||||
* await page.goto('https://github.com/login');
|
||||
* await login(page);
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* ```txt
|
||||
* Error: Timed out 5000ms waiting for expect(locator).toBeVisible()
|
||||
* ... error details omitted ...
|
||||
*
|
||||
* 8 | await page.getByRole('button', { name: 'Sign in' }).click();
|
||||
* > 9 | await expect(page.getByRole('button', { name: 'View profile and more' })).toBeVisible();
|
||||
* | ^
|
||||
* 10 | });
|
||||
* ```
|
||||
*
|
||||
* As we see above, the test may fail with an error pointing inside the step. If you would like the error to highlight
|
||||
* the "login" step instead of its internals, use the `box` option. An error inside a boxed step points to the step
|
||||
* call site.
|
||||
*
|
||||
* ```js
|
||||
* async function login(page) {
|
||||
* await test.step('login', async () => {
|
||||
* // ...
|
||||
* }, { box: true }); // Note the "box" option here.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* ```txt
|
||||
* Error: Timed out 5000ms waiting for expect(locator).toBeVisible()
|
||||
* ... error details omitted ...
|
||||
*
|
||||
* 14 | await page.goto('https://github.com/login');
|
||||
* > 15 | await login(page);
|
||||
* | ^
|
||||
* 16 | });
|
||||
* ```
|
||||
*
|
||||
* You can also create a TypeScript decorator for a boxed step, similar to a regular step decorator above:
|
||||
*
|
||||
* ```js
|
||||
* function boxedStep(target: Function, context: ClassMethodDecoratorContext) {
|
||||
* return function replacementMethod(...args: any) {
|
||||
* const name = this.constructor.name + '.' + (context.name as string);
|
||||
* return test.step(name, async () => {
|
||||
* return await target.call(this, ...args);
|
||||
* }, { box: true }); // Note the "box" option here.
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* class LoginPage {
|
||||
* constructor(readonly page: Page) {}
|
||||
*
|
||||
* @boxedStep
|
||||
* async login() {
|
||||
* // ....
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* test('example', async ({ page }) => {
|
||||
* const loginPage = new LoginPage(page);
|
||||
* await loginPage.login(); // <-- Error will be reported on this line.
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param title Step name.
|
||||
* @param body Step body.
|
||||
* @param options
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user