chore: migrate builtin reporters to ReporterV2 (#23985)
This allows builtin reporters to handle stdio between onConfigure and onBegin. Fixes #23539.
This commit is contained in:
parent
86c1abd934
commit
7e310f79af
|
|
@ -16,9 +16,10 @@
|
||||||
|
|
||||||
import { colors, ms as milliseconds, parseStackTraceLine } from 'playwright-core/lib/utilsBundle';
|
import { colors, ms as milliseconds, parseStackTraceLine } from 'playwright-core/lib/utilsBundle';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import type { FullConfig, TestCase, Suite, TestResult, TestError, FullResult, TestStep, Location, Reporter } from '../../types/testReporter';
|
import type { FullConfig, TestCase, Suite, TestResult, TestError, FullResult, TestStep, Location } from '../../types/testReporter';
|
||||||
import type { SuitePrivate } from '../../types/reporterPrivate';
|
import type { SuitePrivate } from '../../types/reporterPrivate';
|
||||||
import { monotonicTime } from 'playwright-core/lib/utils';
|
import { monotonicTime } from 'playwright-core/lib/utils';
|
||||||
|
import type { ReporterV2 } from './reporterV2';
|
||||||
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');
|
||||||
|
|
||||||
|
|
@ -43,7 +44,7 @@ type TestSummary = {
|
||||||
fatalErrors: TestError[];
|
fatalErrors: TestError[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export class BaseReporter implements Reporter {
|
export class BaseReporter implements ReporterV2 {
|
||||||
duration = 0;
|
duration = 0;
|
||||||
config!: FullConfig;
|
config!: FullConfig;
|
||||||
suite!: Suite;
|
suite!: Suite;
|
||||||
|
|
@ -60,9 +61,16 @@ export class BaseReporter implements Reporter {
|
||||||
this._ttyWidthForTest = parseInt(process.env.PWTEST_TTY_WIDTH || '', 10);
|
this._ttyWidthForTest = parseInt(process.env.PWTEST_TTY_WIDTH || '', 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
onBegin(config: FullConfig, suite: Suite) {
|
version(): 'v2' {
|
||||||
this.monotonicStartTime = monotonicTime();
|
return 'v2';
|
||||||
|
}
|
||||||
|
|
||||||
|
onConfigure(config: FullConfig) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
onBegin(suite: Suite) {
|
||||||
|
this.monotonicStartTime = monotonicTime();
|
||||||
this.suite = suite;
|
this.suite = suite;
|
||||||
this.totalTestCount = suite.allTests().length;
|
this.totalTestCount = suite.allTests().length;
|
||||||
}
|
}
|
||||||
|
|
@ -82,6 +90,9 @@ export class BaseReporter implements Reporter {
|
||||||
(result as any)[kOutputSymbol].push(output);
|
(result as any)[kOutputSymbol].push(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onTestBegin(test: TestCase, result: TestResult): void {
|
||||||
|
}
|
||||||
|
|
||||||
onTestEnd(test: TestCase, result: TestResult) {
|
onTestEnd(test: TestCase, result: TestResult) {
|
||||||
// Ignore any tests that are run in parallel.
|
// Ignore any tests that are run in parallel.
|
||||||
for (let suite: Suite | undefined = test.parent; suite; suite = suite.parent) {
|
for (let suite: Suite | undefined = test.parent; suite; suite = suite.parent) {
|
||||||
|
|
@ -104,6 +115,19 @@ export class BaseReporter implements Reporter {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onStepBegin(test: TestCase, result: TestResult, step: TestStep): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
onStepEnd(test: TestCase, result: TestResult, step: TestStep): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
async onExit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
printsToStdio() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
protected ttyWidth() {
|
protected ttyWidth() {
|
||||||
return this._ttyWidthForTest || process.stdout.columns || 0;
|
return this._ttyWidthForTest || process.stdout.columns || 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,17 @@
|
||||||
|
|
||||||
import { colors } from 'playwright-core/lib/utilsBundle';
|
import { colors } from 'playwright-core/lib/utilsBundle';
|
||||||
import { BaseReporter, formatError } from './base';
|
import { BaseReporter, formatError } from './base';
|
||||||
import type { FullResult, TestCase, TestResult, FullConfig, Suite, TestError } from '../../types/testReporter';
|
import type { FullResult, TestCase, TestResult, Suite, TestError } from '../../types/testReporter';
|
||||||
|
|
||||||
class DotReporter extends BaseReporter {
|
class DotReporter extends BaseReporter {
|
||||||
private _counter = 0;
|
private _counter = 0;
|
||||||
|
|
||||||
printsToStdio() {
|
override printsToStdio() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
override onBegin(config: FullConfig, suite: Suite) {
|
override onBegin(suite: Suite) {
|
||||||
super.onBegin(config, suite);
|
super.onBegin(suite);
|
||||||
console.log(this.generateStartingMessage());
|
console.log(this.generateStartingMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,50 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Reporter } from '../../types/testReporter';
|
import type { ReporterV2 } from './reporterV2';
|
||||||
|
import type { FullConfig, TestCase, TestError, TestResult, FullResult, TestStep, Suite } from '../../types/testReporter';
|
||||||
|
|
||||||
class EmptyReporter implements Reporter {
|
class EmptyReporter implements ReporterV2 {
|
||||||
|
onConfigure(config: FullConfig) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onBegin(suite: Suite) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onTestBegin(test: TestCase, result: TestResult) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onStdOut(chunk: string | Buffer, test?: TestCase, result?: TestResult) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onStdErr(chunk: string | Buffer, test?: TestCase, result?: TestResult) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onTestEnd(test: TestCase, result: TestResult) {
|
||||||
|
}
|
||||||
|
|
||||||
|
async onEnd(result: FullResult) {
|
||||||
|
}
|
||||||
|
|
||||||
|
async onExit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
onError(error: TestError) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onStepBegin(test: TestCase, result: TestResult, step: TestStep) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onStepEnd(test: TestCase, result: TestResult, step: TestStep) {
|
||||||
|
}
|
||||||
|
|
||||||
|
printsToStdio() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
version(): 'v2' {
|
||||||
|
return 'v2';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EmptyReporter;
|
export default EmptyReporter;
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ class GitHubLogger {
|
||||||
export class GitHubReporter extends BaseReporter {
|
export class GitHubReporter extends BaseReporter {
|
||||||
githubLogger = new GitHubLogger();
|
githubLogger = new GitHubLogger();
|
||||||
|
|
||||||
printsToStdio() {
|
override printsToStdio() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import type { TransformCallback } from 'stream';
|
import type { TransformCallback } from 'stream';
|
||||||
import { Transform } from 'stream';
|
import { Transform } from 'stream';
|
||||||
import type { FullConfig, Reporter, Suite } from '../../types/testReporter';
|
import type { FullConfig, Suite } from '../../types/testReporter';
|
||||||
import { HttpServer, assert, calculateSha1, copyFileAndMakeWritable, removeFolders } from 'playwright-core/lib/utils';
|
import { HttpServer, assert, calculateSha1, copyFileAndMakeWritable, removeFolders } from 'playwright-core/lib/utils';
|
||||||
import type { JsonAttachment, JsonReport, JsonSuite, JsonTestCase, JsonTestResult, JsonTestStep } from './raw';
|
import type { JsonAttachment, JsonReport, JsonSuite, JsonTestCase, JsonTestResult, JsonTestStep } from './raw';
|
||||||
import RawReporter from './raw';
|
import RawReporter from './raw';
|
||||||
|
|
@ -31,6 +31,7 @@ import { yazl } from 'playwright-core/lib/zipBundle';
|
||||||
import { mime } from 'playwright-core/lib/utilsBundle';
|
import { mime } from 'playwright-core/lib/utilsBundle';
|
||||||
import type { HTMLReport, Stats, TestAttachment, TestCase, TestCaseSummary, TestFile, TestFileSummary, TestResult, TestStep } from '@html-reporter/types';
|
import type { HTMLReport, Stats, TestAttachment, TestCase, TestCaseSummary, TestFile, TestFileSummary, TestResult, TestStep } from '@html-reporter/types';
|
||||||
import { FullConfigInternal } from '../common/config';
|
import { FullConfigInternal } from '../common/config';
|
||||||
|
import EmptyReporter from './empty';
|
||||||
|
|
||||||
type TestEntry = {
|
type TestEntry = {
|
||||||
testCase: TestCase;
|
testCase: TestCase;
|
||||||
|
|
@ -47,7 +48,7 @@ type HtmlReporterOptions = {
|
||||||
attachmentsBaseURL?: string,
|
attachmentsBaseURL?: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
class HtmlReporter implements Reporter {
|
class HtmlReporter extends EmptyReporter {
|
||||||
private config!: FullConfig;
|
private config!: FullConfig;
|
||||||
private suite!: Suite;
|
private suite!: Suite;
|
||||||
private _options: HtmlReporterOptions;
|
private _options: HtmlReporterOptions;
|
||||||
|
|
@ -57,21 +58,25 @@ class HtmlReporter implements Reporter {
|
||||||
private _buildResult: { ok: boolean, singleTestId: string | undefined } | undefined;
|
private _buildResult: { ok: boolean, singleTestId: string | undefined } | undefined;
|
||||||
|
|
||||||
constructor(options: HtmlReporterOptions) {
|
constructor(options: HtmlReporterOptions) {
|
||||||
|
super();
|
||||||
this._options = options;
|
this._options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
printsToStdio() {
|
override printsToStdio() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onBegin(config: FullConfig, suite: Suite) {
|
override onConfigure(config: FullConfig) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
override onBegin(suite: Suite) {
|
||||||
const { outputFolder, open, attachmentsBaseURL } = this._resolveOptions();
|
const { outputFolder, open, attachmentsBaseURL } = this._resolveOptions();
|
||||||
this._outputFolder = outputFolder;
|
this._outputFolder = outputFolder;
|
||||||
this._open = open;
|
this._open = open;
|
||||||
this._attachmentsBaseURL = attachmentsBaseURL;
|
this._attachmentsBaseURL = attachmentsBaseURL;
|
||||||
const reportedWarnings = new Set<string>();
|
const reportedWarnings = new Set<string>();
|
||||||
for (const project of config.projects) {
|
for (const project of this.config.projects) {
|
||||||
if (outputFolder.startsWith(project.outputDir) || project.outputDir.startsWith(outputFolder)) {
|
if (outputFolder.startsWith(project.outputDir) || project.outputDir.startsWith(outputFolder)) {
|
||||||
const key = outputFolder + '|' + project.outputDir;
|
const key = outputFolder + '|' + project.outputDir;
|
||||||
if (reportedWarnings.has(key))
|
if (reportedWarnings.has(key))
|
||||||
|
|
@ -100,7 +105,7 @@ class HtmlReporter implements Reporter {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async onEnd() {
|
override async onEnd() {
|
||||||
const projectSuites = this.suite.suites;
|
const projectSuites = this.suite.suites;
|
||||||
const reports = projectSuites.map(suite => {
|
const reports = projectSuites.map(suite => {
|
||||||
const rawReporter = new RawReporter();
|
const rawReporter = new RawReporter();
|
||||||
|
|
@ -112,7 +117,7 @@ class HtmlReporter implements Reporter {
|
||||||
this._buildResult = await builder.build(this.config.metadata, reports);
|
this._buildResult = await builder.build(this.config.metadata, reports);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onExit() {
|
override async onExit() {
|
||||||
if (process.env.CI || !this._buildResult)
|
if (process.env.CI || !this._buildResult)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,44 +19,47 @@ import { colors } from 'playwright-core/lib/utilsBundle';
|
||||||
import { codeFrameColumns } from '../transform/babelBundle';
|
import { codeFrameColumns } from '../transform/babelBundle';
|
||||||
import type { FullConfig, TestCase, TestError, TestResult, FullResult, TestStep } from '../../types/testReporter';
|
import type { FullConfig, TestCase, TestError, TestResult, FullResult, TestStep } from '../../types/testReporter';
|
||||||
import { Suite } from '../common/test';
|
import { Suite } from '../common/test';
|
||||||
import { Multiplexer } from './multiplexer';
|
|
||||||
import { prepareErrorStack, relativeFilePath } from './base';
|
import { prepareErrorStack, relativeFilePath } from './base';
|
||||||
import type { ReporterV2 } from './reporterV2';
|
import type { ReporterV2 } from './reporterV2';
|
||||||
|
|
||||||
export class InternalReporter implements ReporterV2 {
|
export class InternalReporter implements ReporterV2 {
|
||||||
private _multiplexer: Multiplexer;
|
private _reporter: ReporterV2;
|
||||||
private _didBegin = false;
|
private _didBegin = false;
|
||||||
private _config!: FullConfig;
|
private _config!: FullConfig;
|
||||||
|
|
||||||
constructor(reporters: ReporterV2[]) {
|
constructor(reporter: ReporterV2) {
|
||||||
this._multiplexer = new Multiplexer(reporters);
|
this._reporter = reporter;
|
||||||
|
}
|
||||||
|
|
||||||
|
version(): 'v2' {
|
||||||
|
return 'v2';
|
||||||
}
|
}
|
||||||
|
|
||||||
onConfigure(config: FullConfig) {
|
onConfigure(config: FullConfig) {
|
||||||
this._config = config;
|
this._config = config;
|
||||||
this._multiplexer.onConfigure(config);
|
this._reporter.onConfigure(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
onBegin(suite: Suite) {
|
onBegin(suite: Suite) {
|
||||||
this._didBegin = true;
|
this._didBegin = true;
|
||||||
this._multiplexer.onBegin(suite);
|
this._reporter.onBegin(suite);
|
||||||
}
|
}
|
||||||
|
|
||||||
onTestBegin(test: TestCase, result: TestResult) {
|
onTestBegin(test: TestCase, result: TestResult) {
|
||||||
this._multiplexer.onTestBegin(test, result);
|
this._reporter.onTestBegin(test, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
onStdOut(chunk: string | Buffer, test?: TestCase, result?: TestResult) {
|
onStdOut(chunk: string | Buffer, test?: TestCase, result?: TestResult) {
|
||||||
this._multiplexer.onStdOut(chunk, test, result);
|
this._reporter.onStdOut(chunk, test, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
onStdErr(chunk: string | Buffer, test?: TestCase, result?: TestResult) {
|
onStdErr(chunk: string | Buffer, test?: TestCase, result?: TestResult) {
|
||||||
this._multiplexer.onStdErr(chunk, test, result);
|
this._reporter.onStdErr(chunk, test, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
onTestEnd(test: TestCase, result: TestResult) {
|
onTestEnd(test: TestCase, result: TestResult) {
|
||||||
this._addSnippetToTestErrors(test, result);
|
this._addSnippetToTestErrors(test, result);
|
||||||
this._multiplexer.onTestEnd(test, result);
|
this._reporter.onTestEnd(test, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onEnd(result: FullResult) {
|
async onEnd(result: FullResult) {
|
||||||
|
|
@ -64,29 +67,29 @@ export class InternalReporter implements ReporterV2 {
|
||||||
// onBegin was not reported, emit it.
|
// onBegin was not reported, emit it.
|
||||||
this.onBegin(new Suite('', 'root'));
|
this.onBegin(new Suite('', 'root'));
|
||||||
}
|
}
|
||||||
await this._multiplexer.onEnd(result);
|
await this._reporter.onEnd(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onExit() {
|
async onExit() {
|
||||||
await this._multiplexer.onExit();
|
await this._reporter.onExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
onError(error: TestError) {
|
onError(error: TestError) {
|
||||||
addLocationAndSnippetToError(this._config, error);
|
addLocationAndSnippetToError(this._config, error);
|
||||||
this._multiplexer.onError(error);
|
this._reporter.onError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
onStepBegin(test: TestCase, result: TestResult, step: TestStep) {
|
onStepBegin(test: TestCase, result: TestResult, step: TestStep) {
|
||||||
this._multiplexer.onStepBegin(test, result, step);
|
this._reporter.onStepBegin(test, result, step);
|
||||||
}
|
}
|
||||||
|
|
||||||
onStepEnd(test: TestCase, result: TestResult, step: TestStep) {
|
onStepEnd(test: TestCase, result: TestResult, step: TestStep) {
|
||||||
this._addSnippetToStepError(test, step);
|
this._addSnippetToStepError(test, step);
|
||||||
this._multiplexer.onStepEnd(test, result, step);
|
this._reporter.onStepEnd(test, result, step);
|
||||||
}
|
}
|
||||||
|
|
||||||
printsToStdio() {
|
printsToStdio() {
|
||||||
return this._multiplexer.printsToStdio();
|
return this._reporter.printsToStdio();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addSnippetToTestErrors(test: TestCase, result: TestResult) {
|
private _addSnippetToTestErrors(test: TestCase, result: TestResult) {
|
||||||
|
|
|
||||||
|
|
@ -16,40 +16,45 @@
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import type { FullConfig, TestCase, Suite, TestResult, TestError, TestStep, FullResult, Location, Reporter, JSONReport, JSONReportSuite, JSONReportSpec, JSONReportTest, JSONReportTestResult, JSONReportTestStep, JSONReportError } from '../../types/testReporter';
|
import type { FullConfig, TestCase, Suite, TestResult, TestError, TestStep, FullResult, Location, JSONReport, JSONReportSuite, JSONReportSpec, JSONReportTest, JSONReportTestResult, JSONReportTestStep, JSONReportError } from '../../types/testReporter';
|
||||||
import { formatError, prepareErrorStack } from './base';
|
import { formatError, prepareErrorStack } from './base';
|
||||||
import { MultiMap } from 'playwright-core/lib/utils';
|
import { MultiMap } from 'playwright-core/lib/utils';
|
||||||
import { assert } from 'playwright-core/lib/utils';
|
import { assert } from 'playwright-core/lib/utils';
|
||||||
import { FullProjectInternal } from '../common/config';
|
import { FullProjectInternal } from '../common/config';
|
||||||
|
import EmptyReporter from './empty';
|
||||||
|
|
||||||
export function toPosixPath(aPath: string): string {
|
export function toPosixPath(aPath: string): string {
|
||||||
return aPath.split(path.sep).join(path.posix.sep);
|
return aPath.split(path.sep).join(path.posix.sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
class JSONReporter implements Reporter {
|
class JSONReporter extends EmptyReporter {
|
||||||
config!: FullConfig;
|
config!: FullConfig;
|
||||||
suite!: Suite;
|
suite!: Suite;
|
||||||
private _errors: TestError[] = [];
|
private _errors: TestError[] = [];
|
||||||
private _outputFile: string | undefined;
|
private _outputFile: string | undefined;
|
||||||
|
|
||||||
constructor(options: { outputFile?: string } = {}) {
|
constructor(options: { outputFile?: string } = {}) {
|
||||||
|
super();
|
||||||
this._outputFile = options.outputFile || reportOutputNameFromEnv();
|
this._outputFile = options.outputFile || reportOutputNameFromEnv();
|
||||||
}
|
}
|
||||||
|
|
||||||
printsToStdio() {
|
override printsToStdio() {
|
||||||
return !this._outputFile;
|
return !this._outputFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
onBegin(config: FullConfig, suite: Suite) {
|
override onConfigure(config: FullConfig) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
override onBegin(suite: Suite) {
|
||||||
this.suite = suite;
|
this.suite = suite;
|
||||||
}
|
}
|
||||||
|
|
||||||
onError(error: TestError): void {
|
override onError(error: TestError): void {
|
||||||
this._errors.push(error);
|
this._errors.push(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onEnd(result: FullResult) {
|
override async onEnd(result: FullResult) {
|
||||||
outputReport(this._serializeReport(), this.config, this._outputFile);
|
outputReport(this._serializeReport(), this.config, this._outputFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,13 @@
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import type { FullConfig, FullResult, Reporter, Suite, TestCase } from '../../types/testReporter';
|
import type { FullConfig, FullResult, Suite, TestCase } from '../../types/testReporter';
|
||||||
import { monotonicTime } from 'playwright-core/lib/utils';
|
import { monotonicTime } from 'playwright-core/lib/utils';
|
||||||
import { formatFailure, stripAnsiEscapes } from './base';
|
import { formatFailure, stripAnsiEscapes } from './base';
|
||||||
import { assert } from 'playwright-core/lib/utils';
|
import { assert } from 'playwright-core/lib/utils';
|
||||||
|
import EmptyReporter from './empty';
|
||||||
|
|
||||||
class JUnitReporter implements Reporter {
|
class JUnitReporter extends EmptyReporter {
|
||||||
private config!: FullConfig;
|
private config!: FullConfig;
|
||||||
private suite!: Suite;
|
private suite!: Suite;
|
||||||
private timestamp!: Date;
|
private timestamp!: Date;
|
||||||
|
|
@ -33,22 +34,26 @@ class JUnitReporter implements Reporter {
|
||||||
private stripANSIControlSequences = false;
|
private stripANSIControlSequences = false;
|
||||||
|
|
||||||
constructor(options: { outputFile?: string, stripANSIControlSequences?: boolean } = {}) {
|
constructor(options: { outputFile?: string, stripANSIControlSequences?: boolean } = {}) {
|
||||||
|
super();
|
||||||
this.outputFile = options.outputFile || reportOutputNameFromEnv();
|
this.outputFile = options.outputFile || reportOutputNameFromEnv();
|
||||||
this.stripANSIControlSequences = options.stripANSIControlSequences || false;
|
this.stripANSIControlSequences = options.stripANSIControlSequences || false;
|
||||||
}
|
}
|
||||||
|
|
||||||
printsToStdio() {
|
override printsToStdio() {
|
||||||
return !this.outputFile;
|
return !this.outputFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
onBegin(config: FullConfig, suite: Suite) {
|
override onConfigure(config: FullConfig) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
override onBegin(suite: Suite) {
|
||||||
this.suite = suite;
|
this.suite = suite;
|
||||||
this.timestamp = new Date();
|
this.timestamp = new Date();
|
||||||
this.startTime = monotonicTime();
|
this.startTime = monotonicTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
async onEnd(result: FullResult) {
|
override async onEnd(result: FullResult) {
|
||||||
const duration = monotonicTime() - this.startTime;
|
const duration = monotonicTime() - this.startTime;
|
||||||
const children: XMLEntry[] = [];
|
const children: XMLEntry[] = [];
|
||||||
for (const projectSuite of this.suite.suites) {
|
for (const projectSuite of this.suite.suites) {
|
||||||
|
|
|
||||||
|
|
@ -16,19 +16,19 @@
|
||||||
|
|
||||||
import { colors } from 'playwright-core/lib/utilsBundle';
|
import { colors } from 'playwright-core/lib/utilsBundle';
|
||||||
import { BaseReporter, formatError, formatFailure, formatTestTitle } from './base';
|
import { BaseReporter, formatError, formatFailure, formatTestTitle } from './base';
|
||||||
import type { FullConfig, TestCase, Suite, TestResult, FullResult, TestStep, TestError } from '../../types/testReporter';
|
import type { TestCase, Suite, TestResult, FullResult, TestStep, TestError } from '../../types/testReporter';
|
||||||
|
|
||||||
class LineReporter extends BaseReporter {
|
class LineReporter extends BaseReporter {
|
||||||
private _current = 0;
|
private _current = 0;
|
||||||
private _failures = 0;
|
private _failures = 0;
|
||||||
private _lastTest: TestCase | undefined;
|
private _lastTest: TestCase | undefined;
|
||||||
|
|
||||||
printsToStdio() {
|
override printsToStdio() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
override onBegin(config: FullConfig, suite: Suite) {
|
override onBegin(suite: Suite) {
|
||||||
super.onBegin(config, suite);
|
super.onBegin(suite);
|
||||||
console.log(this.generateStartingMessage());
|
console.log(this.generateStartingMessage());
|
||||||
console.log();
|
console.log();
|
||||||
}
|
}
|
||||||
|
|
@ -62,17 +62,20 @@ class LineReporter extends BaseReporter {
|
||||||
console.log();
|
console.log();
|
||||||
}
|
}
|
||||||
|
|
||||||
onTestBegin(test: TestCase, result: TestResult) {
|
override onTestBegin(test: TestCase, result: TestResult) {
|
||||||
|
super.onTestBegin(test, result);
|
||||||
++this._current;
|
++this._current;
|
||||||
this._updateLine(test, result, undefined);
|
this._updateLine(test, result, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
onStepBegin(test: TestCase, result: TestResult, step: TestStep) {
|
override onStepBegin(test: TestCase, result: TestResult, step: TestStep) {
|
||||||
|
super.onStepBegin(test, result, step);
|
||||||
if (step.category === 'test.step')
|
if (step.category === 'test.step')
|
||||||
this._updateLine(test, result, step);
|
this._updateLine(test, result, step);
|
||||||
}
|
}
|
||||||
|
|
||||||
onStepEnd(test: TestCase, result: TestResult, step: TestStep) {
|
override onStepEnd(test: TestCase, result: TestResult, step: TestStep) {
|
||||||
|
super.onStepEnd(test, result, step);
|
||||||
if (step.category === 'test.step')
|
if (step.category === 'test.step')
|
||||||
this._updateLine(test, result, step.parent);
|
this._updateLine(test, result, step.parent);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
import { colors, ms as milliseconds } from 'playwright-core/lib/utilsBundle';
|
import { colors, ms as milliseconds } from 'playwright-core/lib/utilsBundle';
|
||||||
import { BaseReporter, formatError, formatTestTitle, stepSuffix, stripAnsiEscapes } from './base';
|
import { BaseReporter, formatError, formatTestTitle, stepSuffix, stripAnsiEscapes } from './base';
|
||||||
import type { FullConfig, FullResult, Suite, TestCase, TestError, TestResult, TestStep } from '../../types/testReporter';
|
import type { FullResult, Suite, TestCase, TestError, TestResult, TestStep } from '../../types/testReporter';
|
||||||
|
|
||||||
// Allow it in the Visual Studio Code Terminal and the new Windows Terminal
|
// Allow it in the Visual Studio Code Terminal and the new Windows Terminal
|
||||||
const DOES_NOT_SUPPORT_UTF8_IN_TERMINAL = process.platform === 'win32' && process.env.TERM_PROGRAM !== 'vscode' && !process.env.WT_SESSION;
|
const DOES_NOT_SUPPORT_UTF8_IN_TERMINAL = process.platform === 'win32' && process.env.TERM_PROGRAM !== 'vscode' && !process.env.WT_SESSION;
|
||||||
|
|
@ -41,12 +41,12 @@ class ListReporter extends BaseReporter {
|
||||||
this._liveTerminal = process.stdout.isTTY || !!process.env.PWTEST_TTY_WIDTH;
|
this._liveTerminal = process.stdout.isTTY || !!process.env.PWTEST_TTY_WIDTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
printsToStdio() {
|
override printsToStdio() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
override onBegin(config: FullConfig, suite: Suite) {
|
override onBegin(suite: Suite) {
|
||||||
super.onBegin(config, suite);
|
super.onBegin(suite);
|
||||||
const startingMessage = this.generateStartingMessage();
|
const startingMessage = this.generateStartingMessage();
|
||||||
if (startingMessage) {
|
if (startingMessage) {
|
||||||
console.log(startingMessage);
|
console.log(startingMessage);
|
||||||
|
|
@ -54,7 +54,8 @@ class ListReporter extends BaseReporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onTestBegin(test: TestCase, result: TestResult) {
|
override onTestBegin(test: TestCase, result: TestResult) {
|
||||||
|
super.onTestBegin(test, result);
|
||||||
if (this._liveTerminal)
|
if (this._liveTerminal)
|
||||||
this._maybeWriteNewLine();
|
this._maybeWriteNewLine();
|
||||||
this._resultIndex.set(result, String(this._resultIndex.size + 1));
|
this._resultIndex.set(result, String(this._resultIndex.size + 1));
|
||||||
|
|
@ -77,7 +78,8 @@ class ListReporter extends BaseReporter {
|
||||||
this._dumpToStdio(test, chunk, process.stderr);
|
this._dumpToStdio(test, chunk, process.stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
onStepBegin(test: TestCase, result: TestResult, step: TestStep) {
|
override onStepBegin(test: TestCase, result: TestResult, step: TestStep) {
|
||||||
|
super.onStepBegin(test, result, step);
|
||||||
if (step.category !== 'test.step')
|
if (step.category !== 'test.step')
|
||||||
return;
|
return;
|
||||||
const testIndex = this._resultIndex.get(result)!;
|
const testIndex = this._resultIndex.get(result)!;
|
||||||
|
|
@ -102,7 +104,8 @@ class ListReporter extends BaseReporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onStepEnd(test: TestCase, result: TestResult, step: TestStep) {
|
override onStepEnd(test: TestCase, result: TestResult, step: TestStep) {
|
||||||
|
super.onStepEnd(test, result, step);
|
||||||
if (step.category !== 'test.step')
|
if (step.category !== 'test.step')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ class MarkdownReporter extends BaseReporter {
|
||||||
this._options = options;
|
this._options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
printsToStdio() {
|
override printsToStdio() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,10 @@ export class Multiplexer implements ReporterV2 {
|
||||||
this._reporters = reporters;
|
this._reporters = reporters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version(): 'v2' {
|
||||||
|
return 'v2';
|
||||||
|
}
|
||||||
|
|
||||||
onConfigure(config: FullConfig) {
|
onConfigure(config: FullConfig) {
|
||||||
for (const reporter of this._reporters)
|
for (const reporter of this._reporters)
|
||||||
wrap(() => reporter.onConfigure(config));
|
wrap(() => reporter.onConfigure(config));
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ export interface ReporterV2 {
|
||||||
onStepBegin(test: TestCase, result: TestResult, step: TestStep): void;
|
onStepBegin(test: TestCase, result: TestResult, step: TestStep): void;
|
||||||
onStepEnd(test: TestCase, result: TestResult, step: TestStep): void;
|
onStepEnd(test: TestCase, result: TestResult, step: TestStep): void;
|
||||||
printsToStdio(): boolean;
|
printsToStdio(): boolean;
|
||||||
|
version(): 'v2';
|
||||||
}
|
}
|
||||||
|
|
||||||
type StdIOChunk = {
|
type StdIOChunk = {
|
||||||
|
|
@ -37,7 +38,16 @@ type StdIOChunk = {
|
||||||
result?: TestResult;
|
result?: TestResult;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ReporterV2Wrapper implements ReporterV2 {
|
export function wrapReporterAsV2(reporter: Reporter | ReporterV2): ReporterV2 {
|
||||||
|
try {
|
||||||
|
if ('version' in reporter && reporter.version() === 'v2')
|
||||||
|
return reporter as ReporterV2;
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
return new ReporterV2Wrapper(reporter as Reporter);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReporterV2Wrapper implements ReporterV2 {
|
||||||
private _reporter: Reporter;
|
private _reporter: Reporter;
|
||||||
private _deferred: { error?: TestError, stdout?: StdIOChunk, stderr?: StdIOChunk }[] | null = [];
|
private _deferred: { error?: TestError, stdout?: StdIOChunk, stderr?: StdIOChunk }[] | null = [];
|
||||||
private _config!: FullConfig;
|
private _config!: FullConfig;
|
||||||
|
|
@ -46,6 +56,10 @@ export class ReporterV2Wrapper implements ReporterV2 {
|
||||||
this._reporter = reporter;
|
this._reporter = reporter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version(): 'v2' {
|
||||||
|
return 'v2';
|
||||||
|
}
|
||||||
|
|
||||||
onConfigure(config: FullConfig) {
|
onConfigure(config: FullConfig) {
|
||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,10 @@ export class TeleReporterEmitter implements ReporterV2 {
|
||||||
this._skipBuffers = skipBuffers;
|
this._skipBuffers = skipBuffers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version(): 'v2' {
|
||||||
|
return 'v2';
|
||||||
|
}
|
||||||
|
|
||||||
onConfigure(config: FullConfig) {
|
onConfigure(config: FullConfig) {
|
||||||
this._rootDir = config.rootDir;
|
this._rootDir = config.rootDir;
|
||||||
this._messageSink({ method: 'onConfigure', params: { config: this._serializeConfig(config) } });
|
this._messageSink({ method: 'onConfigure', params: { config: this._serializeConfig(config) } });
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import type { FullConfig, Reporter, TestError } from '../../types/testReporter';
|
import type { FullConfig, TestError } from '../../types/testReporter';
|
||||||
import { formatError } from '../reporters/base';
|
import { formatError } from '../reporters/base';
|
||||||
import DotReporter from '../reporters/dot';
|
import DotReporter from '../reporters/dot';
|
||||||
import EmptyReporter from '../reporters/empty';
|
import EmptyReporter from '../reporters/empty';
|
||||||
|
|
@ -31,10 +31,11 @@ import type { BuiltInReporter, FullConfigInternal } from '../common/config';
|
||||||
import { loadReporter } from './loadUtils';
|
import { loadReporter } from './loadUtils';
|
||||||
import { BlobReporter } from '../reporters/blob';
|
import { BlobReporter } from '../reporters/blob';
|
||||||
import type { ReporterDescription } from '../../types/test';
|
import type { ReporterDescription } from '../../types/test';
|
||||||
import { type ReporterV2, ReporterV2Wrapper } from '../reporters/reporterV2';
|
import { type ReporterV2, wrapReporterAsV2 } from '../reporters/reporterV2';
|
||||||
|
|
||||||
export async function createReporters(config: FullConfigInternal, mode: 'list' | 'run' | 'ui' | 'merge', descriptions?: ReporterDescription[]): Promise<ReporterV2[]> {
|
export async function createReporters(config: FullConfigInternal, mode: 'list' | 'run' | 'ui' | 'merge', descriptions?: ReporterDescription[]): Promise<ReporterV2[]> {
|
||||||
const defaultReporters: { [key in Exclude<BuiltInReporter, 'blob'>]: new(arg: any) => Reporter } = {
|
const defaultReporters: { [key in BuiltInReporter]: new(arg: any) => ReporterV2 } = {
|
||||||
|
blob: BlobReporter,
|
||||||
dot: mode === 'list' ? ListModeReporter : DotReporter,
|
dot: mode === 'list' ? ListModeReporter : DotReporter,
|
||||||
line: mode === 'list' ? ListModeReporter : LineReporter,
|
line: mode === 'list' ? ListModeReporter : LineReporter,
|
||||||
list: mode === 'list' ? ListModeReporter : ListReporter,
|
list: mode === 'list' ? ListModeReporter : ListReporter,
|
||||||
|
|
@ -50,18 +51,16 @@ export async function createReporters(config: FullConfigInternal, mode: 'list' |
|
||||||
for (const r of descriptions) {
|
for (const r of descriptions) {
|
||||||
const [name, arg] = r;
|
const [name, arg] = r;
|
||||||
const options = { ...arg, configDir: config.configDir };
|
const options = { ...arg, configDir: config.configDir };
|
||||||
if (name === 'blob') {
|
if (name in defaultReporters) {
|
||||||
reporters.push(new BlobReporter(options));
|
reporters.push(new defaultReporters[name as keyof typeof defaultReporters](options));
|
||||||
} else if (name in defaultReporters) {
|
|
||||||
reporters.push(new ReporterV2Wrapper(new defaultReporters[name as keyof typeof defaultReporters](options)));
|
|
||||||
} else {
|
} else {
|
||||||
const reporterConstructor = await loadReporter(config, name);
|
const reporterConstructor = await loadReporter(config, name);
|
||||||
reporters.push(new ReporterV2Wrapper(new reporterConstructor(options)));
|
reporters.push(wrapReporterAsV2(new reporterConstructor(options)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (process.env.PW_TEST_REPORTER) {
|
if (process.env.PW_TEST_REPORTER) {
|
||||||
const reporterConstructor = await loadReporter(config, process.env.PW_TEST_REPORTER);
|
const reporterConstructor = await loadReporter(config, process.env.PW_TEST_REPORTER);
|
||||||
reporters.push(new ReporterV2Wrapper(new reporterConstructor()));
|
reporters.push(wrapReporterAsV2(new reporterConstructor()));
|
||||||
}
|
}
|
||||||
|
|
||||||
const someReporterPrintsToStdio = reporters.some(r => r.printsToStdio());
|
const someReporterPrintsToStdio = reporters.some(r => r.printsToStdio());
|
||||||
|
|
@ -69,18 +68,21 @@ export async function createReporters(config: FullConfigInternal, mode: 'list' |
|
||||||
// Add a line/dot/list-mode reporter for convenience.
|
// Add a line/dot/list-mode reporter for convenience.
|
||||||
// Important to put it first, jsut in case some other reporter stalls onEnd.
|
// Important to put it first, jsut in case some other reporter stalls onEnd.
|
||||||
if (mode === 'list')
|
if (mode === 'list')
|
||||||
reporters.unshift(new ReporterV2Wrapper(new ListModeReporter()));
|
reporters.unshift(new ListModeReporter());
|
||||||
else
|
else
|
||||||
reporters.unshift(new ReporterV2Wrapper(!process.env.CI ? new LineReporter({ omitFailures: true }) : new DotReporter()));
|
reporters.unshift(!process.env.CI ? new LineReporter({ omitFailures: true }) : new DotReporter());
|
||||||
}
|
}
|
||||||
return reporters;
|
return reporters;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ListModeReporter implements Reporter {
|
class ListModeReporter extends EmptyReporter {
|
||||||
private config!: FullConfig;
|
private config!: FullConfig;
|
||||||
|
|
||||||
onBegin(config: FullConfig, suite: Suite): void {
|
override onConfigure(config: FullConfig) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
override onBegin(suite: Suite): void {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(`Listing tests:`);
|
console.log(`Listing tests:`);
|
||||||
const tests = suite.allTests();
|
const tests = suite.allTests();
|
||||||
|
|
@ -88,7 +90,7 @@ class ListModeReporter implements Reporter {
|
||||||
for (const test of tests) {
|
for (const test of tests) {
|
||||||
// root, project, file, ...describes, test
|
// root, project, file, ...describes, test
|
||||||
const [, projectName, , ...titles] = test.titlePath();
|
const [, projectName, , ...titles] = test.titlePath();
|
||||||
const location = `${path.relative(config.rootDir, test.location.file)}:${test.location.line}:${test.location.column}`;
|
const location = `${path.relative(this.config.rootDir, test.location.file)}:${test.location.line}:${test.location.column}`;
|
||||||
const projectTitle = projectName ? `[${projectName}] › ` : '';
|
const projectTitle = projectName ? `[${projectName}] › ` : '';
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(` ${projectTitle}${location} › ${titles.join(' › ')}`);
|
console.log(` ${projectTitle}${location} › ${titles.join(' › ')}`);
|
||||||
|
|
@ -98,7 +100,7 @@ class ListModeReporter implements Reporter {
|
||||||
console.log(`Total: ${tests.length} ${tests.length === 1 ? 'test' : 'tests'} in ${files.size} ${files.size === 1 ? 'file' : 'files'}`);
|
console.log(`Total: ${tests.length} ${tests.length === 1 ? 'test' : 'tests'} in ${files.size} ${files.size === 1 ? 'file' : 'files'}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
onError(error: TestError) {
|
override onError(error: TestError) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error('\n' + formatError(this.config, error, false).message);
|
console.error('\n' + formatError(this.config, error, false).message);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import { colors } from 'playwright-core/lib/utilsBundle';
|
||||||
import { runWatchModeLoop } from './watchMode';
|
import { runWatchModeLoop } from './watchMode';
|
||||||
import { runUIMode } from './uiMode';
|
import { runUIMode } from './uiMode';
|
||||||
import { InternalReporter } from '../reporters/internalReporter';
|
import { InternalReporter } from '../reporters/internalReporter';
|
||||||
|
import { Multiplexer } from '../reporters/multiplexer';
|
||||||
|
|
||||||
type ProjectConfigWithFiles = {
|
type ProjectConfigWithFiles = {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -69,7 +70,7 @@ export class Runner {
|
||||||
// Legacy webServer support.
|
// Legacy webServer support.
|
||||||
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
|
webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p }));
|
||||||
|
|
||||||
const reporter = new InternalReporter(await createReporters(config, listOnly ? 'list' : 'run'));
|
const reporter = new InternalReporter(new Multiplexer(await createReporters(config, listOnly ? 'list' : 'run')));
|
||||||
const taskRunner = listOnly ? createTaskRunnerForList(config, reporter, 'in-process', { failOnLoadErrors: true })
|
const taskRunner = listOnly ? createTaskRunnerForList(config, reporter, 'in-process', { failOnLoadErrors: true })
|
||||||
: createTaskRunner(config, reporter);
|
: createTaskRunner(config, reporter);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import type { FSWatcher } from 'chokidar';
|
||||||
import { open } from 'playwright-core/lib/utilsBundle';
|
import { open } from 'playwright-core/lib/utilsBundle';
|
||||||
import ListReporter from '../reporters/list';
|
import ListReporter from '../reporters/list';
|
||||||
import type { OpenTraceViewerOptions, Transport } from 'playwright-core/lib/server/trace/viewer/traceViewer';
|
import type { OpenTraceViewerOptions, Transport } from 'playwright-core/lib/server/trace/viewer/traceViewer';
|
||||||
import { ReporterV2Wrapper } from '../reporters/reporterV2';
|
import { Multiplexer } from '../reporters/multiplexer';
|
||||||
|
|
||||||
class UIMode {
|
class UIMode {
|
||||||
private _config: FullConfigInternal;
|
private _config: FullConfigInternal;
|
||||||
|
|
@ -68,7 +68,7 @@ class UIMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
async runGlobalSetup(): Promise<FullResult['status']> {
|
async runGlobalSetup(): Promise<FullResult['status']> {
|
||||||
const reporter = new InternalReporter([new ReporterV2Wrapper(new ListReporter())]);
|
const reporter = new InternalReporter(new ListReporter());
|
||||||
const taskRunner = createTaskRunnerForWatchSetup(this._config, reporter);
|
const taskRunner = createTaskRunnerForWatchSetup(this._config, reporter);
|
||||||
reporter.onConfigure(this._config.config);
|
reporter.onConfigure(this._config.config);
|
||||||
const testRun = new TestRun(this._config, reporter);
|
const testRun = new TestRun(this._config, reporter);
|
||||||
|
|
@ -161,8 +161,7 @@ class UIMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _listTests() {
|
private async _listTests() {
|
||||||
const listReporter = new TeleReporterEmitter(e => this._dispatchEvent(e.method, e.params), true);
|
const reporter = new InternalReporter(new TeleReporterEmitter(e => this._dispatchEvent(e.method, e.params), true));
|
||||||
const reporter = new InternalReporter([listReporter]);
|
|
||||||
this._config.cliListOnly = true;
|
this._config.cliListOnly = true;
|
||||||
this._config.testIdMatcher = undefined;
|
this._config.testIdMatcher = undefined;
|
||||||
const taskRunner = createTaskRunnerForList(this._config, reporter, 'out-of-process', { failOnLoadErrors: false });
|
const taskRunner = createTaskRunnerForList(this._config, reporter, 'out-of-process', { failOnLoadErrors: false });
|
||||||
|
|
@ -188,7 +187,7 @@ class UIMode {
|
||||||
|
|
||||||
const reporters = await createReporters(this._config, 'ui');
|
const reporters = await createReporters(this._config, 'ui');
|
||||||
reporters.push(new TeleReporterEmitter(e => this._dispatchEvent(e.method, e.params), true));
|
reporters.push(new TeleReporterEmitter(e => this._dispatchEvent(e.method, e.params), true));
|
||||||
const reporter = new InternalReporter(reporters);
|
const reporter = new InternalReporter(new Multiplexer(reporters));
|
||||||
const taskRunner = createTaskRunnerForWatch(this._config, reporter);
|
const taskRunner = createTaskRunnerForWatch(this._config, reporter);
|
||||||
const testRun = new TestRun(this._config, reporter);
|
const testRun = new TestRun(this._config, reporter);
|
||||||
clearCompilationCache();
|
clearCompilationCache();
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ import { enquirer } from '../utilsBundle';
|
||||||
import { separator } from '../reporters/base';
|
import { separator } from '../reporters/base';
|
||||||
import { PlaywrightServer } from 'playwright-core/lib/remote/playwrightServer';
|
import { PlaywrightServer } from 'playwright-core/lib/remote/playwrightServer';
|
||||||
import ListReporter from '../reporters/list';
|
import ListReporter from '../reporters/list';
|
||||||
import { ReporterV2Wrapper } from '../reporters/reporterV2';
|
|
||||||
|
|
||||||
class FSWatcher {
|
class FSWatcher {
|
||||||
private _dirtyTestFiles = new Map<FullProjectInternal, Set<string>>();
|
private _dirtyTestFiles = new Map<FullProjectInternal, Set<string>>();
|
||||||
|
|
@ -113,7 +112,7 @@ export async function runWatchModeLoop(config: FullConfigInternal): Promise<Full
|
||||||
p.project.retries = 0;
|
p.project.retries = 0;
|
||||||
|
|
||||||
// Perform global setup.
|
// Perform global setup.
|
||||||
const reporter = new InternalReporter([new ReporterV2Wrapper(new ListReporter())]);
|
const reporter = new InternalReporter(new ListReporter());
|
||||||
const testRun = new TestRun(config, reporter);
|
const testRun = new TestRun(config, reporter);
|
||||||
const taskRunner = createTaskRunnerForWatchSetup(config, reporter);
|
const taskRunner = createTaskRunnerForWatchSetup(config, reporter);
|
||||||
reporter.onConfigure(config.config);
|
reporter.onConfigure(config.config);
|
||||||
|
|
@ -281,7 +280,7 @@ async function runTests(config: FullConfigInternal, failedTestIdCollector: Set<s
|
||||||
title?: string,
|
title?: string,
|
||||||
}) {
|
}) {
|
||||||
printConfiguration(config, options?.title);
|
printConfiguration(config, options?.title);
|
||||||
const reporter = new InternalReporter([new ReporterV2Wrapper(new ListReporter())]);
|
const reporter = new InternalReporter(new ListReporter());
|
||||||
const taskRunner = createTaskRunnerForWatch(config, reporter, options?.additionalFileMatcher);
|
const taskRunner = createTaskRunnerForWatch(config, reporter, options?.additionalFileMatcher);
|
||||||
const testRun = new TestRun(config, reporter);
|
const testRun = new TestRun(config, reporter);
|
||||||
clearCompilationCache();
|
clearCompilationCache();
|
||||||
|
|
|
||||||
|
|
@ -606,6 +606,8 @@ const refreshRootSuite = (eraseResults: boolean): Promise<void> => {
|
||||||
};
|
};
|
||||||
let config: FullConfig;
|
let config: FullConfig;
|
||||||
receiver = new TeleReporterReceiver(pathSeparator, {
|
receiver = new TeleReporterReceiver(pathSeparator, {
|
||||||
|
version: () => 'v2',
|
||||||
|
|
||||||
onConfigure: (c: FullConfig) => {
|
onConfigure: (c: FullConfig) => {
|
||||||
config = c;
|
config = c;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -704,3 +704,32 @@ test('should be able to ignore "stderr"', async ({ runInlineTest }, { workerInde
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
expect(result.output).not.toContain('error from server');
|
expect(result.output).not.toContain('error from server');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should forward stdout when set to "pipe" before server is ready', async ({ runInlineTest }, { workerIndex }) => {
|
||||||
|
test.skip(process.platform === 'win32', 'No sending SIGINT on Windows');
|
||||||
|
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'web-server.js': `
|
||||||
|
console.log('output from server');
|
||||||
|
console.log('\\n%%SEND-SIGINT%%');
|
||||||
|
setTimeout(() => {}, 10000000);
|
||||||
|
`,
|
||||||
|
'test.spec.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('pass', async ({}) => {});
|
||||||
|
`,
|
||||||
|
'playwright.config.ts': `
|
||||||
|
module.exports = {
|
||||||
|
webServer: {
|
||||||
|
command: 'node web-server.js',
|
||||||
|
port: 12345,
|
||||||
|
stdout: 'pipe',
|
||||||
|
timeout: 3000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
`,
|
||||||
|
}, { workers: 1 }, {}, { sendSIGINTAfter: 1 });
|
||||||
|
expect(result.passed).toBe(0);
|
||||||
|
expect(result.output).toContain('[WebServer] output from server');
|
||||||
|
expect(result.output).not.toContain('Timed out waiting 3000ms');
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue