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(config-editors): Remove side effects from all config editor plugins #1317

Merged
merged 55 commits into from
Feb 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
eb51b11
Add dependency injection idea
nicojs Dec 26, 2018
b5d16e8
Merge branch 'master' into 667-remove-side-effects
nicojs Dec 28, 2018
f000155
Merge branch 'master' into 667-remove-side-effects
nicojs Dec 29, 2018
9a532c7
Started with implementation of new pugin mechanism.
nicojs Dec 30, 2018
4137cde
Finish up the broadcast reporter migration
nicojs Dec 30, 2018
7842052
Let the logger be injected in the HtmlReporter
nicojs Dec 30, 2018
c265402
test(TestInjector): Implement and use TestInjector
nicojs Dec 31, 2018
f4cd5d8
Add the ability to inject values into a tree of dependencies.
nicojs Jan 1, 2019
50a01a6
Improve HTML reporter typings
nicojs Jan 2, 2019
9647484
Rename CorrespondingTypes
nicojs Jan 2, 2019
3bc1785
Refactor all the things.
nicojs Jan 7, 2019
22c1902
Test: Add unit tests for `StrykerError` and `errorToString`
nicojs Jan 7, 2019
48acafa
Test: Update tests for stryker-api
nicojs Jan 7, 2019
014a167
Add npmignore and npmrc to typed-inject
nicojs Jan 7, 2019
e5d230b
Rename - plugins -> Plugins part 1
nicojs Jan 7, 2019
4fac5ee
Rename - plugins -> Plugins part 2
nicojs Jan 7, 2019
a609213
Update plugin loader to load from correct node_modules directory
nicojs Jan 7, 2019
8f66a39
Add typed-inject to local install of e2e tests
nicojs Jan 7, 2019
46711ed
Add missing dependency to `polymer-project` e2e test
nicojs Jan 7, 2019
a781d7c
Add missing dependency to `jest-react` e2e test
nicojs Jan 7, 2019
b22365d
Remove stryker-api dependency from stryker-util
nicojs Jan 7, 2019
90f0fcb
docs(typed-inject): Add README
nicojs Jan 8, 2019
8be8358
docs(typed-inject): Add "magic tokens" section to readme
nicojs Jan 8, 2019
dfeb4b0
docs(typed-html): add gh markdown table
nicojs Jan 8, 2019
dabc80a
test(typed-inject): Add integration tests
nicojs Jan 8, 2019
f9dfc62
test(typed-inject): Add more unit and integration tests
nicojs Jan 8, 2019
bb1ff55
refactor(stryker-api): Rename stryker-api/di to stryker-api/plugin an…
nicojs Jan 9, 2019
ef8fb92
Merge branch 'master' into 667-remove-side-effects
nicojs Jan 9, 2019
a159897
refactor(html-reporter): Revert back html reporter in order to presen…
nicojs Jan 9, 2019
ce338cc
fix(plugin-loader): Fix dynamicly loading of deprecated plugins
nicojs Jan 9, 2019
266c1b6
feat(plugins): Make sure you can also create a plugin with a factory …
nicojs Jan 10, 2019
8222f5f
feat(plugins): Load all config editor plugins in the new way
nicojs Jan 10, 2019
38d3176
feat(plugins): Remove side effects from loading the jest config edito…
nicojs Jan 10, 2019
bbcda68
feat(mocha-runner): Remove side effects from MockConfigEditor plugin
nicojs Jan 10, 2019
a102dd4
feat(plugin): Remove side effects from typescript config editor
nicojs Jan 10, 2019
37a2e68
refactor(plugins): Move all stryker plugin stuff back to stryker-api
nicojs Jan 11, 2019
2dc8ed6
docs(api): Document the plugin api
nicojs Jan 11, 2019
416fadf
refactor: Change naming of plugin contexts and methods for declaring …
nicojs Jan 11, 2019
8e828b9
Merge branch '667-remove-side-effects' into 667-remove-side-effects-c…
nicojs Jan 11, 2019
41788db
Allign with the new naming in stryker-api
nicojs Jan 11, 2019
5aa28a2
Add missing dev dependency
nicojs Jan 11, 2019
940b28e
Add typed-inject as dev dep of mocha-runner to get access to it's types
nicojs Jan 11, 2019
028b8d4
Merge branch 'master' into 667-remove-side-effects-config-editors
nicojs Jan 16, 2019
74608ae
feat(di): Create a `PluginCreator` that can be reused for all plugin …
nicojs Jan 17, 2019
b1022f4
Merge branch '667-remove-side-effects' into 667-remove-side-effects-c…
nicojs Jan 17, 2019
524b670
Use plugin creator
nicojs Jan 17, 2019
6da9395
Fix typo in typed-inject readme (#1321)
hugo-vrijswijk Jan 17, 2019
c1ebd45
Merge branch '667-remove-side-effects' into 667-remove-side-effects-c…
nicojs Jan 17, 2019
8291cb1
Merge branch 'master' into 667-remove-side-effects-config-editors
nicojs Jan 23, 2019
7d79f10
s
nicojs Jan 23, 2019
2944875
Update `pluginCreator` -> `pluginCreatorConfigEditor`
nicojs Jan 23, 2019
f6365b7
Re-export the typed-inject API from stryker-api, so it can be used in…
nicojs Jan 23, 2019
c73c19c
Remove typed-inject as dependency from jest runner
nicojs Jan 23, 2019
39dcf73
Merge branch 'master' into 667-remove-side-effects-config-editors
nicojs Feb 5, 2019
5d1b70a
Fix mocha test after merge
nicojs Feb 5, 2019
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
5 changes: 5 additions & 0 deletions packages/stryker-api/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@ export * from './src/plugin/Contexts';
export * from './src/plugin/Plugins';
export * from './src/plugin/PluginKind';
export * from './src/plugin/tokens';
export * from 'typed-inject/src/api/Injectable';
export * from 'typed-inject/src/api/Injector';
export * from 'typed-inject/src/api/InjectionToken';
export * from 'typed-inject/src/api/CorrespondingType';
export * from 'typed-inject/src/api/Scope';
3 changes: 2 additions & 1 deletion packages/stryker-api/src/plugin/Plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { TestRunner } from '../../test_runner';
import { Reporter } from '../../report';
import { Mutator } from '../../mutant';
import { Transpiler } from '../../transpile';
import { PluginContexts } from './Contexts';
import { ConfigEditor } from '../../config';
import { PluginContexts } from './Contexts';
import { InjectionToken, InjectableClass, InjectableFunction } from 'typed-inject';
import { PluginKind } from './PluginKind';

Expand Down Expand Up @@ -89,4 +89,5 @@ export type Plugins = {
*/
export interface PluginResolver {
resolve<T extends keyof Plugins>(kind: T, name: string): Plugins[T];
resolveAll<T extends keyof Plugins>(kind: T): Plugins[T][];
}
1 change: 1 addition & 0 deletions packages/stryker-jest-runner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
},
"homepage": "https://github.com/stryker-mutator/stryker/tree/master/packages/stryker-jest-runner#readme",
"devDependencies": {
"@stryker-mutator/test-helpers": "0.0.0",
"@types/semver": "~5.5.0",
"jest": "~23.6.0",
"react": "~16.7.0",
Expand Down
9 changes: 5 additions & 4 deletions packages/stryker-jest-runner/src/JestConfigEditor.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import * as fs from 'fs';
import { Config, ConfigEditor } from 'stryker-api/config';
import JestConfigLoader from './configLoaders/JestConfigLoader';
import CustomJestConfigLoader from './configLoaders/CustomJestConfigLoader';
import ReactScriptsJestConfigLoader from './configLoaders/ReactScriptsJestConfigLoader';
import ReactScriptsTSJestConfigLoader from './configLoaders/ReactScriptsTSJestConfigLoader';
import JEST_OVERRIDE_OPTIONS from './jestOverrideOptions';
import { Configuration } from 'jest';
import { getLogger } from 'stryker-api/logging';
import { Logger } from 'stryker-api/logging';
import { tokens, commonTokens } from 'stryker-api/plugin';

const DEFAULT_PROJECT_NAME = 'custom';
const DEFAULT_PROJECT_NAME_DEPRECATED = 'default';

export default class JestConfigEditor implements ConfigEditor {

public log = getLogger(JestConfigEditor.name);
public static inject = tokens(commonTokens.logger);
constructor(private readonly log: Logger) { }

public edit(strykerConfig: Config): void {
// If there is no Jest property on the Stryker config create it
Expand Down Expand Up @@ -41,7 +42,7 @@ export default class JestConfigEditor implements ConfigEditor {
private getConfigLoader(projectType: string): JestConfigLoader {
switch (projectType.toLowerCase()) {
case DEFAULT_PROJECT_NAME:
return new CustomJestConfigLoader(process.cwd(), fs);
return new CustomJestConfigLoader(process.cwd());
case 'react':
return new ReactScriptsJestConfigLoader(process.cwd());
case 'react-ts':
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import JestConfigLoader from './JestConfigLoader';
import * as path from 'path';
import fs = require('fs');
import { Configuration } from 'jest';

/**
* The Default config loader will load the Jest configuration using the package.json in the package root
*/
export default class CustomJestConfigLoader implements JestConfigLoader {
private readonly _fs: any;
private readonly _projectRoot: string;
private readonly _loader: NodeRequire;

constructor(projectRoot: string, fs: any, loader?: NodeRequire) {
constructor(projectRoot: string, private readonly _loader: NodeRequireFunction = require) {
this._projectRoot = projectRoot;
this._fs = fs;
this._loader = loader || /* istanbul ignore next */ require;
}

public loadConfig(): Configuration {
const jestConfig = this.readConfigFromJestConfigFile() || this.readConfigFromPackageJson() || {};

return jestConfig;
}

Expand All @@ -30,7 +26,7 @@ export default class CustomJestConfigLoader implements JestConfigLoader {

private readConfigFromPackageJson() {
try {
return JSON.parse(this._fs.readFileSync(path.join(this._projectRoot, 'package.json'), 'utf8')).jest;
return JSON.parse(fs.readFileSync(path.join(this._projectRoot, 'package.json'), 'utf8')).jest;
} catch { /* Don't return anything (implicitly return undefined) */ }
}
}
6 changes: 4 additions & 2 deletions packages/stryker-jest-runner/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { ConfigEditorFactory } from 'stryker-api/config';
import { PluginKind, declareClassPlugin } from 'stryker-api/plugin';
import { TestRunnerFactory } from 'stryker-api/test_runner';
import JestConfigEditor from './JestConfigEditor';
import JestTestRunner from './JestTestRunner';

process.env.BABEL_ENV = 'test';

ConfigEditorFactory.instance().register('jest', JestConfigEditor);
export const strykerPlugins = [
declareClassPlugin(PluginKind.ConfigEditor, 'jest', JestConfigEditor)
];
TestRunnerFactory.instance().register('jest', JestTestRunner);
2 changes: 2 additions & 0 deletions packages/stryker-jest-runner/test/helpers/initSinon.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as sinon from 'sinon';
import { testInjector } from '@stryker-mutator/test-helpers';

afterEach(() => {
sinon.restore();
testInjector.reset();
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Config } from 'stryker-api/config';
import { expect } from 'chai';
import * as sinon from 'sinon';
import * as path from 'path';
import { testInjector } from '@stryker-mutator/test-helpers';

describe('Integration test for Jest ConfigEditor', () => {
let jestConfigEditor: JestConfigEditor;
Expand All @@ -15,7 +16,7 @@ describe('Integration test for Jest ConfigEditor', () => {
getProjectRootStub = sinon.stub(process, 'cwd');
getProjectRootStub.returns(projectRoot);

jestConfigEditor = new JestConfigEditor();
jestConfigEditor = testInjector.injector.injectClass(JestConfigEditor);

config = new Config();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ paths.appTsTestConfig = require.resolve('../../testResources/reactTsProject/tsco

import JestConfigEditor from '../../src/JestConfigEditor';
import JestTestRunner from '../../src/JestTestRunner';
import { testInjector } from '@stryker-mutator/test-helpers';

// Get the actual project root, since we will stub process.cwd later on
const jestProjectRoot = process.cwd();
Expand Down Expand Up @@ -39,7 +40,7 @@ describe('Integration test for Strykers Jest runner', () => {
beforeEach(() => {
processCwdStub = sinon.stub(process, 'cwd');

jestConfigEditor = new JestConfigEditor();
jestConfigEditor = testInjector.injector.injectClass(JestConfigEditor);

runnerOptions = {
fileNames: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import CustomJestConfigLoader, * as defaultJestConfigLoader from '../../src/conf
import ReactScriptsJestConfigLoader, * as reactScriptsJestConfigLoader from '../../src/configLoaders/ReactScriptsJestConfigLoader';
import ReactScriptsTSJestConfigLoader, * as reactScriptsTSJestConfigLoader from '../../src/configLoaders/ReactScriptsTSJestConfigLoader';
import { Configuration } from 'jest';
import currentLogMock from '../helpers/logMock';
import { testInjector } from '@stryker-mutator/test-helpers';

describe('JestConfigEditor', () => {
describe.only('JestConfigEditor', () => {
let sut: JestConfigEditor;
let customConfigLoaderStub: ConfigLoaderStub;
let reactScriptsJestConfigLoaderStub: ConfigLoaderStub;
Expand All @@ -29,7 +29,7 @@ describe('JestConfigEditor', () => {
reactScriptsJestConfigLoaderStub.loadConfig.returns(defaultOptions);
reactScriptsTSJestConfigLoaderStub.loadConfig.returns(defaultOptions);

sut = new JestConfigEditor();
sut = testInjector.injector.injectClass(JestConfigEditor);
config = new Config();
});

Expand Down Expand Up @@ -78,14 +78,14 @@ describe('JestConfigEditor', () => {
const projectType = 'custom';
config.jest = { project: projectType };
sut.edit(config);
expect(currentLogMock().warn).calledWith('DEPRECATED: `jest.project` is renamed to `jest.projectType`. Please change it in your stryker configuration.');
expect(testInjector.logger.warn).calledWith('DEPRECATED: `jest.project` is renamed to `jest.projectType`. Please change it in your stryker configuration.');
expect(config.jest.projectType).eq(projectType);
});

it('should warn when using deprecated "default" project type', () => {
config.jest = { projectType: 'default' };
sut.edit(config);
expect(currentLogMock().warn).calledWith('DEPRECATED: The \'default\' `jest.projectType` is renamed to \'custom\'. Please rename it in your stryker configuration.');
expect(testInjector.logger.warn).calledWith('DEPRECATED: The \'default\' `jest.projectType` is renamed to \'custom\'. Please rename it in your stryker configuration.');
expect(config.jest.projectType).eq('custom');
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,26 @@ import { expect, assert } from 'chai';
import * as path from 'path';
import * as fs from 'fs';

const fakeRequire: any = {
require: () => { }
};

describe(CustomJestConfigLoader.name, () => {
let defaultConfigLoader: CustomJestConfigLoader;
const projectRoot: string = '/path/to/project/root';
const fsStub: FsStub = {};
let readFileSyncStub: sinon.SinonStub;
let requireStub: sinon.SinonStub;

beforeEach(() => {
fsStub.readFileSync = sinon.stub(fs, 'readFileSync');
requireStub = sinon.stub(fakeRequire, 'require');
readFileSyncStub = sinon.stub(fs, 'readFileSync');
requireStub = sinon.stub();

fsStub.readFileSync.returns('{ "jest": { "exampleProperty": "examplePackageJsonValue" }}');
readFileSyncStub.returns('{ "jest": { "exampleProperty": "examplePackageJsonValue" }}');
requireStub.returns({ exampleProperty: 'exampleJestConfigValue' });

defaultConfigLoader = new CustomJestConfigLoader(projectRoot, fs, fakeRequire.require);
defaultConfigLoader = new CustomJestConfigLoader(projectRoot, requireStub);
});

it('should load the Jest configuration from the jest.config.js in the project root', () => {
const config = defaultConfigLoader.loadConfig();

assert(requireStub.calledWith(path.join(projectRoot, 'jest.config.js')), `loader not called with ${projectRoot}/jest.config.js`);
expect(requireStub).calledWith(path.join(projectRoot, 'jest.config.js'));
expect(config).to.deep.equal({
exampleProperty: 'exampleJestConfigValue'
});
Expand All @@ -37,20 +33,16 @@ describe(CustomJestConfigLoader.name, () => {
requireStub.throws(Error('ENOENT: no such file or directory, open package.json'));
const config = defaultConfigLoader.loadConfig();

assert(fsStub.readFileSync.calledWith(path.join(projectRoot, 'package.json'), 'utf8'), `readFileSync not called with ${projectRoot}/package.json`);
assert(readFileSyncStub.calledWith(path.join(projectRoot, 'package.json'), 'utf8'), `readFileSync not called with ${projectRoot}/package.json`);
expect(config).to.deep.equal({
exampleProperty: 'examplePackageJsonValue'
});
});

it('should load the default Jest configuration if there is no package.json config or jest.config.js', () => {
requireStub.throws(Error('ENOENT: no such file or directory, open package.json'));
fsStub.readFileSync.returns('{ }'); // note, no `jest` key here!
readFileSyncStub.returns('{ }'); // note, no `jest` key here!
const config = defaultConfigLoader.loadConfig();
expect(config).to.deep.equal({});
});
});

interface FsStub {
[key: string]: sinon.SinonStub;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import { expect, assert } from 'chai';
import * as path from 'path';
import * as fs from 'fs';

const fakeRequire: any = {
require: () => { }
};

describe(`${CustomJestConfigLoader.name} integration`, () => {
let sut: CustomJestConfigLoader;
const projectRoot: string = '/path/to/project/root';
Expand All @@ -16,12 +12,12 @@ describe(`${CustomJestConfigLoader.name} integration`, () => {

beforeEach(() => {
fsStub.readFileSync = sinon.stub(fs, 'readFileSync');
requireStub = sinon.stub(fakeRequire, 'require');
requireStub = sinon.stub();

fsStub.readFileSync.returns('{ "jest": { "exampleProperty": "examplePackageJsonValue" }}');
requireStub.returns({ exampleProperty: 'exampleJestConfigValue' });

sut = new CustomJestConfigLoader(projectRoot, fs, fakeRequire.require);
sut = new CustomJestConfigLoader(projectRoot, requireStub);
});

it('should load the Jest configuration from the jest.config.js in the projectroot', () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/stryker-jest-runner/tsconfig.test.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
"references": [
{
"path": "./tsconfig.src.json"
},
{
"path": "../stryker-test-helpers/tsconfig.src.json"
}
]
}
4 changes: 2 additions & 2 deletions packages/stryker-mocha-runner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@
"tslib": "~1.9.3"
},
"devDependencies": {
"@stryker-mutator/test-helpers": "0.0.0",
"@types/multimatch": "~2.1.2",
"stryker-api": "^0.23.0",
"stryker-mocha-framework": "^0.14.0",
"@stryker-mutator/test-helpers": "0.0.0"
"stryker-mocha-framework": "^0.14.0"
},
"peerDependencies": {
"mocha": ">= 2.3.3 < 6",
Expand Down
7 changes: 6 additions & 1 deletion packages/stryker-mocha-runner/src/MochaConfigEditor.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { ConfigEditor, Config } from 'stryker-api/config';
import { mochaOptionsKey } from './MochaRunnerOptions';
import MochaOptionsLoader from './MochaOptionsLoader';
import { tokens } from 'stryker-api/plugin';

export default class MochaConfigEditor implements ConfigEditor {

public static inject = tokens('loader');
constructor(private readonly loader: MochaOptionsLoader) {}

public edit(config: Config): void {
config[mochaOptionsKey] = new MochaOptionsLoader().load(config);
config[mochaOptionsKey] = this.loader.load(config);
}
}
7 changes: 5 additions & 2 deletions packages/stryker-mocha-runner/src/MochaOptionsLoader.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import * as path from 'path';
import * as fs from 'fs';
import { StrykerOptions } from 'stryker-api/core';
import { getLogger } from 'stryker-api/logging';
import MochaRunnerOptions, { mochaOptionsKey } from './MochaRunnerOptions';
import { tokens, commonTokens } from 'stryker-api/plugin';
import { Logger } from 'stryker-api/logging';

export default class MochaOptionsLoader {

private readonly log = getLogger(MochaOptionsLoader.name);
private readonly DEFAULT_MOCHA_OPTS = 'test/mocha.opts';

public static inject = tokens(commonTokens.logger);
constructor(private readonly log: Logger) { }

public load(config: StrykerOptions): MochaRunnerOptions {
const mochaOptions = Object.assign({}, config[mochaOptionsKey]) as MochaRunnerOptions;
return Object.assign(this.loadMochaOptsFile(mochaOptions.opts), mochaOptions);
Expand Down
15 changes: 13 additions & 2 deletions packages/stryker-mocha-runner/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { TestRunnerFactory } from 'stryker-api/test_runner';
import { ConfigEditorFactory } from 'stryker-api/config';
import { declareFactoryPlugin, PluginKind, BaseContext, tokens, commonTokens, Injector } from 'stryker-api/plugin';

import MochaTestRunner from './MochaTestRunner';
import MochaConfigEditor from './MochaConfigEditor';
import MochaOptionsLoader from './MochaOptionsLoader';

TestRunnerFactory.instance().register('mocha', MochaTestRunner);
ConfigEditorFactory.instance().register('mocha-runner', MochaConfigEditor);

export const strykerPlugins = [
declareFactoryPlugin(PluginKind.ConfigEditor, 'mocha-runner', mochaConfigEditorFactory)
];

mochaConfigEditorFactory.inject = tokens(commonTokens.injector);
function mochaConfigEditorFactory(injector: Injector<BaseContext>): MochaConfigEditor {
return injector
.provideClass('loader', MochaOptionsLoader)
.injectClass(MochaConfigEditor);
}
8 changes: 3 additions & 5 deletions packages/stryker-mocha-runner/test/helpers/initSinon.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import * as sinon from 'sinon';

beforeEach(() => {
global.sandbox = sinon.createSandbox();
});
import { testInjector } from '@stryker-mutator/test-helpers';

afterEach(() => {
global.sandbox.restore();
sinon.restore();
testInjector.reset();
});