Skip to content

Commit

Permalink
feat(config): add read initial options helper (#13356)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicojs committed Oct 3, 2022
1 parent 8219820 commit d78baab
Show file tree
Hide file tree
Showing 14 changed files with 352 additions and 48 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,7 @@

### 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))

Expand Down
148 changes: 148 additions & 0 deletions e2e/__tests__/readInitialOptions.test.ts
@@ -0,0 +1,148 @@
/**
* 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 execa = require('execa');
import type {ReadJestConfigOptions, readInitialOptions} from 'jest-config';

function resolveFixture(...pathSegments: Array<string>) {
return path.resolve(__dirname, '..', 'read-initial-options', ...pathSegments);
}

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<typeof readInitialOptions> {
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');
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 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 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');
const {config, configPath} = await proxyReadInitialOptions(undefined, {
cwd: rootDir,
});
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');
const {config, configPath} = await proxyReadInitialOptions(undefined, {
cwd: rootDir,
});
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 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 proxyReadInitialOptions(undefined, {
cwd: rootDir,
});
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',
);
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),
});
expect(configPath).toEqual(expectedConfigFile);
});

test('should give an error when there are multiple config files', async () => {
const cwd = resolveFixture('multiple-config-files');
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');
});

test('should be able to ignore multiple config files error', async () => {
const cwd = resolveFixture('multiple-config-files');
const {config, configPath} = await proxyReadInitialOptions(undefined, {
cwd,
skipMultipleConfigError: true,
});
expect(config).toEqual({
jestConfig: 'jest.config.js',
rootDir: resolveFixture('multiple-config-files'),
});
expect(configPath).toEqual(
resolveFixture('multiple-config-files', 'jest.config.js'),
);
});
});
11 changes: 11 additions & 0 deletions e2e/read-initial-options/async-config/jest.config.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.
*/
module.exports = async function () {
return {
jestConfig: 'async-config',
};
};
9 changes: 9 additions & 0 deletions e2e/read-initial-options/js-config/jest.config.js
@@ -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.
*/
module.exports = {
jestConfig: 'jest.config.js',
};
3 changes: 3 additions & 0 deletions e2e/read-initial-options/json-config/jest.config.json
@@ -0,0 +1,3 @@
{
"jestConfig": "jest.config.json"
}
9 changes: 9 additions & 0 deletions 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',
};
9 changes: 9 additions & 0 deletions e2e/read-initial-options/multiple-config-files/jest.config.js
@@ -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.
*/
module.exports = {
jestConfig: 'jest.config.js',
};
@@ -0,0 +1,3 @@
{
"jestConfig": "jest.config.json"
}
5 changes: 5 additions & 0 deletions e2e/read-initial-options/pkg-config/package.json
@@ -0,0 +1,5 @@
{
"jest": {
"jestConfig": "package.json"
}
}
22 changes: 22 additions & 0 deletions e2e/read-initial-options/readOptions.js
@@ -0,0 +1,22 @@
/**
* 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() {
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().catch(err => {
console.error(err);
process.exitCode = 1;
});
9 changes: 9 additions & 0 deletions e2e/read-initial-options/ts-config/jest.config.ts
@@ -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.ts',
};
3 changes: 1 addition & 2 deletions packages/jest-config/src/__tests__/readConfig.test.ts
Expand Up @@ -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 */,
Expand Down
30 changes: 30 additions & 0 deletions packages/jest-config/src/__tests__/readInitialOptions.test.ts
@@ -0,0 +1,30 @@
/**
* 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 type {Config} from '@jest/types';
import {readInitialOptions} from '../index';

describe(readInitialOptions, () => {
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).toBeNull();
});

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).toBeNull();
});
// Note: actual file reading is tested in e2e test
});

0 comments on commit d78baab

Please sign in to comment.