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

feat: requireAndTranspileModule support ESM #11232

Merged
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Expand Up @@ -18,15 +18,20 @@
- `[jest-core]` Add support for `testSequencer` written in ESM ([#11207](https://github.com/facebook/jest/pull/11207))
- `[jest-core]` Add support for `globalSetup` and `globalTeardown` written in ESM ([#11267](https://github.com/facebook/jest/pull/11267))
- `[jest-core]` Add support for `watchPlugins` written in ESM ([#11315](https://github.com/facebook/jest/pull/11315))
- `[jest-core]` Add support for `runner` written in ESM ([#11232](https://github.com/facebook/jest/pull/11232))
- `[jest-core]` Handle `globalSetup` and `globalTeardown` ESM support by requireAndTranspileModule ([#11232](https://github.com/facebook/jest/pull/11232))
WeiAnAn marked this conversation as resolved.
Show resolved Hide resolved
- `[jest-environment-node]` Add AbortController to globals ([#11182](https://github.com/facebook/jest/pull/11182))
- `[@jest/fake-timers]` Update to `@sinonjs/fake-timers` to v7 ([#11198](https://github.com/facebook/jest/pull/11198))
- `[jest-haste-map]` Handle injected scm clocks ([#10966](https://github.com/facebook/jest/pull/10966))
- `[jest-haste-map]` Add `enableSymlinks` configuration option to follow symlinks for test files ([#9351](https://github.com/facebook/jest/pull/9351))
- `[jest-repl, jest-runner]` [**BREAKING**] Run transforms over environment ([#8751](https://github.com/facebook/jest/pull/8751))
- `[jest-repl]` Add support for `testEnvironment` written in ESM ([#11232](https://github.com/facebook/jest/pull/11232))
- `[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-runner]` Add support for `testRunner` written in ESM ([#11232](https://github.com/facebook/jest/pull/11232))
- `[jest-runner]` Handle `testEnvironment` ESM support by requireAndTranspileModule ([#11232](https://github.com/facebook/jest/pull/11232))
WeiAnAn marked this conversation as resolved.
Show resolved Hide resolved
- `[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))
Expand All @@ -37,6 +42,10 @@
- `[jest-transform]` [**BREAKING**] Do not export `ScriptTransformer` class, instead export the async function `createScriptTransformer` ([#11163](https://github.com/facebook/jest/pull/11163))
- `[jest-transform]` Async code transformations ([#9889](https://github.com/facebook/jest/pull/9889))
- `[jest-transform]` Support transpiled transformers ([#11193](https://github.com/facebook/jest/pull/11193))
- `[jest-transform]` [**BREAKING**] `requireAndTranspileModule` always return a `Promise`, and the third parameter type is changed to `RequireAndTranspileModuleOptions` which accept `applyInteropRequireDefault` option ([#11232](https://github.com/facebook/jest/pull/11232))
- `[jest-transform]` [**BREAKING**] `createTranspilingRequire` return function which return a `Promise` now ([#11232](https://github.com/facebook/jest/pull/11232))
- `[jest-util]` add requireOrImportModule for importing CJS or ESM ([#11199](https://github.com/facebook/jest/pull/11199))
- `[jest-util]` add `applyInteropRequireDefault` option on `requireOrImportModule` ([#11232](https://github.com/facebook/jest/pull/11232))
- `[jest-watcher]` Added support for clearing the line when `<C-u>` is pressed in a watch mode pattern prompt ([#11358](https://github.com/facebook/jest/pull/11358))
- `[jest-worker]` Add support for custom task queues and adds a `PriorityQueue` implementation. ([#10921](https://github.com/facebook/jest/pull/10921))
- `[jest-worker]` Add in-order scheduling policy to jest worker ([10902](https://github.com/facebook/jest/pull/10902))
Expand Down
38 changes: 38 additions & 0 deletions e2e/__tests__/esmGlobalSetup.test.ts
@@ -0,0 +1,38 @@
/**
SimenB marked this conversation as resolved.
Show resolved Hide resolved
* 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 {tmpdir} from 'os';
import * as path from 'path';
import * as fs from 'graceful-fs';
import {onNodeVersions} from '@jest/test-utils';
import {cleanup} from '../Utils';
import {json as runWithJson} from '../runJest';

const DIR = path.join(tmpdir(), 'jest-esm-global-setup');
const e2eDir = path.resolve(__dirname, '../esm-global-setup');

beforeEach(() => {
cleanup(DIR);
});

afterAll(() => {
cleanup(DIR);
});

onNodeVersions('^12.16.0 || >=13.7.0', () => {
test('globalSetup is triggered once before all test suites', () => {
const result = runWithJson(e2eDir, [], {
nodeOptions: '--experimental-vm-modules',
});

expect(result.exitCode).toBe(0);
const files = fs.readdirSync(DIR);
expect(files).toHaveLength(1);
const setup = fs.readFileSync(path.join(DIR, files[0]), 'utf8');
expect(setup).toBe('setup');
});
});
39 changes: 39 additions & 0 deletions e2e/__tests__/esmGlobalTeardown.test.ts
@@ -0,0 +1,39 @@
/**
SimenB marked this conversation as resolved.
Show resolved Hide resolved
* 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 {tmpdir} from 'os';
import * as path from 'path';
import * as fs from 'graceful-fs';
import {onNodeVersions} from '@jest/test-utils';
import {createDirectory} from 'jest-util';
import {cleanup} from '../Utils';
import {json as runWithJson} from '../runJest';

const DIR = path.join(tmpdir(), 'jest-esm-global-teardown');
const e2eDir = path.resolve(__dirname, '../esm-global-teardown');

beforeEach(() => {
cleanup(DIR);
});
afterAll(() => {
cleanup(DIR);
});

onNodeVersions('^12.16.0 || >=13.7.0', () => {
test('globalTeardown is triggered once after all test suites', () => {
createDirectory(DIR);
const result = runWithJson(e2eDir, [], {
nodeOptions: '--experimental-vm-modules',
});

expect(result.exitCode).toBe(0);
const files = fs.readdirSync(DIR);
expect(files).toHaveLength(1);
const teardown = fs.readFileSync(path.join(DIR, files[0]), 'utf8');
expect(teardown).toBe('teardown');
});
});
3 changes: 3 additions & 0 deletions e2e/__tests__/globalSetup.test.ts
Expand Up @@ -27,6 +27,7 @@ const customTransformDIR = path.join(
const nodeModulesDIR = path.join(tmpdir(), 'jest-global-setup-node-modules');
const rejectionDir = path.join(tmpdir(), 'jest-global-setup-rejection');
const e2eDir = path.resolve(__dirname, '../global-setup');
const esmTmpDir = path.join(tmpdir(), 'jest-global-setup-esm');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this (and the one in teardown) used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the directory is created in setup file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, but what is that directory used for? In the test it looks like it's created and deleted but never used

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GlobalSetup/Teardown will read this directory and do some assertion.
If we don't clean directory before testing, it will failed in second round.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even after 884c7e0 (#11232)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.
It fix #11267 globalSetup/globalTeardown e2e test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aha, gotcha!


beforeAll(() => {
runYarnInstall(e2eDir);
Expand All @@ -39,6 +40,7 @@ beforeEach(() => {
cleanup(customTransformDIR);
cleanup(nodeModulesDIR);
cleanup(rejectionDir);
cleanup(esmTmpDir);
});

afterAll(() => {
Expand All @@ -48,6 +50,7 @@ afterAll(() => {
cleanup(customTransformDIR);
cleanup(nodeModulesDIR);
cleanup(rejectionDir);
cleanup(esmTmpDir);
});

test('globalSetup is triggered once before all test suites', () => {
Expand Down
3 changes: 3 additions & 0 deletions e2e/__tests__/globalTeardown.test.ts
Expand Up @@ -17,6 +17,7 @@ const DIR = path.join(tmpdir(), 'jest-global-teardown');
const project1DIR = path.join(tmpdir(), 'jest-global-teardown-project-1');
const project2DIR = path.join(tmpdir(), 'jest-global-teardown-project-2');
const e2eDir = path.resolve(__dirname, '../global-teardown');
const esmTmpDir = path.join(tmpdir(), 'jest-global-teardown-esm');

beforeAll(() => {
runYarnInstall(e2eDir);
Expand All @@ -26,11 +27,13 @@ beforeEach(() => {
cleanup(DIR);
cleanup(project1DIR);
cleanup(project2DIR);
cleanup(esmTmpDir);
});
afterAll(() => {
cleanup(DIR);
cleanup(project1DIR);
cleanup(project2DIR);
cleanup(esmTmpDir);
});

test('globalTeardown is triggered once after all test suites', () => {
Expand Down
29 changes: 29 additions & 0 deletions e2e/__tests__/transform.test.ts
Expand Up @@ -315,4 +315,33 @@ onNodeVersions('^12.17.0 || >=13.2.0', () => {
expect(json.numPassedTests).toBe(1);
});
});

describe('transform-esm-runner', () => {
const dir = path.resolve(__dirname, '../transform/transform-esm-runner');
test('runs test with native ESM', () => {
const {json, stderr} = runWithJson(dir, ['--no-cache'], {
nodeOptions: '--experimental-vm-modules',
});

expect(stderr).toMatch(/PASS/);
expect(json.success).toBe(true);
expect(json.numPassedTests).toBe(1);
});
});

describe('transform-esm-testrunner', () => {
const dir = path.resolve(
__dirname,
'../transform/transform-esm-testrunner',
);
test('runs test with native ESM', () => {
const {json, stderr} = runWithJson(dir, ['--no-cache'], {
nodeOptions: '--experimental-vm-modules',
});

expect(stderr).toMatch(/PASS/);
expect(json.success).toBe(true);
expect(json.numPassedTests).toBe(1);
});
});
});
18 changes: 18 additions & 0 deletions e2e/esm-global-setup/__tests__/setup.test.js
@@ -0,0 +1,18 @@
/**
* 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 os from 'os';
import path from 'path';
import fs from 'graceful-fs';

const DIR = path.join(os.tmpdir(), 'jest-esm-global-setup');

test('should exist setup file', () => {
const files = fs.readdirSync(DIR);
expect(files).toHaveLength(1);
const setup = fs.readFileSync(path.join(DIR, files[0]), 'utf8');
expect(setup).toBe('setup');
});
10 changes: 10 additions & 0 deletions e2e/esm-global-setup/package.json
@@ -0,0 +1,10 @@
{
"type": "module",
"jest": {
"testEnvironment": "node",
"globalSetup": "<rootDir>/setup.mjs"
},
"devDependencies": {
"jest-util": "*"
}
}
24 changes: 24 additions & 0 deletions e2e/esm-global-setup/setup.mjs
@@ -0,0 +1,24 @@
/**
* 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 crypto from 'crypto';
import os from 'os';
import path from 'path';
import fs from 'graceful-fs';
import jestUtil from 'jest-util';

const {createDirectory} = jestUtil;

const DIR = path.join(os.tmpdir(), 'jest-esm-global-setup');

export default function () {
return new Promise((resolve, reject) => {
createDirectory(DIR);
const fileId = crypto.randomBytes(20).toString('hex');
fs.writeFileSync(path.join(DIR, fileId), 'setup');
resolve();
});
}
18 changes: 18 additions & 0 deletions e2e/esm-global-teardown/__tests__/teardown.test.js
@@ -0,0 +1,18 @@
/**
* 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';

import os from 'os';
import path from 'path';
import fs from 'graceful-fs';

const DIR = path.join(os.tmpdir(), 'jest-esm-global-teardown');

test('should not exist teardown file', () => {
const files = fs.readdirSync(DIR);
expect(files).toHaveLength(0);
});
7 changes: 7 additions & 0 deletions e2e/esm-global-teardown/package.json
@@ -0,0 +1,7 @@
{
"type": "module",
"jest": {
"testEnvironment": "node",
"globalTeardown": "<rootDir>/teardown.mjs"
}
}
24 changes: 24 additions & 0 deletions e2e/esm-global-teardown/teardown.mjs
@@ -0,0 +1,24 @@
/**
* 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 crypto from 'crypto';
import os from 'os';
import path from 'path';
import fs from 'graceful-fs';
import jestUtil from 'jest-util';

const {createDirectory} = jestUtil;

const DIR = path.join(os.tmpdir(), 'jest-esm-global-teardown');

export default function () {
return new Promise((resolve, reject) => {
createDirectory(DIR);
const fileId = crypto.randomBytes(20).toString('hex');
fs.writeFileSync(path.join(DIR, fileId), 'teardown');
resolve();
});
}
1 change: 0 additions & 1 deletion e2e/test-environment-async/__tests__/custom.test.js
Expand Up @@ -6,7 +6,6 @@
*
*/
'use strict';
/* eslint-env browser*/

test('setup', () => {
expect(global.setup).toBe('setup');
Expand Down
10 changes: 10 additions & 0 deletions e2e/transform/transform-esm-runner/__tests__/add.test.js
@@ -0,0 +1,10 @@
/**
* 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.
*/

it('should add two numbers', () => {
expect(1 + 1).toBe(2);
});
7 changes: 7 additions & 0 deletions e2e/transform/transform-esm-runner/package.json
@@ -0,0 +1,7 @@
{
"type": "module",
"jest": {
"rootDir": "./",
"runner": "<rootDir>/runner.mjs"
}
}
48 changes: 48 additions & 0 deletions e2e/transform/transform-esm-runner/runner.mjs
@@ -0,0 +1,48 @@
/**
* 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 testResult from '@jest/test-result';

const {createEmptyTestResult} = testResult;
Comment on lines +8 to +10
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import testResult from '@jest/test-result';
const {createEmptyTestResult} = testResult;
import {createEmptyTestResult} from '@jest/test-result';

does this work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No.
Because ts will compile to common js modules, and export using Object.defineProperty with getter.
ESM named import from CJS module only support exports.name = value way. esm commonjs namespaces

CI error log

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested. It worked.


export default class BaseTestRunner {
constructor(globalConfig, context) {
this._globalConfig = globalConfig;
this._context = context || {};
}

async runTests(tests, watcher, onStart, onResult, onFailure) {
return tests.reduce(
(promise, test) =>
promise
.then(async () => {
await onStart(test);
return {
...createEmptyTestResult(),
numPassingTests: 1,
testFilePath: test.path,
testResults: [
{
ancestorTitles: [],
duration: 2,
failureDetails: [],
failureMessages: [],
fullName: 'sample test',
location: null,
numPassingAsserts: 1,
status: 'passed',
title: 'sample test',
},
],
};
})
.then(result => onResult(test, result))
.catch(err => onFailure(test, err)),
Promise.resolve(),
);
}
}
10 changes: 10 additions & 0 deletions e2e/transform/transform-esm-testrunner/__tests__/add.test.js
@@ -0,0 +1,10 @@
/**
* 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.
*/

it('should add two numbers', () => {
expect(1 + 1).toBe(2);
});
7 changes: 7 additions & 0 deletions e2e/transform/transform-esm-testrunner/package.json
@@ -0,0 +1,7 @@
{
"type": "module",
"jest": {
"rootDir": "./",
"testRunner": "<rootDir>/test-runner.mjs"
}
}