@@ -32,6 +32,13 @@ class RefTracker {
32
32
33
33
type Key = string | symbol
34
34
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
+
35
42
function isSpecialProp ( prop : Key , parentType : string ) {
36
43
return parentType . includes ( 'Function' )
37
44
&& typeof prop === 'string'
@@ -54,6 +61,10 @@ export class VitestMocker {
54
61
55
62
private filterPublicKeys : ( symbol | string ) [ ]
56
63
64
+ private mockContext : MockContext = {
65
+ callstack : null ,
66
+ }
67
+
57
68
constructor (
58
69
public executor : VitestExecutor ,
59
70
) {
@@ -201,9 +212,9 @@ export class VitestMocker {
201
212
throw this . createError (
202
213
`[vitest] No "${ String ( prop ) } " export is defined on the "${ mockpath } " mock. `
203
214
+ '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( )
207
218
return {
208
219
...actual,
209
220
// your mocked methods
@@ -221,6 +232,10 @@ export class VitestMocker {
221
232
return moduleExports
222
233
}
223
234
235
+ public getMockContext ( ) {
236
+ return this . mockContext
237
+ }
238
+
224
239
public getMockPath ( dep : string ) {
225
240
return `mock:${ dep } `
226
241
}
@@ -407,9 +422,9 @@ export class VitestMocker {
407
422
this . deleteCachedItem ( id )
408
423
}
409
424
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 ] )
413
428
return result as T
414
429
}
415
430
@@ -453,9 +468,14 @@ export class VitestMocker {
453
468
if ( typeof mock === 'function' && ! callstack . includes ( mockPath ) && ! callstack . includes ( url ) ) {
454
469
try {
455
470
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
456
475
return await this . callFunctionMock ( mockPath , mock )
457
476
}
458
477
finally {
478
+ this . mockContext . callstack = null
459
479
const indexMock = callstack . indexOf ( mockPath )
460
480
callstack . splice ( indexMock , 1 )
461
481
}
0 commit comments