Skip to content

Commit

Permalink
Implement module.parent (#4614)
Browse files Browse the repository at this point in the history
* Implement module.parent.require

* Implement module.parent.filename

* Implement module.parent.id

* Try to set parent to actual parent module

* Fix tests

* styel nit to minimize diff

* Fix failing test

* Make module.parent a lazy getter

* make `parent` enumerable

* Share type definition for ModuleRegistry

* module.parent should be null not undefined for entrypoints
  • Loading branch information
SimenB authored and cpojer committed Oct 7, 2017
1 parent 6646c3b commit 7b0e108
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 18 deletions.
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

0 comments on commit 7b0e108

Please sign in to comment.