@@ -180,6 +180,7 @@ export default class Runtime {
180
180
private _currentlyExecutingModulePath : string ;
181
181
private readonly _environment : JestEnvironment ;
182
182
private readonly _explicitShouldMock : Map < string , boolean > ;
183
+ private readonly _explicitShouldMockModule : Map < string , boolean > ;
183
184
private _fakeTimersImplementation :
184
185
| LegacyFakeTimers < unknown >
185
186
| ModernFakeTimers
@@ -194,6 +195,8 @@ export default class Runtime {
194
195
> ;
195
196
private _mockRegistry : Map < string , any > ;
196
197
private _isolatedMockRegistry : Map < string , any > | null ;
198
+ private _moduleMockRegistry : Map < string , VMModule > ;
199
+ private readonly _moduleMockFactories : Map < string , ( ) => unknown > ;
197
200
private readonly _moduleMocker : ModuleMocker ;
198
201
private _isolatedModuleRegistry : ModuleRegistry | null ;
199
202
private _moduleRegistry : ModuleRegistry ;
@@ -217,6 +220,7 @@ export default class Runtime {
217
220
private readonly _transitiveShouldMock : Map < string , boolean > ;
218
221
private _unmockList : RegExp | undefined ;
219
222
private readonly _virtualMocks : Map < string , boolean > ;
223
+ private readonly _virtualModuleMocks : Map < string , boolean > ;
220
224
private _moduleImplementation ?: typeof nativeModule . Module ;
221
225
private readonly jestObjectCaches : Map < string , Jest > ;
222
226
private jestGlobals ?: JestGlobals ;
@@ -236,11 +240,14 @@ export default class Runtime {
236
240
this . _currentlyExecutingModulePath = '' ;
237
241
this . _environment = environment ;
238
242
this . _explicitShouldMock = new Map ( ) ;
243
+ this . _explicitShouldMockModule = new Map ( ) ;
239
244
this . _internalModuleRegistry = new Map ( ) ;
240
245
this . _isCurrentlyExecutingManualMock = null ;
241
246
this . _mainModule = null ;
242
247
this . _mockFactories = new Map ( ) ;
243
248
this . _mockRegistry = new Map ( ) ;
249
+ this . _moduleMockRegistry = new Map ( ) ;
250
+ this . _moduleMockFactories = new Map ( ) ;
244
251
invariant (
245
252
this . _environment . moduleMocker ,
246
253
'`moduleMocker` must be set on an environment when created' ,
@@ -260,6 +267,7 @@ export default class Runtime {
260
267
this . _fileTransforms = new Map ( ) ;
261
268
this . _fileTransformsMutex = new Map ( ) ;
262
269
this . _virtualMocks = new Map ( ) ;
270
+ this . _virtualModuleMocks = new Map ( ) ;
263
271
this . jestObjectCaches = new Map ( ) ;
264
272
265
273
this . _mockMetaDataCache = new Map ( ) ;
@@ -523,6 +531,16 @@ export default class Runtime {
523
531
524
532
const [ path , query ] = specifier . split ( '?' ) ;
525
533
534
+ if (
535
+ this . _shouldMock (
536
+ referencingIdentifier ,
537
+ path ,
538
+ this . _explicitShouldMockModule ,
539
+ )
540
+ ) {
541
+ return this . importMock ( referencingIdentifier , path , context ) ;
542
+ }
543
+
526
544
const resolved = this . _resolveModule ( referencingIdentifier , path ) ;
527
545
528
546
if (
@@ -563,6 +581,8 @@ export default class Runtime {
563
581
async unstable_importModule (
564
582
from : Config . Path ,
565
583
moduleName ?: string ,
584
+ // TODO: implement this
585
+ _isImportActual = false ,
566
586
) : Promise < void > {
567
587
invariant (
568
588
runtimeSupportsVmModules ,
@@ -612,6 +632,109 @@ export default class Runtime {
612
632
return evaluateSyntheticModule ( module ) ;
613
633
}
614
634
635
+ private async importMock < T = unknown > (
636
+ from : Config . Path ,
637
+ moduleName : string ,
638
+ context : VMContext ,
639
+ ) : Promise < T > {
640
+ const moduleID = this . _resolver . getModuleID (
641
+ this . _virtualModuleMocks ,
642
+ from ,
643
+ moduleName ,
644
+ ) ;
645
+
646
+ if ( this . _moduleMockRegistry . has ( moduleID ) ) {
647
+ return this . _moduleMockRegistry . get ( moduleID ) ;
648
+ }
649
+
650
+ if ( this . _moduleMockFactories . has ( moduleID ) ) {
651
+ const invokedFactory : any = await this . _moduleMockFactories . get (
652
+ moduleID ,
653
+ // has check above makes this ok
654
+ ) ! ( ) ;
655
+
656
+ const module = new SyntheticModule (
657
+ Object . keys ( invokedFactory ) ,
658
+ function ( ) {
659
+ Object . entries ( invokedFactory ) . forEach ( ( [ key , value ] ) => {
660
+ // @ts -expect-error: TS doesn't know what `this` is
661
+ this . setExport ( key , value ) ;
662
+ } ) ;
663
+ } ,
664
+ // should identifier be `node://${moduleName}`?
665
+ { context, identifier : moduleName } ,
666
+ ) ;
667
+
668
+ this . _moduleMockRegistry . set ( moduleID , module ) ;
669
+
670
+ return evaluateSyntheticModule ( module ) ;
671
+ }
672
+
673
+ const manualMockOrStub = this . _resolver . getMockModule ( from , moduleName ) ;
674
+
675
+ let modulePath =
676
+ this . _resolver . getMockModule ( from , moduleName ) ||
677
+ this . _resolveModule ( from , moduleName ) ;
678
+
679
+ let isManualMock =
680
+ manualMockOrStub &&
681
+ ! this . _resolver . resolveStubModuleName ( from , moduleName ) ;
682
+ if ( ! isManualMock ) {
683
+ // If the actual module file has a __mocks__ dir sitting immediately next
684
+ // to it, look to see if there is a manual mock for this file.
685
+ //
686
+ // subDir1/my_module.js
687
+ // subDir1/__mocks__/my_module.js
688
+ // subDir2/my_module.js
689
+ // subDir2/__mocks__/my_module.js
690
+ //
691
+ // Where some other module does a relative require into each of the
692
+ // respective subDir{1,2} directories and expects a manual mock
693
+ // corresponding to that particular my_module.js file.
694
+
695
+ const moduleDir = path . dirname ( modulePath ) ;
696
+ const moduleFileName = path . basename ( modulePath ) ;
697
+ const potentialManualMock = path . join (
698
+ moduleDir ,
699
+ '__mocks__' ,
700
+ moduleFileName ,
701
+ ) ;
702
+ if ( fs . existsSync ( potentialManualMock ) ) {
703
+ isManualMock = true ;
704
+ modulePath = potentialManualMock ;
705
+ }
706
+ }
707
+ if ( isManualMock ) {
708
+ const localModule : InitialModule = {
709
+ children : [ ] ,
710
+ exports : { } ,
711
+ filename : modulePath ,
712
+ id : modulePath ,
713
+ loaded : false ,
714
+ path : modulePath ,
715
+ } ;
716
+
717
+ this . _loadModule (
718
+ localModule ,
719
+ from ,
720
+ moduleName ,
721
+ modulePath ,
722
+ undefined ,
723
+ this . _moduleMockRegistry ,
724
+ ) ;
725
+
726
+ this . _moduleMockRegistry . set ( moduleID , localModule . exports ) ;
727
+ } else {
728
+ // Look for a real module to generate an automock from
729
+ this . _moduleMockRegistry . set (
730
+ moduleID ,
731
+ this . _generateMock ( from , moduleName ) ,
732
+ ) ;
733
+ }
734
+
735
+ return this . _moduleMockRegistry . get ( moduleID ) ;
736
+ }
737
+
615
738
private getExportsOfCjs ( modulePath : Config . Path ) {
616
739
const cachedNamedExports = this . _cjsNamedExports . get ( modulePath ) ;
617
740
@@ -643,7 +766,7 @@ export default class Runtime {
643
766
from : Config . Path ,
644
767
moduleName ?: string ,
645
768
options ?: InternalModuleOptions ,
646
- isRequireActual ?: boolean | null ,
769
+ isRequireActual = false ,
647
770
) : T {
648
771
const moduleID = this . _resolver . getModuleID (
649
772
this . _virtualMocks ,
@@ -770,17 +893,12 @@ export default class Runtime {
770
893
moduleName ,
771
894
) ;
772
895
773
- if (
774
- this . _isolatedMockRegistry &&
775
- this . _isolatedMockRegistry . get ( moduleID )
776
- ) {
777
- return this . _isolatedMockRegistry . get ( moduleID ) ;
778
- } else if ( this . _mockRegistry . get ( moduleID ) ) {
779
- return this . _mockRegistry . get ( moduleID ) ;
780
- }
781
-
782
896
const mockRegistry = this . _isolatedMockRegistry || this . _mockRegistry ;
783
897
898
+ if ( mockRegistry . get ( moduleID ) ) {
899
+ return mockRegistry . get ( moduleID ) ;
900
+ }
901
+
784
902
if ( this . _mockFactories . has ( moduleID ) ) {
785
903
// has check above makes this ok
786
904
const module = this . _mockFactories . get ( moduleID ) ! ( ) ;
@@ -896,7 +1014,7 @@ export default class Runtime {
896
1014
}
897
1015
898
1016
try {
899
- if ( this . _shouldMock ( from , moduleName ) ) {
1017
+ if ( this . _shouldMock ( from , moduleName , this . _explicitShouldMock ) ) {
900
1018
return this . requireMock < T > ( from , moduleName ) ;
901
1019
} else {
902
1020
return this . requireModule < T > ( from , moduleName ) ;
@@ -952,6 +1070,7 @@ export default class Runtime {
952
1070
this . _moduleRegistry . clear ( ) ;
953
1071
this . _esmoduleRegistry . clear ( ) ;
954
1072
this . _cjsNamedExports . clear ( ) ;
1073
+ this . _moduleMockRegistry . clear ( ) ;
955
1074
956
1075
if ( this . _environment ) {
957
1076
if ( this . _environment . global ) {
@@ -1043,6 +1162,26 @@ export default class Runtime {
1043
1162
this . _mockFactories . set ( moduleID , mockFactory ) ;
1044
1163
}
1045
1164
1165
+ private setModuleMock (
1166
+ from : string ,
1167
+ moduleName : string ,
1168
+ mockFactory : ( ) => Promise < unknown > | unknown ,
1169
+ options ?: { virtual ?: boolean } ,
1170
+ ) : void {
1171
+ if ( options ?. virtual ) {
1172
+ const mockPath = this . _resolver . getModulePath ( from , moduleName ) ;
1173
+
1174
+ this . _virtualModuleMocks . set ( mockPath , true ) ;
1175
+ }
1176
+ const moduleID = this . _resolver . getModuleID (
1177
+ this . _virtualModuleMocks ,
1178
+ from ,
1179
+ moduleName ,
1180
+ ) ;
1181
+ this . _explicitShouldMockModule . set ( moduleID , true ) ;
1182
+ this . _moduleMockFactories . set ( moduleID , mockFactory ) ;
1183
+ }
1184
+
1046
1185
restoreAllMocks ( ) : void {
1047
1186
this . _moduleMocker . restoreAllMocks ( ) ;
1048
1187
}
@@ -1063,12 +1202,15 @@ export default class Runtime {
1063
1202
this . _internalModuleRegistry . clear ( ) ;
1064
1203
this . _mainModule = null ;
1065
1204
this . _mockFactories . clear ( ) ;
1205
+ this . _moduleMockFactories . clear ( ) ;
1066
1206
this . _mockMetaDataCache . clear ( ) ;
1067
1207
this . _shouldMockModuleCache . clear ( ) ;
1068
1208
this . _shouldUnmockTransitiveDependenciesCache . clear ( ) ;
1069
1209
this . _explicitShouldMock . clear ( ) ;
1210
+ this . _explicitShouldMockModule . clear ( ) ;
1070
1211
this . _transitiveShouldMock . clear ( ) ;
1071
1212
this . _virtualMocks . clear ( ) ;
1213
+ this . _virtualModuleMocks . clear ( ) ;
1072
1214
this . _cacheFS . clear ( ) ;
1073
1215
this . _unmockList = undefined ;
1074
1216
@@ -1516,8 +1658,11 @@ export default class Runtime {
1516
1658
) ;
1517
1659
}
1518
1660
1519
- private _shouldMock ( from : Config . Path , moduleName : string ) : boolean {
1520
- const explicitShouldMock = this . _explicitShouldMock ;
1661
+ private _shouldMock (
1662
+ from : Config . Path ,
1663
+ moduleName : string ,
1664
+ explicitShouldMock : Map < string , boolean > ,
1665
+ ) : boolean {
1521
1666
const moduleID = this . _resolver . getModuleID (
1522
1667
this . _virtualMocks ,
1523
1668
from ,
@@ -1687,6 +1832,24 @@ export default class Runtime {
1687
1832
this . setMock ( from , moduleName , mockFactory , options ) ;
1688
1833
return jestObject ;
1689
1834
} ;
1835
+ const mockModule : Jest [ 'mockModule' ] = (
1836
+ moduleName ,
1837
+ mockFactory ,
1838
+ options ,
1839
+ ) => {
1840
+ if ( mockFactory !== undefined ) {
1841
+ this . setModuleMock ( from , moduleName , mockFactory , options ) ;
1842
+ return jestObject ;
1843
+ }
1844
+
1845
+ const moduleID = this . _resolver . getModuleID (
1846
+ this . _virtualMocks ,
1847
+ from ,
1848
+ moduleName ,
1849
+ ) ;
1850
+ this . _explicitShouldMockModule . set ( moduleID , true ) ;
1851
+ return jestObject ;
1852
+ } ;
1690
1853
const clearAllMocks = ( ) => {
1691
1854
this . clearAllMocks ( ) ;
1692
1855
return jestObject ;
@@ -1785,6 +1948,7 @@ export default class Runtime {
1785
1948
isMockFunction : this . _moduleMocker . isMockFunction ,
1786
1949
isolateModules,
1787
1950
mock,
1951
+ mockModule,
1788
1952
requireActual : this . requireActual . bind ( this , from ) ,
1789
1953
requireMock : this . requireMock . bind ( this , from ) ,
1790
1954
resetAllMocks,
0 commit comments