chore: move github reporter formatting out of base (#33213)
This commit is contained in:
parent
29ca54eb38
commit
8ec981c394
|
@ -23,12 +23,6 @@ import { resolveReporterOutputPath } from '../util';
|
|||
export type TestResultOutput = { chunk: string | Buffer, type: 'stdout' | 'stderr' };
|
||||
export const kOutputSymbol = Symbol('output');
|
||||
|
||||
type Annotation = {
|
||||
title: string;
|
||||
message: string;
|
||||
location?: Location;
|
||||
};
|
||||
|
||||
type ErrorDetails = {
|
||||
message: string;
|
||||
location?: Location;
|
||||
|
@ -260,9 +254,7 @@ export class BaseReporter implements ReporterV2 {
|
|||
private _printFailures(failures: TestCase[]) {
|
||||
console.log('');
|
||||
failures.forEach((test, index) => {
|
||||
console.log(formatFailure(this.config, test, {
|
||||
index: index + 1,
|
||||
}).message);
|
||||
console.log(formatFailure(this.config, test, index + 1));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -285,14 +277,8 @@ export class BaseReporter implements ReporterV2 {
|
|||
}
|
||||
}
|
||||
|
||||
export function formatFailure(config: FullConfig, test: TestCase, options: {index?: number, includeStdio?: boolean, includeAttachments?: boolean} = {}): {
|
||||
message: string,
|
||||
annotations: Annotation[]
|
||||
} {
|
||||
const { index, includeStdio, includeAttachments = true } = options;
|
||||
export function formatFailure(config: FullConfig, test: TestCase, index?: number): string {
|
||||
const lines: string[] = [];
|
||||
const title = formatTestTitle(config, test);
|
||||
const annotations: Annotation[] = [];
|
||||
const header = formatTestHeader(config, test, { indent: ' ', index, mode: 'error' });
|
||||
lines.push(colors.red(header));
|
||||
for (const result of test.results) {
|
||||
|
@ -307,62 +293,48 @@ export function formatFailure(config: FullConfig, test: TestCase, options: {inde
|
|||
}
|
||||
resultLines.push(...retryLines);
|
||||
resultLines.push(...errors.map(error => '\n' + error.message));
|
||||
if (includeAttachments) {
|
||||
for (let i = 0; i < result.attachments.length; ++i) {
|
||||
const attachment = result.attachments[i];
|
||||
const hasPrintableContent = attachment.contentType.startsWith('text/');
|
||||
if (!attachment.path && !hasPrintableContent)
|
||||
continue;
|
||||
resultLines.push('');
|
||||
resultLines.push(colors.cyan(separator(` attachment #${i + 1}: ${attachment.name} (${attachment.contentType})`)));
|
||||
if (attachment.path) {
|
||||
const relativePath = path.relative(process.cwd(), attachment.path);
|
||||
resultLines.push(colors.cyan(` ${relativePath}`));
|
||||
// Make this extensible
|
||||
if (attachment.name === 'trace') {
|
||||
const packageManagerCommand = getPackageManagerExecCommand();
|
||||
resultLines.push(colors.cyan(` Usage:`));
|
||||
resultLines.push('');
|
||||
resultLines.push(colors.cyan(` ${packageManagerCommand} playwright show-trace ${quotePathIfNeeded(relativePath)}`));
|
||||
resultLines.push('');
|
||||
}
|
||||
} else {
|
||||
if (attachment.contentType.startsWith('text/') && attachment.body) {
|
||||
let text = attachment.body.toString();
|
||||
if (text.length > 300)
|
||||
text = text.slice(0, 300) + '...';
|
||||
for (const line of text.split('\n'))
|
||||
resultLines.push(colors.cyan(` ${line}`));
|
||||
}
|
||||
}
|
||||
resultLines.push(colors.cyan(separator(' ')));
|
||||
}
|
||||
}
|
||||
const output = ((result as any)[kOutputSymbol] || []) as TestResultOutput[];
|
||||
if (includeStdio && output.length) {
|
||||
const outputText = output.map(({ chunk, type }) => {
|
||||
const text = chunk.toString('utf8');
|
||||
if (type === 'stderr')
|
||||
return colors.red(stripAnsiEscapes(text));
|
||||
return text;
|
||||
}).join('');
|
||||
for (let i = 0; i < result.attachments.length; ++i) {
|
||||
const attachment = result.attachments[i];
|
||||
const hasPrintableContent = attachment.contentType.startsWith('text/');
|
||||
if (!attachment.path && !hasPrintableContent)
|
||||
continue;
|
||||
resultLines.push('');
|
||||
resultLines.push(colors.gray(separator('--- Test output')) + '\n\n' + outputText + '\n' + separator());
|
||||
}
|
||||
for (const error of errors) {
|
||||
annotations.push({
|
||||
location: error.location,
|
||||
title,
|
||||
message: [header, ...retryLines, error.message].join('\n'),
|
||||
});
|
||||
resultLines.push(colors.cyan(separator(` attachment #${i + 1}: ${attachment.name} (${attachment.contentType})`)));
|
||||
if (attachment.path) {
|
||||
const relativePath = path.relative(process.cwd(), attachment.path);
|
||||
resultLines.push(colors.cyan(` ${relativePath}`));
|
||||
// Make this extensible
|
||||
if (attachment.name === 'trace') {
|
||||
const packageManagerCommand = getPackageManagerExecCommand();
|
||||
resultLines.push(colors.cyan(` Usage:`));
|
||||
resultLines.push('');
|
||||
resultLines.push(colors.cyan(` ${packageManagerCommand} playwright show-trace ${quotePathIfNeeded(relativePath)}`));
|
||||
resultLines.push('');
|
||||
}
|
||||
} else {
|
||||
if (attachment.contentType.startsWith('text/') && attachment.body) {
|
||||
let text = attachment.body.toString();
|
||||
if (text.length > 300)
|
||||
text = text.slice(0, 300) + '...';
|
||||
for (const line of text.split('\n'))
|
||||
resultLines.push(colors.cyan(` ${line}`));
|
||||
}
|
||||
}
|
||||
resultLines.push(colors.cyan(separator(' ')));
|
||||
}
|
||||
lines.push(...resultLines);
|
||||
}
|
||||
lines.push('');
|
||||
return {
|
||||
message: lines.join('\n'),
|
||||
annotations
|
||||
};
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
export function formatRetry(result: TestResult) {
|
||||
const retryLines = [];
|
||||
if (result.retry) {
|
||||
retryLines.push('');
|
||||
retryLines.push(colors.gray(separator(` Retry #${result.retry}`)));
|
||||
}
|
||||
return retryLines;
|
||||
}
|
||||
|
||||
function quotePathIfNeeded(path: string): string {
|
||||
|
@ -428,7 +400,7 @@ export function formatTestTitle(config: FullConfig, test: TestCase, step?: TestS
|
|||
return `${projectTitle}${location} › ${titles.join(' › ')}${stepSuffix(step)}${tags}`;
|
||||
}
|
||||
|
||||
function formatTestHeader(config: FullConfig, test: TestCase, options: { indent?: string, index?: number, mode?: 'default' | 'error' } = {}): string {
|
||||
export function formatTestHeader(config: FullConfig, test: TestCase, options: { indent?: string, index?: number, mode?: 'default' | 'error' } = {}): string {
|
||||
const title = formatTestTitle(config, test);
|
||||
const header = `${options.indent || ''}${options.index ? options.index + ') ' : ''}${title}`;
|
||||
let fullHeader = header;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
import { ms as milliseconds } from 'playwright-core/lib/utilsBundle';
|
||||
import path from 'path';
|
||||
import { BaseReporter, formatError, formatFailure, stripAnsiEscapes } from './base';
|
||||
import { BaseReporter, colors, formatError, formatResultFailure, formatRetry, formatTestHeader, formatTestTitle, stripAnsiEscapes } from './base';
|
||||
import type { TestCase, FullResult, TestError } from '../../types/testReporter';
|
||||
|
||||
type GitHubLogType = 'debug' | 'notice' | 'warning' | 'error';
|
||||
|
@ -100,22 +100,23 @@ export class GitHubReporter extends BaseReporter {
|
|||
|
||||
private _printFailureAnnotations(failures: TestCase[]) {
|
||||
failures.forEach((test, index) => {
|
||||
const { annotations } = formatFailure(this.config, test, {
|
||||
index: index + 1,
|
||||
includeStdio: true,
|
||||
includeAttachments: false,
|
||||
});
|
||||
annotations.forEach(({ location, title, message }) => {
|
||||
const options: GitHubLogOptions = {
|
||||
file: workspaceRelativePath(location?.file || test.location.file),
|
||||
title,
|
||||
};
|
||||
if (location) {
|
||||
options.line = location.line;
|
||||
options.col = location.column;
|
||||
const title = formatTestTitle(this.config, test);
|
||||
const header = formatTestHeader(this.config, test, { indent: ' ', index: index + 1, mode: 'error' });
|
||||
for (const result of test.results) {
|
||||
const errors = formatResultFailure(test, result, ' ', colors.enabled);
|
||||
for (const error of errors) {
|
||||
const options: GitHubLogOptions = {
|
||||
file: workspaceRelativePath(error.location?.file || test.location.file),
|
||||
title,
|
||||
};
|
||||
if (error.location) {
|
||||
options.line = error.location.line;
|
||||
options.col = error.location.column;
|
||||
}
|
||||
const message = [header, ...formatRetry(result), error.message].join('\n');
|
||||
this.githubLogger.error(message, options);
|
||||
}
|
||||
this.githubLogger.error(message, options);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -188,7 +188,7 @@ class JUnitReporter implements ReporterV2 {
|
|||
message: `${path.basename(test.location.file)}:${test.location.line}:${test.location.column} ${test.title}`,
|
||||
type: 'FAILURE',
|
||||
},
|
||||
text: stripAnsiEscapes(formatFailure(this.config, test).message)
|
||||
text: stripAnsiEscapes(formatFailure(this.config, test))
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -82,9 +82,7 @@ class LineReporter extends BaseReporter {
|
|||
if (!this.willRetry(test) && (test.outcome() === 'flaky' || test.outcome() === 'unexpected' || result.status === 'interrupted')) {
|
||||
if (!process.env.PW_TEST_DEBUG_REPORTERS)
|
||||
process.stdout.write(`\u001B[1A\u001B[2K`);
|
||||
console.log(formatFailure(this.config, test, {
|
||||
index: ++this._failures
|
||||
}).message);
|
||||
console.log(formatFailure(this.config, test, ++this._failures));
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue