UNPKG

@stryker-mutator/core

Version:

The extendable JavaScript mutation testing framework

184 lines 7.9 kB
import childProcess from 'child_process'; import { promises as fsPromises } from 'fs'; import { commonTokens, tokens } from '@stryker-mutator/api/plugin'; import { notEmpty } from '@stryker-mutator/util'; import { initializerTokens } from './index.js'; export class StrykerInitializer { log; out; client; customInitializers; configWriter; gitignoreWriter; inquirer; static inject = tokens(commonTokens.logger, initializerTokens.out, initializerTokens.npmClient, initializerTokens.customInitializers, initializerTokens.configWriter, initializerTokens.gitignoreWriter, initializerTokens.inquirer); constructor(log, out, client, customInitializers, configWriter, gitignoreWriter, inquirer) { this.log = log; this.out = out; this.client = client; this.customInitializers = customInitializers; this.configWriter = configWriter; this.gitignoreWriter = gitignoreWriter; this.inquirer = inquirer; } /** * Runs the initializer will prompt the user for questions about his setup. After that, install plugins and configure Stryker. * @function */ async initialize() { await this.configWriter.guardForExistingConfig(); this.patchProxies(); const selectedPreset = await this.selectCustomInitializer(); let configFileName; if (selectedPreset) { configFileName = await this.initiateInitializer(this.configWriter, selectedPreset); } else { configFileName = await this.initiateCustom(this.configWriter); } await this.gitignoreWriter.addStrykerTempFolder(); this.out(`Done configuring stryker. Please review "${configFileName}", you might need to configure your test runner correctly.`); this.out("Let's kill some mutants with this command: `stryker run`"); } /** * The typed rest client works only with the specific HTTP_PROXY and HTTPS_PROXY env settings. * Let's make sure they are available. */ patchProxies() { const copyEnvVariable = (from, to) => { if (process.env[from] && !process.env[to]) { process.env[to] = process.env[from]; } }; copyEnvVariable('http_proxy', 'HTTP_PROXY'); copyEnvVariable('https_proxy', 'HTTPS_PROXY'); } async selectCustomInitializer() { const customInitializer = this.customInitializers; if (customInitializer.length) { this.log.debug(`Found presets: ${JSON.stringify(customInitializer)}`); return this.inquirer.promptPresets(customInitializer); } else { this.log.debug('No presets have been configured, reverting to custom configuration'); return undefined; } } async initiateInitializer(configWriter, selectedPreset) { const presetConfig = await selectedPreset.createConfig(); const isJsonSelected = await this.selectJsonConfigType(); const configFileName = await configWriter.writeCustomInitializer(presetConfig, isJsonSelected); if (presetConfig.additionalConfigFiles) { await Promise.all(Object.entries(presetConfig.additionalConfigFiles).map(([name, content]) => fsPromises.writeFile(name, content))); } const selectedPackageManager = await this.selectPackageManager(); this.installNpmDependencies(this.ensureCoreDependencyIncluded(presetConfig.dependencies), selectedPackageManager); return configFileName; } async initiateCustom(configWriter) { const selectedTestRunner = await this.selectTestRunner(); const buildCommand = await this.getBuildCommand(selectedTestRunner); const selectedReporters = await this.selectReporters(); const selectedPackageManager = await this.selectPackageManager(); const isJsonSelected = await this.selectJsonConfigType(); const npmDependencies = this.getSelectedNpmDependencies([selectedTestRunner].concat(selectedReporters)); const packageInfo = await this.fetchAdditionalConfig(npmDependencies); const pkgInfoOfSelectedTestRunner = packageInfo.find((pkg) => pkg.name == selectedTestRunner.pkg?.name); const additionalConfig = packageInfo .map((dep) => dep.initStrykerConfig ?? {}) .filter(notEmpty); const configFileName = await configWriter.write(selectedTestRunner, buildCommand, selectedReporters, selectedPackageManager, npmDependencies.map((pkg) => pkg.name), additionalConfig, pkgInfoOfSelectedTestRunner?.homepage ?? "(missing 'homepage' URL in package.json)", isJsonSelected); this.installNpmDependencies(this.ensureCoreDependencyIncluded(npmDependencies.map((pkg) => pkg.name)), selectedPackageManager); return configFileName; } async selectTestRunner() { const testRunnerOptions = await this.client.getTestRunnerOptions(); this.log.debug(`Found test runners: ${JSON.stringify(testRunnerOptions)}`); return this.inquirer.promptTestRunners(testRunnerOptions); } async getBuildCommand(selectedTestRunner) { if (selectedTestRunner.name !== 'jest') { return this.inquirer.promptBuildCommand(); } return { name: '', pkg: null }; } async selectReporters() { const reporterOptions = await this.client.getTestReporterOptions(); reporterOptions.push({ name: 'html', pkg: null, }, { name: 'clear-text', pkg: null, }, { name: 'progress', pkg: null, }, { name: 'dashboard', pkg: null, }); return this.inquirer.promptReporters(reporterOptions); } async selectPackageManager() { return this.inquirer.promptPackageManager([ { name: "npm" /* PackageManager.Npm */, pkg: null, }, { name: "yarn" /* PackageManager.Yarn */, pkg: null, }, { name: "pnpm" /* PackageManager.Pnpm */, pkg: null, }, ]); } async selectJsonConfigType() { return this.inquirer.promptJsonConfigFormat(); } getSelectedNpmDependencies(selectedOptions) { return selectedOptions .filter(notEmpty) .map((option) => option.pkg) .filter(notEmpty); } /** * Install the npm packages * @function */ installNpmDependencies(dependencies, selectedOption) { if (dependencies.length === 0) { return; } const dependencyArg = dependencies.join(' '); this.out('Installing NPM dependencies...'); const cmd = this.getInstallCommand(selectedOption.name, dependencyArg); this.out(cmd); try { childProcess.execSync(cmd, { stdio: [0, 1, 2] }); } catch { this.out(`An error occurred during installation, please try it yourself: "${cmd}"`); } } getInstallCommand(packageManager, dependencyArg) { switch (packageManager) { case "yarn" /* PackageManager.Yarn */: return `yarn add ${dependencyArg} --dev`; case "pnpm" /* PackageManager.Pnpm */: return `pnpm add -D ${dependencyArg}`; case "npm" /* PackageManager.Npm */: return `npm i --save-dev ${dependencyArg}`; } } async fetchAdditionalConfig(dependencies) { return await Promise.all(dependencies.map((dep) => this.client.getAdditionalConfig(dep))); } ensureCoreDependencyIncluded(dependencies) { return Array.from(new Set(['@stryker-mutator/core', ...dependencies])); } } //# sourceMappingURL=stryker-initializer.js.map