diff --git a/docs/src/test-api/class-teststepinfo.md b/docs/src/test-api/class-teststepinfo.md index 0a0b521debedc..34abfaf0bf48e 100644 --- a/docs/src/test-api/class-teststepinfo.md +++ b/docs/src/test-api/class-teststepinfo.md @@ -128,3 +128,9 @@ A skip condition. Test step is skipped when the condition is `true`. - `description` ?<[string]> Optional description that will be reflected in a test report. + +## property: TestStepInfo.titlePath +* since: v1.55 +- type: <[Array]<[string]>> + +The full title path starting with the test file name, including the step titles. See also [`property: TestInfo.titlePath`]. diff --git a/packages/playwright-core/browsers.json b/packages/playwright-core/browsers.json index b6f5ac94be049..5d1c0bdae15e1 100644 --- a/packages/playwright-core/browsers.json +++ b/packages/playwright-core/browsers.json @@ -15,15 +15,15 @@ }, { "name": "chromium-tip-of-tree", - "revision": "1350", + "revision": "1351", "installByDefault": false, - "browserVersion": "140.0.7301.0" + "browserVersion": "140.0.7311.0" }, { "name": "chromium-tip-of-tree-headless-shell", - "revision": "1350", + "revision": "1351", "installByDefault": false, - "browserVersion": "140.0.7301.0" + "browserVersion": "140.0.7311.0" }, { "name": "firefox", diff --git a/packages/playwright/src/worker/testInfo.ts b/packages/playwright/src/worker/testInfo.ts index 19a2fa5071281..0eb185c21134c 100644 --- a/packages/playwright/src/worker/testInfo.ts +++ b/packages/playwright/src/worker/testInfo.ts @@ -286,7 +286,7 @@ export class TestInfoImpl implements TestInfo { ...data, steps: [], attachmentIndices, - info: new TestStepInfoImpl(this, stepId), + info: new TestStepInfoImpl(this, stepId, data.title, parentStep?.info), complete: result => { if (step.endWallTime) return; @@ -589,12 +589,16 @@ export class TestStepInfoImpl implements TestStepInfo { private _testInfo: TestInfoImpl; private _stepId: string; + private _title: string; + private _parentStep?: TestStepInfoImpl; skip: (arg?: any, description?: string) => void; - constructor(testInfo: TestInfoImpl, stepId: string) { + constructor(testInfo: TestInfoImpl, stepId: string, title: string, parentStep?: TestStepInfoImpl) { this._testInfo = testInfo; this._stepId = stepId; + this._title = title; + this._parentStep = parentStep; this.skip = wrapFunctionWithLocation((location: Location, ...args: unknown[]) => { // skip(); // skip(condition: boolean, description: string); @@ -627,6 +631,11 @@ export class TestStepInfoImpl implements TestStepInfo { async attach(name: string, options?: { body?: string | Buffer; contentType?: string; path?: string; }): Promise { this._attachToStep(await normalizeAndSaveAttachment(this._testInfo.outputPath(), name, options)); } + + get titlePath(): string[] { + const parent = this._parentStep ?? this._testInfo; + return [...parent.titlePath, this._title]; + } } export class TestSkipError extends Error { diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index cfc062bafbb27..1a94bc605c8ac 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -9914,6 +9914,12 @@ export interface TestStepInfo { * @param description Optional description that will be reflected in a test report. */ skip(condition: boolean, description?: string): void; + + /** + * The full title path starting with the test file name, including the step titles. See also + * [testInfo.titlePath](https://playwright.dev/docs/api/class-testinfo#test-info-title-path). + */ + titlePath: Array; } /** diff --git a/tests/playwright-test/test-step.spec.ts b/tests/playwright-test/test-step.spec.ts index 2e2447dab5051..dd56fa860c133 100644 --- a/tests/playwright-test/test-step.spec.ts +++ b/tests/playwright-test/test-step.spec.ts @@ -1637,6 +1637,38 @@ hook |After Hooks `); }); +test('step.titlePath works in custom matcher', { annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/36739' } }, async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'a.test.ts': ` + import { test, expect as baseExpect } from '@playwright/test'; + const expect = baseExpect.extend({ + async toHaveTitlePath(receiver, expected: string[]) { + return await test.step('get titlepath', async step => { + const actual = step.titlePath; + try { + expect(actual).toEqual(expected); + return { name: 'toHaveTitlePath', pass: true, message: '' }; + } catch { + return { name: 'toHaveTitlePath', pass: false, message: () => \`Expected \${JSON.stringify(expected)}, got \${JSON.stringify(actual)}\` }; + } + }); + }, + }); + test('test', async ({ }) => { + await expect().toHaveTitlePath(['a.test.ts', 'test', 'toHaveTitlePath', 'get titlepath']); + await test.step('outer step', async () => { + await expect().toHaveTitlePath(['a.test.ts', 'test', 'outer step', 'toHaveTitlePath', 'get titlepath']); + await test.step('inner step', async () => { + await expect().toHaveTitlePath(['a.test.ts', 'test', 'outer step', 'inner step', 'toHaveTitlePath', 'get titlepath']); + }); + }); + }); + ` + }, { reporter: '' }); + + expect(result.exitCode).toBe(0); +}); + test('should differentiate test.skip and step.skip', async ({ runInlineTest }) => { const result = await runInlineTest({ 'reporter.ts': stepIndentReporter,