mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
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 type TestResultOutput = { chunk: string | Buffer, type: 'stdout' | 'stderr' };
|
||||||
export const kOutputSymbol = Symbol('output');
|
export const kOutputSymbol = Symbol('output');
|
||||||
|
|
||||||
type Annotation = {
|
|
||||||
title: string;
|
|
||||||
message: string;
|
|
||||||
location?: Location;
|
|
||||||
};
|
|
||||||
|
|
||||||
type ErrorDetails = {
|
type ErrorDetails = {
|
||||||
message: string;
|
message: string;
|
||||||
location?: Location;
|
location?: Location;
|
||||||
@ -260,9 +254,7 @@ export class BaseReporter implements ReporterV2 {
|
|||||||
private _printFailures(failures: TestCase[]) {
|
private _printFailures(failures: TestCase[]) {
|
||||||
console.log('');
|
console.log('');
|
||||||
failures.forEach((test, index) => {
|
failures.forEach((test, index) => {
|
||||||
console.log(formatFailure(this.config, test, {
|
console.log(formatFailure(this.config, test, index + 1));
|
||||||
index: index + 1,
|
|
||||||
}).message);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,14 +277,8 @@ export class BaseReporter implements ReporterV2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatFailure(config: FullConfig, test: TestCase, options: {index?: number, includeStdio?: boolean, includeAttachments?: boolean} = {}): {
|
export function formatFailure(config: FullConfig, test: TestCase, index?: number): string {
|
||||||
message: string,
|
|
||||||
annotations: Annotation[]
|
|
||||||
} {
|
|
||||||
const { index, includeStdio, includeAttachments = true } = options;
|
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
const title = formatTestTitle(config, test);
|
|
||||||
const annotations: Annotation[] = [];
|
|
||||||
const header = formatTestHeader(config, test, { indent: ' ', index, mode: 'error' });
|
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) {
|
||||||
@ -307,62 +293,48 @@ export function formatFailure(config: FullConfig, test: TestCase, options: {inde
|
|||||||
}
|
}
|
||||||
resultLines.push(...retryLines);
|
resultLines.push(...retryLines);
|
||||||
resultLines.push(...errors.map(error => '\n' + error.message));
|
resultLines.push(...errors.map(error => '\n' + error.message));
|
||||||
if (includeAttachments) {
|
for (let i = 0; i < result.attachments.length; ++i) {
|
||||||
for (let i = 0; i < result.attachments.length; ++i) {
|
const attachment = result.attachments[i];
|
||||||
const attachment = result.attachments[i];
|
const hasPrintableContent = attachment.contentType.startsWith('text/');
|
||||||
const hasPrintableContent = attachment.contentType.startsWith('text/');
|
if (!attachment.path && !hasPrintableContent)
|
||||||
if (!attachment.path && !hasPrintableContent)
|
continue;
|
||||||
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('');
|
|
||||||
resultLines.push('');
|
resultLines.push('');
|
||||||
resultLines.push(colors.gray(separator('--- Test output')) + '\n\n' + outputText + '\n' + separator());
|
resultLines.push(colors.cyan(separator(` attachment #${i + 1}: ${attachment.name} (${attachment.contentType})`)));
|
||||||
}
|
if (attachment.path) {
|
||||||
for (const error of errors) {
|
const relativePath = path.relative(process.cwd(), attachment.path);
|
||||||
annotations.push({
|
resultLines.push(colors.cyan(` ${relativePath}`));
|
||||||
location: error.location,
|
// Make this extensible
|
||||||
title,
|
if (attachment.name === 'trace') {
|
||||||
message: [header, ...retryLines, error.message].join('\n'),
|
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(...resultLines);
|
||||||
}
|
}
|
||||||
lines.push('');
|
lines.push('');
|
||||||
return {
|
return lines.join('\n');
|
||||||
message: lines.join('\n'),
|
}
|
||||||
annotations
|
|
||||||
};
|
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 {
|
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}`;
|
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 title = formatTestTitle(config, test);
|
||||||
const header = `${options.indent || ''}${options.index ? options.index + ') ' : ''}${title}`;
|
const header = `${options.indent || ''}${options.index ? options.index + ') ' : ''}${title}`;
|
||||||
let fullHeader = header;
|
let fullHeader = header;
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import { ms as milliseconds } from 'playwright-core/lib/utilsBundle';
|
import { ms as milliseconds } from 'playwright-core/lib/utilsBundle';
|
||||||
import path from 'path';
|
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';
|
import type { TestCase, FullResult, TestError } from '../../types/testReporter';
|
||||||
|
|
||||||
type GitHubLogType = 'debug' | 'notice' | 'warning' | 'error';
|
type GitHubLogType = 'debug' | 'notice' | 'warning' | 'error';
|
||||||
@ -100,22 +100,23 @@ export class GitHubReporter extends BaseReporter {
|
|||||||
|
|
||||||
private _printFailureAnnotations(failures: TestCase[]) {
|
private _printFailureAnnotations(failures: TestCase[]) {
|
||||||
failures.forEach((test, index) => {
|
failures.forEach((test, index) => {
|
||||||
const { annotations } = formatFailure(this.config, test, {
|
const title = formatTestTitle(this.config, test);
|
||||||
index: index + 1,
|
const header = formatTestHeader(this.config, test, { indent: ' ', index: index + 1, mode: 'error' });
|
||||||
includeStdio: true,
|
for (const result of test.results) {
|
||||||
includeAttachments: false,
|
const errors = formatResultFailure(test, result, ' ', colors.enabled);
|
||||||
});
|
for (const error of errors) {
|
||||||
annotations.forEach(({ location, title, message }) => {
|
const options: GitHubLogOptions = {
|
||||||
const options: GitHubLogOptions = {
|
file: workspaceRelativePath(error.location?.file || test.location.file),
|
||||||
file: workspaceRelativePath(location?.file || test.location.file),
|
title,
|
||||||
title,
|
};
|
||||||
};
|
if (error.location) {
|
||||||
if (location) {
|
options.line = error.location.line;
|
||||||
options.line = location.line;
|
options.col = error.location.column;
|
||||||
options.col = 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}`,
|
message: `${path.basename(test.location.file)}:${test.location.line}:${test.location.column} ${test.title}`,
|
||||||
type: 'FAILURE',
|
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 (!this.willRetry(test) && (test.outcome() === 'flaky' || test.outcome() === 'unexpected' || result.status === 'interrupted')) {
|
||||||
if (!process.env.PW_TEST_DEBUG_REPORTERS)
|
if (!process.env.PW_TEST_DEBUG_REPORTERS)
|
||||||
process.stdout.write(`\u001B[1A\u001B[2K`);
|
process.stdout.write(`\u001B[1A\u001B[2K`);
|
||||||
console.log(formatFailure(this.config, test, {
|
console.log(formatFailure(this.config, test, ++this._failures));
|
||||||
index: ++this._failures
|
|
||||||
}).message);
|
|
||||||
console.log();
|
console.log();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user