diff --git a/CHANGELOG.md b/CHANGELOG.md index 41e06c7a83f9..7d468dc0fc47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ - `[jest-runner]` [**BREAKING**] set exit code to 1 if test logs after teardown ([#10728](https://github.com/facebook/jest/pull/10728)) - `[jest-runner]` [**BREAKING**] Run transforms over `runnner` ([#8823](https://github.com/facebook/jest/pull/8823)) - `[jest-runner]` [**BREAKING**] Run transforms over `testRunnner` ([#8823](https://github.com/facebook/jest/pull/8823)) +- `[jest-runner]` Possibility to use ESM for test environment ([11033](https://github.com/facebook/jest/pull/11033)) - `[jest-runtime]` Detect reexports from CJS as named exports in ESM ([#10988](https://github.com/facebook/jest/pull/10988)) - `[jest-runtime]` Support for async code transformations ([#11191](https://github.com/facebook/jest/pull/11191) & [#11220](https://github.com/facebook/jest/pull/11220)) - `[jest-reporters]` Add static filepath property to all reporters ([#11015](https://github.com/facebook/jest/pull/11015)) diff --git a/e2e/__tests__/testEnvironmentEsm.ts b/e2e/__tests__/testEnvironmentEsm.ts new file mode 100644 index 000000000000..1b604d3f9273 --- /dev/null +++ b/e2e/__tests__/testEnvironmentEsm.ts @@ -0,0 +1,20 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import {resolve} from 'path'; +import {onNodeVersions} from '@jest/test-utils'; +import runJest from '../runJest'; + +// The versions where vm.Module exists and commonjs with "exports" is not broken +onNodeVersions('^12.16.0 || >=13.7.0', () => { + it('support test environment written in ESM', () => { + const DIR = resolve(__dirname, '../test-environment-esm'); + const {exitCode} = runJest(DIR); + + expect(exitCode).toBe(0); + }); +}); diff --git a/e2e/test-environment-esm/EnvESM.js b/e2e/test-environment-esm/EnvESM.js new file mode 100644 index 000000000000..daf46b97fd90 --- /dev/null +++ b/e2e/test-environment-esm/EnvESM.js @@ -0,0 +1,15 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import NodeEnvironment from 'jest-environment-node'; + +export default class Env extends NodeEnvironment { + constructor(...args) { + super(...args); + this.global.someVar = 42; + } +} diff --git a/e2e/test-environment-esm/__tests__/testUsingESMTestEnv.test.js b/e2e/test-environment-esm/__tests__/testUsingESMTestEnv.test.js new file mode 100644 index 000000000000..b60ff5d6d94f --- /dev/null +++ b/e2e/test-environment-esm/__tests__/testUsingESMTestEnv.test.js @@ -0,0 +1,11 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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'; + +test('dummy', () => { + expect(global.someVar).toBe(42); +}); diff --git a/e2e/test-environment-esm/package.json b/e2e/test-environment-esm/package.json new file mode 100644 index 000000000000..32b3c79ef350 --- /dev/null +++ b/e2e/test-environment-esm/package.json @@ -0,0 +1,7 @@ +{ + "type": "module", + "jest": { + "testEnvironment": "/EnvESM.js", + "transform": {} + } +} diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 4e8237c283ec..396bd6c415f7 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -6,6 +6,7 @@ * */ +import {pathToFileURL} from 'url'; import chalk = require('chalk'); import * as fs from 'graceful-fs'; import sourcemapSupport = require('source-map-support'); @@ -105,10 +106,39 @@ async function runTestInternal( const cacheFS = new Map([[path, testSource]]); const transformer = await createScriptTransformer(config, cacheFS); - - const TestEnvironment: typeof JestEnvironment = interopRequireDefault( - transformer.requireAndTranspileModule(testEnvironment), - ).default; + let TestEnvironment: typeof JestEnvironment; + try { + TestEnvironment = interopRequireDefault( + transformer.requireAndTranspileModule(testEnvironment), + ).default; + } catch (err) { + if (err.code === 'ERR_REQUIRE_ESM') { + try { + const configUrl = pathToFileURL(testEnvironment); + + // node `import()` supports URL, but TypeScript doesn't know that + const importedConfig = await import(configUrl.href); + + if (!importedConfig.default) { + throw new Error( + `Jest: Failed to load mjs config file ${testEnvironment} - did you use a default export?`, + ); + } + + TestEnvironment = importedConfig.default; + } catch (innerError) { + if (innerError.message === 'Not supported') { + throw new Error( + `Jest: Your version of Node does not support dynamic import - please enable it or use a .cjs file extension for file ${testEnvironment}`, + ); + } + + throw innerError; + } + } else { + throw err; + } + } const testFramework: TestFramework = interopRequireDefault( transformer.requireAndTranspileModule( process.env.JEST_JASMINE === '1' ? 'jest-jasmine2' : config.testRunner,