mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: move markdown reporter to playwright-dashboard (#35465)
This commit is contained in:
parent
6c5f3bbe39
commit
19d0d54e66
2
package-lock.json
generated
2
package-lock.json
generated
@ -9052,7 +9052,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.52.0-next"
|
"@playwright/test": "1.52.0-next"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
|
|||||||
@ -19,6 +19,6 @@
|
|||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.52.0-next"
|
"@playwright/test": "1.52.0-next"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,10 +14,11 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { MarkdownReporter } from 'playwright/lib/internalsForTest';
|
|
||||||
import { context, getOctokit } from '@actions/github';
|
import { context, getOctokit } from '@actions/github';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
|
|
||||||
|
import MarkdownReporter from './markdownReporter';
|
||||||
|
|
||||||
import type { MetadataWithCommitInfo } from 'playwright/src/isomorphic/types';
|
import type { MetadataWithCommitInfo } from 'playwright/src/isomorphic/types';
|
||||||
import type { FullConfig } from '@playwright/test';
|
import type { FullConfig } from '@playwright/test';
|
||||||
|
|
||||||
|
|||||||
146
packages/playwright-dashboard/src/markdownReporter.ts
Normal file
146
packages/playwright-dashboard/src/markdownReporter.ts
Normal file
@ -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 path from 'path';
|
||||||
|
|
||||||
import { fileDependenciesForTest } from './transform/compilationCache';
|
import { fileDependenciesForTest } from './transform/compilationCache';
|
||||||
export { default as MarkdownReporter } from './reporters/markdown';
|
|
||||||
|
|
||||||
export function fileDependencies() {
|
export function fileDependencies() {
|
||||||
return Object.fromEntries([...fileDependenciesForTest().entries()].map(entry => (
|
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 path from 'path';
|
||||||
import { expect, test } from './playwright-test-fixtures';
|
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 }) => {
|
test('simple report', async ({ runInlineTest }) => {
|
||||||
const files = {
|
const files = {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user