@@ -155,6 +155,7 @@ export default class Runtime {
155
155
private _currentlyExecutingModulePath : string ;
156
156
private readonly _environment : JestEnvironment ;
157
157
private readonly _explicitShouldMock : Map < string , boolean > ;
158
+ private readonly _explicitShouldMockModule : Map < string , boolean > ;
158
159
private _fakeTimersImplementation :
159
160
| LegacyFakeTimers < unknown >
160
161
| ModernFakeTimers
@@ -169,6 +170,8 @@ export default class Runtime {
169
170
> ;
170
171
private _mockRegistry : Map < string , any > ;
171
172
private _isolatedMockRegistry : Map < string , any > | null ;
173
+ private _moduleMockRegistry : Map < string , VMModule > ;
174
+ private readonly _moduleMockFactories : Map < string , ( ) => unknown > ;
172
175
private readonly _moduleMocker : ModuleMocker ;
173
176
private _isolatedModuleRegistry : ModuleRegistry | null ;
174
177
private _moduleRegistry : ModuleRegistry ;
@@ -191,6 +194,7 @@ export default class Runtime {
191
194
private readonly _transitiveShouldMock : Map < string , boolean > ;
192
195
private _unmockList : RegExp | undefined ;
193
196
private readonly _virtualMocks : Map < string , boolean > ;
197
+ private readonly _virtualModuleMocks : Map < string , boolean > ;
194
198
private _moduleImplementation ?: typeof nativeModule . Module ;
195
199
private readonly jestObjectCaches : Map < string , Jest > ;
196
200
private jestGlobals ?: JestGlobals ;
@@ -210,11 +214,14 @@ export default class Runtime {
210
214
this . _currentlyExecutingModulePath = '' ;
211
215
this . _environment = environment ;
212
216
this . _explicitShouldMock = new Map ( ) ;
217
+ this . _explicitShouldMockModule = new Map ( ) ;
213
218
this . _internalModuleRegistry = new Map ( ) ;
214
219
this . _isCurrentlyExecutingManualMock = null ;
215
220
this . _mainModule = null ;
216
221
this . _mockFactories = new Map ( ) ;
217
222
this . _mockRegistry = new Map ( ) ;
223
+ this . _moduleMockRegistry = new Map ( ) ;
224
+ this . _moduleMockFactories = new Map ( ) ;
218
225
invariant (
219
226
this . _environment . moduleMocker ,
220
227
'`moduleMocker` must be set on an environment when created' ,
@@ -233,6 +240,7 @@ export default class Runtime {
233
240
this . _sourceMapRegistry = new Map ( ) ;
234
241
this . _fileTransforms = new Map ( ) ;
235
242
this . _virtualMocks = new Map ( ) ;
243
+ this . _virtualModuleMocks = new Map ( ) ;
236
244
this . jestObjectCaches = new Map ( ) ;
237
245
238
246
this . _mockMetaDataCache = new Map ( ) ;
@@ -458,6 +466,16 @@ export default class Runtime {
458
466
459
467
const [ path , query ] = specifier . split ( '?' ) ;
460
468
469
+ if (
470
+ this . _shouldMock (
471
+ referencingIdentifier ,
472
+ path ,
473
+ this . _explicitShouldMockModule ,
474
+ )
475
+ ) {
476
+ return this . importMock ( referencingIdentifier , path , context ) ;
477
+ }
478
+
461
479
const resolved = this . _resolveModule ( referencingIdentifier , path ) ;
462
480
463
481
if (
@@ -498,6 +516,8 @@ export default class Runtime {
498
516
async unstable_importModule (
499
517
from : Config . Path ,
500
518
moduleName ?: string ,
519
+ // TODO: implement this
520
+ _isImportActual = false ,
501
521
) : Promise < void > {
502
522
invariant (
503
523
runtimeSupportsVmModules ,
@@ -547,6 +567,109 @@ export default class Runtime {
547
567
return evaluateSyntheticModule ( module ) ;
548
568
}
549
569
570
+ private async importMock < T = unknown > (
571
+ from : Config . Path ,
572
+ moduleName : string ,
573
+ context : VMContext ,
574
+ ) : Promise < T > {
575
+ const moduleID = this . _resolver . getModuleID (
576
+ this . _virtualModuleMocks ,
577
+ from ,
578
+ moduleName ,
579
+ ) ;
580
+
581
+ if ( this . _moduleMockRegistry . has ( moduleID ) ) {
582
+ return this . _moduleMockRegistry . get ( moduleID ) ;
583
+ }
584
+
585
+ if ( this . _moduleMockFactories . has ( moduleID ) ) {
586
+ const invokedFactory : any = await this . _moduleMockFactories . get (
587
+ moduleID ,
588
+ // has check above makes this ok
589
+ ) ! ( ) ;
590
+
591
+ const module = new SyntheticModule (
592
+ Object . keys ( invokedFactory ) ,
593
+ function ( ) {
594
+ Object . entries ( invokedFactory ) . forEach ( ( [ key , value ] ) => {
595
+ // @ts -expect-error: TS doesn't know what `this` is
596
+ this . setExport ( key , value ) ;
597
+ } ) ;
598
+ } ,
599
+ // should identifier be `node://${moduleName}`?
600
+ { context, identifier : moduleName } ,
601
+ ) ;
602
+
603
+ this . _moduleMockRegistry . set ( moduleID , module ) ;
604
+
605
+ return evaluateSyntheticModule ( module ) ;
606
+ }
607
+
608
+ const manualMockOrStub = this . _resolver . getMockModule ( from , moduleName ) ;
609
+
610
+ let modulePath =
611
+ this . _resolver . getMockModule ( from , moduleName ) ||
612
+ this . _resolveModule ( from , moduleName ) ;
613
+
614
+ let isManualMock =
615
+ manualMockOrStub &&
616
+ ! this . _resolver . resolveStubModuleName ( from , moduleName ) ;
617
+ if ( ! isManualMock ) {
618
+ // If the actual module file has a __mocks__ dir sitting immediately next
619
+ // to it, look to see if there is a manual mock for this file.
620
+ //
621
+ // subDir1/my_module.js
622
+ // subDir1/__mocks__/my_module.js
623
+ // subDir2/my_module.js
624
+ // subDir2/__mocks__/my_module.js
625
+ //
626
+ // Where some other module does a relative require into each of the
627
+ // respective subDir{1,2} directories and expects a manual mock
628
+ // corresponding to that particular my_module.js file.
629
+
630
+ const moduleDir = path . dirname ( modulePath ) ;
631
+ const moduleFileName = path . basename ( modulePath ) ;
632
+ const potentialManualMock = path . join (
633
+ moduleDir ,
634
+ '__mocks__' ,
635
+ moduleFileName ,
636
+ ) ;
637
+ if ( fs . existsSync ( potentialManualMock ) ) {
638
+ isManualMock = true ;
639
+ modulePath = potentialManualMock ;
640
+ }
641
+ }
642
+ if ( isManualMock ) {
643
+ const localModule : InitialModule = {
644
+ children : [ ] ,
645
+ exports : { } ,
646
+ filename : modulePath ,
647
+ id : modulePath ,
648
+ loaded : false ,
649
+ path : modulePath ,
650
+ } ;
651
+
652
+ this . _loadModule (
653
+ localModule ,
654
+ from ,
655
+ moduleName ,
656
+ modulePath ,
657
+ undefined ,
658
+ this . _moduleMockRegistry ,
659
+ ) ;
660
+
661
+ this . _moduleMockRegistry . set ( moduleID , localModule . exports ) ;
662
+ } else {
663
+ // Look for a real module to generate an automock from
664
+ this . _moduleMockRegistry . set (
665
+ moduleID ,
666
+ this . _generateMock ( from , moduleName ) ,
667
+ ) ;
668
+ }
669
+
670
+ return this . _moduleMockRegistry . get ( moduleID ) ;
671
+ }
672
+
550
673
private getExportsOfCjs ( modulePath : Config . Path ) {
551
674
const cachedNamedExports = this . _cjsNamedExports . get ( modulePath ) ;
552
675
@@ -578,7 +701,7 @@ export default class Runtime {
578
701
from : Config . Path ,
579
702
moduleName ?: string ,
580
703
options ?: InternalModuleOptions ,
581
- isRequireActual ?: boolean | null ,
704
+ isRequireActual = false ,
582
705
) : T {
583
706
const moduleID = this . _resolver . getModuleID (
584
707
this . _virtualMocks ,
@@ -615,12 +738,10 @@ export default class Runtime {
615
738
616
739
if ( options ?. isInternalModule ) {
617
740
moduleRegistry = this . _internalModuleRegistry ;
741
+ } else if ( this . _isolatedModuleRegistry ) {
742
+ moduleRegistry = this . _isolatedModuleRegistry ;
618
743
} else {
619
- if ( this . _isolatedModuleRegistry ) {
620
- moduleRegistry = this . _isolatedModuleRegistry ;
621
- } else {
622
- moduleRegistry = this . _moduleRegistry ;
623
- }
744
+ moduleRegistry = this . _moduleRegistry ;
624
745
}
625
746
626
747
const module = moduleRegistry . get ( modulePath ) ;
@@ -687,17 +808,12 @@ export default class Runtime {
687
808
moduleName ,
688
809
) ;
689
810
690
- if (
691
- this . _isolatedMockRegistry &&
692
- this . _isolatedMockRegistry . get ( moduleID )
693
- ) {
694
- return this . _isolatedMockRegistry . get ( moduleID ) ;
695
- } else if ( this . _mockRegistry . get ( moduleID ) ) {
696
- return this . _mockRegistry . get ( moduleID ) ;
697
- }
698
-
699
811
const mockRegistry = this . _isolatedMockRegistry || this . _mockRegistry ;
700
812
813
+ if ( mockRegistry . get ( moduleID ) ) {
814
+ return mockRegistry . get ( moduleID ) ;
815
+ }
816
+
701
817
if ( this . _mockFactories . has ( moduleID ) ) {
702
818
// has check above makes this ok
703
819
const module = this . _mockFactories . get ( moduleID ) ! ( ) ;
@@ -814,7 +930,7 @@ export default class Runtime {
814
930
}
815
931
816
932
try {
817
- if ( this . _shouldMock ( from , moduleName ) ) {
933
+ if ( this . _shouldMock ( from , moduleName , this . _explicitShouldMock ) ) {
818
934
return this . requireMock < T > ( from , moduleName ) ;
819
935
} else {
820
936
return this . requireModule < T > ( from , moduleName ) ;
@@ -870,6 +986,7 @@ export default class Runtime {
870
986
this . _moduleRegistry . clear ( ) ;
871
987
this . _esmoduleRegistry . clear ( ) ;
872
988
this . _cjsNamedExports . clear ( ) ;
989
+ this . _moduleMockRegistry . clear ( ) ;
873
990
874
991
if ( this . _environment ) {
875
992
if ( this . _environment . global ) {
@@ -958,6 +1075,26 @@ export default class Runtime {
958
1075
this . _mockFactories . set ( moduleID , mockFactory ) ;
959
1076
}
960
1077
1078
+ private setModuleMock (
1079
+ from : string ,
1080
+ moduleName : string ,
1081
+ mockFactory : ( ) => Promise < unknown > | unknown ,
1082
+ options ?: { virtual ?: boolean } ,
1083
+ ) : void {
1084
+ if ( options ?. virtual ) {
1085
+ const mockPath = this . _resolver . getModulePath ( from , moduleName ) ;
1086
+
1087
+ this . _virtualModuleMocks . set ( mockPath , true ) ;
1088
+ }
1089
+ const moduleID = this . _resolver . getModuleID (
1090
+ this . _virtualModuleMocks ,
1091
+ from ,
1092
+ moduleName ,
1093
+ ) ;
1094
+ this . _explicitShouldMockModule . set ( moduleID , true ) ;
1095
+ this . _moduleMockFactories . set ( moduleID , mockFactory ) ;
1096
+ }
1097
+
961
1098
restoreAllMocks ( ) : void {
962
1099
this . _moduleMocker . restoreAllMocks ( ) ;
963
1100
}
@@ -978,12 +1115,15 @@ export default class Runtime {
978
1115
this . _internalModuleRegistry . clear ( ) ;
979
1116
this . _mainModule = null ;
980
1117
this . _mockFactories . clear ( ) ;
1118
+ this . _moduleMockFactories . clear ( ) ;
981
1119
this . _mockMetaDataCache . clear ( ) ;
982
1120
this . _shouldMockModuleCache . clear ( ) ;
983
1121
this . _shouldUnmockTransitiveDependenciesCache . clear ( ) ;
984
1122
this . _explicitShouldMock . clear ( ) ;
1123
+ this . _explicitShouldMockModule . clear ( ) ;
985
1124
this . _transitiveShouldMock . clear ( ) ;
986
1125
this . _virtualMocks . clear ( ) ;
1126
+ this . _virtualModuleMocks . clear ( ) ;
987
1127
this . _cacheFS . clear ( ) ;
988
1128
this . _unmockList = undefined ;
989
1129
@@ -1424,8 +1564,11 @@ export default class Runtime {
1424
1564
) ;
1425
1565
}
1426
1566
1427
- private _shouldMock ( from : Config . Path , moduleName : string ) : boolean {
1428
- const explicitShouldMock = this . _explicitShouldMock ;
1567
+ private _shouldMock (
1568
+ from : Config . Path ,
1569
+ moduleName : string ,
1570
+ explicitShouldMock : Map < string , boolean > ,
1571
+ ) : boolean {
1429
1572
const moduleID = this . _resolver . getModuleID (
1430
1573
this . _virtualMocks ,
1431
1574
from ,
@@ -1593,6 +1736,24 @@ export default class Runtime {
1593
1736
this . setMock ( from , moduleName , mockFactory , options ) ;
1594
1737
return jestObject ;
1595
1738
} ;
1739
+ const mockModule : Jest [ 'mockModule' ] = (
1740
+ moduleName ,
1741
+ mockFactory ,
1742
+ options ,
1743
+ ) => {
1744
+ if ( mockFactory !== undefined ) {
1745
+ this . setModuleMock ( from , moduleName , mockFactory , options ) ;
1746
+ return jestObject ;
1747
+ }
1748
+
1749
+ const moduleID = this . _resolver . getModuleID (
1750
+ this . _virtualMocks ,
1751
+ from ,
1752
+ moduleName ,
1753
+ ) ;
1754
+ this . _explicitShouldMockModule . set ( moduleID , true ) ;
1755
+ return jestObject ;
1756
+ } ;
1596
1757
const clearAllMocks = ( ) => {
1597
1758
this . clearAllMocks ( ) ;
1598
1759
return jestObject ;
@@ -1691,6 +1852,7 @@ export default class Runtime {
1691
1852
isMockFunction : this . _moduleMocker . isMockFunction ,
1692
1853
isolateModules,
1693
1854
mock,
1855
+ mockModule,
1694
1856
requireActual : this . requireActual . bind ( this , from ) ,
1695
1857
requireMock : this . requireMock . bind ( this , from ) ,
1696
1858
resetAllMocks,
0 commit comments