mirror of
https://gitlab.com/gitlab-org/gitlab-foss.git
synced 2025-08-03 15:53:48 +00:00
192 lines
5.6 KiB
JavaScript
Executable File
192 lines
5.6 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
const { spawnSync } = require('node:child_process');
|
|
const { readFileSync } = require('node:fs');
|
|
const defaultChalk = require('chalk');
|
|
const { program } = require('commander');
|
|
|
|
const IS_CI = Boolean(process.env.CI);
|
|
|
|
const VUE_3_TESTING_DOCS_URL =
|
|
// eslint-disable-next-line no-restricted-syntax
|
|
'https://docs.gitlab.com/ee/development/testing_guide/testing_vue3.html';
|
|
const VUE_3_TESTING_EPIC = 'https://gitlab.com/groups/gitlab-org/-/epics/11740';
|
|
|
|
// Force basic color output in CI
|
|
const chalk = new defaultChalk.constructor({ level: IS_CI ? 1 : undefined });
|
|
|
|
function showVue3Help() {
|
|
console.warn(' ');
|
|
console.warn(
|
|
chalk.green.bold('Having trouble getting tests to pass under Vue 3? These resources may help:'),
|
|
);
|
|
console.warn(' ');
|
|
console.warn(` - ${chalk.green('Vue 3 testing documentation:')} ${VUE_3_TESTING_DOCS_URL}`);
|
|
console.warn(` - ${chalk.green('Epic for fixing tests under Vue 3:')} w${VUE_3_TESTING_EPIC}`);
|
|
}
|
|
|
|
function parseArgumentsAndEnvironment() {
|
|
program
|
|
.description(`Runs Jest under CI.`)
|
|
.option(
|
|
'--vue3',
|
|
'Run tests under Vue 3 (via @vue/compat). The default is to run under Vue 2. The VUE_VERSION environment variable must agree with this option.',
|
|
)
|
|
.option(
|
|
'--include-vue3-quarantined',
|
|
'Include tests normally quarantined under Vue 3. This is currently used for nightly pipelines. Has no effect without --vue3. The default (given --vue3) is to exclude quarantined tests.',
|
|
)
|
|
.option(
|
|
'--predictive',
|
|
'Only run specs affected by the changes in the merge request. The default is to run all specs.',
|
|
)
|
|
.option(
|
|
'--fixtures',
|
|
'Only run specs which rely on generated fixtures. The default is to only run specs which do not rely on generated fixtures.',
|
|
)
|
|
.option(
|
|
'--coverage',
|
|
"Tell Jest to generate coverage. If not specified, it's enabled only on non-FOSS branch or tag pipelines under Vue 2, non-predictive runs.",
|
|
)
|
|
.parse(process.argv);
|
|
const options = program.opts();
|
|
|
|
if (!IS_CI) {
|
|
console.warn('This script is intended to run in CI only.');
|
|
if (options.vue3) showVue3Help();
|
|
process.exit(1);
|
|
}
|
|
|
|
if (options.vue3 && process.env.VUE_VERSION !== '3') {
|
|
console.warn(
|
|
`Expected environment variable VUE_VERSION=3 given option '--vue3', got VUE_VERSION="${process.env.VUE_VERSION}".`,
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!options.vue3 && ![undefined, '2'].includes(process.env.VUE_VERSION)) {
|
|
console.warn(
|
|
`Expected unset environment variable VUE_VERSION, or VUE_VERSION=2, got VUE_VERSION="${process.env.VUE_VERSION}".`,
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
const changedFiles = [];
|
|
if (options.predictive) {
|
|
const { RSPEC_MATCHING_JS_FILES_PATH, RSPEC_CHANGED_FILES_PATH } = process.env;
|
|
|
|
for (const [name, path] of Object.entries({
|
|
RSPEC_CHANGED_FILES_PATH,
|
|
RSPEC_MATCHING_JS_FILES_PATH,
|
|
})) {
|
|
try {
|
|
const contents = readFileSync(path, { encoding: 'UTF-8' });
|
|
changedFiles.push(...contents.split(/\s+/).filter(Boolean));
|
|
} catch (error) {
|
|
console.warn(
|
|
`Failed to read from path ${path} given by environment variable ${name}`,
|
|
error,
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!changedFiles) {
|
|
console.warn('No changed files detected; will not run Jest.');
|
|
process.exit(0);
|
|
}
|
|
}
|
|
|
|
const coverage =
|
|
options.coverage ||
|
|
(!process.env.CI_MERGE_REQUEST_IID &&
|
|
!/^as-if-foss\//.test(process.env.CI_COMMIT_BRANCH) &&
|
|
!options.vue3 &&
|
|
!options.predictive);
|
|
|
|
return {
|
|
vue3: options.vue3,
|
|
includeVue3Quarantined: options.includeVue3Quarantined,
|
|
predictive: options.predictive,
|
|
fixtures: options.fixtures,
|
|
coverage,
|
|
nodeIndex: process.env.CI_NODE_INDEX ?? '1',
|
|
nodeTotal: process.env.CI_NODE_TOTAL ?? '1',
|
|
changedFiles,
|
|
};
|
|
}
|
|
|
|
function loggedSpawnSync(command, args, options) {
|
|
const env = ['JEST_FIXTURE_JOBS_ONLY', 'VUE_VERSION']
|
|
.map((name) => `${name}=${options.env[name] ?? ''}`)
|
|
.join(' ');
|
|
const fullCommand = `${env} ${command} ${args.join(' ')}`;
|
|
console.warn(`Running command:\n${fullCommand}`);
|
|
const childProcess = spawnSync(command, args, options);
|
|
console.warn(`Command ${fullCommand} exited with status ${childProcess.status}`);
|
|
return childProcess;
|
|
}
|
|
|
|
function runJest({
|
|
vue3,
|
|
includeVue3Quarantined,
|
|
predictive,
|
|
fixtures,
|
|
coverage,
|
|
nodeIndex,
|
|
nodeTotal,
|
|
changedFiles,
|
|
}) {
|
|
const commonArguments = [
|
|
'--config',
|
|
'jest.config.js',
|
|
'--ci',
|
|
`--shard=${nodeIndex}/${nodeTotal}`,
|
|
'--logHeapUsage',
|
|
];
|
|
|
|
const sequencerArguments = [
|
|
'--testSequencer',
|
|
vue3 && !includeVue3Quarantined
|
|
? './scripts/frontend/skip_specs_broken_in_vue_compat_fixture_ci_sequencer.js'
|
|
: './scripts/frontend/fixture_ci_sequencer.js',
|
|
];
|
|
|
|
const predictiveArguments = predictive
|
|
? ['--passWithNoTests', '--findRelatedTests', ...changedFiles]
|
|
: [];
|
|
|
|
const coverageArguments = coverage ? ['--coverage'] : [];
|
|
|
|
const childProcess = loggedSpawnSync(
|
|
'node_modules/.bin/jest',
|
|
[...commonArguments, ...sequencerArguments, ...predictiveArguments, ...coverageArguments],
|
|
{
|
|
stdio: 'inherit',
|
|
env: {
|
|
...process.env,
|
|
...(fixtures ? { JEST_FIXTURE_JOBS_ONLY: '1' } : {}),
|
|
},
|
|
},
|
|
);
|
|
|
|
return childProcess;
|
|
}
|
|
|
|
function main() {
|
|
const config = parseArgumentsAndEnvironment();
|
|
const childProcess = runJest(config);
|
|
|
|
if (childProcess.status !== 0 && config.vue3) {
|
|
showVue3Help();
|
|
}
|
|
|
|
return childProcess.status;
|
|
}
|
|
|
|
try {
|
|
process.exitCode = main();
|
|
} catch (error) {
|
|
process.exitCode = 1;
|
|
console.error(error);
|
|
}
|