diff --git a/CHANGELOG.md b/CHANGELOG.md index 90b7ab710a0e..bb04b78ffd50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## master +### Features + +* `[jest-runtime]` Provide `require.main` property set to module with test suite + ([#5618](https://github.com/facebook/jest/pull/5618)) + ## 22.4.0 ### Fixes diff --git a/integration-tests/__tests__/require_main.test.js b/integration-tests/__tests__/require_main.test.js new file mode 100644 index 000000000000..3853811e675b --- /dev/null +++ b/integration-tests/__tests__/require_main.test.js @@ -0,0 +1,18 @@ +/** + * 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. + * + * @flow + */ + +'use strict'; + +const runJest = require('../runJest'); + +test('provides `require.main` set to test suite module', () => { + const {stderr, stdout} = runJest('require-main'); + expect(stdout).not.toMatch('No tests found'); + expect(stderr).toMatch(/PASS __tests__(\/|\\+)loader\.test\.js/); +}); diff --git a/integration-tests/require-main/__tests__/loader.test.js b/integration-tests/require-main/__tests__/loader.test.js new file mode 100644 index 000000000000..00792b335788 --- /dev/null +++ b/integration-tests/require-main/__tests__/loader.test.js @@ -0,0 +1,13 @@ +/** + * 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. + */ +'use strict'; + +const loader = require('../loader'); + +test('loader should load a module', () => { + expect(loader('../example.js')).toBeTruthy(); +}); diff --git a/integration-tests/require-main/example.js b/integration-tests/require-main/example.js new file mode 100644 index 000000000000..694e77b679ab --- /dev/null +++ b/integration-tests/require-main/example.js @@ -0,0 +1,8 @@ +/** + * 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. + */ + +module.exports = true; diff --git a/integration-tests/require-main/loader.js b/integration-tests/require-main/loader.js new file mode 100644 index 000000000000..3a4743d92aed --- /dev/null +++ b/integration-tests/require-main/loader.js @@ -0,0 +1,11 @@ +/** + * 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. + */ +const path = require('path'); + +module.exports = function load(moduleId) { + return require(path.join(path.dirname(require.main.filename), moduleId)); +}; diff --git a/integration-tests/require-main/package.json b/integration-tests/require-main/package.json new file mode 100644 index 000000000000..148788b25446 --- /dev/null +++ b/integration-tests/require-main/package.json @@ -0,0 +1,5 @@ +{ + "jest": { + "testEnvironment": "node" + } +} diff --git a/packages/jest-runtime/src/__tests__/runtime_require_mock.test.js b/packages/jest-runtime/src/__tests__/runtime_require_mock.test.js index 46581a34a8f5..38dab7d03279 100644 --- a/packages/jest-runtime/src/__tests__/runtime_require_mock.test.js +++ b/packages/jest-runtime/src/__tests__/runtime_require_mock.test.js @@ -173,5 +173,14 @@ describe('Runtime', () => { expect(exports.isManualMockModule).toBe(true); }); }); + it('provides `require.main` in mock', () => + createRuntime(__filename).then(runtime => { + runtime._moduleRegistry[__filename] = module; + runtime.setMock(__filename, 'export_main', () => require.main, { + virtual: true, + }); + const mainModule = runtime.requireMock(__filename, 'export_main'); + expect(mainModule).toBe(module); + })); }); }); diff --git a/packages/jest-runtime/src/__tests__/runtime_require_module.test.js b/packages/jest-runtime/src/__tests__/runtime_require_module.test.js index 0bc393f34320..af8907c08b52 100644 --- a/packages/jest-runtime/src/__tests__/runtime_require_module.test.js +++ b/packages/jest-runtime/src/__tests__/runtime_require_module.test.js @@ -130,6 +130,17 @@ describe('Runtime requireModule', () => { }); }); }); + it('provides `require.main` to modules', () => + createRuntime(__filename).then(runtime => { + runtime._moduleRegistry[__filename] = module; + [ + './test_root/modules_with_main/export_main.js', + './test_root/modules_with_main/re_export_main.js', + ].forEach(modulePath => { + const mainModule = runtime.requireModule(__filename, modulePath); + expect(mainModule).toBe(module); + }); + })); it('throws on non-existent @providesModule modules', () => createRuntime(__filename).then(runtime => { diff --git a/packages/jest-runtime/src/__tests__/test_root/modules_with_main/export_main.js b/packages/jest-runtime/src/__tests__/test_root/modules_with_main/export_main.js new file mode 100644 index 000000000000..36be25279217 --- /dev/null +++ b/packages/jest-runtime/src/__tests__/test_root/modules_with_main/export_main.js @@ -0,0 +1,10 @@ +/** + * 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. + */ + +'use strict'; + +module.exports = require.main; diff --git a/packages/jest-runtime/src/__tests__/test_root/modules_with_main/re_export_main.js b/packages/jest-runtime/src/__tests__/test_root/modules_with_main/re_export_main.js new file mode 100644 index 000000000000..eb09de924753 --- /dev/null +++ b/packages/jest-runtime/src/__tests__/test_root/modules_with_main/re_export_main.js @@ -0,0 +1,10 @@ +/** + * 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. + */ + +'use strict'; + +module.exports = require('./export_main'); diff --git a/packages/jest-runtime/src/index.js b/packages/jest-runtime/src/index.js index a75e091d5e78..7b739fd4e32d 100644 --- a/packages/jest-runtime/src/index.js +++ b/packages/jest-runtime/src/index.js @@ -519,7 +519,7 @@ class Runtime { localModule.paths = this._resolver.getModulePaths(dirname); Object.defineProperty(localModule, 'require', { - value: this._createRequireImplementation(filename, options), + value: this._createRequireImplementation(localModule, options), }); const transformedFile = this._scriptTransformer.transform( @@ -678,18 +678,38 @@ class Runtime { } _createRequireImplementation( - from: Path, + from: Module, options: ?InternalModuleOptions, ): LocalModuleRequire { const moduleRequire = options && options.isInternalModule - ? (moduleName: string) => this.requireInternalModule(from, moduleName) - : this.requireModuleOrMock.bind(this, from); + ? (moduleName: string) => + this.requireInternalModule(from.filename, moduleName) + : this.requireModuleOrMock.bind(this, from.filename); moduleRequire.cache = Object.create(null); moduleRequire.extensions = Object.create(null); - moduleRequire.requireActual = this.requireModule.bind(this, from); - moduleRequire.requireMock = this.requireMock.bind(this, from); - moduleRequire.resolve = moduleName => this._resolveModule(from, moduleName); + moduleRequire.requireActual = this.requireModule.bind(this, from.filename); + moduleRequire.requireMock = this.requireMock.bind(this, from.filename); + moduleRequire.resolve = moduleName => + this._resolveModule(from.filename, moduleName); + Object.defineProperty( + moduleRequire, + 'main', + ({ + enumerable: true, + get() { + let mainModule = from.parent; + while ( + mainModule && + mainModule.parent && + mainModule.id !== mainModule.parent.id + ) { + mainModule = mainModule.parent; + } + return mainModule; + }, + }: Object), + ); return moduleRequire; }