diff --git a/CHANGELOG.md b/CHANGELOG.md index e6077184f0f3..3d47f0537aa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixes +- `[jest-runtime]` Fix for mocks not working with module name mapper ([#7787](https://github.com/facebook/jest/pull/7787)) - `[jest-cli]` Break dependency cycle when using Jest programmatically ([#7707](https://github.com/facebook/jest/pull/7707)) - `[jest-config]` Extract setupFilesAfterEnv from preset ([#7724](https://github.com/facebook/jest/pull/7724)) diff --git a/e2e/__tests__/moduleNameMapper.test.js b/e2e/__tests__/moduleNameMapper.test.js index 969b387372ed..e768af3e48bc 100644 --- a/e2e/__tests__/moduleNameMapper.test.js +++ b/e2e/__tests__/moduleNameMapper.test.js @@ -7,7 +7,7 @@ * @flow */ -import runJest from '../runJest'; +import runJest, {json as runWithJson} from '../runJest'; import {extractSummary} from '../Utils'; import {wrap} from 'jest-snapshot-serializer-raw'; @@ -28,3 +28,9 @@ test('moduleNameMapper correct configuration', () => { expect(status).toBe(0); expect(wrap(rest)).toMatchSnapshot(); }); + +test('moduleNameMapper with mocking', () => { + const {json} = runWithJson('module-name-mapper-mock'); + expect(json.numTotalTests).toBe(2); + expect(json.success).toBe(true); +}); diff --git a/e2e/module-name-mapper-mock/__tests__/storage/track/Track.test.js b/e2e/module-name-mapper-mock/__tests__/storage/track/Track.test.js new file mode 100644 index 000000000000..2a1737478788 --- /dev/null +++ b/e2e/module-name-mapper-mock/__tests__/storage/track/Track.test.js @@ -0,0 +1,14 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const {Track} = require('../../../src/storage/track/Track'); +jest.mock('@@storage/track/Track'); + +test('relative import', () => { + const track = new Track(); + expect(track.someRandomFunction).not.toBeCalled(); +}); diff --git a/e2e/module-name-mapper-mock/__tests__/storage/track/TrackExpected.test.js b/e2e/module-name-mapper-mock/__tests__/storage/track/TrackExpected.test.js new file mode 100644 index 000000000000..86224f67fe7d --- /dev/null +++ b/e2e/module-name-mapper-mock/__tests__/storage/track/TrackExpected.test.js @@ -0,0 +1,14 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const {Track} = require('@@storage/track/Track'); +jest.mock('@@storage/track/Track'); + +test('through moduleNameMapper', () => { + const track = new Track(); + expect(track.someRandomFunction).not.toBeCalled(); +}); diff --git a/e2e/module-name-mapper-mock/package.json b/e2e/module-name-mapper-mock/package.json new file mode 100644 index 000000000000..8a7b6db67be1 --- /dev/null +++ b/e2e/module-name-mapper-mock/package.json @@ -0,0 +1,8 @@ +{ + "jest": { + "moduleNameMapper": { + "^@@storage/(.*)$": "/src/storage/$1" + }, + "testEnvironment": "node" + } +} diff --git a/e2e/module-name-mapper-mock/src/storage/track/Track.js b/e2e/module-name-mapper-mock/src/storage/track/Track.js new file mode 100644 index 000000000000..e91d9a9b047b --- /dev/null +++ b/e2e/module-name-mapper-mock/src/storage/track/Track.js @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports.Track = class Track { + someRandomFunction() { + return 42; + } +}; diff --git a/packages/jest-resolve/src/index.js b/packages/jest-resolve/src/index.js index 94784e8c0b51..dd9feb66cea7 100644 --- a/packages/jest-resolve/src/index.js +++ b/packages/jest-resolve/src/index.js @@ -251,7 +251,7 @@ class Resolver { if (mock) { return mock; } else { - const moduleName = this._resolveStubModuleName(from, name); + const moduleName = this.resolveStubModuleName(from, name); if (moduleName) { return this.getModule(moduleName) || moduleName; } @@ -340,7 +340,7 @@ class Resolver { ); } - _resolveStubModuleName(from: Path, moduleName: string): ?Path { + resolveStubModuleName(from: Path, moduleName: string): ?Path { const dirname = path.dirname(from); const paths = this._options.modulePaths; const extensions = this._options.extensions.slice(); diff --git a/packages/jest-runtime/src/__tests__/runtime_gen_mock_from_module.test.js b/packages/jest-runtime/src/__tests__/runtime_gen_mock_from_module.test.js index d76898b32d27..818b87eaec97 100644 --- a/packages/jest-runtime/src/__tests__/runtime_gen_mock_from_module.test.js +++ b/packages/jest-runtime/src/__tests__/runtime_gen_mock_from_module.test.js @@ -10,6 +10,10 @@ let createRuntime; +const moduleNameMapper = { + 'module/name/(.*)': '/mapped_module_$1.js', +}; + describe('Runtime', () => { beforeEach(() => { createRuntime = require('createRuntime'); @@ -35,6 +39,16 @@ describe('Runtime', () => { expect(mock.fn()).toBe(undefined); expect(module.getModuleStateValue()).toBe(origModuleStateValue); })); + + it('resolves mapped modules correctly', () => + createRuntime(__filename, {moduleNameMapper}).then(runtime => { + const root = runtime.requireModule(runtime.__mockRootPath); + const mockModule = root.jest.genMockFromModule( + 'module/name/genMockFromModule', + ); + + expect(mockModule.test.mock).toBeTruthy(); + })); }); it('creates mock objects in the right environment', () => diff --git a/packages/jest-runtime/src/__tests__/test_root/mapped_module_genMockFromModule.js b/packages/jest-runtime/src/__tests__/test_root/mapped_module_genMockFromModule.js new file mode 100644 index 000000000000..9e23e5b26b30 --- /dev/null +++ b/packages/jest-runtime/src/__tests__/test_root/mapped_module_genMockFromModule.js @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = { + test: () => '42', +}; diff --git a/packages/jest-runtime/src/index.js b/packages/jest-runtime/src/index.js index 7c69e5925152..806926f45025 100644 --- a/packages/jest-runtime/src/index.js +++ b/packages/jest-runtime/src/index.js @@ -389,37 +389,43 @@ class Runtime { return (mockRegistry[moduleID] = this._mockFactories[moduleID]()); } - let manualMock = this._resolver.getMockModule(from, moduleName); + const manualMockOrStub = this._resolver.getMockModule(from, moduleName); let modulePath; - if (manualMock) { - modulePath = this._resolveModule(from, manualMock); + if (manualMockOrStub) { + modulePath = this._resolveModule(from, manualMockOrStub); } else { modulePath = this._resolveModule(from, moduleName); } - // 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)) { - manualMock = true; - modulePath = potentialManualMock; - } - if (manualMock) { + 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: Module = { children: [], exports: {}, @@ -741,8 +747,9 @@ class Runtime { } _generateMock(from: Path, moduleName: string) { - const modulePath = this._resolveModule(from, moduleName); - + const modulePath = + this._resolver.resolveStubModuleName(from, moduleName) || + this._resolveModule(from, moduleName); if (!(modulePath in this._mockMetaDataCache)) { // This allows us to handle circular dependencies while generating an // automock