mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
parent
c668d83b93
commit
8249e61262
10 changed files with 204 additions and 109 deletions
|
@ -487,6 +487,7 @@
|
|||
"@istanbuljs/schema": "^0.1.2",
|
||||
"@jest/console": "^26.6.2",
|
||||
"@jest/reporters": "^26.6.2",
|
||||
"@jest/types": "^26",
|
||||
"@kbn/axe-config": "link:bazel-bin/packages/kbn-axe-config",
|
||||
"@kbn/babel-plugin-synthetic-packages": "link:bazel-bin/packages/kbn-babel-plugin-synthetic-packages",
|
||||
"@kbn/babel-preset": "link:bazel-bin/packages/kbn-babel-preset",
|
||||
|
@ -895,10 +896,12 @@
|
|||
"jest-canvas-mock": "^2.3.1",
|
||||
"jest-circus": "^26.6.3",
|
||||
"jest-cli": "^26.6.3",
|
||||
"jest-config": "^26",
|
||||
"jest-diff": "^26.6.2",
|
||||
"jest-environment-jsdom": "^26.6.2",
|
||||
"jest-environment-jsdom-thirteen": "^1.0.1",
|
||||
"jest-raw-loader": "^1.0.1",
|
||||
"jest-runtime": "^26",
|
||||
"jest-silent-reporter": "^0.5.0",
|
||||
"jest-snapshot": "^26.6.2",
|
||||
"jest-specific-snapshot": "2.0.0",
|
||||
|
|
|
@ -65,6 +65,7 @@ RUNTIME_DEPS = [
|
|||
"@npm//jest-styled-components",
|
||||
"@npm//joi",
|
||||
"@npm//js-yaml",
|
||||
"@npm//minimatch",
|
||||
"@npm//mustache",
|
||||
"@npm//normalize-path",
|
||||
"@npm//prettier",
|
||||
|
@ -113,6 +114,7 @@ TYPES_DEPS = [
|
|||
"@npm//@types/js-yaml",
|
||||
"@npm//@types/joi",
|
||||
"@npm//@types/lodash",
|
||||
"@npm//@types/minimatch",
|
||||
"@npm//@types/mustache",
|
||||
"@npm//@types/normalize-path",
|
||||
"@npm//@types/node",
|
||||
|
|
74
packages/kbn-test/src/jest/configs/get_all_jest_paths.ts
Normal file
74
packages/kbn-test/src/jest/configs/get_all_jest_paths.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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 Fs from 'fs';
|
||||
import Path from 'path';
|
||||
|
||||
import execa from 'execa';
|
||||
import minimatch from 'minimatch';
|
||||
import { REPO_ROOT } from '@kbn/utils';
|
||||
|
||||
// @ts-expect-error jest-preset is necessarily a JS file
|
||||
import { testMatch } from '../../../jest-preset';
|
||||
|
||||
const UNIT_CONFIG_NAME = 'jest.config.js';
|
||||
const INTEGRATION_CONFIG_NAME = 'jest.integration.config.js';
|
||||
|
||||
export async function getAllJestPaths() {
|
||||
const proc = await execa('git', ['ls-files', '-comt', '--exclude-standard'], {
|
||||
cwd: REPO_ROOT,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
buffer: true,
|
||||
});
|
||||
|
||||
const testsRe = (testMatch as string[]).map((p) => minimatch.makeRe(p));
|
||||
const classify = (rel: string) => {
|
||||
if (testsRe.some((re) => re.test(rel))) {
|
||||
return 'test' as const;
|
||||
}
|
||||
|
||||
const basename = Path.basename(rel);
|
||||
return basename === UNIT_CONFIG_NAME || basename === INTEGRATION_CONFIG_NAME
|
||||
? ('config' as const)
|
||||
: undefined;
|
||||
};
|
||||
|
||||
const tests = new Set<string>();
|
||||
const configs = new Set<string>();
|
||||
|
||||
for (const line of proc.stdout.split('\n').map((l) => l.trim())) {
|
||||
if (!line) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const rel = line.slice(2); // trim the single char status from the line
|
||||
const type = classify(rel);
|
||||
|
||||
if (!type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const set = type === 'test' ? tests : configs;
|
||||
const abs = Path.resolve(REPO_ROOT, rel);
|
||||
|
||||
if (line.startsWith('C ')) {
|
||||
// this line indicates that the previous path is changed in the working tree, so we need to determine if
|
||||
// it was deleted, and if so, remove it from the set we added it to
|
||||
if (!Fs.existsSync(abs)) {
|
||||
set.delete(abs);
|
||||
}
|
||||
} else {
|
||||
set.add(abs);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
tests,
|
||||
configs,
|
||||
};
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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 { readConfig } from 'jest-config';
|
||||
import { createContext } from 'jest-runtime';
|
||||
import { SearchSource } from 'jest';
|
||||
import { asyncMapWithLimit } from '@kbn/std';
|
||||
|
||||
const EMPTY_ARGV = {
|
||||
$0: '',
|
||||
_: [],
|
||||
};
|
||||
|
||||
const NO_WARNINGS_CONSOLE = {
|
||||
...console,
|
||||
warn() {
|
||||
// ignore haste-map warnings
|
||||
},
|
||||
};
|
||||
|
||||
export interface TestsForConfigPath {
|
||||
path: string;
|
||||
testPaths: Set<string>;
|
||||
}
|
||||
|
||||
export async function getTestsForConfigPaths(
|
||||
configPaths: Iterable<string>
|
||||
): Promise<TestsForConfigPath[]> {
|
||||
return await asyncMapWithLimit(configPaths, 60, async (path) => {
|
||||
const config = await readConfig(EMPTY_ARGV, path);
|
||||
const searchSource = new SearchSource(
|
||||
await createContext(config.projectConfig, {
|
||||
maxWorkers: 1,
|
||||
watchman: false,
|
||||
watch: false,
|
||||
console: NO_WARNINGS_CONSOLE,
|
||||
})
|
||||
);
|
||||
|
||||
const results = await searchSource.getTestPaths(config.globalConfig, undefined, undefined);
|
||||
|
||||
return {
|
||||
path,
|
||||
testPaths: new Set(results.tests.map((t) => t.path)),
|
||||
};
|
||||
});
|
||||
}
|
|
@ -6,4 +6,5 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export * from './jest_configs';
|
||||
export * from './get_all_jest_paths';
|
||||
export * from './get_tests_for_config_paths';
|
||||
|
|
|
@ -6,89 +6,92 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { writeFileSync } from 'fs';
|
||||
import path from 'path';
|
||||
import Mustache from 'mustache';
|
||||
|
||||
import Path from 'path';
|
||||
import { run } from '@kbn/dev-cli-runner';
|
||||
import { createFailError } from '@kbn/dev-cli-errors';
|
||||
import { REPO_ROOT } from '@kbn/utils';
|
||||
import { getAllRepoRelativeBazelPackageDirs } from '@kbn/bazel-packages';
|
||||
|
||||
import { JestConfigs, CONFIG_NAMES } from './configs';
|
||||
import { getAllJestPaths, getTestsForConfigPaths } from './configs';
|
||||
|
||||
const unitTestingTemplate: string = `module.exports = {
|
||||
preset: '@kbn/test/jest_node',
|
||||
rootDir: '{{{relToRoot}}}',
|
||||
roots: ['<rootDir>/{{{modulePath}}}'],
|
||||
const fmtMs = (ms: number) => {
|
||||
if (ms < 1000) {
|
||||
return `${Math.round(ms)} ms`;
|
||||
}
|
||||
|
||||
return `${(Math.round(ms) / 1000).toFixed(2)} s`;
|
||||
};
|
||||
`;
|
||||
|
||||
const integrationTestingTemplate: string = `module.exports = {
|
||||
preset: '@kbn/test/jest_integration_node',
|
||||
rootDir: '{{{relToRoot}}}',
|
||||
roots: ['<rootDir>/{{{modulePath}}}'],
|
||||
};
|
||||
`;
|
||||
|
||||
const roots: string[] = [
|
||||
'x-pack/plugins/security_solution/public',
|
||||
'x-pack/plugins/security_solution/server',
|
||||
'x-pack/plugins/security_solution',
|
||||
'x-pack/plugins',
|
||||
'src/plugins',
|
||||
'test',
|
||||
'src/core',
|
||||
'src',
|
||||
...getAllRepoRelativeBazelPackageDirs(),
|
||||
];
|
||||
const fmtList = (list: Iterable<string>) => [...list].map((i) => ` - ${i}`).join('\n');
|
||||
|
||||
export async function runCheckJestConfigsCli() {
|
||||
run(
|
||||
async ({ flags: { fix = false }, log }) => {
|
||||
const jestConfigs = new JestConfigs(REPO_ROOT, roots);
|
||||
async ({ log }) => {
|
||||
const start = performance.now();
|
||||
|
||||
const missing = await jestConfigs.allMissing();
|
||||
const jestPaths = await getAllJestPaths();
|
||||
const allConfigs = await getTestsForConfigPaths(jestPaths.configs);
|
||||
const missingConfigs = new Set<string>();
|
||||
const multipleConfigs = new Set<{ configs: string[]; rel: string }>();
|
||||
|
||||
if (missing.length) {
|
||||
log.error(
|
||||
`The following Jest config files do not exist for which there are test files for:\n${[
|
||||
...missing,
|
||||
]
|
||||
.map((file) => ` - ${file}`)
|
||||
.join('\n')}`
|
||||
);
|
||||
for (const testPath of jestPaths.tests) {
|
||||
const configs = allConfigs
|
||||
.filter((c) => c.testPaths.has(testPath))
|
||||
.map((c) => Path.relative(REPO_ROOT, c.path))
|
||||
.sort((a, b) => Path.dirname(a).localeCompare(Path.dirname(b)));
|
||||
|
||||
if (fix) {
|
||||
missing.forEach((file) => {
|
||||
const template = file.endsWith(CONFIG_NAMES.unit)
|
||||
? unitTestingTemplate
|
||||
: integrationTestingTemplate;
|
||||
|
||||
const modulePath = path.dirname(file);
|
||||
const content = Mustache.render(template, {
|
||||
relToRoot: path.relative(modulePath, '.'),
|
||||
modulePath,
|
||||
});
|
||||
|
||||
writeFileSync(file, content);
|
||||
log.info('created %s', file);
|
||||
if (configs.length === 0) {
|
||||
missingConfigs.add(Path.relative(REPO_ROOT, testPath));
|
||||
} else if (configs.length > 1) {
|
||||
multipleConfigs.add({
|
||||
configs,
|
||||
rel: Path.relative(REPO_ROOT, testPath),
|
||||
});
|
||||
} else {
|
||||
throw createFailError(
|
||||
`Run 'node scripts/check_jest_configs --fix' to create the missing config files`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (missingConfigs.size) {
|
||||
log.error(
|
||||
`The following test files are not selected by any jest config file:\n${fmtList(
|
||||
missingConfigs
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
if (multipleConfigs.size) {
|
||||
const overlaps = new Map<string, { configs: string[]; rels: string[] }>();
|
||||
for (const { configs, rel } of multipleConfigs) {
|
||||
const key = configs.join(':');
|
||||
const group = overlaps.get(key);
|
||||
if (group) {
|
||||
group.rels.push(rel);
|
||||
} else {
|
||||
overlaps.set(key, {
|
||||
configs,
|
||||
rels: [rel],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const list = [...overlaps.values()]
|
||||
.map(
|
||||
({ configs, rels }) =>
|
||||
`configs: ${configs
|
||||
.map((c) => Path.relative(REPO_ROOT, c))
|
||||
.join(', ')}\ntests:\n${fmtList(rels)}`
|
||||
)
|
||||
.join('\n\n');
|
||||
|
||||
log.error(`The following test files are selected by multiple config files:\n${list}`);
|
||||
}
|
||||
|
||||
if (missingConfigs.size || multipleConfigs.size) {
|
||||
throw createFailError('Please resolve the previously logged issues.');
|
||||
}
|
||||
|
||||
log.success('Checked all jest config files in', fmtMs(performance.now() - start));
|
||||
},
|
||||
{
|
||||
description: 'Check that all test files are covered by a Jest config',
|
||||
flags: {
|
||||
boolean: ['fix'],
|
||||
help: `
|
||||
--fix Attempt to create missing config files
|
||||
`,
|
||||
},
|
||||
description: 'Check that all test files are covered by one, and only one, Jest config',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../..',
|
||||
roots: ['<rootDir>/src/core'],
|
||||
testRunner: 'jasmine2',
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../..',
|
||||
roots: ['<rootDir>/src/plugins/chart_expressions'],
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../..',
|
||||
roots: ['<rootDir>/src/plugins/vis_types'],
|
||||
};
|
|
@ -2567,7 +2567,7 @@
|
|||
"@types/istanbul-reports" "^1.1.1"
|
||||
"@types/yargs" "^13.0.0"
|
||||
|
||||
"@jest/types@^26.6.2":
|
||||
"@jest/types@^26", "@jest/types@^26.6.2":
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e"
|
||||
integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==
|
||||
|
@ -17750,7 +17750,7 @@ jest-cli@^26.6.3:
|
|||
prompts "^2.0.1"
|
||||
yargs "^15.4.1"
|
||||
|
||||
jest-config@^26.6.3:
|
||||
jest-config@^26, jest-config@^26.6.3:
|
||||
version "26.6.3"
|
||||
resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349"
|
||||
integrity sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==
|
||||
|
@ -18163,7 +18163,7 @@ jest-runner@^26.6.3:
|
|||
source-map-support "^0.5.6"
|
||||
throat "^5.0.0"
|
||||
|
||||
jest-runtime@^26.6.3:
|
||||
jest-runtime@^26, jest-runtime@^26.6.3:
|
||||
version "26.6.3"
|
||||
resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b"
|
||||
integrity sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue