@@ -20,16 +20,15 @@ import { eventsHelper } from '@utils/eventsHelper';
2020
2121import { addSuggestedRebaseline } from './rebase' ;
2222import { WorkerHost } from './workerHost' ;
23- import { FullConfigInternal , ipc , test as testNs } from '../common' ;
23+ import { ipc , test as testNs } from '../common' ;
2424import { addLocationAndSnippetToError } from '../reporters/internalReporter' ;
2525import { serializeError } from '../util' ;
2626
2727import type { RegisteredListener } from '@utils/eventsHelper' ;
28- import type { FailureTracker } from './failureTracker' ;
2928import type { ProcessExitData } from './processHost' ;
29+ import type { TestRun } from './tasks' ;
3030import type { TestGroup } from './testGroups' ;
3131import type { TestError , TestResult , TestStep } from '../../types/testReporter' ;
32- import type { ReporterV2 } from '../reporters/reporterV2' ;
3332
3433export type EnvByProjectId = Map < string , Record < string , string | undefined > > ;
3534
@@ -42,18 +41,14 @@ export class Dispatcher {
4241 private _finished = new ManualPromise < void > ( ) ;
4342 private _isStopped = true ;
4443
45- private _config : FullConfigInternal ;
46- private _reporter : ReporterV2 ;
47- private _failureTracker : FailureTracker ;
44+ private _testRun : TestRun ;
4845
4946 private _extraEnvByProjectId : EnvByProjectId = new Map ( ) ;
5047 private _producedEnvByProjectId : EnvByProjectId = new Map ( ) ;
5148
52- constructor ( config : FullConfigInternal , reporter : ReporterV2 , failureTracker : FailureTracker ) {
53- this . _config = config ;
54- this . _reporter = reporter ;
55- this . _failureTracker = failureTracker ;
56- for ( const project of config . projects ) {
49+ constructor ( testRun : TestRun ) {
50+ this . _testRun = testRun ;
51+ for ( const project of testRun . config . projects ) {
5752 if ( project . workers )
5853 this . _workerLimitPerProjectId . set ( project . id , project . workers ) ;
5954 }
@@ -97,7 +92,7 @@ export class Dispatcher {
9792
9893 // 3. Claim both the job and the worker slot.
9994 this . _queue . splice ( jobIndex , 1 ) ;
100- const jobDispatcher = new JobDispatcher ( job , this . _config , this . _reporter , this . _failureTracker , ( ) => this . stop ( ) . catch ( ( ) => { } ) ) ;
95+ const jobDispatcher = new JobDispatcher ( job , this . _testRun , ( ) => this . stop ( ) . catch ( ( ) => { } ) ) ;
10196 this . _workerSlots [ workerIndex ] . jobDispatcher = jobDispatcher ;
10297
10398 // 4. Run the job. This is the only async operation.
@@ -132,7 +127,7 @@ export class Dispatcher {
132127 // 2. Start the worker if it is down.
133128 let startError ;
134129 if ( ! worker ) {
135- worker = this . _createWorker ( job , index , ipc . serializeConfig ( this . _config , true ) ) ;
130+ worker = this . _createWorker ( job , index , ipc . serializeConfig ( this . _testRun . config , true ) ) ;
136131 this . _workerSlots [ index ] . worker = worker ;
137132 worker . on ( 'exit' , ( ) => this . _workerSlots [ index ] . worker = undefined ) ;
138133 startError = await worker . start ( ) ;
@@ -198,10 +193,10 @@ export class Dispatcher {
198193 this . _isStopped = false ;
199194 this . _workerSlots = [ ] ;
200195 // 0. Stop right away if we have reached max failures.
201- if ( this . _failureTracker . hasReachedMaxFailures ( ) )
196+ if ( this . _testRun . hasReachedMaxFailures ( ) )
202197 void this . stop ( ) ;
203198 // 1. Allocate workers.
204- for ( let i = 0 ; i < this . _config . config . workers ; i ++ )
199+ for ( let i = 0 ; i < this . _testRun . config . config . workers ; i ++ )
205200 this . _workerSlots . push ( { } ) ;
206201 // 2. Schedule enough jobs.
207202 for ( let i = 0 ; i < this . _workerSlots . length ; i ++ )
@@ -213,15 +208,15 @@ export class Dispatcher {
213208 }
214209
215210 _createWorker ( testGroup : TestGroup , parallelIndex : number , loaderData : ipc . SerializedConfig ) {
216- const projectConfig = this . _config . projects . find ( p => p . id === testGroup . projectId ) ! ;
217- const outputDir = projectConfig . project . outputDir ;
211+ const project = this . _testRun . config . projects . find ( p => p . id === testGroup . projectId ) ! ;
212+ const pauseAtEnd = this . _testRun . topLevelProjects . includes ( project ) && ! ! this . _testRun . options . pauseAtEnd ;
218213 const worker = new WorkerHost ( testGroup , {
219214 parallelIndex,
220215 config : loaderData ,
221216 extraEnv : this . _extraEnvByProjectId . get ( testGroup . projectId ) || { } ,
222- outputDir,
223- pauseOnError : this . _failureTracker . pauseOnError ( ) ,
224- pauseAtEnd : this . _failureTracker . pauseAtEnd ( projectConfig ) ,
217+ outputDir : project . project . outputDir ,
218+ pauseOnError : ! ! this . _testRun . options . pauseOnError ,
219+ pauseAtEnd,
225220 } ) ;
226221 const handleOutput = ( params : ipc . TestOutputPayload ) => {
227222 const chunk = chunkFromParams ( params ) ;
@@ -239,17 +234,17 @@ export class Dispatcher {
239234 worker . on ( 'stdOut' , ( params : ipc . TestOutputPayload ) => {
240235 const { chunk, test, result } = handleOutput ( params ) ;
241236 result ?. stdout . push ( chunk ) ;
242- this . _reporter . onStdOut ?.( chunk , test , result ) ;
237+ this . _testRun . reporter . onStdOut ?.( chunk , test , result ) ;
243238 } ) ;
244239 worker . on ( 'stdErr' , ( params : ipc . TestOutputPayload ) => {
245240 const { chunk, test, result } = handleOutput ( params ) ;
246241 result ?. stderr . push ( chunk ) ;
247- this . _reporter . onStdErr ?.( chunk , test , result ) ;
242+ this . _testRun . reporter . onStdErr ?.( chunk , test , result ) ;
248243 } ) ;
249244 worker . on ( 'teardownErrors' , ( params : ipc . TeardownErrorsPayload ) => {
250- this . _failureTracker . onWorkerError ( ) ;
245+ this . _testRun . hasWorkerErrors = true ;
251246 for ( const error of params . fatalErrors )
252- this . _reporter . onError ?.( error ) ;
247+ this . _testRun . reporter . onError ?.( error ) ;
253248 } ) ;
254249 worker . on ( 'exit' , ( ) => {
255250 const producedEnv = this . _producedEnvByProjectId . get ( testGroup . projectId ) || { } ;
@@ -275,9 +270,7 @@ class JobDispatcher {
275270 jobResult = new ManualPromise < { newJob ?: TestGroup , didFail : boolean } > ( ) ;
276271
277272 readonly job : TestGroup ;
278- private _config : FullConfigInternal ;
279- private _reporter : ReporterV2 ;
280- private _failureTracker : FailureTracker ;
273+ private _testRun : TestRun ;
281274 private _stopCallback : ( ) => void ;
282275 private _listeners : RegisteredListener [ ] = [ ] ;
283276 private _failedTests = new Set < testNs . TestCase > ( ) ;
@@ -288,11 +281,9 @@ class JobDispatcher {
288281 private _workerIndex = 0 ;
289282 private _currentlyRunning : { test : testNs . TestCase , result : TestResult } | undefined ;
290283
291- constructor ( job : TestGroup , config : FullConfigInternal , reporter : ReporterV2 , failureTracker : FailureTracker , stopCallback : ( ) => void ) {
284+ constructor ( job : TestGroup , testRun : TestRun , stopCallback : ( ) => void ) {
292285 this . job = job ;
293- this . _config = config ;
294- this . _reporter = reporter ;
295- this . _failureTracker = failureTracker ;
286+ this . _testRun = testRun ;
296287 this . _stopCallback = stopCallback ;
297288 this . _remainingByTestId = new Map ( this . job . tests . map ( e => [ e . id , e ] ) ) ;
298289 }
@@ -308,12 +299,12 @@ class JobDispatcher {
308299 result . parallelIndex = this . _parallelIndex ;
309300 result . workerIndex = this . _workerIndex ;
310301 result . startTime = new Date ( params . startWallTime ) ;
311- this . _reporter . onTestBegin ?.( test , result ) ;
302+ this . _testRun . reporter . onTestBegin ?.( test , result ) ;
312303 this . _currentlyRunning = { test, result } ;
313304 }
314305
315306 private _onTestEnd ( params : ipc . TestEndPayload ) {
316- if ( this . _failureTracker . hasReachedMaxFailures ( ) ) {
307+ if ( this . _testRun . hasReachedMaxFailures ( ) ) {
317308 // Do not show more than one error to avoid confusion, but report
318309 // as interrupted to indicate that we did actually start the test.
319310 params . status = 'interrupted' ;
@@ -377,7 +368,7 @@ class JobDispatcher {
377368 } ;
378369 steps . set ( params . stepId , step ) ;
379370 ( parentStep || result ) . steps . push ( step ) ;
380- this . _reporter . onStepBegin ?.( test , result , step ) ;
371+ this . _testRun . reporter . onStepBegin ?.( test , result , step ) ;
381372 }
382373
383374 private _onStepEnd ( params : ipc . StepEndPayload ) {
@@ -389,7 +380,7 @@ class JobDispatcher {
389380 const { result, steps, test } = data ;
390381 const step = steps . get ( params . stepId ) ;
391382 if ( ! step ) {
392- this . _reporter . onStdErr ?.( 'Internal error: step end without step begin: ' + params . stepId , test , result ) ;
383+ this . _testRun . reporter . onStdErr ?.( 'Internal error: step end without step begin: ' + params . stepId , test , result ) ;
393384 return ;
394385 }
395386 step . duration = params . wallTime - step . startTime . getTime ( ) ;
@@ -399,7 +390,7 @@ class JobDispatcher {
399390 addSuggestedRebaseline ( step . location ! , params . suggestedRebaseline ) ;
400391 step . annotations = params . annotations ;
401392 steps . delete ( params . stepId ) ;
402- this . _reporter . onStepEnd ?.( test , result , step ) ;
393+ this . _testRun . reporter . onStepEnd ?.( test , result , step ) ;
403394 }
404395
405396 private _onAttach ( params : ipc . AttachmentPayload ) {
@@ -420,7 +411,7 @@ class JobDispatcher {
420411 if ( step )
421412 step . attachments . push ( attachment ) ;
422413 else
423- this . _reporter . onStdErr ?.( 'Internal error: step id not found: ' + params . stepId ) ;
414+ this . _testRun . reporter . onStdErr ?.( 'Internal error: step id not found: ' + params . stepId ) ;
424415 }
425416 }
426417
@@ -432,7 +423,7 @@ class JobDispatcher {
432423 result = runData . result ;
433424 } else {
434425 result = test . _appendTestResult ( ) ;
435- this . _reporter . onTestBegin ?.( test , result ) ;
426+ this . _testRun . reporter . onTestBegin ?.( test , result ) ;
436427 }
437428 result . errors = [ ...errors ] ;
438429 result . error = result . errors [ 0 ] ;
@@ -445,7 +436,7 @@ class JobDispatcher {
445436 for ( const test of this . _remainingByTestId . values ( ) ) {
446437 if ( ! testIds . has ( test . id ) )
447438 continue ;
448- if ( ! this . _failureTracker . hasReachedMaxFailures ( ) ) {
439+ if ( ! this . _testRun . hasReachedMaxFailures ( ) ) {
449440 this . _failTestWithErrors ( test , errors ) ;
450441 errors = [ ] ; // Only report errors for the first test.
451442 }
@@ -454,9 +445,9 @@ class JobDispatcher {
454445 if ( errors . length ) {
455446 // We had fatal errors after all tests have passed - most likely in some teardown.
456447 // Let's just fail the test run.
457- this . _failureTracker . onWorkerError ( ) ;
448+ this . _testRun . hasWorkerErrors = true ;
458449 for ( const error of errors )
459- this . _reporter . onError ?.( error ) ;
450+ this . _testRun . reporter . onError ?.( error ) ;
460451 }
461452 }
462453
@@ -592,11 +583,11 @@ class JobDispatcher {
592583 throw new Error ( 'Test has already stopped' ) ;
593584 const response = await worker . sendCustomMessage ( { testId : test . id , request : message . request } ) ;
594585 if ( response . error )
595- addLocationAndSnippetToError ( this . _config . config , response . error ) ;
586+ addLocationAndSnippetToError ( this . _testRun . config . config , response . error ) ;
596587 return response ;
597588 } catch ( e ) {
598589 const error = serializeError ( e ) ;
599- addLocationAndSnippetToError ( this . _config . config , error ) ;
590+ addLocationAndSnippetToError ( this . _testRun . config . config , error ) ;
600591 return { response : undefined , error } ;
601592 }
602593 } ;
@@ -605,10 +596,10 @@ class JobDispatcher {
605596 result . errors = params . errors ;
606597 result . error = result . errors [ 0 ] ;
607598
608- void this . _reporter . onTestPaused ?.( test , result ) . then ( ( ) => {
599+ void this . _testRun . reporter . onTestPaused ?.( test , result ) . then ( ( ) => {
609600 worker . sendResume ( { } ) ;
610601 } ) ;
611- this . _failureTracker . onTestPaused ?. ( { ...params , sendMessage } ) ;
602+ this . _testRun . onTestPaused ( { ...params , sendMessage } ) ;
612603 }
613604
614605 skipWholeJob ( ) : boolean {
@@ -620,10 +611,10 @@ class JobDispatcher {
620611 // with skipped tests mixed in-between non-skipped. This makes
621612 // for a better reporter experience.
622613 const allTestsSkipped = this . job . tests . every ( test => test . expectedStatus === 'skipped' ) ;
623- if ( allTestsSkipped && ! this . _failureTracker . hasReachedMaxFailures ( ) ) {
614+ if ( allTestsSkipped && ! this . _testRun . hasReachedMaxFailures ( ) ) {
624615 for ( const test of this . job . tests ) {
625616 const result = test . _appendTestResult ( ) ;
626- this . _reporter . onTestBegin ?.( test , result ) ;
617+ this . _testRun . reporter . onTestBegin ?.( test , result ) ;
627618 result . status = 'skipped' ;
628619 // This must mirror _onTestEnd() above
629620 result . annotations = [ ...test . annotations ] ;
@@ -639,13 +630,14 @@ class JobDispatcher {
639630 }
640631
641632 private _reportTestEnd ( test : testNs . TestCase , result : TestResult ) {
642- this . _reporter . onTestEnd ?.( test , result ) ;
643- const hadMaxFailures = this . _failureTracker . hasReachedMaxFailures ( ) ;
644- this . _failureTracker . onTestEnd ( test , result ) ;
645- if ( this . _failureTracker . hasReachedMaxFailures ( ) ) {
633+ this . _testRun . reporter . onTestEnd ?.( test , result ) ;
634+ const hadMaxFailures = this . _testRun . hasReachedMaxFailures ( ) ;
635+ // Test is considered failing after the last retry.
636+ if ( test . outcome ( ) === 'unexpected' && test . results . length > test . retries )
637+ ++ this . _testRun . failedTestCount ;
638+ if ( ! hadMaxFailures && this . _testRun . hasReachedMaxFailures ( ) ) {
646639 this . _stopCallback ( ) ;
647- if ( ! hadMaxFailures )
648- this . _reporter . onError ?.( { message : colors . red ( `Testing stopped early after ${ this . _failureTracker . maxFailures ( ) } maximum allowed failures.` ) } ) ;
640+ this . _testRun . reporter . onError ?.( { message : colors . red ( `Testing stopped early after ${ this . _testRun . config . config . maxFailures } maximum allowed failures.` ) } ) ;
649641 }
650642 }
651643}
0 commit comments