diff --git a/docs/src/test-api/class-test.md b/docs/src/test-api/class-test.md index 9b56fbf2e6462..b6ae7d1522116 100644 --- a/docs/src/test-api/class-test.md +++ b/docs/src/test-api/class-test.md @@ -1710,6 +1710,11 @@ Step body. 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. +### option: Test.step.location +* since: v1.48 +- `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. + ## method: Test.use * since: v1.10 diff --git a/packages/playwright/src/common/testType.ts b/packages/playwright/src/common/testType.ts index 5c7850a3df07f..f0882735dc7e2 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 } = {}): Promise { + async _step(title: string, body: () => Promise, options: {box?: boolean, location?: Location } = {}): 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, box: options.box }); + const step = testInfo._addStep({ category: 'test.step', title, location: options.location, 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 09b5c7efdb82c..e576457d3b1d1 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 }): Promise; + step(title: string, body: () => T | Promise, options?: { box?: boolean, location?: Location }): 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 f7538de3e9f79..cfd1b526d791e 100644 --- a/tests/playwright-test/test-step.spec.ts +++ b/tests/playwright-test/test-step.spec.ts @@ -1239,3 +1239,42 @@ fixture | fixture: page fixture | fixture: context `); }); + +test('test location to test.step', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'reporter.ts': stepIndentReporter, + 'helper.ts': ` + import { test } from '@playwright/test'; + + export async function dummyStep(test, title, action, location) { + return await test.step(title, action, { location }); + } + + export function getCustomLocation() { + return { file: 'dummy-file.ts', line: 123, column: 45 }; + } + `, + 'playwright.config.ts': ` + module.exports = { + reporter: './reporter', + }; + `, + 'a.test.ts': ` + import { test } from '@playwright/test'; + import { dummyStep, getCustomLocation } from './helper'; + + test('custom location test', async () => { + const location = getCustomLocation(); + await dummyStep(test, 'Perform a dummy step', async () => { + }, location); + }); + ` + }, { reporter: '', workers: 1 }); + + expect(result.exitCode).toBe(0); + expect(stripAnsi(result.output)).toBe(` +hook |Before Hooks +test.step |Perform a dummy step @ dummy-file.ts:123 +hook |After Hooks +`); +}); \ No newline at end of file diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index 90ef7fa75a52c..be1fa7ee373d3 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 }): Promise; + step(title: string, body: () => T | Promise, options?: { box?: boolean, location?: Location }): Promise; expect: Expect<{}>; extend(fixtures: Fixtures): TestType; info(): TestInfo;