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

feat(Stryker CLI 'init'): Support for preset configuration during 'stryker init' #1248

Merged
merged 15 commits into from
Nov 27, 2018

Conversation

Wmaarts
Copy link
Contributor

@Wmaarts Wmaarts commented Nov 22, 2018

Implementation of #1049

? Are you using one of these frameworks? Then select a preset configuration. (Use arrow keys)
> angular
  react
  vueJs
  ──────────────
  none

Small disclaimer

(my first time contributing to an open-source project, hope I'm doing this right. Also, I'm new to the stryker project. 😄 )

Summary

  • Running 'stryker init' prompts the user to select a preset (or do custom setup).
  • Supports Angular, React and vuejs
  • Loads a fixed configuration and installs appropriate dependencies
  • Supports adding preset-specific questions
    • For example: React preset prompts the user to select between JSX and TSX config
  • Some unit tests written
  • All based on the the stryker handbook

Implementation

Where?

Presets are defined in the stryker package under \src\initializer\presets.

Adding a new preset

Extends the StrykerPreset class like so:

export class MyPreset extends StrykerPreset { 
    public dependencies: string[] = [];        // preset-specific dependencies here
    public conf: string = '';                  // will be loaded into stryker.conf.js
    public async prompt(): Promise<void> { };  // will be called after selecting this preset
}

Add your preset to StrykerPresets.ts like so:

const strykerPresets: PresetOption[] = [
        {name: 'angular', create() { return new AngularPreset(); } },
        {name: 'react', create() { return new ReactPreset(); } },
        {name: 'vueJs', create() { return new VueJsPreset(); } },
        {name: 'myPreset', create() { return new MyPreset(); } } // <- add this line
        // Add new presets here
];

Done. 👍

Shortcomings

  • Has only been fully tested on a small Angular project (I had nothing else at hand... )
  • No integration tests yet
  • (nitpick) When using angular, the tests should be run using npx stryker run instead of stryker run, but when the angular preset is chosen the user is still told to use stryker run.

@ghost ghost added the 🔎 Needs review label Nov 22, 2018
Copy link
Member

@nicojs nicojs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great already! Thanks for picking this up! I've got a few remarks, but mostly small ones.

I like the idea of the different StrykerPreset classes being able to ask additional questions. It is a clean way to separate them from the StrykerInitializer class itself.

I would like to have a unit tests for the different Stryker initializers themselves. Can be small, just asserting a part of the configuration. If you want, I can help with that.

@@ -47,7 +47,6 @@
"devDependencies": {
"@babel/types": "~7.1.2",
"@types/babel__generator": "^7.0.0",
"@types/babel__parser": "^7.0.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this in the PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to do with this PR (#1243) and also (#1247). These changes were needed in order to make 'npm install' work and they accidentally ended up in this PR too.
I merged stryker/master into my fork branch, I think that fixes it.

package.json Outdated
@@ -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",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this in the PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above comment

@@ -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",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove these dependencies? We're planning to run Stryker on Stryker in the build in Februari

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll remove then, yes. These were accidentally generated while testing stryker init.

await this.fetchAdditionalConfig(npmDependencies));
this.installNpmDependencies(npmDependencies, selectedPackageManager);
const selectedPreset = await this.selectPreset();
if (selectedPreset) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we introduce a function here in the then branch?

this.installNpmDependencies(selectedPresetInstance.dependencies, selectedPackageManager);
}
else {
const selectedTestRunner = await this.selectTestRunner();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idem for this else branch

import PresetOption from './PresetOption';

const strykerPresets: PresetOption[] = [
{name: 'angular', create() { return new AngularPreset(); } },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we remove this second layer? Seems complicated. Couldn't we create an array of StrykerPreset classes here?

export default const strykerPresets = [ new AngularPreset(), new ReactPreset(), new VueJsPreset() ];

We could add the name property to the StrykerPreset class as an abstract property.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea was to preserve memory by only instantiating a preset once it's needed. Hence the arrow functions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand, but you'll save a couple of bytes at most. I would rather have a piece of code that is better to understand and easier to maintain.

Ever heard of the phrase:

Premature optimization is the root of all evil

Copy link
Contributor Author

@Wmaarts Wmaarts Nov 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but you'll save a couple of bytes at most.

Fair point. 😅
I implemented your suggestion here.

'stryker-typescript',
'stryker-html-reporter'
];
public conf = `{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a reference to this conf object to keep it in sync with the handbook?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same goes for the other presets

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A better idea is to add a link to the specific handbook article at the top of the configuration file (in comments). That way, people know where to find more information.

@@ -0,0 +1,9 @@
abstract class StrykerPreset {
public abstract dependencies: string[];
public abstract conf: string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like this api. There is an implicit dependency between the prompt() method and the conf and dependencies properties. As in, you need to call prompt before you can read the properties. Can we make the configuration and dependencies a return value of prompt. Maybe we shouldn't call it prompt anymore, because it does more. Maybe call it configure? I'm open to suggestions.

@@ -0,0 +1,6 @@
import StrykerPreset from '../../src/initializer/presets/StrykerPreset';

export class StrykerPresetMock extends StrykerPreset {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this class reside in the StrykerInitializerSpec? Seems that it is only used there. I don't want helpers to get cluttered with mocks that are only used once.

@@ -103,6 +116,77 @@ describe('StrykerInitializer', () => {
expect(promptPackageManagers.choices).to.deep.eq(['npm', 'yarn']);
});

it('should immediately complete when a preset and package manager is chosen', async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great tests here! 👍

@Wmaarts
Copy link
Contributor Author

Wmaarts commented Nov 23, 2018

Pushed some changes that were suggested, but I'm not sure why the build is failing now.

@ghost ghost assigned nicojs Nov 27, 2018
@nicojs
Copy link
Member

nicojs commented Nov 27, 2018

@Wmaarts

I'm not sure why the build is failing now.

I don't know either. I've merged master and pushed to your branch. New build is running.

EDIT: found the issue. stryker-api was no longer a dev-dependency of stryker. As such, lerna decided to run the compilation of typescript code in parallel. This broke the build, as stryker needs the types of stryker-api.

@nicojs
Copy link
Member

nicojs commented Nov 27, 2018

I've committed a few more changes. Namely renamed some stuff, changed the abstract classes to interfaces where appropriate and added the handbook url to the outputted stryker.conf.js file.

@nicojs nicojs merged commit 5673e6b into stryker-mutator:master Nov 27, 2018
@ghost ghost removed the 🔎 Needs review label Nov 27, 2018
@nicojs
Copy link
Member

nicojs commented Nov 27, 2018

Thanks again for your help @Wmaarts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants