chore: move markdown reporter to playwright-dashboard (#35465)
This commit is contained in:
parent
6c5f3bbe39
commit
19d0d54e66
|
@ -9052,7 +9052,7 @@
|
|||
"version": "0.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.52.0-next"
|
||||
"@playwright/test": "1.52.0-next"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
|
|
@ -19,6 +19,6 @@
|
|||
"./package.json": "./package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"playwright-core": "1.52.0-next"
|
||||
"@playwright/test": "1.52.0-next"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,10 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { MarkdownReporter } from 'playwright/lib/internalsForTest';
|
||||
import { context, getOctokit } from '@actions/github';
|
||||
import * as core from '@actions/core';
|
||||
|
||||
import MarkdownReporter from './markdownReporter';
|
||||
|
||||
import type { MetadataWithCommitInfo } from 'playwright/src/isomorphic/types';
|
||||
import type { FullConfig } from '@playwright/test';
|
||||
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import type { FullConfig, FullResult, Reporter, Suite, TestCase, TestError } from '@playwright/test/reporter';
|
||||
|
||||
type MarkdownReporterOptions = {
|
||||
configDir: string, // TODO: make it public?
|
||||
outputFile?: string;
|
||||
};
|
||||
|
||||
class MarkdownReporter implements Reporter {
|
||||
private _options: MarkdownReporterOptions;
|
||||
private _fatalErrors: TestError[] = [];
|
||||
private _config!: FullConfig;
|
||||
private _suite!: Suite;
|
||||
|
||||
constructor(options: MarkdownReporterOptions) {
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
printsToStdio() {
|
||||
return false;
|
||||
}
|
||||
|
||||
onBegin(config: FullConfig, suite: Suite) {
|
||||
this._config = config;
|
||||
this._suite = suite;
|
||||
}
|
||||
|
||||
onError(error: TestError) {
|
||||
this._fatalErrors.push(error);
|
||||
}
|
||||
|
||||
async onEnd(result: FullResult) {
|
||||
const summary = this._generateSummary();
|
||||
const lines: string[] = [];
|
||||
if (this._fatalErrors.length)
|
||||
lines.push(`**${this._fatalErrors.length} fatal errors, not part of any test**`);
|
||||
if (summary.unexpected.length) {
|
||||
lines.push(`**${summary.unexpected.length} failed**`);
|
||||
this._printTestList(':x:', summary.unexpected, lines);
|
||||
}
|
||||
if (summary.flaky.length) {
|
||||
lines.push(`<details>`);
|
||||
lines.push(`<summary><b>${summary.flaky.length} flaky</b></summary>`);
|
||||
this._printTestList(':warning:', summary.flaky, lines, ' <br/>');
|
||||
lines.push(`</details>`);
|
||||
lines.push(``);
|
||||
}
|
||||
if (summary.interrupted.length) {
|
||||
lines.push(`<details>`);
|
||||
lines.push(`<summary><b>${summary.interrupted.length} interrupted</b></summary>`);
|
||||
this._printTestList(':warning:', summary.interrupted, lines, ' <br/>');
|
||||
lines.push(`</details>`);
|
||||
lines.push(``);
|
||||
}
|
||||
const skipped = summary.skipped ? `, ${summary.skipped} skipped` : '';
|
||||
const didNotRun = summary.didNotRun ? `, ${summary.didNotRun} did not run` : '';
|
||||
lines.push(`**${summary.expected} passed${skipped}${didNotRun}**`);
|
||||
lines.push(`:heavy_check_mark::heavy_check_mark::heavy_check_mark:`);
|
||||
lines.push(``);
|
||||
|
||||
await this.publishReport(lines.join('\n'));
|
||||
}
|
||||
|
||||
protected async publishReport(report: string): Promise<void> {
|
||||
const maybeRelativeFile = this._options.outputFile || 'report.md';
|
||||
const reportFile = path.resolve(this._options.configDir, maybeRelativeFile);
|
||||
await fs.promises.mkdir(path.dirname(reportFile), { recursive: true });
|
||||
await fs.promises.writeFile(reportFile, report);
|
||||
}
|
||||
|
||||
protected _generateSummary() {
|
||||
let didNotRun = 0;
|
||||
let skipped = 0;
|
||||
let expected = 0;
|
||||
const interrupted: TestCase[] = [];
|
||||
const interruptedToPrint: TestCase[] = [];
|
||||
const unexpected: TestCase[] = [];
|
||||
const flaky: TestCase[] = [];
|
||||
|
||||
this._suite.allTests().forEach(test => {
|
||||
switch (test.outcome()) {
|
||||
case 'skipped': {
|
||||
if (test.results.some(result => result.status === 'interrupted')) {
|
||||
if (test.results.some(result => !!result.error))
|
||||
interruptedToPrint.push(test);
|
||||
interrupted.push(test);
|
||||
} else if (!test.results.length || test.expectedStatus !== 'skipped') {
|
||||
++didNotRun;
|
||||
} else {
|
||||
++skipped;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'expected': ++expected; break;
|
||||
case 'unexpected': unexpected.push(test); break;
|
||||
case 'flaky': flaky.push(test); break;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
didNotRun,
|
||||
skipped,
|
||||
expected,
|
||||
interrupted,
|
||||
unexpected,
|
||||
flaky,
|
||||
};
|
||||
}
|
||||
|
||||
private _printTestList(prefix: string, tests: TestCase[], lines: string[], suffix?: string) {
|
||||
for (const test of tests)
|
||||
lines.push(`${prefix} ${formatTestTitle(this._config.rootDir, test)}${suffix || ''}`);
|
||||
lines.push(``);
|
||||
}
|
||||
}
|
||||
|
||||
function formatTestTitle(rootDir: string, test: TestCase): string {
|
||||
// root, project, file, ...describes, test
|
||||
const [, projectName, , ...titles] = test.titlePath();
|
||||
const relativeTestPath = path.relative(rootDir, test.location.file);
|
||||
const location = `${relativeTestPath}:${test.location.line}:${test.location.column}`;
|
||||
const projectTitle = projectName ? `[${projectName}] › ` : '';
|
||||
const testTitle = `${projectTitle}${location} › ${titles.join(' › ')}`;
|
||||
const extraTags = test.tags.filter(t => !testTitle.includes(t));
|
||||
return `${testTitle}${extraTags.length ? ' ' + extraTags.join(' ') : ''}`;
|
||||
}
|
||||
|
||||
export default MarkdownReporter;
|
|
@ -17,7 +17,6 @@
|
|||
import path from 'path';
|
||||
|
||||
import { fileDependenciesForTest } from './transform/compilationCache';
|
||||
export { default as MarkdownReporter } from './reporters/markdown';
|
||||
|
||||
export function fileDependencies() {
|
||||
return Object.fromEntries([...fileDependenciesForTest().entries()].map(entry => (
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import { resolveReporterOutputPath } from '../util';
|
||||
import { TerminalReporter } from './base';
|
||||
|
||||
import type { FullResult, TestCase } from '../../types/testReporter';
|
||||
|
||||
type MarkdownReporterOptions = {
|
||||
configDir: string,
|
||||
outputFile?: string;
|
||||
};
|
||||
|
||||
class MarkdownReporter extends TerminalReporter {
|
||||
private _options: MarkdownReporterOptions;
|
||||
|
||||
constructor(options: MarkdownReporterOptions) {
|
||||
super();
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
printsToStdio() {
|
||||
return false;
|
||||
}
|
||||
|
||||
override async onEnd(result: FullResult) {
|
||||
await super.onEnd(result);
|
||||
const summary = this.generateSummary();
|
||||
const lines: string[] = [];
|
||||
if (summary.fatalErrors.length)
|
||||
lines.push(`**${summary.fatalErrors.length} fatal errors, not part of any test**`);
|
||||
if (summary.unexpected.length) {
|
||||
lines.push(`**${summary.unexpected.length} failed**`);
|
||||
this._printTestList(':x:', summary.unexpected, lines);
|
||||
}
|
||||
if (summary.flaky.length) {
|
||||
lines.push(`<details>`);
|
||||
lines.push(`<summary><b>${summary.flaky.length} flaky</b></summary>`);
|
||||
this._printTestList(':warning:', summary.flaky, lines, ' <br/>');
|
||||
lines.push(`</details>`);
|
||||
lines.push(``);
|
||||
}
|
||||
if (summary.interrupted.length) {
|
||||
lines.push(`<details>`);
|
||||
lines.push(`<summary><b>${summary.interrupted.length} interrupted</b></summary>`);
|
||||
this._printTestList(':warning:', summary.interrupted, lines, ' <br/>');
|
||||
lines.push(`</details>`);
|
||||
lines.push(``);
|
||||
}
|
||||
const skipped = summary.skipped ? `, ${summary.skipped} skipped` : '';
|
||||
const didNotRun = summary.didNotRun ? `, ${summary.didNotRun} did not run` : '';
|
||||
lines.push(`**${summary.expected} passed${skipped}${didNotRun}**`);
|
||||
lines.push(`:heavy_check_mark::heavy_check_mark::heavy_check_mark:`);
|
||||
lines.push(``);
|
||||
|
||||
await this.publishReport(lines.join('\n'));
|
||||
}
|
||||
|
||||
protected async publishReport(report: string): Promise<void> {
|
||||
const reportFile = resolveReporterOutputPath('report.md', this._options.configDir, this._options.outputFile);
|
||||
await fs.promises.mkdir(path.dirname(reportFile), { recursive: true });
|
||||
await fs.promises.writeFile(reportFile, report);
|
||||
}
|
||||
|
||||
private _printTestList(prefix: string, tests: TestCase[], lines: string[], suffix?: string) {
|
||||
for (const test of tests)
|
||||
lines.push(`${prefix} ${this.formatTestTitle(test)}${suffix || ''}`);
|
||||
lines.push(``);
|
||||
}
|
||||
}
|
||||
|
||||
export default MarkdownReporter;
|
|
@ -18,7 +18,7 @@ import fs from 'fs';
|
|||
import path from 'path';
|
||||
import { expect, test } from './playwright-test-fixtures';
|
||||
|
||||
const markdownReporter = require.resolve('../../packages/playwright/lib/reporters/markdown');
|
||||
const markdownReporter = require.resolve('../../packages/playwright-dashboard/lib/markdownReporter');
|
||||
|
||||
test('simple report', async ({ runInlineTest }) => {
|
||||
const files = {
|
||||
|
|
Loading…
Reference in New Issue