@@ -148,6 +148,7 @@ export default class Runtime {
148
148
private _currentlyExecutingModulePath : string ;
149
149
private readonly _environment : JestEnvironment ;
150
150
private readonly _explicitShouldMock : Map < string , boolean > ;
151
+ private readonly _explicitShouldMockModule : Map < string , boolean > ;
151
152
private _fakeTimersImplementation :
152
153
| LegacyFakeTimers < unknown >
153
154
| ModernFakeTimers
@@ -162,6 +163,8 @@ export default class Runtime {
162
163
> ;
163
164
private _mockRegistry : Map < string , any > ;
164
165
private _isolatedMockRegistry : Map < string , any > | null ;
166
+ private _moduleMockRegistry : Map < string , VMModule > ;
167
+ private readonly _moduleMockFactories : Map < string , ( ) => unknown > ;
165
168
private readonly _moduleMocker : ModuleMocker ;
166
169
private _isolatedModuleRegistry : ModuleRegistry | null ;
167
170
private _moduleRegistry : ModuleRegistry ;
@@ -182,6 +185,7 @@ export default class Runtime {
182
185
private readonly _transitiveShouldMock : Map < string , boolean > ;
183
186
private _unmockList : RegExp | undefined ;
184
187
private readonly _virtualMocks : Map < string , boolean > ;
188
+ private readonly _virtualModuleMocks : Map < string , boolean > ;
185
189
private _moduleImplementation ?: typeof nativeModule . Module ;
186
190
private readonly jestObjectCaches : Map < string , Jest > ;
187
191
private jestGlobals ?: JestGlobals ;
@@ -200,11 +204,14 @@ export default class Runtime {
200
204
this . _currentlyExecutingModulePath = '' ;
201
205
this . _environment = environment ;
202
206
this . _explicitShouldMock = new Map ( ) ;
207
+ this . _explicitShouldMockModule = new Map ( ) ;
203
208
this . _internalModuleRegistry = new Map ( ) ;
204
209
this . _isCurrentlyExecutingManualMock = null ;
205
210
this . _mainModule = null ;
206
211
this . _mockFactories = new Map ( ) ;
207
212
this . _mockRegistry = new Map ( ) ;
213
+ this . _moduleMockRegistry = new Map ( ) ;
214
+ this . _moduleMockFactories = new Map ( ) ;
208
215
invariant (
209
216
this . _environment . moduleMocker ,
210
217
'`moduleMocker` must be set on an environment when created' ,
@@ -221,6 +228,7 @@ export default class Runtime {
221
228
this . _sourceMapRegistry = new Map ( ) ;
222
229
this . _fileTransforms = new Map ( ) ;
223
230
this . _virtualMocks = new Map ( ) ;
231
+ this . _virtualModuleMocks = new Map ( ) ;
224
232
this . jestObjectCaches = new Map ( ) ;
225
233
226
234
this . _mockMetaDataCache = new Map ( ) ;
@@ -488,6 +496,16 @@ export default class Runtime {
488
496
489
497
const [ path , query ] = specifier . split ( '?' ) ;
490
498
499
+ if (
500
+ this . _shouldMock (
501
+ referencingIdentifier ,
502
+ path ,
503
+ this . _explicitShouldMockModule ,
504
+ )
505
+ ) {
506
+ return this . importMock ( referencingIdentifier , path , context ) ;
507
+ }
508
+
491
509
const resolved = this . _resolveModule ( referencingIdentifier , path ) ;
492
510
493
511
if (
@@ -503,6 +521,8 @@ export default class Runtime {
503
521
async unstable_importModule (
504
522
from : Config . Path ,
505
523
moduleName ?: string ,
524
+ // TODO: implement this
525
+ _isImportActual = false ,
506
526
) : Promise < void > {
507
527
invariant (
508
528
runtimeSupportsVmModules ,
@@ -556,11 +576,114 @@ export default class Runtime {
556
576
return evaluateSyntheticModule ( module ) ;
557
577
}
558
578
579
+ private async importMock < T = unknown > (
580
+ from : Config . Path ,
581
+ moduleName : string ,
582
+ context : VMContext ,
583
+ ) : Promise < T > {
584
+ const moduleID = this . _resolver . getModuleID (
585
+ this . _virtualModuleMocks ,
586
+ from ,
587
+ moduleName ,
588
+ ) ;
589
+
590
+ if ( this . _moduleMockRegistry . has ( moduleID ) ) {
591
+ return this . _moduleMockRegistry . get ( moduleID ) ;
592
+ }
593
+
594
+ if ( this . _moduleMockFactories . has ( moduleID ) ) {
595
+ const invokedFactory : any = await this . _moduleMockFactories . get (
596
+ moduleID ,
597
+ // has check above makes this ok
598
+ ) ! ( ) ;
599
+
600
+ const module = new SyntheticModule (
601
+ Object . keys ( invokedFactory ) ,
602
+ function ( ) {
603
+ Object . entries ( invokedFactory ) . forEach ( ( [ key , value ] ) => {
604
+ // @ts -expect-error: TS doesn't know what `this` is
605
+ this . setExport ( key , value ) ;
606
+ } ) ;
607
+ } ,
608
+ // should identifier be `node://${moduleName}`?
609
+ { context, identifier : moduleName } ,
610
+ ) ;
611
+
612
+ this . _moduleMockRegistry . set ( moduleID , module ) ;
613
+
614
+ return evaluateSyntheticModule ( module ) ;
615
+ }
616
+
617
+ const manualMockOrStub = this . _resolver . getMockModule ( from , moduleName ) ;
618
+
619
+ let modulePath =
620
+ this . _resolver . getMockModule ( from , moduleName ) ||
621
+ this . _resolveModule ( from , moduleName ) ;
622
+
623
+ let isManualMock =
624
+ manualMockOrStub &&
625
+ ! this . _resolver . resolveStubModuleName ( from , moduleName ) ;
626
+ if ( ! isManualMock ) {
627
+ // If the actual module file has a __mocks__ dir sitting immediately next
628
+ // to it, look to see if there is a manual mock for this file.
629
+ //
630
+ // subDir1/my_module.js
631
+ // subDir1/__mocks__/my_module.js
632
+ // subDir2/my_module.js
633
+ // subDir2/__mocks__/my_module.js
634
+ //
635
+ // Where some other module does a relative require into each of the
636
+ // respective subDir{1,2} directories and expects a manual mock
637
+ // corresponding to that particular my_module.js file.
638
+
639
+ const moduleDir = path . dirname ( modulePath ) ;
640
+ const moduleFileName = path . basename ( modulePath ) ;
641
+ const potentialManualMock = path . join (
642
+ moduleDir ,
643
+ '__mocks__' ,
644
+ moduleFileName ,
645
+ ) ;
646
+ if ( fs . existsSync ( potentialManualMock ) ) {
647
+ isManualMock = true ;
648
+ modulePath = potentialManualMock ;
649
+ }
650
+ }
651
+ if ( isManualMock ) {
652
+ const localModule : InitialModule = {
653
+ children : [ ] ,
654
+ exports : { } ,
655
+ filename : modulePath ,
656
+ id : modulePath ,
657
+ loaded : false ,
658
+ path : modulePath ,
659
+ } ;
660
+
661
+ this . _loadModule (
662
+ localModule ,
663
+ from ,
664
+ moduleName ,
665
+ modulePath ,
666
+ undefined ,
667
+ this . _moduleMockRegistry ,
668
+ ) ;
669
+
670
+ this . _moduleMockRegistry . set ( moduleID , localModule . exports ) ;
671
+ } else {
672
+ // Look for a real module to generate an automock from
673
+ this . _moduleMockRegistry . set (
674
+ moduleID ,
675
+ this . _generateMock ( from , moduleName ) ,
676
+ ) ;
677
+ }
678
+
679
+ return this . _moduleMockRegistry . get ( moduleID ) ;
680
+ }
681
+
559
682
requireModule < T = unknown > (
560
683
from : Config . Path ,
561
684
moduleName ?: string ,
562
685
options ?: InternalModuleOptions ,
563
- isRequireActual ?: boolean | null ,
686
+ isRequireActual = false ,
564
687
) : T {
565
688
const moduleID = this . _resolver . getModuleID (
566
689
this . _virtualMocks ,
@@ -597,12 +720,10 @@ export default class Runtime {
597
720
598
721
if ( options ?. isInternalModule ) {
599
722
moduleRegistry = this . _internalModuleRegistry ;
723
+ } else if ( this . _isolatedModuleRegistry ) {
724
+ moduleRegistry = this . _isolatedModuleRegistry ;
600
725
} else {
601
- if ( this . _isolatedModuleRegistry ) {
602
- moduleRegistry = this . _isolatedModuleRegistry ;
603
- } else {
604
- moduleRegistry = this . _moduleRegistry ;
605
- }
726
+ moduleRegistry = this . _moduleRegistry ;
606
727
}
607
728
608
729
const module = moduleRegistry . get ( modulePath ) ;
@@ -663,17 +784,12 @@ export default class Runtime {
663
784
moduleName ,
664
785
) ;
665
786
666
- if (
667
- this . _isolatedMockRegistry &&
668
- this . _isolatedMockRegistry . get ( moduleID )
669
- ) {
670
- return this . _isolatedMockRegistry . get ( moduleID ) ;
671
- } else if ( this . _mockRegistry . get ( moduleID ) ) {
672
- return this . _mockRegistry . get ( moduleID ) ;
673
- }
674
-
675
787
const mockRegistry = this . _isolatedMockRegistry || this . _mockRegistry ;
676
788
789
+ if ( mockRegistry . get ( moduleID ) ) {
790
+ return mockRegistry . get ( moduleID ) ;
791
+ }
792
+
677
793
if ( this . _mockFactories . has ( moduleID ) ) {
678
794
// has check above makes this ok
679
795
const module = this . _mockFactories . get ( moduleID ) ! ( ) ;
@@ -790,7 +906,7 @@ export default class Runtime {
790
906
}
791
907
792
908
try {
793
- if ( this . _shouldMock ( from , moduleName ) ) {
909
+ if ( this . _shouldMock ( from , moduleName , this . _explicitShouldMock ) ) {
794
910
return this . requireMock < T > ( from , moduleName ) ;
795
911
} else {
796
912
return this . requireModule < T > ( from , moduleName ) ;
@@ -845,6 +961,7 @@ export default class Runtime {
845
961
this . _mockRegistry . clear ( ) ;
846
962
this . _moduleRegistry . clear ( ) ;
847
963
this . _esmoduleRegistry . clear ( ) ;
964
+ this . _moduleMockRegistry . clear ( ) ;
848
965
849
966
if ( this . _environment ) {
850
967
if ( this . _environment . global ) {
@@ -933,6 +1050,26 @@ export default class Runtime {
933
1050
this . _mockFactories . set ( moduleID , mockFactory ) ;
934
1051
}
935
1052
1053
+ private setModuleMock (
1054
+ from : string ,
1055
+ moduleName : string ,
1056
+ mockFactory : ( ) => Promise < unknown > | unknown ,
1057
+ options ?: { virtual ?: boolean } ,
1058
+ ) : void {
1059
+ if ( options ?. virtual ) {
1060
+ const mockPath = this . _resolver . getModulePath ( from , moduleName ) ;
1061
+
1062
+ this . _virtualModuleMocks . set ( mockPath , true ) ;
1063
+ }
1064
+ const moduleID = this . _resolver . getModuleID (
1065
+ this . _virtualModuleMocks ,
1066
+ from ,
1067
+ moduleName ,
1068
+ ) ;
1069
+ this . _explicitShouldMockModule . set ( moduleID , true ) ;
1070
+ this . _moduleMockFactories . set ( moduleID , mockFactory ) ;
1071
+ }
1072
+
936
1073
restoreAllMocks ( ) : void {
937
1074
this . _moduleMocker . restoreAllMocks ( ) ;
938
1075
}
@@ -953,12 +1090,15 @@ export default class Runtime {
953
1090
this . _internalModuleRegistry . clear ( ) ;
954
1091
this . _mainModule = null ;
955
1092
this . _mockFactories . clear ( ) ;
1093
+ this . _moduleMockFactories . clear ( ) ;
956
1094
this . _mockMetaDataCache . clear ( ) ;
957
1095
this . _shouldMockModuleCache . clear ( ) ;
958
1096
this . _shouldUnmockTransitiveDependenciesCache . clear ( ) ;
959
1097
this . _explicitShouldMock . clear ( ) ;
1098
+ this . _explicitShouldMockModule . clear ( ) ;
960
1099
this . _transitiveShouldMock . clear ( ) ;
961
1100
this . _virtualMocks . clear ( ) ;
1101
+ this . _virtualModuleMocks . clear ( ) ;
962
1102
this . _cacheFS . clear ( ) ;
963
1103
this . _unmockList = undefined ;
964
1104
@@ -1350,8 +1490,11 @@ export default class Runtime {
1350
1490
) ;
1351
1491
}
1352
1492
1353
- private _shouldMock ( from : Config . Path , moduleName : string ) : boolean {
1354
- const explicitShouldMock = this . _explicitShouldMock ;
1493
+ private _shouldMock (
1494
+ from : Config . Path ,
1495
+ moduleName : string ,
1496
+ explicitShouldMock : Map < string , boolean > ,
1497
+ ) : boolean {
1355
1498
const moduleID = this . _resolver . getModuleID (
1356
1499
this . _virtualMocks ,
1357
1500
from ,
@@ -1519,6 +1662,24 @@ export default class Runtime {
1519
1662
this . setMock ( from , moduleName , mockFactory , options ) ;
1520
1663
return jestObject ;
1521
1664
} ;
1665
+ const mockModule : Jest [ 'mockModule' ] = (
1666
+ moduleName ,
1667
+ mockFactory ,
1668
+ options ,
1669
+ ) => {
1670
+ if ( mockFactory !== undefined ) {
1671
+ this . setModuleMock ( from , moduleName , mockFactory , options ) ;
1672
+ return jestObject ;
1673
+ }
1674
+
1675
+ const moduleID = this . _resolver . getModuleID (
1676
+ this . _virtualMocks ,
1677
+ from ,
1678
+ moduleName ,
1679
+ ) ;
1680
+ this . _explicitShouldMockModule . set ( moduleID , true ) ;
1681
+ return jestObject ;
1682
+ } ;
1522
1683
const clearAllMocks = ( ) => {
1523
1684
this . clearAllMocks ( ) ;
1524
1685
return jestObject ;
@@ -1617,6 +1778,7 @@ export default class Runtime {
1617
1778
isMockFunction : this . _moduleMocker . isMockFunction ,
1618
1779
isolateModules,
1619
1780
mock,
1781
+ mockModule,
1620
1782
requireActual : this . requireActual . bind ( this , from ) ,
1621
1783
requireMock : this . requireMock . bind ( this , from ) ,
1622
1784
resetAllMocks,
0 commit comments