Skip to content

Commit b644d85

Browse files
philiiiiiippSimenB
authored andcommitted
Allow the cli to use a string for --maxWorkers (#8565)
1 parent 5d42d0a commit b644d85

File tree

11 files changed

+62
-21
lines changed

11 files changed

+62
-21
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
- `[jest-cli]` Improve chai support (with detailed output, to match jest exceptions) ([#8454](https://github.com/facebook/jest/pull/8454))
99
- `[*]` Manage the global timeout with `--testTimeout` command line argument. ([#8456](https://github.com/facebook/jest/pull/8456))
1010
- `[pretty-format]` Render custom displayName of memoized components
11+
- `[jest-validate]` Allow `maxWorkers` as part of the `jest.config.js` ([#8565](https://github.com/facebook/jest/pull/8565))
1112

1213
### Fixes
1314

15+
- `[jest-cli]` Allow `--maxWorkers` to work with % input again ([#8565](https://github.com/facebook/jest/pull/8565))
1416
- `[babel-plugin-jest-hoist]` Expand list of whitelisted globals in global mocks ([#8429](https://github.com/facebook/jest/pull/8429)
1517
- `[jest-core]` Make watch plugin initialization errors look nice ([#8422](https://github.com/facebook/jest/pull/8422))
1618
- `[jest-snapshot]` Prevent inline snapshots from drifting when inline snapshots are updated ([#8492](https://github.com/facebook/jest/pull/8492))

packages/jest-cli/src/__tests__/cli/args.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,15 @@ describe('check', () => {
5050
it('raises an exception if maxWorkers is specified with no number', () => {
5151
const argv = ({maxWorkers: undefined} as unknown) as Config.Argv;
5252
expect(() => check(argv)).toThrow(
53-
'The --maxWorkers (-w) option requires a number to be specified',
53+
'The --maxWorkers (-w) option requires a number or string to be specified',
5454
);
5555
});
5656

57+
it('allows maxWorkers to be a %', () => {
58+
const argv = ({maxWorkers: '50%'} as unknown) as Config.Argv;
59+
expect(() => check(argv)).not.toThrow();
60+
});
61+
5762
it('raises an exception if config is not a valid JSON string', () => {
5863
const argv = {config: 'x:1'} as Config.Argv;
5964
expect(() => check(argv)).toThrow(

packages/jest-cli/src/cli/args.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ export const check = (argv: Config.Argv) => {
4242

4343
if (argv.hasOwnProperty('maxWorkers') && argv.maxWorkers === undefined) {
4444
throw new Error(
45-
'The --maxWorkers (-w) option requires a number to be specified.\n' +
45+
'The --maxWorkers (-w) option requires a number or string to be specified.\n' +
4646
'Example usage: jest --maxWorkers 2\n' +
47+
'Example usage: jest --maxWorkers 50%\n' +
4748
'Or did you mean --watch?',
4849
);
4950
}
@@ -349,7 +350,7 @@ export const options = {
349350
'will spawn for running tests. This defaults to the number of the ' +
350351
'cores available on your machine. (its usually best not to override ' +
351352
'this default)',
352-
type: 'number' as 'number',
353+
type: 'string' as 'string',
353354
},
354355
moduleDirectories: {
355356
description:

packages/jest-cli/src/init/__tests__/__snapshots__/init.test.js.snap

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ module.exports = {
8181
// A set of global variables that need to be available in all test environments
8282
// globals: {},
8383
84+
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
85+
// maxWorkers: \\"50%\\",
86+
8487
// An array of directory names to be searched recursively up from the requiring module's location
8588
// moduleDirectories: [
8689
// \\"node_modules\\"

packages/jest-config/src/Defaults.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const defaultOptions: Config.DefaultOptions = {
4040
throwOnModuleCollision: false,
4141
},
4242
maxConcurrency: 5,
43+
maxWorkers: '50%',
4344
moduleDirectories: ['node_modules'],
4445
moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx', 'node'],
4546
moduleNameMapper: {},

packages/jest-config/src/Descriptions.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ const descriptions: {[key in keyof Config.InitialOptions]: string} = {
3737
'A path to a module which exports an async function that is triggered once after all test suites',
3838
globals:
3939
'A set of global variables that need to be available in all test environments',
40+
maxWorkers:
41+
'The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.',
4042
moduleDirectories:
4143
"An array of directory names to be searched recursively up from the requiring module's location",
4244
moduleFileExtensions: 'An array of file extensions your modules use',

packages/jest-config/src/ValidConfig.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const initialOptions: Config.InitialOptions = {
6363
lastCommit: false,
6464
logHeapUsage: true,
6565
maxConcurrency: 5,
66+
maxWorkers: '50%',
6667
moduleDirectories: ['node_modules'],
6768
moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx', 'node'],
6869
moduleLoader: '<rootDir>',

packages/jest-config/src/getMaxWorkers.ts

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,34 @@ import {Config} from '@jest/types';
1010

1111
export default function getMaxWorkers(
1212
argv: Partial<Pick<Config.Argv, 'maxWorkers' | 'runInBand' | 'watch'>>,
13+
defaultOptions?: Partial<Pick<Config.Argv, 'maxWorkers'>>,
1314
): number {
1415
if (argv.runInBand) {
1516
return 1;
1617
} else if (argv.maxWorkers) {
17-
// TODO: How to type this properly? Should probably use `coerce` from `yargs`
18-
const maxWorkers = argv.maxWorkers;
19-
const parsed = parseInt(maxWorkers as string, 10);
20-
21-
if (
22-
typeof maxWorkers === 'string' &&
23-
maxWorkers.trim().endsWith('%') &&
24-
parsed > 0 &&
25-
parsed <= 100
26-
) {
27-
const cpus = os.cpus().length;
28-
const workers = Math.floor((parsed / 100) * cpus);
29-
return workers >= 1 ? workers : 1;
30-
}
31-
32-
return parsed > 0 ? parsed : 1;
18+
return parseWorkers(argv.maxWorkers);
19+
} else if (defaultOptions && defaultOptions.maxWorkers) {
20+
return parseWorkers(defaultOptions.maxWorkers);
3321
} else {
3422
// In watch mode, Jest should be unobtrusive and not use all available CPUs.
3523
const cpus = os.cpus() ? os.cpus().length : 1;
3624
return Math.max(argv.watch ? Math.floor(cpus / 2) : cpus - 1, 1);
3725
}
3826
}
27+
28+
const parseWorkers = (maxWorkers: string | number): number => {
29+
const parsed = parseInt(maxWorkers.toString(), 10);
30+
31+
if (
32+
typeof maxWorkers === 'string' &&
33+
maxWorkers.trim().endsWith('%') &&
34+
parsed > 0 &&
35+
parsed <= 100
36+
) {
37+
const cpus = os.cpus().length;
38+
const workers = Math.floor((parsed / 100) * cpus);
39+
return workers >= 1 ? workers : 1;
40+
}
41+
42+
return parsed > 0 ? parsed : 1;
43+
};

packages/jest-config/src/normalize.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -928,7 +928,7 @@ export default function normalize(
928928
(newOptions.maxConcurrency as unknown) as string,
929929
10,
930930
);
931-
newOptions.maxWorkers = getMaxWorkers(argv);
931+
newOptions.maxWorkers = getMaxWorkers(argv, options);
932932

933933
if (newOptions.testRegex!.length && options.testMatch) {
934934
throw createConfigError(

packages/jest-types/src/Config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export type DefaultOptions = {
5555
globalSetup: string | null | undefined;
5656
globalTeardown: string | null | undefined;
5757
haste: HasteConfig;
58+
maxWorkers: number | string;
5859
maxConcurrency: number;
5960
moduleDirectories: Array<string>;
6061
moduleFileExtensions: Array<string>;
@@ -156,6 +157,7 @@ export type InitialOptions = {
156157
listTests?: boolean;
157158
mapCoverage?: boolean;
158159
maxConcurrency?: number;
160+
maxWorkers: number | string;
159161
moduleDirectories?: Array<string>;
160162
moduleFileExtensions?: Array<string>;
161163
moduleLoader?: Path;

packages/jest-validate/src/validate.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
*/
77

88
import {ValidationOptions} from './types';
9-
109
import defaultConfig from './defaultConfig';
10+
import {ValidationError} from './utils';
1111

1212
let hasDeprecationWarnings = false;
1313

@@ -46,6 +46,21 @@ const _validate = (
4646
);
4747

4848
hasDeprecationWarnings = hasDeprecationWarnings || isDeprecatedKey;
49+
} else if (allowsMultipleTypes(key)) {
50+
const value = config[key];
51+
52+
if (
53+
typeof options.condition === 'function' &&
54+
typeof options.error === 'function'
55+
) {
56+
if (key === 'maxWorkers' && !isOfTypeStringOrNumber(value)) {
57+
throw new ValidationError(
58+
'Validation Error',
59+
`${key} has to be of type string or number`,
60+
`maxWorkers=50% or\nmaxWorkers=3`,
61+
);
62+
}
63+
}
4964
} else if (Object.hasOwnProperty.call(exampleConfig, key)) {
5065
if (
5166
typeof options.condition === 'function' &&
@@ -76,6 +91,10 @@ const _validate = (
7691
return {hasDeprecationWarnings};
7792
};
7893

94+
const allowsMultipleTypes = (key: string): boolean => key === 'maxWorkers';
95+
const isOfTypeStringOrNumber = (value: any): boolean =>
96+
typeof value === 'number' || typeof value === 'string';
97+
7998
const validate = (config: Record<string, any>, options: ValidationOptions) => {
8099
hasDeprecationWarnings = false;
81100

0 commit comments

Comments
 (0)