@@ -148,6 +148,7 @@ export default class Runtime {
148148 private _currentlyExecutingModulePath : string ;
149149 private readonly _environment : JestEnvironment ;
150150 private readonly _explicitShouldMock : Map < string , boolean > ;
151+ private readonly _explicitShouldMockModule : Map < string , boolean > ;
151152 private _fakeTimersImplementation :
152153 | LegacyFakeTimers < unknown >
153154 | ModernFakeTimers
@@ -162,6 +163,8 @@ export default class Runtime {
162163 > ;
163164 private _mockRegistry : Map < string , any > ;
164165 private _isolatedMockRegistry : Map < string , any > | null ;
166+ private _moduleMockRegistry : Map < string , VMModule > ;
167+ private readonly _moduleMockFactories : Map < string , ( ) => unknown > ;
165168 private readonly _moduleMocker : ModuleMocker ;
166169 private _isolatedModuleRegistry : ModuleRegistry | null ;
167170 private _moduleRegistry : ModuleRegistry ;
@@ -183,6 +186,7 @@ export default class Runtime {
183186 private readonly _transitiveShouldMock : Map < string , boolean > ;
184187 private _unmockList : RegExp | undefined ;
185188 private readonly _virtualMocks : Map < string , boolean > ;
189+ private readonly _virtualModuleMocks : Map < string , boolean > ;
186190 private _moduleImplementation ?: typeof nativeModule . Module ;
187191 private readonly jestObjectCaches : Map < string , Jest > ;
188192 private jestGlobals ?: JestGlobals ;
@@ -201,11 +205,14 @@ export default class Runtime {
201205 this . _currentlyExecutingModulePath = '' ;
202206 this . _environment = environment ;
203207 this . _explicitShouldMock = new Map ( ) ;
208+ this . _explicitShouldMockModule = new Map ( ) ;
204209 this . _internalModuleRegistry = new Map ( ) ;
205210 this . _isCurrentlyExecutingManualMock = null ;
206211 this . _mainModule = null ;
207212 this . _mockFactories = new Map ( ) ;
208213 this . _mockRegistry = new Map ( ) ;
214+ this . _moduleMockRegistry = new Map ( ) ;
215+ this . _moduleMockFactories = new Map ( ) ;
209216 invariant (
210217 this . _environment . moduleMocker ,
211218 '`moduleMocker` must be set on an environment when created' ,
@@ -223,6 +230,7 @@ export default class Runtime {
223230 this . _sourceMapRegistry = new Map ( ) ;
224231 this . _fileTransforms = new Map ( ) ;
225232 this . _virtualMocks = new Map ( ) ;
233+ this . _virtualModuleMocks = new Map ( ) ;
226234 this . jestObjectCaches = new Map ( ) ;
227235
228236 this . _mockMetaDataCache = new Map ( ) ;
@@ -490,6 +498,16 @@ export default class Runtime {
490498
491499 const [ path , query ] = specifier . split ( '?' ) ;
492500
501+ if (
502+ this . _shouldMock (
503+ referencingIdentifier ,
504+ path ,
505+ this . _explicitShouldMockModule ,
506+ )
507+ ) {
508+ return this . importMock ( referencingIdentifier , path , context ) ;
509+ }
510+
493511 const resolved = this . _resolveModule ( referencingIdentifier , path ) ;
494512
495513 if (
@@ -505,6 +523,8 @@ export default class Runtime {
505523 async unstable_importModule (
506524 from : Config . Path ,
507525 moduleName ?: string ,
526+ // TODO: implement this
527+ _isImportActual = false ,
508528 ) : Promise < void > {
509529 invariant (
510530 runtimeSupportsVmModules ,
@@ -552,6 +572,109 @@ export default class Runtime {
552572 return evaluateSyntheticModule ( module ) ;
553573 }
554574
575+ private async importMock < T = unknown > (
576+ from : Config . Path ,
577+ moduleName : string ,
578+ context : VMContext ,
579+ ) : Promise < T > {
580+ const moduleID = this . _resolver . getModuleID (
581+ this . _virtualModuleMocks ,
582+ from ,
583+ moduleName ,
584+ ) ;
585+
586+ if ( this . _moduleMockRegistry . has ( moduleID ) ) {
587+ return this . _moduleMockRegistry . get ( moduleID ) ;
588+ }
589+
590+ if ( this . _moduleMockFactories . has ( moduleID ) ) {
591+ const invokedFactory : any = await this . _moduleMockFactories . get (
592+ moduleID ,
593+ // has check above makes this ok
594+ ) ! ( ) ;
595+
596+ const module = new SyntheticModule (
597+ Object . keys ( invokedFactory ) ,
598+ function ( ) {
599+ Object . entries ( invokedFactory ) . forEach ( ( [ key , value ] ) => {
600+ // @ts -expect-error: TS doesn't know what `this` is
601+ this . setExport ( key , value ) ;
602+ } ) ;
603+ } ,
604+ // should identifier be `node://${moduleName}`?
605+ { context, identifier : moduleName } ,
606+ ) ;
607+
608+ this . _moduleMockRegistry . set ( moduleID , module ) ;
609+
610+ return evaluateSyntheticModule ( module ) ;
611+ }
612+
613+ const manualMockOrStub = this . _resolver . getMockModule ( from , moduleName ) ;
614+
615+ let modulePath =
616+ this . _resolver . getMockModule ( from , moduleName ) ||
617+ this . _resolveModule ( from , moduleName ) ;
618+
619+ let isManualMock =
620+ manualMockOrStub &&
621+ ! this . _resolver . resolveStubModuleName ( from , moduleName ) ;
622+ if ( ! isManualMock ) {
623+ // If the actual module file has a __mocks__ dir sitting immediately next
624+ // to it, look to see if there is a manual mock for this file.
625+ //
626+ // subDir1/my_module.js
627+ // subDir1/__mocks__/my_module.js
628+ // subDir2/my_module.js
629+ // subDir2/__mocks__/my_module.js
630+ //
631+ // Where some other module does a relative require into each of the
632+ // respective subDir{1,2} directories and expects a manual mock
633+ // corresponding to that particular my_module.js file.
634+
635+ const moduleDir = path . dirname ( modulePath ) ;
636+ const moduleFileName = path . basename ( modulePath ) ;
637+ const potentialManualMock = path . join (
638+ moduleDir ,
639+ '__mocks__' ,
640+ moduleFileName ,
641+ ) ;
642+ if ( fs . existsSync ( potentialManualMock ) ) {
643+ isManualMock = true ;
644+ modulePath = potentialManualMock ;
645+ }
646+ }
647+ if ( isManualMock ) {
648+ const localModule : InitialModule = {
649+ children : [ ] ,
650+ exports : { } ,
651+ filename : modulePath ,
652+ id : modulePath ,
653+ loaded : false ,
654+ path : modulePath ,
655+ } ;
656+
657+ this . _loadModule (
658+ localModule ,
659+ from ,
660+ moduleName ,
661+ modulePath ,
662+ undefined ,
663+ this . _moduleMockRegistry ,
664+ ) ;
665+
666+ this . _moduleMockRegistry . set ( moduleID , localModule . exports ) ;
667+ } else {
668+ // Look for a real module to generate an automock from
669+ this . _moduleMockRegistry . set (
670+ moduleID ,
671+ this . _generateMock ( from , moduleName ) ,
672+ ) ;
673+ }
674+
675+ return this . _moduleMockRegistry . get ( moduleID ) ;
676+ }
677+
555678 private getExportsOfCjs ( modulePath : Config . Path ) {
556679 const cachedNamedExports = this . _cjsNamedExports . get ( modulePath ) ;
557680
@@ -583,7 +706,7 @@ export default class Runtime {
583706 from : Config . Path ,
584707 moduleName ?: string ,
585708 options ?: InternalModuleOptions ,
586- isRequireActual ?: boolean | null ,
709+ isRequireActual = false ,
587710 ) : T {
588711 const moduleID = this . _resolver . getModuleID (
589712 this . _virtualMocks ,
@@ -620,12 +743,10 @@ export default class Runtime {
620743
621744 if ( options ?. isInternalModule ) {
622745 moduleRegistry = this . _internalModuleRegistry ;
746+ } else if ( this . _isolatedModuleRegistry ) {
747+ moduleRegistry = this . _isolatedModuleRegistry ;
623748 } else {
624- if ( this . _isolatedModuleRegistry ) {
625- moduleRegistry = this . _isolatedModuleRegistry ;
626- } else {
627- moduleRegistry = this . _moduleRegistry ;
628- }
749+ moduleRegistry = this . _moduleRegistry ;
629750 }
630751
631752 const module = moduleRegistry . get ( modulePath ) ;
@@ -686,17 +807,12 @@ export default class Runtime {
686807 moduleName ,
687808 ) ;
688809
689- if (
690- this . _isolatedMockRegistry &&
691- this . _isolatedMockRegistry . get ( moduleID )
692- ) {
693- return this . _isolatedMockRegistry . get ( moduleID ) ;
694- } else if ( this . _mockRegistry . get ( moduleID ) ) {
695- return this . _mockRegistry . get ( moduleID ) ;
696- }
697-
698810 const mockRegistry = this . _isolatedMockRegistry || this . _mockRegistry ;
699811
812+ if ( mockRegistry . get ( moduleID ) ) {
813+ return mockRegistry . get ( moduleID ) ;
814+ }
815+
700816 if ( this . _mockFactories . has ( moduleID ) ) {
701817 // has check above makes this ok
702818 const module = this . _mockFactories . get ( moduleID ) ! ( ) ;
@@ -813,7 +929,7 @@ export default class Runtime {
813929 }
814930
815931 try {
816- if ( this . _shouldMock ( from , moduleName ) ) {
932+ if ( this . _shouldMock ( from , moduleName , this . _explicitShouldMock ) ) {
817933 return this . requireMock < T > ( from , moduleName ) ;
818934 } else {
819935 return this . requireModule < T > ( from , moduleName ) ;
@@ -869,6 +985,7 @@ export default class Runtime {
869985 this . _moduleRegistry . clear ( ) ;
870986 this . _esmoduleRegistry . clear ( ) ;
871987 this . _cjsNamedExports . clear ( ) ;
988+ this . _moduleMockRegistry . clear ( ) ;
872989
873990 if ( this . _environment ) {
874991 if ( this . _environment . global ) {
@@ -957,6 +1074,26 @@ export default class Runtime {
9571074 this . _mockFactories . set ( moduleID , mockFactory ) ;
9581075 }
9591076
1077+ private setModuleMock (
1078+ from : string ,
1079+ moduleName : string ,
1080+ mockFactory : ( ) => Promise < unknown > | unknown ,
1081+ options ?: { virtual ?: boolean } ,
1082+ ) : void {
1083+ if ( options ?. virtual ) {
1084+ const mockPath = this . _resolver . getModulePath ( from , moduleName ) ;
1085+
1086+ this . _virtualModuleMocks . set ( mockPath , true ) ;
1087+ }
1088+ const moduleID = this . _resolver . getModuleID (
1089+ this . _virtualModuleMocks ,
1090+ from ,
1091+ moduleName ,
1092+ ) ;
1093+ this . _explicitShouldMockModule . set ( moduleID , true ) ;
1094+ this . _moduleMockFactories . set ( moduleID , mockFactory ) ;
1095+ }
1096+
9601097 restoreAllMocks ( ) : void {
9611098 this . _moduleMocker . restoreAllMocks ( ) ;
9621099 }
@@ -977,12 +1114,15 @@ export default class Runtime {
9771114 this . _internalModuleRegistry . clear ( ) ;
9781115 this . _mainModule = null ;
9791116 this . _mockFactories . clear ( ) ;
1117+ this . _moduleMockFactories . clear ( ) ;
9801118 this . _mockMetaDataCache . clear ( ) ;
9811119 this . _shouldMockModuleCache . clear ( ) ;
9821120 this . _shouldUnmockTransitiveDependenciesCache . clear ( ) ;
9831121 this . _explicitShouldMock . clear ( ) ;
1122+ this . _explicitShouldMockModule . clear ( ) ;
9841123 this . _transitiveShouldMock . clear ( ) ;
9851124 this . _virtualMocks . clear ( ) ;
1125+ this . _virtualModuleMocks . clear ( ) ;
9861126 this . _cacheFS . clear ( ) ;
9871127 this . _unmockList = undefined ;
9881128
@@ -1374,8 +1514,11 @@ export default class Runtime {
13741514 ) ;
13751515 }
13761516
1377- private _shouldMock ( from : Config . Path , moduleName : string ) : boolean {
1378- const explicitShouldMock = this . _explicitShouldMock ;
1517+ private _shouldMock (
1518+ from : Config . Path ,
1519+ moduleName : string ,
1520+ explicitShouldMock : Map < string , boolean > ,
1521+ ) : boolean {
13791522 const moduleID = this . _resolver . getModuleID (
13801523 this . _virtualMocks ,
13811524 from ,
@@ -1543,6 +1686,24 @@ export default class Runtime {
15431686 this . setMock ( from , moduleName , mockFactory , options ) ;
15441687 return jestObject ;
15451688 } ;
1689+ const mockModule : Jest [ 'mockModule' ] = (
1690+ moduleName ,
1691+ mockFactory ,
1692+ options ,
1693+ ) => {
1694+ if ( mockFactory !== undefined ) {
1695+ this . setModuleMock ( from , moduleName , mockFactory , options ) ;
1696+ return jestObject ;
1697+ }
1698+
1699+ const moduleID = this . _resolver . getModuleID (
1700+ this . _virtualMocks ,
1701+ from ,
1702+ moduleName ,
1703+ ) ;
1704+ this . _explicitShouldMockModule . set ( moduleID , true ) ;
1705+ return jestObject ;
1706+ } ;
15461707 const clearAllMocks = ( ) => {
15471708 this . clearAllMocks ( ) ;
15481709 return jestObject ;
@@ -1641,6 +1802,7 @@ export default class Runtime {
16411802 isMockFunction : this . _moduleMocker . isMockFunction ,
16421803 isolateModules,
16431804 mock,
1805+ mockModule,
16441806 requireActual : this . requireActual . bind ( this , from ) ,
16451807 requireMock : this . requireMock . bind ( this , from ) ,
16461808 resetAllMocks,
0 commit comments