Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(presets): update init templates for 4.0 release #2526

Merged
merged 3 commits into from Oct 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/core/src/initializer/StrykerConfigWriter.ts
@@ -1,6 +1,6 @@
import { existsSync, promises as fs } from 'fs';

import { StrykerOptions } from '@stryker-mutator/api/core';
import { PartialStrykerOptions, StrykerOptions } from '@stryker-mutator/api/core';
import { Logger } from '@stryker-mutator/api/logging';
import { commonTokens, tokens } from '@stryker-mutator/api/plugin';
import { childProcessAsPromised } from '@stryker-mutator/util';
Expand Down Expand Up @@ -60,22 +60,22 @@ export default class StrykerConfigWriter {
*/
public async writePreset(presetConfig: PresetConfiguration, exportAsJson: boolean) {
const config = {
comment: `This config was generated using a preset. Please see the handbook for more information: ${presetConfig.handbookUrl}`,
_comment: `This config was generated using a preset. Please see the handbook for more information: ${presetConfig.handbookUrl}`,
...presetConfig.config,
};

return this.writeStrykerConfig(config, exportAsJson);
}

private writeStrykerConfig(config: Partial<StrykerOptions>, exportAsJson: boolean) {
private writeStrykerConfig(config: PartialStrykerOptions, exportAsJson: boolean) {
if (exportAsJson) {
return this.writeJsonConfig(config);
} else {
return this.writeJsConfig(config);
}
}

private async writeJsConfig(commentedConfig: Partial<StrykerOptions>) {
private async writeJsConfig(commentedConfig: PartialStrykerOptions) {
this.out(`Writing & formatting ${STRYKER_JS_CONFIG_FILE}...`);
const rawConfig = this.stringify(commentedConfig);
const formattedConfig = `/**
Expand All @@ -93,7 +93,7 @@ export default class StrykerConfigWriter {
return STRYKER_JS_CONFIG_FILE;
}

private async writeJsonConfig(commentedConfig: Partial<StrykerOptions>) {
private async writeJsonConfig(commentedConfig: PartialStrykerOptions) {
this.out(`Writing & formatting ${STRYKER_JSON_CONFIG_FILE}...`);
const typedConfig = {
$schema: './node_modules/@stryker-mutator/core/schema/stryker-schema.json',
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/initializer/StrykerInitializer.ts
@@ -1,4 +1,5 @@
import * as child from 'child_process';
import { promises as fs } from 'fs';

import { commonTokens, tokens } from '@stryker-mutator/api/plugin';
import { Logger } from '@stryker-mutator/api/logging';
Expand Down Expand Up @@ -87,6 +88,9 @@ export default class StrykerInitializer {
const presetConfig = await selectedPreset.createConfig();
const isJsonSelected = await this.selectJsonConfigType();
const configFileName = await configWriter.writePreset(presetConfig, isJsonSelected);
if (presetConfig.additionalConfigFiles) {
await Promise.all(presetConfig.additionalConfigFiles.map(({ name, content }) => fs.writeFile(name, content)));
}
const selectedPackageManager = await this.selectPackageManager();
this.installNpmDependencies(presetConfig.dependencies, selectedPackageManager);
return configFileName;
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/initializer/presets/AngularPreset.ts
Expand Up @@ -10,7 +10,7 @@ const handbookUrl = 'https://github.com/stryker-mutator/stryker-handbook/blob/ma
export class AngularPreset implements Preset {
public readonly name = 'angular-cli';
// Please keep config in sync with handbook
private readonly dependencies = ['@stryker-mutator/core', '@stryker-mutator/karma-runner', '@stryker-mutator/typescript'];
private readonly dependencies = ['@stryker-mutator/karma-runner'];
private readonly config: Partial<StrykerOptions> = {
mutate: ['src/**/*.ts', '!src/**/*.spec.ts', '!src/test.ts', '!src/environments/*.ts'],
testRunner: 'karma',
Expand All @@ -24,8 +24,8 @@ export class AngularPreset implements Preset {
reporters: ['progress', 'clear-text', 'html'],
concurrency: Math.floor(os.cpus().length / 2),
// eslint-disable-next-line camelcase
maxConcurrentTestRunners_comment: 'Recommended to use about half of your available cores when running stryker with angular',
coverageAnalysis: 'off',
concurrency_comment: 'Recommended to use about half of your available cores when running stryker with angular',
coverageAnalysis: 'perTest',
};

public async createConfig(): Promise<PresetConfiguration> {
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/initializer/presets/PresetConfiguration.ts
@@ -1,7 +1,8 @@
import { StrykerOptions } from '@stryker-mutator/api/core';
import { File, PartialStrykerOptions } from '@stryker-mutator/api/core';

export default interface PresetConfiguration {
config: Partial<StrykerOptions>;
config: PartialStrykerOptions;
handbookUrl: string;
dependencies: string[];
additionalConfigFiles?: File[];
}
35 changes: 3 additions & 32 deletions packages/core/src/initializer/presets/ReactPreset.ts
@@ -1,4 +1,3 @@
import inquirer = require('inquirer');
import { StrykerOptions } from '@stryker-mutator/api/core';

import Preset from './Preset';
Expand All @@ -12,9 +11,9 @@ const handbookUrl = 'https://github.com/stryker-mutator/stryker-handbook/blob/ma
*/
export class ReactPreset implements Preset {
public readonly name = 'create-react-app';
private readonly generalDependencies = ['@stryker-mutator/core', '@stryker-mutator/jest-runner'];
private readonly dependencies = ['@stryker-mutator/jest-runner'];

private readonly sharedConfig: Partial<StrykerOptions> = {
private readonly config: Partial<StrykerOptions> = {
testRunner: 'jest',
reporters: ['progress', 'clear-text', 'html'],
coverageAnalysis: 'off',
Expand All @@ -23,35 +22,7 @@ export class ReactPreset implements Preset {
},
};

private readonly tsxDependencies = ['@stryker-mutator/typescript', ...this.generalDependencies];
private readonly tsxConf: Partial<StrykerOptions> = {
mutate: ['src/**/*.ts?(x)', '!src/**/*@(.test|.spec|Spec).ts?(x)'],
...this.sharedConfig,
};

private readonly jsxDependencies = ['@stryker-mutator/javascript-mutator', ...this.generalDependencies];
private readonly jsxConf: Partial<StrykerOptions> = {
mutate: ['src/**/*.js?(x)', '!src/**/*@(.test|.spec|Spec).js?(x)'],
...this.sharedConfig,
};

public async createConfig(): Promise<PresetConfiguration> {
const choices = ['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}`);
}
return { config: this.config, handbookUrl, dependencies: this.dependencies };
}
}
103 changes: 58 additions & 45 deletions packages/core/src/initializer/presets/VueJsPreset.ts
@@ -1,5 +1,5 @@
import inquirer = require('inquirer');
import { StrykerOptions } from '@stryker-mutator/api/core';
import { File, PartialStrykerOptions } from '@stryker-mutator/api/core';

import Preset from './Preset';
import PresetConfiguration from './PresetConfiguration';
Expand All @@ -11,90 +11,103 @@ 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-mutator/core', '@stryker-mutator/vue-mutator'];
public readonly name = 'vue-cli';

private readonly jestDependency = '@stryker-mutator/jest-runner';
private readonly jestConf: Partial<StrykerOptions> = {
mutate: ['src/**/*.js', 'src/**/*.ts', 'src/**/*.vue'],
private readonly jestConf: PartialStrykerOptions = {
testRunner: 'jest',
mutator: {
plugins: [],
},
jest: {
// config: require('path/to/your/custom/jestConfig.js')
},
reporters: ['progress', 'clear-text', 'html'],
coverageAnalysis: 'off',
};

private readonly karmaDependency = '@stryker-mutator/karma-runner';
private readonly karmaConf: Partial<StrykerOptions> = {
mutate: ['src/**/*.js', 'src/**/*.ts', 'src/**/*.vue'],
testRunner: 'karma',
karma: {
configFile: 'test/unit/karma.conf.js',
config: {
browsers: ['ChromeHeadless'],
},
private readonly mochaConf: PartialStrykerOptions = {
testRunner: 'mocha',
mutator: {
plugins: [],
},
mochaOptions: {
require: ['@vue/cli-plugin-unit-mocha/setup.js'],
spec: ['dist/js/chunk-vendors.js', 'dist/js/tests.js'],
},
buildCommand: 'webpack --config webpack.config.stryker.js',
reporters: ['progress', 'clear-text', 'html'],
coverageAnalysis: 'off',
coverageAnalysis: 'perTest',
};

public async createConfig(): Promise<PresetConfiguration> {
const testRunnerChoices = ['karma', 'jest'];
const testRunnerChoices = ['mocha', '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 = ['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.getConfig(chosenTestRunner),
dependencies: this.createDependencies(chosenTestRunner, chosenScript),
dependencies: this.createDependencies(chosenTestRunner),
handbookUrl,
additionalConfigFiles: this.getAdditionalConfigFiles(chosenTestRunner),
};
}

private getConfig(testRunner: string) {
if (testRunner === 'karma') {
return this.karmaConf;
if (testRunner === 'mocha') {
return this.mochaConf;
} 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 getAdditionalConfigFiles(testRunner: string): File[] | undefined {
if (testRunner === 'mocha') {
return [
new File(
'webpack.config.stryker.js',
`
const glob = require('glob');

// Set env
process.env.BABEL_ENV = 'test';
process.env.NODE_ENV = 'test';
process.env.VUE_CLI_BABEL_TARGET_NODE = 'true';
process.env.VUE_CLI_TRANSPILE_BABEL_RUNTIME = 'true';

// Load webpack config
const conf = require('@vue/cli-service/webpack.config.js');

// Override the entry files
conf.entry = {
// Choose your test files here:
tests: glob.sync('{test,tests}/**/*+(spec).js').map((fileName) => \`./\${fileName}\`),
};

private getScriptDependency(script: string): string {
if (script === 'typescript') {
return '@stryker-mutator/typescript';
} else if (script === 'javascript') {
return '@stryker-mutator/javascript-mutator';
module.exports = conf;
`
),
];
} else {
throw new Error(`Invalid script chosen: ${script}`);
return;
}
}

private getTestRunnerDependency(testRunner: string): string {
if (testRunner === 'karma') {
return this.karmaDependency;
private createDependencies(testRunner: string): string[] {
const dependencies = [];
dependencies.push(...this.getTestRunnerDependency(testRunner));
return dependencies;
}

private getTestRunnerDependency(testRunner: string): string[] {
if (testRunner === 'mocha') {
return ['@stryker-mutator/mocha-runner', 'glob', 'webpack-cli'];
} else if (testRunner === 'jest') {
return this.jestDependency;
return ['@stryker-mutator/jest-runner'];
} else {
throw new Error(`Invalid test runner chosen: ${testRunner}`);
}
Expand Down
46 changes: 6 additions & 40 deletions packages/core/test/unit/initializer/Presets.spec.ts
Expand Up @@ -39,22 +39,6 @@ describe('Presets', () => {
it('should have the name "create-react-app"', () => {
expect(reactPreset.name).to.eq('create-react-app');
});

it('should install @stryker-mutator/typescript when TSX is chosen', async () => {
inquirerPrompt.resolves({
choice: 'TSX',
});
const config = await reactPreset.createConfig();
expect(config.dependencies).to.include('@stryker-mutator/typescript');
});

it('should install @stryker-mutator/javascript-mutator when JSX is chosen', async () => {
inquirerPrompt.resolves({
choice: 'JSX',
});
const config = await reactPreset.createConfig();
expect(config.dependencies).to.include('@stryker-mutator/javascript-mutator');
});
});

describe('VueJsPreset', () => {
Expand All @@ -64,21 +48,21 @@ describe('Presets', () => {
vueJsPreset = new VueJsPreset();
inquirerPrompt.resolves({
script: 'typescript',
testRunner: 'karma',
testRunner: 'jest',
});
});

it('should have the name "vueJs"', () => {
expect(vueJsPreset.name).to.eq('vueJs');
it('should have the name "vue-cli"', () => {
expect(vueJsPreset.name).to.eq('vue-cli');
});

it('should install @stryker-mutator/karma-runner when karma is chosen', async () => {
it('should install @stryker-mutator/mocha-runner when mocha is chosen', async () => {
inquirerPrompt.resolves({
script: 'typescript',
testRunner: 'karma',
testRunner: 'mocha',
});
const config = await vueJsPreset.createConfig();
expect(config.dependencies).to.include('@stryker-mutator/karma-runner');
expect(config.dependencies).to.include('@stryker-mutator/mocha-runner');
});

it('should install @stryker-mutator/jest-runner when jest is chosen', async () => {
Expand All @@ -89,23 +73,5 @@ describe('Presets', () => {
const config = await vueJsPreset.createConfig();
expect(config.dependencies).to.include('@stryker-mutator/jest-runner');
});

it('should install @stryker-mutator/typescript when typescript is chosen', async () => {
inquirerPrompt.resolves({
script: 'typescript',
testRunner: 'karma',
});
const config = await vueJsPreset.createConfig();
expect(config.dependencies).to.include('@stryker-mutator/typescript');
});

it('should install @stryker-mutator/javascript-mutator when javascript is chosen', async () => {
inquirerPrompt.resolves({
script: 'javascript',
testRunner: 'karma',
});
const config = await vueJsPreset.createConfig();
expect(config.dependencies).to.include('@stryker-mutator/javascript-mutator');
});
});
});