@@ -15,15 +15,17 @@ import type {
1515 TaskUpdateEvent ,
1616 Test ,
1717 TestContext ,
18+ WriteableTestContext ,
1819} from './types/tasks'
1920import { shuffle } from '@vitest/utils'
2021import { processError } from '@vitest/utils/error'
2122import { collectTests } from './collect'
22- import { PendingError } from './errors'
23+ import { abortContextSignal } from './context'
24+ import { PendingError , TestRunAbortError } from './errors'
2325import { callFixtureCleanup } from './fixture'
2426import { getBeforeHookCleanupCallback } from './hooks'
2527import { getFn , getHooks } from './map'
26- import { setCurrentTest } from './test-state'
28+ import { addRunningTest , getRunningTests , setCurrentTest } from './test-state'
2729import { limitConcurrency } from './utils/limit-concurrency'
2830import { partitionSuiteChildren } from './utils/suite'
2931import { hasFailed , hasTests } from './utils/tasks'
@@ -87,12 +89,14 @@ async function callTestHooks(
8789 return
8890 }
8991
92+ const context = test . context as WriteableTestContext
93+
9094 const onTestFailed = test . context . onTestFailed
9195 const onTestFinished = test . context . onTestFinished
92- test . context . onTestFailed = ( ) => {
96+ context . onTestFailed = ( ) => {
9397 throw new Error ( `Cannot call "onTestFailed" inside a test hook.` )
9498 }
95- test . context . onTestFinished = ( ) => {
99+ context . onTestFinished = ( ) => {
96100 throw new Error ( `Cannot call "onTestFinished" inside a test hook.` )
97101 }
98102
@@ -115,8 +119,8 @@ async function callTestHooks(
115119 }
116120 }
117121
118- test . context . onTestFailed = onTestFailed
119- test . context . onTestFinished = onTestFinished
122+ context . onTestFailed = onTestFailed
123+ context . onTestFinished = onTestFinished
120124}
121125
122126export async function callSuiteHook < T extends keyof SuiteHooks > (
@@ -145,7 +149,11 @@ export async function callSuiteHook<T extends keyof SuiteHooks>(
145149 }
146150
147151 async function runHook ( hook : Function ) {
148- return getBeforeHookCleanupCallback ( hook , await hook ( ...args ) )
152+ return getBeforeHookCleanupCallback (
153+ hook ,
154+ await hook ( ...args ) ,
155+ name === 'beforeEach' ? args [ 0 ] : undefined ,
156+ )
149157 }
150158
151159 if ( sequence === 'parallel' ) {
@@ -274,6 +282,7 @@ export async function runTest(test: Test, runner: VitestRunner): Promise<void> {
274282 }
275283 updateTask ( 'test-prepare' , test , runner )
276284
285+ const cleanupRunningTest = addRunningTest ( test )
277286 setCurrentTest ( test )
278287
279288 const suite = test . suite || test . file
@@ -374,6 +383,7 @@ export async function runTest(test: Test, runner: VitestRunner): Promise<void> {
374383 }
375384 updateTask ( 'test-finished' , test , runner )
376385 setCurrentTest ( undefined )
386+ cleanupRunningTest ( )
377387 return
378388 }
379389
@@ -405,6 +415,7 @@ export async function runTest(test: Test, runner: VitestRunner): Promise<void> {
405415 }
406416 }
407417
418+ cleanupRunningTest ( )
408419 setCurrentTest ( undefined )
409420
410421 test . result . duration = now ( ) - start
@@ -588,21 +599,38 @@ export async function runFiles(files: File[], runner: VitestRunner): Promise<voi
588599}
589600
590601export async function startTests ( specs : string [ ] | FileSpecification [ ] , runner : VitestRunner ) : Promise < File [ ] > {
591- const paths = specs . map ( f => typeof f === 'string' ? f : f . filepath )
592- await runner . onBeforeCollect ?.( paths )
602+ const cancel = runner . cancel ?. bind ( runner )
603+ // Ideally, we need to have an event listener for this, but only have a runner here.
604+ // Adding another onCancel felt wrong (maybe it needs to be refactored)
605+ runner . cancel = ( reason ) => {
606+ // We intentionally create only one error since there is only one test run that can be cancelled
607+ const error = new TestRunAbortError ( 'The test run was aborted by the user.' , reason )
608+ getRunningTests ( ) . forEach ( test =>
609+ abortContextSignal ( test . context , error ) ,
610+ )
611+ return cancel ?.( reason )
612+ }
593613
594- const files = await collectTests ( specs , runner )
614+ try {
615+ const paths = specs . map ( f => typeof f === 'string' ? f : f . filepath )
616+ await runner . onBeforeCollect ?.( paths )
595617
596- await runner . onCollected ?.( files )
597- await runner . onBeforeRunFiles ?.( files )
618+ const files = await collectTests ( specs , runner )
598619
599- await runFiles ( files , runner )
620+ await runner . onCollected ?.( files )
621+ await runner . onBeforeRunFiles ?.( files )
600622
601- await runner . onAfterRunFiles ?. ( files )
623+ await runFiles ( files , runner )
602624
603- await finishSendTasksUpdate ( runner )
625+ await runner . onAfterRunFiles ?. ( files )
604626
605- return files
627+ await finishSendTasksUpdate ( runner )
628+
629+ return files
630+ }
631+ finally {
632+ runner . cancel = cancel
633+ }
606634}
607635
608636async function publicCollect ( specs : string [ ] | FileSpecification [ ] , runner : VitestRunner ) : Promise < File [ ] > {
0 commit comments