@@ -156,6 +156,7 @@ export default class Runtime {
156
156
private _currentlyExecutingModulePath : string ;
157
157
private readonly _environment : JestEnvironment ;
158
158
private readonly _explicitShouldMock : Map < string , boolean > ;
159
+ private readonly _explicitShouldMockModule : Map < string , boolean > ;
159
160
private _fakeTimersImplementation :
160
161
| LegacyFakeTimers < unknown >
161
162
| ModernFakeTimers
@@ -170,6 +171,8 @@ export default class Runtime {
170
171
> ;
171
172
private _mockRegistry : Map < string , any > ;
172
173
private _isolatedMockRegistry : Map < string , any > | null ;
174
+ private _moduleMockRegistry : Map < string , VMModule > ;
175
+ private readonly _moduleMockFactories : Map < string , ( ) => unknown > ;
173
176
private readonly _moduleMocker : ModuleMocker ;
174
177
private _isolatedModuleRegistry : ModuleRegistry | null ;
175
178
private _moduleRegistry : ModuleRegistry ;
@@ -193,6 +196,7 @@ export default class Runtime {
193
196
private readonly _transitiveShouldMock : Map < string , boolean > ;
194
197
private _unmockList : RegExp | undefined ;
195
198
private readonly _virtualMocks : Map < string , boolean > ;
199
+ private readonly _virtualModuleMocks : Map < string , boolean > ;
196
200
private _moduleImplementation ?: typeof nativeModule . Module ;
197
201
private readonly jestObjectCaches : Map < string , Jest > ;
198
202
private jestGlobals ?: JestGlobals ;
@@ -212,11 +216,14 @@ export default class Runtime {
212
216
this . _currentlyExecutingModulePath = '' ;
213
217
this . _environment = environment ;
214
218
this . _explicitShouldMock = new Map ( ) ;
219
+ this . _explicitShouldMockModule = new Map ( ) ;
215
220
this . _internalModuleRegistry = new Map ( ) ;
216
221
this . _isCurrentlyExecutingManualMock = null ;
217
222
this . _mainModule = null ;
218
223
this . _mockFactories = new Map ( ) ;
219
224
this . _mockRegistry = new Map ( ) ;
225
+ this . _moduleMockRegistry = new Map ( ) ;
226
+ this . _moduleMockFactories = new Map ( ) ;
220
227
invariant (
221
228
this . _environment . moduleMocker ,
222
229
'`moduleMocker` must be set on an environment when created' ,
@@ -236,6 +243,7 @@ export default class Runtime {
236
243
this . _fileTransforms = new Map ( ) ;
237
244
this . _fileTransformsMutex = new Map ( ) ;
238
245
this . _virtualMocks = new Map ( ) ;
246
+ this . _virtualModuleMocks = new Map ( ) ;
239
247
this . jestObjectCaches = new Map ( ) ;
240
248
241
249
this . _mockMetaDataCache = new Map ( ) ;
@@ -499,6 +507,16 @@ export default class Runtime {
499
507
500
508
const [ path , query ] = specifier . split ( '?' ) ;
501
509
510
+ if (
511
+ this . _shouldMock (
512
+ referencingIdentifier ,
513
+ path ,
514
+ this . _explicitShouldMockModule ,
515
+ )
516
+ ) {
517
+ return this . importMock ( referencingIdentifier , path , context ) ;
518
+ }
519
+
502
520
const resolved = this . _resolveModule ( referencingIdentifier , path ) ;
503
521
504
522
if (
@@ -539,6 +557,8 @@ export default class Runtime {
539
557
async unstable_importModule (
540
558
from : Config . Path ,
541
559
moduleName ?: string ,
560
+ // TODO: implement this
561
+ _isImportActual = false ,
542
562
) : Promise < void > {
543
563
invariant (
544
564
runtimeSupportsVmModules ,
@@ -588,6 +608,109 @@ export default class Runtime {
588
608
return evaluateSyntheticModule ( module ) ;
589
609
}
590
610
611
+ private async importMock < T = unknown > (
612
+ from : Config . Path ,
613
+ moduleName : string ,
614
+ context : VMContext ,
615
+ ) : Promise < T > {
616
+ const moduleID = this . _resolver . getModuleID (
617
+ this . _virtualModuleMocks ,
618
+ from ,
619
+ moduleName ,
620
+ ) ;
621
+
622
+ if ( this . _moduleMockRegistry . has ( moduleID ) ) {
623
+ return this . _moduleMockRegistry . get ( moduleID ) ;
624
+ }
625
+
626
+ if ( this . _moduleMockFactories . has ( moduleID ) ) {
627
+ const invokedFactory : any = await this . _moduleMockFactories . get (
628
+ moduleID ,
629
+ // has check above makes this ok
630
+ ) ! ( ) ;
631
+
632
+ const module = new SyntheticModule (
633
+ Object . keys ( invokedFactory ) ,
634
+ function ( ) {
635
+ Object . entries ( invokedFactory ) . forEach ( ( [ key , value ] ) => {
636
+ // @ts -expect-error: TS doesn't know what `this` is
637
+ this . setExport ( key , value ) ;
638
+ } ) ;
639
+ } ,
640
+ // should identifier be `node://${moduleName}`?
641
+ { context, identifier : moduleName } ,
642
+ ) ;
643
+
644
+ this . _moduleMockRegistry . set ( moduleID , module ) ;
645
+
646
+ return evaluateSyntheticModule ( module ) ;
647
+ }
648
+
649
+ const manualMockOrStub = this . _resolver . getMockModule ( from , moduleName ) ;
650
+
651
+ let modulePath =
652
+ this . _resolver . getMockModule ( from , moduleName ) ||
653
+ this . _resolveModule ( from , moduleName ) ;
654
+
655
+ let isManualMock =
656
+ manualMockOrStub &&
657
+ ! this . _resolver . resolveStubModuleName ( from , moduleName ) ;
658
+ if ( ! isManualMock ) {
659
+ // If the actual module file has a __mocks__ dir sitting immediately next
660
+ // to it, look to see if there is a manual mock for this file.
661
+ //
662
+ // subDir1/my_module.js
663
+ // subDir1/__mocks__/my_module.js
664
+ // subDir2/my_module.js
665
+ // subDir2/__mocks__/my_module.js
666
+ //
667
+ // Where some other module does a relative require into each of the
668
+ // respective subDir{1,2} directories and expects a manual mock
669
+ // corresponding to that particular my_module.js file.
670
+
671
+ const moduleDir = path . dirname ( modulePath ) ;
672
+ const moduleFileName = path . basename ( modulePath ) ;
673
+ const potentialManualMock = path . join (
674
+ moduleDir ,
675
+ '__mocks__' ,
676
+ moduleFileName ,
677
+ ) ;
678
+ if ( fs . existsSync ( potentialManualMock ) ) {
679
+ isManualMock = true ;
680
+ modulePath = potentialManualMock ;
681
+ }
682
+ }
683
+ if ( isManualMock ) {
684
+ const localModule : InitialModule = {
685
+ children : [ ] ,
686
+ exports : { } ,
687
+ filename : modulePath ,
688
+ id : modulePath ,
689
+ loaded : false ,
690
+ path : modulePath ,
691
+ } ;
692
+
693
+ this . _loadModule (
694
+ localModule ,
695
+ from ,
696
+ moduleName ,
697
+ modulePath ,
698
+ undefined ,
699
+ this . _moduleMockRegistry ,
700
+ ) ;
701
+
702
+ this . _moduleMockRegistry . set ( moduleID , localModule . exports ) ;
703
+ } else {
704
+ // Look for a real module to generate an automock from
705
+ this . _moduleMockRegistry . set (
706
+ moduleID ,
707
+ this . _generateMock ( from , moduleName ) ,
708
+ ) ;
709
+ }
710
+
711
+ return this . _moduleMockRegistry . get ( moduleID ) ;
712
+ }
713
+
591
714
private getExportsOfCjs ( modulePath : Config . Path ) {
592
715
const cachedNamedExports = this . _cjsNamedExports . get ( modulePath ) ;
593
716
@@ -619,7 +742,7 @@ export default class Runtime {
619
742
from : Config . Path ,
620
743
moduleName ?: string ,
621
744
options ?: InternalModuleOptions ,
622
- isRequireActual ?: boolean | null ,
745
+ isRequireActual = false ,
623
746
) : T {
624
747
const moduleID = this . _resolver . getModuleID (
625
748
this . _virtualMocks ,
@@ -743,17 +866,12 @@ export default class Runtime {
743
866
moduleName ,
744
867
) ;
745
868
746
- if (
747
- this . _isolatedMockRegistry &&
748
- this . _isolatedMockRegistry . get ( moduleID )
749
- ) {
750
- return this . _isolatedMockRegistry . get ( moduleID ) ;
751
- } else if ( this . _mockRegistry . get ( moduleID ) ) {
752
- return this . _mockRegistry . get ( moduleID ) ;
753
- }
754
-
755
869
const mockRegistry = this . _isolatedMockRegistry || this . _mockRegistry ;
756
870
871
+ if ( mockRegistry . get ( moduleID ) ) {
872
+ return mockRegistry . get ( moduleID ) ;
873
+ }
874
+
757
875
if ( this . _mockFactories . has ( moduleID ) ) {
758
876
// has check above makes this ok
759
877
const module = this . _mockFactories . get ( moduleID ) ! ( ) ;
@@ -869,7 +987,7 @@ export default class Runtime {
869
987
}
870
988
871
989
try {
872
- if ( this . _shouldMock ( from , moduleName ) ) {
990
+ if ( this . _shouldMock ( from , moduleName , this . _explicitShouldMock ) ) {
873
991
return this . requireMock < T > ( from , moduleName ) ;
874
992
} else {
875
993
return this . requireModule < T > ( from , moduleName ) ;
@@ -925,6 +1043,7 @@ export default class Runtime {
925
1043
this . _moduleRegistry . clear ( ) ;
926
1044
this . _esmoduleRegistry . clear ( ) ;
927
1045
this . _cjsNamedExports . clear ( ) ;
1046
+ this . _moduleMockRegistry . clear ( ) ;
928
1047
929
1048
if ( this . _environment ) {
930
1049
if ( this . _environment . global ) {
@@ -1014,6 +1133,26 @@ export default class Runtime {
1014
1133
this . _mockFactories . set ( moduleID , mockFactory ) ;
1015
1134
}
1016
1135
1136
+ private setModuleMock (
1137
+ from : string ,
1138
+ moduleName : string ,
1139
+ mockFactory : ( ) => Promise < unknown > | unknown ,
1140
+ options ?: { virtual ?: boolean } ,
1141
+ ) : void {
1142
+ if ( options ?. virtual ) {
1143
+ const mockPath = this . _resolver . getModulePath ( from , moduleName ) ;
1144
+
1145
+ this . _virtualModuleMocks . set ( mockPath , true ) ;
1146
+ }
1147
+ const moduleID = this . _resolver . getModuleID (
1148
+ this . _virtualModuleMocks ,
1149
+ from ,
1150
+ moduleName ,
1151
+ ) ;
1152
+ this . _explicitShouldMockModule . set ( moduleID , true ) ;
1153
+ this . _moduleMockFactories . set ( moduleID , mockFactory ) ;
1154
+ }
1155
+
1017
1156
restoreAllMocks ( ) : void {
1018
1157
this . _moduleMocker . restoreAllMocks ( ) ;
1019
1158
}
@@ -1034,12 +1173,15 @@ export default class Runtime {
1034
1173
this . _internalModuleRegistry . clear ( ) ;
1035
1174
this . _mainModule = null ;
1036
1175
this . _mockFactories . clear ( ) ;
1176
+ this . _moduleMockFactories . clear ( ) ;
1037
1177
this . _mockMetaDataCache . clear ( ) ;
1038
1178
this . _shouldMockModuleCache . clear ( ) ;
1039
1179
this . _shouldUnmockTransitiveDependenciesCache . clear ( ) ;
1040
1180
this . _explicitShouldMock . clear ( ) ;
1181
+ this . _explicitShouldMockModule . clear ( ) ;
1041
1182
this . _transitiveShouldMock . clear ( ) ;
1042
1183
this . _virtualMocks . clear ( ) ;
1184
+ this . _virtualModuleMocks . clear ( ) ;
1043
1185
this . _cacheFS . clear ( ) ;
1044
1186
this . _unmockList = undefined ;
1045
1187
@@ -1479,8 +1621,11 @@ export default class Runtime {
1479
1621
) ;
1480
1622
}
1481
1623
1482
- private _shouldMock ( from : Config . Path , moduleName : string ) : boolean {
1483
- const explicitShouldMock = this . _explicitShouldMock ;
1624
+ private _shouldMock (
1625
+ from : Config . Path ,
1626
+ moduleName : string ,
1627
+ explicitShouldMock : Map < string , boolean > ,
1628
+ ) : boolean {
1484
1629
const moduleID = this . _resolver . getModuleID (
1485
1630
this . _virtualMocks ,
1486
1631
from ,
@@ -1650,6 +1795,24 @@ export default class Runtime {
1650
1795
this . setMock ( from , moduleName , mockFactory , options ) ;
1651
1796
return jestObject ;
1652
1797
} ;
1798
+ const mockModule : Jest [ 'mockModule' ] = (
1799
+ moduleName ,
1800
+ mockFactory ,
1801
+ options ,
1802
+ ) => {
1803
+ if ( mockFactory !== undefined ) {
1804
+ this . setModuleMock ( from , moduleName , mockFactory , options ) ;
1805
+ return jestObject ;
1806
+ }
1807
+
1808
+ const moduleID = this . _resolver . getModuleID (
1809
+ this . _virtualMocks ,
1810
+ from ,
1811
+ moduleName ,
1812
+ ) ;
1813
+ this . _explicitShouldMockModule . set ( moduleID , true ) ;
1814
+ return jestObject ;
1815
+ } ;
1653
1816
const clearAllMocks = ( ) => {
1654
1817
this . clearAllMocks ( ) ;
1655
1818
return jestObject ;
@@ -1748,6 +1911,7 @@ export default class Runtime {
1748
1911
isMockFunction : this . _moduleMocker . isMockFunction ,
1749
1912
isolateModules,
1750
1913
mock,
1914
+ mockModule,
1751
1915
requireActual : this . requireActual . bind ( this , from ) ,
1752
1916
requireMock : this . requireMock . bind ( this , from ) ,
1753
1917
resetAllMocks,
0 commit comments