Skip to content

Commit 56389a4

Browse files
fix(cli): support Jest-specific CLI flag aliases (#4124)
This adds support for the following Jest-specific CLI flag aliases: - "b" for "bail" - "e" for "expand" - "w" for "maxWorkers" - "o" for "onlyChanged" - "f" for "onlyFailures" - "i" for "runInBand" - "t" for "testNamePattern" - "u" for "updateSnapshot" Two additional flags supported by Jest ('c' for 'config' and 'h' for 'help') were already supported by Stencil. This also makes a small refactor to the CLI argument parser, mainly building up the `flags.unknownArgs` array proactively whenever we're in a situation where we don't know what to do, rather than filtering all the arguments after the fact.
1 parent 23a73f0 commit 56389a4

File tree

4 files changed

+135
-31
lines changed

4 files changed

+135
-31
lines changed

src/cli/config-flags.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,25 @@ export const CLI_FLAG_ALIASES: AliasMap = {
213213
h: 'help',
214214
p: 'port',
215215
v: 'version',
216+
217+
// JEST SPECIFIC CLI FLAGS
218+
// these are defined in
219+
// https://github.com/facebook/jest/blob/4156f86/packages/jest-cli/src/args.ts
220+
b: 'bail',
221+
e: 'expand',
222+
f: 'onlyFailures',
223+
i: 'runInBand',
224+
o: 'onlyChanged',
225+
t: 'testNamePattern',
226+
u: 'updateSnapshot',
227+
w: 'maxWorkers',
216228
};
217229

218230
/**
219231
* A regular expression which can be used to match a CLI flag for one of our
220232
* short aliases.
221233
*/
222-
export const CLI_FLAG_REGEX = new RegExp(`^-[chpv]{1}$`);
234+
export const CLI_FLAG_REGEX = new RegExp(`^-[chpvbewofitu]{1}$`);
223235

224236
/**
225237
* Given two types `K` and `T` where `K` extends `ReadonlyArray<string>`,

src/cli/parse-flags.ts

+45-30
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,6 @@ export const parseFlags = (args: string[]): ConfigFlags => {
4242
}
4343
}
4444

45-
// to find unknown / unrecognized arguments we filter `args`, including only
46-
// arguments whose normalized form is not found in `knownArgs`. `knownArgs`
47-
// is populated during the call to `parseArgs` above. For arguments like
48-
// `--foobar` the string `"--foobar"` will be added, while for more
49-
// complicated arguments like `--bizBoz=bop` or `--bizBoz bop` just the
50-
// string `"--bizBoz"` will be added.
51-
flags.unknownArgs = flags.args.filter((arg: string) => !flags.knownArgs.includes(parseEqualsArg(arg)[0]));
52-
5345
return flags;
5446
};
5547

@@ -131,13 +123,13 @@ const parseCLITerm = (flags: ConfigFlags, args: string[]) => {
131123
else if (arg.startsWith('-') && arg.includes('=')) {
132124
// we're dealing with an AliasEqualsArg, we have a special helper for that
133125
const [originalArg, value] = parseEqualsArg(arg);
134-
setCLIArg(flags, arg.split('=')[0], normalizeFlagName(originalArg), value);
126+
setCLIArg(flags, desugarRawAlias(originalArg), normalizeFlagName(originalArg), value);
135127
}
136128

137129
// AliasArg → "-" AliasName ( " " CLIValue )? ;
138130
else if (CLI_FLAG_REGEX.test(arg)) {
139131
// this is a short alias, like `-c` for Config
140-
setCLIArg(flags, arg, normalizeFlagName(arg), parseCLIValue(args));
132+
setCLIArg(flags, desugarRawAlias(arg), normalizeFlagName(arg), parseCLIValue(args));
141133
}
142134

143135
// NegativeDashArg → "--no-" ArgName ;
@@ -164,13 +156,14 @@ const parseCLITerm = (flags: ConfigFlags, args: string[]) => {
164156
// SimpleArg → "--" ArgName ( " " CLIValue )? ;
165157
else if (arg.startsWith('--') && arg.length > '--'.length) {
166158
setCLIArg(flags, arg, normalizeFlagName(arg), parseCLIValue(args));
159+
} else {
160+
// if we get here then `arg` is not an argument in our list of supported
161+
// arguments. This doesn't necessarily mean we want to report an error or
162+
// anything though! Instead, with unknown / unrecognized arguments we want
163+
// to stick them into the `unknownArgs` array, which is used when we pass
164+
// CLI args to Jest, for instance.
165+
flags.unknownArgs.push(arg);
167166
}
168-
169-
// if we get here it is not an argument in our list of supported arguments.
170-
// This doesn't necessarily mean we want to report an error or anything
171-
// though! Instead, with unknown / unrecognized arguments we stick them into
172-
// the `unknownArgs` array, which is used when we pass CLI args to Jest, for
173-
// instance. So we just return void here.
174167
};
175168

176169
/**
@@ -219,7 +212,7 @@ const normalizeFlagName = (flagName: string): string => {
219212
* @param value the raw value to be set onto the config flags object
220213
*/
221214
const setCLIArg = (flags: ConfigFlags, rawArg: string, normalizedArg: string, value: CLIValueResult) => {
222-
normalizedArg = dereferenceAlias(normalizedArg);
215+
normalizedArg = desugarAlias(normalizedArg);
223216

224217
// We're setting a boolean!
225218
if (readOnlyArrayHasStringMember(BOOLEAN_CLI_FLAGS, normalizedArg)) {
@@ -320,6 +313,14 @@ const setCLIArg = (flags: ConfigFlags, rawArg: string, normalizedArg: string, va
320313
} else {
321314
throwCLIParsingError(rawArg, 'expected to receive a valid log level but received nothing');
322315
}
316+
} else {
317+
// we haven't found this flag in any of our lists of arguments, so we
318+
// should put it in our list of unknown arguments
319+
flags.unknownArgs.push(rawArg);
320+
321+
if (typeof value === 'string') {
322+
flags.unknownArgs.push(value);
323+
}
323324
}
324325
};
325326

@@ -355,9 +356,10 @@ type CLIValueResult = string | typeof Empty;
355356
* A little helper which tries to parse a CLI value (as opposed to a flag) off
356357
* of the argument array.
357358
*
358-
* We support a variety of different argument formats, but all of them start
359-
* with `-`, so we can check the first character to test whether the next token
360-
* in our array of CLI arguments is a flag name or a value.
359+
* We support a variety of different argument formats for flags (as opposed to
360+
* values), but all of them start with `-`, so we can check the first character
361+
* to test whether the next token in our array of CLI arguments is a flag name
362+
* or a value.
361363
*
362364
* @param args an array of CLI args
363365
* @returns either a string result or an Empty sentinel
@@ -454,22 +456,35 @@ const throwNumberParsingError = (flag: string, value: string) => {
454456
};
455457

456458
/**
457-
* A little helper to 'dereference' a flag alias, which if you squint a little
458-
* you can think of like a pointer to a full flag name. Thus 'c' is like a
459-
* pointer to 'config', so here we're doing something like `*c`. Of course, this
460-
* being JS, this is just a metaphor!
459+
* A little helper to 'desugar' a flag alias, meaning expand it to its full
460+
* name. For instance, the alias `"c"` will desugar to `"config"`.
461461
*
462-
* If no 'dereference' is found for the possible alias we just return the
463-
* passed string unmodified.
462+
* If no expansion is found for the possible alias we just return the passed
463+
* string unmodified.
464464
*
465465
* @param maybeAlias a string which _could_ be an alias to a full flag name
466466
* @returns the full aliased flag name, if found, or the passed string if not
467467
*/
468-
const dereferenceAlias = (maybeAlias: string): string => {
469-
const possibleDereference = CLI_FLAG_ALIASES[maybeAlias];
468+
const desugarAlias = (maybeAlias: string): string => {
469+
const possiblyDesugared = CLI_FLAG_ALIASES[maybeAlias];
470470

471-
if (typeof possibleDereference === 'string') {
472-
return possibleDereference;
471+
if (typeof possiblyDesugared === 'string') {
472+
return possiblyDesugared;
473473
}
474474
return maybeAlias;
475475
};
476+
477+
/**
478+
* Desugar a 'raw' alias (with a leading dash) and return an equivalent,
479+
* desugared argument.
480+
*
481+
* For instance, passing `"-c` will return `"--config"`.
482+
*
483+
* The reason we'd like to do this is not so much for our own code, but so that
484+
* we can transform an alias like `"-u"` to `"--updateSnapshot"` in order to
485+
* pass it along to Jest.
486+
*
487+
* @param rawAlias a CLI flag alias as found on the command line (like `"-c"`)
488+
* @returns an equivalent full command (like `"--config"`)
489+
*/
490+
const desugarRawAlias = (rawAlias: string): string => '--' + desugarAlias(normalizeFlagName(rawAlias));

src/cli/test/parse-flags.spec.ts

+37
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { toDashCase } from '@utils';
33
import { LogLevel } from '../../declarations';
44
import {
55
BOOLEAN_CLI_FLAGS,
6+
ConfigFlags,
67
NUMBER_CLI_FLAGS,
78
STRING_ARRAY_CLI_FLAGS,
89
STRING_CLI_FLAGS,
@@ -230,11 +231,47 @@ describe('parseFlags', () => {
230231
it('should parse -c /my-config.js', () => {
231232
const flags = parseFlags(['-c', '/my-config.js']);
232233
expect(flags.config).toBe('/my-config.js');
234+
expect(flags.knownArgs).toEqual(['--config', '/my-config.js']);
233235
});
234236

235237
it('should parse -c=/my-config.js', () => {
236238
const flags = parseFlags(['-c=/my-config.js']);
237239
expect(flags.config).toBe('/my-config.js');
240+
expect(flags.knownArgs).toEqual(['--config', '/my-config.js']);
241+
});
242+
});
243+
244+
describe('Jest aliases', () => {
245+
it.each([
246+
['w', 'maxWorkers', '4'],
247+
['t', 'testNamePattern', 'testname'],
248+
])('should support the string Jest alias %p for %p', (alias, fullArgument, value) => {
249+
const flags = parseFlags([`-${alias}`, value]);
250+
expect(flags.knownArgs).toEqual([`--${fullArgument}`, value]);
251+
expect(flags.unknownArgs).toHaveLength(0);
252+
});
253+
254+
it.each([
255+
['w', 'maxWorkers', '4'],
256+
['t', 'testNamePattern', 'testname'],
257+
])('should support the string Jest alias %p for %p in an AliasEqualsArg', (alias, fullArgument, value) => {
258+
const flags = parseFlags([`-${alias}=${value}`]);
259+
expect(flags.knownArgs).toEqual([`--${fullArgument}`, value]);
260+
expect(flags.unknownArgs).toHaveLength(0);
261+
});
262+
263+
it.each<[string, keyof ConfigFlags]>([
264+
['b', 'bail'],
265+
['e', 'expand'],
266+
['o', 'onlyChanged'],
267+
['f', 'onlyFailures'],
268+
['i', 'runInBand'],
269+
['u', 'updateSnapshot'],
270+
])('should support the boolean Jest alias %p for %p', (alias, fullArgument) => {
271+
const flags = parseFlags([`-${alias}`]);
272+
expect(flags.knownArgs).toEqual([`--${fullArgument}`]);
273+
expect(flags[fullArgument]).toBe(true);
274+
expect(flags.unknownArgs).toHaveLength(0);
238275
});
239276
});
240277
});

src/testing/jest/test/jest-config.spec.ts

+40
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { Config } from '@jest/types';
12
import type * as d from '@stencil/core/declarations';
23
import { mockValidatedConfig } from '@stencil/core/testing';
34
import path from 'path';
@@ -225,4 +226,43 @@ describe('jest-config', () => {
225226
// the `_` field holds any filename pattern matches
226227
expect(jestArgv._).toEqual(['foobar/*', 'my-spec.ts']);
227228
});
229+
230+
describe('Jest aliases', () => {
231+
it('should support the string Jest alias "-w=4" for maxWorkers', () => {
232+
const args = ['test', '--spec', '-w=4'];
233+
const jestArgv = buildJestArgv(mockValidatedConfig({ flags: parseFlags(args) }));
234+
expect(jestArgv.maxWorkers).toBe(4);
235+
});
236+
237+
it('should support the string Jest alias "-w 4" for maxWorkers', () => {
238+
const args = ['test', '--spec', '-w', '4'];
239+
const jestArgv = buildJestArgv(mockValidatedConfig({ flags: parseFlags(args) }));
240+
expect(jestArgv.maxWorkers).toBe(4);
241+
});
242+
243+
it('should support the string Jest alias "-t" for testNamePattern', () => {
244+
const args = ['test', '--spec', '-t=my-test-pattern'];
245+
const jestArgv = buildJestArgv(mockValidatedConfig({ flags: parseFlags(args) }));
246+
expect(jestArgv.testNamePattern).toBe('my-test-pattern');
247+
});
248+
249+
it('should support the string Jest alias "-t pattern" for testNamePattern', () => {
250+
const args = ['test', '--spec', '-t', 'my-test-pattern'];
251+
const jestArgv = buildJestArgv(mockValidatedConfig({ flags: parseFlags(args) }));
252+
expect(jestArgv.testNamePattern).toBe('my-test-pattern');
253+
});
254+
255+
it.each<[string, keyof Config.Argv]>([
256+
['b', 'bail'],
257+
['e', 'expand'],
258+
['o', 'onlyChanged'],
259+
['f', 'onlyFailures'],
260+
['i', 'runInBand'],
261+
['u', 'updateSnapshot'],
262+
])('should support the boolean Jest alias %p for %p', (alias, fullArgument) => {
263+
const args = ['test', '--spec', `-${alias}`];
264+
const jestArgv = buildJestArgv(mockValidatedConfig({ flags: parseFlags(args) }));
265+
expect(jestArgv[fullArgument]).toBe(true);
266+
});
267+
});
228268
});

0 commit comments

Comments
 (0)