UNPKG

10.8 kBJavaScriptView Raw
1import semver from 'semver';
2guardMinimalNodeVersion();
3import { Command } from 'commander';
4import { ALL_REPORT_TYPES } from '@stryker-mutator/api/core';
5import { initializerFactory } from './initializer/index.js';
6import { LogConfigurator } from './logging/index.js';
7import { Stryker } from './stryker.js';
8import { defaultOptions } from './config/index.js';
9import { strykerEngines, strykerVersion } from './stryker-package.js';
10/**
11 * Interpret a command line argument and add it to an object.
12 * @param object The object to assign the value to.
13 * @param key The property name under which the value needs to be stored.
14 */
15function deepOption(object, key) {
16 return (value) => {
17 object[key] = value;
18 return undefined;
19 };
20}
21const list = createSplitter(',');
22function createSplitter(sep) {
23 return (val) => val.split(sep).filter(Boolean);
24}
25function parseBoolean(val) {
26 const v = val.toLocaleLowerCase();
27 return v !== 'false' && v !== '0';
28}
29export class StrykerCli {
30 constructor(argv, program = new Command(), runMutationTest = async (options) => new Stryker(options).runMutationTest()) {
31 this.argv = argv;
32 this.program = program;
33 this.runMutationTest = runMutationTest;
34 this.command = '';
35 this.strykerConfig = null;
36 }
37 run() {
38 const dashboard = {};
39 this.program
40 // eslint-disable-next-line @typescript-eslint/no-require-imports
41 .version(strykerVersion)
42 .usage('<command> [options] [configFile]')
43 .description(`Possible commands:
44 run: Run mutation testing
45 init: Initialize Stryker for your project
46
47 Optional location to a JSON or JavaScript config file as the last argument. If it's a JavaScript file, that file should export the config directly.`)
48 .arguments('<command> [configFile]')
49 .action((cmd, config) => {
50 this.command = cmd;
51 this.strykerConfig = config;
52 })
53 .option('-f, --files <allFiles>', '[DEPRECATED, please use the inverse option `--ignorePatterns` instead] A comma separated list of patterns used for selecting all files needed to run the tests. For a more detailed way of selecting input files, please use a configFile. Example: src/**/*.js,!src/index.js,a.js,test/**/*.js.', list)
54 .option('--ignorePatterns <filesToIgnore>', 'A comma separated list of patterns used for specifying which files need to be ignored. This should only be used in cases where you experience a slow Stryker startup, because too many (or too large) files are copied to the sandbox that are not needed to run the tests. For example, image or movie directories. Note: This option will have no effect when using the --inPlace option. The directories `node_modules`, `.git` and some others are always ignored. Example: --ignorePatterns dist', list)
55 .option('--ignoreStatic', 'Ignore static mutants. Static mutants are mutants which are only executed during the loading of a file.')
56 .option('--incremental', "Enable 'incremental mode'. Stryker will store results in a file and use that file to speed up the next --incremental run")
57 .option('--incrementalFile <file>', 'Specify the file to use for incremental mode.')
58 .option('--force', 'Run all mutants, even if --incremental is provided and an incremental file exists. Can be used to force a rebuild of the incremental file.')
59 .option('-m, --mutate <filesToMutate>', 'A comma separated list of globbing expression used for selecting the files that should be mutated. Example: src/**/*.js,a.js. You can also specify specific lines and columns to mutate by adding :startLine[:startColumn]-endLine[:endColumn]. This will execute all mutants inside that range. It cannot be combined with glob patterns. Example: src/index.js:1:3-1:5', list)
60 .option('-b, --buildCommand <command>', 'Configure a build command to run after mutating the code, but before mutants are tested. This is generally used to transpile your code before testing.' +
61 " Only configure this if your test runner doesn't take care of this already and you're not using just-in-time transpiler like `babel/register` or `ts-node`.")
62 .option('--dryRunOnly', 'Execute the initial test run only, without doing actual mutation testing. Doing a dry run only can be used to test that StrykerJS can run your test setup, for example, in CI pipelines.')
63 .option('--checkers <listOfCheckersOrEmptyString>', 'A comma separated list of checkers to use, for example --checkers typescript', createSplitter(','))
64 .option('--checkerNodeArgs <listOfNodeArgs>', 'A list of node args to be passed to checker child processes.', createSplitter(' '))
65 .option('--coverageAnalysis <perTest|all|off>', `The coverage analysis strategy you want to use. Default value: "${defaultOptions.coverageAnalysis}"`)
66 .option('--testRunner <name>', 'The name of the test runner you want to use')
67 .option('--testRunnerNodeArgs <listOfNodeArgs>', 'A comma separated list of node args to be passed to test runner child processes.', createSplitter(' '))
68 .option('--reporters <name>', 'A comma separated list of the names of the reporter(s) you want to use', list)
69 .option('--plugins <listOfPlugins>', 'A list of plugins you want stryker to load (`require`).', list)
70 .option('--appendPlugins <listOfPlugins>', 'A list of additional plugins you want Stryker to load (`require`) without overwriting the (default) `plugins`.', list)
71 .option('--timeoutMS <number>', 'Tweak the absolute timeout used to wait for a test runner to complete', parseInt)
72 .option('--timeoutFactor <number>', 'Tweak the standard deviation relative to the normal test run of a mutated test', parseFloat)
73 .option('--dryRunTimeoutMinutes <number>', 'Configure an absolute timeout for the initial test run. (It can take a while.)', parseFloat)
74 .option('--maxConcurrentTestRunners <n>', 'Set the number of max concurrent test runner to spawn (default: cpuCount)', parseInt)
75 .option('-c, --concurrency <n>', 'Set the concurrency of workers. Stryker will always run checkers and test runners in parallel by creating worker processes (default: cpuCount - 1)', parseInt)
76 .option('--disableBail', 'Force the test runner to keep running tests, even when a mutant is already killed.')
77 .option('--maxTestRunnerReuse <n>', 'Restart each test runner worker process after `n` runs. Not recommended unless you are experiencing memory leaks that you are unable to resolve. Configuring `0` here means infinite reuse.', parseInt)
78 .option('--logLevel <level>', `Set the log level for the console. Possible values: fatal, error, warn, info, debug, trace and off. Default is "${defaultOptions.logLevel}"`)
79 .option('--fileLogLevel <level>', `Set the log4js log level for the "stryker.log" file. Possible values: fatal, error, warn, info, debug, trace and off. Default is "${defaultOptions.fileLogLevel}"`)
80 .option('--allowConsoleColors <true/false>', 'Indicates whether or not Stryker should use colors in console.', parseBoolean)
81 .option('--dashboard.project <name>', 'Indicates which project name to use if the "dashboard" reporter is enabled. Defaults to the git url configured in the environment of your CI server.', deepOption(dashboard, 'project'))
82 .option('--dashboard.version <version>', 'Indicates which version to use if the "dashboard" reporter is enabled. Defaults to the branch name or tag name configured in the environment of your CI server.', deepOption(dashboard, 'version'))
83 .option('--dashboard.module <name>', 'Indicates which module name to use if the "dashboard" reporter is enabled.', deepOption(dashboard, 'module'))
84 .option('--dashboard.baseUrl <url>', `Indicates which baseUrl to use when reporting to the stryker dashboard. Default: "${defaultOptions.dashboard.baseUrl}"`, deepOption(dashboard, 'baseUrl'))
85 .option(`--dashboard.reportType <${ALL_REPORT_TYPES.join('|')}>`, `Send a full report (inc. source code and mutant results) or only the mutation score. Default: ${defaultOptions.dashboard.reportType}`, deepOption(dashboard, 'reportType'))
86 .option('--inPlace', 'Enable Stryker to mutate your files in place and put back the originals after its done. Note: mutating your files in place is generally not needed for mutation testing.')
87 .option('--tempDirName <name>', 'Set the name of the directory that is used by Stryker as a working directory. This directory will be cleaned after a successful run')
88 .option('--cleanTempDir <true/false>', `Choose whether or not to clean the temp dir (which is "${defaultOptions.tempDirName}" inside the current working directory by default) after a successful run. The temp dir will never be removed when the run failed for some reason (for debugging purposes).`, parseBoolean)
89 .showSuggestionAfterError()
90 .parse(this.argv);
91 // Earliest opportunity to configure the log level based on the logLevel argument
92 const options = this.program.opts();
93 LogConfigurator.configureMainProcess(options.logLevel);
94 // Cleanup commander state
95 delete options.version;
96 Object.keys(options)
97 .filter((key) => key.startsWith('dashboard.'))
98 .forEach((key) => delete options[key]);
99 if (this.strykerConfig) {
100 options.configFile = this.strykerConfig;
101 }
102 if (Object.keys(dashboard).length > 0) {
103 options.dashboard = dashboard;
104 }
105 const commands = {
106 init: () => initializerFactory().initialize(),
107 run: () => this.runMutationTest(options),
108 };
109 if (Object.keys(commands).includes(this.command)) {
110 const promise = commands[this.command]();
111 promise.catch(() => {
112 process.exitCode = 1;
113 });
114 }
115 else {
116 console.error('Unknown command: "%s", supported commands: [%s], or use `stryker --help`.', this.command, Object.keys(commands));
117 }
118 }
119}
120export function guardMinimalNodeVersion(processVersion = process.version) {
121 // eslint-disable-next-line @typescript-eslint/no-require-imports
122 if (!semver.satisfies(processVersion, strykerEngines.node)) {
123 throw new Error(`Node.js version ${processVersion} detected. StrykerJS requires version to match ${strykerEngines.node}. Please update your Node.js version or visit https://nodejs.org/ for additional instructions`);
124 }
125}
126//# sourceMappingURL=stryker-cli.js.map
\No newline at end of file