mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-13 17:14:02 +03:00
fix(html): put HTML report next to package.json
by default (#13141)
Fixes #12970
This commit is contained in:
parent
de0a457856
commit
aa1daeba85
@ -17,7 +17,7 @@
|
||||
import { installTransform, setCurrentlyLoadingTestFile } from './transform';
|
||||
import type { Config, FullProject, Project, ReporterDescription, PreserveOutput } from './types';
|
||||
import type { FullConfigInternal } from './types';
|
||||
import { mergeObjects, errorWithFile } from './util';
|
||||
import { getPackageJsonPath, mergeObjects, errorWithFile } from './util';
|
||||
import { setCurrentlyLoadingFileSuite } from './globals';
|
||||
import { Suite } from './test';
|
||||
import { SerializedLoaderData } from './ipc';
|
||||
@ -99,6 +99,7 @@ export class Loader {
|
||||
const configUse = mergeObjects(this._defaultConfig.use, config.use);
|
||||
config = mergeObjects(mergeObjects(this._defaultConfig, config), { use: configUse });
|
||||
|
||||
(this._fullConfig as any).__configDir = configDir;
|
||||
this._fullConfig.rootDir = config.testDir || this._configDir;
|
||||
this._fullConfig.forbidOnly = takeFirst(this._configOverrides.forbidOnly, config.forbidOnly, baseFullConfig.forbidOnly);
|
||||
this._fullConfig.fullyParallel = takeFirst(this._configOverrides.fullyParallel, config.fullyParallel, baseFullConfig.fullyParallel);
|
||||
@ -498,30 +499,6 @@ export function fileIsModule(file: string): boolean {
|
||||
return folderIsModule(folder);
|
||||
}
|
||||
|
||||
const folderToPackageJsonPath = new Map<string, string>();
|
||||
|
||||
function getPackageJsonPath(folderPath: string): string {
|
||||
const cached = folderToPackageJsonPath.get(folderPath);
|
||||
if (cached !== undefined)
|
||||
return cached;
|
||||
|
||||
const packageJsonPath = path.join(folderPath, 'package.json');
|
||||
if (fs.existsSync(packageJsonPath)) {
|
||||
folderToPackageJsonPath.set(folderPath, packageJsonPath);
|
||||
return packageJsonPath;
|
||||
}
|
||||
|
||||
const parentFolder = path.dirname(folderPath);
|
||||
if (folderPath === parentFolder) {
|
||||
folderToPackageJsonPath.set(folderPath, '');
|
||||
return '';
|
||||
}
|
||||
|
||||
const result = getPackageJsonPath(parentFolder);
|
||||
folderToPackageJsonPath.set(folderPath, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function folderIsModule(folder: string): boolean {
|
||||
const packageJsonPath = getPackageJsonPath(folder);
|
||||
if (!packageJsonPath)
|
||||
|
@ -26,6 +26,7 @@ import RawReporter, { JsonAttachment, JsonReport, JsonSuite, JsonTestCase, JsonT
|
||||
import assert from 'assert';
|
||||
import yazl from 'yazl';
|
||||
import { stripAnsiEscapes } from './base';
|
||||
import { getPackageJsonPath } from '../util';
|
||||
|
||||
export type Stats = {
|
||||
total: number;
|
||||
@ -115,16 +116,19 @@ type TestEntry = {
|
||||
|
||||
const kMissingContentType = 'x-playwright/missing';
|
||||
|
||||
type HtmlReportOpenOption = 'always' | 'never' | 'on-failure';
|
||||
type HtmlReporterOptions = {
|
||||
outputFolder?: string,
|
||||
open?: HtmlReportOpenOption,
|
||||
};
|
||||
|
||||
class HtmlReporter implements Reporter {
|
||||
private config!: FullConfig;
|
||||
private suite!: Suite;
|
||||
private _outputFolder: string | undefined;
|
||||
private _open: 'always' | 'never' | 'on-failure';
|
||||
private _options: HtmlReporterOptions;
|
||||
|
||||
constructor(options: { outputFolder?: string, open?: 'always' | 'never' | 'on-failure' } = {}) {
|
||||
// TODO: resolve relative to config.
|
||||
this._outputFolder = options.outputFolder;
|
||||
this._open = process.env.PW_TEST_HTML_REPORT_OPEN as any || options.open || 'on-failure';
|
||||
constructor(options: HtmlReporterOptions = {}) {
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
printsToStdio() {
|
||||
@ -136,49 +140,68 @@ class HtmlReporter implements Reporter {
|
||||
this.suite = suite;
|
||||
}
|
||||
|
||||
_resolveOptions(): { outputFolder: string, open: HtmlReportOpenOption } {
|
||||
let { outputFolder } = this._options;
|
||||
const configDir: string = (this.config as any).__configDir;
|
||||
if (outputFolder)
|
||||
outputFolder = path.resolve(configDir, outputFolder);
|
||||
return {
|
||||
outputFolder: reportFolderFromEnv() ?? outputFolder ?? defaultReportFolder(configDir),
|
||||
open: process.env.PW_TEST_HTML_REPORT_OPEN as any || this._options.open || 'on-failure',
|
||||
};
|
||||
}
|
||||
|
||||
async onEnd() {
|
||||
const { open, outputFolder } = this._resolveOptions();
|
||||
const projectSuites = this.suite.suites;
|
||||
const reports = projectSuites.map(suite => {
|
||||
const rawReporter = new RawReporter();
|
||||
const report = rawReporter.generateProjectReport(this.config, suite);
|
||||
return report;
|
||||
});
|
||||
const reportFolder = htmlReportFolder(this._outputFolder);
|
||||
await removeFolders([reportFolder]);
|
||||
const builder = new HtmlBuilder(reportFolder);
|
||||
await removeFolders([outputFolder]);
|
||||
const builder = new HtmlBuilder(outputFolder);
|
||||
const { ok, singleTestId } = await builder.build(new RawReporter().generateAttachments(this.config), reports);
|
||||
|
||||
if (process.env.CI)
|
||||
return;
|
||||
|
||||
const shouldOpen = this._open === 'always' || (!ok && this._open === 'on-failure');
|
||||
|
||||
const shouldOpen = open === 'always' || (!ok && open === 'on-failure');
|
||||
if (shouldOpen) {
|
||||
await showHTMLReport(reportFolder, singleTestId);
|
||||
await showHTMLReport(outputFolder, singleTestId);
|
||||
} else {
|
||||
const outputFolderPath = htmlReportFolder(this._outputFolder) === defaultReportFolder() ? '' : ' ' + path.relative(process.cwd(), htmlReportFolder(this._outputFolder));
|
||||
const relativeReportPath = outputFolder === standaloneDefaultFolder() ? '' : ' ' + path.relative(process.cwd(), outputFolder);
|
||||
console.log('');
|
||||
console.log('To open last HTML report run:');
|
||||
console.log(colors.cyan(`
|
||||
npx playwright show-report${outputFolderPath}
|
||||
npx playwright show-report${relativeReportPath}
|
||||
`));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function htmlReportFolder(outputFolder?: string): string {
|
||||
function reportFolderFromEnv(): string | undefined {
|
||||
if (process.env[`PLAYWRIGHT_HTML_REPORT`])
|
||||
return path.resolve(process.cwd(), process.env[`PLAYWRIGHT_HTML_REPORT`]);
|
||||
if (outputFolder)
|
||||
return outputFolder;
|
||||
return defaultReportFolder();
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function defaultReportFolder(): string {
|
||||
return path.resolve(process.cwd(), 'playwright-report');
|
||||
function defaultReportFolder(searchForPackageJson: string): string {
|
||||
let basePath = getPackageJsonPath(searchForPackageJson);
|
||||
if (basePath)
|
||||
basePath = path.dirname(basePath);
|
||||
else
|
||||
basePath = process.cwd();
|
||||
return path.resolve(basePath, 'playwright-report');
|
||||
}
|
||||
|
||||
function standaloneDefaultFolder(): string {
|
||||
return reportFolderFromEnv() ?? defaultReportFolder(process.cwd());
|
||||
}
|
||||
|
||||
export async function showHTMLReport(reportFolder: string | undefined, testId?: string) {
|
||||
const folder = reportFolder || htmlReportFolder();
|
||||
const folder = reportFolder ?? standaloneDefaultFolder();
|
||||
try {
|
||||
assert(fs.statSync(folder).isDirectory());
|
||||
} catch (e) {
|
||||
@ -224,7 +247,7 @@ class HtmlBuilder {
|
||||
private _hasTraces = false;
|
||||
|
||||
constructor(outputDir: string) {
|
||||
this._reportFolder = path.resolve(process.cwd(), outputDir);
|
||||
this._reportFolder = outputDir;
|
||||
fs.mkdirSync(this._reportFolder, { recursive: true });
|
||||
this._dataZipFile = new yazl.ZipFile();
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import util from 'util';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import url from 'url';
|
||||
import colors from 'colors/safe';
|
||||
@ -253,3 +254,27 @@ export function currentExpectTimeout(options: { timeout?: number }) {
|
||||
return defaultExpectTimeout;
|
||||
}
|
||||
|
||||
const folderToPackageJsonPath = new Map<string, string>();
|
||||
|
||||
export function getPackageJsonPath(folderPath: string): string {
|
||||
const cached = folderToPackageJsonPath.get(folderPath);
|
||||
if (cached !== undefined)
|
||||
return cached;
|
||||
|
||||
const packageJsonPath = path.join(folderPath, 'package.json');
|
||||
if (fs.existsSync(packageJsonPath)) {
|
||||
folderToPackageJsonPath.set(folderPath, packageJsonPath);
|
||||
return packageJsonPath;
|
||||
}
|
||||
|
||||
const parentFolder = path.dirname(folderPath);
|
||||
if (folderPath === parentFolder) {
|
||||
folderToPackageJsonPath.set(folderPath, '');
|
||||
return '';
|
||||
}
|
||||
|
||||
const result = getPackageJsonPath(parentFolder);
|
||||
folderToPackageJsonPath.set(folderPath, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import { test as baseTest, expect, createImage } from './playwright-test-fixtures';
|
||||
import { HttpServer } from '../../packages/playwright-core/lib/utils/httpServer';
|
||||
import { startHtmlReportServer } from '../../packages/playwright-test/lib/reporters/html';
|
||||
@ -70,6 +71,31 @@ test('should generate report', async ({ runInlineTest, showReport, page }) => {
|
||||
await expect(page.locator('.metadata-view')).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('should generate report wrt package.json', async ({ runInlineTest }, testInfo) => {
|
||||
const result = await runInlineTest({
|
||||
'foo/package.json': `{ "name": "foo" }`,
|
||||
'foo/bar/playwright.config.js': `
|
||||
module.exports = { projects: [ {} ] };
|
||||
`,
|
||||
'foo/bar/baz/tests/a.spec.js': `
|
||||
const { test } = pwt;
|
||||
const fs = require('fs');
|
||||
test('pass', ({}, testInfo) => {
|
||||
});
|
||||
`
|
||||
}, { 'reporter': 'html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' }, {
|
||||
cwd: 'foo/bar/baz/tests',
|
||||
usesCustomOutputDir: true
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
expect(fs.existsSync(testInfo.outputPath('playwright-report'))).toBe(false);
|
||||
expect(fs.existsSync(testInfo.outputPath('foo', 'playwright-report'))).toBe(true);
|
||||
expect(fs.existsSync(testInfo.outputPath('foo', 'bar', 'playwright-report'))).toBe(false);
|
||||
expect(fs.existsSync(testInfo.outputPath('foo', 'bar', 'baz', 'tests', 'playwright-report'))).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
test('should not throw when attachment is missing', async ({ runInlineTest, page, showReport }, testInfo) => {
|
||||
const result = await runInlineTest({
|
||||
'playwright.config.ts': `
|
||||
|
Loading…
Reference in New Issue
Block a user