Skip to content

Commit

Permalink
feat(dependency injection): Add dependency injection for plugins (#1313)
Browse files Browse the repository at this point in the history
This PR adds dependency injection to Stryker, in preparation of removing the side effects from the way we now load plugins. Instead of registering yourself to the correct factory (for example, by calling `ReporterFactory.instance().register`), it relies on plugins exporting a property called `strykerPlugins`. 

Just to be clear: this PR does fix not the issue reported in #667. It only adds the dependency injection mechanism to Stryker. We still need to do PR's for each package (can go relatively fast)

The content of this PR:

1. Add a dependency injection framework called `typed-inject` to the packages folder; A type safe dependency injection framework
1. Add a package `stryker-api/di` which contains dependency injection related interfaces to help plugins know what to (type-safely) inject
1. Add `typed-inject` as a dependency to the core Stryker package and use it. Reporter plugins are loaded via this new mechanism.
1. Alter the `PluginLoader` in order to enable dependency injection in all plugins. It is still backward compatible with the old way of loading plugins.
1. Add a package to contain generic test helpers called `@stryker-mutator/test-helpers`. This package contains a `testInjector` to help inject common stuff into injectables for testability purposes.
1. It updated the `BroadcastReporter` and all build-in reporters to now use the new dependency injection mechanism.
  • Loading branch information
nicojs committed Jan 23, 2019
1 parent ce7a410 commit f90cd56
Show file tree
Hide file tree
Showing 160 changed files with 2,562 additions and 703 deletions.
3 changes: 2 additions & 1 deletion e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@stryker-mutator/util": "../packages/stryker-util",
"stryker-typescript": "../packages/stryker-typescript",
"stryker-vue-mutator": "../packages/stryker-vue-mutator",
"stryker-webpack-transpiler": "../packages/stryker-webpack-transpiler"
"stryker-webpack-transpiler": "../packages/stryker-webpack-transpiler",
"typed-inject": "../packages/typed-inject"
}
}
28 changes: 21 additions & 7 deletions e2e/test/angular-project/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion e2e/test/angular-project/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"stryker": "../../../packages/stryker",
"stryker-karma-runner": "../../../packages/stryker-karma-runner",
"stryker-typescript": "../../../packages/stryker-typescript",
"@stryker-mutator/util": "../../../packages/stryker-util"
"@stryker-mutator/util": "../../../packages/stryker-util",
"typed-inject": "../../../packages/typed-inject"
}
}
3 changes: 2 additions & 1 deletion e2e/test/jest-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"stryker": "../../../packages/stryker",
"stryker-javascript-mutator": "../../../packages/stryker-javascript-mutator",
"stryker-jest-runner": "../../../packages/stryker-jest-runner",
"@stryker-mutator/util": "../../../packages/stryker-util"
"@stryker-mutator/util": "../../../packages/stryker-util",
"typed-inject": "../../../packages/typed-inject"
}
}
3 changes: 2 additions & 1 deletion e2e/test/polymer-project/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"stryker": "../../../packages/stryker",
"stryker-wct-runner": "../../../packages/stryker-wct-runner",
"stryker-javascript-mutator": "../../../packages/stryker-javascript-mutator",
"@stryker-mutator/util": "../../../packages/stryker-util"
"@stryker-mutator/util": "../../../packages/stryker-util",
"typed-inject": "../../../packages/typed-inject"
},
"scripts": {
"prepare": "install-local",
Expand Down
3 changes: 2 additions & 1 deletion e2e/test/vue-javascript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@
"stryker-javascript-mutator": "../../../packages/stryker-javascript-mutator",
"stryker-karma-runner": "../../../packages/stryker-karma-runner",
"stryker-vue-mutator": "../../../packages/stryker-vue-mutator",
"@stryker-mutator/util": "../../../packages/stryker-util"
"@stryker-mutator/util": "../../../packages/stryker-util",
"typed-inject": "../../../packages/typed-inject"
},
"engines": {
"node": ">= 6.0.0",
Expand Down
3 changes: 2 additions & 1 deletion packages/stryker-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"tslib": "~1.9.3"
},
"devDependencies": {
"surrial": "~0.1.1"
"surrial": "~0.1.1",
"typed-inject": "0.0.0"
}
}
4 changes: 4 additions & 0 deletions packages/stryker-api/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './src/plugin/Contexts';
export * from './src/plugin/Plugins';
export * from './src/plugin/PluginKind';
export * from './src/plugin/tokens';
2 changes: 1 addition & 1 deletion packages/stryker-api/src/config/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default class Config implements StrykerOptions {
};
public allowConsoleColors: boolean = true;

