@@ -154,6 +154,7 @@ export default class Runtime {
154
154
private _currentlyExecutingModulePath : string ;
155
155
private readonly _environment : JestEnvironment ;
156
156
private readonly _explicitShouldMock : Map < string , boolean > ;
157
+ private readonly _explicitShouldMockModule : Map < string , boolean > ;
157
158
private _fakeTimersImplementation :
158
159
| LegacyFakeTimers < unknown >
159
160
| ModernFakeTimers
@@ -168,6 +169,8 @@ export default class Runtime {
168
169
> ;
169
170
private _mockRegistry : Map < string , any > ;
170
171
private _isolatedMockRegistry : Map < string , any > | null ;
172
+ private _moduleMockRegistry : Map < string , VMModule > ;
173
+ private readonly _moduleMockFactories : Map < string , ( ) => unknown > ;
171
174
private readonly _moduleMocker : ModuleMocker ;
172
175
private _isolatedModuleRegistry : ModuleRegistry | null ;
173
176
private _moduleRegistry : ModuleRegistry ;
@@ -190,6 +193,7 @@ export default class Runtime {
190
193
private readonly _transitiveShouldMock : Map < string , boolean > ;
191
194
private _unmockList : RegExp | undefined ;
192
195
private readonly _virtualMocks : Map < string , boolean > ;
196
+ private readonly _virtualModuleMocks : Map < string , boolean > ;
193
197
private _moduleImplementation ?: typeof nativeModule . Module ;
194
198
private readonly jestObjectCaches : Map < string , Jest > ;
195
199
private jestGlobals ?: JestGlobals ;
@@ -208,11 +212,14 @@ export default class Runtime {
208
212
this . _currentlyExecutingModulePath = '' ;
209
213
this . _environment = environment ;
210
214
this . _explicitShouldMock = new Map ( ) ;
215
+ this . _explicitShouldMockModule = new Map ( ) ;
211
216
this . _internalModuleRegistry = new Map ( ) ;
212
217
this . _isCurrentlyExecutingManualMock = null ;
213
218
this . _mainModule = null ;
214
219
this . _mockFactories = new Map ( ) ;
215
220
this . _mockRegistry = new Map ( ) ;
221
+ this . _moduleMockRegistry = new Map ( ) ;
222
+ this . _moduleMockFactories = new Map ( ) ;
216
223
invariant (
217
224
this . _environment . moduleMocker ,
218
225
'`moduleMocker` must be set on an environment when created' ,
@@ -231,6 +238,7 @@ export default class Runtime {
231
238
this . _sourceMapRegistry = new Map ( ) ;
232
239
this . _fileTransforms = new Map ( ) ;
233
240
this . _virtualMocks = new Map ( ) ;
241
+ this . _virtualModuleMocks = new Map ( ) ;
234
242
this . jestObjectCaches = new Map ( ) ;
235
243
236
244
this . _mockMetaDataCache = new Map ( ) ;
@@ -452,6 +460,16 @@ export default class Runtime {
452
460
453
461
const [ path , query ] = specifier . split ( '?' ) ;
454
462
463
+ if (
464
+ this . _shouldMock (
465
+ referencingIdentifier ,
466
+ path ,
467
+ this . _explicitShouldMockModule ,
468
+ )
469
+ ) {
470
+ return this . importMock ( referencingIdentifier , path , context ) ;
471
+ }
472
+
455
473
const resolved = this . _resolveModule ( referencingIdentifier , path ) ;
456
474
457
475
if (
@@ -492,6 +510,8 @@ export default class Runtime {
492
510
async unstable_importModule (
493
511
from : Config . Path ,
494
512
moduleName ?: string ,
513
+ // TODO: implement this
514
+ _isImportActual = false ,
495
515
) : Promise < void > {
496
516
invariant (
497
517
runtimeSupportsVmModules ,
@@ -541,6 +561,109 @@ export default class Runtime {
541
561
return evaluateSyntheticModule ( module ) ;
542
562
}
543
563
564
+ private async importMock < T = unknown > (
565
+ from : Config . Path ,
566
+ moduleName : string ,
567
+ context : VMContext ,
568
+ ) : Promise < T > {
569
+ const moduleID = this . _resolver . getModuleID (
570
+ this . _virtualModuleMocks ,
571
+ from ,
572
+ moduleName ,
573
+ ) ;
574
+
575
+ if ( this . _moduleMockRegistry . has ( moduleID ) ) {
576
+ return this . _moduleMockRegistry . get ( moduleID ) ;
577
+ }
578
+
579
+ if ( this . _moduleMockFactories . has ( moduleID ) ) {
580
+ const invokedFactory : any = await this . _moduleMockFactories . get (
581
+ moduleID ,
582
+ // has check above makes this ok
583
+ ) ! ( ) ;
584
+
585
+ const module = new SyntheticModule (
586
+ Object . keys ( invokedFactory ) ,
587
+ function ( ) {
588
+ Object . entries ( invokedFactory ) . forEach ( ( [ key , value ] ) => {
589
+ // @ts -expect-error: TS doesn't know what `this` is
590
+ this . setExport ( key , value ) ;
591
+ } ) ;
592
+ } ,
593
+ // should identifier be `node://${moduleName}`?
594
+ { context, identifier : moduleName } ,
595
+ ) ;
596
+
597
+ this . _moduleMockRegistry . set ( moduleID , module ) ;
598
+
599
+ return evaluateSyntheticModule ( module ) ;
600
+ }
601
+
602
+ const manualMockOrStub = this . _resolver . getMockModule ( from , moduleName ) ;
603
+
604
+ let modulePath =
605
+ this . _resolver . getMockModule ( from , moduleName ) ||
606
+ this . _resolveModule ( from , moduleName ) ;
607
+
608
+ let isManualMock =
609
+ manualMockOrStub &&
610
+ ! this . _resolver . resolveStubModuleName ( from , moduleName ) ;
611
+ if ( ! isManualMock ) {
612
+ // If the actual module file has a __mocks__ dir sitting immediately next
613
+ // to it, look to see if there is a manual mock for this file.
614
+ //
615
+ // subDir1/my_module.js
616
+ // subDir1/__mocks__/my_module.js
617
+ // subDir2/my_module.js
618
+ // subDir2/__mocks__/my_module.js
619
+ //
620
+ // Where some other module does a relative require into each of the
621
+ // respective subDir{1,2} directories and expects a manual mock
622
+ // corresponding to that particular my_module.js file.
623
+
624
+ const moduleDir = path . dirname ( modulePath ) ;
625
+ const moduleFileName = path . basename ( modulePath ) ;
626
+ const potentialManualMock = path . join (
627
+ moduleDir ,
628
+ '__mocks__' ,
629
+ moduleFileName ,
630
+ ) ;
631
+ if ( fs . existsSync ( potentialManualMock ) ) {
632
+ isManualMock = true ;
633
+ modulePath = potentialManualMock ;
634
+ }
635
+ }
636
+ if ( isManualMock ) {
637
+ const localModule : InitialModule = {
638
+ children : [ ] ,
639
+ exports : { } ,
640
+ filename : modulePath ,
641
+ id : modulePath ,
642
+ loaded : false ,
643
+ path : modulePath ,
644
+ } ;
645
+
646
+ this . _loadModule (
647
+ localModule ,
648
+ from ,
649
+ moduleName ,
650
+ modulePath ,
651
+ undefined ,
652
+ this . _moduleMockRegistry ,
653
+ ) ;
654
+
655
+ this . _moduleMockRegistry . set ( moduleID , localModule . exports ) ;
656
+ } else {
657
+ // Look for a real module to generate an automock from
658
+ this . _moduleMockRegistry . set (
659
+ moduleID ,
660
+ this . _generateMock ( from , moduleName ) ,
661
+ ) ;
662
+ }
663
+
664
+ return this . _moduleMockRegistry . get ( moduleID ) ;
665
+ }
666
+
544
667
private getExportsOfCjs ( modulePath : Config . Path ) {
545
668
const cachedNamedExports = this . _cjsNamedExports . get ( modulePath ) ;
546
669
@@ -572,7 +695,7 @@ export default class Runtime {
572
695
from : Config . Path ,
573
696
moduleName ?: string ,
574
697
options ?: InternalModuleOptions ,
575
- isRequireActual ?: boolean | null ,
698
+ isRequireActual = false ,
576
699
) : T {
577
700
const moduleID = this . _resolver . getModuleID (
578
701
this . _virtualMocks ,
@@ -609,12 +732,10 @@ export default class Runtime {
609
732
610
733
if ( options ?. isInternalModule ) {
611
734
moduleRegistry = this . _internalModuleRegistry ;
735
+ } else if ( this . _isolatedModuleRegistry ) {
736
+ moduleRegistry = this . _isolatedModuleRegistry ;
612
737
} else {
613
- if ( this . _isolatedModuleRegistry ) {
614
- moduleRegistry = this . _isolatedModuleRegistry ;
615
- } else {
616
- moduleRegistry = this . _moduleRegistry ;
617
- }
738
+ moduleRegistry = this . _moduleRegistry ;
618
739
}
619
740
620
741
const module = moduleRegistry . get ( modulePath ) ;
@@ -681,17 +802,12 @@ export default class Runtime {
681
802
moduleName ,
682
803
) ;
683
804
684
- if (
685
- this . _isolatedMockRegistry &&
686
- this . _isolatedMockRegistry . get ( moduleID )
687
- ) {
688
- return this . _isolatedMockRegistry . get ( moduleID ) ;
689
- } else if ( this . _mockRegistry . get ( moduleID ) ) {
690
- return this . _mockRegistry . get ( moduleID ) ;
691
- }
692
-
693
805
const mockRegistry = this . _isolatedMockRegistry || this . _mockRegistry ;
694
806
807
+ if ( mockRegistry . get ( moduleID ) ) {
808
+ return mockRegistry . get ( moduleID ) ;
809
+ }
810
+
695
811
if ( this . _mockFactories . has ( moduleID ) ) {
696
812
// has check above makes this ok
697
813
const module = this . _mockFactories . get ( moduleID ) ! ( ) ;
@@ -808,7 +924,7 @@ export default class Runtime {
808
924
}
809
925
810
926
try {
811
- if ( this . _shouldMock ( from , moduleName ) ) {
927
+ if ( this . _shouldMock ( from , moduleName , this . _explicitShouldMock ) ) {
812
928
return this . requireMock < T > ( from , moduleName ) ;
813
929
} else {
814
930
return this . requireModule < T > ( from , moduleName ) ;
@@ -864,6 +980,7 @@ export default class Runtime {
864
980
this . _moduleRegistry . clear ( ) ;
865
981
this . _esmoduleRegistry . clear ( ) ;
866
982
this . _cjsNamedExports . clear ( ) ;
983
+ this . _moduleMockRegistry . clear ( ) ;
867
984
868
985
if ( this . _environment ) {
869
986
if ( this . _environment . global ) {
@@ -952,6 +1069,26 @@ export default class Runtime {
952
1069
this . _mockFactories . set ( moduleID , mockFactory ) ;
953
1070
}
954
1071
1072
+ private setModuleMock (
1073
+ from : string ,
1074
+ moduleName : string ,
1075
+ mockFactory : ( ) => Promise < unknown > | unknown ,
1076
+ options ?: { virtual ?: boolean } ,
1077
+ ) : void {
1078
+ if ( options ?. virtual ) {
1079
+ const mockPath = this . _resolver . getModulePath ( from , moduleName ) ;
1080
+
1081
+ this . _virtualModuleMocks . set ( mockPath , true ) ;
1082
+ }
1083
+ const moduleID = this . _resolver . getModuleID (
1084
+ this . _virtualModuleMocks ,
1085
+ from ,
1086
+ moduleName ,
1087
+ ) ;
1088
+ this . _explicitShouldMockModule . set ( moduleID , true ) ;
1089
+ this . _moduleMockFactories . set ( moduleID , mockFactory ) ;
1090
+ }
1091
+
955
1092
restoreAllMocks ( ) : void {
956
1093
this . _moduleMocker . restoreAllMocks ( ) ;
957
1094
}
@@ -972,12 +1109,15 @@ export default class Runtime {
972
1109
this . _internalModuleRegistry . clear ( ) ;
973
1110
this . _mainModule = null ;
974
1111
this . _mockFactories . clear ( ) ;
1112
+ this . _moduleMockFactories . clear ( ) ;
975
1113
this . _mockMetaDataCache . clear ( ) ;
976
1114
this . _shouldMockModuleCache . clear ( ) ;
977
1115
this . _shouldUnmockTransitiveDependenciesCache . clear ( ) ;
978
1116
this . _explicitShouldMock . clear ( ) ;
1117
+ this . _explicitShouldMockModule . clear ( ) ;
979
1118
this . _transitiveShouldMock . clear ( ) ;
980
1119
this . _virtualMocks . clear ( ) ;
1120
+ this . _virtualModuleMocks . clear ( ) ;
981
1121
this . _cacheFS . clear ( ) ;
982
1122
this . _unmockList = undefined ;
983
1123
@@ -1375,8 +1515,11 @@ export default class Runtime {
1375
1515
) ;
1376
1516
}
1377
1517
1378
- private _shouldMock ( from : Config . Path , moduleName : string ) : boolean {
1379
- const explicitShouldMock = this . _explicitShouldMock ;
1518
+ private _shouldMock (
1519
+ from : Config . Path ,
1520
+ moduleName : string ,
1521
+ explicitShouldMock : Map < string , boolean > ,
1522
+ ) : boolean {
1380
1523
const moduleID = this . _resolver . getModuleID (
1381
1524
this . _virtualMocks ,
1382
1525
from ,
@@ -1544,6 +1687,24 @@ export default class Runtime {
1544
1687
this . setMock ( from , moduleName , mockFactory , options ) ;
1545
1688
return jestObject ;
1546
1689
} ;
1690
+ const mockModule : Jest [ 'mockModule' ] = (
1691
+ moduleName ,
1692
+ mockFactory ,
1693
+ options ,
1694
+ ) => {
1695
+ if ( mockFactory !== undefined ) {
1696
+ this . setModuleMock ( from , moduleName , mockFactory , options ) ;
1697
+ return jestObject ;
1698
+ }
1699
+
1700
+ const moduleID = this . _resolver . getModuleID (
1701
+ this . _virtualMocks ,
1702
+ from ,
1703
+ moduleName ,
1704
+ ) ;
1705
+ this . _explicitShouldMockModule . set ( moduleID , true ) ;
1706
+ return jestObject ;
1707
+ } ;
1547
1708
const clearAllMocks = ( ) => {
1548
1709
this . clearAllMocks ( ) ;
1549
1710
return jestObject ;
@@ -1642,6 +1803,7 @@ export default class Runtime {
1642
1803
isMockFunction : this . _moduleMocker . isMockFunction ,
1643
1804
isolateModules,
1644
1805
mock,
1806
+ mockModule,
1645
1807
requireActual : this . requireActual . bind ( this , from ) ,
1646
1808
requireMock : this . requireMock . bind ( this , from ) ,
1647
1809
resetAllMocks,
0 commit comments