mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[jest] parse CLI flags correctly (#146844)
Fixes https://github.com/elastic/kibana/issues/144051 Rather than just parsing process.argv with the default config of getopts, which treats flags like `-u` into a "value collecting" flag, this updates the way we call getopts so that all known jest CLI flags are properly handled. This is accomplished by parsing the output of `yarn jest --help` and then using that information to power `node scripts/jest`. To update the known CLI flags we just need to run `yarn jest --help | node scripts/read_jest_help.mjs` (for some reason I don't understand, Jest does not produce it's entire `--help` output when called from node, only when called from a terminal).
This commit is contained in:
parent
061517fe3e
commit
c38315f91c
4 changed files with 288 additions and 1 deletions
|
@ -9,6 +9,7 @@ PKG_REQUIRE_NAME = "@kbn/test"
|
|||
SOURCE_FILES = glob(
|
||||
[
|
||||
"src/failed_tests_reporter/es_config",
|
||||
"src/jest/jest_flags.json",
|
||||
"**/*.html",
|
||||
"**/*.js",
|
||||
"**/*.ts",
|
||||
|
@ -148,6 +149,10 @@ jsts_transpiler(
|
|||
name = "target_node",
|
||||
srcs = SRCS,
|
||||
build_pkg_name = package_name(),
|
||||
additional_args = [
|
||||
"--copy-files",
|
||||
"--quiet"
|
||||
]
|
||||
)
|
||||
|
||||
ts_config(
|
||||
|
|
121
packages/kbn-test/src/jest/jest_flags.json
Normal file
121
packages/kbn-test/src/jest/jest_flags.json
Normal file
|
@ -0,0 +1,121 @@
|
|||
{
|
||||
"string": [
|
||||
"cacheDirectory",
|
||||
"changedSince",
|
||||
"collectCoverageFrom",
|
||||
"config",
|
||||
"coverageDirectory",
|
||||
"coveragePathIgnorePatterns",
|
||||
"coverageProvider",
|
||||
"coverageReporters",
|
||||
"coverageThreshold",
|
||||
"env",
|
||||
"filter",
|
||||
"globals",
|
||||
"globalSetup",
|
||||
"globalTeardown",
|
||||
"haste",
|
||||
"ignoreProjects",
|
||||
"maxConcurrency",
|
||||
"maxWorkers",
|
||||
"moduleDirectories",
|
||||
"moduleFileExtensions",
|
||||
"moduleNameMapper",
|
||||
"modulePathIgnorePatterns",
|
||||
"modulePaths",
|
||||
"notifyMode",
|
||||
"outputFile",
|
||||
"preset",
|
||||
"prettierPath",
|
||||
"projects",
|
||||
"reporters",
|
||||
"resolver",
|
||||
"rootDir",
|
||||
"roots",
|
||||
"runner",
|
||||
"seed",
|
||||
"selectProjects",
|
||||
"setupFiles",
|
||||
"setupFilesAfterEnv",
|
||||
"shard",
|
||||
"snapshotSerializers",
|
||||
"testEnvironment",
|
||||
"testEnvironmentOptions",
|
||||
"testFailureExitCode",
|
||||
"testMatch",
|
||||
"testNamePattern",
|
||||
"testPathIgnorePatterns",
|
||||
"testPathPattern",
|
||||
"testRegex",
|
||||
"testResultsProcessor",
|
||||
"testRunner",
|
||||
"testSequencer",
|
||||
"testTimeout",
|
||||
"transform",
|
||||
"transformIgnorePatterns",
|
||||
"unmockedModulePathPatterns",
|
||||
"watchPathIgnorePatterns"
|
||||
],
|
||||
"boolean": [
|
||||
"all",
|
||||
"automock",
|
||||
"bail",
|
||||
"cache",
|
||||
"changedFilesWithAncestor",
|
||||
"ci",
|
||||
"clearCache",
|
||||
"clearMocks",
|
||||
"collectCoverage",
|
||||
"color",
|
||||
"colors",
|
||||
"coverage",
|
||||
"debug",
|
||||
"detectLeaks",
|
||||
"detectOpenHandles",
|
||||
"errorOnDeprecated",
|
||||
"expand",
|
||||
"findRelatedTests",
|
||||
"forceExit",
|
||||
"help",
|
||||
"init",
|
||||
"injectGlobals",
|
||||
"json",
|
||||
"lastCommit",
|
||||
"listTests",
|
||||
"logHeapUsage",
|
||||
"noStackTrace",
|
||||
"notify",
|
||||
"onlyChanged",
|
||||
"onlyFailures",
|
||||
"passWithNoTests",
|
||||
"resetMocks",
|
||||
"resetModules",
|
||||
"restoreMocks",
|
||||
"runInBand",
|
||||
"runTestsByPath",
|
||||
"showConfig",
|
||||
"showSeed",
|
||||
"silent",
|
||||
"skipFilter",
|
||||
"testLocationInResults",
|
||||
"updateSnapshot",
|
||||
"useStderr",
|
||||
"verbose",
|
||||
"version",
|
||||
"watch",
|
||||
"watchAll",
|
||||
"watchman"
|
||||
],
|
||||
"alias": {
|
||||
"b": "bail",
|
||||
"c": "config",
|
||||
"e": "expand",
|
||||
"f": "onlyFailures",
|
||||
"h": "help",
|
||||
"i": "runInBand",
|
||||
"o": "onlyChanged",
|
||||
"t": "testNamePattern",
|
||||
"u": "updateSnapshot",
|
||||
"w": "maxWorkers"
|
||||
}
|
||||
}
|
|
@ -22,16 +22,46 @@ import { existsSync } from 'fs';
|
|||
import { run } from 'jest';
|
||||
import { ToolingLog } from '@kbn/tooling-log';
|
||||
import { getTimeReporter } from '@kbn/ci-stats-reporter';
|
||||
import { createFailError } from '@kbn/dev-cli-errors';
|
||||
import { REPO_ROOT } from '@kbn/utils';
|
||||
import { map } from 'lodash';
|
||||
import getopts from 'getopts';
|
||||
import jestFlags from './jest_flags.json';
|
||||
|
||||
// yarn test:jest src/core/server/saved_objects
|
||||
// yarn test:jest src/core/public/core_system.test.ts
|
||||
// :kibana/src/core/server/saved_objects yarn test:jest
|
||||
|
||||
export function runJest(configName = 'jest.config.js') {
|
||||
const argv = getopts(process.argv.slice(2));
|
||||
const unknownFlag: string[] = [];
|
||||
const argv = getopts(process.argv.slice(2), {
|
||||
...jestFlags,
|
||||
unknown(v) {
|
||||
unknownFlag.push(v);
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
if (argv.help) {
|
||||
run();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (unknownFlag.length) {
|
||||
const flags = unknownFlag.join(', ');
|
||||
|
||||
throw createFailError(
|
||||
`unexpected flag: ${flags}
|
||||
|
||||
If this flag is valid you might need to update the flags in "packages/kbn-test/src/jest/run.js".
|
||||
|
||||
Run 'yarn jest --help | node scripts/read_jest_help.mjs' to update this scripts knowledge of what
|
||||
flags jest supports
|
||||
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
const devConfigName = 'jest.config.dev.js';
|
||||
|
||||
const log = new ToolingLog({
|
||||
|
|
131
scripts/read_jest_help.mjs
Normal file
131
scripts/read_jest_help.mjs
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import Fsp from 'fs/promises';
|
||||
import Path from 'path';
|
||||
|
||||
import { createFailError } from '@kbn/dev-cli-errors';
|
||||
import { run } from '@kbn/dev-cli-runner';
|
||||
import { REPO_ROOT } from '@kbn/utils';
|
||||
|
||||
const FLAGS_FILE = 'packages/kbn-test/src/jest/jest_flags.json';
|
||||
|
||||
function readStdin() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let buffer = '';
|
||||
let timer = setTimeout(() => {
|
||||
reject(
|
||||
createFailError('you must pipe the output of `yarn jest --help` to this script', {
|
||||
showHelp: true,
|
||||
})
|
||||
);
|
||||
}, 1000);
|
||||
|
||||
process.stdin
|
||||
.on('data', (chunk) => {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = undefined;
|
||||
}
|
||||
|
||||
buffer += chunk;
|
||||
})
|
||||
.on('end', () => resolve(buffer))
|
||||
.on('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
run(
|
||||
async ({ log }) => {
|
||||
const lines = (await readStdin()).split('\n');
|
||||
|
||||
/** @type {{ string: string[], boolean: string[], alias: Record<string, string> }} */
|
||||
const flags = { string: [], boolean: [], alias: {} };
|
||||
|
||||
/** @type {string | undefined} */
|
||||
let currentFlag;
|
||||
|
||||
for (const line of lines) {
|
||||
const flagMatch = line.match(/^\s+(?:-(\w), )?--(\w+)\s+/);
|
||||
const typeMatch = line.match(/\[(boolean|string|array|number|choices: [^\]]+)\]/);
|
||||
|
||||
if (flagMatch && currentFlag) {
|
||||
throw createFailError(`unable to determine type for flag [${currentFlag}]`);
|
||||
}
|
||||
|
||||
if (flagMatch) {
|
||||
currentFlag = flagMatch[2];
|
||||
if (flagMatch[1]) {
|
||||
flags.alias[flagMatch[1]] = flagMatch[2];
|
||||
}
|
||||
}
|
||||
|
||||
if (currentFlag && typeMatch) {
|
||||
switch (typeMatch[1]) {
|
||||
case 'string':
|
||||
case 'array':
|
||||
case 'number':
|
||||
flags.string.push(currentFlag);
|
||||
break;
|
||||
case 'boolean':
|
||||
flags.boolean.push(currentFlag);
|
||||
break;
|
||||
default:
|
||||
if (typeMatch[1].startsWith('choices: ')) {
|
||||
flags.string.push(currentFlag);
|
||||
break;
|
||||
}
|
||||
|
||||
throw createFailError(`unexpected flag type [${typeMatch[1]}]`);
|
||||
}
|
||||
currentFlag = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
await Fsp.writeFile(
|
||||
Path.resolve(REPO_ROOT, FLAGS_FILE),
|
||||
JSON.stringify(
|
||||
{
|
||||
string: flags.string.sort(function (a, b) {
|
||||
return a.localeCompare(b);
|
||||
}),
|
||||
boolean: flags.boolean.sort(function (a, b) {
|
||||
return a.localeCompare(b);
|
||||
}),
|
||||
alias: Object.fromEntries(
|
||||
Object.entries(flags.alias).sort(function (a, b) {
|
||||
return a[0].localeCompare(b[0]);
|
||||
})
|
||||
),
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
);
|
||||
|
||||
log.success('wrote jest flag info to', FLAGS_FILE);
|
||||
log.warning('make sure you bootstrap to rebuild @kbn/test');
|
||||
},
|
||||
{
|
||||
usage: `yarn jest --help | node scripts/read_jest_help.mjs`,
|
||||
description: `
|
||||
Jest no longer exposes the ability to parse CLI flags externally, so we use this
|
||||
script to read the help output and convert it into parameters we can pass to getopts()
|
||||
which will parse the flags similar to how Jest does it.
|
||||
|
||||
getopts() doesn't support things like enums, or number flags, but if we use the generated
|
||||
config then it will at least interpret which flags are expected, which are invalid, and
|
||||
allow us to determine the correct config path based on the provided path while passing
|
||||
the rest of the args directly to jest.
|
||||
`,
|
||||
flags: {
|
||||
allowUnexpected: true,
|
||||
guessTypesForUnexpectedFlags: false,
|
||||
},
|
||||
}
|
||||
);
|
Loading…
Add table
Add a link
Reference in a new issue