Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for mocks not working with module name mapper #7787

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion e2e/__tests__/moduleNameMapper.test.js
Expand Up @@ -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';

Expand All @@ -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);
});
@@ -0,0 +1,7 @@
const {Track} = require('../../../src/storage/track/Track');
jest.mock('@@storage/track/Track');

test('relative import', () => {
const track = new Track();
expect(track.someRandomFunction).not.toBeCalled();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@grosto, I'm currently experiencing the problem this PR was supposed to solve; my aliased components aren't being mocked.

Reviewing this expected solution, I suspect it's broken again and was not caught because of this test. I believe this expectation here would always be true event if the mock is not successful. In order for this test to be valuable, someRandomFunction should be called if there is no mock, but I don't see any case where it would be called.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dbroytman could you open up a new issue with a reproducion? Or even better, a PR fixing it 🙂

});
@@ -0,0 +1,7 @@
const {Track} = require('@@storage/track/Track');
jest.mock('@@storage/track/Track');

test('through moduleNameMapper', () => {
const track = new Track();
expect(track.someRandomFunction).not.toBeCalled();
});
8 changes: 8 additions & 0 deletions e2e/module-name-mapper-mock/package.json
@@ -0,0 +1,8 @@
{
"jest": {
"moduleNameMapper": {
"^@@storage/(.*)$": "<rootDir>/src/storage/$1"
},
"testEnvironment": "node"
}
}
SimenB marked this conversation as resolved.
Show resolved Hide resolved
8 changes: 8 additions & 0 deletions e2e/module-name-mapper-mock/src/storage/track/Track.js
@@ -0,0 +1,8 @@
module.exports.Track = class Track {
constructor() {
/**/
SimenB marked this conversation as resolved.
Show resolved Hide resolved
}
someRandomFunction() {
return 42;
}
};
4 changes: 2 additions & 2 deletions packages/jest-resolve/src/index.js
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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();
Expand Down
Expand Up @@ -10,6 +10,10 @@

let createRuntime;

const moduleNameMapper = {
'module/name/(.*)': '<rootDir>/mapped_module_$1.js',
};

describe('Runtime', () => {
beforeEach(() => {
createRuntime = require('createRuntime');
Expand All @@ -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',
);
console.log(mockModule);
SimenB marked this conversation as resolved.
Show resolved Hide resolved
expect(mockModule.test.mock).toBeTruthy();
}));
});

it('creates mock objects in the right environment', () =>
Expand Down
@@ -0,0 +1,3 @@
module.exports = {
test: () => '42',
};
63 changes: 35 additions & 28 deletions packages/jest-runtime/src/index.js
Expand Up @@ -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: {},
Expand Down Expand Up @@ -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
Expand Down