From 13d5d0d9d1681b4a1f72c7397097d094f2ea240a Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Wed, 23 Sep 2026 17:20:55 -0700 Subject: [PATCH] fix(html reporter): print show-report path relative to npm invocation dir --- packages/playwright/src/reporters/html.ts | 5 +++- tests/playwright-test/reporter-html.spec.ts | 32 +++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/playwright/src/reporters/html.ts b/packages/playwright/src/reporters/html.ts index 30abcc8254e21..da7ad610da842 100644 --- a/packages/playwright/src/reporters/html.ts +++ b/packages/playwright/src/reporters/html.ts @@ -177,7 +177,10 @@ class HtmlReporter implements ReporterV2 { await showHTMLReport(this._outputFolder, this._host, this._port, singleTestId); } else if (this._options._mode === 'test' && !!process.stdin.isTTY) { const packageManagerCommand = getPackageManagerExecCommand(); - const relativeReportPath = this._outputFolder === standaloneDefaultFolder() ? '' : ' ' + path.relative(process.cwd(), this._outputFolder); + // npm sets INIT_CWD to "the full path you were in when you ran npm run". + const invocationDir = process.env.INIT_CWD || process.cwd(); + const isDefaultFolder = invocationDir === process.cwd() && this._outputFolder === standaloneDefaultFolder(); + const relativeReportPath = isDefaultFolder ? '' : ' ' + (path.relative(invocationDir, this._outputFolder) || '.'); const hostArg = this._host ? ` --host ${this._host}` : ''; const portArg = this._port ? ` --port ${this._port}` : ''; writeLine(''); diff --git a/tests/playwright-test/reporter-html.spec.ts b/tests/playwright-test/reporter-html.spec.ts index 3853d0e1746c5..85f79c83c42ac 100644 --- a/tests/playwright-test/reporter-html.spec.ts +++ b/tests/playwright-test/reporter-html.spec.ts @@ -3793,6 +3793,38 @@ test('should support merge files option', async ({ runInlineTest, showReport, pa `); }); +test('should print show-report command without path when invoked from the report dir', async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + process.stdin.isTTY = true; + export default { reporter: 'html' }; + `, + 'a.test.js': ` + import { test } from '@playwright/test'; + test('passes', () => {}); + `, + }, {}, { PLAYWRIGHT_HTML_OPEN: 'never', INIT_CWD: testInfo.outputPath() }); + expect(result.exitCode).toBe(0); + expect(result.output).toContain('To open last HTML report run:'); + expect(result.output).toMatch(/npx playwright show-report\n/); +}); + +test('should print show-report command with path relative to npm invocation dir', async ({ runInlineTest }, testInfo) => { + const result = await runInlineTest({ + 'sub/package.json': `{ "name": "sub" }`, + 'sub/playwright.config.ts': ` + process.stdin.isTTY = true; + export default { reporter: 'html' }; + `, + 'sub/a.test.js': ` + import { test } from '@playwright/test'; + test('passes', () => {}); + `, + }, {}, { PLAYWRIGHT_HTML_OPEN: 'never', INIT_CWD: testInfo.outputPath() }, { cwd: 'sub' }); + expect(result.exitCode).toBe(0); + expect(result.output).toContain(`npx playwright show-report ${path.join('sub', 'playwright-report')}\n`); +}); + test.describe('show-report .zip support', () => { test('should serve a zipped report', async ({ runInlineTest, childProcess, findFreePort, page }, testInfo) => { await runInlineTest({