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

Implement module.parent #4614

Merged
merged 11 commits into from
Oct 7, 2017
2 changes: 2 additions & 0 deletions packages/jest-cli/src/__tests__/search_source.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,12 +373,14 @@ describe('SearchSource', () => {

it('finds tests that depend directly on the path', () => {
const filePath = path.join(rootDir, 'RegularModule.js');
const file2Path = path.join(rootDir, 'RequireRegularModule.js');
const loggingDep = path.join(rootDir, 'logging.js');
const parentDep = path.join(rootDir, 'ModuleWithSideEffects.js');
const data = searchSource.findRelatedTests(new Set([filePath]));
expect(toPaths(data.tests).sort()).toEqual([
parentDep,
filePath,
file2Path,
loggingDep,
rootPath,
]);
Expand Down
47 changes: 41 additions & 6 deletions packages/jest-runtime/src/__tests__/runtime_require_module.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

'use strict';

const path = require('path');
import path from 'path';
import slash from 'slash';

let createRuntime;

Expand All @@ -27,16 +28,50 @@ describe('Runtime requireModule', () => {
}));

it('provides `module.parent` to modules', () =>
createRuntime(__filename).then(runtime => {
const exports = runtime.requireModule(
runtime.__mockRootPath,
'RequireRegularModule',
);
expect(Object.keys(exports.parent)).toEqual([
'exports',
'filename',
'id',
'children',
'parent',
'paths',
'require',
]);
}));

it('`module.parent` should be undefined for entrypoints', () =>
createRuntime(__filename).then(runtime => {
const exports = runtime.requireModule(
runtime.__mockRootPath,
'RegularModule',
);
expect(exports.parent).toEqual({
exports: {},
filename: 'mock.js',
id: 'mockParent',
});
expect(exports.parent).toBeNull();
}));

it('resolve module.parent.require correctly', () =>
createRuntime(__filename).then(runtime => {
const exports = runtime.requireModule(
runtime.__mockRootPath,
'inner_parent_module',
);
expect(exports.outputString).toEqual('This should happen');
}));

it('resolve module.parent.filename correctly', () =>
createRuntime(__filename).then(runtime => {
const exports = runtime.requireModule(
runtime.__mockRootPath,
'inner_parent_module',
);

expect(slash(exports.parentFileName.replace(__dirname, ''))).toEqual(
'/test_root/inner_parent_module.js',
);
}));

it('provides `module.filename` to modules', () =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. 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.
*
* @providesModule RequireRegularModule
*/

'use strict';

module.exports.parent = require('RegularModule').parent;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. 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.
*
* @providesModule inner_parent_module
*/

'use strict';

const impl = require('module-needing-parent');

module.exports = impl;

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 25 additions & 12 deletions packages/jest-runtime/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ type CoverageOptions = {
mapCoverage: boolean,
};

type ModuleRegistry = {[key: string]: Module};

type BooleanObject = {[key: string]: boolean};
type CacheFS = {[path: Path]: string};

Expand All @@ -75,12 +77,6 @@ const getModuleNameMapper = (config: ProjectConfig) => {
return null;
};

const mockParentModule = {
exports: {},
filename: 'mock.js',
id: 'mockParent',
};

const unmockRegExpCache = new WeakMap();

class Runtime {
Expand All @@ -92,13 +88,13 @@ class Runtime {
_currentlyExecutingModulePath: string;
_environment: Environment;
_explicitShouldMock: BooleanObject;
_internalModuleRegistry: {[key: string]: Module};
_internalModuleRegistry: ModuleRegistry;
_isCurrentlyExecutingManualMock: ?string;
_mockFactories: {[key: string]: () => any};
_mockMetaDataCache: {[key: string]: MockFunctionMetadata};
_mockRegistry: {[key: string]: any};
_moduleMocker: ModuleMocker;
_moduleRegistry: {[key: string]: Module};
_moduleRegistry: ModuleRegistry;
_resolver: Resolver;
_shouldAutoMock: boolean;
_shouldMockModuleCache: BooleanObject;
Expand Down Expand Up @@ -329,7 +325,7 @@ class Runtime {
// $FlowFixMe
localModule.exports = require(modulePath);
} else {
this._execModule(localModule, options);
this._execModule(localModule, options, moduleRegistry, from);
}
}
return moduleRegistry[modulePath].exports;
Expand Down Expand Up @@ -390,7 +386,7 @@ class Runtime {
filename: modulePath,
id: modulePath,
};
this._execModule(localModule);
this._execModule(localModule, undefined, this._mockRegistry, from);
this._mockRegistry[moduleID] = localModule.exports;
} else {
// Look for a real module to generate an automock from
Expand Down Expand Up @@ -478,7 +474,12 @@ class Runtime {
return to ? this._resolver.resolveModule(from, to) : from;
}

_execModule(localModule: Module, options: ?InternalModuleOptions) {
_execModule(
localModule: Module,
options: ?InternalModuleOptions,
moduleRegistry: ModuleRegistry,
from: Path,
) {
// If the environment was disposed, prevent this module from being executed.
if (!this._environment.global) {
return;
Expand All @@ -493,7 +494,19 @@ class Runtime {

const dirname = path.dirname(filename);
localModule.children = [];
localModule.parent = mockParentModule;

Object.defineProperty(
localModule,
'parent',
// https://github.com/facebook/flow/issues/285#issuecomment-270810619
({
enumerable: true,
get() {
return moduleRegistry[from] || null;
},
}: Object),
);

localModule.paths = this._resolver.getModulePaths(dirname);
localModule.require = this._createRequireImplementation(filename, options);

Expand Down