403 lines
10 KiB
JavaScript
403 lines
10 KiB
JavaScript
'use strict';
|
|
|
|
const {spawn} = require('child_process');
|
|
const chalk = require('chalk');
|
|
const yargs = require('yargs');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const semver = require('semver');
|
|
|
|
const ossConfig = './scripts/jest/config.source.js';
|
|
const wwwConfig = './scripts/jest/config.source-www.js';
|
|
const devToolsConfig = './scripts/jest/config.build-devtools.js';
|
|
|
|
// TODO: These configs are separate but should be rolled into the configs above
|
|
// so that the CLI can provide them as options for any of the configs.
|
|
const persistentConfig = './scripts/jest/config.source-persistent.js';
|
|
const buildConfig = './scripts/jest/config.build.js';
|
|
|
|
const {ReactVersion} = require('../../ReactVersions');
|
|
|
|
const argv = yargs
|
|
.parserConfiguration({
|
|
// Important: This option tells yargs to move all other options not
|
|
// specified here into the `_` key. We use this to send all of the
|
|
// Jest options that we don't use through to Jest (like --watch).
|
|
'unknown-options-as-args': true,
|
|
})
|
|
.wrap(yargs.terminalWidth())
|
|
.options({
|
|
debug: {
|
|
alias: 'd',
|
|
describe: 'Run with node debugger attached.',
|
|
requiresArg: false,
|
|
type: 'boolean',
|
|
default: false,
|
|
},
|
|
project: {
|
|
alias: 'p',
|
|
describe: 'Run the given project.',
|
|
requiresArg: true,
|
|
type: 'string',
|
|
default: 'default',
|
|
choices: ['default', 'devtools'],
|
|
},
|
|
releaseChannel: {
|
|
alias: 'r',
|
|
describe: 'Run with the given release channel.',
|
|
requiresArg: true,
|
|
type: 'string',
|
|
default: 'experimental',
|
|
choices: ['experimental', 'stable', 'www-classic', 'www-modern'],
|
|
},
|
|
env: {
|
|
alias: 'e',
|
|
describe: 'Run with the given node environment.',
|
|
requiresArg: true,
|
|
type: 'string',
|
|
choices: ['development', 'production'],
|
|
},
|
|
prod: {
|
|
describe: 'Run with NODE_ENV=production.',
|
|
requiresArg: false,
|
|
type: 'boolean',
|
|
default: false,
|
|
},
|
|
dev: {
|
|
describe: 'Run with NODE_ENV=development.',
|
|
requiresArg: false,
|
|
type: 'boolean',
|
|
default: false,
|
|
},
|
|
variant: {
|
|
alias: 'v',
|
|
describe: 'Run with www variant set to true.',
|
|
requiresArg: false,
|
|
type: 'boolean',
|
|
},
|
|
build: {
|
|
alias: 'b',
|
|
describe: 'Run tests on builds.',
|
|
requiresArg: false,
|
|
type: 'boolean',
|
|
default: false,
|
|
},
|
|
persistent: {
|
|
alias: 'n',
|
|
describe: 'Run with persistence.',
|
|
requiresArg: false,
|
|
type: 'boolean',
|
|
default: false,
|
|
},
|
|
ci: {
|
|
describe: 'Run tests in CI',
|
|
requiresArg: false,
|
|
type: 'boolean',
|
|
default: false,
|
|
},
|
|
deprecated: {
|
|
describe: 'Print deprecation message for command.',
|
|
requiresArg: true,
|
|
type: 'string',
|
|
},
|
|
compactConsole: {
|
|
alias: 'c',
|
|
describe: 'Compact console output (hide file locations).',
|
|
requiresArg: false,
|
|
type: 'boolean',
|
|
default: false,
|
|
},
|
|
reactVersion: {
|
|
describe: 'DevTools testing for specific version of React',
|
|
requiresArg: true,
|
|
type: 'string',
|
|
},
|
|
sourceMaps: {
|
|
describe:
|
|
'Enable inline source maps when transforming source files with Jest. Useful for debugging, but makes it slower.',
|
|
type: 'boolean',
|
|
default: false,
|
|
},
|
|
}).argv;
|
|
|
|
function logError(message) {
|
|
console.error(chalk.red(`\n${message}`));
|
|
}
|
|
function isWWWConfig() {
|
|
return (
|
|
(argv.releaseChannel === 'www-classic' ||
|
|
argv.releaseChannel === 'www-modern') &&
|
|
argv.project !== 'devtools'
|
|
);
|
|
}
|
|
|
|
function isOSSConfig() {
|
|
return (
|
|
argv.releaseChannel === 'stable' || argv.releaseChannel === 'experimental'
|
|
);
|
|
}
|
|
|
|
function validateOptions() {
|
|
let success = true;
|
|
|
|
if (argv.project === 'devtools') {
|
|
if (argv.prod) {
|
|
logError(
|
|
'DevTool tests do not support --prod. Remove this option to continue.'
|
|
);
|
|
success = false;
|
|
}
|
|
|
|
if (argv.dev) {
|
|
logError(
|
|
'DevTool tests do not support --dev. Remove this option to continue.'
|
|
);
|
|
success = false;
|
|
}
|
|
|
|
if (argv.env) {
|
|
logError(
|
|
'DevTool tests do not support --env. Remove this option to continue.'
|
|
);
|
|
success = false;
|
|
}
|
|
|
|
if (argv.persistent) {
|
|
logError(
|
|
'DevTool tests do not support --persistent. Remove this option to continue.'
|
|
);
|
|
success = false;
|
|
}
|
|
|
|
if (argv.variant) {
|
|
logError(
|
|
'DevTool tests do not support --variant. Remove this option to continue.'
|
|
);
|
|
success = false;
|
|
}
|
|
|
|
if (!argv.build) {
|
|
logError('DevTool tests require --build.');
|
|
success = false;
|
|
}
|
|
|
|
if (argv.reactVersion) {
|
|
if (!semver.validRange(argv.reactVersion)) {
|
|
success = false;
|
|
logError('please specify a valid version range for --reactVersion');
|
|
}
|
|
} else {
|
|
argv.reactVersion = ReactVersion;
|
|
}
|
|
} else {
|
|
if (argv.compactConsole) {
|
|
logError('Only DevTool tests support compactConsole flag.');
|
|
success = false;
|
|
}
|
|
if (argv.reactVersion) {
|
|
logError('Only DevTools tests supports the --reactVersion flag.');
|
|
success = false;
|
|
}
|
|
}
|
|
|
|
if (isWWWConfig()) {
|
|
if (argv.variant === undefined) {
|
|
// Turn internal experiments on by default
|
|
argv.variant = true;
|
|
}
|
|
} else {
|
|
if (argv.variant) {
|
|
logError(
|
|
'Variant is only supported for the www release channels. Update these options to continue.'
|
|
);
|
|
success = false;
|
|
}
|
|
}
|
|
|
|
if (argv.build && argv.persistent) {
|
|
logError(
|
|
'Persistence is not supported for build targets. Update these options to continue.'
|
|
);
|
|
success = false;
|
|
}
|
|
|
|
if (!isOSSConfig() && argv.persistent) {
|
|
logError(
|
|
'Persistence only supported for oss release channels. Update these options to continue.'
|
|
);
|
|
success = false;
|
|
}
|
|
|
|
if (argv.build && isWWWConfig()) {
|
|
logError(
|
|
'Build targets are only not supported for www release channels. Update these options to continue.'
|
|
);
|
|
success = false;
|
|
}
|
|
|
|
if (argv.env && argv.env !== 'production' && argv.prod) {
|
|
logError(
|
|
'Build type does not match --prod. Update these options to continue.'
|
|
);
|
|
success = false;
|
|
}
|
|
|
|
if (argv.env && argv.env !== 'development' && argv.dev) {
|
|
logError(
|
|
'Build type does not match --dev. Update these options to continue.'
|
|
);
|
|
success = false;
|
|
}
|
|
|
|
if (argv.prod && argv.dev) {
|
|
logError(
|
|
'Cannot supply both --prod and --dev. Remove one of these options to continue.'
|
|
);
|
|
success = false;
|
|
}
|
|
|
|
if (argv.build) {
|
|
// TODO: We could build this if it hasn't been built yet.
|
|
const buildDir = path.resolve('./build');
|
|
if (!fs.existsSync(buildDir)) {
|
|
logError(
|
|
'Build directory does not exist, please run `yarn build` or remove the --build option.'
|
|
);
|
|
success = false;
|
|
} else if (Date.now() - fs.statSync(buildDir).mtimeMs > 1000 * 60 * 15) {
|
|
logError(
|
|
'Warning: Running a build test with a build directory older than 15 minutes.\nPlease remember to run `yarn build` when using --build.'
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!success) {
|
|
console.log(''); // Extra newline.
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
function getCommandArgs() {
|
|
// Add the correct Jest config.
|
|
const args = ['./scripts/jest/jest.js', '--config'];
|
|
if (argv.project === 'devtools') {
|
|
args.push(devToolsConfig);
|
|
} else if (argv.build) {
|
|
args.push(buildConfig);
|
|
} else if (argv.persistent) {
|
|
args.push(persistentConfig);
|
|
} else if (isWWWConfig()) {
|
|
args.push(wwwConfig);
|
|
} else if (isOSSConfig()) {
|
|
args.push(ossConfig);
|
|
} else {
|
|
// We should not get here.
|
|
logError('Unrecognized release channel');
|
|
process.exit(1);
|
|
}
|
|
|
|
// Set the debug options, if necessary.
|
|
if (argv.debug) {
|
|
args.unshift('--inspect-brk');
|
|
args.push('--runInBand');
|
|
|
|
// Prevent console logs from being hidden until test completes.
|
|
args.push('--useStderr');
|
|
}
|
|
|
|
// CI Environments have limited workers.
|
|
if (argv.ci) {
|
|
args.push('--maxWorkers=2');
|
|
}
|
|
|
|
// Push the remaining args onto the command.
|
|
// This will send args like `--watch` to Jest.
|
|
args.push(...argv._);
|
|
|
|
return args;
|
|
}
|
|
|
|
function getEnvars() {
|
|
const envars = {
|
|
NODE_ENV: argv.env || 'development',
|
|
RELEASE_CHANNEL: argv.releaseChannel.match(/modern|experimental/)
|
|
? 'experimental'
|
|
: 'stable',
|
|
|
|
// Pass this flag through to the config environment
|
|
// so the base config can conditionally load the console setup file.
|
|
compactConsole: argv.compactConsole,
|
|
};
|
|
|
|
if (argv.prod) {
|
|
envars.NODE_ENV = 'production';
|
|
}
|
|
|
|
if (argv.dev) {
|
|
envars.NODE_ENV = 'development';
|
|
}
|
|
|
|
if (argv.variant) {
|
|
envars.VARIANT = true;
|
|
}
|
|
|
|
if (argv.reactVersion) {
|
|
envars.REACT_VERSION = semver.coerce(argv.reactVersion);
|
|
}
|
|
|
|
if (argv.sourceMaps) {
|
|
// This is off by default because it slows down the test runner, but it's
|
|
// super useful when running the debugger.
|
|
envars.JEST_ENABLE_SOURCE_MAPS = 'inline';
|
|
}
|
|
|
|
return envars;
|
|
}
|
|
|
|
function main() {
|
|
if (argv.deprecated) {
|
|
console.log(chalk.red(`\nPlease run: \`${argv.deprecated}\` instead.\n`));
|
|
return;
|
|
}
|
|
|
|
validateOptions();
|
|
|
|
const args = getCommandArgs();
|
|
const envars = getEnvars();
|
|
const env = Object.entries(envars).map(([k, v]) => `${k}=${v}`);
|
|
|
|
// Print the full command we're actually running.
|
|
const command = `$ ${env.join(' ')} node ${args.join(' ')}`;
|
|
console.log(chalk.dim(command));
|
|
|
|
// Print the release channel and project we're running for quick confirmation.
|
|
console.log(
|
|
chalk.blue(
|
|
`\nRunning tests for ${argv.project} (${argv.releaseChannel})...`
|
|
)
|
|
);
|
|
|
|
// Print a message that the debugger is starting just
|
|
// for some extra feedback when running the debugger.
|
|
if (argv.debug) {
|
|
console.log(chalk.green('\nStarting debugger...'));
|
|
console.log(chalk.green('Open chrome://inspect and press "inspect"\n'));
|
|
}
|
|
|
|
// Run Jest.
|
|
const jest = spawn('node', args, {
|
|
stdio: 'inherit',
|
|
env: {...envars, ...process.env},
|
|
});
|
|
|
|
// Ensure we close our process when we get a failure case.
|
|
jest.on('close', code => {
|
|
// Forward the exit code from the Jest process.
|
|
if (code === 1) {
|
|
process.exit(1);
|
|
}
|
|
});
|
|
}
|
|
|
|
main();
|