Skip to content

Commit

Permalink
feat(test-runner): Use new plugin system to load TestRunner plugins (#…
Browse files Browse the repository at this point in the history
…1361)

* stryker-jasmine-runner
* stryker-jest-runner
* stryker-karma-runner
* stryker-mocha-runner
* stryker-wct-runner

Set stryker-api as dependency instead of dev-dependency for all plugins.
  • Loading branch information
nicojs committed Feb 6, 2019
1 parent aaecb99 commit 266247b
Show file tree
Hide file tree
Showing 67 changed files with 643 additions and 750 deletions.
7 changes: 4 additions & 3 deletions packages/stryker-jasmine-runner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@
},
"homepage": "https://github.com/stryker-mutator/stryker/tree/master/packages/stryker-jasmine-runner#readme",
"peerDependencies": {
"jasmine": ">=2",
"stryker-api": ">=0.18.0 <0.24.0"
"jasmine": ">=2"
},
"devDependencies": {
"stryker-api": "^0.23.0",
"stryker-jasmine": "^0.11.0",
"@stryker-mutator/test-helpers": "0.0.0"
},
"dependencies": {
"stryker-api": "^0.23.0"
},
"initStrykerConfig": {
"jasmineConfigFile": "spec/support/jasmine.json"
},
Expand Down
13 changes: 7 additions & 6 deletions packages/stryker-jasmine-runner/src/JasmineTestRunner.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { EOL } from 'os';
import { TestRunner, RunResult, TestResult, RunStatus, RunnerOptions } from 'stryker-api/test_runner';
import { TestRunner, RunResult, TestResult, RunStatus } from 'stryker-api/test_runner';
import { Jasmine, toStrykerTestResult, evalGlobal } from './helpers';
import { tokens, commonTokens } from 'stryker-api/plugin';
import { StrykerOptions } from 'stryker-api/core';

export default class JasmineTestRunner implements TestRunner {

private readonly jasmineConfigFile: string | undefined;
private readonly fileNames: ReadonlyArray<string>;
private readonly Date: typeof Date = Date; // take Date prototype now we still can (user might choose to mock it away)

constructor(runnerOptions: RunnerOptions) {
this.jasmineConfigFile = runnerOptions.strykerOptions.jasmineConfigFile;
this.fileNames = runnerOptions.fileNames;
public static inject = tokens(commonTokens.sandboxFileNames, commonTokens.options);
constructor(private readonly fileNames: ReadonlyArray<string>, options: StrykerOptions) {
this.jasmineConfigFile = options.jasmineConfigFile;
}

public run(options: { testHooks?: string }): Promise<RunResult> {
Expand Down Expand Up @@ -46,7 +47,7 @@ export default class JasmineTestRunner implements TestRunner {
errorMessages: ['An error occurred while loading your jasmine specs' + EOL + (error.stack || error.message || error.toString())],
status: RunStatus.Error,
tests: []
}));
}));
}

private createJasmineRunner() {
Expand Down
7 changes: 4 additions & 3 deletions packages/stryker-jasmine-runner/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { TestRunnerFactory } from 'stryker-api/test_runner';

import JasmineTestRunner from './JasmineTestRunner';
import { declareClassPlugin, PluginKind } from 'stryker-api/plugin';

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

afterEach(() => {
sinon.reset();
testInjector.reset();
});
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,12 @@ describe('JasmineRunner integration', () => {

beforeEach(() => {
process.chdir(path.resolve(__dirname, '../../testResources/jasmine-init'));
sut = new JasmineTestRunner({
fileNames: [
sut = new JasmineTestRunner([
path.resolve('lib', 'jasmine_examples', 'Player.js'),
path.resolve('lib', 'jasmine_examples', 'Song.js'),
path.resolve('spec', 'helpers', 'jasmine_examples', 'SpecHelper.js'),
path.resolve('spec', 'jasmine_examples', 'PlayerSpec.js')
],
strykerOptions: factory.strykerOptions({ jasmineConfigFile: 'spec/support/jasmine.json' })
});
], factory.strykerOptions({ jasmineConfigFile: 'spec/support/jasmine.json' }));
});
it('should run the specs', async () => {
const runResult = await sut.run({});
Expand Down Expand Up @@ -122,11 +119,7 @@ describe('JasmineRunner integration', () => {

beforeEach(() => {
process.chdir(path.resolve(__dirname, '../../testResources/errors'));
sut = new JasmineTestRunner({
fileNames: [path.resolve('lib', 'error.js'),
path.resolve('spec', 'errorSpec.js')
], strykerOptions: factory.strykerOptions()
});
sut = new JasmineTestRunner([path.resolve('lib', 'error.js'), path.resolve('spec', 'errorSpec.js')], factory.strykerOptions());
});

it('should be able to tell the error', async () => {
Expand All @@ -143,12 +136,10 @@ describe('JasmineRunner integration', () => {
describe('when it includes failed tests', () => {
beforeEach(() => {
process.chdir(path.resolve(__dirname, '../../testResources/test-failures'));
sut = new JasmineTestRunner({
fileNames: [
sut = new JasmineTestRunner([
path.resolve('lib', 'foo.js'),
path.resolve('spec', 'fooSpec.js')
], strykerOptions: factory.strykerOptions()
});
], factory.strykerOptions());
});

it('should complete with one test failure', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe('JasmineTestRunner', () => {
sandbox.stub(helpers, 'Jasmine').returns(jasmineStub);
fileNames = ['foo.js', 'bar.js'];
clock = sandbox.useFakeTimers();
sut = new JasmineTestRunner({ fileNames, strykerOptions: factory.strykerOptions({ jasmineConfigFile: 'jasmineConfFile' }) });
sut = new JasmineTestRunner(fileNames, factory.strykerOptions({ jasmineConfigFile: 'jasmineConfFile' }));
});

afterEach(() => {
Expand Down
5 changes: 2 additions & 3 deletions packages/stryker-jasmine/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,9 @@
},
"homepage": "https://github.com/stryker-mutator/stryker/tree/master/packages/stryker-jasmine#readme",
"peerDependencies": {
"jasmine-core": ">=2",
"stryker-api": ">=0.18.0 <0.24.0"
"jasmine-core": ">=2"
},
"devDependencies": {
"dependencies": {
"stryker-api": "^0.23.0"
},
"contributors": []
Expand Down
25 changes: 12 additions & 13 deletions packages/stryker-jest-runner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,16 @@
"react": "~16.7.0",
"react-dom": "~16.7.0",
"react-scripts": "~2.1.0",
"react-scripts-ts": "~3.1.0",
"react-scripts-ts": "~3.1.0"
},
"peerDependencies": {
"jest": ">= 22.0.0"
},
"dependencies": {
"semver": "~5.6.0",
"stryker-api": "^0.23.0"
},
"peerDependencies": {
"jest": ">= 22.0.0",
"stryker-api": ">=0.18.0 <0.24.0"
},
"dependencies": {
"semver": "~5.6.0"
},
"initStrykerConfig": {
"coverageAnalysis": "off"
}
}
},
"initStrykerConfig": {
"coverageAnalysis": "off"
}
}
41 changes: 25 additions & 16 deletions packages/stryker-jest-runner/src/JestTestRunner.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
import { getLogger } from 'stryker-api/logging';
import { RunnerOptions, RunResult, TestRunner, RunStatus, TestResult, TestStatus, RunOptions } from 'stryker-api/test_runner';
import { Logger } from 'stryker-api/logging';
import { RunResult, TestRunner, RunStatus, TestResult, TestStatus, RunOptions } from 'stryker-api/test_runner';
import * as jest from 'jest';
import JestTestAdapterFactory from './jestTestAdapters/JestTestAdapterFactory';
import { jestTestAdapterFactory, JEST_VERSION_TOKEN } from './jestTestAdapters';
import { tokens, commonTokens, Injector, OptionsContext } from 'stryker-api/plugin';
import { StrykerOptions } from 'stryker-api/core';
import JestTestAdapter from './jestTestAdapters/JestTestAdapter';

export function jestTestRunnerFactory(injector: Injector<OptionsContext>) {
return injector
.provideValue(PROCESS_ENV_TOKEN, process.env)
.provideValue(JEST_VERSION_TOKEN, require('jest/package.json').version as string)
.provideFactory(JEST_TEST_ADAPTER_TOKEN, jestTestAdapterFactory)
.injectClass(JestTestRunner);
}
jestTestRunnerFactory.inject = tokens(commonTokens.injector);

export const PROCESS_ENV_TOKEN = 'PROCESS_ENV_TOKEN';
export const JEST_TEST_ADAPTER_TOKEN = 'jestTestAdapter';

export default class JestTestRunner implements TestRunner {
private readonly log = getLogger(JestTestRunner.name);
private readonly jestConfig: jest.Configuration;
private readonly processEnvRef: NodeJS.ProcessEnv;
private readonly enableFindRelatedTests: boolean;

public constructor(options: RunnerOptions, processEnvRef?: NodeJS.ProcessEnv) {
// Make sure process can be mocked by tests by passing it in the constructor
this.processEnvRef = processEnvRef || /* istanbul ignore next */ process.env;
private readonly enableFindRelatedTests: boolean;

public static inject = tokens(commonTokens.logger, commonTokens.options, PROCESS_ENV_TOKEN, JEST_TEST_ADAPTER_TOKEN);
public constructor(private readonly log: Logger, options: StrykerOptions, private readonly processEnvRef: NodeJS.ProcessEnv, private readonly jestTestAdapter: JestTestAdapter) {
// Get jest configuration from stryker options and assign it to jestConfig
this.jestConfig = options.strykerOptions.jest.config;
this.jestConfig = options.jest.config;

// Get enableFindRelatedTests from stryker jest options or default to true
this.enableFindRelatedTests = options.strykerOptions.jest.enableFindRelatedTests;
this.enableFindRelatedTests = options.jest.enableFindRelatedTests;
if (this.enableFindRelatedTests === undefined) {
this.enableFindRelatedTests = true;
}
Expand All @@ -31,16 +43,13 @@ export default class JestTestRunner implements TestRunner {
// basePath will be used in future releases of Stryker as a way to define the project root
// Default to process.cwd when basePath is not set for now, should be removed when issue is solved
// https://github.com/stryker-mutator/stryker/issues/650
this.jestConfig.rootDir = options.strykerOptions.basePath || process.cwd();
this.jestConfig.rootDir = options.basePath || process.cwd();
this.log.debug(`Project root is ${this.jestConfig.rootDir}`);
}

public async run(options: RunOptions): Promise<RunResult> {
this.setNodeEnv();

const jestTestRunner = JestTestAdapterFactory.getJestTestAdapter();

const { results } = await jestTestRunner.run(this.jestConfig, process.cwd(), this.enableFindRelatedTests ? options.mutatedFileName : undefined);
const { results } = await this.jestTestAdapter.run(this.jestConfig, process.cwd(), this.enableFindRelatedTests ? options.mutatedFileName : undefined);

// Get the non-empty errorMessages from the jest RunResult, it's safe to cast to Array<string> here because we filter the empty error messages
const errorMessages = results.testResults.map((testSuite: jest.TestResult) => testSuite.failureMessage).filter(errorMessage => (errorMessage)) as string[];
Expand Down
9 changes: 4 additions & 5 deletions packages/stryker-jest-runner/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { PluginKind, declareClassPlugin } from 'stryker-api/plugin';
import { TestRunnerFactory } from 'stryker-api/test_runner';
import { PluginKind, declareClassPlugin, declareFactoryPlugin } from 'stryker-api/plugin';
import JestConfigEditor from './JestConfigEditor';
import JestTestRunner from './JestTestRunner';
import { jestTestRunnerFactory } from './JestTestRunner';

process.env.BABEL_ENV = 'test';

export const strykerPlugins = [
declareClassPlugin(PluginKind.ConfigEditor, 'jest', JestConfigEditor)
declareClassPlugin(PluginKind.ConfigEditor, 'jest', JestConfigEditor),
declareFactoryPlugin(PluginKind.TestRunner, 'jest', jestTestRunnerFactory)
];
TestRunnerFactory.instance().register('jest', JestTestRunner);
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import JestTestAdapter from './JestTestAdapter';
import { getLogger } from 'stryker-api/logging';
import { Logger } from 'stryker-api/logging';
import { Configuration, runCLI, RunResult } from 'jest';
import { tokens, commonTokens } from 'stryker-api/plugin';

export default class JestPromiseTestAdapter implements JestTestAdapter {
private readonly log = getLogger(JestPromiseTestAdapter.name);

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

public run(jestConfig: Configuration, projectRoot: string, fileNameUnderTest?: string): Promise<RunResult> {
jestConfig.reporters = [];
Expand Down

This file was deleted.

21 changes: 21 additions & 0 deletions packages/stryker-jest-runner/src/jestTestAdapters/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import JestTestAdapter from './JestTestAdapter';
import JestPromiseAdapter from './JestPromiseTestAdapter';
import * as semver from 'semver';
import { Injector, BaseContext, tokens, commonTokens} from 'stryker-api/plugin';
import { Logger } from 'stryker-api/logging';

export const JEST_VERSION_TOKEN = 'jestVersion';

export function jestTestAdapterFactory(log: Logger, jestVersion: string, injector: Injector<BaseContext>) {
if (semver.satisfies(jestVersion, '<22.0.0')) {
log.debug('Detected Jest below 22.0.0: %s', jestVersion);
throw new Error('You need Jest version >= 22.0.0 to use Stryker');
} else {
return injector.injectClass(JestPromiseAdapter);
}
}

jestTestAdapterFactory.inject = tokens(commonTokens.logger, JEST_VERSION_TOKEN, commonTokens.injector);
export {
JestTestAdapter
};
30 changes: 0 additions & 30 deletions packages/stryker-jest-runner/test/helpers/logMock.ts

This file was deleted.

0 comments on commit 266247b

Please sign in to comment.