Skip to content

Commit 176e16f

Browse files
committed
feat(runtime): add jest.mockModule
1 parent e0bc54d commit 176e16f

File tree

3 files changed

+80
-9
lines changed

3 files changed

+80
-9
lines changed

e2e/native-esm/__tests__/native-esm.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ test('require of ESM should throw correct error', () => {
184184
});
185185

186186
test('can mock module', async () => {
187-
jestObject.unstable_mockModule('../mockedModule.mjs', () => ({foo: 'bar'}), {
187+
jestObject.mockModule('../mockedModule.mjs', () => ({foo: 'bar'}), {
188188
virtual: true,
189189
});
190190

packages/jest-environment/src/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,9 @@ export interface Jest {
173173
/**
174174
* Mocks a module with the provided module factory when it is being imported.
175175
*/
176-
unstable_mockModule<T = unknown>(
176+
mockModule<T = unknown>(
177177
moduleName: string,
178-
moduleFactory: () => Promise<T> | T,
178+
moduleFactory?: () => Promise<T> | T,
179179
options?: {virtual?: boolean},
180180
): Jest;
181181
/**

packages/jest-runtime/src/index.ts

+77-6
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,8 @@ export default class Runtime {
716716
async unstable_importModule(
717717
from: string,
718718
moduleName?: string,
719+
// TODO: implement this
720+
_isImportActual = false,
719721
): Promise<void> {
720722
invariant(
721723
runtimeSupportsVmModules,
@@ -793,6 +795,7 @@ export default class Runtime {
793795
this.setExport(key, value);
794796
});
795797
},
798+
// should identifier be `node://${moduleName}`?
796799
{context, identifier: moduleName},
797800
);
798801

@@ -801,7 +804,69 @@ export default class Runtime {
801804
return evaluateSyntheticModule(module);
802805
}
803806

804-
throw new Error('Attempting to import a mock without a factory');
807+
const manualMockOrStub = this._resolver.getMockModule(from, moduleName);
808+
809+
let modulePath =
810+
this._resolver.getMockModule(from, moduleName) ||
811+
this._resolveModule(from, moduleName);
812+
813+
let isManualMock =
814+
manualMockOrStub &&
815+
!this._resolver.resolveStubModuleName(from, moduleName);
816+
if (!isManualMock) {
817+
// If the actual module file has a __mocks__ dir sitting immediately next
818+
// to it, look to see if there is a manual mock for this file.
819+
//
820+
// subDir1/my_module.js
821+
// subDir1/__mocks__/my_module.js
822+
// subDir2/my_module.js
823+
// subDir2/__mocks__/my_module.js
824+
//
825+
// Where some other module does a relative require into each of the
826+
// respective subDir{1,2} directories and expects a manual mock
827+
// corresponding to that particular my_module.js file.
828+
829+
const moduleDir = path.dirname(modulePath);
830+
const moduleFileName = path.basename(modulePath);
831+
const potentialManualMock = path.join(
832+
moduleDir,
833+
'__mocks__',
834+
moduleFileName,
835+
);
836+
if (fs.existsSync(potentialManualMock)) {
837+
isManualMock = true;
838+
modulePath = potentialManualMock;
839+
}
840+
}
841+
if (isManualMock) {
842+
const localModule: InitialModule = {
843+
children: [],
844+
exports: {},
845+
filename: modulePath,
846+
id: modulePath,
847+
loaded: false,
848+
path: modulePath,
849+
};
850+
851+
this._loadModule(
852+
localModule,
853+
from,
854+
moduleName,
855+
modulePath,
856+
undefined,
857+
this._moduleMockRegistry,
858+
);
859+
860+
this._moduleMockRegistry.set(moduleID, localModule.exports);
861+
} else {
862+
// Look for a real module to generate an automock from
863+
this._moduleMockRegistry.set(
864+
moduleID,
865+
this._generateMock(from, moduleName),
866+
);
867+
}
868+
869+
return this._moduleMockRegistry.get(moduleID);
805870
}
806871

807872
private getExportsOfCjs(modulePath: string) {
@@ -2041,16 +2106,22 @@ export default class Runtime {
20412106
this.setMock(from, moduleName, mockFactory, options);
20422107
return jestObject;
20432108
};
2044-
const mockModule: Jest['unstable_mockModule'] = (
2109+
const mockModule: Jest['mockModule'] = (
20452110
moduleName,
20462111
mockFactory,
20472112
options,
20482113
) => {
2049-
if (typeof mockFactory !== 'function') {
2050-
throw new Error('`unstable_mockModule` must be passed a mock factory');
2114+
if (mockFactory !== undefined) {
2115+
this.setModuleMock(from, moduleName, mockFactory, options);
2116+
return jestObject;
20512117
}
20522118

2053-
this.setModuleMock(from, moduleName, mockFactory, options);
2119+
const moduleID = this._resolver.getModuleID(
2120+
this._virtualMocks,
2121+
from,
2122+
moduleName,
2123+
);
2124+
this._explicitShouldMockModule.set(moduleID, true);
20542125
return jestObject;
20552126
};
20562127
const clearAllMocks = () => {
@@ -2161,6 +2232,7 @@ export default class Runtime {
21612232
isolateModules,
21622233
mock,
21632234
mocked,
2235+
mockModule,
21642236
requireActual: this.requireActual.bind(this, from),
21652237
requireMock: this.requireMock.bind(this, from),
21662238
resetAllMocks,
@@ -2197,7 +2269,6 @@ export default class Runtime {
21972269
setTimeout,
21982270
spyOn,
21992271
unmock,
2200-
unstable_mockModule: mockModule,
22012272
useFakeTimers,
22022273
useRealTimers,
22032274
};

0 commit comments

Comments
 (0)