@@ -178,15 +178,15 @@ async function callSuiteHook<T extends keyof SuiteHooks>(
178178 name : T ,
179179 runner : VitestRunner ,
180180 args : SuiteHooks [ T ] [ 0 ] extends ( ...args : infer A ) => Awaitable < any > ? A : never ,
181- ) : Promise < unknown [ ] > {
181+ cleanups : unknown [ ] = [ ] ,
182+ ) : Promise < void > {
182183 const sequence = runner . config . sequence . hooks
183184
184- const callbacks : unknown [ ] = [ ]
185185 // stop at file level
186186 const parentSuite : Suite | null = 'filepath' in suite ? null : suite . suite || suite . file
187187
188188 if ( name === 'beforeEach' && parentSuite ) {
189- callbacks . push ( ... ( await callSuiteHook ( parentSuite , currentTask , name , runner , args ) ) )
189+ await callSuiteHook ( parentSuite , currentTask , name , runner , args , cleanups )
190190 }
191191
192192 const hooks = getSuiteHooks ( suite , name , sequence )
@@ -206,10 +206,25 @@ async function callSuiteHook<T extends keyof SuiteHooks>(
206206 }
207207
208208 if ( sequence === 'parallel' ) {
209- callbacks . push ( ...( await Promise . all ( hooks . map ( ( hook ) => runHook ( hook ) ) ) ) )
209+ const results = await Promise . allSettled ( hooks . map ( ( hook ) => runHook ( hook ) ) )
210+ // hooks can reject with the same `signal.reason`
211+ const errors = new Set < unknown > ( )
212+ for ( const result of results ) {
213+ if ( result . status === 'fulfilled' ) {
214+ cleanups . push ( result . value )
215+ } else {
216+ errors . add ( result . reason )
217+ }
218+ }
219+ if ( errors . size === 1 ) {
220+ throw [ ...errors ] [ 0 ]
221+ }
222+ if ( errors . size > 1 ) {
223+ throw new AggregateError ( [ ...errors ] , `Multiple "${ name } " hooks failed.` )
224+ }
210225 } else {
211226 for ( const hook of hooks ) {
212- callbacks . push ( await runHook ( hook ) )
227+ cleanups . push ( await runHook ( hook ) )
213228 }
214229 }
215230
@@ -218,10 +233,8 @@ async function callSuiteHook<T extends keyof SuiteHooks>(
218233 }
219234
220235 if ( name === 'afterEach' && parentSuite ) {
221- callbacks . push ( ... ( await callSuiteHook ( parentSuite , currentTask , name , runner , args ) ) )
236+ await callSuiteHook ( parentSuite , currentTask , name , runner , args )
222237 }
223-
224- return callbacks
225238}
226239
227240function getAroundEachHooks ( suite : Suite ) : AroundEachListener [ ] {
@@ -603,7 +616,7 @@ async function runTest(test: Test, runner: VitestRunner): Promise<void> {
603616 const retry = getRetryCount ( test . retry )
604617 for ( let retryCount = 0 ; retryCount <= retry ; retryCount ++ ) {
605618 const attemptErrorsStart = test . result . errors ?. length ?? 0
606- let beforeEachCleanups : unknown [ ] = [ ]
619+ const beforeEachCleanups : unknown [ ] = [ ]
607620 // fixtureCheckpoint is passed by callAroundEachHooks - it represents the count
608621 // of fixture cleanup functions AFTER all aroundEach fixtures have been resolved
609622 // but BEFORE the test runs. This allows us to clean up only fixtures created
@@ -617,8 +630,15 @@ async function runTest(test: Test, runner: VitestRunner): Promise<void> {
617630
618631 test . result ! . repeatCount = repeatCount
619632
620- beforeEachCleanups = await $ ( 'test.beforeEach' , ( ) =>
621- callSuiteHook ( suite , test , 'beforeEach' , runner , [ test . context , suite ] ) ,
633+ await $ ( 'test.beforeEach' , ( ) =>
634+ callSuiteHook (
635+ suite ,
636+ test ,
637+ 'beforeEach' ,
638+ runner ,
639+ [ test . context , suite ] ,
640+ beforeEachCleanups ,
641+ ) ,
622642 )
623643
624644 if ( runner . runTask ) {
@@ -840,7 +860,7 @@ async function runSuite(suite: Suite, runner: VitestRunner): Promise<void> {
840860
841861 updateTask ( 'suite-prepare' , suite , runner )
842862
843- let beforeAllCleanups : unknown [ ] = [ ]
863+ const beforeAllCleanups : unknown [ ] = [ ]
844864
845865 if ( suite . mode === 'skip' ) {
846866 suite . result . state = 'skip'
@@ -859,8 +879,8 @@ async function runSuite(suite: Suite, runner: VitestRunner): Promise<void> {
859879 try {
860880 // beforeAll
861881 try {
862- beforeAllCleanups = await $ ( 'suite.beforeAll' , ( ) =>
863- callSuiteHook ( suite , suite , 'beforeAll' , runner , [ suite ] ) ,
882+ await $ ( 'suite.beforeAll' , ( ) =>
883+ callSuiteHook ( suite , suite , 'beforeAll' , runner , [ suite ] , beforeAllCleanups ) ,
864884 )
865885 } catch ( e ) {
866886 failTask ( suite . result ! , e , runner . config . _diffOptions )
0 commit comments