chore: split InternalReporter and Multiplexer (#22671)
The multiplexer will be reused in the blob report merger.
This commit is contained in:
		
							parent
							
								
									e809ecdc5d
								
							
						
					
					
						commit
						51aca72c35
					
				|  | @ -15,11 +15,11 @@ | |||
|  */ | ||||
| 
 | ||||
| import type { FullConfig, Suite } from '../../types/testReporter'; | ||||
| import type { Multiplexer } from '../reporters/multiplexer'; | ||||
| import type { InternalReporter } from '../reporters/internalReporter'; | ||||
| 
 | ||||
| export interface TestRunnerPlugin { | ||||
|   name: string; | ||||
|   setup?(config: FullConfig, configDir: string, reporter: Multiplexer): Promise<void>; | ||||
|   setup?(config: FullConfig, configDir: string, reporter: InternalReporter): Promise<void>; | ||||
|   babelPlugins?(): Promise<[string, any?][]>; | ||||
|   begin?(suite: Suite): Promise<void>; | ||||
|   end?(): Promise<void>; | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ import type { FullConfig } from '../../types/testReporter'; | |||
| import type { TestRunnerPlugin } from '.'; | ||||
| import type { FullConfigInternal } from '../common/config'; | ||||
| import { envWithoutExperimentalLoaderOptions } from '../util'; | ||||
| import type { Multiplexer } from '../reporters/multiplexer'; | ||||
| import type { InternalReporter } from '../reporters/internalReporter'; | ||||
| 
 | ||||
| 
 | ||||
| export type WebServerPluginOptions = { | ||||
|  | @ -49,7 +49,7 @@ export class WebServerPlugin implements TestRunnerPlugin { | |||
|   private _processExitedPromise!: Promise<any>; | ||||
|   private _options: WebServerPluginOptions; | ||||
|   private _checkPortOnly: boolean; | ||||
|   private _reporter?: Multiplexer; | ||||
|   private _reporter?: InternalReporter; | ||||
|   name = 'playwright:webserver'; | ||||
| 
 | ||||
|   constructor(options: WebServerPluginOptions, checkPortOnly: boolean) { | ||||
|  | @ -57,7 +57,7 @@ export class WebServerPlugin implements TestRunnerPlugin { | |||
|     this._checkPortOnly = checkPortOnly; | ||||
|   } | ||||
| 
 | ||||
|   public async setup(config: FullConfig, configDir: string, reporter: Multiplexer) { | ||||
|   public async setup(config: FullConfig, configDir: string, reporter: InternalReporter) { | ||||
|     this._reporter = reporter; | ||||
|     this._isAvailable = getIsAvailableFunction(this._options.url, this._checkPortOnly, !!this._options.ignoreHTTPSErrors, this._reporter.onStdErr?.bind(this._reporter)); | ||||
|     this._options.cwd = this._options.cwd ? path.resolve(configDir, this._options.cwd) : configDir; | ||||
|  | @ -148,7 +148,7 @@ async function isPortUsed(port: number): Promise<boolean> { | |||
|   return await innerIsPortUsed('127.0.0.1') || await innerIsPortUsed('::1'); | ||||
| } | ||||
| 
 | ||||
| async function isURLAvailable(url: URL, ignoreHTTPSErrors: boolean, onStdErr: Multiplexer['onStdErr']) { | ||||
| async function isURLAvailable(url: URL, ignoreHTTPSErrors: boolean, onStdErr: InternalReporter['onStdErr']) { | ||||
|   let statusCode = await httpStatusCode(url, ignoreHTTPSErrors, onStdErr); | ||||
|   if (statusCode === 404 && url.pathname === '/') { | ||||
|     const indexUrl = new URL(url); | ||||
|  | @ -158,7 +158,7 @@ async function isURLAvailable(url: URL, ignoreHTTPSErrors: boolean, onStdErr: Mu | |||
|   return statusCode >= 200 && statusCode < 404; | ||||
| } | ||||
| 
 | ||||
| async function httpStatusCode(url: URL, ignoreHTTPSErrors: boolean, onStdErr: Multiplexer['onStdErr']): Promise<number> { | ||||
| async function httpStatusCode(url: URL, ignoreHTTPSErrors: boolean, onStdErr: InternalReporter['onStdErr']): Promise<number> { | ||||
|   return new Promise(resolve => { | ||||
|     debugWebServer(`HTTP GET: ${url}`); | ||||
|     httpRequest({ | ||||
|  | @ -191,7 +191,7 @@ async function waitFor(waitFn: () => Promise<boolean>, cancellationToken: { canc | |||
|   } | ||||
| } | ||||
| 
 | ||||
| function getIsAvailableFunction(url: string, checkPortOnly: boolean, ignoreHTTPSErrors: boolean, onStdErr: Multiplexer['onStdErr']) { | ||||
| function getIsAvailableFunction(url: string, checkPortOnly: boolean, ignoreHTTPSErrors: boolean, onStdErr: InternalReporter['onStdErr']) { | ||||
|   const urlObject = new URL(url); | ||||
|   if (!checkPortOnly) | ||||
|     return () => isURLAvailable(urlObject, ignoreHTTPSErrors, onStdErr); | ||||
|  |  | |||
|  | @ -0,0 +1,121 @@ | |||
| /** | ||||
|  * 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 type { FullConfig, TestCase, TestError, TestResult, FullResult, TestStep, Reporter } from '../../types/testReporter'; | ||||
| import { Suite } from '../common/test'; | ||||
| import type { FullConfigInternal } from '../common/config'; | ||||
| import { addSnippetToError } from './base'; | ||||
| import { Multiplexer } from './multiplexer'; | ||||
| 
 | ||||
| type StdIOChunk = { | ||||
|   chunk: string | Buffer; | ||||
|   test?: TestCase; | ||||
|   result?: TestResult; | ||||
| }; | ||||
| 
 | ||||
| export class InternalReporter { | ||||
|   private _multiplexer: Multiplexer; | ||||
|   private _deferred: { error?: TestError, stdout?: StdIOChunk, stderr?: StdIOChunk }[] | null = []; | ||||
|   private _config!: FullConfigInternal; | ||||
| 
 | ||||
|   constructor(reporters: Reporter[]) { | ||||
|     this._multiplexer = new Multiplexer(reporters); | ||||
|   } | ||||
| 
 | ||||
|   onConfigure(config: FullConfigInternal) { | ||||
|     this._config = config; | ||||
|   } | ||||
| 
 | ||||
|   onBegin(config: FullConfig, suite: Suite) { | ||||
|     this._multiplexer.onBegin(config, suite); | ||||
| 
 | ||||
|     const deferred = this._deferred!; | ||||
|     this._deferred = null; | ||||
|     for (const item of deferred) { | ||||
|       if (item.error) | ||||
|         this.onError(item.error); | ||||
|       if (item.stdout) | ||||
|         this.onStdOut(item.stdout.chunk, item.stdout.test, item.stdout.result); | ||||
|       if (item.stderr) | ||||
|         this.onStdErr(item.stderr.chunk, item.stderr.test, item.stderr.result); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   onTestBegin(test: TestCase, result: TestResult) { | ||||
|     this._multiplexer.onTestBegin(test, result); | ||||
|   } | ||||
| 
 | ||||
|   onStdOut(chunk: string | Buffer, test?: TestCase, result?: TestResult) { | ||||
|     if (this._deferred) { | ||||
|       this._deferred.push({ stdout: { chunk, test, result } }); | ||||
|       return; | ||||
|     } | ||||
|     this._multiplexer.onStdOut(chunk, test, result); | ||||
|   } | ||||
| 
 | ||||
|   onStdErr(chunk: string | Buffer, test?: TestCase, result?: TestResult) { | ||||
|     if (this._deferred) { | ||||
|       this._deferred.push({ stderr: { chunk, test, result } }); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     this._multiplexer.onStdErr(chunk, test, result); | ||||
|   } | ||||
| 
 | ||||
|   onTestEnd(test: TestCase, result: TestResult) { | ||||
|     this._addSnippetToTestErrors(test, result); | ||||
|     this._multiplexer.onTestEnd(test, result); | ||||
|   } | ||||
| 
 | ||||
|   async onEnd() { } | ||||
| 
 | ||||
|   async onExit(result: FullResult) { | ||||
|     if (this._deferred) { | ||||
|       // onBegin was not reported, emit it.
 | ||||
|       this.onBegin(this._config.config, new Suite('', 'root')); | ||||
|     } | ||||
|     await this._multiplexer.onEnd(result); | ||||
|     await this._multiplexer.onExit(); | ||||
|   } | ||||
| 
 | ||||
|   onError(error: TestError) { | ||||
|     if (this._deferred) { | ||||
|       this._deferred.push({ error }); | ||||
|       return; | ||||
|     } | ||||
|     addSnippetToError(this._config.config, error); | ||||
|     this._multiplexer.onError(error); | ||||
|   } | ||||
| 
 | ||||
|   onStepBegin(test: TestCase, result: TestResult, step: TestStep) { | ||||
|     this._multiplexer.onStepBegin(test, result, step); | ||||
|   } | ||||
| 
 | ||||
|   onStepEnd(test: TestCase, result: TestResult, step: TestStep) { | ||||
|     this._addSnippetToStepError(test, step); | ||||
|     this._multiplexer.onStepEnd(test, result, step); | ||||
|   } | ||||
| 
 | ||||
|   private _addSnippetToTestErrors(test: TestCase, result: TestResult) { | ||||
|     for (const error of result.errors) | ||||
|       addSnippetToError(this._config.config, error, test.location.file); | ||||
|   } | ||||
| 
 | ||||
|   private _addSnippetToStepError(test: TestCase, step: TestStep) { | ||||
|     if (step.error) | ||||
|       addSnippetToError(this._config.config, step.error, test.location.file); | ||||
|   } | ||||
| } | ||||
|  | @ -15,47 +15,18 @@ | |||
|  */ | ||||
| 
 | ||||
| import type { FullConfig, TestCase, TestError, TestResult, FullResult, TestStep, Reporter } from '../../types/testReporter'; | ||||
| import { Suite } from '../common/test'; | ||||
| import type { FullConfigInternal } from '../common/config'; | ||||
| import { addSnippetToError } from './base'; | ||||
| import type { Suite } from '../common/test'; | ||||
| 
 | ||||
| type StdIOChunk = { | ||||
|   chunk: string | Buffer; | ||||
|   test?: TestCase; | ||||
|   result?: TestResult; | ||||
| }; | ||||
| 
 | ||||
| export class Multiplexer { | ||||
| export class Multiplexer implements Reporter { | ||||
|   private _reporters: Reporter[]; | ||||
|   private _deferred: { error?: TestError, stdout?: StdIOChunk, stderr?: StdIOChunk }[] | null = []; | ||||
|   private _config!: FullConfigInternal; | ||||
| 
 | ||||
|   constructor(reporters: Reporter[]) { | ||||
|     this._reporters = reporters; | ||||
|   } | ||||
| 
 | ||||
|   printsToStdio() { | ||||
|     return this._reporters.some(r => r.printsToStdio ? r.printsToStdio() : true); | ||||
|   } | ||||
| 
 | ||||
|   onConfigure(config: FullConfigInternal) { | ||||
|     this._config = config; | ||||
|   } | ||||
| 
 | ||||
|   onBegin(config: FullConfig, suite: Suite) { | ||||
|     for (const reporter of this._reporters) | ||||
|       wrap(() => reporter.onBegin?.(config, suite)); | ||||
| 
 | ||||
|     const deferred = this._deferred!; | ||||
|     this._deferred = null; | ||||
|     for (const item of deferred) { | ||||
|       if (item.error) | ||||
|         this.onError(item.error); | ||||
|       if (item.stdout) | ||||
|         this.onStdOut(item.stdout.chunk, item.stdout.test, item.stdout.result); | ||||
|       if (item.stderr) | ||||
|         this.onStdErr(item.stderr.chunk, item.stderr.test, item.stderr.result); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   onTestBegin(test: TestCase, result: TestResult) { | ||||
|  | @ -64,76 +35,52 @@ export class Multiplexer { | |||
|   } | ||||
| 
 | ||||
|   onStdOut(chunk: string | Buffer, test?: TestCase, result?: TestResult) { | ||||
|     if (this._deferred) { | ||||
|       this._deferred.push({ stdout: { chunk, test, result } }); | ||||
|       return; | ||||
|     } | ||||
|     for (const reporter of this._reporters) | ||||
|       wrap(() => reporter.onStdOut?.(chunk, test, result)); | ||||
|   } | ||||
| 
 | ||||
|   onStdErr(chunk: string | Buffer, test?: TestCase, result?: TestResult) { | ||||
|     if (this._deferred) { | ||||
|       this._deferred.push({ stderr: { chunk, test, result } }); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     for (const reporter of this._reporters) | ||||
|       wrap(() => reporter.onStdErr?.(chunk, test, result)); | ||||
|   } | ||||
| 
 | ||||
|   onTestEnd(test: TestCase, result: TestResult) { | ||||
|     this._addSnippetToTestErrors(test, result); | ||||
|     for (const reporter of this._reporters) | ||||
|       wrap(() => reporter.onTestEnd?.(test, result)); | ||||
|   } | ||||
| 
 | ||||
|   async onEnd() { } | ||||
| 
 | ||||
|   async onExit(result: FullResult) { | ||||
|     if (this._deferred) { | ||||
|       // onBegin was not reported, emit it.
 | ||||
|       this.onBegin(this._config.config, new Suite('', 'root')); | ||||
|   async onEnd(result: FullResult) { | ||||
|     for (const reporter of this._reporters) | ||||
|       await wrapAsync(() => reporter.onEnd?.(result)); | ||||
|   } | ||||
| 
 | ||||
|   async onExit() { | ||||
|     for (const reporter of this._reporters) | ||||
|       await Promise.resolve().then(() => reporter.onEnd?.(result)).catch(e => console.error('Error in reporter', e)); | ||||
| 
 | ||||
|     for (const reporter of this._reporters) | ||||
|       await Promise.resolve().then(() => reporter.onExit?.()).catch(e => console.error('Error in reporter', e)); | ||||
|       await wrapAsync(() => reporter.onExit?.()); | ||||
|   } | ||||
| 
 | ||||
|   onError(error: TestError) { | ||||
|     if (this._deferred) { | ||||
|       this._deferred.push({ error }); | ||||
|       return; | ||||
|     } | ||||
|     addSnippetToError(this._config.config, error); | ||||
|     for (const reporter of this._reporters) | ||||
|       wrap(() => reporter.onError?.(error)); | ||||
|   } | ||||
| 
 | ||||
|   onStepBegin(test: TestCase, result: TestResult, step: TestStep) { | ||||
|     for (const reporter of this._reporters) | ||||
|       wrap(() => (reporter as any).onStepBegin?.(test, result, step)); | ||||
|       wrap(() => reporter.onStepBegin?.(test, result, step)); | ||||
|   } | ||||
| 
 | ||||
|   onStepEnd(test: TestCase, result: TestResult, step: TestStep) { | ||||
|     this._addSnippetToStepError(test, step); | ||||
|     for (const reporter of this._reporters) | ||||
|       wrap(() => (reporter as any).onStepEnd?.(test, result, step)); | ||||
|       wrap(() => reporter.onStepEnd?.(test, result, step)); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|   private _addSnippetToTestErrors(test: TestCase, result: TestResult) { | ||||
|     for (const error of result.errors) | ||||
|       addSnippetToError(this._config.config, error, test.location.file); | ||||
| async function wrapAsync(callback: () => void | Promise<void>) { | ||||
|   try { | ||||
|     await callback(); | ||||
|   } catch (e) { | ||||
|     console.error('Error in reporter', e); | ||||
|   } | ||||
| 
 | ||||
|   private _addSnippetToStepError(test: TestCase, step: TestStep) { | ||||
|     if (step.error) | ||||
|       addSnippetToError(this._config.config, step.error, test.location.file); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| function wrap(callback: () => void) { | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ import { ManualPromise } from 'playwright-core/lib/utils'; | |||
| import { WorkerHost } from './workerHost'; | ||||
| import type { TestGroup } from './testGroups'; | ||||
| import type { FullConfigInternal } from '../common/config'; | ||||
| import type { Multiplexer } from '../reporters/multiplexer'; | ||||
| import type { InternalReporter } from '../reporters/internalReporter'; | ||||
| 
 | ||||
| type TestResultData = { | ||||
|   result: TestResult; | ||||
|  | @ -46,14 +46,14 @@ export class Dispatcher { | |||
| 
 | ||||
|   private _testById = new Map<string, TestData>(); | ||||
|   private _config: FullConfigInternal; | ||||
|   private _reporter: Multiplexer; | ||||
|   private _reporter: InternalReporter; | ||||
|   private _hasWorkerErrors = false; | ||||
|   private _failureCount = 0; | ||||
| 
 | ||||
|   private _extraEnvByProjectId: EnvByProjectId = new Map(); | ||||
|   private _producedEnvByProjectId: EnvByProjectId = new Map(); | ||||
| 
 | ||||
|   constructor(config: FullConfigInternal, reporter: Multiplexer) { | ||||
|   constructor(config: FullConfigInternal, reporter: InternalReporter) { | ||||
|     this._config = config; | ||||
|     this._reporter = reporter; | ||||
|   } | ||||
|  | @ -75,7 +75,7 @@ export class Dispatcher { | |||
|       for (const test of group.tests) { | ||||
|         const result = test._appendTestResult(); | ||||
|         result.status = 'skipped'; | ||||
|         this._reporter.onTestBegin?.(test, result); | ||||
|         this._reporter.onTestBegin(test, result); | ||||
|         test.annotations = [...test._staticAnnotations]; | ||||
|         this._reportTestEnd(test, result); | ||||
|       } | ||||
|  | @ -222,7 +222,7 @@ export class Dispatcher { | |||
|       result.parallelIndex = worker.parallelIndex; | ||||
|       result.workerIndex = worker.workerIndex; | ||||
|       result.startTime = new Date(params.startWallTime); | ||||
|       this._reporter.onTestBegin?.(data.test, result); | ||||
|       this._reporter.onTestBegin(data.test, result); | ||||
|       worker.currentTestId = params.testId; | ||||
|     }; | ||||
|     worker.addListener('testBegin', onTestBegin); | ||||
|  | @ -284,7 +284,7 @@ export class Dispatcher { | |||
|       }; | ||||
|       steps.set(params.stepId, step); | ||||
|       (parentStep || result).steps.push(step); | ||||
|       this._reporter.onStepBegin?.(data.test, result, step); | ||||
|       this._reporter.onStepBegin(data.test, result, step); | ||||
|     }; | ||||
|     worker.on('stepBegin', onStepBegin); | ||||
| 
 | ||||
|  | @ -298,14 +298,14 @@ export class Dispatcher { | |||
|       const { result, steps } = runData; | ||||
|       const step = steps.get(params.stepId); | ||||
|       if (!step) { | ||||
|         this._reporter.onStdErr?.('Internal error: step end without step begin: ' + params.stepId, data.test, result); | ||||
|         this._reporter.onStdErr('Internal error: step end without step begin: ' + params.stepId, data.test, result); | ||||
|         return; | ||||
|       } | ||||
|       step.duration = params.wallTime - step.startTime.getTime(); | ||||
|       if (params.error) | ||||
|         step.error = params.error; | ||||
|       steps.delete(params.stepId); | ||||
|       this._reporter.onStepEnd?.(data.test, result, step); | ||||
|       this._reporter.onStepEnd(data.test, result, step); | ||||
|     }; | ||||
|     worker.on('stepEnd', onStepEnd); | ||||
| 
 | ||||
|  | @ -342,7 +342,7 @@ export class Dispatcher { | |||
|               if (onlyStartedTests) | ||||
|                 return true; | ||||
|               result = data.test._appendTestResult(); | ||||
|               this._reporter.onTestBegin?.(test, result); | ||||
|               this._reporter.onTestBegin(test, result); | ||||
|             } | ||||
|             result.errors = [...errors]; | ||||
|             result.error = result.errors[0]; | ||||
|  | @ -358,7 +358,7 @@ export class Dispatcher { | |||
|           // Let's just fail the test run.
 | ||||
|           this._hasWorkerErrors = true; | ||||
|           for (const error of params.fatalErrors) | ||||
|             this._reporter.onError?.(error); | ||||
|             this._reporter.onError(error); | ||||
|         } | ||||
|       }; | ||||
| 
 | ||||
|  | @ -410,7 +410,7 @@ export class Dispatcher { | |||
| 
 | ||||
|         // Emulate a "skipped" run, and drop this test from remaining.
 | ||||
|         const result = test._appendTestResult(); | ||||
|         this._reporter.onTestBegin?.(test, result); | ||||
|         this._reporter.onTestBegin(test, result); | ||||
|         result.status = 'skipped'; | ||||
|         this._reportTestEnd(test, result); | ||||
|         return false; | ||||
|  | @ -470,17 +470,17 @@ export class Dispatcher { | |||
|     worker.on('stdOut', (params: TestOutputPayload) => { | ||||
|       const { chunk, test, result } = handleOutput(params); | ||||
|       result?.stdout.push(chunk); | ||||
|       this._reporter.onStdOut?.(chunk, test, result); | ||||
|       this._reporter.onStdOut(chunk, test, result); | ||||
|     }); | ||||
|     worker.on('stdErr', (params: TestOutputPayload) => { | ||||
|       const { chunk, test, result } = handleOutput(params); | ||||
|       result?.stderr.push(chunk); | ||||
|       this._reporter.onStdErr?.(chunk, test, result); | ||||
|       this._reporter.onStdErr(chunk, test, result); | ||||
|     }); | ||||
|     worker.on('teardownErrors', (params: TeardownErrorsPayload) => { | ||||
|       this._hasWorkerErrors = true; | ||||
|       for (const error of params.fatalErrors) | ||||
|         this._reporter.onError?.(error); | ||||
|         this._reporter.onError(error); | ||||
|     }); | ||||
|     worker.on('exit', () => { | ||||
|       const producedEnv = this._producedEnvByProjectId.get(testGroup.projectId) || {}; | ||||
|  | @ -509,7 +509,7 @@ export class Dispatcher { | |||
|   private _reportTestEnd(test: TestCase, result: TestResult) { | ||||
|     if (result.status !== 'skipped' && result.status !== test.expectedStatus) | ||||
|       ++this._failureCount; | ||||
|     this._reporter.onTestEnd?.(test, result); | ||||
|     this._reporter.onTestEnd(test, result); | ||||
|     const maxFailures = this._config.config.maxFailures; | ||||
|     if (maxFailures && this._failureCount === maxFailures) | ||||
|       this.stop().catch(e => {}); | ||||
|  |  | |||
|  | @ -25,7 +25,7 @@ import type { FullConfigInternal } from '../common/config'; | |||
| import { colors } from 'playwright-core/lib/utilsBundle'; | ||||
| import { runWatchModeLoop } from './watchMode'; | ||||
| import { runUIMode } from './uiMode'; | ||||
| import { Multiplexer } from '../reporters/multiplexer'; | ||||
| import { InternalReporter } from '../reporters/internalReporter'; | ||||
| 
 | ||||
| export class Runner { | ||||
|   private _config: FullConfigInternal; | ||||
|  | @ -69,7 +69,7 @@ export class Runner { | |||
|     // Legacy webServer support.
 | ||||
|     webServerPluginsForConfig(config).forEach(p => config.plugins.push({ factory: p })); | ||||
| 
 | ||||
|     const reporter = new Multiplexer(await createReporters(config, listOnly ? 'list' : 'run')); | ||||
|     const reporter = new InternalReporter(await createReporters(config, listOnly ? 'list' : 'run')); | ||||
|     const taskRunner = listOnly ? createTaskRunnerForList(config, reporter, 'in-process') | ||||
|       : createTaskRunner(config, reporter); | ||||
| 
 | ||||
|  |  | |||
|  | @ -19,20 +19,20 @@ import { ManualPromise, monotonicTime } from 'playwright-core/lib/utils'; | |||
| import type { FullResult, TestError } from '../../reporter'; | ||||
| import { SigIntWatcher } from './sigIntWatcher'; | ||||
| import { serializeError } from '../util'; | ||||
| import type { Multiplexer } from '../reporters/multiplexer'; | ||||
| import type { InternalReporter } from '../reporters/internalReporter'; | ||||
| 
 | ||||
| type TaskTeardown = () => Promise<any> | undefined; | ||||
| export type Task<Context> = (context: Context, errors: TestError[]) => Promise<TaskTeardown | void> | undefined; | ||||
| 
 | ||||
| export class TaskRunner<Context> { | ||||
|   private _tasks: { name: string, task: Task<Context> }[] = []; | ||||
|   private _reporter: Multiplexer; | ||||
|   private _reporter: InternalReporter; | ||||
|   private _hasErrors = false; | ||||
|   private _interrupted = false; | ||||
|   private _isTearDown = false; | ||||
|   private _globalTimeoutForError: number; | ||||
| 
 | ||||
|   constructor(reporter: Multiplexer, globalTimeoutForError: number) { | ||||
|   constructor(reporter: InternalReporter, globalTimeoutForError: number) { | ||||
|     this._reporter = reporter; | ||||
|     this._globalTimeoutForError = globalTimeoutForError; | ||||
|   } | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ import { promisify } from 'util'; | |||
| import { debug, rimraf } from 'playwright-core/lib/utilsBundle'; | ||||
| import { Dispatcher, type EnvByProjectId } from './dispatcher'; | ||||
| import type { TestRunnerPluginRegistration } from '../plugins'; | ||||
| import type { Multiplexer } from '../reporters/multiplexer'; | ||||
| import type { InternalReporter } from '../reporters/internalReporter'; | ||||
| import { createTestGroups, type TestGroup } from '../runner/testGroups'; | ||||
| import type { Task } from './taskRunner'; | ||||
| import { TaskRunner } from './taskRunner'; | ||||
|  | @ -44,7 +44,7 @@ export type Phase = { | |||
| }; | ||||
| 
 | ||||
| export class TestRun { | ||||
|   readonly reporter: Multiplexer; | ||||
|   readonly reporter: InternalReporter; | ||||
|   readonly config: FullConfigInternal; | ||||
|   rootSuite: Suite | undefined = undefined; | ||||
|   readonly phases: Phase[] = []; | ||||
|  | @ -53,13 +53,13 @@ export class TestRun { | |||
|   projectType: Map<FullProjectInternal, 'top-level' | 'dependency'> = new Map(); | ||||
|   projectSuites: Map<FullProjectInternal, Suite[]> = new Map(); | ||||
| 
 | ||||
|   constructor(config: FullConfigInternal, reporter: Multiplexer) { | ||||
|   constructor(config: FullConfigInternal, reporter: InternalReporter) { | ||||
|     this.config = config; | ||||
|     this.reporter = reporter; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export function createTaskRunner(config: FullConfigInternal, reporter: Multiplexer): TaskRunner<TestRun> { | ||||
| export function createTaskRunner(config: FullConfigInternal, reporter: InternalReporter): TaskRunner<TestRun> { | ||||
|   const taskRunner = new TaskRunner<TestRun>(reporter, config.config.globalTimeout); | ||||
|   addGlobalSetupTasks(taskRunner, config); | ||||
|   taskRunner.addTask('load tests', createLoadTask('in-process', true)); | ||||
|  | @ -67,13 +67,13 @@ export function createTaskRunner(config: FullConfigInternal, reporter: Multiplex | |||
|   return taskRunner; | ||||
| } | ||||
| 
 | ||||
| export function createTaskRunnerForWatchSetup(config: FullConfigInternal, reporter: Multiplexer): TaskRunner<TestRun> { | ||||
| export function createTaskRunnerForWatchSetup(config: FullConfigInternal, reporter: InternalReporter): TaskRunner<TestRun> { | ||||
|   const taskRunner = new TaskRunner<TestRun>(reporter, 0); | ||||
|   addGlobalSetupTasks(taskRunner, config); | ||||
|   return taskRunner; | ||||
| } | ||||
| 
 | ||||
| export function createTaskRunnerForWatch(config: FullConfigInternal, reporter: Multiplexer, additionalFileMatcher?: Matcher): TaskRunner<TestRun> { | ||||
| export function createTaskRunnerForWatch(config: FullConfigInternal, reporter: InternalReporter, additionalFileMatcher?: Matcher): TaskRunner<TestRun> { | ||||
|   const taskRunner = new TaskRunner<TestRun>(reporter, 0); | ||||
|   taskRunner.addTask('load tests', createLoadTask('out-of-process', true, additionalFileMatcher)); | ||||
|   addRunTasks(taskRunner, config); | ||||
|  | @ -91,7 +91,7 @@ function addGlobalSetupTasks(taskRunner: TaskRunner<TestRun>, config: FullConfig | |||
| function addRunTasks(taskRunner: TaskRunner<TestRun>, config: FullConfigInternal) { | ||||
|   taskRunner.addTask('create phases', createPhasesTask()); | ||||
|   taskRunner.addTask('report begin', async ({ reporter, rootSuite }) => { | ||||
|     reporter.onBegin?.(config.config, rootSuite!); | ||||
|     reporter.onBegin(config.config, rootSuite!); | ||||
|     return () => reporter.onEnd(); | ||||
|   }); | ||||
|   for (const plugin of config.plugins) | ||||
|  | @ -101,11 +101,11 @@ function addRunTasks(taskRunner: TaskRunner<TestRun>, config: FullConfigInternal | |||
|   return taskRunner; | ||||
| } | ||||
| 
 | ||||
| export function createTaskRunnerForList(config: FullConfigInternal, reporter: Multiplexer, mode: 'in-process' | 'out-of-process'): TaskRunner<TestRun> { | ||||
| export function createTaskRunnerForList(config: FullConfigInternal, reporter: InternalReporter, mode: 'in-process' | 'out-of-process'): TaskRunner<TestRun> { | ||||
|   const taskRunner = new TaskRunner<TestRun>(reporter, config.config.globalTimeout); | ||||
|   taskRunner.addTask('load tests', createLoadTask(mode, false)); | ||||
|   taskRunner.addTask('report begin', async ({ reporter, rootSuite }) => { | ||||
|     reporter.onBegin?.(config.config, rootSuite!); | ||||
|     reporter.onBegin(config.config, rootSuite!); | ||||
|     return () => reporter.onEnd(); | ||||
|   }); | ||||
|   return taskRunner; | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ import { isUnderTest, ManualPromise } from 'playwright-core/lib/utils'; | |||
| import type { FullResult } from '../../reporter'; | ||||
| import { clearCompilationCache, collectAffectedTestFiles, dependenciesForTestFile } from '../common/compilationCache'; | ||||
| import type { FullConfigInternal } from '../common/config'; | ||||
| import { Multiplexer } from '../reporters/multiplexer'; | ||||
| import { InternalReporter } from '../reporters/internalReporter'; | ||||
| import { TeleReporterEmitter } from '../reporters/teleEmitter'; | ||||
| import { createReporters } from './reporters'; | ||||
| import { TestRun, createTaskRunnerForList, createTaskRunnerForWatch, createTaskRunnerForWatchSetup } from './tasks'; | ||||
|  | @ -65,7 +65,7 @@ class UIMode { | |||
|   } | ||||
| 
 | ||||
|   async runGlobalSetup(): Promise<FullResult['status']> { | ||||
|     const reporter = new Multiplexer([new ListReporter()]); | ||||
|     const reporter = new InternalReporter([new ListReporter()]); | ||||
|     const taskRunner = createTaskRunnerForWatchSetup(this._config, reporter); | ||||
|     reporter.onConfigure(this._config); | ||||
|     const testRun = new TestRun(this._config, reporter); | ||||
|  | @ -144,7 +144,7 @@ class UIMode { | |||
| 
 | ||||
|   private async _listTests() { | ||||
|     const listReporter = new TeleReporterEmitter(e => this._dispatchEvent(e)); | ||||
|     const reporter = new Multiplexer([listReporter]); | ||||
|     const reporter = new InternalReporter([listReporter]); | ||||
|     this._config.cliListOnly = true; | ||||
|     this._config.testIdMatcher = undefined; | ||||
|     const taskRunner = createTaskRunnerForList(this._config, reporter, 'out-of-process'); | ||||
|  | @ -169,7 +169,7 @@ class UIMode { | |||
| 
 | ||||
|     const reporters = await createReporters(this._config, 'ui'); | ||||
|     reporters.push(new TeleReporterEmitter(e => this._dispatchEvent(e))); | ||||
|     const reporter = new Multiplexer(reporters); | ||||
|     const reporter = new InternalReporter(reporters); | ||||
|     const taskRunner = createTaskRunnerForWatch(this._config, reporter); | ||||
|     const testRun = new TestRun(this._config, reporter); | ||||
|     clearCompilationCache(); | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ | |||
| import readline from 'readline'; | ||||
| import { createGuid, ManualPromise } from 'playwright-core/lib/utils'; | ||||
| import type { FullConfigInternal, FullProjectInternal } from '../common/config'; | ||||
| import { Multiplexer } from '../reporters/multiplexer'; | ||||
| import { InternalReporter } from '../reporters/internalReporter'; | ||||
| import { createFileMatcher, createFileMatcherFromArguments } from '../util'; | ||||
| import type { Matcher } from '../util'; | ||||
| import { TestRun, createTaskRunnerForWatch, createTaskRunnerForWatchSetup } from './tasks'; | ||||
|  | @ -112,7 +112,7 @@ export async function runWatchModeLoop(config: FullConfigInternal): Promise<Full | |||
|     p.project.retries = 0; | ||||
| 
 | ||||
|   // Perform global setup.
 | ||||
|   const reporter = new Multiplexer([new ListReporter()]); | ||||
|   const reporter = new InternalReporter([new ListReporter()]); | ||||
|   const testRun = new TestRun(config, reporter); | ||||
|   const taskRunner = createTaskRunnerForWatchSetup(config, reporter); | ||||
|   reporter.onConfigure(config); | ||||
|  | @ -275,7 +275,7 @@ async function runTests(config: FullConfigInternal, failedTestIdCollector: Set<s | |||
|     title?: string, | ||||
|   }) { | ||||
|   printConfiguration(config, options?.title); | ||||
|   const reporter = new Multiplexer([new ListReporter()]); | ||||
|   const reporter = new InternalReporter([new ListReporter()]); | ||||
|   const taskRunner = createTaskRunnerForWatch(config, reporter, options?.additionalFileMatcher); | ||||
|   const testRun = new TestRun(config, reporter); | ||||
|   clearCompilationCache(); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue