Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## master

### Features

- `[jest-validate]` Add `recursive` and `blacklist` options for deep config checks ([#6802](https://github.com/facebook/jest/pull/6802))

### Fixes

- `[jest-config]` Fix `--coverage` with `--findRelatedTests` overwriting `collectCoverageFrom` options ([#6736](https://github.com/facebook/jest/pull/6736))
Expand Down
7 changes: 7 additions & 0 deletions packages/jest-config/src/normalize.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,9 +332,16 @@ const showTestPathPatternError = (testPathPattern: string) => {

export default function normalize(options: InitialOptions, argv: Argv) {
const {hasDeprecationWarnings} = validate(options, {
blacklist: [
'moduleNameMapper',
'transform',
'globals',
'collectCoverageOnlyFrom',
],
comment: DOCUMENTATION_NOTE,
deprecatedConfig: DEPRECATED_CONFIG,
exampleConfig: VALID_CONFIG,
recursive: true,
});

options = normalizePreprocessor(
Expand Down
6 changes: 6 additions & 0 deletions packages/jest-config/src/valid_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ export default ({
coverageThreshold: {
global: {
branches: 50,
functions: 100,
lines: 100,
statements: 100,
},
},
displayName: 'project-name',
Expand All @@ -46,6 +49,9 @@ export default ({
globalTeardown: 'teardown.js',
globals: {},
haste: {
defaultPlatform: 'ios',
hasteImplModulePath: '<rootDir>/haste_impl.js',
platforms: ['ios', 'android'],
providesModuleNodeModules: ['react', 'react-native'],
},
json: false,
Expand Down
9 changes: 8 additions & 1 deletion packages/jest-validate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Where `ValidationOptions` are:

```js
type ValidationOptions = {
blacklist?: Array<string>,
comment?: string,
condition?: (option: any, validOption: any) => boolean,
deprecate?: (
Expand All @@ -34,6 +35,7 @@ type ValidationOptions = {
options: ValidationOptions,
) => void,
exampleConfig: Object,
recursive?: boolean,
title?: Title,
unknown?: (
config: Object,
Expand All @@ -60,11 +62,13 @@ Almost anything can be overwritten to suite your needs.

### Options

- `blacklist` – optional array of string keyPaths that should be excluded from deep (recursive) validation.
- `comment` – optional string to be rendered below error/warning message.
- `condition` – an optional function with validation condition.
- `deprecate`, `error`, `unknown` – optional functions responsible for displaying warning and error messages.
- `deprecatedConfig` – optional object with deprecated config keys.
- `exampleConfig` – the only **required** option with configuration against which you'd like to test.
- `recursive` - optional boolean determining whether recursively compare `exampleConfig` to `config`.
- `title` – optional object of titles for errors and messages.

You will find examples of `condition`, `deprecate`, `error`, `unknown`, and `deprecatedConfig` inside source of this repository, named respectively.
Expand All @@ -84,6 +88,7 @@ validate(config, {
comment: ' Documentation: http://custom-docs.com',
deprecatedConfig,
exampleConfig,
recursive: true,
title: {
deprecation: 'Custom Deprecation',
// leaving 'error' and 'warning' as default
Expand Down Expand Up @@ -116,7 +121,9 @@ This will output:

Example:
{
"transform": {"^.+\\.js$": "<rootDir>/preprocessor.js"}
"transform": {
"^.+\\.js$": "<rootDir>/preprocessor.js"
}
}

Documentation: http://custom-docs.com
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ exports[`pretty prints valid config for Array 1`] = `
<red></>
<red> Example:</>
<red> {</>
<red> <bold>\\"coverageReporters\\"</>: <bold>[\\"json\\", \\"text\\", \\"lcov\\", \\"clover\\"]</></>
<red> <bold>\\"coverageReporters\\"</>: <bold>[</></>
<red><bold> \\"json\\",</></>
<red><bold> \\"text\\",</></>
<red><bold> \\"lcov\\",</></>
<red><bold> \\"clover\\"</></>
<red><bold> ]</></>
<red> }</>
<red></>"
`;
Expand Down Expand Up @@ -77,7 +82,12 @@ exports[`pretty prints valid config for Object 1`] = `
<red></>
<red> Example:</>
<red> {</>
<red> <bold>\\"haste\\"</>: <bold>{\\"providesModuleNodeModules\\": [\\"react\\", \\"react-native\\"]}</></>
<red> <bold>\\"haste\\"</>: <bold>{</></>
<red><bold> \\"providesModuleNodeModules\\": [</></>
<red><bold> \\"react\\",</></>
<red><bold> \\"react-native\\"</></>
<red><bold> ]</></>
<red><bold> }</></>
<red> }</>
<red></>"
`;
Expand Down Expand Up @@ -122,7 +132,10 @@ exports[`works with custom errors 1`] = `
<red></>
<red> Example:</>
<red> {</>
<red> <bold>\\"test\\"</>: <bold>[1, 2]</></>
<red> <bold>\\"test\\"</>: <bold>[</></>
<red><bold> 1,</></>
<red><bold> 2</></>
<red><bold> ]</></>
<red> }</>
<red></>
<red>My custom comment</>"
Expand Down
84 changes: 69 additions & 15 deletions packages/jest-validate/src/__tests__/validate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,41 +18,41 @@ const {
deprecatedConfig,
} = require('./fixtures/jest_config');

test('validates default Jest config', () => {
test('recursively validates default Jest config', () => {
expect(
validate(defaultConfig, {
exampleConfig: validConfig,
recursive: true,
}),
).toEqual({
hasDeprecationWarnings: false,
isValid: true,
});
});

test('validates default jest-validate config', () => {
test('recursively validates default jest-validate config', () => {
expect(
validate(jestValidateDefaultConfig, {
exampleConfig: jestValidateExampleConfig,
recursive: true,
}),
).toEqual({
hasDeprecationWarnings: false,
isValid: true,
});
});

[
[{automock: []}, 'Boolean'],
[{coverageReporters: {}}, 'Array'],
[{preset: 1337}, 'String'],
[{haste: 42}, 'Object'],
].forEach(([config, type]) => {
test(`pretty prints valid config for ${type}`, () => {
expect(() =>
validate(config, {
exampleConfig: validConfig,
}),
).toThrowErrorMatchingSnapshot();
});
test.each([
['Boolean', {automock: []}],
['Array', {coverageReporters: {}}],
['String', {preset: 1337}],
['Object', {haste: 42}],
])('pretty prints valid config for %s', (type, config) => {
expect(() =>
validate(config, {
exampleConfig: validConfig,
}),
).toThrowErrorMatchingSnapshot();
});

test(`pretty prints valid config for Function`, () => {
Expand All @@ -61,6 +61,7 @@ test(`pretty prints valid config for Function`, () => {
expect(() =>
validate(config, {
exampleConfig: validConfig,
recursive: true,
}),
).toThrowErrorMatchingSnapshot();
});
Expand All @@ -76,6 +77,58 @@ test('omits null and undefined config values', () => {
});
});

test('recursively omits null and undefined config values', () => {
const config = {
haste: {
providesModuleNodeModules: null,
},
};
expect(
validate(config, {exampleConfig: validConfig, recursive: true}),
).toEqual({
hasDeprecationWarnings: false,
isValid: true,
});
});

test('respects blacklist', () => {
const warn = console.warn;
console.warn = jest.fn();
const config = {
something: {
nested: {
some_random_key: 'value',
some_random_key2: 'value2',
},
},
};
const exampleConfig = {
something: {
nested: {
test: true,
},
},
};

validate(config, {
exampleConfig,
recursive: true,
});

expect(console.warn).toBeCalled();

console.warn.mockReset();

validate(config, {
blacklist: ['something.nested'],
exampleConfig,
recursive: true,
});

expect(console.warn).not.toBeCalled();
console.warn = warn;
});

test('displays warning for unknown config options', () => {
const config = {unkwon: {}};
const validConfig = {unknown: 'string'};
Expand All @@ -97,6 +150,7 @@ test('displays warning for deprecated config options', () => {
validate(config, {
deprecatedConfig,
exampleConfig: validConfig,
recursive: true,
}),
).toEqual({
hasDeprecationWarnings: true,
Expand Down
5 changes: 3 additions & 2 deletions packages/jest-validate/src/default_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@ import type {ValidationOptions} from './types';
import {deprecationWarning} from './deprecated';
import {unknownOptionWarning} from './warnings';
import {errorMessage} from './errors';
import exampleConfig from './example_config';
import validationCondition from './condition';
import {ERROR, DEPRECATION, WARNING} from './utils';

export default ({
blacklist: [],
comment: '',
condition: validationCondition,
deprecate: deprecationWarning,
deprecatedConfig: {},
error: errorMessage,
exampleConfig,
exampleConfig: {},
recursive: false,
title: {
deprecation: DEPRECATION,
error: ERROR,
Expand Down
11 changes: 8 additions & 3 deletions packages/jest-validate/src/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,27 @@ import type {ValidationOptions} from './types';

import chalk from 'chalk';
import getType from 'jest-get-type';
import {format, ValidationError, ERROR} from './utils';
import {formatPrettyObject, ValidationError, ERROR} from './utils';

export const errorMessage = (
option: string,
received: any,
defaultValue: any,
options: ValidationOptions,
path?: Array<string>,
): void => {
const message = ` Option ${chalk.bold(`"${option}"`)} must be of type:
const message = ` Option ${chalk.bold(
`"${path && path.length > 0 ? path.join('.') + '.' : ''}${option}"`,
)} must be of type:
${chalk.bold.green(getType(defaultValue))}
but instead received:
${chalk.bold.red(getType(received))}

Example:
{
${chalk.bold(`"${option}"`)}: ${chalk.bold(format(defaultValue))}
${chalk.bold(`"${option}"`)}: ${chalk.bold(
formatPrettyObject(defaultValue),
)}
}`;

const comment = options.comment;
Expand Down
2 changes: 2 additions & 0 deletions packages/jest-validate/src/example_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import type {ValidationOptions} from './types';

const config: ValidationOptions = {
blacklist: [],
comment: ' A comment',
condition: (option, validOption) => true,
deprecate: (config, option, deprecatedOptions, options) => false,
Expand All @@ -18,6 +19,7 @@ const config: ValidationOptions = {
},
error: (option, received, defaultValue, options) => {},
exampleConfig: {key: 'value', test: 'case'},
recursive: true,
title: {
deprecation: 'Deprecation Warning',
error: 'Validation Error',
Expand Down
4 changes: 4 additions & 0 deletions packages/jest-validate/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Title = {|
|};

export type ValidationOptions = {
blacklist?: Array<string>,
Copy link
Member

Choose a reason for hiding this comment

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

recursiveBlacklist? Or something like that, I think it should be tied to what the blacklist is for, so I don't have to look up the docs

comment?: string,
condition?: (option: any, validOption: any) => boolean,
deprecate?: (
Expand All @@ -28,13 +29,16 @@ export type ValidationOptions = {
received: any,
defaultValue: any,
options: ValidationOptions,
path?: Array<string>,
) => void,
exampleConfig: Object,
recursive?: boolean,
title?: Title,
unknown?: (
config: Object,
exampleConfig: Object,
option: string,
options: ValidationOptions,
path?: Array<string>,
) => void,
};
7 changes: 7 additions & 0 deletions packages/jest-validate/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ export const format = (value: any): string =>
? value.toString()
: prettyFormat(value, {min: true});

export const formatPrettyObject = (value: any): string =>
typeof value === 'function'
? value.toString()
: JSON.stringify(value, null, 2)
.split('\n')
.join('\n ');

export class ValidationError extends Error {
name: string;
message: string;
Expand Down
Loading