From 176e16f6412fac6cc2fdd1d71b18ae9ffd9b4245 Mon Sep 17 00:00:00 2001
From: Simen Bekkhus <sbekkhus91@gmail.com>
Date: Wed, 23 Dec 2020 16:20:17 +0100
Subject: [PATCH 1/2] feat(runtime): add jest.mockModule

---
 e2e/native-esm/__tests__/native-esm.test.js |  2 +-
 packages/jest-environment/src/index.ts      |  4 +-
 packages/jest-runtime/src/index.ts          | 83 +++++++++++++++++++--
 3 files changed, 80 insertions(+), 9 deletions(-)

diff --git a/e2e/native-esm/__tests__/native-esm.test.js b/e2e/native-esm/__tests__/native-esm.test.js
index 494d67e62146..0d2536a10fa0 100644
--- a/e2e/native-esm/__tests__/native-esm.test.js
+++ b/e2e/native-esm/__tests__/native-esm.test.js
@@ -184,7 +184,7 @@ test('require of ESM should throw correct error', () => {
 });
 
 test('can mock module', async () => {
-  jestObject.unstable_mockModule('../mockedModule.mjs', () => ({foo: 'bar'}), {
+  jestObject.mockModule('../mockedModule.mjs', () => ({foo: 'bar'}), {
     virtual: true,
   });
 
diff --git a/packages/jest-environment/src/index.ts b/packages/jest-environment/src/index.ts
index 7a6e1cc8f7ef..0f0bd6500075 100644
--- a/packages/jest-environment/src/index.ts
+++ b/packages/jest-environment/src/index.ts
@@ -173,9 +173,9 @@ export interface Jest {
   /**
    * Mocks a module with the provided module factory when it is being imported.
    */
-  unstable_mockModule<T = unknown>(
+  mockModule<T = unknown>(
     moduleName: string,
-    moduleFactory: () => Promise<T> | T,
+    moduleFactory?: () => Promise<T> | T,
     options?: {virtual?: boolean},
   ): Jest;
   /**
diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts
index 1dc2c9a56322..99e19a5d1604 100644
--- a/packages/jest-runtime/src/index.ts
+++ b/packages/jest-runtime/src/index.ts
@@ -716,6 +716,8 @@ export default class Runtime {
   async unstable_importModule(
     from: string,
     moduleName?: string,
+    // TODO: implement this
+    _isImportActual = false,
   ): Promise<void> {
     invariant(
       runtimeSupportsVmModules,
@@ -793,6 +795,7 @@ export default class Runtime {
             this.setExport(key, value);
           });
         },
+        // should identifier be `node://${moduleName}`?
         {context, identifier: moduleName},
       );
 
@@ -801,7 +804,69 @@ export default class Runtime {
       return evaluateSyntheticModule(module);
     }
 
-    throw new Error('Attempting to import a mock without a factory');
+    const manualMockOrStub = this._resolver.getMockModule(from, moduleName);
+
+    let modulePath =
+      this._resolver.getMockModule(from, moduleName) ||
+      this._resolveModule(from, moduleName);
+
+    let isManualMock =
+      manualMockOrStub &&
+      !this._resolver.resolveStubModuleName(from, moduleName);
+    if (!isManualMock) {
+      // If the actual module file has a __mocks__ dir sitting immediately next
+      // to it, look to see if there is a manual mock for this file.
+      //
+      // subDir1/my_module.js
+      // subDir1/__mocks__/my_module.js
+      // subDir2/my_module.js
+      // subDir2/__mocks__/my_module.js
+      //
+      // Where some other module does a relative require into each of the
+      // respective subDir{1,2} directories and expects a manual mock
+      // corresponding to that particular my_module.js file.
+
+      const moduleDir = path.dirname(modulePath);
+      const moduleFileName = path.basename(modulePath);
+      const potentialManualMock = path.join(
+        moduleDir,
+        '__mocks__',
+        moduleFileName,
+      );
+      if (fs.existsSync(potentialManualMock)) {
+        isManualMock = true;
+        modulePath = potentialManualMock;
+      }
+    }
+    if (isManualMock) {
+      const localModule: InitialModule = {
+        children: [],
+        exports: {},
+        filename: modulePath,
+        id: modulePath,
+        loaded: false,
+        path: modulePath,
+      };
+
+      this._loadModule(
+        localModule,
+        from,
+        moduleName,
+        modulePath,
+        undefined,
+        this._moduleMockRegistry,
+      );
+
+      this._moduleMockRegistry.set(moduleID, localModule.exports);
+    } else {
+      // Look for a real module to generate an automock from
+      this._moduleMockRegistry.set(
+        moduleID,
+        this._generateMock(from, moduleName),
+      );
+    }
+
+    return this._moduleMockRegistry.get(moduleID);
   }
 
   private getExportsOfCjs(modulePath: string) {
@@ -2041,16 +2106,22 @@ export default class Runtime {
       this.setMock(from, moduleName, mockFactory, options);
       return jestObject;
     };
-    const mockModule: Jest['unstable_mockModule'] = (
+    const mockModule: Jest['mockModule'] = (
       moduleName,
       mockFactory,
       options,
     ) => {
-      if (typeof mockFactory !== 'function') {
-        throw new Error('`unstable_mockModule` must be passed a mock factory');
+      if (mockFactory !== undefined) {
+        this.setModuleMock(from, moduleName, mockFactory, options);
+        return jestObject;
       }
 
-      this.setModuleMock(from, moduleName, mockFactory, options);
+      const moduleID = this._resolver.getModuleID(
+        this._virtualMocks,
+        from,
+        moduleName,
+      );
+      this._explicitShouldMockModule.set(moduleID, true);
       return jestObject;
     };
     const clearAllMocks = () => {
@@ -2161,6 +2232,7 @@ export default class Runtime {
       isolateModules,
       mock,
       mocked,
+      mockModule,
       requireActual: this.requireActual.bind(this, from),
       requireMock: this.requireMock.bind(this, from),
       resetAllMocks,
@@ -2197,7 +2269,6 @@ export default class Runtime {
       setTimeout,
       spyOn,
       unmock,
-      unstable_mockModule: mockModule,
       useFakeTimers,
       useRealTimers,
     };

From 3895c42fb392d887904247270aa86f6f88b09ec4 Mon Sep 17 00:00:00 2001
From: Simen Bekkhus <sbekkhus91@gmail.com>
Date: Wed, 23 Dec 2020 16:46:01 +0100
Subject: [PATCH 2/2] remove dead code

---
 packages/jest-runtime/src/index.ts | 65 +-----------------------------
 1 file changed, 1 insertion(+), 64 deletions(-)

diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts
index 99e19a5d1604..dc0929c926d1 100644
--- a/packages/jest-runtime/src/index.ts
+++ b/packages/jest-runtime/src/index.ts
@@ -795,7 +795,6 @@ export default class Runtime {
             this.setExport(key, value);
           });
         },
-        // should identifier be `node://${moduleName}`?
         {context, identifier: moduleName},
       );
 
@@ -804,69 +803,7 @@ export default class Runtime {
       return evaluateSyntheticModule(module);
     }
 
-    const manualMockOrStub = this._resolver.getMockModule(from, moduleName);
-
-    let modulePath =
-      this._resolver.getMockModule(from, moduleName) ||
-      this._resolveModule(from, moduleName);
-
-    let isManualMock =
-      manualMockOrStub &&
-      !this._resolver.resolveStubModuleName(from, moduleName);
-    if (!isManualMock) {
-      // If the actual module file has a __mocks__ dir sitting immediately next
-      // to it, look to see if there is a manual mock for this file.
-      //
-      // subDir1/my_module.js
-      // subDir1/__mocks__/my_module.js
-      // subDir2/my_module.js
-      // subDir2/__mocks__/my_module.js
-      //
-      // Where some other module does a relative require into each of the
-      // respective subDir{1,2} directories and expects a manual mock
-      // corresponding to that particular my_module.js file.
-
-      const moduleDir = path.dirname(modulePath);
-      const moduleFileName = path.basename(modulePath);
-      const potentialManualMock = path.join(
-        moduleDir,
-        '__mocks__',
-        moduleFileName,
-      );
-      if (fs.existsSync(potentialManualMock)) {
-        isManualMock = true;
-        modulePath = potentialManualMock;
-      }
-    }
-    if (isManualMock) {
-      const localModule: InitialModule = {
-        children: [],
-        exports: {},
-        filename: modulePath,
-        id: modulePath,
-        loaded: false,
-        path: modulePath,
-      };
-
-      this._loadModule(
-        localModule,
-        from,
-        moduleName,
-        modulePath,
-        undefined,
-        this._moduleMockRegistry,
-      );
-
-      this._moduleMockRegistry.set(moduleID, localModule.exports);
-    } else {
-      // Look for a real module to generate an automock from
-      this._moduleMockRegistry.set(
-        moduleID,
-        this._generateMock(from, moduleName),
-      );
-    }
-
-    return this._moduleMockRegistry.get(moduleID);
+    throw new Error('Attempting to import a mock without a factory');
   }
 
   private getExportsOfCjs(modulePath: string) {