Skip to content

Commit

Permalink
feat(jest-config): Throw an error instead of showing a warning if mul…
Browse files Browse the repository at this point in the history
…tiple configs are used
  • Loading branch information
TrickyPi committed Feb 28, 2022
1 parent c2872aa commit 83e9127
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 94 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Features

- `[jest-config]` Throw an error instead of showing a warning if multiple configs are used ([#12506](https://github.com/facebook/jest/pull/12506))
- `[babel-jest]` Export `createTransformer` function ([#12399](https://github.com/facebook/jest/pull/12399))
- `[expect]` Expose `AsymmetricMatchers`, `MatcherFunction` and `MatcherFunctionWithState` interfaces ([#12363](https://github.com/facebook/jest/pull/12363), [#12376](https://github.com/facebook/jest/pull/12376))
- `[jest-circus, jest-jasmine2]` Allowed classes and functions as `describe` and `it`/`test` names ([#12484](https://github.com/facebook/jest/pull/12484))
Expand Down
22 changes: 9 additions & 13 deletions e2e/__tests__/__snapshots__/multipleConfigs.ts.snap
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`multiple configs will warn 1`] = `
"● Multiple configurations found:
exports[`multiple configs will throw error 1`] = `
"Error: ● Multiple configurations found:
* <rootDir>/e2e/multiple-configs/jest.config.js
* <rootDir>/e2e/multiple-configs/jest.config.json
* \`jest\` key in <rootDir>/e2e/multiple-configs/package.json
Expand All @@ -12,14 +12,10 @@ exports[`multiple configs will warn 1`] = `
Configuration Documentation:
https://jestjs.io/docs/configuration.html
PASS Config from js file __tests__/test.js
✓ dummy test"
`;
exports[`multiple configs will warn 2`] = `
"Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: <<REPLACED>>
Ran all test suites."
`;
at resolveConfigPathByTraversing (<rootDir>/packages/jest-config/build/resolveConfigPath.js:166:11)
at resolveConfigPath (<rootDir>/packages/jest-config/build/resolveConfigPath.js:141:10)
at readConfig (<rootDir>/packages/jest-config/build/index.js:211:49)
at readConfigs (<rootDir>/packages/jest-config/build/index.js:403:32)
at runCLI (<rootDir>/packages/jest-core/build/cli/index.js:133:29)
at Object.run (<rootDir>/packages/jest-cli/build/cli/index.js:155:62)"
`;
16 changes: 5 additions & 11 deletions e2e/__tests__/multipleConfigs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,30 @@

import * as path from 'path';
import slash = require('slash');
import {extractSummary} from '../Utils';
import runJest from '../runJest';

const MULTIPLE_CONFIGS_WARNING_TEXT = 'Multiple configurations found';

test('multiple configs will warn', () => {
test('multiple configs will throw error', () => {
const rootDir = slash(path.resolve(__dirname, '../..'));
const {exitCode, stderr} = runJest('multiple-configs', [], {
skipPkgJsonCheck: true,
});

expect(exitCode).toBe(0);
expect(exitCode).toBe(1);
expect(stderr).toContain(MULTIPLE_CONFIGS_WARNING_TEXT);

const cleanStdErr = stderr.replace(new RegExp(rootDir, 'g'), '<rootDir>');
const {rest, summary} = extractSummary(cleanStdErr);

expect(rest).toMatchSnapshot();
expect(summary).toMatchSnapshot();
expect(cleanStdErr).toMatchSnapshot();
});

test('multiple configs warning can be suppressed by using --config', () => {
const {exitCode, stderr} = runJest(
test('multiple configs error can be suppressed by using --config', () => {
const {exitCode} = runJest(
'multiple-configs',
['--config', 'jest.config.json'],
{
skipPkgJsonCheck: true,
},
);

expect(exitCode).toBe(0);
expect(stderr).not.toContain(MULTIPLE_CONFIGS_WARNING_TEXT);
});
53 changes: 9 additions & 44 deletions packages/jest-config/src/__tests__/resolveConfigPath.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,6 @@ const ERROR_PATTERN = /Could not find a config file based on provided values/;
const NO_ROOT_DIR_ERROR_PATTERN = /Can't find a root directory/;
const MULTIPLE_CONFIGS_ERROR_PATTERN = /Multiple configurations found/;

const mockConsoleWarn = () => {
jest.spyOn(console, 'warn');
const mockedConsoleWarn = console.warn as jest.Mock<void, Array<any>>;

// We will mock console.warn because it would produce a lot of noise in the tests
mockedConsoleWarn.mockImplementation(() => {});

return mockedConsoleWarn;
};

beforeEach(() => cleanup(DIR));
afterEach(() => cleanup(DIR));

Expand Down Expand Up @@ -56,8 +46,6 @@ describe.each(JEST_CONFIG_EXT_ORDER.slice(0))(
});

test(`directory path with "${extension}"`, () => {
const mockedConsoleWarn = mockConsoleWarn();

const relativePackageJsonPath = 'a/b/c/package.json';
const absolutePackageJsonPath = path.resolve(
DIR,
Expand All @@ -81,7 +69,6 @@ describe.each(JEST_CONFIG_EXT_ORDER.slice(0))(

writeFiles(DIR, {[relativePackageJsonPath]: ''});

mockedConsoleWarn.mockClear();
// absolute
expect(
resolveConfigPath(path.dirname(absolutePackageJsonPath), DIR),
Expand All @@ -91,12 +78,10 @@ describe.each(JEST_CONFIG_EXT_ORDER.slice(0))(
expect(
resolveConfigPath(path.dirname(relativePackageJsonPath), DIR),
).toBe(absolutePackageJsonPath);
expect(mockedConsoleWarn).not.toBeCalled();

// jest.config.js takes precedence
writeFiles(DIR, {[relativeJestConfigPath]: ''});

mockedConsoleWarn.mockClear();
// absolute
expect(
resolveConfigPath(path.dirname(absolutePackageJsonPath), DIR),
Expand All @@ -106,30 +91,19 @@ describe.each(JEST_CONFIG_EXT_ORDER.slice(0))(
expect(
resolveConfigPath(path.dirname(relativePackageJsonPath), DIR),
).toBe(absoluteJestConfigPath);
expect(mockedConsoleWarn).not.toBeCalled();

// jest.config.js and package.json with 'jest' cannot be used together
writeFiles(DIR, {[relativePackageJsonPath]: JSON.stringify({jest: {}})});

// absolute
mockedConsoleWarn.mockClear();
expect(
expect(() =>
resolveConfigPath(path.dirname(absolutePackageJsonPath), DIR),
).toBe(absoluteJestConfigPath);
expect(mockedConsoleWarn).toBeCalledTimes(1);
expect(mockedConsoleWarn.mock.calls[0].join()).toMatch(
MULTIPLE_CONFIGS_ERROR_PATTERN,
);
).toThrowError(MULTIPLE_CONFIGS_ERROR_PATTERN);

// relative
mockedConsoleWarn.mockClear();
expect(
expect(() =>
resolveConfigPath(path.dirname(relativePackageJsonPath), DIR),
).toBe(absoluteJestConfigPath);
expect(mockedConsoleWarn).toBeCalledTimes(1);
expect(mockedConsoleWarn.mock.calls[0].join()).toMatch(
MULTIPLE_CONFIGS_ERROR_PATTERN,
);
).toThrowError(MULTIPLE_CONFIGS_ERROR_PATTERN);

expect(() => {
resolveConfigPath(
Expand All @@ -146,8 +120,7 @@ const pickPairsWithSameOrder = <T>(array: ReadonlyArray<T>) =>
.map((value1, idx, arr) =>
arr.slice(idx + 1).map(value2 => [value1, value2]),
)
// TODO: use .flat() when we drop Node 10
.reduce((acc, val) => acc.concat(val), []);
.flat();

test('pickPairsWithSameOrder', () => {
expect(pickPairsWithSameOrder([1, 2, 3])).toStrictEqual([
Expand All @@ -158,11 +131,9 @@ test('pickPairsWithSameOrder', () => {
});

describe.each(pickPairsWithSameOrder(JEST_CONFIG_EXT_ORDER))(
'Using multiple configs shows warning',
'Using multiple configs shows error',
(extension1, extension2) => {
test(`Using jest.config${extension1} and jest.config${extension2} shows warning`, () => {
const mockedConsoleWarn = mockConsoleWarn();

test(`Using jest.config${extension1} and jest.config${extension2} shows error`, () => {
const relativeJestConfigPaths = [
`a/b/c/jest.config${extension1}`,
`a/b/c/jest.config${extension2}`,
Expand All @@ -173,15 +144,9 @@ describe.each(pickPairsWithSameOrder(JEST_CONFIG_EXT_ORDER))(
[relativeJestConfigPaths[1]]: '',
});

// multiple configs here, should print warning
mockedConsoleWarn.mockClear();
expect(
expect(() =>
resolveConfigPath(path.dirname(relativeJestConfigPaths[0]), DIR),
).toBe(path.resolve(DIR, relativeJestConfigPaths[0]));
expect(mockedConsoleWarn).toBeCalledTimes(1);
expect(mockedConsoleWarn.mock.calls[0].join()).toMatch(
MULTIPLE_CONFIGS_ERROR_PATTERN,
);
).toThrowError(MULTIPLE_CONFIGS_ERROR_PATTERN);
});
},
);
6 changes: 3 additions & 3 deletions packages/jest-config/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export async function readConfig(
skipArgvConfigOption?: boolean,
parentConfigDirname?: string | null,
projectIndex = Infinity,
skipMultipleConfigWarning = false,
skipMultipleConfigError = false,
): Promise<ReadConfig> {
let rawOptions: Config.InitialOptions;
let configPath = null;
Expand Down Expand Up @@ -78,15 +78,15 @@ export async function readConfig(
configPath = resolveConfigPath(
argv.config,
process.cwd(),
skipMultipleConfigWarning,
skipMultipleConfigError,
);
rawOptions = await readConfigFileAndSetRootDir(configPath);
} else {
// Otherwise just try to find config in the current rootDir.
configPath = resolveConfigPath(
packageRootOrConfig,
process.cwd(),
skipMultipleConfigWarning,
skipMultipleConfigError,
);
rawOptions = await readConfigFileAndSetRootDir(configPath);
}
Expand Down
44 changes: 21 additions & 23 deletions packages/jest-config/src/resolveConfigPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const getConfigFilename = (ext: string) => JEST_CONFIG_BASE_NAME + ext;
export default function resolveConfigPath(
pathToResolve: string,
cwd: string,
skipMultipleConfigWarning = false,
skipMultipleConfigError = false,
): string {
if (!path.isAbsolute(cwd)) {
throw new Error(`"cwd" must be an absolute path. cwd: ${cwd}`);
Expand Down Expand Up @@ -58,15 +58,15 @@ export default function resolveConfigPath(
absolutePath,
pathToResolve,
cwd,
skipMultipleConfigWarning,
skipMultipleConfigError,
);
}

const resolveConfigPathByTraversing = (
pathToResolve: string,
initialPath: string,
cwd: string,
skipMultipleConfigWarning: boolean,
skipMultipleConfigError: boolean,
): string => {
const configFiles = JEST_CONFIG_EXT_ORDER.map(ext =>
path.resolve(pathToResolve, getConfigFilename(ext)),
Expand All @@ -77,8 +77,8 @@ const resolveConfigPathByTraversing = (
configFiles.push(packageJson);
}

if (!skipMultipleConfigWarning && configFiles.length > 1) {
console.warn(makeMultipleConfigsWarning(configFiles));
if (!skipMultipleConfigError && configFiles.length > 1) {
throw new Error(makeMultipleConfigsErrorMessage(configFiles));
}

if (configFiles.length > 0 || packageJson) {
Expand All @@ -96,7 +96,7 @@ const resolveConfigPathByTraversing = (
path.dirname(pathToResolve),
initialPath,
cwd,
skipMultipleConfigWarning,
skipMultipleConfigError,
);
};

Expand Down Expand Up @@ -137,20 +137,18 @@ function extraIfPackageJson(configPath: string) {
return '';
}

const makeMultipleConfigsWarning = (configPaths: Array<string>) =>
chalk.yellow(
[
chalk.bold('\u25cf Multiple configurations found:'),
...configPaths.map(
configPath =>
` * ${extraIfPackageJson(configPath)}${slash(configPath)}`,
),
'',
' Implicit config resolution does not allow multiple configuration files.',
' Either remove unused config files or select one explicitly with `--config`.',
'',
' Configuration Documentation:',
' https://jestjs.io/docs/configuration.html',
'',
].join('\n'),
);
const makeMultipleConfigsErrorMessage = (configPaths: Array<string>) =>
[
chalk.bold('\u25cf Multiple configurations found:'),
...configPaths.map(
configPath =>
` * ${extraIfPackageJson(configPath)}${slash(configPath)}`,
),
'',
' Implicit config resolution does not allow multiple configuration files.',
' Either remove unused config files or select one explicitly with `--config`.',
'',
' Configuration Documentation:',
' https://jestjs.io/docs/configuration.html',
'',
].join('\n');

0 comments on commit 83e9127

Please sign in to comment.