Skip to content

Commit

Permalink
feat(init): add buildCommand question when running (#3213)
Browse files Browse the repository at this point in the history
On running `stryker init`, the user is prompted for a custom build command to run provided that they do not choose the jest test runner.

![image](https://user-images.githubusercontent.com/44368997/137911833-801d7637-eca3-450c-a7d6-d03078581dc6.png)

Co-authored-by: Nico Jansen <jansennico@gmail.com>
  • Loading branch information
radiantly and nicojs committed Oct 25, 2021
1 parent 4861dac commit b9d5980
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 10 deletions.
4 changes: 4 additions & 0 deletions packages/core/src/initializer/stryker-config-writer.ts
Expand Up @@ -38,6 +38,7 @@ export class StrykerConfigWriter {
*/
public write(
selectedTestRunner: PromptOption,
buildCommand: PromptOption,
selectedReporters: PromptOption[],
selectedPackageManager: PromptOption,
additionalPiecesOfConfig: Array<Partial<StrykerOptions>>,
Expand All @@ -50,6 +51,9 @@ export class StrykerConfigWriter {
coverageAnalysis: CommandTestRunner.is(selectedTestRunner.name) ? 'off' : 'perTest',
};

// Only write buildCommand to config file if non-empty
if (buildCommand.name) configObject.buildCommand = buildCommand.name;

Object.assign(configObject, ...additionalPiecesOfConfig);
return this.writeStrykerConfig(configObject, exportAsJson);
}
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/initializer/stryker-initializer.ts
Expand Up @@ -98,12 +98,14 @@ export class StrykerInitializer {

private async initiateCustom(configWriter: StrykerConfigWriter) {
const selectedTestRunner = await this.selectTestRunner();
const buildCommand = await this.getBuildCommand(selectedTestRunner);
const selectedReporters = await this.selectReporters();
const selectedPackageManager = await this.selectPackageManager();
const isJsonSelected = await this.selectJsonConfigType();
const npmDependencies = this.getSelectedNpmDependencies([selectedTestRunner].concat(selectedReporters));
const configFileName = await configWriter.write(
selectedTestRunner,
buildCommand,
selectedReporters,
selectedPackageManager,
await this.fetchAdditionalConfig(npmDependencies),
Expand All @@ -122,6 +124,11 @@ export class StrykerInitializer {
return this.inquirer.promptTestRunners(testRunnerOptions);
}

private async getBuildCommand(selectedTestRunner: PromptOption): Promise<PromptOption> {
const shouldSkipQuestion = selectedTestRunner.name === 'jest';
return this.inquirer.promptBuildCommand(shouldSkipQuestion);
}

private async selectReporters(): Promise<PromptOption[]> {
const reporterOptions = await this.client.getTestReporterOptions();
reporterOptions.push(
Expand Down
12 changes: 12 additions & 0 deletions packages/core/src/initializer/stryker-inquirer.ts
Expand Up @@ -43,6 +43,18 @@ export class StrykerInquirer {
}
}

public async promptBuildCommand(skip: boolean): Promise<PromptOption> {
const { buildCommand } = await inquirer.prompt<{ buildCommand: string }>({
message:
'What build command should be executed just before running your tests? For example: "npm run build" or "tsc -b" (leave empty when this is not needed).',
name: 'buildCommand',
default: 'none',
when: !skip,
});

return { name: buildCommand !== 'none' ? buildCommand : '', pkg: null };
}

public async promptReporters(options: PromptOption[]): Promise<PromptOption[]> {
const answers = await inquirer.prompt<{ reporters: string[] }>({
choices: options.map((_) => _.name),
Expand Down
72 changes: 62 additions & 10 deletions packages/core/test/unit/initializer/stryker-initializer.spec.ts
Expand Up @@ -62,7 +62,7 @@ describe(StrykerInitializer.name, () => {

describe('initialize()', () => {
beforeEach(() => {
stubTestRunners('@stryker-mutator/awesome-runner', 'stryker-hyper-runner', 'stryker-ghost-runner');
stubTestRunners('@stryker-mutator/awesome-runner', 'stryker-hyper-runner', 'stryker-ghost-runner', '@stryker-mutator/jest-runner');
stubMutators('@stryker-mutator/typescript', '@stryker-mutator/javascript-mutator');
stubReporters('stryker-dimension-reporter', '@stryker-mutator/mars-reporter');
stubPackageClient({
Expand All @@ -77,6 +77,7 @@ describe(StrykerInitializer.name, () => {
files: [],
someOtherSetting: 'enabled',
},
'@stryker-mutator/jest-runner': null,
});
fsWriteFile.resolves();
presets.push(presetMock);
Expand All @@ -91,20 +92,24 @@ describe(StrykerInitializer.name, () => {

await sut.initialize();

expect(inquirerPrompt).callCount(5);
const [promptPreset, promptTestRunner, promptReporters, promptPackageManagers, promptConfigTypes]: inquirer.ListQuestion[] = [
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],
];
expect(inquirerPrompt).callCount(6);
const [promptPreset, promptTestRunner, promptBuildCommand, promptReporters, promptPackageManagers, promptConfigTypes]: inquirer.ListQuestion[] =
[
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],
];

expect(promptPreset.type).to.eq('list');
expect(promptPreset.name).to.eq('preset');
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']);
expect(promptTestRunner.choices).to.deep.eq(['awesome', 'hyper', 'ghost', 'jest', new inquirer.Separator(), 'command']);
expect(promptBuildCommand.name).to.eq('buildCommand');
expect(promptReporters.type).to.eq('checkbox');
expect(promptReporters.choices).to.deep.eq(['dimension', 'mars', 'html', 'clear-text', 'progress', 'dashboard']);
expect(promptPackageManagers.type).to.eq('list');
Expand Down Expand Up @@ -249,6 +254,53 @@ describe(StrykerInitializer.name, () => {
expect(fs.promises.writeFile).calledWith('stryker.conf.json', sinon.match('"files": []'));
});

it('should not prompt for buildCommand if test runner is jest', async () => {
inquirerPrompt.resolves({
packageManager: 'npm',
reporters: ['dimension', 'mars', 'progress'],
testRunner: 'jest',
configType: 'JSON',
buildCommand: 'none',
});

await sut.initialize();

const promptBuildCommand = inquirerPrompt.getCalls().filter((call) => call.args[0].name === 'buildCommand');
expect(promptBuildCommand.length === 1);
expect(promptBuildCommand[0].args[0].when).to.be.false;
expect(fs.promises.writeFile).calledWith(
'stryker.conf.json',
sinon.match((val) => !val.includes('"buildCommand": '))
);
});

it('should not write "buildCommand" config option if empty buildCommand entered', async () => {
inquirerPrompt.resolves({
packageManager: 'npm',
reporters: [],
testRunner: 'hyper',
configType: 'JSON',
buildCommand: 'none',
});
await sut.initialize();
expect(fs.promises.writeFile).calledWith(
'stryker.conf.json',
sinon.match((val) => !val.includes('"buildCommand": '))
);
});

it('should save entered build command', async () => {
inquirerPrompt.resolves({
packageManager: 'npm',
reporters: [],
testRunner: 'hyper',
configType: 'JSON',
buildCommand: 'npm run build',
});
await sut.initialize();
expect(fs.promises.writeFile).calledWith('stryker.conf.json', sinon.match('"buildCommand": "npm run build"'));
});

it('should set "coverageAnalysis" to "off" when the command test runner is chosen', async () => {
inquirerPrompt.resolves({
packageManager: 'npm',
Expand Down

0 comments on commit b9d5980

Please sign in to comment.