Skip to content

Commit 7b0e108

Browse files
SimenBcpojer
authored andcommitted
Implement module.parent (#4614)
* 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
1 parent 6646c3b commit 7b0e108

8 files changed

Lines changed: 127 additions & 18 deletions

File tree

packages/jest-cli/src/__tests__/search_source.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,12 +373,14 @@ describe('SearchSource', () => {
373373

374374
it('finds tests that depend directly on the path', () => {
375375
const filePath = path.join(rootDir, 'RegularModule.js');
376+
const file2Path = path.join(rootDir, 'RequireRegularModule.js');
376377
const loggingDep = path.join(rootDir, 'logging.js');
377378
const parentDep = path.join(rootDir, 'ModuleWithSideEffects.js');
378379
const data = searchSource.findRelatedTests(new Set([filePath]));
379380
expect(toPaths(data.tests).sort()).toEqual([
380381
parentDep,
381382
filePath,
383+
file2Path,
382384
loggingDep,
383385
rootPath,
384386
]);

packages/jest-runtime/src/__tests__/runtime_require_module.test.js

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88

99
'use strict';
1010

11-
const path = require('path');
11+
import path from 'path';
12+
import slash from 'slash';
1213

1314
let createRuntime;
1415

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

2930
it('provides `module.parent` to modules', () =>
31+
createRuntime(__filename).then(runtime => {
32+
const exports = runtime.requireModule(
33+
runtime.__mockRootPath,
34+
'RequireRegularModule',
35+
);
36+
expect(Object.keys(exports.parent)).toEqual([
37+
'exports',
38+
'filename',
39+
'id',
40+
'children',
41+
'parent',
42+
'paths',
43+
'require',
44+
]);
45+
}));
46+
47+
it('`module.parent` should be undefined for entrypoints', () =>
3048
createRuntime(__filename).then(runtime => {
3149
const exports = runtime.requireModule(
3250
runtime.__mockRootPath,
3351
'RegularModule',
3452
);
35-
expect(exports.parent).toEqual({
36-
exports: {},
37-
filename: 'mock.js',
38-
id: 'mockParent',
39-
});
53+
expect(exports.parent).toBeNull();
54+
}));
55+
56+
it('resolve module.parent.require correctly', () =>
57+
createRuntime(__filename).then(runtime => {
58+
const exports = runtime.requireModule(
59+
runtime.__mockRootPath,
60+
'inner_parent_module',
61+
);
62+
expect(exports.outputString).toEqual('This should happen');
63+
}));
64+
65+
it('resolve module.parent.filename correctly', () =>
66+
createRuntime(__filename).then(runtime => {
67+
const exports = runtime.requireModule(
68+
runtime.__mockRootPath,
69+
'inner_parent_module',
70+
);
71+
72+
expect(slash(exports.parentFileName.replace(__dirname, ''))).toEqual(
73+
'/test_root/inner_parent_module.js',
74+
);
4075
}));
4176

4277
it('provides `module.filename` to modules', () =>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @providesModule RequireRegularModule
8+
*/
9+
10+
'use strict';
11+
12+
module.exports.parent = require('RegularModule').parent;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @providesModule inner_parent_module
8+
*/
9+
10+
'use strict';
11+
12+
const impl = require('module-needing-parent');
13+
14+
module.exports = impl;

packages/jest-runtime/src/__tests__/test_root/node_modules/module-needing-parent/index.js

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/jest-runtime/src/__tests__/test_root/node_modules/module-needing-parent/node_modules/parent-module/index.js

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/jest-runtime/src/__tests__/test_root/node_modules/parent-module/index.js

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/jest-runtime/src/index.js

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ type CoverageOptions = {
5757
mapCoverage: boolean,
5858
};
5959

60+
type ModuleRegistry = {[key: string]: Module};
61+
6062
type BooleanObject = {[key: string]: boolean};
6163
type CacheFS = {[path: Path]: string};
6264

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

78-
const mockParentModule = {
79-
exports: {},
80-
filename: 'mock.js',
81-
id: 'mockParent',
82-
};
83-
8480
const unmockRegExpCache = new WeakMap();
8581

8682
class Runtime {
@@ -92,13 +88,13 @@ class Runtime {
9288
_currentlyExecutingModulePath: string;
9389
_environment: Environment;
9490
_explicitShouldMock: BooleanObject;
95-
_internalModuleRegistry: {[key: string]: Module};
91+
_internalModuleRegistry: ModuleRegistry;
9692
_isCurrentlyExecutingManualMock: ?string;
9793
_mockFactories: {[key: string]: () => any};
9894
_mockMetaDataCache: {[key: string]: MockFunctionMetadata};
9995
_mockRegistry: {[key: string]: any};
10096
_moduleMocker: ModuleMocker;
101-
_moduleRegistry: {[key: string]: Module};
97+
_moduleRegistry: ModuleRegistry;
10298
_resolver: Resolver;
10399
_shouldAutoMock: boolean;
104100
_shouldMockModuleCache: BooleanObject;
@@ -329,7 +325,7 @@ class Runtime {
329325
// $FlowFixMe
330326
localModule.exports = require(modulePath);
331327
} else {
332-
this._execModule(localModule, options);
328+
this._execModule(localModule, options, moduleRegistry, from);
333329
}
334330
}
335331
return moduleRegistry[modulePath].exports;
@@ -390,7 +386,7 @@ class Runtime {
390386
filename: modulePath,
391387
id: modulePath,
392388
};
393-
this._execModule(localModule);
389+
this._execModule(localModule, undefined, this._mockRegistry, from);
394390
this._mockRegistry[moduleID] = localModule.exports;
395391
} else {
396392
// Look for a real module to generate an automock from
@@ -478,7 +474,12 @@ class Runtime {
478474
return to ? this._resolver.resolveModule(from, to) : from;
479475
}
480476

481-
_execModule(localModule: Module, options: ?InternalModuleOptions) {
477+
_execModule(
478+
localModule: Module,
479+
options: ?InternalModuleOptions,
480+
moduleRegistry: ModuleRegistry,
481+
from: Path,
482+
) {
482483
// If the environment was disposed, prevent this module from being executed.
483484
if (!this._environment.global) {
484485
return;
@@ -493,7 +494,19 @@ class Runtime {
493494

494495
const dirname = path.dirname(filename);
495496
localModule.children = [];
496-
localModule.parent = mockParentModule;
497+
498+
Object.defineProperty(
499+
localModule,
500+
'parent',
501+
// https://github.com/facebook/flow/issues/285#issuecomment-270810619
502+
({
503+
enumerable: true,
504+
get() {
505+
return moduleRegistry[from] || null;
506+
},
507+
}: Object),
508+
);
509+
497510
localModule.paths = this._resolver.getModulePaths(dirname);
498511
localModule.require = this._createRequireImplementation(filename, options);
499512

0 commit comments

Comments
 (0)