@@ -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 ;
@@ -183,6 +186,7 @@ export default class Runtime {
183
186
private readonly _transitiveShouldMock : Map < string , boolean > ;
184
187
private _unmockList : RegExp | undefined ;
185
188
private readonly _virtualMocks : Map < string , boolean > ;
189
+ private readonly _virtualModuleMocks : Map < string , boolean > ;
186
190
private _moduleImplementation ?: typeof nativeModule . Module ;
187
191
private readonly jestObjectCaches : Map < string , Jest > ;
188
192
private jestGlobals ?: JestGlobals ;
@@ -201,11 +205,14 @@ export default class Runtime {
201
205
this . _currentlyExecutingModulePath = '' ;
202
206
this . _environment = environment ;
203
207
this . _explicitShouldMock = new Map ( ) ;
208
+ this . _explicitShouldMockModule = new Map ( ) ;
204
209
this . _internalModuleRegistry = new Map ( ) ;
205
210
this . _isCurrentlyExecutingManualMock = null ;
206
211
this . _mainModule = null ;
207
212
this . _mockFactories = new Map ( ) ;
208
213
this . _mockRegistry = new Map ( ) ;
214
+ this . _moduleMockRegistry = new Map ( ) ;
215
+ this . _moduleMockFactories = new Map ( ) ;
209
216
invariant (
210
217
this . _environment . moduleMocker ,
211
218
'`moduleMocker` must be set on an environment when created' ,
@@ -223,6 +230,7 @@ export default class Runtime {
223
230
this . _sourceMapRegistry = new Map ( ) ;
224
231
this . _fileTransforms = new Map ( ) ;
225
232
this . _virtualMocks = new Map ( ) ;
233
+ this . _virtualModuleMocks = new Map ( ) ;
226
234
this . jestObjectCaches = new Map ( ) ;
227
235
228
236
this . _mockMetaDataCache = new Map ( ) ;
@@ -490,6 +498,16 @@ export default class Runtime {
490
498
491
499
const [ path , query ] = specifier . split ( '?' ) ;
492
500
501
+ if (
502
+ this . _shouldMock (
503
+ referencingIdentifier ,
504
+ path ,
505
+ this . _explicitShouldMockModule ,
506
+ )
507
+ ) {
508
+ return this . importMock ( referencingIdentifier , path , context ) ;
509
+ }
510
+
493
511
const resolved = this . _resolveModule ( referencingIdentifier , path ) ;
494
512
495
513
if (
@@ -505,6 +523,8 @@ export default class Runtime {
505
523
async unstable_importModule (
506
524
from : Config . Path ,
507
525
moduleName ?: string ,
526
+ // TODO: implement this
527
+ _isImportActual = false ,
508
528
) : Promise < void > {
509
529
invariant (
510
530
runtimeSupportsVmModules ,
@@ -552,6 +572,109 @@ export default class Runtime {
552
572
return evaluateSyntheticModule ( module ) ;
553
573
}
554
574
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
+
555
678
private getExportsOfCjs ( modulePath : Config . Path ) {
556
679
const cachedNamedExports = this . _cjsNamedExports . get ( modulePath ) ;
557
680
@@ -583,7 +706,7 @@ export default class Runtime {
583
706
from : Config . Path ,
584
707
moduleName ?: string ,
585
708
options ?: InternalModuleOptions ,
586
- isRequireActual ?: boolean | null ,
709
+ isRequireActual = false ,
587
710
) : T {
588
711
const moduleID = this . _resolver . getModuleID (
589
712
this . _virtualMocks ,
@@ -620,12 +743,10 @@ export default class Runtime {
620
743
621
744
if ( options ?. isInternalModule ) {
622
745
moduleRegistry = this . _internalModuleRegistry ;
746
+ } else if ( this . _isolatedModuleRegistry ) {
747
+ moduleRegistry = this . _isolatedModuleRegistry ;
623
748
} else {
624
- if ( this . _isolatedModuleRegistry ) {
625
- moduleRegistry = this . _isolatedModuleRegistry ;
626
- } else {
627
- moduleRegistry = this . _moduleRegistry ;
628
- }
749
+ moduleRegistry = this . _moduleRegistry ;
629
750
}
630
751
631
752
const module = moduleRegistry . get ( modulePath ) ;
@@ -686,17 +807,12 @@ export default class Runtime {
686
807
moduleName ,
687
808
) ;
688
809
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
-
698
810
const mockRegistry = this . _isolatedMockRegistry || this . _mockRegistry ;
699
811
812
+ if ( mockRegistry . get ( moduleID ) ) {
813
+ return mockRegistry . get ( moduleID ) ;
814
+ }
815
+
700
816
if ( this . _mockFactories . has ( moduleID ) ) {
701
817
// has check above makes this ok
702
818
const module = this . _mockFactories . get ( moduleID ) ! ( ) ;
@@ -813,7 +929,7 @@ export default class Runtime {
813
929
}
814
930
815
931
try {
816
- if ( this . _shouldMock ( from , moduleName ) ) {
932
+ if ( this . _shouldMock ( from , moduleName , this . _explicitShouldMock ) ) {
817
933
return this . requireMock < T > ( from , moduleName ) ;
818
934
} else {
819
935
return this . requireModule < T > ( from , moduleName ) ;
@@ -869,6 +985,7 @@ export default class Runtime {
869
985
this . _moduleRegistry . clear ( ) ;
870
986
this . _esmoduleRegistry . clear ( ) ;
871
987
this . _cjsNamedExports . clear ( ) ;
988
+ this . _moduleMockRegistry . clear ( ) ;
872
989
873
990
if ( this . _environment ) {
874
991
if ( this . _environment . global ) {
@@ -957,6 +1074,26 @@ export default class Runtime {
957
1074
this . _mockFactories . set ( moduleID , mockFactory ) ;
958
1075
}
959
1076
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
+
960
1097
restoreAllMocks ( ) : void {
961
1098
this . _moduleMocker . restoreAllMocks ( ) ;
962
1099
}
@@ -977,12 +1114,15 @@ export default class Runtime {
977
1114
this . _internalModuleRegistry . clear ( ) ;
978
1115
this . _mainModule = null ;
979
1116
this . _mockFactories . clear ( ) ;
1117
+ this . _moduleMockFactories . clear ( ) ;
980
1118
this . _mockMetaDataCache . clear ( ) ;
981
1119
this . _shouldMockModuleCache . clear ( ) ;
982
1120
this . _shouldUnmockTransitiveDependenciesCache . clear ( ) ;
983
1121
this . _explicitShouldMock . clear ( ) ;
1122
+ this . _explicitShouldMockModule . clear ( ) ;
984
1123
this . _transitiveShouldMock . clear ( ) ;
985
1124
this . _virtualMocks . clear ( ) ;
1125
+ this . _virtualModuleMocks . clear ( ) ;
986
1126
this . _cacheFS . clear ( ) ;
987
1127
this . _unmockList = undefined ;
988
1128
@@ -1374,8 +1514,11 @@ export default class Runtime {
1374
1514
) ;
1375
1515
}
1376
1516
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 {
1379
1522
const moduleID = this . _resolver . getModuleID (
1380
1523
this . _virtualMocks ,
1381
1524
from ,
@@ -1543,6 +1686,24 @@ export default class Runtime {
1543
1686
this . setMock ( from , moduleName , mockFactory , options ) ;
1544
1687
return jestObject ;
1545
1688
} ;
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
+ } ;
1546
1707
const clearAllMocks = ( ) => {
1547
1708
this . clearAllMocks ( ) ;
1548
1709
return jestObject ;
@@ -1641,6 +1802,7 @@ export default class Runtime {
1641
1802
isMockFunction : this . _moduleMocker . isMockFunction ,
1642
1803
isolateModules,
1643
1804
mock,
1805
+ mockModule,
1644
1806
requireActual : this . requireActual . bind ( this , from ) ,
1645
1807
requireMock : this . requireMock . bind ( this , from ) ,
1646
1808
resetAllMocks,
0 commit comments