chore: render failed steps in the basic reporters (#22200)

Fixes #20532
This commit is contained in:
Pavel Feldman 2023-04-05 13:03:42 -07:00 committed by GitHub
parent 0d31d69d65
commit 159e71982e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 121 additions and 24 deletions

View file

@ -144,17 +144,17 @@ export class BaseReporter implements Reporter {
if (unexpected.length) { if (unexpected.length) {
tokens.push(colors.red(` ${unexpected.length} failed`)); tokens.push(colors.red(` ${unexpected.length} failed`));
for (const test of unexpected) for (const test of unexpected)
tokens.push(colors.red(formatTestHeader(this.config, test, ' '))); tokens.push(colors.red(formatTestHeader(this.config, test, { indent: ' ' })));
} }
if (interrupted.length) { if (interrupted.length) {
tokens.push(colors.yellow(` ${interrupted.length} interrupted`)); tokens.push(colors.yellow(` ${interrupted.length} interrupted`));
for (const test of interrupted) for (const test of interrupted)
tokens.push(colors.yellow(formatTestHeader(this.config, test, ' '))); tokens.push(colors.yellow(formatTestHeader(this.config, test, { indent: ' ' })));
} }
if (flaky.length) { if (flaky.length) {
tokens.push(colors.yellow(` ${flaky.length} flaky`)); tokens.push(colors.yellow(` ${flaky.length} flaky`));
for (const test of flaky) for (const test of flaky)
tokens.push(colors.yellow(formatTestHeader(this.config, test, ' '))); tokens.push(colors.yellow(formatTestHeader(this.config, test, { indent: ' ' })));
} }
if (skipped) if (skipped)
tokens.push(colors.yellow(` ${skipped} skipped`)); tokens.push(colors.yellow(` ${skipped} skipped`));
@ -251,7 +251,7 @@ export function formatFailure(config: FullConfig, test: TestCase, options: {inde
const lines: string[] = []; const lines: string[] = [];
const title = formatTestTitle(config, test); const title = formatTestTitle(config, test);
const annotations: Annotation[] = []; const annotations: Annotation[] = [];
const header = formatTestHeader(config, test, ' ', index); const header = formatTestHeader(config, test, { indent: ' ', index, mode: 'error' });
lines.push(colors.red(header)); lines.push(colors.red(header));
for (const result of test.results) { for (const result of test.results) {
const resultLines: string[] = []; const resultLines: string[] = [];
@ -371,10 +371,31 @@ export function formatTestTitle(config: FullConfig, test: TestCase, step?: TestS
return `${projectTitle}${location} ${titles.join(' ')}${stepSuffix(step)}`; return `${projectTitle}${location} ${titles.join(' ')}${stepSuffix(step)}`;
} }
function formatTestHeader(config: FullConfig, test: TestCase, indent: string, index?: number): string { function formatTestHeader(config: FullConfig, test: TestCase, options: { indent?: string, index?: number, mode?: 'default' | 'error' } = {}): string {
const title = formatTestTitle(config, test); const title = formatTestTitle(config, test);
const header = `${indent}${index ? index + ') ' : ''}${title}`; const header = `${options.indent || ''}${options.index ? options.index + ') ' : ''}${title}`;
return separator(header); let fullHeader = header;
// Render the path to the deepest failing test.step.
if (options.mode === 'error') {
const stepPaths = new Set<string>();
for (const result of test.results.filter(r => !!r.errors.length)) {
const stepPath: string[] = [];
const visit = (steps: TestStep[]) => {
const errors = steps.filter(s => s.error);
if (errors.length > 1)
return;
if (errors.length === 1 && errors[0].category === 'test.step') {
stepPath.push(errors[0].title);
visit(errors[0].steps);
}
};
visit(result.steps);
stepPaths.add(['', ...stepPath].join(' '));
}
fullHeader = header + (stepPaths.size === 1 ? stepPaths.values().next().value : '');
}
return separator(fullHeader);
} }
export function formatError(config: FullConfig, error: TestError, highlightCode: boolean): ErrorDetails { export function formatError(config: FullConfig, error: TestError, highlightCode: boolean): ErrorDetails {

View file

@ -104,3 +104,61 @@ test('should print output', async ({ runInlineTest }) => {
'full-line', 'full-line',
].join('\n')); ].join('\n'));
}); });
test('should render failed test steps', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
import { test, expect } from '@playwright/test';
test('passes', async ({}) => {
await test.step('outer 1.0', async () => {
await test.step('inner 1.1', async () => {
expect(1).toBe(2);
});
});
});
`,
}, { reporter: 'line' });
const text = result.output;
expect(text).toContain('1) a.test.ts:3:11 passes outer 1.0 inner 1.1 ──');
expect(result.exitCode).toBe(1);
});
test('should not render more than one failed test steps in header', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
import { test, expect } from '@playwright/test';
test('passes', async ({}) => {
await test.step('outer 1.0', async () => {
await test.step('inner 1.1', async () => {
expect.soft(1).toBe(2);
});
await test.step('inner 1.2', async () => {
expect.soft(1).toBe(2);
});
});
});
`,
}, { reporter: 'line' });
const text = result.output;
expect(text).toContain('1) a.test.ts:3:11 passes outer 1.0 ──');
expect(result.exitCode).toBe(1);
});
test('should not render more than one failed test steps in header (2)', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
import { test, expect } from '@playwright/test';
test('passes', async ({}) => {
await test.step('outer 1.0', async () => {
await test.step('inner 1.1', async () => {
expect.soft(1).toBe(2);
});
});
expect.soft(1).toBe(2);
});
`,
}, { reporter: 'line' });
const text = result.output;
expect(text).toContain('1) a.test.ts:3:11 passes ──');
expect(result.exitCode).toBe(1);
});

View file

@ -87,7 +87,7 @@ test('render steps', async ({ runInlineTest }) => {
]); ]);
}); });
test('render steps inlint', async ({ runInlineTest }) => { test('render steps inline', async ({ runInlineTest }) => {
const result = await runInlineTest({ const result = await runInlineTest({
'a.test.ts': ` 'a.test.ts': `
import { test, expect } from '@playwright/test'; import { test, expect } from '@playwright/test';
@ -209,6 +209,24 @@ test('should truncate long test names', async ({ runInlineTest }) => {
expect(lines[7]).toBe(` - 4 …› a.test.ts:10:12 skipped very long name`); expect(lines[7]).toBe(` - 4 …› a.test.ts:10:12 skipped very long name`);
}); });
test('render failed test steps', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
import { test, expect } from '@playwright/test';
test('passes', async ({}) => {
await test.step('outer 1.0', async () => {
await test.step('inner 1.1', async () => {
expect(1).toBe(2);
});
});
});
`,
}, { reporter: 'list' });
const text = result.output;
expect(text).toContain('1) a.test.ts:3:11 passes outer 1.0 inner 1.1 ──');
expect(result.exitCode).toBe(1);
});
function simpleAnsiRenderer(text, ttyWidth) { function simpleAnsiRenderer(text, ttyWidth) {
let lineNumber = 0; let lineNumber = 0;
let columnNumber = 0; let columnNumber = 0;

View file

@ -5,16 +5,16 @@
"packages": { "packages": {
"": { "": {
"dependencies": { "dependencies": {
"@playwright/test": "1.33.0-alpha-mar-20-2023" "@playwright/test": "1.33.0-alpha-apr-4-2023"
} }
}, },
"node_modules/@playwright/test": { "node_modules/@playwright/test": {
"version": "1.33.0-alpha-mar-20-2023", "version": "1.33.0-alpha-apr-4-2023",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.33.0-alpha-mar-20-2023.tgz", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.33.0-alpha-apr-4-2023.tgz",
"integrity": "sha512-547fmDpyQKHMsV17WSXQf1x2XkJ80raOpw+eHpmiROItsMpe7lqUC4OI1hgMxo5miuyPBWOABuuFnXKS11uaFw==", "integrity": "sha512-sURaHids6lQjVEi2qBm4eJzgg8khwbbwTO/nkmc/fH0xek7BQNo1zcJ+MvrYtarmCeKbMoOj1x/O8FBNgox61g==",
"dependencies": { "dependencies": {
"@types/node": "*", "@types/node": "*",
"playwright-core": "1.33.0-alpha-mar-20-2023" "playwright-core": "1.33.0-alpha-apr-4-2023"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -45,9 +45,9 @@
} }
}, },
"node_modules/playwright-core": { "node_modules/playwright-core": {
"version": "1.33.0-alpha-mar-20-2023", "version": "1.33.0-alpha-apr-4-2023",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.33.0-alpha-mar-20-2023.tgz", "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.33.0-alpha-apr-4-2023.tgz",
"integrity": "sha512-cjyn0tVVtJa7Nd3/FZf3GHyyDZPEeVTLN2kZ/G3589yiV6oEZgKgxwtMuUK7NPctmctUFPN6eKaPihH6rpNKbA==", "integrity": "sha512-rPormzFZ2n1AkAqG9ZdeaLkv9mrIkBpidtU47r50RYhe/K5PJDg/kanc4YQzG1ZAPU/t+ijjf/OyGhI/p7piKA==",
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
}, },
@ -58,13 +58,13 @@
}, },
"dependencies": { "dependencies": {
"@playwright/test": { "@playwright/test": {
"version": "1.33.0-alpha-mar-20-2023", "version": "1.33.0-alpha-apr-4-2023",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.33.0-alpha-mar-20-2023.tgz", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.33.0-alpha-apr-4-2023.tgz",
"integrity": "sha512-547fmDpyQKHMsV17WSXQf1x2XkJ80raOpw+eHpmiROItsMpe7lqUC4OI1hgMxo5miuyPBWOABuuFnXKS11uaFw==", "integrity": "sha512-sURaHids6lQjVEi2qBm4eJzgg8khwbbwTO/nkmc/fH0xek7BQNo1zcJ+MvrYtarmCeKbMoOj1x/O8FBNgox61g==",
"requires": { "requires": {
"@types/node": "*", "@types/node": "*",
"fsevents": "2.3.2", "fsevents": "2.3.2",
"playwright-core": "1.33.0-alpha-mar-20-2023" "playwright-core": "1.33.0-alpha-apr-4-2023"
} }
}, },
"@types/node": { "@types/node": {
@ -79,9 +79,9 @@
"optional": true "optional": true
}, },
"playwright-core": { "playwright-core": {
"version": "1.33.0-alpha-mar-20-2023", "version": "1.33.0-alpha-apr-4-2023",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.33.0-alpha-mar-20-2023.tgz", "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.33.0-alpha-apr-4-2023.tgz",
"integrity": "sha512-cjyn0tVVtJa7Nd3/FZf3GHyyDZPEeVTLN2kZ/G3589yiV6oEZgKgxwtMuUK7NPctmctUFPN6eKaPihH6rpNKbA==" "integrity": "sha512-rPormzFZ2n1AkAqG9ZdeaLkv9mrIkBpidtU47r50RYhe/K5PJDg/kanc4YQzG1ZAPU/t+ijjf/OyGhI/p7piKA=="
} }
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"private": true, "private": true,
"dependencies": { "dependencies": {
"@playwright/test": "1.33.0-alpha-mar-20-2023" "@playwright/test": "1.33.0-alpha-apr-4-2023"
} }
} }