Skip to content

Commit b9a1823

Browse files
aslushnikovdgozman
andauthored
cherry-pick(release-1.14): show stdio for failures in terminal reporters (#8187)
PR #8150 SHA 44cdda4 Fixes #7900 Co-authored-by: Dmitry Gozman <[email protected]>
1 parent cba2f05 commit b9a1823

File tree

8 files changed

+88
-23
lines changed

8 files changed

+88
-23
lines changed

src/test/reporters/base.ts

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,28 +25,37 @@ import { FullConfig, TestCase, Suite, TestResult, TestError, Reporter, FullResul
2525

2626
const stackUtils = new StackUtils();
2727

28+
type TestResultOutput = { chunk: string | Buffer, type: 'stdout' | 'stderr' };
29+
const kOutputSymbol = Symbol('output');
30+
2831
export class BaseReporter implements Reporter {
2932
duration = 0;
3033
config!: FullConfig;
3134
suite!: Suite;
3235
result!: FullResult;
3336
fileDurations = new Map<string, number>();
3437
monotonicStartTime: number = 0;
38+
private printTestOutput = !process.env.PWTEST_SKIP_TEST_OUTPUT;
3539

3640
onBegin(config: FullConfig, suite: Suite) {
3741
this.monotonicStartTime = monotonicTime();
3842
this.config = config;
3943
this.suite = suite;
4044
}
4145

42-
onStdOut(chunk: string | Buffer) {
43-
if (!this.config.quiet)
44-
process.stdout.write(chunk);
46+
onStdOut(chunk: string | Buffer, test?: TestCase, result?: TestResult) {
47+
this._appendOutput({ chunk, type: 'stdout' }, result);
48+
}
49+
50+
onStdErr(chunk: string | Buffer, test?: TestCase, result?: TestResult) {
51+
this._appendOutput({ chunk, type: 'stderr' }, result);
4552
}
4653

47-
onStdErr(chunk: string | Buffer) {
48-
if (!this.config.quiet)
49-
process.stderr.write(chunk);
54+
private _appendOutput(output: TestResultOutput, result: TestResult | undefined) {
55+
if (!result)
56+
return;
57+
(result as any)[kOutputSymbol] = (result as any)[kOutputSymbol] || [];
58+
(result as any)[kOutputSymbol].push(output);
5059
}
5160

5261
onTestEnd(test: TestCase, result: TestResult) {
@@ -133,7 +142,7 @@ export class BaseReporter implements Reporter {
133142

134143
private _printFailures(failures: TestCase[]) {
135144
failures.forEach((test, index) => {
136-
console.log(formatFailure(this.config, test, index + 1));
145+
console.log(formatFailure(this.config, test, index + 1, this.printTestOutput));
137146
});
138147
}
139148

@@ -142,7 +151,7 @@ export class BaseReporter implements Reporter {
142151
}
143152
}
144153

145-
export function formatFailure(config: FullConfig, test: TestCase, index?: number): string {
154+
export function formatFailure(config: FullConfig, test: TestCase, index?: number, stdio?: boolean): string {
146155
const tokens: string[] = [];
147156
tokens.push(formatTestHeader(config, test, ' ', index));
148157
for (const result of test.results) {
@@ -155,6 +164,17 @@ export function formatFailure(config: FullConfig, test: TestCase, index?: number
155164
tokens.push(colors.gray(pad(` Retry #${result.retry}${statusSuffix}`, '-')));
156165
}
157166
tokens.push(...resultTokens);
167+
const output = ((result as any)[kOutputSymbol] || []) as TestResultOutput[];
168+
if (stdio && output.length) {
169+
const outputText = output.map(({ chunk, type }) => {
170+
const text = chunk.toString('utf8');
171+
if (type === 'stderr')
172+
return colors.red(stripAnsiEscapes(text));
173+
return text;
174+
}).join('');
175+
tokens.push('');
176+
tokens.push(colors.gray(pad('--- Test output', '-')) + '\n\n' + outputText + '\n' + pad('', '-'));
177+
}
158178
}
159179
tokens.push('');
160180
return tokens.join('\n');
@@ -219,7 +239,9 @@ function formatError(error: TestError, file?: string) {
219239
}
220240

221241
function pad(line: string, char: string): string {
222-
return line + ' ' + colors.gray(char.repeat(Math.max(0, 100 - line.length - 1)));
242+
if (line)
243+
line += ' ';
244+
return line + colors.gray(char.repeat(Math.max(0, 100 - line.length)));
223245
}
224246

225247
function indent(lines: string, tab: string) {
@@ -244,6 +266,6 @@ function monotonicTime(): number {
244266
}
245267

246268
const asciiRegex = new RegExp('[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))', 'g');
247-
export function stripAscii(str: string): string {
269+
export function stripAnsiEscapes(str: string): string {
248270
return str.replace(asciiRegex, '');
249271
}

src/test/reporters/dot.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,18 @@ import { FullResult, TestCase, TestResult } from '../../../types/testReporter';
2121
class DotReporter extends BaseReporter {
2222
private _counter = 0;
2323

24+
onStdOut(chunk: string | Buffer, test?: TestCase, result?: TestResult) {
25+
super.onStdOut(chunk, test, result);
26+
if (!this.config.quiet)
27+
process.stdout.write(chunk);
28+
}
29+
30+
onStdErr(chunk: string | Buffer, test?: TestCase, result?: TestResult) {
31+
super.onStdErr(chunk, test, result);
32+
if (!this.config.quiet)
33+
process.stderr.write(chunk);
34+
}
35+
2436
onTestEnd(test: TestCase, result: TestResult) {
2537
super.onTestEnd(test, result);
2638
if (this._counter === 80) {

src/test/reporters/junit.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import fs from 'fs';
1818
import path from 'path';
1919
import { FullConfig, FullResult, Reporter, Suite, TestCase } from '../../../types/testReporter';
2020
import { monotonicTime } from '../util';
21-
import { formatFailure, formatTestTitle, stripAscii } from './base';
21+
import { formatFailure, formatTestTitle, stripAnsiEscapes } from './base';
2222

2323
class JUnitReporter implements Reporter {
2424
private config!: FullConfig;
@@ -142,7 +142,7 @@ class JUnitReporter implements Reporter {
142142
message: `${path.basename(test.location.file)}:${test.location.line}:${test.location.column} ${test.title}`,
143143
type: 'FAILURE',
144144
},
145-
text: stripAscii(formatFailure(this.config, test))
145+
text: stripAnsiEscapes(formatFailure(this.config, test))
146146
});
147147
}
148148
for (const result of test.results) {

src/test/reporters/line.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@ class LineReporter extends BaseReporter {
3030
console.log();
3131
}
3232

33-
onStdOut(chunk: string | Buffer, test?: TestCase) {
33+
onStdOut(chunk: string | Buffer, test?: TestCase, result?: TestResult) {
34+
super.onStdOut(chunk, test, result);
3435
this._dumpToStdio(test, chunk, process.stdout);
3536
}
3637

37-
onStdErr(chunk: string | Buffer, test?: TestCase) {
38+
onStdErr(chunk: string | Buffer, test?: TestCase, result?: TestResult) {
39+
super.onStdErr(chunk, test, result);
3840
this._dumpToStdio(test, chunk, process.stderr);
3941
}
4042

src/test/reporters/list.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ const POSITIVE_STATUS_MARK = DOES_NOT_SUPPORT_UTF8_IN_TERMINAL ? 'ok' : '✓';
2727
const NEGATIVE_STATUS_MARK = DOES_NOT_SUPPORT_UTF8_IN_TERMINAL ? 'x' : '✘';
2828

2929
class ListReporter extends BaseReporter {
30-
private _failure = 0;
3130
private _lastRow = 0;
3231
private _testRows = new Map<TestCase, number>();
3332
private _needNewLine = false;
@@ -44,16 +43,18 @@ class ListReporter extends BaseReporter {
4443
process.stdout.write('\n');
4544
this._lastRow++;
4645
}
47-
process.stdout.write(' ' + colors.gray(formatTestTitle(this.config, test) + ': ') + '\n');
46+
process.stdout.write(' ' + colors.gray(formatTestTitle(this.config, test)) + '\n');
4847
}
4948
this._testRows.set(test, this._lastRow++);
5049
}
5150

52-
onStdOut(chunk: string | Buffer, test?: TestCase) {
51+
onStdOut(chunk: string | Buffer, test?: TestCase, result?: TestResult) {
52+
super.onStdOut(chunk, test, result);
5353
this._dumpToStdio(test, chunk, process.stdout);
5454
}
5555

56-
onStdErr(chunk: string | Buffer, test?: TestCase) {
56+
onStdErr(chunk: string | Buffer, test?: TestCase, result?: TestResult) {
57+
super.onStdErr(chunk, test, result);
5758
this._dumpToStdio(test, chunk, process.stdout);
5859
}
5960

@@ -76,13 +77,13 @@ class ListReporter extends BaseReporter {
7677
const title = formatTestTitle(this.config, test);
7778
let text = '';
7879
if (result.status === 'skipped') {
79-
text = colors.green(' - ') + colors.cyan(title);
80+
text = colors.green(' - ') + colors.cyan(title);
8081
} else {
8182
const statusMark = (' ' + (result.status === 'passed' ? POSITIVE_STATUS_MARK : NEGATIVE_STATUS_MARK)).padEnd(5);
8283
if (result.status === test.expectedStatus)
8384
text = '\u001b[2K\u001b[0G' + colors.green(statusMark) + colors.gray(title) + duration;
8485
else
85-
text = '\u001b[2K\u001b[0G' + colors.red(`${statusMark}${++this._failure}) ` + title) + duration;
86+
text = '\u001b[2K\u001b[0G' + colors.red(statusMark + title) + duration;
8687
}
8788

8889
const testRow = this._testRows.get(test)!;

tests/playwright-test/base-reporter.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import { test, expect, stripAscii } from './playwright-test-fixtures';
1818
import * as path from 'path';
19+
import colors from 'colors/safe';
1920

2021
test('handle long test names', async ({ runInlineTest }) => {
2122
const title = 'title'.repeat(30);
@@ -158,3 +159,25 @@ test('should not print slow tests', async ({ runInlineTest }) => {
158159
expect(result.passed).toBe(4);
159160
expect(stripAscii(result.output)).not.toContain('Slow test');
160161
});
162+
163+
test('should print stdio for failures', async ({ runInlineTest }) => {
164+
const result = await runInlineTest({
165+
'a.test.js': `
166+
const { test } = pwt;
167+
test('fails', async ({}) => {
168+
console.log('my log 1');
169+
console.error('my error');
170+
console.log('my log 2');
171+
expect(1).toBe(2);
172+
});
173+
`,
174+
}, {}, { PWTEST_SKIP_TEST_OUTPUT: '' });
175+
expect(result.exitCode).toBe(1);
176+
expect(result.failed).toBe(1);
177+
expect(result.output).toContain('Test output');
178+
expect(result.output).toContain([
179+
'my log 1\n',
180+
colors.red('my error\n'),
181+
'my log 2\n',
182+
].join(''));
183+
});

tests/playwright-test/list-reporter.spec.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,18 @@ test('render each test with project name', async ({ runInlineTest }) => {
3232
test('passes', async ({}) => {
3333
expect(0).toBe(0);
3434
});
35+
test.skip('skipped', async () => {
36+
});
3537
`,
3638
}, { reporter: 'list' });
3739
const text = stripAscii(result.output);
3840
const positiveStatusMarkPrefix = process.platform === 'win32' ? 'ok' : '✓ ';
3941
const negativateStatusMarkPrefix = process.platform === 'win32' ? 'x ' : '✘ ';
40-
expect(text).toContain(`${negativateStatusMarkPrefix} 1) [foo] › a.test.ts:6:7 › fails`);
41-
expect(text).toContain(`${negativateStatusMarkPrefix} 2) [bar] › a.test.ts:6:7 › fails`);
42+
expect(text).toContain(`${negativateStatusMarkPrefix} [foo] › a.test.ts:6:7 › fails`);
43+
expect(text).toContain(`${negativateStatusMarkPrefix} [bar] › a.test.ts:6:7 › fails`);
4244
expect(text).toContain(`${positiveStatusMarkPrefix} [foo] › a.test.ts:9:7 › passes`);
4345
expect(text).toContain(`${positiveStatusMarkPrefix} [bar] › a.test.ts:9:7 › passes`);
46+
expect(text).toContain(`- [foo] › a.test.ts:12:12 › skipped`);
47+
expect(text).toContain(`- [bar] › a.test.ts:12:12 › skipped`);
4448
expect(result.exitCode).toBe(1);
4549
});

tests/playwright-test/playwright-test-fixtures.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,11 @@ async function runPlaywrightTest(baseDir: string, params: any, env: Env, options
145145
const testProcess = spawn('node', args, {
146146
env: {
147147
...process.env,
148-
...env,
149148
PLAYWRIGHT_JSON_OUTPUT_NAME: reportFile,
150149
PWTEST_CACHE_DIR: cacheDir,
151150
PWTEST_CLI_ALLOW_TEST_COMMAND: '1',
151+
PWTEST_SKIP_TEST_OUTPUT: '1',
152+
...env,
152153
},
153154
cwd: baseDir
154155
});

0 commit comments

Comments
 (0)