@@ -32,6 +32,13 @@ class RefTracker {
3232
3333type Key = string | symbol
3434
35+ interface MockContext {
36+ /**
37+ * When mocking with a factory, this refers to the module that imported the mock.
38+ */
39+ callstack : null | string [ ]
40+ }
41+
3542function isSpecialProp ( prop : Key , parentType : string ) {
3643 return parentType . includes ( 'Function' )
3744 && typeof prop === 'string'
@@ -54,6 +61,10 @@ export class VitestMocker {
5461
5562 private filterPublicKeys : ( symbol | string ) [ ]
5663
64+ private mockContext : MockContext = {
65+ callstack : null ,
66+ }
67+
5768 constructor (
5869 public executor : VitestExecutor ,
5970 ) {
@@ -201,9 +212,9 @@ export class VitestMocker {
201212 throw this . createError (
202213 `[vitest] No "${ String ( prop ) } " export is defined on the "${ mockpath } " mock. `
203214 + 'Did you forget to return it from "vi.mock"?'
204- + '\nIf you need to partially mock a module, you can use "vi.importActual" inside:\n\n'
205- + `${ c . green ( `vi.mock("${ mockpath } ", async () => {
206- const actual = await vi.importActual(" ${ mockpath } " )
215+ + '\nIf you need to partially mock a module, you can use "importOriginal" helper inside:\n\n'
216+ + `${ c . green ( `vi.mock("${ mockpath } ", async (importOriginal ) => {
217+ const actual = await importOriginal( )
207218 return {
208219 ...actual,
209220 // your mocked methods
@@ -221,6 +232,10 @@ export class VitestMocker {
221232 return moduleExports
222233 }
223234
235+ public getMockContext ( ) {
236+ return this . mockContext
237+ }
238+
224239 public getMockPath ( dep : string ) {
225240 return `mock:${ dep } `
226241 }
@@ -407,9 +422,9 @@ export class VitestMocker {
407422 this . deleteCachedItem ( id )
408423 }
409424
410- public async importActual < T > ( rawId : string , importee : string ) : Promise < T > {
411- const { id, fsPath } = await this . resolvePath ( rawId , importee )
412- const result = await this . executor . cachedRequest ( id , fsPath , [ importee ] )
425+ public async importActual < T > ( rawId : string , importer : string , callstack ?: string [ ] | null ) : Promise < T > {
426+ const { id, fsPath } = await this . resolvePath ( rawId , importer )
427+ const result = await this . executor . cachedRequest ( id , fsPath , callstack || [ importer ] )
413428 return result as T
414429 }
415430
@@ -453,9 +468,14 @@ export class VitestMocker {
453468 if ( typeof mock === 'function' && ! callstack . includes ( mockPath ) && ! callstack . includes ( url ) ) {
454469 try {
455470 callstack . push ( mockPath )
471+ // this will not work if user does Promise.all(import(), import())
472+ // we can also use AsyncLocalStorage to store callstack, but this won't work in the browser
473+ // maybe we should improve mock API in the future?
474+ this . mockContext . callstack = callstack
456475 return await this . callFunctionMock ( mockPath , mock )
457476 }
458477 finally {
478+ this . mockContext . callstack = null
459479 const indexMock = callstack . indexOf ( mockPath )
460480 callstack . splice ( indexMock , 1 )
461481 }
0 commit comments