diff --git a/docs/src/test-api/class-test.md b/docs/src/test-api/class-test.md index b6ae7d1522..67e06961a9 100644 --- a/docs/src/test-api/class-test.md +++ b/docs/src/test-api/class-test.md @@ -1715,6 +1715,11 @@ Whether to box the step in the report. Defaults to `false`. When the step is box - `location` <[Location]> Specifies a custom location for the step to be shown in test reports. By default, location of the [`method: Test.step`] call is shown. +### option: Test.step.params +* since: v1.48 +- `params` ?<[Object]> +An optional key-value object where keys are step parameter names and values are their corresponding parameter values. This information is shown in the trace view. + ## method: Test.use * since: v1.10 diff --git a/packages/playwright/src/common/testType.ts b/packages/playwright/src/common/testType.ts index f0882735dc..2f106f3fac 100644 --- a/packages/playwright/src/common/testType.ts +++ b/packages/playwright/src/common/testType.ts @@ -259,11 +259,11 @@ export class TestTypeImpl { suite._use.push({ fixtures, location }); } - async _step(title: string, body: () => Promise, options: {box?: boolean, location?: Location } = {}): Promise { + async _step(title: string, body: () => Promise, options: { box?: boolean, location?: Location, params?: Record } = {}): Promise { const testInfo = currentTestInfo(); if (!testInfo) throw new Error(`test.step() can only be called from a test`); - const step = testInfo._addStep({ category: 'test.step', title, location: options.location, box: options.box }); + const step = testInfo._addStep({ category: 'test.step', title, location: options.location, params: options.params, box: options.box }); return await zones.run('stepZone', step, async () => { try { const result = await body(); diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index f158baac36..4a046b7c46 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -4703,7 +4703,7 @@ export interface TestType(title: string, body: () => T | Promise, options?: { box?: boolean, location?: Location }): Promise; + step(title: string, body: () => T | Promise, options?: { box?: boolean, location?: Location, params?: Record }): Promise; /** * `expect` function can be used to create test assertions. Read more about [test assertions](https://playwright.dev/docs/test-assertions). * diff --git a/tests/playwright-test/test-step.spec.ts b/tests/playwright-test/test-step.spec.ts index f0bd883777..87e5ea408c 100644 --- a/tests/playwright-test/test-step.spec.ts +++ b/tests/playwright-test/test-step.spec.ts @@ -15,6 +15,8 @@ */ import { test, expect, stripAnsi } from './playwright-test-fixtures'; +import { parseTrace } from '../config/utils'; +import { type ActionTreeItem, buildActionTree } from 'trace-viewer/src/ui/modelUtil'; const stepIndentReporter = ` import { FullConfig, Location, Reporter, Suite, TestStep } from '@playwright/test/reporter'; @@ -1298,3 +1300,57 @@ hook |After Hooks }); expect(exitCode).toBe(0); }); + +test('should allow passing params to test.step', async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'reporter.ts': stepIndentReporter, + 'helper.ts': ` + import { test, TestType } from '@playwright/test'; + + export async function sayHello(name: string) { + await test.step('says hello', () => {}, { params: { name } }); + } + `, + 'playwright.config.ts': ` + module.exports = { + reporter: './reporter', + use: { + trace: 'on', + } + }; + `, + 'a.test.ts': ` + import { test } from '@playwright/test'; + import { sayHello } from './helper'; + + test('params', async () => { + await sayHello('World'); + }); + ` + }, { workers: 1 }); + + expect(result.exitCode).toBe(0); + + const trace = await parseTrace(testInfo.outputPath('test-results', 'a-params', 'trace.zip')); + + const { rootItem } = buildActionTree(trace.model.actions); + const actionTreeWithParams: string[] = []; + const visit = (actionItem: ActionTreeItem, indent: string) => { + const line = [ + indent, + actionItem.action?.apiName || actionItem.id, + actionItem.action?.params && ` params: ${JSON.stringify(actionItem.action?.params)}`, + ].filter(Boolean).join(''); + + actionTreeWithParams.push(line); + for (const child of actionItem.children) + visit(child, indent + ' '); + }; + rootItem.children.forEach(a => visit(a, '')); + + expect(actionTreeWithParams).toEqual([ + 'Before Hooks params: {}', + 'says hello params: {"name":"World"}', + 'After Hooks params: {}', + ]); +}); diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index be1fa7ee37..cf4b4eccd2 100644 --- a/utils/generate_types/overrides-test.d.ts +++ b/utils/generate_types/overrides-test.d.ts @@ -128,7 +128,7 @@ export interface TestType Promise | any): void; afterAll(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise | any): void; use(fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs>): void; - step(title: string, body: () => T | Promise, options?: { box?: boolean, location?: Location }): Promise; + step(title: string, body: () => T | Promise, options?: { box?: boolean, location?: Location, params?: Record }): Promise; expect: Expect<{}>; extend(fixtures: Fixtures): TestType; info(): TestInfo;