public set(newConfig: StrykerOptions) {
public set(newConfig: Partial<StrykerOptions>) {
if (newConfig) {
Object.keys(newConfig).forEach(key => {
if (typeof newConfig[key] !== 'undefined') {
Expand Down
32 changes: 16 additions & 16 deletions packages/stryker-api/src/core/StrykerOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface StrykerOptions {
/**
* A list of globbing expression used for selecting the files that should be mutated.
*/
mutate?: string[];
mutate: string[];

/**
* With `files` you can choose which files should be included in your test runner sandbox.
Expand All @@ -32,7 +32,7 @@ interface StrykerOptions {
* all the CPU cores of your machine. Default: infinity, Stryker will decide for you and tries to use
* all CPUs in your machine optimally.
*/
maxConcurrentTestRunners?: number;
maxConcurrentTestRunners: number;

/**
* A location to a config file. That file should export a function which accepts a "config" object which it uses to configure stryker
Expand All @@ -45,9 +45,9 @@ interface StrykerOptions {
testFramework?: string;

/**
* The name of the test runner to use (default is the same name as the testFramework)
* The name of the test runner to use (default is 'command')
*/
testRunner?: string;
testRunner: string;

/**
* The mutant generator to use to generate mutants based on your input file.
Expand All @@ -61,7 +61,7 @@ interface StrykerOptions {
* * The `excludedMutations` property is mandatory and contains the names of the specific mutation types to exclude from testing.
* * The values must match the given names of the mutations. For example: 'BinaryExpression', 'BooleanSubstitution', etc.
*/
mutator?: string | MutatorDescriptor;
mutator: string | MutatorDescriptor;

/**
* The names of the transpilers to use (in order). Default: [].
Expand All @@ -80,12 +80,12 @@ interface StrykerOptions {
*
* Transpilers should ignore files marked with `transpiled = false`. See `files` array.
*/
transpilers?: string[];
transpilers: string[];

/**
* Thresholds for mutation score.
*/
thresholds?: Partial<MutationScoreThresholds>;
thresholds: MutationScoreThresholds;

/**
* Indicates which coverage analysis strategy to use.
Expand All @@ -95,7 +95,7 @@ interface StrykerOptions {
* 'all': Analyse the coverage for the entire test suite.
* 'off': Don't use coverage analysis
*/
coverageAnalysis?: 'perTest' | 'all' | 'off';
coverageAnalysis: 'perTest' | 'all' | 'off';

/**
* DEPRECATED PROPERTY. Please use the `reporters` property
Expand All @@ -106,24 +106,24 @@ interface StrykerOptions {
* Possible values: 'clear-text', 'progress'.
* Load more plugins to be able to use more reporters
*/
reporters?: string[];
reporters: string[];

/**
* The log level for logging to a file. If defined, stryker will output a log file called "stryker.log".
* Default: "off"
*/
fileLogLevel?: LogLevel;
fileLogLevel: LogLevel;

/**
* The log level for logging to the console. Default: "info".
*/
logLevel?: LogLevel;
logLevel: LogLevel;

/**
* Indicates whether or not to symlink the node_modules folder inside the sandbox folder(s).
* Default: true
*/
symlinkNodeModules?: boolean;
symlinkNodeModules: boolean;

/**
* DEPRECATED PROPERTY. Please use the `timeoutMS` property
Expand All @@ -132,17 +132,17 @@ interface StrykerOptions {
/**
* Amount of additional time, in milliseconds, the mutation test is allowed to run
*/
timeoutMS?: number;
timeoutMS: number;

/**
* The factor is applied on top of the other timeouts when during mutation testing
*/
timeoutFactor?: number;
timeoutFactor: number;

/**
* A list of plugins. These plugins will be imported ('required') by Stryker upon loading.
*/
plugins?: string[];
plugins: string[];

/**
* DEPRECATED
Expand All @@ -154,7 +154,7 @@ interface StrykerOptions {
* Indicates whether or not to use colors in console.
* Default: true
*/
allowConsoleColors?: boolean;
allowConsoleColors: boolean;
}

export default StrykerOptions;
53 changes: 53 additions & 0 deletions packages/stryker-api/src/plugin/Contexts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { LoggerFactoryMethod, Logger } from '../../logging';
import { StrykerOptions } from '../../core';
import { PluginResolver } from './Plugins';
import { Config } from '../../config';
import { PluginKind } from './PluginKind';
import { commonTokens } from './tokens';

/**
* The basic dependency injection context within Stryker
*/
export interface BaseContext {
[commonTokens.getLogger]: LoggerFactoryMethod;
[commonTokens.logger]: Logger;
[commonTokens.pluginResolver]: PluginResolver;
}

/**
* The dependency injection context for most of Stryker's plugins.
* Can inject basic stuff as well as the Stryker options
*/
export interface OptionsContext extends BaseContext {
[commonTokens.options]: StrykerOptions;
/**
* @deprecated This is just here to migrate between old and new plugins. Don't use this! Use `options` instead
*/
[commonTokens.config]: Config;
}

/**
* The dependency injection context for a `TranspilerPlugin`
*/
export interface TranspilerPluginContext extends OptionsContext {
[commonTokens.produceSourceMaps]: boolean;
}

/**
* The dependency injection context for a `TestRunnerPlugin`
*/
export interface TestRunnerPluginContext extends OptionsContext {
[commonTokens.sandboxFileNames]: ReadonlyArray<string>;
}

/**
* Lookup type for plugin contexts by kind.
*/
export interface PluginContexts {
[PluginKind.ConfigEditor]: BaseContext;
[PluginKind.Mutator]: OptionsContext;
[PluginKind.Reporter]: OptionsContext;
[PluginKind.TestFramework]: OptionsContext;
[PluginKind.TestRunner]: TestRunnerPluginContext;
[PluginKind.Transpiler]: TranspilerPluginContext;
}
11 changes: 11 additions & 0 deletions packages/stryker-api/src/plugin/PluginKind.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* The plugin kinds supported by Stryker
*/
export enum PluginKind {
ConfigEditor = 'ConfigEditor',
TestRunner = 'TestRunner',
TestFramework = 'TestFramework',
Transpiler = 'Transpiler',
Mutator = 'Mutator',
Reporter = 'Reporter'
}

0 comments on commit f90cd56

Please sign in to comment.