From 8d598dd1e610acee796468e772cbc15613ff2451 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Sun, 2 Oct 2022 13:20:59 +0200 Subject: [PATCH 01/24] feat(config): add read initial options helper Add `readInitialOptions` function to the jest-config package. This is a helper function that can be useful for consumers of the jest programmatic api. It exposes the functionality that jest uses to read config from different sources. --- .../__fixtures__/async-config/jest.config.js | 5 + .../src/__fixtures__/js-config/jest.config.js | 3 + .../__fixtures__/json-config/jest.config.json | 3 + .../multiple-config-files/jest.config.js | 3 + .../multiple-config-files/jest.config.json | 3 + .../src/__fixtures__/pkg-config/package.json | 5 + .../src/__fixtures__/ts-config/jest.config.ts | 3 + .../src/__tests__/readInitialOptions.test.ts | 118 +++++++++++++++ packages/jest-config/src/index.ts | 138 ++++++++++++------ 9 files changed, 235 insertions(+), 46 deletions(-) create mode 100644 packages/jest-config/src/__fixtures__/async-config/jest.config.js create mode 100644 packages/jest-config/src/__fixtures__/js-config/jest.config.js create mode 100644 packages/jest-config/src/__fixtures__/json-config/jest.config.json create mode 100644 packages/jest-config/src/__fixtures__/multiple-config-files/jest.config.js create mode 100644 packages/jest-config/src/__fixtures__/multiple-config-files/jest.config.json create mode 100644 packages/jest-config/src/__fixtures__/pkg-config/package.json create mode 100644 packages/jest-config/src/__fixtures__/ts-config/jest.config.ts create mode 100644 packages/jest-config/src/__tests__/readInitialOptions.test.ts diff --git a/packages/jest-config/src/__fixtures__/async-config/jest.config.js b/packages/jest-config/src/__fixtures__/async-config/jest.config.js new file mode 100644 index 000000000000..7d0206905c19 --- /dev/null +++ b/packages/jest-config/src/__fixtures__/async-config/jest.config.js @@ -0,0 +1,5 @@ +module.exports = async function () { + return { + jestConfig: 'async-config', + }; +}; diff --git a/packages/jest-config/src/__fixtures__/js-config/jest.config.js b/packages/jest-config/src/__fixtures__/js-config/jest.config.js new file mode 100644 index 000000000000..ca94a9b4d2f6 --- /dev/null +++ b/packages/jest-config/src/__fixtures__/js-config/jest.config.js @@ -0,0 +1,3 @@ +module.exports = { + jestConfig: 'jest.config.js', +}; diff --git a/packages/jest-config/src/__fixtures__/json-config/jest.config.json b/packages/jest-config/src/__fixtures__/json-config/jest.config.json new file mode 100644 index 000000000000..bf022d79f271 --- /dev/null +++ b/packages/jest-config/src/__fixtures__/json-config/jest.config.json @@ -0,0 +1,3 @@ +{ + "jestConfig": "jest.config.json" +} diff --git a/packages/jest-config/src/__fixtures__/multiple-config-files/jest.config.js b/packages/jest-config/src/__fixtures__/multiple-config-files/jest.config.js new file mode 100644 index 000000000000..ca94a9b4d2f6 --- /dev/null +++ b/packages/jest-config/src/__fixtures__/multiple-config-files/jest.config.js @@ -0,0 +1,3 @@ +module.exports = { + jestConfig: 'jest.config.js', +}; diff --git a/packages/jest-config/src/__fixtures__/multiple-config-files/jest.config.json b/packages/jest-config/src/__fixtures__/multiple-config-files/jest.config.json new file mode 100644 index 000000000000..bf022d79f271 --- /dev/null +++ b/packages/jest-config/src/__fixtures__/multiple-config-files/jest.config.json @@ -0,0 +1,3 @@ +{ + "jestConfig": "jest.config.json" +} diff --git a/packages/jest-config/src/__fixtures__/pkg-config/package.json b/packages/jest-config/src/__fixtures__/pkg-config/package.json new file mode 100644 index 000000000000..766501789210 --- /dev/null +++ b/packages/jest-config/src/__fixtures__/pkg-config/package.json @@ -0,0 +1,5 @@ +{ + "jest": { + "jestConfig": "package.json" + } +} diff --git a/packages/jest-config/src/__fixtures__/ts-config/jest.config.ts b/packages/jest-config/src/__fixtures__/ts-config/jest.config.ts new file mode 100644 index 000000000000..64d592d87ceb --- /dev/null +++ b/packages/jest-config/src/__fixtures__/ts-config/jest.config.ts @@ -0,0 +1,3 @@ +export default { + jestConfig: 'jest.config.ts', +}; diff --git a/packages/jest-config/src/__tests__/readInitialOptions.test.ts b/packages/jest-config/src/__tests__/readInitialOptions.test.ts new file mode 100644 index 000000000000..d16512e9b763 --- /dev/null +++ b/packages/jest-config/src/__tests__/readInitialOptions.test.ts @@ -0,0 +1,118 @@ +import path = require('path'); +import type {Config} from 'jest-runner'; +import {readInitialOptions} from '../index'; + +function resolveFixture(...pathSegments: Array) { + return path.resolve(__dirname, '..', '__fixtures__', ...pathSegments); +} + +describe(readInitialOptions.name, () => { + test('should read from the cwd by default', async () => { + const configFile = resolveFixture('js-config', 'jest.config.js'); + const rootDir = resolveFixture('js-config'); + jest.spyOn(process, 'cwd').mockReturnValue(rootDir); + const {config, configPath} = await readInitialOptions(); + expect(config).toEqual({jestConfig: 'jest.config.js', rootDir}); + expect(configPath).toEqual(configFile); + }); + test('should read a jest.config.js file', async () => { + const configFile = resolveFixture('js-config', 'jest.config.js'); + const rootDir = resolveFixture('js-config'); + const {config, configPath} = await readInitialOptions(configFile); + expect(config).toEqual({jestConfig: 'jest.config.js', rootDir}); + expect(configPath).toEqual(configFile); + }); + test('should read a package.json file', async () => { + const configFile = resolveFixture('pkg-config', 'package.json'); + const rootDir = resolveFixture('pkg-config'); + const {config, configPath} = await readInitialOptions(configFile); + expect(config).toEqual({jestConfig: 'package.json', rootDir}); + expect(configPath).toEqual(configFile); + }); + test('should read a jest.config.ts file', async () => { + const configFile = resolveFixture('ts-config', 'jest.config.ts'); + const rootDir = resolveFixture('ts-config'); + const {config, configPath} = await readInitialOptions(configFile); + expect(config).toEqual({jestConfig: 'jest.config.ts', rootDir}); + expect(configPath).toEqual(configFile); + }); + test('should read a jest.config.json file', async () => { + const configFile = resolveFixture('json-config', 'jest.config.json'); + const rootDir = resolveFixture('json-config'); + const {config, configPath} = await readInitialOptions(configFile); + expect(config).toEqual({jestConfig: 'jest.config.json', rootDir}); + expect(configPath).toEqual(configFile); + }); + test('should read a jest config exporting an async function', async () => { + const configFile = resolveFixture('async-config', 'jest.config.js'); + const rootDir = resolveFixture('async-config'); + const {config, configPath} = await readInitialOptions(configFile); + expect(config).toEqual({jestConfig: 'async-config', rootDir}); + expect(configPath).toEqual(configFile); + }); + test('should be able to use serialized jest config', async () => { + const inputConfig = {jestConfig: 'serialized'}; + const {config, configPath} = await readInitialOptions( + JSON.stringify(inputConfig), + ); + expect(config).toEqual({...inputConfig, rootDir: process.cwd()}); + expect(configPath).toEqual(null); + }); + + test('should allow deserialized options', async () => { + const inputConfig = {jestConfig: 'deserialized'}; + const {config, configPath} = await readInitialOptions(undefined, { + packageRootOrConfig: inputConfig as Config.InitialOptions, + parentConfigDirname: process.cwd(), + }); + expect(config).toEqual({...inputConfig, rootDir: process.cwd()}); + expect(configPath).toEqual(null); + }); + + test('should be able to skip config reading, instead read from cwd', async () => { + const expectedConfigFile = resolveFixture( + 'json-config', + 'jest.config.json', + ); + jest.spyOn(process, 'cwd').mockReturnValue(resolveFixture('json-config')); + const {config, configPath} = await readInitialOptions( + resolveFixture('js-config', 'jest.config.js'), + { + readFromCwdInstead: true, + }, + ); + expect(config).toEqual({ + jestConfig: 'jest.config.json', + rootDir: path.dirname(expectedConfigFile), + }); + expect(configPath).toEqual(expectedConfigFile); + }); + + test('should give an error when there are multiple config files', async () => { + const cwd = resolveFixture('multiple-config-files'); + jest.spyOn(process, 'cwd').mockReturnValue(cwd); + const error: Error = await readInitialOptions().catch(error => error); + expect(error.message).toContain('Multiple configurations found'); + expect(error.message).toContain( + resolveFixture('multiple-config-files', 'jest.config.js'), + ); + expect(error.message).toContain( + resolveFixture('multiple-config-files', 'jest.config.json'), + ); + }); + + test('should be able to ignore multiple config files error', async () => { + const cwd = resolveFixture('multiple-config-files'); + jest.spyOn(process, 'cwd').mockReturnValue(cwd); + const {config, configPath} = await readInitialOptions(undefined, { + skipMultipleConfigError: true, + }); + expect(config).toEqual({ + jestConfig: 'jest.config.js', + rootDir: resolveFixture('multiple-config-files'), + }); + expect(configPath).toEqual( + resolveFixture('multiple-config-files', 'jest.config.js'), + ); + }); +}); diff --git a/packages/jest-config/src/index.ts b/packages/jest-config/src/index.ts index de947b1d5de2..14626084aed9 100644 --- a/packages/jest-config/src/index.ts +++ b/packages/jest-config/src/index.ts @@ -43,56 +43,18 @@ export async function readConfig( projectIndex = Infinity, skipMultipleConfigError = false, ): Promise { - let rawOptions: Config.InitialOptions; - let configPath = null; - - if (typeof packageRootOrConfig !== 'string') { - if (parentConfigDirname) { - rawOptions = packageRootOrConfig; - rawOptions.rootDir = rawOptions.rootDir - ? replaceRootDirInPath(parentConfigDirname, rawOptions.rootDir) - : parentConfigDirname; - } else { - throw new Error( - 'Jest: Cannot use configuration as an object without a file path.', - ); - } - } else if (isJSONString(argv.config)) { - // A JSON string was passed to `--config` argument and we can parse it - // and use as is. - let config; - try { - config = JSON.parse(argv.config); - } catch { - throw new Error( - 'There was an error while parsing the `--config` argument as a JSON string.', - ); - } - - // NOTE: we might need to resolve this dir to an absolute path in the future - config.rootDir = config.rootDir || packageRootOrConfig; - rawOptions = config; - // A string passed to `--config`, which is either a direct path to the config - // or a path to directory containing `package.json`, `jest.config.js` or `jest.config.ts` - } else if (!skipArgvConfigOption && typeof argv.config == 'string') { - configPath = resolveConfigPath( - argv.config, - process.cwd(), - skipMultipleConfigError, - ); - rawOptions = await readConfigFileAndSetRootDir(configPath); - } else { - // Otherwise just try to find config in the current rootDir. - configPath = resolveConfigPath( + const {config: initialOptions, configPath} = await readInitialOptions( + argv?.config, + { packageRootOrConfig, - process.cwd(), + parentConfigDirname, + readFromCwdInstead: skipArgvConfigOption, skipMultipleConfigError, - ); - rawOptions = await readConfigFileAndSetRootDir(configPath); - } + }, + ); const {options, hasDeprecationWarnings} = await normalize( - rawOptions, + initialOptions, argv, configPath, projectIndex, @@ -267,6 +229,90 @@ This usually means that your ${chalk.bold( } }; +/** +/** + * Reads the jest config, without validating them or filling it out with defaults. + * @param config The path to the file or serialized config. + * @param param1 Additional options + * @returns The raw initial config (not validated) + */ +export async function readInitialOptions( + config?: string, + { + packageRootOrConfig = process.cwd(), + parentConfigDirname = null, + readFromCwdInstead = false, + skipMultipleConfigError = false, + }: { + /** + * The package root or deserialized config (default is cwd) + */ + packageRootOrConfig?: string | Config.InitialOptions; + /** + * When the `packageRootOrConfig` contains config, this parameter should + * contain the dirname of the parent config + */ + parentConfigDirname?: null | string; + /** + * Indicates whether or not to read the specified config file from disk. + * When true, jest will read try to read config from the current working directory. + * (default is false) + */ + readFromCwdInstead?: boolean; + /** + * Indicates whether or not to ignore the error of jest finding multiple config files. + * (default is false) + */ + skipMultipleConfigError?: boolean; + } = {}, +): Promise<{config: Config.InitialOptions; configPath: string | null}> { + if (typeof packageRootOrConfig !== 'string') { + if (parentConfigDirname) { + const rawOptions = packageRootOrConfig; + rawOptions.rootDir = rawOptions.rootDir + ? replaceRootDirInPath(parentConfigDirname, rawOptions.rootDir) + : parentConfigDirname; + return {config: rawOptions, configPath: null}; + } else { + throw new Error( + 'Jest: Cannot use configuration as an object without a file path.', + ); + } + } else if (isJSONString(config)) { + // A JSON string was passed to `--config` argument and we can parse it + // and use as is. + let initialOptions; + try { + initialOptions = JSON.parse(config); + } catch { + throw new Error( + 'There was an error while parsing the `--config` argument as a JSON string.', + ); + } + + // NOTE: we might need to resolve this dir to an absolute path in the future + initialOptions.rootDir = initialOptions.rootDir || packageRootOrConfig; + return {config: initialOptions, configPath: null}; + // A string passed to `--config`, which is either a direct path to the config + // or a path to directory containing `package.json`, `jest.config.js` or `jest.config.ts` + } else if (!readFromCwdInstead && typeof config == 'string') { + const configPath = resolveConfigPath( + config, + process.cwd(), + skipMultipleConfigError, + ); + return {config: await readConfigFileAndSetRootDir(configPath), configPath}; + } else { + // Otherwise just try to find config in the current rootDir. + const configPath = resolveConfigPath( + packageRootOrConfig, + process.cwd(), + skipMultipleConfigError, + ); + return {config: await readConfigFileAndSetRootDir(configPath), configPath}; + } +} + // Possible scenarios: // 1. jest --config config.json // 2. jest --projects p1 p2 From aabb8de896f2f3a076be8927e6a8699e273c4ac8 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Sun, 2 Oct 2022 13:21:23 +0200 Subject: [PATCH 02/24] Enable experimental-vm-modules to run jest. --- .vscode/launch.json | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 5de4feacd272..8802afcba97c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,6 +13,9 @@ "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "disableOptimisticBPs": true, + "env": { + "NODE_OPTIONS": "--experimental-vm-modules" + }, "windows": { "program": "${workspaceFolder}/node_modules/jest/bin/jest" } diff --git a/package.json b/package.json index 425b52667541..09110bd7b714 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "clean-e2e": "node ./scripts/cleanE2e.mjs", "crowdin:upload": "echo 'Uploading sources to Crowdin' && crowdin upload sources --config ./crowdin.yaml", "crowdin:download": "echo 'Downloading translations from Crowdin' && crowdin download --config ./crowdin.yaml", - "jest": "node ./packages/jest-cli/bin/jest.js", + "jest": "node --experimental-vm-modules ./packages/jest-cli/bin/jest.js", "jest-jasmine": "JEST_JASMINE=1 yarn jest", "jest-jasmine-ci": "yarn jest-jasmine --color --config jest.config.ci.mjs", "jest-coverage": "yarn jest --coverage", From e9b5c8700e194ed4e8a8bc9a1b88dbc439e21b1b Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Sun, 2 Oct 2022 13:28:23 +0200 Subject: [PATCH 03/24] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1ef3104cc39..91c521c46c17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - `[jest-core]` Enable testResultsProcessor to be async ([#13343](https://github.com/facebook/jest/pull/13343)) - `[expect, @jest/expect-utils]` Allow `isA` utility to take a type argument ([#13355](https://github.com/facebook/jest/pull/13355)) +- `[jest-config]` Add `readInitialConfig` utility function ([#13356](https://github.com/facebook/jest/pull/13356)) ### Fixes From b6276fbc57925710e63681cc82293b4f72d74455 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Sun, 2 Oct 2022 13:30:09 +0200 Subject: [PATCH 04/24] Add copyright headers --- .../src/__fixtures__/async-config/jest.config.js | 6 ++++++ .../jest-config/src/__fixtures__/js-config/jest.config.js | 6 ++++++ .../src/__fixtures__/multiple-config-files/jest.config.js | 6 ++++++ .../jest-config/src/__fixtures__/ts-config/jest.config.ts | 6 ++++++ .../jest-config/src/__tests__/readInitialOptions.test.ts | 6 ++++++ 5 files changed, 30 insertions(+) diff --git a/packages/jest-config/src/__fixtures__/async-config/jest.config.js b/packages/jest-config/src/__fixtures__/async-config/jest.config.js index 7d0206905c19..00df2a4f3837 100644 --- a/packages/jest-config/src/__fixtures__/async-config/jest.config.js +++ b/packages/jest-config/src/__fixtures__/async-config/jest.config.js @@ -1,3 +1,9 @@ +/** + * 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. + */ module.exports = async function () { return { jestConfig: 'async-config', diff --git a/packages/jest-config/src/__fixtures__/js-config/jest.config.js b/packages/jest-config/src/__fixtures__/js-config/jest.config.js index ca94a9b4d2f6..40ef4689be89 100644 --- a/packages/jest-config/src/__fixtures__/js-config/jest.config.js +++ b/packages/jest-config/src/__fixtures__/js-config/jest.config.js @@ -1,3 +1,9 @@ +/** + * 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. + */ module.exports = { jestConfig: 'jest.config.js', }; diff --git a/packages/jest-config/src/__fixtures__/multiple-config-files/jest.config.js b/packages/jest-config/src/__fixtures__/multiple-config-files/jest.config.js index ca94a9b4d2f6..40ef4689be89 100644 --- a/packages/jest-config/src/__fixtures__/multiple-config-files/jest.config.js +++ b/packages/jest-config/src/__fixtures__/multiple-config-files/jest.config.js @@ -1,3 +1,9 @@ +/** + * 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. + */ module.exports = { jestConfig: 'jest.config.js', }; diff --git a/packages/jest-config/src/__fixtures__/ts-config/jest.config.ts b/packages/jest-config/src/__fixtures__/ts-config/jest.config.ts index 64d592d87ceb..bf05ee62de87 100644 --- a/packages/jest-config/src/__fixtures__/ts-config/jest.config.ts +++ b/packages/jest-config/src/__fixtures__/ts-config/jest.config.ts @@ -1,3 +1,9 @@ +/** + * 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. + */ export default { jestConfig: 'jest.config.ts', }; diff --git a/packages/jest-config/src/__tests__/readInitialOptions.test.ts b/packages/jest-config/src/__tests__/readInitialOptions.test.ts index d16512e9b763..5d5e666f026e 100644 --- a/packages/jest-config/src/__tests__/readInitialOptions.test.ts +++ b/packages/jest-config/src/__tests__/readInitialOptions.test.ts @@ -1,3 +1,9 @@ +/** + * 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 path = require('path'); import type {Config} from 'jest-runner'; import {readInitialOptions} from '../index'; From d93a12c47478319a95a203208749b82749fdebf0 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Sun, 2 Oct 2022 13:53:21 +0200 Subject: [PATCH 05/24] Update assert: also use forward slashes on windows. --- .../jest-config/src/__tests__/readInitialOptions.test.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/jest-config/src/__tests__/readInitialOptions.test.ts b/packages/jest-config/src/__tests__/readInitialOptions.test.ts index 5d5e666f026e..caa06adb6f29 100644 --- a/packages/jest-config/src/__tests__/readInitialOptions.test.ts +++ b/packages/jest-config/src/__tests__/readInitialOptions.test.ts @@ -99,12 +99,8 @@ describe(readInitialOptions.name, () => { jest.spyOn(process, 'cwd').mockReturnValue(cwd); const error: Error = await readInitialOptions().catch(error => error); expect(error.message).toContain('Multiple configurations found'); - expect(error.message).toContain( - resolveFixture('multiple-config-files', 'jest.config.js'), - ); - expect(error.message).toContain( - resolveFixture('multiple-config-files', 'jest.config.json'), - ); + expect(error.message).toContain('multiple-config-files/jest.config.js'); + expect(error.message).toContain('multiple-config-files/jest.config.json'); }); test('should be able to ignore multiple config files error', async () => { From eadd24e314de34f13a6f2e533f0033940aaeb3b8 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Sun, 2 Oct 2022 13:56:55 +0200 Subject: [PATCH 06/24] Update packages/jest-config/src/index.ts Co-authored-by: Simen Bekkhus --- packages/jest-config/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-config/src/index.ts b/packages/jest-config/src/index.ts index 14626084aed9..a74da0f2b508 100644 --- a/packages/jest-config/src/index.ts +++ b/packages/jest-config/src/index.ts @@ -258,7 +258,7 @@ export async function readInitialOptions( * When true, jest will read try to read config from the current working directory. * (default is false) */ - readFromCwdInstead?: boolean; + readFromCwd?: boolean; /** * Indicates whether or not to ignore the error of jest finding multiple config files. * (default is false) From 947bfaa10d618657055c9d84e06bb686752ce054 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Sun, 2 Oct 2022 14:00:35 +0200 Subject: [PATCH 07/24] Update changelog sorting --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91c521c46c17..d74de77d36f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,9 @@ ### Features +- `[jest-config]` Add `readInitialConfig` utility function ([#13356](https://github.com/facebook/jest/pull/13356)) - `[jest-core]` Enable testResultsProcessor to be async ([#13343](https://github.com/facebook/jest/pull/13343)) - `[expect, @jest/expect-utils]` Allow `isA` utility to take a type argument ([#13355](https://github.com/facebook/jest/pull/13355)) -- `[jest-config]` Add `readInitialConfig` utility function ([#13356](https://github.com/facebook/jest/pull/13356)) ### Fixes From 7793ff2ef5b4f5e61d76ab5efbb84d50f4b658ec Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Sun, 2 Oct 2022 14:04:27 +0200 Subject: [PATCH 08/24] Move integration tests to e2e --- e2e/__tests__/readInitialOptions.test.ts | 101 ++++++++++++++++++ .../async-config/jest.config.js | 0 .../js-config/jest.config.js | 0 .../json-config/jest.config.json | 0 .../multiple-config-files/jest.config.js | 0 .../multiple-config-files/jest.config.json | 0 .../pkg-config/package.json | 0 .../ts-config/jest.config.ts | 0 .../src/__tests__/readInitialOptions.test.ts | 92 +--------------- 9 files changed, 102 insertions(+), 91 deletions(-) create mode 100644 e2e/__tests__/readInitialOptions.test.ts rename {packages/jest-config/src/__fixtures__ => e2e/read-initial-options}/async-config/jest.config.js (100%) rename {packages/jest-config/src/__fixtures__ => e2e/read-initial-options}/js-config/jest.config.js (100%) rename {packages/jest-config/src/__fixtures__ => e2e/read-initial-options}/json-config/jest.config.json (100%) rename {packages/jest-config/src/__fixtures__ => e2e/read-initial-options}/multiple-config-files/jest.config.js (100%) rename {packages/jest-config/src/__fixtures__ => e2e/read-initial-options}/multiple-config-files/jest.config.json (100%) rename {packages/jest-config/src/__fixtures__ => e2e/read-initial-options}/pkg-config/package.json (100%) rename {packages/jest-config/src/__fixtures__ => e2e/read-initial-options}/ts-config/jest.config.ts (100%) diff --git a/e2e/__tests__/readInitialOptions.test.ts b/e2e/__tests__/readInitialOptions.test.ts new file mode 100644 index 000000000000..60254a719dac --- /dev/null +++ b/e2e/__tests__/readInitialOptions.test.ts @@ -0,0 +1,101 @@ +/** + * 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 path = require('path'); +import {readInitialOptions} from 'jest-config'; + +function resolveFixture(...pathSegments: Array) { + return path.resolve(__dirname, '..', 'read-initial-options', ...pathSegments); +} + +describe(readInitialOptions.name, () => { + test('should read from the cwd by default', async () => { + const configFile = resolveFixture('js-config', 'jest.config.js'); + const rootDir = resolveFixture('js-config'); + jest.spyOn(process, 'cwd').mockReturnValue(rootDir); + const {config, configPath} = await readInitialOptions(); + expect(config).toEqual({jestConfig: 'jest.config.js', rootDir}); + expect(configPath).toEqual(configFile); + }); + test('should read a jest.config.js file', async () => { + const configFile = resolveFixture('js-config', 'jest.config.js'); + const rootDir = resolveFixture('js-config'); + const {config, configPath} = await readInitialOptions(configFile); + expect(config).toEqual({jestConfig: 'jest.config.js', rootDir}); + expect(configPath).toEqual(configFile); + }); + test('should read a package.json file', async () => { + const configFile = resolveFixture('pkg-config', 'package.json'); + const rootDir = resolveFixture('pkg-config'); + const {config, configPath} = await readInitialOptions(configFile); + expect(config).toEqual({jestConfig: 'package.json', rootDir}); + expect(configPath).toEqual(configFile); + }); + test('should read a jest.config.ts file', async () => { + const configFile = resolveFixture('ts-config', 'jest.config.ts'); + const rootDir = resolveFixture('ts-config'); + const {config, configPath} = await readInitialOptions(configFile); + expect(config).toEqual({jestConfig: 'jest.config.ts', rootDir}); + expect(configPath).toEqual(configFile); + }); + test('should read a jest.config.json file', async () => { + const configFile = resolveFixture('json-config', 'jest.config.json'); + const rootDir = resolveFixture('json-config'); + const {config, configPath} = await readInitialOptions(configFile); + expect(config).toEqual({jestConfig: 'jest.config.json', rootDir}); + expect(configPath).toEqual(configFile); + }); + test('should read a jest config exporting an async function', async () => { + const configFile = resolveFixture('async-config', 'jest.config.js'); + const rootDir = resolveFixture('async-config'); + const {config, configPath} = await readInitialOptions(configFile); + expect(config).toEqual({jestConfig: 'async-config', rootDir}); + expect(configPath).toEqual(configFile); + }); + + test('should be able to skip config reading, instead read from cwd', async () => { + const expectedConfigFile = resolveFixture( + 'json-config', + 'jest.config.json', + ); + jest.spyOn(process, 'cwd').mockReturnValue(resolveFixture('json-config')); + const {config, configPath} = await readInitialOptions( + resolveFixture('js-config', 'jest.config.js'), + { + readFromCwdInstead: true, + }, + ); + expect(config).toEqual({ + jestConfig: 'jest.config.json', + rootDir: path.dirname(expectedConfigFile), + }); + expect(configPath).toEqual(expectedConfigFile); + }); + + test('should give an error when there are multiple config files', async () => { + const cwd = resolveFixture('multiple-config-files'); + jest.spyOn(process, 'cwd').mockReturnValue(cwd); + const error: Error = await readInitialOptions().catch(error => error); + expect(error.message).toContain('Multiple configurations found'); + expect(error.message).toContain('multiple-config-files/jest.config.js'); + expect(error.message).toContain('multiple-config-files/jest.config.json'); + }); + + test('should be able to ignore multiple config files error', async () => { + const cwd = resolveFixture('multiple-config-files'); + jest.spyOn(process, 'cwd').mockReturnValue(cwd); + const {config, configPath} = await readInitialOptions(undefined, { + skipMultipleConfigError: true, + }); + expect(config).toEqual({ + jestConfig: 'jest.config.js', + rootDir: resolveFixture('multiple-config-files'), + }); + expect(configPath).toEqual( + resolveFixture('multiple-config-files', 'jest.config.js'), + ); + }); +}); diff --git a/packages/jest-config/src/__fixtures__/async-config/jest.config.js b/e2e/read-initial-options/async-config/jest.config.js similarity index 100% rename from packages/jest-config/src/__fixtures__/async-config/jest.config.js rename to e2e/read-initial-options/async-config/jest.config.js diff --git a/packages/jest-config/src/__fixtures__/js-config/jest.config.js b/e2e/read-initial-options/js-config/jest.config.js similarity index 100% rename from packages/jest-config/src/__fixtures__/js-config/jest.config.js rename to e2e/read-initial-options/js-config/jest.config.js diff --git a/packages/jest-config/src/__fixtures__/json-config/jest.config.json b/e2e/read-initial-options/json-config/jest.config.json similarity index 100% rename from packages/jest-config/src/__fixtures__/json-config/jest.config.json rename to e2e/read-initial-options/json-config/jest.config.json diff --git a/packages/jest-config/src/__fixtures__/multiple-config-files/jest.config.js b/e2e/read-initial-options/multiple-config-files/jest.config.js similarity index 100% rename from packages/jest-config/src/__fixtures__/multiple-config-files/jest.config.js rename to e2e/read-initial-options/multiple-config-files/jest.config.js diff --git a/packages/jest-config/src/__fixtures__/multiple-config-files/jest.config.json b/e2e/read-initial-options/multiple-config-files/jest.config.json similarity index 100% rename from packages/jest-config/src/__fixtures__/multiple-config-files/jest.config.json rename to e2e/read-initial-options/multiple-config-files/jest.config.json diff --git a/packages/jest-config/src/__fixtures__/pkg-config/package.json b/e2e/read-initial-options/pkg-config/package.json similarity index 100% rename from packages/jest-config/src/__fixtures__/pkg-config/package.json rename to e2e/read-initial-options/pkg-config/package.json diff --git a/packages/jest-config/src/__fixtures__/ts-config/jest.config.ts b/e2e/read-initial-options/ts-config/jest.config.ts similarity index 100% rename from packages/jest-config/src/__fixtures__/ts-config/jest.config.ts rename to e2e/read-initial-options/ts-config/jest.config.ts diff --git a/packages/jest-config/src/__tests__/readInitialOptions.test.ts b/packages/jest-config/src/__tests__/readInitialOptions.test.ts index caa06adb6f29..4feb9c2a6154 100644 --- a/packages/jest-config/src/__tests__/readInitialOptions.test.ts +++ b/packages/jest-config/src/__tests__/readInitialOptions.test.ts @@ -4,58 +4,10 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -import path = require('path'); import type {Config} from 'jest-runner'; import {readInitialOptions} from '../index'; -function resolveFixture(...pathSegments: Array) { - return path.resolve(__dirname, '..', '__fixtures__', ...pathSegments); -} - describe(readInitialOptions.name, () => { - test('should read from the cwd by default', async () => { - const configFile = resolveFixture('js-config', 'jest.config.js'); - const rootDir = resolveFixture('js-config'); - jest.spyOn(process, 'cwd').mockReturnValue(rootDir); - const {config, configPath} = await readInitialOptions(); - expect(config).toEqual({jestConfig: 'jest.config.js', rootDir}); - expect(configPath).toEqual(configFile); - }); - test('should read a jest.config.js file', async () => { - const configFile = resolveFixture('js-config', 'jest.config.js'); - const rootDir = resolveFixture('js-config'); - const {config, configPath} = await readInitialOptions(configFile); - expect(config).toEqual({jestConfig: 'jest.config.js', rootDir}); - expect(configPath).toEqual(configFile); - }); - test('should read a package.json file', async () => { - const configFile = resolveFixture('pkg-config', 'package.json'); - const rootDir = resolveFixture('pkg-config'); - const {config, configPath} = await readInitialOptions(configFile); - expect(config).toEqual({jestConfig: 'package.json', rootDir}); - expect(configPath).toEqual(configFile); - }); - test('should read a jest.config.ts file', async () => { - const configFile = resolveFixture('ts-config', 'jest.config.ts'); - const rootDir = resolveFixture('ts-config'); - const {config, configPath} = await readInitialOptions(configFile); - expect(config).toEqual({jestConfig: 'jest.config.ts', rootDir}); - expect(configPath).toEqual(configFile); - }); - test('should read a jest.config.json file', async () => { - const configFile = resolveFixture('json-config', 'jest.config.json'); - const rootDir = resolveFixture('json-config'); - const {config, configPath} = await readInitialOptions(configFile); - expect(config).toEqual({jestConfig: 'jest.config.json', rootDir}); - expect(configPath).toEqual(configFile); - }); - test('should read a jest config exporting an async function', async () => { - const configFile = resolveFixture('async-config', 'jest.config.js'); - const rootDir = resolveFixture('async-config'); - const {config, configPath} = await readInitialOptions(configFile); - expect(config).toEqual({jestConfig: 'async-config', rootDir}); - expect(configPath).toEqual(configFile); - }); test('should be able to use serialized jest config', async () => { const inputConfig = {jestConfig: 'serialized'}; const {config, configPath} = await readInitialOptions( @@ -74,47 +26,5 @@ describe(readInitialOptions.name, () => { expect(config).toEqual({...inputConfig, rootDir: process.cwd()}); expect(configPath).toEqual(null); }); - - test('should be able to skip config reading, instead read from cwd', async () => { - const expectedConfigFile = resolveFixture( - 'json-config', - 'jest.config.json', - ); - jest.spyOn(process, 'cwd').mockReturnValue(resolveFixture('json-config')); - const {config, configPath} = await readInitialOptions( - resolveFixture('js-config', 'jest.config.js'), - { - readFromCwdInstead: true, - }, - ); - expect(config).toEqual({ - jestConfig: 'jest.config.json', - rootDir: path.dirname(expectedConfigFile), - }); - expect(configPath).toEqual(expectedConfigFile); - }); - - test('should give an error when there are multiple config files', async () => { - const cwd = resolveFixture('multiple-config-files'); - jest.spyOn(process, 'cwd').mockReturnValue(cwd); - const error: Error = await readInitialOptions().catch(error => error); - expect(error.message).toContain('Multiple configurations found'); - expect(error.message).toContain('multiple-config-files/jest.config.js'); - expect(error.message).toContain('multiple-config-files/jest.config.json'); - }); - - test('should be able to ignore multiple config files error', async () => { - const cwd = resolveFixture('multiple-config-files'); - jest.spyOn(process, 'cwd').mockReturnValue(cwd); - const {config, configPath} = await readInitialOptions(undefined, { - skipMultipleConfigError: true, - }); - expect(config).toEqual({ - jestConfig: 'jest.config.js', - rootDir: resolveFixture('multiple-config-files'), - }); - expect(configPath).toEqual( - resolveFixture('multiple-config-files', 'jest.config.js'), - ); - }); + // Note: actual file reading is tested in e2e test }); From 153af9b8f643e5a41a44546e2ae193bb2c33b71d Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Sun, 2 Oct 2022 14:04:48 +0200 Subject: [PATCH 09/24] Remove `--experimental-vm-modules` --- .vscode/launch.json | 3 --- package.json | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 8802afcba97c..5de4feacd272 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,9 +13,6 @@ "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "disableOptimisticBPs": true, - "env": { - "NODE_OPTIONS": "--experimental-vm-modules" - }, "windows": { "program": "${workspaceFolder}/node_modules/jest/bin/jest" } diff --git a/package.json b/package.json index 09110bd7b714..425b52667541 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "clean-e2e": "node ./scripts/cleanE2e.mjs", "crowdin:upload": "echo 'Uploading sources to Crowdin' && crowdin upload sources --config ./crowdin.yaml", "crowdin:download": "echo 'Downloading translations from Crowdin' && crowdin download --config ./crowdin.yaml", - "jest": "node --experimental-vm-modules ./packages/jest-cli/bin/jest.js", + "jest": "node ./packages/jest-cli/bin/jest.js", "jest-jasmine": "JEST_JASMINE=1 yarn jest", "jest-jasmine-ci": "yarn jest-jasmine --color --config jest.config.ci.mjs", "jest-coverage": "yarn jest --coverage", From 4294610a453525d5ad11f5f36e5f4cac72c4fd81 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Sun, 2 Oct 2022 14:10:54 +0200 Subject: [PATCH 10/24] Extract `ReadJestConfigOptions` interface --- packages/jest-config/src/index.ts | 51 ++++++++++++++++--------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/packages/jest-config/src/index.ts b/packages/jest-config/src/index.ts index a74da0f2b508..788ce5d63caa 100644 --- a/packages/jest-config/src/index.ts +++ b/packages/jest-config/src/index.ts @@ -229,7 +229,29 @@ This usually means that your ${chalk.bold( } }; -/** +export interface ReadJestConfigOptions { + /** + * The package root or deserialized config (default is cwd) + */ + packageRootOrConfig?: string | Config.InitialOptions; + /** + * When the `packageRootOrConfig` contains config, this parameter should + * contain the dirname of the parent config + */ + parentConfigDirname?: null | string; + /** + * Indicates whether or not to read the specified config file from disk. + * When true, jest will read try to read config from the current working directory. + * (default is false) + */ + readFromCwd?: boolean; + /** + * Indicates whether or not to ignore the error of jest finding multiple config files. + * (default is false) + */ + skipMultipleConfigError?: boolean; +} + /** * Reads the jest config, without validating them or filling it out with defaults. * @param config The path to the file or serialized config. @@ -241,30 +263,9 @@ export async function readInitialOptions( { packageRootOrConfig = process.cwd(), parentConfigDirname = null, - readFromCwdInstead = false, + readFromCwd = false, skipMultipleConfigError = false, - }: { - /** - * The package root or deserialized config (default is cwd) - */ - packageRootOrConfig?: string | Config.InitialOptions; - /** - * When the `packageRootOrConfig` contains config, this parameter should - * contain the dirname of the parent config - */ - parentConfigDirname?: null | string; - /** - * Indicates whether or not to read the specified config file from disk. - * When true, jest will read try to read config from the current working directory. - * (default is false) - */ - readFromCwd?: boolean; - /** - * Indicates whether or not to ignore the error of jest finding multiple config files. - * (default is false) - */ - skipMultipleConfigError?: boolean; - } = {}, + }: ReadJestConfigOptions = {}, ): Promise<{config: Config.InitialOptions; configPath: string | null}> { if (typeof packageRootOrConfig !== 'string') { if (parentConfigDirname) { @@ -295,7 +296,7 @@ export async function readInitialOptions( return {config: initialOptions, configPath: null}; // A string passed to `--config`, which is either a direct path to the config // or a path to directory containing `package.json`, `jest.config.js` or `jest.config.ts` - } else if (!readFromCwdInstead && typeof config == 'string') { + } else if (!readFromCwd && typeof config == 'string') { const configPath = resolveConfigPath( config, process.cwd(), From 6d347a67bc46e23da362e99dd9b5d79e2bfa180e Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Sun, 2 Oct 2022 14:18:41 +0200 Subject: [PATCH 11/24] Finish rename `readFromCwdInstead` -> `readFromCwd` --- e2e/__tests__/readInitialOptions.test.ts | 2 +- packages/jest-config/src/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/__tests__/readInitialOptions.test.ts b/e2e/__tests__/readInitialOptions.test.ts index 60254a719dac..f6b0c56f0db0 100644 --- a/e2e/__tests__/readInitialOptions.test.ts +++ b/e2e/__tests__/readInitialOptions.test.ts @@ -65,7 +65,7 @@ describe(readInitialOptions.name, () => { const {config, configPath} = await readInitialOptions( resolveFixture('js-config', 'jest.config.js'), { - readFromCwdInstead: true, + readFromCwd: true, }, ); expect(config).toEqual({ diff --git a/packages/jest-config/src/index.ts b/packages/jest-config/src/index.ts index 788ce5d63caa..06613c1b0b96 100644 --- a/packages/jest-config/src/index.ts +++ b/packages/jest-config/src/index.ts @@ -48,7 +48,7 @@ export async function readConfig( { packageRootOrConfig, parentConfigDirname, - readFromCwdInstead: skipArgvConfigOption, + readFromCwd: skipArgvConfigOption, skipMultipleConfigError, }, ); From c2dffe995af845ccc0bd4affc936d8013f90bf19 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Sun, 2 Oct 2022 14:25:31 +0200 Subject: [PATCH 12/24] Import from `@jest/types` instead of `jest-runner` Co-authored-by: Tom Mrazauskas --- packages/jest-config/src/__tests__/readInitialOptions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-config/src/__tests__/readInitialOptions.test.ts b/packages/jest-config/src/__tests__/readInitialOptions.test.ts index 4feb9c2a6154..3bd94b176e92 100644 --- a/packages/jest-config/src/__tests__/readInitialOptions.test.ts +++ b/packages/jest-config/src/__tests__/readInitialOptions.test.ts @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -import type {Config} from 'jest-runner'; +import type {Config} from '@jest/types'; import {readInitialOptions} from '../index'; describe(readInitialOptions.name, () => { From b3a13cc62f597c4390d1ea5c83a79e4ec46f366a Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Sun, 2 Oct 2022 15:23:21 +0200 Subject: [PATCH 13/24] Update packages/jest-config/src/__tests__/readInitialOptions.test.ts Co-authored-by: Tom Mrazauskas --- packages/jest-config/src/__tests__/readInitialOptions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-config/src/__tests__/readInitialOptions.test.ts b/packages/jest-config/src/__tests__/readInitialOptions.test.ts index 3bd94b176e92..de85b0500b0d 100644 --- a/packages/jest-config/src/__tests__/readInitialOptions.test.ts +++ b/packages/jest-config/src/__tests__/readInitialOptions.test.ts @@ -24,7 +24,7 @@ describe(readInitialOptions.name, () => { parentConfigDirname: process.cwd(), }); expect(config).toEqual({...inputConfig, rootDir: process.cwd()}); - expect(configPath).toEqual(null); + expect(configPath).toBeNull(); }); // Note: actual file reading is tested in e2e test }); From c41dbdff20b200f548fca92d5fffc199a6f5c9e7 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Sun, 2 Oct 2022 15:25:01 +0200 Subject: [PATCH 14/24] Update packages/jest-config/src/__tests__/readInitialOptions.test.ts Co-authored-by: Tom Mrazauskas --- packages/jest-config/src/__tests__/readInitialOptions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-config/src/__tests__/readInitialOptions.test.ts b/packages/jest-config/src/__tests__/readInitialOptions.test.ts index de85b0500b0d..dcf57a3d912e 100644 --- a/packages/jest-config/src/__tests__/readInitialOptions.test.ts +++ b/packages/jest-config/src/__tests__/readInitialOptions.test.ts @@ -14,7 +14,7 @@ describe(readInitialOptions.name, () => { JSON.stringify(inputConfig), ); expect(config).toEqual({...inputConfig, rootDir: process.cwd()}); - expect(configPath).toEqual(null); + expect(configPath).toBeNull(); }); test('should allow deserialized options', async () => { From 8b479f57b8e37adc0afaa32c8fc80d5dffca3e1b Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Sun, 2 Oct 2022 15:25:20 +0200 Subject: [PATCH 15/24] Update packages/jest-config/src/__tests__/readInitialOptions.test.ts Co-authored-by: Tom Mrazauskas --- packages/jest-config/src/__tests__/readInitialOptions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-config/src/__tests__/readInitialOptions.test.ts b/packages/jest-config/src/__tests__/readInitialOptions.test.ts index dcf57a3d912e..4b1038275b6d 100644 --- a/packages/jest-config/src/__tests__/readInitialOptions.test.ts +++ b/packages/jest-config/src/__tests__/readInitialOptions.test.ts @@ -7,7 +7,7 @@ import type {Config} from '@jest/types'; import {readInitialOptions} from '../index'; -describe(readInitialOptions.name, () => { +describe(readInitialOptions, () => { test('should be able to use serialized jest config', async () => { const inputConfig = {jestConfig: 'serialized'}; const {config, configPath} = await readInitialOptions( From 9f01d80ea694cfec03f3600d89f498238b6a7d12 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Sun, 2 Oct 2022 15:26:06 +0200 Subject: [PATCH 16/24] Remove final `.name` --- e2e/__tests__/readInitialOptions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/__tests__/readInitialOptions.test.ts b/e2e/__tests__/readInitialOptions.test.ts index f6b0c56f0db0..14aec27367ce 100644 --- a/e2e/__tests__/readInitialOptions.test.ts +++ b/e2e/__tests__/readInitialOptions.test.ts @@ -11,7 +11,7 @@ function resolveFixture(...pathSegments: Array) { return path.resolve(__dirname, '..', 'read-initial-options', ...pathSegments); } -describe(readInitialOptions.name, () => { +describe(readInitialOptions, () => { test('should read from the cwd by default', async () => { const configFile = resolveFixture('js-config', 'jest.config.js'); const rootDir = resolveFixture('js-config'); From 188b0803631a4e93e273e8ea51d2780899a67371 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Mon, 3 Oct 2022 08:24:35 +0200 Subject: [PATCH 17/24] Read config by proxy for ts and mjs files --- e2e/__tests__/readInitialOptions.test.ts | 18 +++++++++++++++++- .../mjs-config/jest.config.mjs | 9 +++++++++ e2e/read-initial-options/readOptions.js | 5 +++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 e2e/read-initial-options/mjs-config/jest.config.mjs create mode 100644 e2e/read-initial-options/readOptions.js diff --git a/e2e/__tests__/readInitialOptions.test.ts b/e2e/__tests__/readInitialOptions.test.ts index 14aec27367ce..17408d44ff41 100644 --- a/e2e/__tests__/readInitialOptions.test.ts +++ b/e2e/__tests__/readInitialOptions.test.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ import path = require('path'); +import execa = require('execa'); import {readInitialOptions} from 'jest-config'; function resolveFixture(...pathSegments: Array) { @@ -37,10 +38,25 @@ describe(readInitialOptions, () => { test('should read a jest.config.ts file', async () => { const configFile = resolveFixture('ts-config', 'jest.config.ts'); const rootDir = resolveFixture('ts-config'); - const {config, configPath} = await readInitialOptions(configFile); + // Read by proxy, because we're running in a VM and are not allowed to import 'ts-node' directly + const {stdout} = await execa('node', ['../readOptions.js'], { + cwd: rootDir, + }); + const {config, configPath} = JSON.parse(stdout); expect(config).toEqual({jestConfig: 'jest.config.ts', rootDir}); expect(configPath).toEqual(configFile); }); + test('should read a jest.config.mjs file', async () => { + const configFile = resolveFixture('mjs-config', 'jest.config.mjs'); + const rootDir = resolveFixture('mjs-config'); + // Read by proxy, because we're running in a VM and are not allowed to `import` directly + const {stdout} = await execa('node', ['../readOptions.js'], { + cwd: rootDir, + }); + const {config, configPath} = JSON.parse(stdout); + expect(config).toEqual({jestConfig: 'jest.config.mjs', rootDir}); + expect(configPath).toEqual(configFile); + }); test('should read a jest.config.json file', async () => { const configFile = resolveFixture('json-config', 'jest.config.json'); const rootDir = resolveFixture('json-config'); diff --git a/e2e/read-initial-options/mjs-config/jest.config.mjs b/e2e/read-initial-options/mjs-config/jest.config.mjs new file mode 100644 index 000000000000..4875bf59fc0c --- /dev/null +++ b/e2e/read-initial-options/mjs-config/jest.config.mjs @@ -0,0 +1,9 @@ +/** + * 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. + */ +export default { + jestConfig: 'jest.config.mjs', +}; diff --git a/e2e/read-initial-options/readOptions.js b/e2e/read-initial-options/readOptions.js new file mode 100644 index 000000000000..e9338575401e --- /dev/null +++ b/e2e/read-initial-options/readOptions.js @@ -0,0 +1,5 @@ +const {readInitialOptions} = require('jest-config'); +async function readConfig() { + console.log(JSON.stringify(await readInitialOptions(process.argv[2]))); +} +readConfig(); From 5c54a6ae11b464c2a1d152866df403ccd0cc861c Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Mon, 3 Oct 2022 08:25:11 +0200 Subject: [PATCH 18/24] Add copyright header --- e2e/read-initial-options/readOptions.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/e2e/read-initial-options/readOptions.js b/e2e/read-initial-options/readOptions.js index e9338575401e..76dd5badc0ac 100644 --- a/e2e/read-initial-options/readOptions.js +++ b/e2e/read-initial-options/readOptions.js @@ -1,3 +1,9 @@ +/** + * 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. + */ const {readInitialOptions} = require('jest-config'); async function readConfig() { console.log(JSON.stringify(await readInitialOptions(process.argv[2]))); From 53b49ba5aa0402ca4b9c8c0ffa8f26f5caabda38 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Mon, 3 Oct 2022 09:55:24 +0200 Subject: [PATCH 19/24] Read by proxy for every test --- e2e/__tests__/readInitialOptions.test.ts | 73 +++++++++++++++++------- e2e/read-initial-options/readOptions.js | 15 ++++- 2 files changed, 66 insertions(+), 22 deletions(-) diff --git a/e2e/__tests__/readInitialOptions.test.ts b/e2e/__tests__/readInitialOptions.test.ts index 17408d44ff41..b21da575ec06 100644 --- a/e2e/__tests__/readInitialOptions.test.ts +++ b/e2e/__tests__/readInitialOptions.test.ts @@ -6,68 +6,99 @@ */ import path = require('path'); import execa = require('execa'); -import {readInitialOptions} from 'jest-config'; +import type {ReadJestConfigOptions, readInitialOptions} from 'jest-config'; function resolveFixture(...pathSegments: Array) { return path.resolve(__dirname, '..', 'read-initial-options', ...pathSegments); } -describe(readInitialOptions, () => { +interface ProxyReadJestConfigOptions extends ReadJestConfigOptions { + cwd?: string; +} + +/** + * These e2e tests are running via a child process, because we're running in a VM and are not allowed to `import` directly + * It also represents a more real-world example of how to run. + */ +async function proxyReadInitialOptions( + configFile?: string | undefined, + options?: ProxyReadJestConfigOptions, +): ReturnType { + const {stdout} = await execa( + 'node', + [ + require.resolve('../read-initial-options/readOptions.js'), + configFile ?? '', + JSON.stringify(options), + ], + { + cwd: options?.cwd, + }, + ); + return JSON.parse(stdout); +} + +describe('readInitialOptions', () => { test('should read from the cwd by default', async () => { const configFile = resolveFixture('js-config', 'jest.config.js'); const rootDir = resolveFixture('js-config'); - jest.spyOn(process, 'cwd').mockReturnValue(rootDir); - const {config, configPath} = await readInitialOptions(); + const {config, configPath} = await proxyReadInitialOptions(undefined, { + cwd: rootDir, + }); expect(config).toEqual({jestConfig: 'jest.config.js', rootDir}); expect(configPath).toEqual(configFile); }); test('should read a jest.config.js file', async () => { const configFile = resolveFixture('js-config', 'jest.config.js'); const rootDir = resolveFixture('js-config'); - const {config, configPath} = await readInitialOptions(configFile); + const {config, configPath} = await proxyReadInitialOptions(undefined, { + cwd: rootDir, + }); expect(config).toEqual({jestConfig: 'jest.config.js', rootDir}); expect(configPath).toEqual(configFile); }); test('should read a package.json file', async () => { const configFile = resolveFixture('pkg-config', 'package.json'); const rootDir = resolveFixture('pkg-config'); - const {config, configPath} = await readInitialOptions(configFile); + const {config, configPath} = await proxyReadInitialOptions(undefined, { + cwd: rootDir, + }); expect(config).toEqual({jestConfig: 'package.json', rootDir}); expect(configPath).toEqual(configFile); }); test('should read a jest.config.ts file', async () => { const configFile = resolveFixture('ts-config', 'jest.config.ts'); const rootDir = resolveFixture('ts-config'); - // Read by proxy, because we're running in a VM and are not allowed to import 'ts-node' directly - const {stdout} = await execa('node', ['../readOptions.js'], { + const {config, configPath} = await proxyReadInitialOptions(undefined, { cwd: rootDir, }); - const {config, configPath} = JSON.parse(stdout); expect(config).toEqual({jestConfig: 'jest.config.ts', rootDir}); expect(configPath).toEqual(configFile); }); test('should read a jest.config.mjs file', async () => { const configFile = resolveFixture('mjs-config', 'jest.config.mjs'); const rootDir = resolveFixture('mjs-config'); - // Read by proxy, because we're running in a VM and are not allowed to `import` directly - const {stdout} = await execa('node', ['../readOptions.js'], { + const {config, configPath} = await proxyReadInitialOptions(undefined, { cwd: rootDir, }); - const {config, configPath} = JSON.parse(stdout); expect(config).toEqual({jestConfig: 'jest.config.mjs', rootDir}); expect(configPath).toEqual(configFile); }); test('should read a jest.config.json file', async () => { const configFile = resolveFixture('json-config', 'jest.config.json'); const rootDir = resolveFixture('json-config'); - const {config, configPath} = await readInitialOptions(configFile); + const {config, configPath} = await proxyReadInitialOptions(undefined, { + cwd: rootDir, + }); expect(config).toEqual({jestConfig: 'jest.config.json', rootDir}); expect(configPath).toEqual(configFile); }); test('should read a jest config exporting an async function', async () => { const configFile = resolveFixture('async-config', 'jest.config.js'); const rootDir = resolveFixture('async-config'); - const {config, configPath} = await readInitialOptions(configFile); + const {config, configPath} = await proxyReadInitialOptions(undefined, { + cwd: rootDir, + }); expect(config).toEqual({jestConfig: 'async-config', rootDir}); expect(configPath).toEqual(configFile); }); @@ -77,13 +108,14 @@ describe(readInitialOptions, () => { 'json-config', 'jest.config.json', ); - jest.spyOn(process, 'cwd').mockReturnValue(resolveFixture('json-config')); - const {config, configPath} = await readInitialOptions( + const {config, configPath} = await proxyReadInitialOptions( resolveFixture('js-config', 'jest.config.js'), { + cwd: resolveFixture('json-config'), readFromCwd: true, }, ); + expect(config).toEqual({ jestConfig: 'jest.config.json', rootDir: path.dirname(expectedConfigFile), @@ -93,8 +125,9 @@ describe(readInitialOptions, () => { test('should give an error when there are multiple config files', async () => { const cwd = resolveFixture('multiple-config-files'); - jest.spyOn(process, 'cwd').mockReturnValue(cwd); - const error: Error = await readInitialOptions().catch(error => error); + const error: Error = await proxyReadInitialOptions(undefined, {cwd}).catch( + error => error, + ); expect(error.message).toContain('Multiple configurations found'); expect(error.message).toContain('multiple-config-files/jest.config.js'); expect(error.message).toContain('multiple-config-files/jest.config.json'); @@ -102,8 +135,8 @@ describe(readInitialOptions, () => { test('should be able to ignore multiple config files error', async () => { const cwd = resolveFixture('multiple-config-files'); - jest.spyOn(process, 'cwd').mockReturnValue(cwd); - const {config, configPath} = await readInitialOptions(undefined, { + const {config, configPath} = await proxyReadInitialOptions(undefined, { + cwd, skipMultipleConfigError: true, }); expect(config).toEqual({ diff --git a/e2e/read-initial-options/readOptions.js b/e2e/read-initial-options/readOptions.js index 76dd5badc0ac..4bcc661dc079 100644 --- a/e2e/read-initial-options/readOptions.js +++ b/e2e/read-initial-options/readOptions.js @@ -6,6 +6,17 @@ */ const {readInitialOptions} = require('jest-config'); async function readConfig() { - console.log(JSON.stringify(await readInitialOptions(process.argv[2]))); + let config = process.argv[2]; + let options = undefined; + if (config === '') { + config = undefined; + } + if (process.argv[3]) { + options = JSON.parse(process.argv[3]); + } + console.log(JSON.stringify(await readInitialOptions(config, options))); } -readConfig(); +readConfig().catch(err => { + console.error(err); + process.exitCode = 1; +}); From 34e1a32010d89dda9d96d348b27436638fd55bd2 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Mon, 3 Oct 2022 10:22:10 +0200 Subject: [PATCH 20/24] Update e2e/__tests__/readInitialOptions.test.ts Co-authored-by: Simen Bekkhus --- e2e/__tests__/readInitialOptions.test.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/e2e/__tests__/readInitialOptions.test.ts b/e2e/__tests__/readInitialOptions.test.ts index b21da575ec06..1ad21a5994f8 100644 --- a/e2e/__tests__/readInitialOptions.test.ts +++ b/e2e/__tests__/readInitialOptions.test.ts @@ -31,9 +31,7 @@ async function proxyReadInitialOptions( configFile ?? '', JSON.stringify(options), ], - { - cwd: options?.cwd, - }, + {cwd: options?.cwd}, ); return JSON.parse(stdout); } From 3cf1f21187c64020031727fdd2600f4eb33db6b1 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Mon, 3 Oct 2022 10:24:55 +0200 Subject: [PATCH 21/24] Refactor if/else -> if --- packages/jest-config/src/index.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/jest-config/src/index.ts b/packages/jest-config/src/index.ts index 06613c1b0b96..9058f9f8b520 100644 --- a/packages/jest-config/src/index.ts +++ b/packages/jest-config/src/index.ts @@ -279,7 +279,8 @@ export async function readInitialOptions( 'Jest: Cannot use configuration as an object without a file path.', ); } - } else if (isJSONString(config)) { + } + if (isJSONString(config)) { // A JSON string was passed to `--config` argument and we can parse it // and use as is. let initialOptions; @@ -296,22 +297,22 @@ export async function readInitialOptions( return {config: initialOptions, configPath: null}; // A string passed to `--config`, which is either a direct path to the config // or a path to directory containing `package.json`, `jest.config.js` or `jest.config.ts` - } else if (!readFromCwd && typeof config == 'string') { + } + if (!readFromCwd && typeof config == 'string') { const configPath = resolveConfigPath( config, process.cwd(), skipMultipleConfigError, ); return {config: await readConfigFileAndSetRootDir(configPath), configPath}; - } else { - // Otherwise just try to find config in the current rootDir. - const configPath = resolveConfigPath( - packageRootOrConfig, - process.cwd(), - skipMultipleConfigError, - ); - return {config: await readConfigFileAndSetRootDir(configPath), configPath}; } + // Otherwise just try to find config in the current rootDir. + const configPath = resolveConfigPath( + packageRootOrConfig, + process.cwd(), + skipMultipleConfigError, + ); + return {config: await readConfigFileAndSetRootDir(configPath), configPath}; } // Possible scenarios: From 3d535ba28e698c59db60d61c8c48eeb6ca246f93 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Mon, 3 Oct 2022 13:28:06 +0200 Subject: [PATCH 22/24] refactor: move some comments around --- packages/jest-config/src/index.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/jest-config/src/index.ts b/packages/jest-config/src/index.ts index 9058f9f8b520..2fd7a3755723 100644 --- a/packages/jest-config/src/index.ts +++ b/packages/jest-config/src/index.ts @@ -281,24 +281,22 @@ export async function readInitialOptions( } } if (isJSONString(config)) { - // A JSON string was passed to `--config` argument and we can parse it - // and use as is. - let initialOptions; try { - initialOptions = JSON.parse(config); + // A JSON string was passed to `--config` argument and we can parse it + // and use as is. + const initialOptions = JSON.parse(config); + // NOTE: we might need to resolve this dir to an absolute path in the future + initialOptions.rootDir = initialOptions.rootDir || packageRootOrConfig; + return {config: initialOptions, configPath: null}; } catch { throw new Error( 'There was an error while parsing the `--config` argument as a JSON string.', ); } - - // NOTE: we might need to resolve this dir to an absolute path in the future - initialOptions.rootDir = initialOptions.rootDir || packageRootOrConfig; - return {config: initialOptions, configPath: null}; - // A string passed to `--config`, which is either a direct path to the config - // or a path to directory containing `package.json`, `jest.config.js` or `jest.config.ts` } if (!readFromCwd && typeof config == 'string') { + // A string passed to `--config`, which is either a direct path to the config + // or a path to directory containing `package.json`, `jest.config.js` or `jest.config.ts` const configPath = resolveConfigPath( config, process.cwd(), From 5ab7237212bb2684a72841f6c21d983d23f5bcfb Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Mon, 3 Oct 2022 13:29:10 +0200 Subject: [PATCH 23/24] Update e2e/__tests__/readInitialOptions.test.ts Co-authored-by: Simen Bekkhus --- e2e/__tests__/readInitialOptions.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/__tests__/readInitialOptions.test.ts b/e2e/__tests__/readInitialOptions.test.ts index 1ad21a5994f8..9d2ba417eb4f 100644 --- a/e2e/__tests__/readInitialOptions.test.ts +++ b/e2e/__tests__/readInitialOptions.test.ts @@ -21,8 +21,8 @@ interface ProxyReadJestConfigOptions extends ReadJestConfigOptions { * It also represents a more real-world example of how to run. */ async function proxyReadInitialOptions( - configFile?: string | undefined, - options?: ProxyReadJestConfigOptions, + configFile: string | undefined, + options: ProxyReadJestConfigOptions, ): ReturnType { const {stdout} = await execa( 'node', From 2dfe6ef5fc1325ef14f183c3c4737eca3a402b5f Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Mon, 3 Oct 2022 13:32:25 +0200 Subject: [PATCH 24/24] Require argv to be supplied --- packages/jest-config/src/__tests__/readConfig.test.ts | 3 +-- packages/jest-config/src/index.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/jest-config/src/__tests__/readConfig.test.ts b/packages/jest-config/src/__tests__/readConfig.test.ts index 1452e8355171..09cb362ecf49 100644 --- a/packages/jest-config/src/__tests__/readConfig.test.ts +++ b/packages/jest-config/src/__tests__/readConfig.test.ts @@ -10,8 +10,7 @@ import {readConfig} from '../index'; test('readConfig() throws when an object is passed without a file path', async () => { await expect( readConfig( - // @ts-expect-error - null /* argv */, + {$0: '', _: []}, {} /* packageRootOrConfig */, false /* skipArgvConfigOption */, null /* parentConfigPath */, diff --git a/packages/jest-config/src/index.ts b/packages/jest-config/src/index.ts index 2fd7a3755723..f8fb20d3590a 100644 --- a/packages/jest-config/src/index.ts +++ b/packages/jest-config/src/index.ts @@ -44,7 +44,7 @@ export async function readConfig( skipMultipleConfigError = false, ): Promise { const {config: initialOptions, configPath} = await readInitialOptions( - argv?.config, + argv.config, { packageRootOrConfig, parentConfigDirname,