chore: move github reporter formatting out of base (#33213)

This commit is contained in:
Yury Semikhatsky 2024-10-22 11:45:12 -07:00 committed by GitHub
parent 29ca54eb38
commit 8ec981c394
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 59 additions and 88 deletions

View File

@ -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;

View File

@ -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);
});
}
});
}
}

View File

@ -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))
});
}

View File

@ -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();
}
}