diff --git a/docs/src/test-reporters-js.md b/docs/src/test-reporters-js.md index 57d34a9bb4c45..37b79d79078f9 100644 --- a/docs/src/test-reporters-js.md +++ b/docs/src/test-reporters-js.md @@ -70,7 +70,7 @@ export default defineConfig({ }); ``` -Here is an example output in the middle of a test run. Failures will be listed at the end. +Here is an example output in the middle of a test run. Failures will be listed at the end by default. ```bash npx playwright test --reporter=list Running 124 tests using 6 workers @@ -97,11 +97,22 @@ export default defineConfig({ }); ``` +You can print failures inline as soon as they are available instead of waiting until the end of the run: + +```js title="playwright.config.ts" +import { defineConfig } from '@playwright/test'; + +export default defineConfig({ + reporter: [['list', { printFailuresInline: true }]], +}); +``` + List report supports the following configuration options and environment variables: | Environment Variable Name | Reporter Config Option| Description | Default |---|---|---|---| | `PLAYWRIGHT_LIST_PRINT_STEPS` | `printSteps` | Whether to print each step on its own line. | `false` +| `PLAYWRIGHT_LIST_PRINT_FAILURES_INLINE` | `printFailuresInline` | Whether to print failure details immediately after a failed test instead of at the end. | `false` | `PLAYWRIGHT_FORCE_TTY` | | Whether to produce output suitable for a live terminal. Supports `true`, `1`, `false`, `0`, `[WIDTH]`, and `[WIDTH]x[HEIGHT]`. `[WIDTH]` and `[WIDTH]x[HEIGHT]` specifies the TTY dimensions. | `true` when terminal is in TTY mode, `false` otherwise. | `FORCE_COLOR` | | Whether to produce colored output. | `true` when terminal is in TTY mode, `false` otherwise. diff --git a/packages/playwright/src/reporters/list.ts b/packages/playwright/src/reporters/list.ts index 198070281b432..1ded22453d657 100644 --- a/packages/playwright/src/reporters/list.ts +++ b/packages/playwright/src/reporters/list.ts @@ -38,11 +38,14 @@ class ListReporter extends TerminalReporter { private _stepIndex = new Map(); private _needNewLine = false; private _printSteps: boolean; + private _printFailuresInline: boolean; + private _failureIndex = 0; private _paused = new Set(); constructor(options?: ListReporterOptions & CommonReporterOptions & TerminalReporterOptions) { super(options); this._printSteps = getAsBooleanFromENV('PLAYWRIGHT_LIST_PRINT_STEPS', options?.printSteps); + this._printFailuresInline = getAsBooleanFromENV('PLAYWRIGHT_LIST_PRINT_FAILURES_INLINE', options?.printFailuresInline); } override onBegin(suite: Suite) { @@ -191,6 +194,15 @@ class ListReporter extends TerminalReporter { const wasPaused = this._paused.delete(result); if (!wasPaused) this._updateTestLine(test, result); + if (!wasPaused && this._printFailuresInline && !this.willRetry(test) && (test.outcome() === 'flaky' || test.outcome() === 'unexpected' || result.status === 'interrupted')) + this._printFailure(test); + } + + private _printFailure(test: TestCase) { + this._maybeWriteNewLine(); + const message = '\n' + this.formatFailure(test, ++this._failureIndex) + '\n'; + this._updateLineCountAndNewLineFlagForOutput(message); + this.screen.stdout.write(message); } private _updateTestLine(test: TestCase, result: TestResult) { @@ -290,7 +302,7 @@ class ListReporter extends TerminalReporter { override async onEnd(result: FullResult) { await super.onEnd(result); this.screen.stdout.write('\n'); - this.epilogue(true); + this.epilogue(!this._printFailuresInline); } } diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index 34651bff3b907..dc9e83bec34e9 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -19,7 +19,7 @@ import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, export * from 'playwright-core'; export type BlobReporterOptions = { outputDir?: string, fileName?: string }; -export type ListReporterOptions = { printSteps?: boolean }; +export type ListReporterOptions = { printSteps?: boolean, printFailuresInline?: boolean }; export type JUnitReporterOptions = { outputFile?: string, stripANSIControlSequences?: boolean, includeProjectInTestName?: boolean, includeRetries?: boolean }; export type JsonReporterOptions = { outputFile?: string }; export type HtmlReporterOptions = { diff --git a/tests/playwright-test/reporter-list.spec.ts b/tests/playwright-test/reporter-list.spec.ts index 0e62965b06477..837427b830926 100644 --- a/tests/playwright-test/reporter-list.spec.ts +++ b/tests/playwright-test/reporter-list.spec.ts @@ -259,6 +259,35 @@ for (const useIntermediateMergeReport of [false, true] as const) { expect(result.exitCode).toBe(1); }); + test('print failures inline with option', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + module.exports = { + reporter: [['list', { printFailuresInline: true }]], + workers: 1, + }; + `, + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('fails early', async ({}) => { + expect(1).toBe(2); + }); + test('runs later', async ({}) => { + }); + `, + }); + const text = result.output; + const failureHeader = '1) a.test.ts:3:15 › fails early'; + const laterTestStatus = `${POSITIVE_STATUS_MARK} 2 a.test.ts:6:15 › runs later`; + const failureIndex = text.indexOf(failureHeader); + const laterTestIndex = text.indexOf(laterTestStatus); + expect(failureIndex).toBeGreaterThan(-1); + expect(laterTestIndex).toBeGreaterThan(failureIndex); + expect(text.indexOf('Error: expect(received).toBe(expected)', failureIndex)).toBeGreaterThan(failureIndex); + expect(text.indexOf(failureHeader, failureIndex + 1)).toBe(-1); + expect(result.exitCode).toBe(1); + }); + test('print stdio', async ({ runInlineTest }) => { const result = await runInlineTest({ 'a.test.ts': ` diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index ac5c365801413..69d2e3ad883a3 100644 --- a/utils/generate_types/overrides-test.d.ts +++ b/utils/generate_types/overrides-test.d.ts @@ -18,7 +18,7 @@ import type { APIRequestContext, Browser, BrowserContext, BrowserContextOptions, export * from 'playwright-core'; export type BlobReporterOptions = { outputDir?: string, fileName?: string }; -export type ListReporterOptions = { printSteps?: boolean }; +export type ListReporterOptions = { printSteps?: boolean, printFailuresInline?: boolean }; export type JUnitReporterOptions = { outputFile?: string, stripANSIControlSequences?: boolean, includeProjectInTestName?: boolean, includeRetries?: boolean }; export type JsonReporterOptions = { outputFile?: string }; export type HtmlReporterOptions = {