From 532b7defb4b6b0b515418469d140462225b888c6 Mon Sep 17 00:00:00 2001 From: WouterA Date: Mon, 19 Nov 2018 14:48:18 +0100 Subject: [PATCH 01/13] Dependency changes --- package.json | 2 +- packages/stryker-javascript-mutator/package.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 75fc882e31..5f1cd1d6ce 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@types/mkdirp": "^0.5.2", "@types/mocha": "^2.2.44", "@types/rimraf": "2.0.2", - "@types/sinon": "^5.0.1", + "@types/sinon": "5.0.5", "@types/sinon-chai": "^3.2.0", "browserify": "^16.2.0", "chai": "^4.1.1", diff --git a/packages/stryker-javascript-mutator/package.json b/packages/stryker-javascript-mutator/package.json index ff51cb2846..ab69aaefe6 100644 --- a/packages/stryker-javascript-mutator/package.json +++ b/packages/stryker-javascript-mutator/package.json @@ -47,7 +47,6 @@ "devDependencies": { "@babel/types": "~7.1.2", "@types/babel__generator": "^7.0.0", - "@types/babel__parser": "^7.0.0", "@types/babel__traverse": "^7.0.0", "stryker-api": "^0.21.5", "stryker-mutator-specification": "^0.6.0" From 0ec20a807a029bfd45b4521f987a1baeb4e3c3a1 Mon Sep 17 00:00:00 2001 From: WouterA Date: Tue, 20 Nov 2018 16:37:45 +0100 Subject: [PATCH 02/13] Prompt for selecting preset config, with simple tests --- packages/stryker/presets/angular.conf.js | 22 ++++++ packages/stryker/presets/react-jsx.conf.js | 12 +++ packages/stryker/presets/react-tsx.conf.js | 12 +++ packages/stryker/presets/vuejs-jest.conf.js | 12 +++ packages/stryker/presets/vuejs-karma.conf.js | 15 ++++ .../stryker/src/initializer/PresetOption.ts | 6 ++ .../src/initializer/StrykerConfigWriter.ts | 14 +++- .../src/initializer/StrykerInitializer.ts | 64 +++++++++++----- .../src/initializer/StrykerInquirer.ts | 14 ++++ .../initializer/StrykerInitializerSpec.ts | 73 ++++++++++++++----- 10 files changed, 202 insertions(+), 42 deletions(-) create mode 100644 packages/stryker/presets/angular.conf.js create mode 100644 packages/stryker/presets/react-jsx.conf.js create mode 100644 packages/stryker/presets/react-tsx.conf.js create mode 100644 packages/stryker/presets/vuejs-jest.conf.js create mode 100644 packages/stryker/presets/vuejs-karma.conf.js create mode 100644 packages/stryker/src/initializer/PresetOption.ts diff --git a/packages/stryker/presets/angular.conf.js b/packages/stryker/presets/angular.conf.js new file mode 100644 index 0000000000..c9f0db6975 --- /dev/null +++ b/packages/stryker/presets/angular.conf.js @@ -0,0 +1,22 @@ +module.exports = function (config) { + config.set({ + mutate: [ + 'src/**/*.ts', + '!src/**/*.spec.ts', + '!src/test.ts', + '!src/environments/*.ts' + ], + mutator: 'typescript', + testRunner: 'karma', + karma: { + configFile: 'src/karma.conf.js', + projectType: 'angular-cli', + config: { + browsers: ['ChromeHeadless'] + } + }, + reporters: ['progress', 'clear-text', 'html'], + // maxConcurrentTestRunners: 2, // Recommended to not use all cores when running stryker with angular. + coverageAnalysis: 'off' + }); +}; \ No newline at end of file diff --git a/packages/stryker/presets/react-jsx.conf.js b/packages/stryker/presets/react-jsx.conf.js new file mode 100644 index 0000000000..ac2822e66a --- /dev/null +++ b/packages/stryker/presets/react-jsx.conf.js @@ -0,0 +1,12 @@ +module.exports = function (config) { + config.set({ + mutate: ['src/**/*.js?(x)', '!src/**/*@(.test|.spec|Spec).js?(x)'], + mutator: 'javascript', + testRunner: 'jest', + reporter: ['progress', 'clear-text', 'html'], + coverageAnalysis: 'off', + jest: { + project: 'react' + } + }); +} \ No newline at end of file diff --git a/packages/stryker/presets/react-tsx.conf.js b/packages/stryker/presets/react-tsx.conf.js new file mode 100644 index 0000000000..c545b182e8 --- /dev/null +++ b/packages/stryker/presets/react-tsx.conf.js @@ -0,0 +1,12 @@ +module.exports = function (config) { + config.set({ + mutate: ['src/**/*.ts?(x)', '!src/**/*@(.test|.spec|Spec).ts?(x)'], + mutator: 'typescript', + testRunner: 'jest', + reporter: ['progress', 'clear-text', 'html'], + coverageAnalysis: 'off', + jest: { + project: 'react' + } + }); +} \ No newline at end of file diff --git a/packages/stryker/presets/vuejs-jest.conf.js b/packages/stryker/presets/vuejs-jest.conf.js new file mode 100644 index 0000000000..e275ceb246 --- /dev/null +++ b/packages/stryker/presets/vuejs-jest.conf.js @@ -0,0 +1,12 @@ +module.exports = function (config) { + config.set({ + mutate: ['src/**/*.js', 'src/**/*.ts', 'src/**/*.vue'], + mutator: 'vue', + testRunner: 'jest', + jest: { + // config: require('path/to/your/custom/jestConfig.js') + }, + reporter: ['progress', 'clear-text', 'html'], + coverageAnalysis: 'off' + }); + }; \ No newline at end of file diff --git a/packages/stryker/presets/vuejs-karma.conf.js b/packages/stryker/presets/vuejs-karma.conf.js new file mode 100644 index 0000000000..b90573577b --- /dev/null +++ b/packages/stryker/presets/vuejs-karma.conf.js @@ -0,0 +1,15 @@ +module.exports = function (config) { + config.set({ + mutate: ['src/**/*.js', 'src/**/*.ts', 'src/**/*.vue'], + mutator: 'vue', + testRunner: 'karma', + karma: { + configFile: 'test/unit/karma.conf.js', + config: { + browsers: ['ChromeHeadless'] + } + }, + reporter: ['progress', 'clear-text', 'html'], + coverageAnalysis: 'off' + }); +}; \ No newline at end of file diff --git a/packages/stryker/src/initializer/PresetOption.ts b/packages/stryker/src/initializer/PresetOption.ts new file mode 100644 index 0000000000..fa79cb0550 --- /dev/null +++ b/packages/stryker/src/initializer/PresetOption.ts @@ -0,0 +1,6 @@ +interface PresetOption { + name: string; + file: string; +} + +export default PresetOption; diff --git a/packages/stryker/src/initializer/StrykerConfigWriter.ts b/packages/stryker/src/initializer/StrykerConfigWriter.ts index b06e2c22cb..c9ccb07df7 100644 --- a/packages/stryker/src/initializer/StrykerConfigWriter.ts +++ b/packages/stryker/src/initializer/StrykerConfigWriter.ts @@ -3,6 +3,7 @@ import { fsAsPromised } from '@stryker-mutator/util'; import { getLogger } from 'stryker-api/logging'; import { StrykerOptions } from 'stryker-api/core'; import PromptOption from './PromptOption'; +import PresetOption from './PresetOption'; import { format } from 'prettier'; const STRYKER_CONFIG_FILE = 'stryker.conf.js'; @@ -47,6 +48,11 @@ export default class StrykerConfigWriter { return this.writeStrykerConfig(configObject); } + public async writePreset(preset: PresetOption) { + const stream = await fsAsPromised.readFile(`presets/${preset.file}`); + return this.writeStrykerConfigRaw(stream.toString()); + } + private configureTestFramework(configObject: Partial, selectedTestFramework: null | PromptOption) { if (selectedTestFramework) { configObject.testFramework = selectedTestFramework.name; @@ -56,9 +62,13 @@ export default class StrykerConfigWriter { } } - private writeStrykerConfig(configObject: Partial) { + private writeStrykerConfigRaw(rawString: string) { this.out('Writing stryker.conf.js...'); - return fsAsPromised.writeFile(STRYKER_CONFIG_FILE, this.wrapInModule(configObject)); + return fsAsPromised.writeFile(STRYKER_CONFIG_FILE, rawString); + } + + private writeStrykerConfig(configObject: Partial) { + return this.writeStrykerConfigRaw(this.wrapInModule(configObject)); } private wrapInModule(configObject: Partial) { diff --git a/packages/stryker/src/initializer/StrykerInitializer.ts b/packages/stryker/src/initializer/StrykerInitializer.ts index 5ec668af4e..8b6294e56c 100644 --- a/packages/stryker/src/initializer/StrykerInitializer.ts +++ b/packages/stryker/src/initializer/StrykerInitializer.ts @@ -6,6 +6,8 @@ import { getLogger } from 'stryker-api/logging'; import { filterEmpty } from '../utils/objectUtils'; import StrykerConfigWriter from './StrykerConfigWriter'; import CommandTestRunner from '../test-runner/CommandTestRunner'; +import { fsAsPromised } from '@stryker-mutator/util'; +import PresetOption from './PresetOption'; const enum PackageManager { Npm = 'npm', @@ -27,26 +29,32 @@ export default class StrykerInitializer { const configWriter = new StrykerConfigWriter(this.out); configWriter.guardForExistingConfig(); this.patchProxies(); - const selectedTestRunner = await this.selectTestRunner(); - const selectedTestFramework = selectedTestRunner && !CommandTestRunner.is(selectedTestRunner.name) - ? await this.selectTestFramework(selectedTestRunner) : null; - const selectedMutator = await this.selectMutator(); - const selectedTranspilers = await this.selectTranspilers(); - const selectedReporters = await this.selectReporters(); - const selectedPackageManager = await this.selectPackageManager(); - const npmDependencies = this.getSelectedNpmDependencies( - [selectedTestRunner, selectedTestFramework, selectedMutator] - .concat(selectedTranspilers) - .concat(selectedReporters) - ); - await configWriter.write(selectedTestRunner, - selectedTestFramework, - selectedMutator, - selectedTranspilers, - selectedReporters, - selectedPackageManager, - await this.fetchAdditionalConfig(npmDependencies)); - this.installNpmDependencies(npmDependencies, selectedPackageManager); + const selectedPreset = await this.selectPreset(); + if (selectedPreset) { + await configWriter.writePreset(selectedPreset); + } + else { + const selectedTestRunner = await this.selectTestRunner(); + const selectedTestFramework = selectedTestRunner && !CommandTestRunner.is(selectedTestRunner.name) + ? await this.selectTestFramework(selectedTestRunner) : null; + const selectedMutator = await this.selectMutator(); + const selectedTranspilers = await this.selectTranspilers(); + const selectedReporters = await this.selectReporters(); + const selectedPackageManager = await this.selectPackageManager(); + const npmDependencies = this.getSelectedNpmDependencies( + [selectedTestRunner, selectedTestFramework, selectedMutator] + .concat(selectedTranspilers) + .concat(selectedReporters) + ); + await configWriter.write(selectedTestRunner, + selectedTestFramework, + selectedMutator, + selectedTranspilers, + selectedReporters, + selectedPackageManager, + await this.fetchAdditionalConfig(npmDependencies)); + this.installNpmDependencies(npmDependencies, selectedPackageManager); + } this.out('Done configuring stryker. Please review `stryker.conf.js`, you might need to configure transpilers or your test runner correctly.'); this.out('Let\'s kill some mutants with this command: `stryker run`'); } @@ -65,6 +73,22 @@ export default class StrykerInitializer { copyEnvVariable('https_proxy', 'HTTPS_PROXY'); } + private async selectPreset(): Promise { + if (fsAsPromised.existsSync('presets')) { + const files: string[] = await fsAsPromised.readdir('presets'); + if (files.length) { + const presetOptions: PresetOption[] = files.map(file => ({ name: file.split('.')[0], file})); + this.log.debug(`Found presets runners: ${JSON.stringify(presetOptions)}`); + return this.inquirer.promptPresets(presetOptions); + } else { + this.log.debug('The presets folder is empty. Reverting to custom configuration.'); + } + } else { + this.log.debug('No presets folder defined. Reverting to custom configuration.'); + } + return null; + } + private async selectTestRunner(): Promise { const testRunnerOptions = await this.client.getTestRunnerOptions(); if (testRunnerOptions.length) { diff --git a/packages/stryker/src/initializer/StrykerInquirer.ts b/packages/stryker/src/initializer/StrykerInquirer.ts index e2fe6d9e9d..e31d0796cf 100644 --- a/packages/stryker/src/initializer/StrykerInquirer.ts +++ b/packages/stryker/src/initializer/StrykerInquirer.ts @@ -1,6 +1,7 @@ import * as inquirer from 'inquirer'; import PromptOption from './PromptOption'; import CommandTestRunner from '../test-runner/CommandTestRunner'; +import PresetOption from './PresetOption'; export interface PromptResult { additionalNpmDependencies: string[]; @@ -9,6 +10,19 @@ export interface PromptResult { export class StrykerInquirer { + public async promptPresets(options: PresetOption[]): Promise { + const choices: inquirer.ChoiceType[] = options.map(_ => _.name); + choices.push(new inquirer.Separator()); + choices.push('none'); + const answers = await inquirer.prompt<{ preset: string }>({ + choices, + message: 'Are you using one of these frameworks? Then select a preset configuration.', + name: 'preset', + type: 'list' + }); + return options.filter(_ => _.name === answers.preset)[0]; + } + public async promptTestRunners(options: PromptOption[]): Promise { const choices: inquirer.ChoiceType[] = options.map(_ => _.name); choices.push(new inquirer.Separator()); diff --git a/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts b/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts index cc2c0581e2..ae71e029bf 100644 --- a/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts +++ b/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts @@ -14,6 +14,8 @@ describe('StrykerInitializer', () => { let sut: StrykerInitializer; let inquirerPrompt: sinon.SinonStub; let childExecSync: sinon.SinonStub; + let fsReaddir: sinon.SinonStub; + let fsReadFile: sinon.SinonStub; let fsWriteFile: sinon.SinonStub; let fsExistsSync: sinon.SinonStub; let restClientPackageGet: sinon.SinonStub; @@ -25,6 +27,8 @@ describe('StrykerInitializer', () => { out = sandbox.stub(); inquirerPrompt = sandbox.stub(inquirer, 'prompt'); childExecSync = sandbox.stub(child, 'execSync'); + fsReaddir = sandbox.stub(fsAsPromised, 'readdir'); + fsReadFile = sandbox.stub(fsAsPromised, 'readFile'); fsWriteFile = sandbox.stub(fsAsPromised, 'writeFile'); fsExistsSync = sandbox.stub(fsAsPromised, 'existsSync'); restClientSearchGet = sandbox.stub(); @@ -67,9 +71,11 @@ describe('StrykerInitializer', () => { 'stryker-webpack': null }); fsWriteFile.resolves({}); + fsExistsSync.withArgs('presets').resolves(true); + fsReaddir.withArgs('presets').resolves(['awesome-preset.conf.txt']); }); - it('should prompt for test runner, test framework, mutator, transpilers, reporters, and package manager', async () => { + it('should prompt for preset, test runner, test framework, mutator, transpilers, reporters, and package manager', async () => { arrangeAnswers({ mutator: 'typescript', packageManager: 'yarn', @@ -79,15 +85,19 @@ describe('StrykerInitializer', () => { transpilers: ['webpack'] }); await sut.initialize(); - expect(inquirerPrompt).to.have.been.callCount(6); - const [promptTestRunner, promptTestFramework, promptMutator, promptTranspilers, promptReporters, promptPackageManagers]: inquirer.Question[] = [ + expect(inquirerPrompt).to.have.been.callCount(7); + const [promptPreset, promptTestRunner, promptTestFramework, promptMutator, promptTranspilers, promptReporters, promptPackageManagers]: inquirer.Question[] = [ inquirerPrompt.getCall(0).args[0], inquirerPrompt.getCall(1).args[0], inquirerPrompt.getCall(2).args[0], inquirerPrompt.getCall(3).args[0], inquirerPrompt.getCall(4).args[0], - inquirerPrompt.getCall(5).args[0] + inquirerPrompt.getCall(5).args[0], + inquirerPrompt.getCall(6).args[0], ]; + expect(promptPreset.type).to.eq('list'); + expect(promptPreset.name).to.eq('preset'); + expect(promptPreset.choices).to.deep.eq(['awesome-preset', new inquirer.Separator(), 'none']); expect(promptTestRunner.type).to.eq('list'); expect(promptTestRunner.name).to.eq('testRunner'); expect(promptTestRunner.choices).to.deep.eq(['awesome', 'hyper', 'ghost', new inquirer.Separator(), 'command']); @@ -103,6 +113,27 @@ describe('StrykerInitializer', () => { expect(promptPackageManagers.choices).to.deep.eq(['npm', 'yarn']); }); + it('should immediately complete when a preset is chosen', async () => { + fsReadFile.withArgs(`presets/awesome-preset.conf.txt`).resolves(''); + inquirerPrompt.resolves({ + preset: 'awesome-preset' + }); + await sut.initialize(); + expect(inquirerPrompt).to.have.been.callCount(1); + expect(out).to.have.been.calledWith('Done configuring stryker. Please review `stryker.conf.js`, you might need to configure transpilers or your test runner correctly.'); + expect(out).to.have.been.calledWith('Let\'s kill some mutants with this command: `stryker run`'); + }); + + it('should correctly load a preset', async () => { + const fileData = 'config-data'; + fsReadFile.withArgs(`presets/awesome-preset.conf.txt`).resolves(fileData); + inquirerPrompt.resolves({ + preset: 'awesome-preset' + }); + await sut.initialize(); + expect(fsWriteFile).to.have.been.calledOnceWith('stryker.conf.js', fileData); + }); + it('should not prompt for testFramework if test runner is "command"', async () => { arrangeAnswers({ testRunner: 'command' }); await sut.initialize(); @@ -118,7 +149,7 @@ describe('StrykerInitializer', () => { transpilers: ['webpack'] }); await sut.initialize(); - expect(inquirerPrompt).to.have.been.callCount(6); + expect(inquirerPrompt).to.have.been.callCount(7); expect(out).to.have.been.calledWith('OK, downgrading coverageAnalysis to "all"'); expect(fsAsPromised.writeFile).to.have.been.calledWith('stryker.conf.js', sinon.match('coverageAnalysis: "all"')); }); @@ -182,7 +213,7 @@ describe('StrykerInitializer', () => { it('should not prompt for test framework', async () => { await sut.initialize(); - expect(inquirerPrompt).to.have.been.callCount(5); + expect(inquirerPrompt).to.have.been.callCount(6); expect(inquirerPrompt).not.calledWithMatch(sinon.match({ name: 'testFramework' })); }); @@ -228,22 +259,22 @@ describe('StrykerInitializer', () => { describe('initialize() when no internet', () => { it('should log error and continue when fetching test runners', async () => { - restClientSearchGet.withArgs('/v2/search?q=keywords:stryker-test-runner').rejects(); - stubMutators('stryker-javascript'); - stubTranspilers('stryker-webpack'); - stubReporters(); - stubPackageClient({ 'stryker-javascript': null, 'stryker-webpack': null }); - inquirerPrompt.resolves({ - packageManager: 'npm', - reporters: ['clear-text'], - transpilers: ['webpack'] - }); + restClientSearchGet.withArgs('/v2/search?q=keywords:stryker-test-runner').rejects(); + stubMutators('stryker-javascript'); + stubTranspilers('stryker-webpack'); + stubReporters(); + stubPackageClient({ 'stryker-javascript': null, 'stryker-webpack': null }); + inquirerPrompt.resolves({ + packageManager: 'npm', + reporters: ['clear-text'], + transpilers: ['webpack'] + }); - await sut.initialize(); + await sut.initialize(); - expect(log.error).to.have.been.calledWith('Unable to reach https://api.npms.io (for query /v2/search?q=keywords:stryker-test-runner). Please check your internet connection.'); - expect(out).to.have.been.calledWith('Unable to select a test runner. You will need to configure it manually.'); - expect(fsAsPromised.writeFile).to.have.been.called; + expect(log.error).to.have.been.calledWith('Unable to reach https://api.npms.io (for query /v2/search?q=keywords:stryker-test-runner). Please check your internet connection.'); + expect(out).to.have.been.calledWith('Unable to select a test runner. You will need to configure it manually.'); + expect(fsAsPromised.writeFile).to.have.been.called; }); it('should log error and continue when fetching test frameworks', async () => { @@ -418,6 +449,7 @@ describe('StrykerInitializer', () => { }; interface StrykerInitAnswers { + preset: string | null; testFramework: string; testRunner: string; mutator: string; @@ -430,6 +462,7 @@ describe('StrykerInitializer', () => { const answers: StrykerInitAnswers = Object.assign({ mutator: 'typescript', packageManager: 'yarn', + preset: null, reporters: ['dimension', 'mars'], testFramework: 'awesome', testRunner: 'awesome', From 0a9ac367d79fa03070609f377d67da567886b605 Mon Sep 17 00:00:00 2001 From: WouterA Date: Thu, 22 Nov 2018 09:14:48 +0100 Subject: [PATCH 03/13] Refactoring and reworking prompting preset logic --- packages/stryker/package.json | 5 +- packages/stryker/presets/angular.conf.js | 22 ----- packages/stryker/presets/react-jsx.conf.js | 12 --- packages/stryker/presets/react-tsx.conf.js | 12 --- packages/stryker/presets/vuejs-jest.conf.js | 12 --- packages/stryker/presets/vuejs-karma.conf.js | 15 ---- .../stryker/src/initializer/PresetOption.ts | 4 +- .../src/initializer/StrykerConfigWriter.ts | 28 ++++--- .../src/initializer/StrykerInitializer.ts | 27 +++---- .../stryker/src/initializer/StrykerPresets.ts | 12 +++ .../src/initializer/presets/AngularPreset.ts | 30 +++++++ .../src/initializer/presets/ReactPreset.ts | 58 +++++++++++++ .../src/initializer/presets/StrykerPreset.ts | 9 +++ .../src/initializer/presets/VueJsPreset.ts | 79 ++++++++++++++++++ packages/stryker/stryker.conf.js | 64 --------------- .../stryker/test/helpers/StrykerPresetMock.ts | 6 ++ .../initializer/StrykerInitializerSpec.ts | 81 +++++++++++++++---- 17 files changed, 297 insertions(+), 179 deletions(-) delete mode 100644 packages/stryker/presets/angular.conf.js delete mode 100644 packages/stryker/presets/react-jsx.conf.js delete mode 100644 packages/stryker/presets/react-tsx.conf.js delete mode 100644 packages/stryker/presets/vuejs-jest.conf.js delete mode 100644 packages/stryker/presets/vuejs-karma.conf.js create mode 100644 packages/stryker/src/initializer/StrykerPresets.ts create mode 100644 packages/stryker/src/initializer/presets/AngularPreset.ts create mode 100644 packages/stryker/src/initializer/presets/ReactPreset.ts create mode 100644 packages/stryker/src/initializer/presets/StrykerPreset.ts create mode 100644 packages/stryker/src/initializer/presets/VueJsPreset.ts delete mode 100644 packages/stryker/stryker.conf.js create mode 100644 packages/stryker/test/helpers/StrykerPresetMock.ts diff --git a/packages/stryker/package.json b/packages/stryker/package.json index 6c25d75306..48a6f5485a 100644 --- a/packages/stryker/package.json +++ b/packages/stryker/package.json @@ -83,7 +83,10 @@ "@types/node": "~10.11.4", "@types/prettier": "~1.13.1", "@types/progress": "~2.0.1", - "stryker-api": "^0.21.5" + "stryker-api": "^0.21.5", + "stryker-html-reporter": "^0.16.7", + "stryker-karma-runner": "^0.20.2", + "stryker-typescript": "^0.15.4" }, "peerDependencies": { "stryker-api": ">=0.18.0 <0.22.0" diff --git a/packages/stryker/presets/angular.conf.js b/packages/stryker/presets/angular.conf.js deleted file mode 100644 index c9f0db6975..0000000000 --- a/packages/stryker/presets/angular.conf.js +++ /dev/null @@ -1,22 +0,0 @@ -module.exports = function (config) { - config.set({ - mutate: [ - 'src/**/*.ts', - '!src/**/*.spec.ts', - '!src/test.ts', - '!src/environments/*.ts' - ], - mutator: 'typescript', - testRunner: 'karma', - karma: { - configFile: 'src/karma.conf.js', - projectType: 'angular-cli', - config: { - browsers: ['ChromeHeadless'] - } - }, - reporters: ['progress', 'clear-text', 'html'], - // maxConcurrentTestRunners: 2, // Recommended to not use all cores when running stryker with angular. - coverageAnalysis: 'off' - }); -}; \ No newline at end of file diff --git a/packages/stryker/presets/react-jsx.conf.js b/packages/stryker/presets/react-jsx.conf.js deleted file mode 100644 index ac2822e66a..0000000000 --- a/packages/stryker/presets/react-jsx.conf.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = function (config) { - config.set({ - mutate: ['src/**/*.js?(x)', '!src/**/*@(.test|.spec|Spec).js?(x)'], - mutator: 'javascript', - testRunner: 'jest', - reporter: ['progress', 'clear-text', 'html'], - coverageAnalysis: 'off', - jest: { - project: 'react' - } - }); -} \ No newline at end of file diff --git a/packages/stryker/presets/react-tsx.conf.js b/packages/stryker/presets/react-tsx.conf.js deleted file mode 100644 index c545b182e8..0000000000 --- a/packages/stryker/presets/react-tsx.conf.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = function (config) { - config.set({ - mutate: ['src/**/*.ts?(x)', '!src/**/*@(.test|.spec|Spec).ts?(x)'], - mutator: 'typescript', - testRunner: 'jest', - reporter: ['progress', 'clear-text', 'html'], - coverageAnalysis: 'off', - jest: { - project: 'react' - } - }); -} \ No newline at end of file diff --git a/packages/stryker/presets/vuejs-jest.conf.js b/packages/stryker/presets/vuejs-jest.conf.js deleted file mode 100644 index e275ceb246..0000000000 --- a/packages/stryker/presets/vuejs-jest.conf.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = function (config) { - config.set({ - mutate: ['src/**/*.js', 'src/**/*.ts', 'src/**/*.vue'], - mutator: 'vue', - testRunner: 'jest', - jest: { - // config: require('path/to/your/custom/jestConfig.js') - }, - reporter: ['progress', 'clear-text', 'html'], - coverageAnalysis: 'off' - }); - }; \ No newline at end of file diff --git a/packages/stryker/presets/vuejs-karma.conf.js b/packages/stryker/presets/vuejs-karma.conf.js deleted file mode 100644 index b90573577b..0000000000 --- a/packages/stryker/presets/vuejs-karma.conf.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = function (config) { - config.set({ - mutate: ['src/**/*.js', 'src/**/*.ts', 'src/**/*.vue'], - mutator: 'vue', - testRunner: 'karma', - karma: { - configFile: 'test/unit/karma.conf.js', - config: { - browsers: ['ChromeHeadless'] - } - }, - reporter: ['progress', 'clear-text', 'html'], - coverageAnalysis: 'off' - }); -}; \ No newline at end of file diff --git a/packages/stryker/src/initializer/PresetOption.ts b/packages/stryker/src/initializer/PresetOption.ts index fa79cb0550..7f47c5240c 100644 --- a/packages/stryker/src/initializer/PresetOption.ts +++ b/packages/stryker/src/initializer/PresetOption.ts @@ -1,6 +1,8 @@ +import StrykerPreset from './presets/StrykerPreset'; + interface PresetOption { name: string; - file: string; + create(): StrykerPreset; } export default PresetOption; diff --git a/packages/stryker/src/initializer/StrykerConfigWriter.ts b/packages/stryker/src/initializer/StrykerConfigWriter.ts index c9ccb07df7..ba64631516 100644 --- a/packages/stryker/src/initializer/StrykerConfigWriter.ts +++ b/packages/stryker/src/initializer/StrykerConfigWriter.ts @@ -3,8 +3,8 @@ import { fsAsPromised } from '@stryker-mutator/util'; import { getLogger } from 'stryker-api/logging'; import { StrykerOptions } from 'stryker-api/core'; import PromptOption from './PromptOption'; -import PresetOption from './PresetOption'; import { format } from 'prettier'; +import StrykerPreset from './presets/StrykerPreset'; const STRYKER_CONFIG_FILE = 'stryker.conf.js'; @@ -48,9 +48,12 @@ export default class StrykerConfigWriter { return this.writeStrykerConfig(configObject); } - public async writePreset(preset: PresetOption) { - const stream = await fsAsPromised.readFile(`presets/${preset.file}`); - return this.writeStrykerConfigRaw(stream.toString()); + /** + * Create stryker.conf.js based on the chosen preset + * @function + */ + public async writePreset(preset: StrykerPreset) { + return this.writeStrykerConfigRaw(preset.conf); } private configureTestFramework(configObject: Partial, selectedTestFramework: null | PromptOption) { @@ -64,19 +67,20 @@ export default class StrykerConfigWriter { private writeStrykerConfigRaw(rawString: string) { this.out('Writing stryker.conf.js...'); - return fsAsPromised.writeFile(STRYKER_CONFIG_FILE, rawString); + const formattedConf = format(` + module.exports = function(config){ + config.set( + ${rawString} + ); + }`, { parser: 'babylon' }); + return fsAsPromised.writeFile(STRYKER_CONFIG_FILE, formattedConf); } private writeStrykerConfig(configObject: Partial) { return this.writeStrykerConfigRaw(this.wrapInModule(configObject)); } - private wrapInModule(configObject: Partial) { - return format(` - module.exports = function(config){ - config.set( - ${JSON.stringify(configObject, null, 2)} - ); - }`, { parser: 'babylon' }); + private wrapInModule(configObject: Partial): string { + return JSON.stringify(configObject, null, 2); } } diff --git a/packages/stryker/src/initializer/StrykerInitializer.ts b/packages/stryker/src/initializer/StrykerInitializer.ts index 8b6294e56c..a5b8fdfbe0 100644 --- a/packages/stryker/src/initializer/StrykerInitializer.ts +++ b/packages/stryker/src/initializer/StrykerInitializer.ts @@ -6,8 +6,8 @@ import { getLogger } from 'stryker-api/logging'; import { filterEmpty } from '../utils/objectUtils'; import StrykerConfigWriter from './StrykerConfigWriter'; import CommandTestRunner from '../test-runner/CommandTestRunner'; -import { fsAsPromised } from '@stryker-mutator/util'; import PresetOption from './PresetOption'; +import StrykerPresets from './StrykerPresets'; const enum PackageManager { Npm = 'npm', @@ -19,7 +19,7 @@ export default class StrykerInitializer { private readonly log = getLogger(StrykerInitializer.name); private readonly inquirer = new StrykerInquirer(); - constructor(private readonly out = console.log, private readonly client: NpmClient = new NpmClient()) { } + constructor(private readonly out = console.log, private readonly client: NpmClient = new NpmClient(), private readonly strykerPresets: PresetOption[] = StrykerPresets) { } /** * Runs the initializer will prompt the user for questions about his setup. After that, install plugins and configure Stryker. @@ -31,7 +31,11 @@ export default class StrykerInitializer { this.patchProxies(); const selectedPreset = await this.selectPreset(); if (selectedPreset) { - await configWriter.writePreset(selectedPreset); + const selectedPresetInstance = selectedPreset.create(); + await selectedPresetInstance.prompt(); + const selectedPackageManager = await this.selectPackageManager(); + await configWriter.writePreset(selectedPresetInstance); + this.installNpmDependencies(selectedPresetInstance.dependencies, selectedPackageManager); } else { const selectedTestRunner = await this.selectTestRunner(); @@ -74,19 +78,14 @@ export default class StrykerInitializer { } private async selectPreset(): Promise { - if (fsAsPromised.existsSync('presets')) { - const files: string[] = await fsAsPromised.readdir('presets'); - if (files.length) { - const presetOptions: PresetOption[] = files.map(file => ({ name: file.split('.')[0], file})); - this.log.debug(`Found presets runners: ${JSON.stringify(presetOptions)}`); - return this.inquirer.promptPresets(presetOptions); - } else { - this.log.debug('The presets folder is empty. Reverting to custom configuration.'); - } + const presetOptions: PresetOption[] = this.strykerPresets; + if (presetOptions.length) { + this.log.debug(`Found presets: ${JSON.stringify(presetOptions)}`); + return this.inquirer.promptPresets(presetOptions); } else { - this.log.debug('No presets folder defined. Reverting to custom configuration.'); + this.log.debug('No presets have been configured, reverting to custom configuration'); + return null; } - return null; } private async selectTestRunner(): Promise { diff --git a/packages/stryker/src/initializer/StrykerPresets.ts b/packages/stryker/src/initializer/StrykerPresets.ts new file mode 100644 index 0000000000..875eac4ae4 --- /dev/null +++ b/packages/stryker/src/initializer/StrykerPresets.ts @@ -0,0 +1,12 @@ +import { AngularPreset } from './presets/AngularPreset'; +import { VueJsPreset } from './presets/VueJsPreset'; +import { ReactPreset } from './presets/ReactPreset'; +import PresetOption from './PresetOption'; + +const strykerPresets: PresetOption[] = [ + {name: 'angular', create() { return new AngularPreset(); } }, + {name: 'react', create() { return new ReactPreset(); } }, + {name: 'vueJs', create() { return new VueJsPreset(); } } + // Add new presets here +]; +export default strykerPresets; diff --git a/packages/stryker/src/initializer/presets/AngularPreset.ts b/packages/stryker/src/initializer/presets/AngularPreset.ts new file mode 100644 index 0000000000..dfd215a97d --- /dev/null +++ b/packages/stryker/src/initializer/presets/AngularPreset.ts @@ -0,0 +1,30 @@ +import StrykerPreset from './StrykerPreset'; + +export class AngularPreset extends StrykerPreset { + public dependencies = [ + 'stryker', + 'stryker-karma-runner', + 'stryker-typescript', + 'stryker-html-reporter' + ]; + public conf = `{ + mutate: [ + 'src/**/*.ts', + '!src/**/*.spec.ts', + '!src/test.ts', + '!src/environments/*.ts' + ], + mutator: 'typescript', + testRunner: 'karma', + karma: { + configFile: 'src/karma.conf.js', + projectType: 'angular-cli', + config: { + browsers: ['ChromeHeadless'] + } + }, + reporters: ['progress', 'clear-text', 'html'], + // maxConcurrentTestRunners: 2, // Recommended to use about half of your available cores when running stryker with angular. + coverageAnalysis: 'off' + }`; +} diff --git a/packages/stryker/src/initializer/presets/ReactPreset.ts b/packages/stryker/src/initializer/presets/ReactPreset.ts new file mode 100644 index 0000000000..55ff832e53 --- /dev/null +++ b/packages/stryker/src/initializer/presets/ReactPreset.ts @@ -0,0 +1,58 @@ +import StrykerPreset from './StrykerPreset'; +import inquirer = require('inquirer'); + +export class ReactPreset extends StrykerPreset { + + public dependencies = [ + 'stryker', + 'stryker-jest-runner', + 'stryker-html-reporter' + ]; + public conf: string; + + private readonly tsxDependencies = ['stryker-typescript', ...this.dependencies]; + private readonly tsxConf = `{ + mutate: ['src/**/*.ts?(x)', '!src/**/*@(.test|.spec|Spec).ts?(x)'], + mutator: 'typescript', + testRunner: 'jest', + reporters: ['progress', 'clear-text', 'html'], + coverageAnalysis: 'off', + jest: { + projectType: 'react' + } + }`; + + private readonly jsxDependencies = ['stryker-javascript-mutator', ...this.dependencies]; + private readonly jsxConf = `{ + mutate: ['src/**/*.js?(x)', '!src/**/*@(.test|.spec|Spec).js?(x)'], + mutator: 'javascript', + testRunner: 'jest', + reporters: ['progress', 'clear-text', 'html'], + coverageAnalysis: 'off', + jest: { + projectType: 'react' + } + }`; + + public async prompt(): Promise { + const choices: inquirer.ChoiceType[] = ['JSX', 'TSX']; + const answers = await inquirer.prompt<{ choice: string }>({ + choices, + message: 'Is your project a JSX project or a TSX project?', + name: 'choice', + type: 'list' + }); + this.load(answers.choice); + } + private load(choice: string) { + if (choice === 'JSX') { + this.dependencies = this.jsxDependencies; + this.conf = this.jsxConf; + } else if (choice === 'TSX') { + this.dependencies = this.tsxDependencies; + this.conf = this.tsxConf; + } else { + throw new Error(`Invalid project type ${choice}`); + } + } +} diff --git a/packages/stryker/src/initializer/presets/StrykerPreset.ts b/packages/stryker/src/initializer/presets/StrykerPreset.ts new file mode 100644 index 0000000000..462f5638ef --- /dev/null +++ b/packages/stryker/src/initializer/presets/StrykerPreset.ts @@ -0,0 +1,9 @@ +abstract class StrykerPreset { + public abstract dependencies: string[]; + public abstract conf: string; + public async prompt(): Promise { + return; + } +} + +export default StrykerPreset; diff --git a/packages/stryker/src/initializer/presets/VueJsPreset.ts b/packages/stryker/src/initializer/presets/VueJsPreset.ts new file mode 100644 index 0000000000..a46151527a --- /dev/null +++ b/packages/stryker/src/initializer/presets/VueJsPreset.ts @@ -0,0 +1,79 @@ +import StrykerPreset from './StrykerPreset'; +import inquirer = require('inquirer'); + +export class VueJsPreset extends StrykerPreset { + public dependencies = [ + 'stryker', + 'stryker-vue-mutator', + 'stryker-html-reporter' + ]; + public conf: string; + + private readonly jestDependency = 'stryker-jest-runner'; + private readonly jestConf = `{ + mutate: ['src/**/*.js', 'src/**/*.ts', 'src/**/*.vue'], + mutator: 'vue', + testRunner: 'jest', + jest: { + // config: require('path/to/your/custom/jestConfig.js') + }, + reporter: ['progress', 'clear-text', 'html'], + coverageAnalysis: 'off' + }`; + + private readonly karmaDependency = 'stryker-karma-runner'; + private readonly karmaConf = `{ + mutate: ['src/**/*.js', 'src/**/*.ts', 'src/**/*.vue'], + mutator: 'vue', + testRunner: 'karma', + karma: { + configFile: 'test/unit/karma.conf.js', + config: { + browsers: ['ChromeHeadless'] + } + }, + reporter: ['progress', 'clear-text', 'html'], + coverageAnalysis: 'off' + }`; + + public async prompt(): Promise { + const testRunnerChoices: inquirer.ChoiceType[] = ['karma', 'jest']; + const testRunnerAnswers = await inquirer.prompt<{ testrunner: string }>({ + choices: testRunnerChoices, + message: 'Which test runner do you want to use?', + name: 'testrunner', + type: 'list' + }); + const scriptChoices: inquirer.ChoiceType[] = ['typescript', 'javascript']; + const scriptAnswers = await inquirer.prompt<{ script: string }>({ + choices: scriptChoices, + message: 'What script does your project use?', + name: 'script', + type: 'list' + }); + this.loadTestRunner(testRunnerAnswers.testrunner); + this.loadScript(scriptAnswers.script); + } + + private loadTestRunner(testrunner: string) { + if (testrunner === 'karma') { + this.dependencies.push(this.karmaDependency); + this.conf = this.karmaConf; + } else if (testrunner === 'jest') { + this.dependencies.push(this.jestDependency); + this.conf = this.jestConf; + } else { + throw new Error(`Invalid test runner chosen: ${testrunner}`); + } + } + + private loadScript(script: string) { + if (script === 'typescript') { + this.dependencies.push('stryker-typescript'); + } else if (script === 'javascript') { + this.dependencies.push('stryker-javascript-mutator'); + } else { + throw new Error(`Invalid script chosen: ${script}`); + } + } +} diff --git a/packages/stryker/stryker.conf.js b/packages/stryker/stryker.conf.js deleted file mode 100644 index 5f77b55ef6..0000000000 --- a/packages/stryker/stryker.conf.js +++ /dev/null @@ -1,64 +0,0 @@ -module.exports = function (config) { - - var typescript = true; - var es6 = true; - - if (typescript) { - config.set({ - files: [ - 'node_modules/stryker-api/*.@(js|map)', - 'node_modules/stryker-api/src/**/*.@(js|map)', - 'package.json', - 'src/**/*.ts', - '!src/**/*.d.ts', - 'test/helpers/**/*.ts', - 'test/unit/**/*.ts', - '!test/**/*.d.ts' - ], - symlinkNodeModules: false, - mutate: ['src/**/*.ts'], - coverageAnalysis: 'perTest', - tsconfigFile: 'tsconfig.json', - mutator: 'typescript', - transpilers: [ - 'typescript' - ], - mochaOptions: { - files: ['test/helpers/**/*.js', 'test/unit/**/*.js'] - } - }) - } else { - config.set({ - files: [ - 'test/helpers/**/*.js', - 'test/unit/**/*.js', - { pattern: 'src/**/*.js', included: false, mutated: true }, - { pattern: 'node_modules/stryker-api/*.js', included: false, mutated: false }, - { pattern: 'node_modules/stryker-api/src/**/*.js', included: false, mutated: false } - ], - coverageAnalysis: 'perTest', - mutator: es6 ? 'javascript' : 'es5' - }); - } - - config.set({ - testFramework: 'mocha', - testRunner: 'mocha', - reporters: ['progress', 'html', 'clear-text', 'event-recorder', 'dashboard'], - maxConcurrentTestRunners: 4, - thresholds: { - high: 80, - low: 60, - break: null - }, - fileLogLevel: 'trace', - logLevel: 'info', - plugins: [ - require.resolve('../stryker-mocha-runner/src/index'), - require.resolve('../stryker-mocha-framework/src/index'), - require.resolve('../stryker-html-reporter/src/index'), - require.resolve('../stryker-typescript/src/index'), - require.resolve('../stryker-javascript-mutator/src/index') - ] - }); -}; diff --git a/packages/stryker/test/helpers/StrykerPresetMock.ts b/packages/stryker/test/helpers/StrykerPresetMock.ts new file mode 100644 index 0000000000..f6900865ec --- /dev/null +++ b/packages/stryker/test/helpers/StrykerPresetMock.ts @@ -0,0 +1,6 @@ +import StrykerPreset from '../../src/initializer/presets/StrykerPreset'; + +export class StrykerPresetMock extends StrykerPreset { + public dependencies: string[] = []; + public conf: string = ''; +} diff --git a/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts b/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts index ae71e029bf..fdb208591f 100644 --- a/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts +++ b/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts @@ -8,27 +8,31 @@ import StrykerInitializer from '../../../src/initializer/StrykerInitializer'; import * as restClient from 'typed-rest-client/RestClient'; import currentLogMock from '../../helpers/logMock'; import { Mock } from '../../helpers/producers'; +import PresetOption from '../../../src/initializer/PresetOption'; +import NpmClient from '../../../src/initializer/NpmClient'; +import { StrykerPresetMock } from '../../helpers/StrykerPresetMock'; +import { format } from 'prettier'; describe('StrykerInitializer', () => { let log: Mock; let sut: StrykerInitializer; let inquirerPrompt: sinon.SinonStub; let childExecSync: sinon.SinonStub; - let fsReaddir: sinon.SinonStub; - let fsReadFile: sinon.SinonStub; let fsWriteFile: sinon.SinonStub; let fsExistsSync: sinon.SinonStub; let restClientPackageGet: sinon.SinonStub; let restClientSearchGet: sinon.SinonStub; let out: sinon.SinonStub; + let presets: PresetOption[]; + let presetMock: StrykerPresetMock; beforeEach(() => { log = currentLogMock(); out = sandbox.stub(); + presets = []; + presetMock = new StrykerPresetMock(); inquirerPrompt = sandbox.stub(inquirer, 'prompt'); childExecSync = sandbox.stub(child, 'execSync'); - fsReaddir = sandbox.stub(fsAsPromised, 'readdir'); - fsReadFile = sandbox.stub(fsAsPromised, 'readFile'); fsWriteFile = sandbox.stub(fsAsPromised, 'writeFile'); fsExistsSync = sandbox.stub(fsAsPromised, 'existsSync'); restClientSearchGet = sandbox.stub(); @@ -40,7 +44,7 @@ describe('StrykerInitializer', () => { .withArgs('npm').returns({ get: restClientPackageGet }); - sut = new StrykerInitializer(out); + sut = new StrykerInitializer(out, new NpmClient(), presets); }); afterEach(() => { @@ -71,8 +75,7 @@ describe('StrykerInitializer', () => { 'stryker-webpack': null }); fsWriteFile.resolves({}); - fsExistsSync.withArgs('presets').resolves(true); - fsReaddir.withArgs('presets').resolves(['awesome-preset.conf.txt']); + presets.push({name: 'awesome-preset', create() { return presetMock; }}); }); it('should prompt for preset, test runner, test framework, mutator, transpilers, reporters, and package manager', async () => { @@ -113,25 +116,75 @@ describe('StrykerInitializer', () => { expect(promptPackageManagers.choices).to.deep.eq(['npm', 'yarn']); }); - it('should immediately complete when a preset is chosen', async () => { - fsReadFile.withArgs(`presets/awesome-preset.conf.txt`).resolves(''); + it('should immediately complete when a preset and package manager is chosen', async () => { inquirerPrompt.resolves({ + packageManager: 'npm', preset: 'awesome-preset' }); await sut.initialize(); - expect(inquirerPrompt).to.have.been.callCount(1); + expect(inquirerPrompt).to.have.been.callCount(2); expect(out).to.have.been.calledWith('Done configuring stryker. Please review `stryker.conf.js`, you might need to configure transpilers or your test runner correctly.'); expect(out).to.have.been.calledWith('Let\'s kill some mutants with this command: `stryker run`'); }); - it('should correctly load a preset', async () => { - const fileData = 'config-data'; - fsReadFile.withArgs(`presets/awesome-preset.conf.txt`).resolves(fileData); + it('should correctly load the stryker configuration file', async () => { + presetMock.conf = `{ + 'awesome-conf': 'awesome', + }`; + const expectedOutput = format(` + module.exports = function(config){ + config.set( + ${presetMock.conf} + ); + }`, { parser: 'babylon' }); + inquirerPrompt.resolves({ + packageManager: 'npm', + preset: 'awesome-preset' + }); + await sut.initialize(); + expect(fsWriteFile).to.have.been.calledWith('stryker.conf.js', expectedOutput); + }); + + it('should correctly load dependencies from the preset', async () => { + presetMock.dependencies = ['my-awesome-dependency', 'another-awesome-dependency']; + inquirerPrompt.resolves({ + packageManager: 'npm', + preset: 'awesome-preset' + }); + await sut.initialize(); + expect(fsWriteFile).to.have.been.calledOnce; + expect(childExecSync).to.have.been.calledWith('npm i --save-dev stryker-api my-awesome-dependency another-awesome-dependency', { stdio: [0, 1, 2] }); + }); + + it('should correctly prompt for additional preset-specific options', async () => { + presetMock.prompt = async () => { + await inquirer.prompt<{ awesome: string }>({ + choices: ['yes', 'no'], + message: 'Are you awesome', + name: 'awesome', + type: 'list' + }); + }; inquirerPrompt.resolves({ + awesome: 'yes', + packageManager: 'npm', preset: 'awesome-preset' }); await sut.initialize(); - expect(fsWriteFile).to.have.been.calledOnceWith('stryker.conf.js', fileData); + expect(inquirerPrompt).to.have.been.callCount(3); + const [promptPreset, promptAwesome, promptPackageManager]: inquirer.Question[] = [ + inquirerPrompt.getCall(0).args[0], + inquirerPrompt.getCall(1).args[0], + inquirerPrompt.getCall(2).args[0] + ]; + expect(promptPreset.type).to.eq('list'); + expect(promptPreset.name).to.eq('preset'); + expect(promptPreset.choices).to.deep.eq(['awesome-preset', new inquirer.Separator(), 'none']); + expect(promptAwesome.type).to.eq('list'); + expect(promptAwesome.name).to.eq('awesome'); + expect(promptAwesome.choices).to.deep.eq(['yes', 'no']); + expect(promptPackageManager.type).to.eq('list'); + expect(promptPackageManager.choices).to.deep.eq(['npm', 'yarn']); }); it('should not prompt for testFramework if test runner is "command"', async () => { From 69abf2aa1789d67672d8daa9c252d2931bebbf76 Mon Sep 17 00:00:00 2001 From: WouterA Date: Thu, 22 Nov 2018 14:30:24 +0100 Subject: [PATCH 04/13] Reincluded stryker.conf.js in stryker package after accidentally deleting it --- packages/stryker/stryker.conf.js | 61 ++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 packages/stryker/stryker.conf.js diff --git a/packages/stryker/stryker.conf.js b/packages/stryker/stryker.conf.js new file mode 100644 index 0000000000..b1826a2c29 --- /dev/null +++ b/packages/stryker/stryker.conf.js @@ -0,0 +1,61 @@ +module.exports = function (config) { + var typescript = true; + var es6 = true; + if (typescript) { + config.set({ + files: [ + 'node_modules/stryker-api/*.@(js|map)', + 'node_modules/stryker-api/src/**/*.@(js|map)', + 'package.json', + 'src/**/*.ts', + '!src/**/*.d.ts', + 'test/helpers/**/*.ts', + 'test/unit/**/*.ts', + '!test/**/*.d.ts' + ], + symlinkNodeModules: false, + mutate: ['src/**/*.ts'], + coverageAnalysis: 'perTest', + tsconfigFile: 'tsconfig.json', + mutator: 'typescript', + transpilers: [ + 'typescript' + ], + mochaOptions: { + files: ['test/helpers/**/*.js', 'test/unit/**/*.js'] + } + }) + } else { + config.set({ + files: [ + 'test/helpers/**/*.js', + 'test/unit/**/*.js', + { pattern: 'src/**/*.js', included: false, mutated: true }, + { pattern: 'node_modules/stryker-api/*.js', included: false, mutated: false }, + { pattern: 'node_modules/stryker-api/src/**/*.js', included: false, mutated: false } + ], + coverageAnalysis: 'perTest', + mutator: es6 ? 'javascript' : 'es5' + }); + } + config.set({ + testFramework: 'mocha', + testRunner: 'mocha', + reporters: ['progress', 'html', 'clear-text', 'event-recorder', 'dashboard'], + maxConcurrentTestRunners: 4, + thresholds: { + high: 80, + low: 60, + break: null + }, + fileLogLevel: 'trace', + logLevel: 'info', + plugins: [ + require.resolve('../stryker-mocha-runner/src/index'), + require.resolve('../stryker-mocha-framework/src/index'), + require.resolve('../stryker-html-reporter/src/index'), + require.resolve('../stryker-typescript/src/index'), + require.resolve('../stryker-javascript-mutator/src/index') + ] + }); + }; \ No newline at end of file From c4d57fddea5f3d5142e438f1725f55b5c3597129 Mon Sep 17 00:00:00 2001 From: WouterA Date: Fri, 23 Nov 2018 09:56:47 +0100 Subject: [PATCH 05/13] Remove unnecessary dependencies --- packages/stryker/package.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/stryker/package.json b/packages/stryker/package.json index 4dab6c5ba0..b2b94d6a11 100644 --- a/packages/stryker/package.json +++ b/packages/stryker/package.json @@ -82,11 +82,7 @@ "@types/istanbul-lib-instrument": "~1.7.0", "@types/node": "~10.11.4", "@types/prettier": "~1.13.1", - "@types/progress": "~2.0.1", - "stryker-api": "^0.21.5", - "stryker-html-reporter": "^0.16.7", - "stryker-karma-runner": "^0.20.2", - "stryker-typescript": "^0.15.4" + "@types/progress": "~2.0.1" }, "peerDependencies": { "stryker-api": ">=0.18.0 <0.22.0" From e3e9518a2abdad335eb2201eda10bd539e480ac4 Mon Sep 17 00:00:00 2001 From: WouterA Date: Fri, 23 Nov 2018 10:57:14 +0100 Subject: [PATCH 06/13] Refactoring of StrykerPreset api. --- .../src/initializer/StrykerConfigWriter.ts | 6 +- .../src/initializer/StrykerInitializer.ts | 58 +++++++++++-------- .../src/initializer/presets/AngularPreset.ts | 12 +++- .../src/initializer/presets/ReactPreset.ts | 24 ++++---- .../src/initializer/presets/StrykerConf.ts | 6 ++ .../src/initializer/presets/StrykerPreset.ts | 9 +-- .../src/initializer/presets/VueJsPreset.ts | 49 +++++++++++----- .../stryker/test/helpers/StrykerPresetMock.ts | 6 -- .../initializer/StrykerInitializerSpec.ts | 18 ++++-- 9 files changed, 118 insertions(+), 70 deletions(-) create mode 100644 packages/stryker/src/initializer/presets/StrykerConf.ts delete mode 100644 packages/stryker/test/helpers/StrykerPresetMock.ts diff --git a/packages/stryker/src/initializer/StrykerConfigWriter.ts b/packages/stryker/src/initializer/StrykerConfigWriter.ts index ba64631516..99011b6560 100644 --- a/packages/stryker/src/initializer/StrykerConfigWriter.ts +++ b/packages/stryker/src/initializer/StrykerConfigWriter.ts @@ -4,7 +4,7 @@ import { getLogger } from 'stryker-api/logging'; import { StrykerOptions } from 'stryker-api/core'; import PromptOption from './PromptOption'; import { format } from 'prettier'; -import StrykerPreset from './presets/StrykerPreset'; +import { StrykerPresetConfig } from './presets/StrykerConf'; const STRYKER_CONFIG_FILE = 'stryker.conf.js'; @@ -52,8 +52,8 @@ export default class StrykerConfigWriter { * Create stryker.conf.js based on the chosen preset * @function */ - public async writePreset(preset: StrykerPreset) { - return this.writeStrykerConfigRaw(preset.conf); + public async writePreset(presetConfig: StrykerPresetConfig) { + return this.writeStrykerConfigRaw(presetConfig.config); } private configureTestFramework(configObject: Partial, selectedTestFramework: null | PromptOption) { diff --git a/packages/stryker/src/initializer/StrykerInitializer.ts b/packages/stryker/src/initializer/StrykerInitializer.ts index a5b8fdfbe0..cd4d7e6753 100644 --- a/packages/stryker/src/initializer/StrykerInitializer.ts +++ b/packages/stryker/src/initializer/StrykerInitializer.ts @@ -31,33 +31,10 @@ export default class StrykerInitializer { this.patchProxies(); const selectedPreset = await this.selectPreset(); if (selectedPreset) { - const selectedPresetInstance = selectedPreset.create(); - await selectedPresetInstance.prompt(); - const selectedPackageManager = await this.selectPackageManager(); - await configWriter.writePreset(selectedPresetInstance); - this.installNpmDependencies(selectedPresetInstance.dependencies, selectedPackageManager); + await this.initiatePreset(configWriter, selectedPreset); } else { - const selectedTestRunner = await this.selectTestRunner(); - const selectedTestFramework = selectedTestRunner && !CommandTestRunner.is(selectedTestRunner.name) - ? await this.selectTestFramework(selectedTestRunner) : null; - const selectedMutator = await this.selectMutator(); - const selectedTranspilers = await this.selectTranspilers(); - const selectedReporters = await this.selectReporters(); - const selectedPackageManager = await this.selectPackageManager(); - const npmDependencies = this.getSelectedNpmDependencies( - [selectedTestRunner, selectedTestFramework, selectedMutator] - .concat(selectedTranspilers) - .concat(selectedReporters) - ); - await configWriter.write(selectedTestRunner, - selectedTestFramework, - selectedMutator, - selectedTranspilers, - selectedReporters, - selectedPackageManager, - await this.fetchAdditionalConfig(npmDependencies)); - this.installNpmDependencies(npmDependencies, selectedPackageManager); + await this.initiateCustom(configWriter); } this.out('Done configuring stryker. Please review `stryker.conf.js`, you might need to configure transpilers or your test runner correctly.'); this.out('Let\'s kill some mutants with this command: `stryker run`'); @@ -88,6 +65,37 @@ export default class StrykerInitializer { } } + private async initiatePreset(configWriter: StrykerConfigWriter, selectedPreset: PresetOption) { + const selectedPresetInstance = selectedPreset.create(); + const presetConfig = await selectedPresetInstance.createConfig(); + const selectedPackageManager = await this.selectPackageManager(); + await configWriter.writePreset(presetConfig); + this.installNpmDependencies(presetConfig.dependencies, selectedPackageManager); + } + + private async initiateCustom(configWriter: StrykerConfigWriter) { + const selectedTestRunner = await this.selectTestRunner(); + const selectedTestFramework = selectedTestRunner && !CommandTestRunner.is(selectedTestRunner.name) + ? await this.selectTestFramework(selectedTestRunner) : null; + const selectedMutator = await this.selectMutator(); + const selectedTranspilers = await this.selectTranspilers(); + const selectedReporters = await this.selectReporters(); + const selectedPackageManager = await this.selectPackageManager(); + const npmDependencies = this.getSelectedNpmDependencies( + [selectedTestRunner, selectedTestFramework, selectedMutator] + .concat(selectedTranspilers) + .concat(selectedReporters) + ); + await configWriter.write(selectedTestRunner, + selectedTestFramework, + selectedMutator, + selectedTranspilers, + selectedReporters, + selectedPackageManager, + await this.fetchAdditionalConfig(npmDependencies)); + this.installNpmDependencies(npmDependencies, selectedPackageManager); + } + private async selectTestRunner(): Promise { const testRunnerOptions = await this.client.getTestRunnerOptions(); if (testRunnerOptions.length) { diff --git a/packages/stryker/src/initializer/presets/AngularPreset.ts b/packages/stryker/src/initializer/presets/AngularPreset.ts index dfd215a97d..cab2dd1ca4 100644 --- a/packages/stryker/src/initializer/presets/AngularPreset.ts +++ b/packages/stryker/src/initializer/presets/AngularPreset.ts @@ -1,13 +1,18 @@ import StrykerPreset from './StrykerPreset'; +import { StrykerPresetConfig } from './StrykerConf'; +/** + * More information can be found in the Stryker handbook: + * https://github.com/stryker-mutator/stryker-handbook/blob/master/stryker/guides/angular.md#angular + */ export class AngularPreset extends StrykerPreset { - public dependencies = [ + private readonly dependencies = [ 'stryker', 'stryker-karma-runner', 'stryker-typescript', 'stryker-html-reporter' ]; - public conf = `{ + private readonly config = `{ mutate: [ 'src/**/*.ts', '!src/**/*.spec.ts', @@ -27,4 +32,7 @@ export class AngularPreset extends StrykerPreset { // maxConcurrentTestRunners: 2, // Recommended to use about half of your available cores when running stryker with angular. coverageAnalysis: 'off' }`; + public async createConfig(): Promise { + return new StrykerPresetConfig(this.config, this.dependencies); + } } diff --git a/packages/stryker/src/initializer/presets/ReactPreset.ts b/packages/stryker/src/initializer/presets/ReactPreset.ts index 55ff832e53..a25b730602 100644 --- a/packages/stryker/src/initializer/presets/ReactPreset.ts +++ b/packages/stryker/src/initializer/presets/ReactPreset.ts @@ -1,16 +1,20 @@ import StrykerPreset from './StrykerPreset'; import inquirer = require('inquirer'); +import { StrykerPresetConfig } from './StrykerConf'; +/** + * More information can be found in the Stryker handbook: + * https://github.com/stryker-mutator/stryker-handbook/blob/master/stryker/guides/react.md#react + */ export class ReactPreset extends StrykerPreset { - public dependencies = [ + private readonly generalDependencies = [ 'stryker', 'stryker-jest-runner', 'stryker-html-reporter' ]; - public conf: string; - private readonly tsxDependencies = ['stryker-typescript', ...this.dependencies]; + private readonly tsxDependencies = ['stryker-typescript', ...this.generalDependencies]; private readonly tsxConf = `{ mutate: ['src/**/*.ts?(x)', '!src/**/*@(.test|.spec|Spec).ts?(x)'], mutator: 'typescript', @@ -22,7 +26,7 @@ export class ReactPreset extends StrykerPreset { } }`; - private readonly jsxDependencies = ['stryker-javascript-mutator', ...this.dependencies]; + private readonly jsxDependencies = ['stryker-javascript-mutator', ...this.generalDependencies]; private readonly jsxConf = `{ mutate: ['src/**/*.js?(x)', '!src/**/*@(.test|.spec|Spec).js?(x)'], mutator: 'javascript', @@ -34,7 +38,7 @@ export class ReactPreset extends StrykerPreset { } }`; - public async prompt(): Promise { + public async createConfig(): Promise { const choices: inquirer.ChoiceType[] = ['JSX', 'TSX']; const answers = await inquirer.prompt<{ choice: string }>({ choices, @@ -42,15 +46,13 @@ export class ReactPreset extends StrykerPreset { name: 'choice', type: 'list' }); - this.load(answers.choice); + return this.load(answers.choice); } - private load(choice: string) { + private load(choice: string): StrykerPresetConfig { if (choice === 'JSX') { - this.dependencies = this.jsxDependencies; - this.conf = this.jsxConf; + return new StrykerPresetConfig(this.jsxConf, this.jsxDependencies); } else if (choice === 'TSX') { - this.dependencies = this.tsxDependencies; - this.conf = this.tsxConf; + return new StrykerPresetConfig(this.tsxConf, this.tsxDependencies); } else { throw new Error(`Invalid project type ${choice}`); } diff --git a/packages/stryker/src/initializer/presets/StrykerConf.ts b/packages/stryker/src/initializer/presets/StrykerConf.ts new file mode 100644 index 0000000000..5bb0274f1a --- /dev/null +++ b/packages/stryker/src/initializer/presets/StrykerConf.ts @@ -0,0 +1,6 @@ +export class StrykerPresetConfig { + constructor( + public config: string, + public dependencies: string[]) { + } +} diff --git a/packages/stryker/src/initializer/presets/StrykerPreset.ts b/packages/stryker/src/initializer/presets/StrykerPreset.ts index 462f5638ef..96e72a69e0 100644 --- a/packages/stryker/src/initializer/presets/StrykerPreset.ts +++ b/packages/stryker/src/initializer/presets/StrykerPreset.ts @@ -1,9 +1,6 @@ +import { StrykerPresetConfig } from './StrykerConf'; + abstract class StrykerPreset { - public abstract dependencies: string[]; - public abstract conf: string; - public async prompt(): Promise { - return; - } + public abstract async createConfig(): Promise; } - export default StrykerPreset; diff --git a/packages/stryker/src/initializer/presets/VueJsPreset.ts b/packages/stryker/src/initializer/presets/VueJsPreset.ts index a46151527a..6da842761b 100644 --- a/packages/stryker/src/initializer/presets/VueJsPreset.ts +++ b/packages/stryker/src/initializer/presets/VueJsPreset.ts @@ -1,13 +1,17 @@ import StrykerPreset from './StrykerPreset'; import inquirer = require('inquirer'); +import { StrykerPresetConfig } from './StrykerConf'; +/** + * More information can be found in the Stryker handbook: + * https://github.com/stryker-mutator/stryker-handbook/blob/master/stryker/guides/vuejs.md#vuejs + */ export class VueJsPreset extends StrykerPreset { - public dependencies = [ + private readonly generalDependencies = [ 'stryker', 'stryker-vue-mutator', 'stryker-html-reporter' ]; - public conf: string; private readonly jestDependency = 'stryker-jest-runner'; private readonly jestConf = `{ @@ -36,7 +40,7 @@ export class VueJsPreset extends StrykerPreset { coverageAnalysis: 'off' }`; - public async prompt(): Promise { + public async createConfig(): Promise { const testRunnerChoices: inquirer.ChoiceType[] = ['karma', 'jest']; const testRunnerAnswers = await inquirer.prompt<{ testrunner: string }>({ choices: testRunnerChoices, @@ -51,29 +55,48 @@ export class VueJsPreset extends StrykerPreset { name: 'script', type: 'list' }); - this.loadTestRunner(testRunnerAnswers.testrunner); - this.loadScript(scriptAnswers.script); + const chosenTestRunner = testRunnerAnswers.testrunner; + const chosenScript = scriptAnswers.script; + return new StrykerPresetConfig( + this.getConfigString(chosenTestRunner), + this.createDependencies(chosenTestRunner, chosenScript) + ); } - private loadTestRunner(testrunner: string) { + private getConfigString(testrunner: string) { if (testrunner === 'karma') { - this.dependencies.push(this.karmaDependency); - this.conf = this.karmaConf; + return this.karmaConf; } else if (testrunner === 'jest') { - this.dependencies.push(this.jestDependency); - this.conf = this.jestConf; + return this.jestConf; } else { throw new Error(`Invalid test runner chosen: ${testrunner}`); } } - private loadScript(script: string) { + private createDependencies(testrunner: string, script: string): string[] { + const dependencies = this.generalDependencies; + dependencies.push(this.getTestRunnerDependency(testrunner)); + dependencies.push(this.getScriptDependency(script)); + return dependencies; + } + + private getScriptDependency(script: string): string { if (script === 'typescript') { - this.dependencies.push('stryker-typescript'); + return 'stryker-typescript'; } else if (script === 'javascript') { - this.dependencies.push('stryker-javascript-mutator'); + return 'stryker-javascript-mutator'; } else { throw new Error(`Invalid script chosen: ${script}`); } } + + private getTestRunnerDependency(testrunner: string): string { + if (testrunner === 'karma') { + return this.karmaDependency; + } else if (testrunner === 'jest') { + return this.jestDependency; + } else { + throw new Error(`Invalid test runner chosen: ${testrunner}`); + } + } } diff --git a/packages/stryker/test/helpers/StrykerPresetMock.ts b/packages/stryker/test/helpers/StrykerPresetMock.ts deleted file mode 100644 index f6900865ec..0000000000 --- a/packages/stryker/test/helpers/StrykerPresetMock.ts +++ /dev/null @@ -1,6 +0,0 @@ -import StrykerPreset from '../../src/initializer/presets/StrykerPreset'; - -export class StrykerPresetMock extends StrykerPreset { - public dependencies: string[] = []; - public conf: string = ''; -} diff --git a/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts b/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts index fdb208591f..68f4afa297 100644 --- a/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts +++ b/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts @@ -10,8 +10,9 @@ import currentLogMock from '../../helpers/logMock'; import { Mock } from '../../helpers/producers'; import PresetOption from '../../../src/initializer/PresetOption'; import NpmClient from '../../../src/initializer/NpmClient'; -import { StrykerPresetMock } from '../../helpers/StrykerPresetMock'; import { format } from 'prettier'; +import { StrykerPresetConfig } from '../../../src/initializer/presets/StrykerConf'; +import StrykerPreset from '../../../src/initializer/presets/StrykerPreset'; describe('StrykerInitializer', () => { let log: Mock; @@ -128,13 +129,13 @@ describe('StrykerInitializer', () => { }); it('should correctly load the stryker configuration file', async () => { - presetMock.conf = `{ + presetMock.config = `{ 'awesome-conf': 'awesome', }`; const expectedOutput = format(` module.exports = function(config){ config.set( - ${presetMock.conf} + ${presetMock.config} ); }`, { parser: 'babylon' }); inquirerPrompt.resolves({ @@ -157,13 +158,14 @@ describe('StrykerInitializer', () => { }); it('should correctly prompt for additional preset-specific options', async () => { - presetMock.prompt = async () => { + presetMock.createConfig = async () => { await inquirer.prompt<{ awesome: string }>({ choices: ['yes', 'no'], message: 'Are you awesome', name: 'awesome', type: 'list' }); + return new StrykerPresetConfig(presetMock.config, presetMock.dependencies); }; inquirerPrompt.resolves({ awesome: 'yes', @@ -524,3 +526,11 @@ describe('StrykerInitializer', () => { inquirerPrompt.resolves(answers); } }); + +class StrykerPresetMock extends StrykerPreset { + public dependencies: string[] = []; + public config: string = ''; + public async createConfig() { + return new StrykerPresetConfig(this.config, this.dependencies); + } +} From bdbb16a5f7559e85654e9742c20eb8d5566fd950 Mon Sep 17 00:00:00 2001 From: WouterA Date: Fri, 23 Nov 2018 14:55:30 +0100 Subject: [PATCH 07/13] Refactor StrykerPreset list to remove arrow function. Also removes the need for PresetOption interface --- packages/stryker/src/initializer/PresetOption.ts | 8 -------- .../stryker/src/initializer/StrykerInitializer.ts | 13 ++++++------- packages/stryker/src/initializer/StrykerInquirer.ts | 4 ++-- packages/stryker/src/initializer/StrykerPresets.ts | 12 ++++-------- .../src/initializer/presets/AngularPreset.ts | 1 + .../stryker/src/initializer/presets/ReactPreset.ts | 2 +- .../src/initializer/presets/StrykerPreset.ts | 1 + .../stryker/src/initializer/presets/VueJsPreset.ts | 1 + .../test/unit/initializer/StrykerInitializerSpec.ts | 6 +++--- 9 files changed, 19 insertions(+), 29 deletions(-) delete mode 100644 packages/stryker/src/initializer/PresetOption.ts diff --git a/packages/stryker/src/initializer/PresetOption.ts b/packages/stryker/src/initializer/PresetOption.ts deleted file mode 100644 index 7f47c5240c..0000000000 --- a/packages/stryker/src/initializer/PresetOption.ts +++ /dev/null @@ -1,8 +0,0 @@ -import StrykerPreset from './presets/StrykerPreset'; - -interface PresetOption { - name: string; - create(): StrykerPreset; -} - -export default PresetOption; diff --git a/packages/stryker/src/initializer/StrykerInitializer.ts b/packages/stryker/src/initializer/StrykerInitializer.ts index cd4d7e6753..b588e7a4f7 100644 --- a/packages/stryker/src/initializer/StrykerInitializer.ts +++ b/packages/stryker/src/initializer/StrykerInitializer.ts @@ -6,8 +6,8 @@ import { getLogger } from 'stryker-api/logging'; import { filterEmpty } from '../utils/objectUtils'; import StrykerConfigWriter from './StrykerConfigWriter'; import CommandTestRunner from '../test-runner/CommandTestRunner'; -import PresetOption from './PresetOption'; import StrykerPresets from './StrykerPresets'; +import StrykerPreset from './presets/StrykerPreset'; const enum PackageManager { Npm = 'npm', @@ -19,7 +19,7 @@ export default class StrykerInitializer { private readonly log = getLogger(StrykerInitializer.name); private readonly inquirer = new StrykerInquirer(); - constructor(private readonly out = console.log, private readonly client: NpmClient = new NpmClient(), private readonly strykerPresets: PresetOption[] = StrykerPresets) { } + constructor(private readonly out = console.log, private readonly client: NpmClient = new NpmClient(), private readonly strykerPresets: StrykerPreset[] = StrykerPresets) { } /** * Runs the initializer will prompt the user for questions about his setup. After that, install plugins and configure Stryker. @@ -54,8 +54,8 @@ export default class StrykerInitializer { copyEnvVariable('https_proxy', 'HTTPS_PROXY'); } - private async selectPreset(): Promise { - const presetOptions: PresetOption[] = this.strykerPresets; + private async selectPreset(): Promise { + const presetOptions: StrykerPreset[] = this.strykerPresets; if (presetOptions.length) { this.log.debug(`Found presets: ${JSON.stringify(presetOptions)}`); return this.inquirer.promptPresets(presetOptions); @@ -65,9 +65,8 @@ export default class StrykerInitializer { } } - private async initiatePreset(configWriter: StrykerConfigWriter, selectedPreset: PresetOption) { - const selectedPresetInstance = selectedPreset.create(); - const presetConfig = await selectedPresetInstance.createConfig(); + private async initiatePreset(configWriter: StrykerConfigWriter, selectedPreset: StrykerPreset) { + const presetConfig = await selectedPreset.createConfig(); const selectedPackageManager = await this.selectPackageManager(); await configWriter.writePreset(presetConfig); this.installNpmDependencies(presetConfig.dependencies, selectedPackageManager); diff --git a/packages/stryker/src/initializer/StrykerInquirer.ts b/packages/stryker/src/initializer/StrykerInquirer.ts index e31d0796cf..39aaa4ba43 100644 --- a/packages/stryker/src/initializer/StrykerInquirer.ts +++ b/packages/stryker/src/initializer/StrykerInquirer.ts @@ -1,7 +1,7 @@ import * as inquirer from 'inquirer'; import PromptOption from './PromptOption'; import CommandTestRunner from '../test-runner/CommandTestRunner'; -import PresetOption from './PresetOption'; +import StrykerPreset from './presets/StrykerPreset'; export interface PromptResult { additionalNpmDependencies: string[]; @@ -10,7 +10,7 @@ export interface PromptResult { export class StrykerInquirer { - public async promptPresets(options: PresetOption[]): Promise { + public async promptPresets(options: StrykerPreset[]): Promise { const choices: inquirer.ChoiceType[] = options.map(_ => _.name); choices.push(new inquirer.Separator()); choices.push('none'); diff --git a/packages/stryker/src/initializer/StrykerPresets.ts b/packages/stryker/src/initializer/StrykerPresets.ts index 875eac4ae4..4c2e381f46 100644 --- a/packages/stryker/src/initializer/StrykerPresets.ts +++ b/packages/stryker/src/initializer/StrykerPresets.ts @@ -1,12 +1,8 @@ import { AngularPreset } from './presets/AngularPreset'; -import { VueJsPreset } from './presets/VueJsPreset'; import { ReactPreset } from './presets/ReactPreset'; -import PresetOption from './PresetOption'; +import StrykerPreset from './presets/StrykerPreset'; +import { VueJsPreset } from './presets/VueJsPreset'; -const strykerPresets: PresetOption[] = [ - {name: 'angular', create() { return new AngularPreset(); } }, - {name: 'react', create() { return new ReactPreset(); } }, - {name: 'vueJs', create() { return new VueJsPreset(); } } - // Add new presets here -]; +// Add new presets here +const strykerPresets: StrykerPreset[] = [ new AngularPreset(), new ReactPreset(), new VueJsPreset() ]; export default strykerPresets; diff --git a/packages/stryker/src/initializer/presets/AngularPreset.ts b/packages/stryker/src/initializer/presets/AngularPreset.ts index cab2dd1ca4..ccba2f8a77 100644 --- a/packages/stryker/src/initializer/presets/AngularPreset.ts +++ b/packages/stryker/src/initializer/presets/AngularPreset.ts @@ -6,6 +6,7 @@ import { StrykerPresetConfig } from './StrykerConf'; * https://github.com/stryker-mutator/stryker-handbook/blob/master/stryker/guides/angular.md#angular */ export class AngularPreset extends StrykerPreset { + public readonly name: string = 'angular'; private readonly dependencies = [ 'stryker', 'stryker-karma-runner', diff --git a/packages/stryker/src/initializer/presets/ReactPreset.ts b/packages/stryker/src/initializer/presets/ReactPreset.ts index a25b730602..ddb8109c8b 100644 --- a/packages/stryker/src/initializer/presets/ReactPreset.ts +++ b/packages/stryker/src/initializer/presets/ReactPreset.ts @@ -7,7 +7,7 @@ import { StrykerPresetConfig } from './StrykerConf'; * https://github.com/stryker-mutator/stryker-handbook/blob/master/stryker/guides/react.md#react */ export class ReactPreset extends StrykerPreset { - + public readonly name: string = 'react'; private readonly generalDependencies = [ 'stryker', 'stryker-jest-runner', diff --git a/packages/stryker/src/initializer/presets/StrykerPreset.ts b/packages/stryker/src/initializer/presets/StrykerPreset.ts index 96e72a69e0..937ceda0e8 100644 --- a/packages/stryker/src/initializer/presets/StrykerPreset.ts +++ b/packages/stryker/src/initializer/presets/StrykerPreset.ts @@ -1,6 +1,7 @@ import { StrykerPresetConfig } from './StrykerConf'; abstract class StrykerPreset { + public abstract readonly name: string; public abstract async createConfig(): Promise; } export default StrykerPreset; diff --git a/packages/stryker/src/initializer/presets/VueJsPreset.ts b/packages/stryker/src/initializer/presets/VueJsPreset.ts index 6da842761b..b430109179 100644 --- a/packages/stryker/src/initializer/presets/VueJsPreset.ts +++ b/packages/stryker/src/initializer/presets/VueJsPreset.ts @@ -7,6 +7,7 @@ import { StrykerPresetConfig } from './StrykerConf'; * https://github.com/stryker-mutator/stryker-handbook/blob/master/stryker/guides/vuejs.md#vuejs */ export class VueJsPreset extends StrykerPreset { + public readonly name: string = 'vueJs'; private readonly generalDependencies = [ 'stryker', 'stryker-vue-mutator', diff --git a/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts b/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts index 68f4afa297..d9e8cf0668 100644 --- a/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts +++ b/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts @@ -8,7 +8,6 @@ import StrykerInitializer from '../../../src/initializer/StrykerInitializer'; import * as restClient from 'typed-rest-client/RestClient'; import currentLogMock from '../../helpers/logMock'; import { Mock } from '../../helpers/producers'; -import PresetOption from '../../../src/initializer/PresetOption'; import NpmClient from '../../../src/initializer/NpmClient'; import { format } from 'prettier'; import { StrykerPresetConfig } from '../../../src/initializer/presets/StrykerConf'; @@ -24,7 +23,7 @@ describe('StrykerInitializer', () => { let restClientPackageGet: sinon.SinonStub; let restClientSearchGet: sinon.SinonStub; let out: sinon.SinonStub; - let presets: PresetOption[]; + let presets: StrykerPreset[]; let presetMock: StrykerPresetMock; beforeEach(() => { @@ -76,7 +75,7 @@ describe('StrykerInitializer', () => { 'stryker-webpack': null }); fsWriteFile.resolves({}); - presets.push({name: 'awesome-preset', create() { return presetMock; }}); + presets.push(presetMock); }); it('should prompt for preset, test runner, test framework, mutator, transpilers, reporters, and package manager', async () => { @@ -528,6 +527,7 @@ describe('StrykerInitializer', () => { }); class StrykerPresetMock extends StrykerPreset { + public name: string = 'awesome-preset'; public dependencies: string[] = []; public config: string = ''; public async createConfig() { From 530bf4975d11e64ced051f7de890b6769ceffbb3 Mon Sep 17 00:00:00 2001 From: WouterA Date: Fri, 23 Nov 2018 16:36:14 +0100 Subject: [PATCH 08/13] Preset-specific test cases for Angular, React and VueJs --- .../unit/initializer/StrykerPresetSpec.ts | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 packages/stryker/test/unit/initializer/StrykerPresetSpec.ts diff --git a/packages/stryker/test/unit/initializer/StrykerPresetSpec.ts b/packages/stryker/test/unit/initializer/StrykerPresetSpec.ts new file mode 100644 index 0000000000..ffaf44f828 --- /dev/null +++ b/packages/stryker/test/unit/initializer/StrykerPresetSpec.ts @@ -0,0 +1,135 @@ +import { expect } from 'chai'; +import { AngularPreset } from '../../../src/initializer/presets/AngularPreset'; +import { ReactPreset } from '../../../src/initializer/presets/ReactPreset'; +import * as inquirer from 'inquirer'; +import { VueJsPreset } from '../../../src/initializer/presets/VueJsPreset'; + +describe('StrykerPreset', () => { + let inquirerPrompt: sinon.SinonStub; + + beforeEach(() => { + inquirerPrompt = sandbox.stub(inquirer, 'prompt'); + }); + describe('AngularPreset', () => { + let angularPreset: AngularPreset; + + beforeEach(() => { + angularPreset = new AngularPreset(); + }); + + it('should have the name "angular"', () => { + expect(angularPreset.name).to.eq('angular'); + }); + + it('should mutate typescript', async () => { + const config = await angularPreset.createConfig(); + expect(config.config).to.contain(`mutator: 'typescript'`); + }); + + it('should use the angular-cli', async () => { + const config = await angularPreset.createConfig(); + expect(config.config).to.contain(`projectType: 'angular-cli'`); + }); + }); + + describe('ReactPreset', () => { + let reactPreset: ReactPreset; + + beforeEach(() => { + reactPreset = new ReactPreset(); + }); + + it('should have the name "react"', () => { + expect(reactPreset.name).to.eq('react'); + }); + + it('should mutate typescript when TSX is chosen', async () => { + inquirerPrompt.resolves({ + choice: 'TSX' + }); + const config = await reactPreset.createConfig(); + expect(config.config).to.contain(`mutator: 'typescript'`); + }); + + it('should install stryker-typescript when TSX is chosen', async () => { + inquirerPrompt.resolves({ + choice: 'TSX' + }); + const config = await reactPreset.createConfig(); + expect(config.dependencies).to.include('stryker-typescript'); + }); + + it('should mutate javascript when JSX is chosen', async () => { + inquirerPrompt.resolves({ + choice: 'JSX' + }); + const config = await reactPreset.createConfig(); + expect(config.config).to.include(`mutator: 'javascript'`); + }); + + it('should install stryker-javascript-mutator when JSX is chosen', async () => { + inquirerPrompt.resolves({ + choice: 'JSX' + }); + const config = await reactPreset.createConfig(); + expect(config.dependencies).to.include('stryker-javascript-mutator'); + }); + }); + + describe('VueJsPreset', () => { + let vueJsPreset: VueJsPreset; + + beforeEach(() => { + vueJsPreset = new VueJsPreset(); + inquirerPrompt.resolves({ + script: 'typescript', + testrunner: 'karma' + }); + }); + + it('should have the name "vueJs"', () => { + expect(vueJsPreset.name).to.eq('vueJs'); + }); + + it('should use the vue mutator', async () => { + const config = await vueJsPreset.createConfig(); + expect(config.config).to.contain(`mutator: 'vue'`); + }); + + it('should install stryker-karma-runner when karma is chosen', async () => { + inquirerPrompt.resolves({ + script: 'typescript', + testrunner: 'karma' + }); + const config = await vueJsPreset.createConfig(); + expect(config.dependencies).to.include('stryker-karma-runner'); + }); + + it('should install stryker-jest-runner when jest is chosen', async () => { + inquirerPrompt.resolves({ + script: 'typescript', + testrunner: 'jest' + }); + const config = await vueJsPreset.createConfig(); + expect(config.dependencies).to.include('stryker-jest-runner'); + }); + + it('should install stryker-typescript when typescript is chosen', async () => { + inquirerPrompt.resolves({ + script: 'typescript', + testrunner: 'karma' + }); + const config = await vueJsPreset.createConfig(); + expect(config.dependencies).to.include('stryker-typescript'); + }); + + it('should install stryker-javascript-mutator when javascript is chosen', async () => { + inquirerPrompt.resolves({ + script: 'javascript', + testrunner: 'karma' + }); + const config = await vueJsPreset.createConfig(); + expect(config.dependencies).to.include('stryker-javascript-mutator'); + }); + }); +}); From 79cfe6008e6d44769dc1982e68439eef2a19c60a Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Tue, 27 Nov 2018 10:15:48 +0100 Subject: [PATCH 09/13] Reset stryker-api as a dev dependency of stryker --- packages/stryker/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/stryker/package.json b/packages/stryker/package.json index b2b94d6a11..0266388e74 100644 --- a/packages/stryker/package.json +++ b/packages/stryker/package.json @@ -82,7 +82,8 @@ "@types/istanbul-lib-instrument": "~1.7.0", "@types/node": "~10.11.4", "@types/prettier": "~1.13.1", - "@types/progress": "~2.0.1" + "@types/progress": "~2.0.1", + "stryker-api": "^0.21.5" }, "peerDependencies": { "stryker-api": ">=0.18.0 <0.22.0" From 0715195d46ee00e218ef528db9bdeecd1e2f1480 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Tue, 27 Nov 2018 12:06:34 +0100 Subject: [PATCH 10/13] Rename some things * `StrykerPreset` -> `Preset` * `StrykerPresetConf` -> `PresetConfiguration` --- .../stryker/src/initializer/presets/Preset.ts | 7 + .../presets/PresetConfiguration.ts | 5 + .../src/initializer/presets/StrykerConf.ts | 6 - .../src/initializer/presets/StrykerPreset.ts | 7 - .../unit/initializer/StrykerPresetSpec.ts | 135 ------------------ 5 files changed, 12 insertions(+), 148 deletions(-) create mode 100644 packages/stryker/src/initializer/presets/Preset.ts create mode 100644 packages/stryker/src/initializer/presets/PresetConfiguration.ts delete mode 100644 packages/stryker/src/initializer/presets/StrykerConf.ts delete mode 100644 packages/stryker/src/initializer/presets/StrykerPreset.ts delete mode 100644 packages/stryker/test/unit/initializer/StrykerPresetSpec.ts diff --git a/packages/stryker/src/initializer/presets/Preset.ts b/packages/stryker/src/initializer/presets/Preset.ts new file mode 100644 index 0000000000..3370f09861 --- /dev/null +++ b/packages/stryker/src/initializer/presets/Preset.ts @@ -0,0 +1,7 @@ +import PresetConfig from './PresetConfiguration'; + +interface Preset { + readonly name: string; + createConfig(): Promise; +} +export default Preset; diff --git a/packages/stryker/src/initializer/presets/PresetConfiguration.ts b/packages/stryker/src/initializer/presets/PresetConfiguration.ts new file mode 100644 index 0000000000..1c669f3284 --- /dev/null +++ b/packages/stryker/src/initializer/presets/PresetConfiguration.ts @@ -0,0 +1,5 @@ +export default interface PresetConfiguration { + config: string; + handbookUrl: string; + dependencies: string[]; +} diff --git a/packages/stryker/src/initializer/presets/StrykerConf.ts b/packages/stryker/src/initializer/presets/StrykerConf.ts deleted file mode 100644 index 5bb0274f1a..0000000000 --- a/packages/stryker/src/initializer/presets/StrykerConf.ts +++ /dev/null @@ -1,6 +0,0 @@ -export class StrykerPresetConfig { - constructor( - public config: string, - public dependencies: string[]) { - } -} diff --git a/packages/stryker/src/initializer/presets/StrykerPreset.ts b/packages/stryker/src/initializer/presets/StrykerPreset.ts deleted file mode 100644 index 937ceda0e8..0000000000 --- a/packages/stryker/src/initializer/presets/StrykerPreset.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { StrykerPresetConfig } from './StrykerConf'; - -abstract class StrykerPreset { - public abstract readonly name: string; - public abstract async createConfig(): Promise; -} -export default StrykerPreset; diff --git a/packages/stryker/test/unit/initializer/StrykerPresetSpec.ts b/packages/stryker/test/unit/initializer/StrykerPresetSpec.ts deleted file mode 100644 index ffaf44f828..0000000000 --- a/packages/stryker/test/unit/initializer/StrykerPresetSpec.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { expect } from 'chai'; -import { AngularPreset } from '../../../src/initializer/presets/AngularPreset'; -import { ReactPreset } from '../../../src/initializer/presets/ReactPreset'; -import * as inquirer from 'inquirer'; -import { VueJsPreset } from '../../../src/initializer/presets/VueJsPreset'; - -describe('StrykerPreset', () => { - let inquirerPrompt: sinon.SinonStub; - - beforeEach(() => { - inquirerPrompt = sandbox.stub(inquirer, 'prompt'); - }); - describe('AngularPreset', () => { - let angularPreset: AngularPreset; - - beforeEach(() => { - angularPreset = new AngularPreset(); - }); - - it('should have the name "angular"', () => { - expect(angularPreset.name).to.eq('angular'); - }); - - it('should mutate typescript', async () => { - const config = await angularPreset.createConfig(); - expect(config.config).to.contain(`mutator: 'typescript'`); - }); - - it('should use the angular-cli', async () => { - const config = await angularPreset.createConfig(); - expect(config.config).to.contain(`projectType: 'angular-cli'`); - }); - }); - - describe('ReactPreset', () => { - let reactPreset: ReactPreset; - - beforeEach(() => { - reactPreset = new ReactPreset(); - }); - - it('should have the name "react"', () => { - expect(reactPreset.name).to.eq('react'); - }); - - it('should mutate typescript when TSX is chosen', async () => { - inquirerPrompt.resolves({ - choice: 'TSX' - }); - const config = await reactPreset.createConfig(); - expect(config.config).to.contain(`mutator: 'typescript'`); - }); - - it('should install stryker-typescript when TSX is chosen', async () => { - inquirerPrompt.resolves({ - choice: 'TSX' - }); - const config = await reactPreset.createConfig(); - expect(config.dependencies).to.include('stryker-typescript'); - }); - - it('should mutate javascript when JSX is chosen', async () => { - inquirerPrompt.resolves({ - choice: 'JSX' - }); - const config = await reactPreset.createConfig(); - expect(config.config).to.include(`mutator: 'javascript'`); - }); - - it('should install stryker-javascript-mutator when JSX is chosen', async () => { - inquirerPrompt.resolves({ - choice: 'JSX' - }); - const config = await reactPreset.createConfig(); - expect(config.dependencies).to.include('stryker-javascript-mutator'); - }); - }); - - describe('VueJsPreset', () => { - let vueJsPreset: VueJsPreset; - - beforeEach(() => { - vueJsPreset = new VueJsPreset(); - inquirerPrompt.resolves({ - script: 'typescript', - testrunner: 'karma' - }); - }); - - it('should have the name "vueJs"', () => { - expect(vueJsPreset.name).to.eq('vueJs'); - }); - - it('should use the vue mutator', async () => { - const config = await vueJsPreset.createConfig(); - expect(config.config).to.contain(`mutator: 'vue'`); - }); - - it('should install stryker-karma-runner when karma is chosen', async () => { - inquirerPrompt.resolves({ - script: 'typescript', - testrunner: 'karma' - }); - const config = await vueJsPreset.createConfig(); - expect(config.dependencies).to.include('stryker-karma-runner'); - }); - - it('should install stryker-jest-runner when jest is chosen', async () => { - inquirerPrompt.resolves({ - script: 'typescript', - testrunner: 'jest' - }); - const config = await vueJsPreset.createConfig(); - expect(config.dependencies).to.include('stryker-jest-runner'); - }); - - it('should install stryker-typescript when typescript is chosen', async () => { - inquirerPrompt.resolves({ - script: 'typescript', - testrunner: 'karma' - }); - const config = await vueJsPreset.createConfig(); - expect(config.dependencies).to.include('stryker-typescript'); - }); - - it('should install stryker-javascript-mutator when javascript is chosen', async () => { - inquirerPrompt.resolves({ - script: 'javascript', - testrunner: 'karma' - }); - const config = await vueJsPreset.createConfig(); - expect(config.dependencies).to.include('stryker-javascript-mutator'); - }); - }); -}); From 2c7e8aba16f15fc718dbf39c90310123eb5cd393 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Tue, 27 Nov 2018 12:07:54 +0100 Subject: [PATCH 11/13] Add handbook url to the stryker configuration file --- .../src/initializer/StrykerConfigWriter.ts | 13 +- .../src/initializer/StrykerInitializer.ts | 14 +- .../stryker/src/initializer/StrykerPresets.ts | 4 +- .../src/initializer/presets/AngularPreset.ts | 38 ++--- .../src/initializer/presets/ReactPreset.ts | 86 +++++------ .../src/initializer/presets/VueJsPreset.ts | 49 ++++--- .../test/unit/initializer/PresetsSpec.ts | 135 ++++++++++++++++++ .../initializer/StrykerInitializerSpec.ts | 98 +++++++------ 8 files changed, 287 insertions(+), 150 deletions(-) create mode 100644 packages/stryker/test/unit/initializer/PresetsSpec.ts diff --git a/packages/stryker/src/initializer/StrykerConfigWriter.ts b/packages/stryker/src/initializer/StrykerConfigWriter.ts index 99011b6560..a147887044 100644 --- a/packages/stryker/src/initializer/StrykerConfigWriter.ts +++ b/packages/stryker/src/initializer/StrykerConfigWriter.ts @@ -4,7 +4,7 @@ import { getLogger } from 'stryker-api/logging'; import { StrykerOptions } from 'stryker-api/core'; import PromptOption from './PromptOption'; import { format } from 'prettier'; -import { StrykerPresetConfig } from './presets/StrykerConf'; +import PresetConfiguration from './presets/PresetConfiguration'; const STRYKER_CONFIG_FILE = 'stryker.conf.js'; @@ -52,8 +52,9 @@ export default class StrykerConfigWriter { * Create stryker.conf.js based on the chosen preset * @function */ - public async writePreset(presetConfig: StrykerPresetConfig) { - return this.writeStrykerConfigRaw(presetConfig.config); + public async writePreset(presetConfig: PresetConfiguration) { + return this.writeStrykerConfigRaw(presetConfig.config, `// This config was generated using a preset. + // Please see the handbook for more information: ${presetConfig.handbookUrl}`); } private configureTestFramework(configObject: Partial, selectedTestFramework: null | PromptOption) { @@ -65,12 +66,12 @@ export default class StrykerConfigWriter { } } - private writeStrykerConfigRaw(rawString: string) { + private writeStrykerConfigRaw(rawConfig: string, rawHeader = '') { this.out('Writing stryker.conf.js...'); - const formattedConf = format(` + const formattedConf = format(`${rawHeader} module.exports = function(config){ config.set( - ${rawString} + ${rawConfig} ); }`, { parser: 'babylon' }); return fsAsPromised.writeFile(STRYKER_CONFIG_FILE, formattedConf); diff --git a/packages/stryker/src/initializer/StrykerInitializer.ts b/packages/stryker/src/initializer/StrykerInitializer.ts index b588e7a4f7..c25f29d428 100644 --- a/packages/stryker/src/initializer/StrykerInitializer.ts +++ b/packages/stryker/src/initializer/StrykerInitializer.ts @@ -7,7 +7,7 @@ import { filterEmpty } from '../utils/objectUtils'; import StrykerConfigWriter from './StrykerConfigWriter'; import CommandTestRunner from '../test-runner/CommandTestRunner'; import StrykerPresets from './StrykerPresets'; -import StrykerPreset from './presets/StrykerPreset'; +import Preset from './presets/Preset'; const enum PackageManager { Npm = 'npm', @@ -19,7 +19,7 @@ export default class StrykerInitializer { private readonly log = getLogger(StrykerInitializer.name); private readonly inquirer = new StrykerInquirer(); - constructor(private readonly out = console.log, private readonly client: NpmClient = new NpmClient(), private readonly strykerPresets: StrykerPreset[] = StrykerPresets) { } + constructor(private readonly out = console.log, private readonly client: NpmClient = new NpmClient(), private readonly strykerPresets: Preset[] = StrykerPresets) { } /** * Runs the initializer will prompt the user for questions about his setup. After that, install plugins and configure Stryker. @@ -54,21 +54,21 @@ export default class StrykerInitializer { copyEnvVariable('https_proxy', 'HTTPS_PROXY'); } - private async selectPreset(): Promise { - const presetOptions: StrykerPreset[] = this.strykerPresets; + private async selectPreset(): Promise { + const presetOptions: Preset[] = this.strykerPresets; if (presetOptions.length) { this.log.debug(`Found presets: ${JSON.stringify(presetOptions)}`); return this.inquirer.promptPresets(presetOptions); } else { this.log.debug('No presets have been configured, reverting to custom configuration'); - return null; + return undefined; } } - private async initiatePreset(configWriter: StrykerConfigWriter, selectedPreset: StrykerPreset) { + private async initiatePreset(configWriter: StrykerConfigWriter, selectedPreset: Preset) { const presetConfig = await selectedPreset.createConfig(); - const selectedPackageManager = await this.selectPackageManager(); await configWriter.writePreset(presetConfig); + const selectedPackageManager = await this.selectPackageManager(); this.installNpmDependencies(presetConfig.dependencies, selectedPackageManager); } diff --git a/packages/stryker/src/initializer/StrykerPresets.ts b/packages/stryker/src/initializer/StrykerPresets.ts index 4c2e381f46..f2424fe911 100644 --- a/packages/stryker/src/initializer/StrykerPresets.ts +++ b/packages/stryker/src/initializer/StrykerPresets.ts @@ -1,8 +1,8 @@ import { AngularPreset } from './presets/AngularPreset'; import { ReactPreset } from './presets/ReactPreset'; -import StrykerPreset from './presets/StrykerPreset'; +import Preset from './presets/Preset'; import { VueJsPreset } from './presets/VueJsPreset'; // Add new presets here -const strykerPresets: StrykerPreset[] = [ new AngularPreset(), new ReactPreset(), new VueJsPreset() ]; +const strykerPresets: Preset[] = [ new AngularPreset(), new ReactPreset(), new VueJsPreset() ]; export default strykerPresets; diff --git a/packages/stryker/src/initializer/presets/AngularPreset.ts b/packages/stryker/src/initializer/presets/AngularPreset.ts index ccba2f8a77..cfee88a288 100644 --- a/packages/stryker/src/initializer/presets/AngularPreset.ts +++ b/packages/stryker/src/initializer/presets/AngularPreset.ts @@ -1,19 +1,19 @@ -import StrykerPreset from './StrykerPreset'; -import { StrykerPresetConfig } from './StrykerConf'; +import Preset from './Preset'; +import PresetConfiguration from './PresetConfiguration'; +import * as os from 'os'; -/** - * More information can be found in the Stryker handbook: - * https://github.com/stryker-mutator/stryker-handbook/blob/master/stryker/guides/angular.md#angular - */ -export class AngularPreset extends StrykerPreset { - public readonly name: string = 'angular'; - private readonly dependencies = [ - 'stryker', - 'stryker-karma-runner', - 'stryker-typescript', - 'stryker-html-reporter' - ]; - private readonly config = `{ +const handbookUrl = 'https://github.com/stryker-mutator/stryker-handbook/blob/master/stryker/guides/angular.md#angular'; + +export class AngularPreset implements Preset { + public readonly name = 'angular-cli'; + // Please keep config in sync with handbook + private readonly dependencies = [ + 'stryker', + 'stryker-karma-runner', + 'stryker-typescript', + 'stryker-html-reporter' + ]; + private readonly config = `{ mutate: [ 'src/**/*.ts', '!src/**/*.spec.ts', @@ -30,10 +30,10 @@ export class AngularPreset extends StrykerPreset { } }, reporters: ['progress', 'clear-text', 'html'], - // maxConcurrentTestRunners: 2, // Recommended to use about half of your available cores when running stryker with angular. + maxConcurrentTestRunners: ${Math.floor(os.cpus().length / 2)}, // Recommended to use about half of your available cores when running stryker with angular. coverageAnalysis: 'off' }`; - public async createConfig(): Promise { - return new StrykerPresetConfig(this.config, this.dependencies); - } + public async createConfig(): Promise { + return { config: this.config, handbookUrl, dependencies: this.dependencies }; + } } diff --git a/packages/stryker/src/initializer/presets/ReactPreset.ts b/packages/stryker/src/initializer/presets/ReactPreset.ts index ddb8109c8b..65269b8f80 100644 --- a/packages/stryker/src/initializer/presets/ReactPreset.ts +++ b/packages/stryker/src/initializer/presets/ReactPreset.ts @@ -1,60 +1,60 @@ -import StrykerPreset from './StrykerPreset'; +import Preset from './Preset'; import inquirer = require('inquirer'); -import { StrykerPresetConfig } from './StrykerConf'; +import PresetConfiguration from './PresetConfiguration'; + +const handbookUrl = 'https://github.com/stryker-mutator/stryker-handbook/blob/master/stryker/guides/react.md#react'; /** * More information can be found in the Stryker handbook: * https://github.com/stryker-mutator/stryker-handbook/blob/master/stryker/guides/react.md#react */ -export class ReactPreset extends StrykerPreset { - public readonly name: string = 'react'; - private readonly generalDependencies = [ - 'stryker', - 'stryker-jest-runner', - 'stryker-html-reporter' - ]; +export class ReactPreset implements Preset { + public readonly name = 'react'; + private readonly generalDependencies = [ + 'stryker', + 'stryker-jest-runner', + 'stryker-html-reporter' + ]; + + private readonly sharedConfig = `testRunner: 'jest', + reporters: ['progress', 'clear-text', 'html'], + coverageAnalysis: 'off', + jest: { + projectType: 'react' + } + `; - private readonly tsxDependencies = ['stryker-typescript', ...this.generalDependencies]; - private readonly tsxConf = `{ + private readonly tsxDependencies = ['stryker-typescript', ...this.generalDependencies]; + private readonly tsxConf = `{ mutate: ['src/**/*.ts?(x)', '!src/**/*@(.test|.spec|Spec).ts?(x)'], mutator: 'typescript', - testRunner: 'jest', - reporters: ['progress', 'clear-text', 'html'], - coverageAnalysis: 'off', - jest: { - projectType: 'react' - } + ${this.sharedConfig} }`; - private readonly jsxDependencies = ['stryker-javascript-mutator', ...this.generalDependencies]; - private readonly jsxConf = `{ + private readonly jsxDependencies = ['stryker-javascript-mutator', ...this.generalDependencies]; + private readonly jsxConf = `{ mutate: ['src/**/*.js?(x)', '!src/**/*@(.test|.spec|Spec).js?(x)'], mutator: 'javascript', - testRunner: 'jest', - reporters: ['progress', 'clear-text', 'html'], - coverageAnalysis: 'off', - jest: { - projectType: 'react' - } + ${this.sharedConfig} }`; - public async createConfig(): Promise { - const choices: inquirer.ChoiceType[] = ['JSX', 'TSX']; - const answers = await inquirer.prompt<{ choice: string }>({ - choices, - message: 'Is your project a JSX project or a TSX project?', - name: 'choice', - type: 'list' - }); - return this.load(answers.choice); - } - private load(choice: string): StrykerPresetConfig { - if (choice === 'JSX') { - return new StrykerPresetConfig(this.jsxConf, this.jsxDependencies); - } else if (choice === 'TSX') { - return new StrykerPresetConfig(this.tsxConf, this.tsxDependencies); - } else { - throw new Error(`Invalid project type ${choice}`); - } + public async createConfig(): Promise { + const choices: inquirer.ChoiceType[] = ['JSX', 'TSX']; + const answers = await inquirer.prompt<{ choice: string }>({ + choices, + message: 'Is your project a JSX project or a TSX project?', + name: 'choice', + type: 'list' + }); + return this.load(answers.choice); + } + private load(choice: string): PresetConfiguration { + if (choice === 'JSX') { + return { config: this.jsxConf, handbookUrl, dependencies: this.jsxDependencies }; + } else if (choice === 'TSX') { + return { config: this.tsxConf, handbookUrl, dependencies: this.tsxDependencies }; + } else { + throw new Error(`Invalid project type ${choice}`); } + } } diff --git a/packages/stryker/src/initializer/presets/VueJsPreset.ts b/packages/stryker/src/initializer/presets/VueJsPreset.ts index b430109179..8073b52806 100644 --- a/packages/stryker/src/initializer/presets/VueJsPreset.ts +++ b/packages/stryker/src/initializer/presets/VueJsPreset.ts @@ -1,13 +1,15 @@ -import StrykerPreset from './StrykerPreset'; +import Preset from './Preset'; import inquirer = require('inquirer'); -import { StrykerPresetConfig } from './StrykerConf'; +import PresetConfiguration from './PresetConfiguration'; + +const handbookUrl = 'https://github.com/stryker-mutator/stryker-handbook/blob/master/stryker/guides/vuejs.md#vuejs'; /** * More information can be found in the Stryker handbook: * https://github.com/stryker-mutator/stryker-handbook/blob/master/stryker/guides/vuejs.md#vuejs */ -export class VueJsPreset extends StrykerPreset { - public readonly name: string = 'vueJs'; +export class VueJsPreset implements Preset { + public readonly name = 'vueJs'; private readonly generalDependencies = [ 'stryker', 'stryker-vue-mutator', @@ -41,42 +43,43 @@ export class VueJsPreset extends StrykerPreset { coverageAnalysis: 'off' }`; - public async createConfig(): Promise { + public async createConfig(): Promise { const testRunnerChoices: inquirer.ChoiceType[] = ['karma', 'jest']; - const testRunnerAnswers = await inquirer.prompt<{ testrunner: string }>({ + const testRunnerAnswers = await inquirer.prompt<{ testRunner: string }>({ choices: testRunnerChoices, message: 'Which test runner do you want to use?', - name: 'testrunner', + name: 'testRunner', type: 'list' }); const scriptChoices: inquirer.ChoiceType[] = ['typescript', 'javascript']; const scriptAnswers = await inquirer.prompt<{ script: string }>({ choices: scriptChoices, - message: 'What script does your project use?', + message: 'Which language does your project use?', name: 'script', type: 'list' }); - const chosenTestRunner = testRunnerAnswers.testrunner; + const chosenTestRunner = testRunnerAnswers.testRunner; const chosenScript = scriptAnswers.script; - return new StrykerPresetConfig( - this.getConfigString(chosenTestRunner), - this.createDependencies(chosenTestRunner, chosenScript) - ); + return { + config: this.getConfigString(chosenTestRunner), + dependencies: this.createDependencies(chosenTestRunner, chosenScript), + handbookUrl + }; } - private getConfigString(testrunner: string) { - if (testrunner === 'karma') { + private getConfigString(testRunner: string) { + if (testRunner === 'karma') { return this.karmaConf; - } else if (testrunner === 'jest') { + } else if (testRunner === 'jest') { return this.jestConf; } else { - throw new Error(`Invalid test runner chosen: ${testrunner}`); + throw new Error(`Invalid test runner chosen: ${testRunner}`); } } - private createDependencies(testrunner: string, script: string): string[] { + private createDependencies(testRunner: string, script: string): string[] { const dependencies = this.generalDependencies; - dependencies.push(this.getTestRunnerDependency(testrunner)); + dependencies.push(this.getTestRunnerDependency(testRunner)); dependencies.push(this.getScriptDependency(script)); return dependencies; } @@ -91,13 +94,13 @@ export class VueJsPreset extends StrykerPreset { } } - private getTestRunnerDependency(testrunner: string): string { - if (testrunner === 'karma') { + private getTestRunnerDependency(testRunner: string): string { + if (testRunner === 'karma') { return this.karmaDependency; - } else if (testrunner === 'jest') { + } else if (testRunner === 'jest') { return this.jestDependency; } else { - throw new Error(`Invalid test runner chosen: ${testrunner}`); + throw new Error(`Invalid test runner chosen: ${testRunner}`); } } } diff --git a/packages/stryker/test/unit/initializer/PresetsSpec.ts b/packages/stryker/test/unit/initializer/PresetsSpec.ts new file mode 100644 index 0000000000..e24d093888 --- /dev/null +++ b/packages/stryker/test/unit/initializer/PresetsSpec.ts @@ -0,0 +1,135 @@ +import { expect } from 'chai'; +import { AngularPreset } from '../../../src/initializer/presets/AngularPreset'; +import { ReactPreset } from '../../../src/initializer/presets/ReactPreset'; +import * as inquirer from 'inquirer'; +import { VueJsPreset } from '../../../src/initializer/presets/VueJsPreset'; + +describe('Presets', () => { + let inquirerPrompt: sinon.SinonStub; + + beforeEach(() => { + inquirerPrompt = sandbox.stub(inquirer, 'prompt'); + }); + describe('AngularPreset', () => { + let angularPreset: AngularPreset; + + beforeEach(() => { + angularPreset = new AngularPreset(); + }); + + it('should have the name "angular-cli"', () => { + expect(angularPreset.name).to.eq('angular-cli'); + }); + + it('should mutate typescript', async () => { + const config = await angularPreset.createConfig(); + expect(config.config).to.contain(`mutator: 'typescript'`); + }); + + it('should use the angular-cli', async () => { + const config = await angularPreset.createConfig(); + expect(config.config).to.contain(`projectType: 'angular-cli'`); + }); + }); + + describe('ReactPreset', () => { + let reactPreset: ReactPreset; + + beforeEach(() => { + reactPreset = new ReactPreset(); + }); + + it('should have the name "react"', () => { + expect(reactPreset.name).to.eq('react'); + }); + + it('should mutate typescript when TSX is chosen', async () => { + inquirerPrompt.resolves({ + choice: 'TSX' + }); + const config = await reactPreset.createConfig(); + expect(config.config).to.contain(`mutator: 'typescript'`); + }); + + it('should install stryker-typescript when TSX is chosen', async () => { + inquirerPrompt.resolves({ + choice: 'TSX' + }); + const config = await reactPreset.createConfig(); + expect(config.dependencies).to.include('stryker-typescript'); + }); + + it('should mutate javascript when JSX is chosen', async () => { + inquirerPrompt.resolves({ + choice: 'JSX' + }); + const config = await reactPreset.createConfig(); + expect(config.config).to.include(`mutator: 'javascript'`); + }); + + it('should install stryker-javascript-mutator when JSX is chosen', async () => { + inquirerPrompt.resolves({ + choice: 'JSX' + }); + const config = await reactPreset.createConfig(); + expect(config.dependencies).to.include('stryker-javascript-mutator'); + }); + }); + + describe('VueJsPreset', () => { + let vueJsPreset: VueJsPreset; + + beforeEach(() => { + vueJsPreset = new VueJsPreset(); + inquirerPrompt.resolves({ + script: 'typescript', + testRunner: 'karma' + }); + }); + + it('should have the name "vueJs"', () => { + expect(vueJsPreset.name).to.eq('vueJs'); + }); + + it('should use the vue mutator', async () => { + const config = await vueJsPreset.createConfig(); + expect(config.config).to.contain(`mutator: 'vue'`); + }); + + it('should install stryker-karma-runner when karma is chosen', async () => { + inquirerPrompt.resolves({ + script: 'typescript', + testRunner: 'karma' + }); + const config = await vueJsPreset.createConfig(); + expect(config.dependencies).to.include('stryker-karma-runner'); + }); + + it('should install stryker-jest-runner when jest is chosen', async () => { + inquirerPrompt.resolves({ + script: 'typescript', + testRunner: 'jest' + }); + const config = await vueJsPreset.createConfig(); + expect(config.dependencies).to.include('stryker-jest-runner'); + }); + + it('should install stryker-typescript when typescript is chosen', async () => { + inquirerPrompt.resolves({ + script: 'typescript', + testRunner: 'karma' + }); + const config = await vueJsPreset.createConfig(); + expect(config.dependencies).to.include('stryker-typescript'); + }); + + it('should install stryker-javascript-mutator when javascript is chosen', async () => { + inquirerPrompt.resolves({ + script: 'javascript', + testRunner: 'karma' + }); + const config = await vueJsPreset.createConfig(); + expect(config.dependencies).to.include('stryker-javascript-mutator'); + }); + }); +}); diff --git a/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts b/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts index d9e8cf0668..bcad7fd558 100644 --- a/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts +++ b/packages/stryker/test/unit/initializer/StrykerInitializerSpec.ts @@ -10,8 +10,8 @@ import currentLogMock from '../../helpers/logMock'; import { Mock } from '../../helpers/producers'; import NpmClient from '../../../src/initializer/NpmClient'; import { format } from 'prettier'; -import { StrykerPresetConfig } from '../../../src/initializer/presets/StrykerConf'; -import StrykerPreset from '../../../src/initializer/presets/StrykerPreset'; +import PresetConfiguration from '../../../src/initializer/presets/PresetConfiguration'; +import Preset from '../../../src/initializer/presets/Preset'; describe('StrykerInitializer', () => { let log: Mock; @@ -23,14 +23,17 @@ describe('StrykerInitializer', () => { let restClientPackageGet: sinon.SinonStub; let restClientSearchGet: sinon.SinonStub; let out: sinon.SinonStub; - let presets: StrykerPreset[]; - let presetMock: StrykerPresetMock; + let presets: Preset[]; + let presetMock: Mock; beforeEach(() => { log = currentLogMock(); out = sandbox.stub(); presets = []; - presetMock = new StrykerPresetMock(); + presetMock = { + createConfig: sandbox.stub(), + name: 'awesome-preset' + }; inquirerPrompt = sandbox.stub(inquirer, 'prompt'); childExecSync = sandbox.stub(child, 'execSync'); fsWriteFile = sandbox.stub(fsAsPromised, 'writeFile'); @@ -100,7 +103,7 @@ describe('StrykerInitializer', () => { ]; expect(promptPreset.type).to.eq('list'); expect(promptPreset.name).to.eq('preset'); - expect(promptPreset.choices).to.deep.eq(['awesome-preset', new inquirer.Separator(), 'none']); + expect(promptPreset.choices).to.deep.eq(['awesome-preset', new inquirer.Separator(), 'None/other']); expect(promptTestRunner.type).to.eq('list'); expect(promptTestRunner.name).to.eq('testRunner'); expect(promptTestRunner.choices).to.deep.eq(['awesome', 'hyper', 'ghost', new inquirer.Separator(), 'command']); @@ -121,6 +124,7 @@ describe('StrykerInitializer', () => { packageManager: 'npm', preset: 'awesome-preset' }); + resolvePresetConfig(); await sut.initialize(); expect(inquirerPrompt).to.have.been.callCount(2); expect(out).to.have.been.calledWith('Done configuring stryker. Please review `stryker.conf.js`, you might need to configure transpilers or your test runner correctly.'); @@ -128,13 +132,20 @@ describe('StrykerInitializer', () => { }); it('should correctly load the stryker configuration file', async () => { - presetMock.config = `{ + const config = `{ 'awesome-conf': 'awesome', }`; + const handbookUrl = 'https://awesome-preset.org'; + resolvePresetConfig({ + config, + handbookUrl + }); const expectedOutput = format(` + // This config was generated using a preset. + // Please see the handbook for more information: ${handbookUrl} module.exports = function(config){ config.set( - ${presetMock.config} + ${config} ); }`, { parser: 'babylon' }); inquirerPrompt.resolves({ @@ -146,7 +157,7 @@ describe('StrykerInitializer', () => { }); it('should correctly load dependencies from the preset', async () => { - presetMock.dependencies = ['my-awesome-dependency', 'another-awesome-dependency']; + resolvePresetConfig({ dependencies: ['my-awesome-dependency', 'another-awesome-dependency'] }); inquirerPrompt.resolves({ packageManager: 'npm', preset: 'awesome-preset' @@ -156,34 +167,21 @@ describe('StrykerInitializer', () => { expect(childExecSync).to.have.been.calledWith('npm i --save-dev stryker-api my-awesome-dependency another-awesome-dependency', { stdio: [0, 1, 2] }); }); - it('should correctly prompt for additional preset-specific options', async () => { - presetMock.createConfig = async () => { - await inquirer.prompt<{ awesome: string }>({ - choices: ['yes', 'no'], - message: 'Are you awesome', - name: 'awesome', - type: 'list' - }); - return new StrykerPresetConfig(presetMock.config, presetMock.dependencies); - }; + it('should correctly load configuration from a preset', async () => { + resolvePresetConfig(); inquirerPrompt.resolves({ - awesome: 'yes', packageManager: 'npm', preset: 'awesome-preset' }); await sut.initialize(); - expect(inquirerPrompt).to.have.been.callCount(3); - const [promptPreset, promptAwesome, promptPackageManager]: inquirer.Question[] = [ + expect(inquirerPrompt).to.have.been.callCount(2); + const [promptPreset, promptPackageManager]: inquirer.Question[] = [ inquirerPrompt.getCall(0).args[0], - inquirerPrompt.getCall(1).args[0], - inquirerPrompt.getCall(2).args[0] + inquirerPrompt.getCall(1).args[0] ]; expect(promptPreset.type).to.eq('list'); expect(promptPreset.name).to.eq('preset'); - expect(promptPreset.choices).to.deep.eq(['awesome-preset', new inquirer.Separator(), 'none']); - expect(promptAwesome.type).to.eq('list'); - expect(promptAwesome.name).to.eq('awesome'); - expect(promptAwesome.choices).to.deep.eq(['yes', 'no']); + expect(promptPreset.choices).to.deep.eq(['awesome-preset', new inquirer.Separator(), 'None/other']); expect(promptPackageManager.type).to.eq('list'); expect(promptPackageManager.choices).to.deep.eq(['npm', 'yarn']); }); @@ -313,22 +311,22 @@ describe('StrykerInitializer', () => { describe('initialize() when no internet', () => { it('should log error and continue when fetching test runners', async () => { - restClientSearchGet.withArgs('/v2/search?q=keywords:stryker-test-runner').rejects(); - stubMutators('stryker-javascript'); - stubTranspilers('stryker-webpack'); - stubReporters(); - stubPackageClient({ 'stryker-javascript': null, 'stryker-webpack': null }); - inquirerPrompt.resolves({ - packageManager: 'npm', - reporters: ['clear-text'], - transpilers: ['webpack'] - }); + restClientSearchGet.withArgs('/v2/search?q=keywords:stryker-test-runner').rejects(); + stubMutators('stryker-javascript'); + stubTranspilers('stryker-webpack'); + stubReporters(); + stubPackageClient({ 'stryker-javascript': null, 'stryker-webpack': null }); + inquirerPrompt.resolves({ + packageManager: 'npm', + reporters: ['clear-text'], + transpilers: ['webpack'] + }); - await sut.initialize(); + await sut.initialize(); - expect(log.error).to.have.been.calledWith('Unable to reach https://api.npms.io (for query /v2/search?q=keywords:stryker-test-runner). Please check your internet connection.'); - expect(out).to.have.been.calledWith('Unable to select a test runner. You will need to configure it manually.'); - expect(fsAsPromised.writeFile).to.have.been.called; + expect(log.error).to.have.been.calledWith('Unable to reach https://api.npms.io (for query /v2/search?q=keywords:stryker-test-runner). Please check your internet connection.'); + expect(out).to.have.been.calledWith('Unable to select a test runner. You will need to configure it manually.'); + expect(fsAsPromised.writeFile).to.have.been.called; }); it('should log error and continue when fetching test frameworks', async () => { @@ -524,13 +522,13 @@ describe('StrykerInitializer', () => { }, answerOverrides); inquirerPrompt.resolves(answers); } -}); -class StrykerPresetMock extends StrykerPreset { - public name: string = 'awesome-preset'; - public dependencies: string[] = []; - public config: string = ''; - public async createConfig() { - return new StrykerPresetConfig(this.config, this.dependencies); + function resolvePresetConfig(presetConfigOverrides?: Partial) { + const presetConfig: PresetConfiguration = { + config: '', + dependencies: [], + handbookUrl: '' + }; + presetMock.createConfig.resolves(Object.assign({}, presetConfig, presetConfigOverrides)); } -} +}); From 520f39e1065263cd5fc4a172956bc6097b0d120b Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Tue, 27 Nov 2018 12:08:17 +0100 Subject: [PATCH 12/13] Fix typing of the prompPresets returned promise --- packages/stryker/src/initializer/StrykerInquirer.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/stryker/src/initializer/StrykerInquirer.ts b/packages/stryker/src/initializer/StrykerInquirer.ts index 39aaa4ba43..ad926cc761 100644 --- a/packages/stryker/src/initializer/StrykerInquirer.ts +++ b/packages/stryker/src/initializer/StrykerInquirer.ts @@ -1,7 +1,7 @@ import * as inquirer from 'inquirer'; import PromptOption from './PromptOption'; import CommandTestRunner from '../test-runner/CommandTestRunner'; -import StrykerPreset from './presets/StrykerPreset'; +import Preset from './presets/Preset'; export interface PromptResult { additionalNpmDependencies: string[]; @@ -10,17 +10,17 @@ export interface PromptResult { export class StrykerInquirer { - public async promptPresets(options: StrykerPreset[]): Promise { + public async promptPresets(options: Preset[]): Promise { const choices: inquirer.ChoiceType[] = options.map(_ => _.name); choices.push(new inquirer.Separator()); - choices.push('none'); + choices.push('None/other'); const answers = await inquirer.prompt<{ preset: string }>({ choices, message: 'Are you using one of these frameworks? Then select a preset configuration.', name: 'preset', type: 'list' }); - return options.filter(_ => _.name === answers.preset)[0]; + return options.find(_ => _.name === answers.preset); } public async promptTestRunners(options: PromptOption[]): Promise { From d8bff5cec31e447cb275bd2e42f0b6eef6ba8fa6 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Tue, 27 Nov 2018 12:09:02 +0100 Subject: [PATCH 13/13] Format files using editorconfig --- .../src/initializer/presets/VueJsPreset.ts | 126 +++++++++--------- .../test/unit/initializer/PresetsSpec.ts | 8 +- 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/packages/stryker/src/initializer/presets/VueJsPreset.ts b/packages/stryker/src/initializer/presets/VueJsPreset.ts index 8073b52806..33fc44bd98 100644 --- a/packages/stryker/src/initializer/presets/VueJsPreset.ts +++ b/packages/stryker/src/initializer/presets/VueJsPreset.ts @@ -9,15 +9,15 @@ const handbookUrl = 'https://github.com/stryker-mutator/stryker-handbook/blob/ma * https://github.com/stryker-mutator/stryker-handbook/blob/master/stryker/guides/vuejs.md#vuejs */ export class VueJsPreset implements Preset { - public readonly name = 'vueJs'; - private readonly generalDependencies = [ - 'stryker', - 'stryker-vue-mutator', - 'stryker-html-reporter' - ]; + public readonly name = 'vueJs'; + private readonly generalDependencies = [ + 'stryker', + 'stryker-vue-mutator', + 'stryker-html-reporter' + ]; - private readonly jestDependency = 'stryker-jest-runner'; - private readonly jestConf = `{ + private readonly jestDependency = 'stryker-jest-runner'; + private readonly jestConf = `{ mutate: ['src/**/*.js', 'src/**/*.ts', 'src/**/*.vue'], mutator: 'vue', testRunner: 'jest', @@ -28,8 +28,8 @@ export class VueJsPreset implements Preset { coverageAnalysis: 'off' }`; - private readonly karmaDependency = 'stryker-karma-runner'; - private readonly karmaConf = `{ + private readonly karmaDependency = 'stryker-karma-runner'; + private readonly karmaConf = `{ mutate: ['src/**/*.js', 'src/**/*.ts', 'src/**/*.vue'], mutator: 'vue', testRunner: 'karma', @@ -43,64 +43,64 @@ export class VueJsPreset implements Preset { coverageAnalysis: 'off' }`; - public async createConfig(): Promise { - const testRunnerChoices: inquirer.ChoiceType[] = ['karma', 'jest']; - const testRunnerAnswers = await inquirer.prompt<{ testRunner: string }>({ - choices: testRunnerChoices, - message: 'Which test runner do you want to use?', - name: 'testRunner', - type: 'list' - }); - const scriptChoices: inquirer.ChoiceType[] = ['typescript', 'javascript']; - const scriptAnswers = await inquirer.prompt<{ script: string }>({ - choices: scriptChoices, - message: 'Which language does your project use?', - name: 'script', - type: 'list' - }); - const chosenTestRunner = testRunnerAnswers.testRunner; - const chosenScript = scriptAnswers.script; - return { - config: this.getConfigString(chosenTestRunner), - dependencies: this.createDependencies(chosenTestRunner, chosenScript), - handbookUrl - }; - } + public async createConfig(): Promise { + const testRunnerChoices: inquirer.ChoiceType[] = ['karma', 'jest']; + const testRunnerAnswers = await inquirer.prompt<{ testRunner: string }>({ + choices: testRunnerChoices, + message: 'Which test runner do you want to use?', + name: 'testRunner', + type: 'list' + }); + const scriptChoices: inquirer.ChoiceType[] = ['typescript', 'javascript']; + const scriptAnswers = await inquirer.prompt<{ script: string }>({ + choices: scriptChoices, + message: 'Which language does your project use?', + name: 'script', + type: 'list' + }); + const chosenTestRunner = testRunnerAnswers.testRunner; + const chosenScript = scriptAnswers.script; + return { + config: this.getConfigString(chosenTestRunner), + dependencies: this.createDependencies(chosenTestRunner, chosenScript), + handbookUrl + }; + } - private getConfigString(testRunner: string) { - if (testRunner === 'karma') { - return this.karmaConf; - } else if (testRunner === 'jest') { - return this.jestConf; - } else { - throw new Error(`Invalid test runner chosen: ${testRunner}`); - } + private getConfigString(testRunner: string) { + if (testRunner === 'karma') { + return this.karmaConf; + } else if (testRunner === 'jest') { + return this.jestConf; + } else { + throw new Error(`Invalid test runner chosen: ${testRunner}`); } + } - private createDependencies(testRunner: string, script: string): string[] { - const dependencies = this.generalDependencies; - dependencies.push(this.getTestRunnerDependency(testRunner)); - dependencies.push(this.getScriptDependency(script)); - return dependencies; - } + private createDependencies(testRunner: string, script: string): string[] { + const dependencies = this.generalDependencies; + dependencies.push(this.getTestRunnerDependency(testRunner)); + dependencies.push(this.getScriptDependency(script)); + return dependencies; + } - private getScriptDependency(script: string): string { - if (script === 'typescript') { - return 'stryker-typescript'; - } else if (script === 'javascript') { - return 'stryker-javascript-mutator'; - } else { - throw new Error(`Invalid script chosen: ${script}`); - } + private getScriptDependency(script: string): string { + if (script === 'typescript') { + return 'stryker-typescript'; + } else if (script === 'javascript') { + return 'stryker-javascript-mutator'; + } else { + throw new Error(`Invalid script chosen: ${script}`); } + } - private getTestRunnerDependency(testRunner: string): string { - if (testRunner === 'karma') { - return this.karmaDependency; - } else if (testRunner === 'jest') { - return this.jestDependency; - } else { - throw new Error(`Invalid test runner chosen: ${testRunner}`); - } + private getTestRunnerDependency(testRunner: string): string { + if (testRunner === 'karma') { + return this.karmaDependency; + } else if (testRunner === 'jest') { + return this.jestDependency; + } else { + throw new Error(`Invalid test runner chosen: ${testRunner}`); } + } } diff --git a/packages/stryker/test/unit/initializer/PresetsSpec.ts b/packages/stryker/test/unit/initializer/PresetsSpec.ts index e24d093888..4ee1e838c8 100644 --- a/packages/stryker/test/unit/initializer/PresetsSpec.ts +++ b/packages/stryker/test/unit/initializer/PresetsSpec.ts @@ -14,11 +14,11 @@ describe('Presets', () => { let angularPreset: AngularPreset; beforeEach(() => { - angularPreset = new AngularPreset(); + angularPreset = new AngularPreset(); }); it('should have the name "angular-cli"', () => { - expect(angularPreset.name).to.eq('angular-cli'); + expect(angularPreset.name).to.eq('angular-cli'); }); it('should mutate typescript', async () => { @@ -40,7 +40,7 @@ describe('Presets', () => { }); it('should have the name "react"', () => { - expect(reactPreset.name).to.eq('react'); + expect(reactPreset.name).to.eq('react'); }); it('should mutate typescript when TSX is chosen', async () => { @@ -88,7 +88,7 @@ describe('Presets', () => { }); it('should have the name "vueJs"', () => { - expect(vueJsPreset.name).to.eq('vueJs'); + expect(vueJsPreset.name).to.eq('vueJs'); }); it('should use the vue mutator', async () => {