feat(report): add show-report option (#8895)

This commit is contained in:
Pavel Feldman 2021-09-13 15:19:40 -07:00 committed by GitHub
parent f8c0f0d637
commit bb33b8923e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 132 additions and 22 deletions

84
package-lock.json generated
View File

@ -37,6 +37,7 @@
"mime": "^2.4.6",
"minimatch": "^3.0.3",
"ms": "^2.1.2",
"open": "^8.2.1",
"pirates": "^4.0.1",
"pixelmatch": "^5.2.1",
"pngjs": "^5.0.0",
@ -3829,6 +3830,14 @@
"integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==",
"dev": true
},
"node_modules/define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
"engines": {
"node": ">=8"
}
},
"node_modules/define-properties": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
@ -6157,6 +6166,20 @@
"node": ">=0.10.0"
}
},
"node_modules/is-docker": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
"bin": {
"is-docker": "cli.js"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-extendable": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
@ -7421,6 +7444,33 @@
"wrappy": "1"
}
},
"node_modules/open": {
"version": "8.2.1",
"resolved": "https://registry.npmjs.org/open/-/open-8.2.1.tgz",
"integrity": "sha512-rXILpcQlkF/QuFez2BJDf3GsqpjGKbkUUToAIGo9A0Q6ZkoSGogZJulrUdwRkrAsoQvoZsrjCYt8+zblOk7JQQ==",
"dependencies": {
"define-lazy-prop": "^2.0.0",
"is-docker": "^2.1.1",
"is-wsl": "^2.2.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/open/node_modules/is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"dependencies": {
"is-docker": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/optionator": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@ -8894,7 +8944,6 @@
},
"node_modules/socksv5/node_modules/ipv6": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/ipv6/-/ipv6-3.1.1.tgz",
"dev": true,
"inBundle": true,
"license": "MIT",
@ -8913,7 +8962,6 @@
},
"node_modules/socksv5/node_modules/ipv6/node_modules/sprintf": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/sprintf/-/sprintf-0.1.3.tgz",
"dev": true,
"inBundle": true,
"engines": {
@ -13727,6 +13775,11 @@
"integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==",
"dev": true
},
"define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="
},
"define-properties": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
@ -15651,6 +15704,11 @@
}
}
},
"is-docker": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="
},
"is-extendable": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
@ -16677,6 +16735,26 @@
"wrappy": "1"
}
},
"open": {
"version": "8.2.1",
"resolved": "https://registry.npmjs.org/open/-/open-8.2.1.tgz",
"integrity": "sha512-rXILpcQlkF/QuFez2BJDf3GsqpjGKbkUUToAIGo9A0Q6ZkoSGogZJulrUdwRkrAsoQvoZsrjCYt8+zblOk7JQQ==",
"requires": {
"define-lazy-prop": "^2.0.0",
"is-docker": "^2.1.1",
"is-wsl": "^2.2.0"
},
"dependencies": {
"is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"requires": {
"is-docker": "^2.0.0"
}
}
}
},
"optionator": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@ -17860,7 +17938,6 @@
"dependencies": {
"ipv6": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/ipv6/-/ipv6-3.1.1.tgz",
"bundled": true,
"dev": true,
"requires": {
@ -17871,7 +17948,6 @@
"dependencies": {
"sprintf": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/sprintf/-/sprintf-0.1.3.tgz",
"bundled": true,
"dev": true
}

View File

@ -69,6 +69,7 @@
"mime": "^2.4.6",
"minimatch": "^3.0.3",
"ms": "^2.1.2",
"open": "^8.2.1",
"pirates": "^4.0.1",
"pixelmatch": "^5.2.1",
"pngjs": "^5.0.0",

View File

@ -237,8 +237,10 @@ if (!process.env.PW_CLI_TARGET_LANG) {
if (playwrightTestPackagePath) {
require(playwrightTestPackagePath).addTestCommand(program);
if (process.env.PW_EXPERIMENTAL)
if (process.env.PW_EXPERIMENTAL) {
require(playwrightTestPackagePath).addGenerateHtmlCommand(program);
require(playwrightTestPackagePath).addShowHtmlCommand(program);
}
} else {
const command = program.command('test').allowUnknownOption(true);
command.description('Run tests with Playwright Test. Available in @playwright/test package.');

View File

@ -16,15 +16,17 @@
/* eslint-disable no-console */
import * as commander from 'commander';
import * as fs from 'fs';
import * as path from 'path';
import commander from 'commander';
import fs from 'fs';
import open from 'open';
import path from 'path';
import type { Config } from './types';
import { Runner, builtInReporters, BuiltInReporter } from './runner';
import { stopProfiling, startProfiling } from './profiler';
import { FilePatternFilter } from './util';
import { Loader } from './loader';
import { HtmlBuilder } from './html/htmlBuilder';
import { HttpServer } from '../utils/httpServer';
const defaultTimeout = 30000;
const defaultReporter: BuiltInReporter = process.env.CI ? 'dot' : 'list';
@ -84,23 +86,12 @@ export function addTestCommand(program: commander.CommanderStatic) {
}
export function addGenerateHtmlCommand(program: commander.CommanderStatic) {
const command = program.command('generate-html');
const command = program.command('generate-report');
command.description('Generate HTML report');
command.option('-c, --config <file>', `Configuration file, or a test directory with optional "${tsConfig}"/"${jsConfig}"`);
command.option('--output <dir>', `Folder for output artifacts (default: "playwright-report")`, 'playwright-report');
command.action(async opts => {
const output = opts.output;
delete opts.output;
const loader = await createLoader(opts);
const outputFolders = new Set(loader.projects().map(p => p.config.outputDir));
const reportFiles = new Set<string>();
for (const outputFolder of outputFolders) {
const reportFolder = path.join(outputFolder, 'report');
const files = fs.readdirSync(reportFolder).filter(f => f.endsWith('.report'));
for (const file of files)
reportFiles.add(path.join(reportFolder, file));
}
new HtmlBuilder([...reportFiles], output, loader.fullConfig().rootDir);
await generateHTMLReport(opts);
}).on('--help', () => {
console.log('');
console.log('Examples:');
@ -109,6 +100,46 @@ export function addGenerateHtmlCommand(program: commander.CommanderStatic) {
});
}
export function addShowHtmlCommand(program: commander.CommanderStatic) {
const command = program.command('show-report');
command.description('Show HTML report for last run');
command.option('-c, --config <file>', `Configuration file, or a test directory with optional "${tsConfig}"/"${jsConfig}"`);
command.option('--output <dir>', `Folder for output artifacts (default: "playwright-report")`, 'playwright-report');
command.action(async opts => {
const output = await generateHTMLReport(opts);
const server = new HttpServer();
server.routePrefix('/', (request, response) => {
let relativePath = request.url!;
if (relativePath === '/')
relativePath = '/index.html';
const absolutePath = path.join(output, ...relativePath.split('/'));
return server.serveFile(response, absolutePath);
});
open(await server.start());
}).on('--help', () => {
console.log('');
console.log('Examples:');
console.log('');
console.log(' $ show-report');
});
}
async function generateHTMLReport(opts: any): Promise<string> {
const output = opts.output;
delete opts.output;
const loader = await createLoader(opts);
const outputFolders = new Set(loader.projects().map(p => p.config.outputDir));
const reportFiles = new Set<string>();
for (const outputFolder of outputFolders) {
const reportFolder = path.join(outputFolder, 'report');
const files = fs.readdirSync(reportFolder).filter(f => f.endsWith('.report'));
for (const file of files)
reportFiles.add(path.join(reportFolder, file));
}
new HtmlBuilder([...reportFiles], output, loader.fullConfig().rootDir);
return output;
}
async function createLoader(opts: { [key: string]: any }): Promise<Loader> {
if (opts.browser) {
const browserOpt = opts.browser.toLowerCase();

View File

@ -50,7 +50,7 @@ export const Report: React.FC = () => {
return <div className='hbox'>
<SplitView sidebarSize={300} orientation='horizontal' sidebarIsFirst={true}>
<TestCaseView testId={testId}></TestCaseView>
<TestCaseView key={testId?.testId} testId={testId}></TestCaseView>
<div className='suite-tree-column'>
<div className='tab-strip'>{
(['Failing', 'All'] as Filter[]).map(item => {