Skip to content

Commit e93b64e

Browse files
authored
cherry-pick(#42566): feat(test): add subtitle option to test.step (#42567)
1 parent 2b7a5f2 commit e93b64e

10 files changed

Lines changed: 101 additions & 12 deletions

File tree

‎docs/src/test-api/class-test.md‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1850,6 +1850,12 @@ Specifies a custom location for the step to be shown in test reports and trace v
18501850

18511851
Arbitrary serializable parameters describing the step. They are reported to the reporters as `testStep.params` and are shown in the trace viewer.
18521852

1853+
### option: Test.step.subtitle
1854+
* since: v1.64
1855+
- `subtitle` <[string]>
1856+
1857+
Step subtitle that complements the title, for example the target of the step. It is reported to the reporters as `testStep.subtitle` and is shown next to the title in test reports and the trace viewer.
1858+
18531859
## async method: Test.step.skip
18541860
* since: v1.50
18551861
- returns: <[void]>
@@ -1903,6 +1909,12 @@ Specifies a custom location for the step to be shown in test reports and trace v
19031909

19041910
Arbitrary serializable parameters describing the step. They are reported to the reporters as `testStep.params` and are shown in the trace viewer.
19051911

1912+
### option: Test.step.skip.subtitle
1913+
* since: v1.64
1914+
- `subtitle` <[string]>
1915+
1916+
Step subtitle that complements the title, for example the target of the step. It is reported to the reporters as `testStep.subtitle` and is shown next to the title in test reports and the trace viewer.
1917+
19061918
### option: Test.step.skip.timeout
19071919
* since: v1.50
19081920
- `timeout` <[float]>

‎docs/src/test-reporter-api/class-teststep.md‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,15 +112,20 @@ User-friendly test step title, for example `Click` or `Navigate`.
112112

113113
User-friendly test step subtitle that complements the title, when available. For Playwright API
114114
calls, it is the target locator or the navigation url. For example, a `Click` step has the clicked
115-
locator as a subtitle. User interfaces typically render the subtitle next to the title or on a
116-
separate line.
115+
locator as a subtitle. [`method: Test.step`] steps carry the subtitle passed by the test author.
116+
User interfaces typically render the subtitle next to the title or on a separate line.
117117

118118
```js
119119
// title `Click`, subtitle `getByRole('button')`
120120
await page.getByRole('button').click();
121121

122122
// title `Navigate`, subtitle `example.com/index.html`
123123
await page.goto('https://example.com/index.html');
124+
125+
// title `Add to cart`, subtitle `SKU 42`
126+
await test.step('Add to cart', async () => {
127+
// ...
128+
}, { subtitle: 'SKU 42' });
124129
```
125130

126131
## method: TestStep.titlePath

‎packages/playwright/src/common/testType.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,12 +274,12 @@ export class TestTypeImpl {
274274
suite._use.push({ fixtures, location });
275275
}
276276

277-
async _step<T>(expectation: 'pass'|'skip', title: string, body: (step: TestStepInfo) => T | Promise<T>, options: {box?: boolean, location?: Location, timeout?: number, params?: Record<string, any> } = {}): Promise<T> {
277+
async _step<T>(expectation: 'pass'|'skip', title: string, body: (step: TestStepInfo) => T | Promise<T>, options: {box?: boolean, location?: Location, timeout?: number, params?: Record<string, any>, subtitle?: string } = {}): Promise<T> {
278278
const testInfo = currentTestInfo();
279279
if (!testInfo)
280280
throw new Error(`test.step() can only be called from a test`);
281281
await testInfo._onUserStepBegin?.(title);
282-
const step = testInfo._addStep({ category: 'test.step', title, location: options.location, box: options.box, params: options.params });
282+
const step = testInfo._addStep({ category: 'test.step', title, subtitle: options.subtitle, location: options.location, box: options.box, params: options.params });
283283
return await currentZone().with('stepZone', step).run(async () => {
284284
try {
285285
let result: Awaited<ReturnType<typeof raceAgainstDeadline<T>>> | undefined = undefined;

‎packages/playwright/types/test.d.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6727,7 +6727,7 @@ export interface TestType<TestArgs extends {}, WorkerArgs extends {}> {
67276727
* @param body Step body.
67286728
* @param options
67296729
*/
6730-
<T>(title: string, body: (step: TestStepInfo) => T | Promise<T>, options?: { box?: boolean, location?: Location, timeout?: number, params?: { [key: string]: any } }): Promise<T>;
6730+
<T>(title: string, body: (step: TestStepInfo) => T | Promise<T>, options?: { box?: boolean, location?: Location, timeout?: number, params?: { [key: string]: any }, subtitle?: string }): Promise<T>;
67316731
/**
67326732
* Mark a test step as "skip" to temporarily disable its execution, useful for steps that are currently failing and
67336733
* planned for a near-term fix. Playwright will not run the step. See also
@@ -6755,7 +6755,7 @@ export interface TestType<TestArgs extends {}, WorkerArgs extends {}> {
67556755
* @param body Step body.
67566756
* @param options
67576757
*/
6758-
skip(title: string, body: (step: TestStepInfo) => any | Promise<any>, options?: { box?: boolean, location?: Location, timeout?: number, params?: { [key: string]: any } }): Promise<void>;
6758+
skip(title: string, body: (step: TestStepInfo) => any | Promise<any>, options?: { box?: boolean, location?: Location, timeout?: number, params?: { [key: string]: any }, subtitle?: string }): Promise<void>;
67596759
}
67606760
/**
67616761
* `expect` function can be used to create test assertions. Read more about [test assertions](https://playwright.dev/docs/test-assertions).

‎packages/playwright/types/testReporter.d.ts‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -921,15 +921,21 @@ export interface TestStep {
921921

922922
/**
923923
* User-friendly test step subtitle that complements the title, when available. For Playwright API calls, it is the
924-
* target locator or the navigation url. For example, a `Click` step has the clicked locator as a subtitle. User
925-
* interfaces typically render the subtitle next to the title or on a separate line.
924+
* target locator or the navigation url. For example, a `Click` step has the clicked locator as a subtitle.
925+
* [test.step(title, body[, options])](https://playwright.dev/docs/api/class-test#test-step) steps carry the subtitle
926+
* passed by the test author. User interfaces typically render the subtitle next to the title or on a separate line.
926927
*
927928
* ```js
928929
* // title `Click`, subtitle `getByRole('button')`
929930
* await page.getByRole('button').click();
930931
*
931932
* // title `Navigate`, subtitle `example.com/index.html`
932933
* await page.goto('https://example.com/index.html');
934+
*
935+
* // title `Add to cart`, subtitle `SKU 42`
936+
* await test.step('Add to cart', async () => {
937+
* // ...
938+
* }, { subtitle: 'SKU 42' });
933939
* ```
934940
*
935941
*/

‎tests/playwright-test/playwright.trace.spec.ts‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,3 +1553,19 @@ test('should record step params in trace', async ({ runInlineTest }, testInfo) =
15531553
expect(actionByTitle('my step').params).toEqual({ foo: 'bar' });
15541554
expect(actionByTitle('Expect "toBe"').params).toEqual({ expected: '1' });
15551555
});
1556+
1557+
test('should record step subtitle in trace', async ({ runInlineTest }, testInfo) => {
1558+
const result = await runInlineTest({
1559+
'a.spec.ts': `
1560+
import { test, expect } from '@playwright/test';
1561+
test('pass', async ({}) => {
1562+
await test.step('my step', async () => {}, { subtitle: 'my subtitle' });
1563+
});
1564+
`,
1565+
}, { trace: 'on' });
1566+
1567+
expect(result.exitCode).toBe(0);
1568+
expect(result.passed).toBe(1);
1569+
const trace = await parseTrace(testInfo.outputPath('test-results', 'a-pass', 'trace.zip'));
1570+
expect(trace.model.actions.find(a => a.title === 'my step')!.subtitle).toBe('my subtitle');
1571+
});

‎tests/playwright-test/reporter-blob.spec.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,7 +1277,7 @@ test('preserve step params', async ({ runInlineTest, mergeReports }) => {
12771277
import { test, expect } from '@playwright/test';
12781278
test('test 1', async ({ page }) => {
12791279
await page.goto('about:blank');
1280-
await test.step('my step', async () => {}, { params: { foo: 'bar', count: 7 } });
1280+
await test.step('my step', async () => {}, { subtitle: 'my subtitle', params: { foo: 'bar', count: 7 } });
12811281
});
12821282
`,
12831283
};
@@ -1286,7 +1286,7 @@ test('preserve step params', async ({ runInlineTest, mergeReports }) => {
12861286
expect(exitCode).toBe(0);
12871287
expect(outputLines).toEqual([
12881288
`Navigate about:blank | {"url":"about:blank"}`,
1289-
`my step | {"foo":"bar","count":7}`,
1289+
`my step my subtitle | {"foo":"bar","count":7}`,
12901290
]);
12911291
});
12921292

‎tests/playwright-test/reporter-html.spec.ts‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,24 @@ for (const useIntermediateMergeReport of [true, false] as const) {
960960
await expect(page.locator('.step-subtitle .step-title-highlight')).toHaveText(['#target']);
961961
});
962962

963+
test('should render test.step subtitle', async ({ runInlineTest, page, showReport }) => {
964+
const result = await runInlineTest({
965+
'a.test.js': `
966+
import { test, expect } from '@playwright/test';
967+
test('has steps', async ({}) => {
968+
await test.step('Add to cart', async () => {}, { subtitle: 'SKU 42' });
969+
});
970+
`,
971+
}, { reporter: 'dot,html' }, { PLAYWRIGHT_HTML_OPEN: 'never' });
972+
expect(result.exitCode).toBe(0);
973+
expect(result.passed).toBe(1);
974+
975+
await showReport();
976+
await page.getByRole('link', { name: 'has steps' }).click();
977+
await expect(page.locator('.step-title-container', { hasText: 'Add to cart' })).toHaveAttribute('aria-label', 'Add to cart SKU 42');
978+
await expect(page.locator('.step-subtitle')).toHaveText('SKU 42');
979+
});
980+
963981
test('should show step snippets from non-root', async ({ runInlineTest, page, showReport }) => {
964982
const result = await runInlineTest({
965983
'playwright.config.js': `

‎tests/playwright-test/test-step.spec.ts‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1899,6 +1899,38 @@ test('should report step params', async ({ runInlineTest }) => {
18991899
]);
19001900
});
19011901

1902+
test('should report step subtitle', async ({ runInlineTest }) => {
1903+
const result = await runInlineTest({
1904+
'reporter.ts': `
1905+
import type { Reporter, TestCase, TestResult, TestStep } from '@playwright/test/reporter';
1906+
export default class MyReporter implements Reporter {
1907+
onStepEnd(test: TestCase, result: TestResult, step: TestStep) {
1908+
if (step.category === 'test.step')
1909+
console.log('%%' + step.title + ' | ' + step.subtitle);
1910+
}
1911+
}
1912+
`,
1913+
'playwright.config.ts': `
1914+
module.exports = { reporter: './reporter' };
1915+
`,
1916+
'a.test.ts': `
1917+
import { test, expect } from '@playwright/test';
1918+
test('pass', async ({}) => {
1919+
await test.step('my step', async () => {}, { subtitle: 'my subtitle' });
1920+
await test.step.skip('skipped step', async () => {}, { subtitle: 'skipped subtitle' });
1921+
await test.step('plain step', async () => {});
1922+
});
1923+
`
1924+
}, { reporter: '' });
1925+
1926+
expect(result.exitCode).toBe(0);
1927+
expect(result.outputLines).toEqual([
1928+
`my step | my subtitle`,
1929+
`skipped step | skipped subtitle`,
1930+
`plain step | undefined`,
1931+
]);
1932+
});
1933+
19021934
test('should report input step params', async ({ runInlineTest }) => {
19031935
const result = await runInlineTest({
19041936
'reporter.ts': `

‎utils/generate_types/overrides-test.d.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,8 @@ export interface TestType<TestArgs extends {}, WorkerArgs extends {}> {
197197
afterAll(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise<any> | any): void;
198198
use(fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs>): void;
199199
step: {
200-
<T>(title: string, body: (step: TestStepInfo) => T | Promise<T>, options?: { box?: boolean, location?: Location, timeout?: number, params?: { [key: string]: any } }): Promise<T>;
201-
skip(title: string, body: (step: TestStepInfo) => any | Promise<any>, options?: { box?: boolean, location?: Location, timeout?: number, params?: { [key: string]: any } }): Promise<void>;
200+
<T>(title: string, body: (step: TestStepInfo) => T | Promise<T>, options?: { box?: boolean, location?: Location, timeout?: number, params?: { [key: string]: any }, subtitle?: string }): Promise<T>;
201+
skip(title: string, body: (step: TestStepInfo) => any | Promise<any>, options?: { box?: boolean, location?: Location, timeout?: number, params?: { [key: string]: any }, subtitle?: string }): Promise<void>;
202202
}
203203
expect: Expect<{}>;
204204
extend<T extends {}, W extends {} = {}>(fixtures: Fixtures<T, W, TestArgs, WorkerArgs>): TestType<TestArgs & T, WorkerArgs & W>;

0 commit comments

Comments
 (0)