Skip to content

Commit

Permalink
Strict and explicit config resolution logic (#4122)
Browse files Browse the repository at this point in the history
* Stricter config resolution logic

* glob projects
  • Loading branch information
aaronabramov authored Jul 27, 2017
1 parent 65fb838 commit 2c16d1e
Show file tree
Hide file tree
Showing 12 changed files with 417 additions and 162 deletions.
95 changes: 95 additions & 0 deletions integration_tests/__tests__/multi_project_runner.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,98 @@ test('can pass projects or global config', () => {
expect(result1.summary).toBe(result2.summary);
expect(sortLines(result1.rest)).toBe(sortLines(result2.rest));
});

test('resolves projects and their <rootDir> properly', () => {
writeFiles(DIR, {
'.watchmanconfig': '',
'package.json': JSON.stringify({
jest: {
projects: [
'project1.conf.json',
'<rootDir>/project2/project2.conf.json',
],
},
}),
'project1.conf.json': JSON.stringify({
name: 'project1',
rootDir: './project1',
// root dir should be this project's directory
setupFiles: ['<rootDir>/project1_setup.js'],
testEnvironment: 'node',
}),
'project1/__tests__/test.test.js': `test('project1', () => expect(global.project1).toBe(true))`,
'project1/project1_setup.js': 'global.project1 = true;',
'project2/__tests__/test.test.js': `test('project2', () => expect(global.project2).toBe(true))`,
'project2/project2.conf.json': JSON.stringify({
name: 'project2',
rootDir: '../', // root dir is set to the top level
setupFiles: ['<rootDir>/project2/project2_setup.js'], // rootDir shold be of the
testEnvironment: 'node',
testPathIgnorePatterns: ['project1'],
}),
'project2/project2_setup.js': 'global.project2 = true;',
});

let stderr;
({stderr} = runJest(DIR));

expect(stderr).toMatch('Ran all test suites in 2 projects.');
expect(stderr).toMatch(' PASS project1/__tests__/test.test.js');
expect(stderr).toMatch(' PASS project2/__tests__/test.test.js');

// Use globs
writeFiles(DIR, {
'dir1/random_file': '',
'dir2/random_file': '',
'package.json': JSON.stringify({
jest: {
projects: ['**/*.conf.json'],
},
}),
});

({stderr} = runJest(DIR));
expect(stderr).toMatch('Ran all test suites in 2 projects.');
expect(stderr).toMatch(' PASS project1/__tests__/test.test.js');
expect(stderr).toMatch(' PASS project2/__tests__/test.test.js');

// Include two projects that will resolve to the same config
writeFiles(DIR, {
'dir1/random_file': '',
'dir2/random_file': '',
'package.json': JSON.stringify({
jest: {
projects: [
'dir1',
'dir2',
'project1.conf.json',
'<rootDir>/project2/project2.conf.json',
],
},
}),
});

({stderr} = runJest(DIR));
expect(stderr).toMatch(
/One or more specified projects share the same config file/,
);

// praject with a directory/file that does not exist
writeFiles(DIR, {
'package.json': JSON.stringify({
jest: {
projects: [
'banana',
'project1.conf.json',
'<rootDir>/project2/project2.conf.json',
],
},
}),
});

({stderr} = runJest(DIR));
expect(stderr).toMatch(
`Can't find a root directory while resolving a config file path.`,
);
expect(stderr).toMatch(/banana/);
});
24 changes: 24 additions & 0 deletions packages/jest-cli/src/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,29 @@ const _printVersionAndExit = outputStream => {
process.exit(0);
};

const _ensureNoDuplicateConfigs = (parsedConfigs, projects) => {
const configPathSet = new Set();

for (const {configPath} of parsedConfigs) {
if (configPathSet.has(configPath)) {
let message =
'One or more specified projects share the same config file\n';

parsedConfigs.forEach(({configPath}, index) => {
message =
message +
'\nProject: "' +
projects[index] +
'"\nConfig: "' +
String(configPath) +
'"';
});
throw new Error(message);
}
configPathSet.add(configPath);
}
};

// Possible scenarios:
// 1. jest --config config.json
// 2. jest --projects p1 p2
Expand Down Expand Up @@ -203,6 +226,7 @@ const _getConfigs = (

if (projects.length > 1) {
const parsedConfigs = projects.map(root => readConfig(argv, root, true));
_ensureNoDuplicateConfigs(parsedConfigs, projects);
configs = parsedConfigs.map(({config}) => config);
if (!hasDeprecationWarnings) {
hasDeprecationWarnings = parsedConfigs.some(
Expand Down
34 changes: 0 additions & 34 deletions packages/jest-config/src/__tests__/normalize.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -915,37 +915,3 @@ describe('preset without setupFiles', () => {
);
});
});

describe('projects', () => {
beforeEach(() => {
jest.resetModules();

const Resolver = require('jest-resolve');
Resolver.findNodeModule = findNodeModule;
});

test('resolves projects correctly', () => {
const root = '/path/to/test';
const glob = require('glob');
glob.sync = jest.fn(
pattern =>
pattern.indexOf('/examples/') !== -1
? [root + '/examples/async', root + '/examples/snapshot']
: [pattern],
);
const normalize = require('../normalize');
const {options} = normalize(
{
projects: ['<rootDir>', '<rootDir>/examples/*'],
rootDir: root,
},
{},
);

expect(options.projects).toEqual([
root,
root + '/examples/async',
root + '/examples/snapshot',
]);
});
});
97 changes: 97 additions & 0 deletions packages/jest-config/src/__tests__/resolve_config_path.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/

import {cleanup, writeFiles} from '../../../../integration_tests/utils';
import os from 'os';
import path from 'path';
import resolveConfigPath from '../resolve_config_path';

import skipOnWindows from '../../../../scripts/skip_on_windows';

skipOnWindows.suite();

const DIR = path.resolve(os.tmpdir(), 'resolve_config_path_test');
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/;

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

test('file path', () => {
const relativeConfigPath = 'a/b/c/my_config.js';
const absoluteConfigPath = path.resolve(DIR, relativeConfigPath);

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

// absolute
expect(resolveConfigPath(absoluteConfigPath, DIR)).toBe(absoluteConfigPath);
expect(() => resolveConfigPath('/does_not_exist', DIR)).toThrowError(
NO_ROOT_DIR_ERROR_PATTERN,
);

// relative
expect(resolveConfigPath(relativeConfigPath, DIR)).toBe(absoluteConfigPath);
expect(() => resolveConfigPath('does_not_exist', DIR)).toThrowError(
NO_ROOT_DIR_ERROR_PATTERN,
);
});

test('directory path', () => {
const relativePackageJsonPath = 'a/b/c/package.json';
const absolutePackageJsonPath = path.resolve(DIR, relativePackageJsonPath);
const relativeJestConfigPath = 'a/b/c/jest.config.js';
const absoluteJestConfigPath = path.resolve(DIR, relativeJestConfigPath);

writeFiles(DIR, {'a/b/c/some_random_file.js': ''});

// no configs yet. should throw
expect(() =>
// absolute
resolveConfigPath(path.dirname(absoluteJestConfigPath), DIR),
).toThrowError(ERROR_PATTERN);

expect(() =>
// relative
resolveConfigPath(path.dirname(relativeJestConfigPath), DIR),
).toThrowError(ERROR_PATTERN);

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

// absolute
expect(resolveConfigPath(path.dirname(absolutePackageJsonPath), DIR)).toBe(
absolutePackageJsonPath,
);

// relative
expect(resolveConfigPath(path.dirname(relativePackageJsonPath), DIR)).toBe(
absolutePackageJsonPath,
);

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

// jest.config.js takes presedence

// absolute
expect(resolveConfigPath(path.dirname(absolutePackageJsonPath), DIR)).toBe(
absoluteJestConfigPath,
);

// relative
expect(resolveConfigPath(path.dirname(relativePackageJsonPath), DIR)).toBe(
absoluteJestConfigPath,
);

expect(() => {
resolveConfigPath(
path.join(path.dirname(relativePackageJsonPath), 'j/x/b/m/'),
DIR,
);
}).toThrowError(NO_ROOT_DIR_ERROR_PATTERN);
});
2 changes: 2 additions & 0 deletions packages/jest-config/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ import path from 'path';
exports.NODE_MODULES = path.sep + 'node_modules' + path.sep;
exports.DEFAULT_JS_PATTERN = '^.+\\.jsx?$';
exports.DEFAULT_REPORTER_LABEL = 'default';
exports.PACKAGE_JSON = 'package.json';
exports.JEST_CONFIG = 'jest.config.js';
86 changes: 0 additions & 86 deletions packages/jest-config/src/find_config.js

This file was deleted.

Loading

0 comments on commit 2c16d1e

Please sign in to comment.