From 266247b2e55f2292bad334f7384218bd335f1d71 Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Wed, 6 Feb 2019 17:14:40 +0100 Subject: [PATCH] feat(test-runner): Use new plugin system to load TestRunner plugins (#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. --- packages/stryker-jasmine-runner/package.json | 7 +- .../src/JasmineTestRunner.ts | 13 ++-- packages/stryker-jasmine-runner/src/index.ts | 7 +- .../test/helpers/initSinon.ts | 7 ++ .../test/integration/JasmineRunner.it.ts | 19 ++--- .../test/unit/JasmineTestRunnerSpec.ts | 2 +- packages/stryker-jasmine/package.json | 5 +- packages/stryker-jest-runner/package.json | 25 +++---- .../stryker-jest-runner/src/JestTestRunner.ts | 41 ++++++---- packages/stryker-jest-runner/src/index.ts | 9 +-- .../JestPromiseTestAdapter.ts | 7 +- .../JestTestAdapterFactory.ts | 26 ------- .../src/jestTestAdapters/index.ts | 21 ++++++ .../test/helpers/logMock.ts | 30 -------- .../integration/StrykerJestRunner.it.spec.ts | 37 ++++----- .../test/unit/JestTestRunnerSpec.ts | 52 +++++-------- .../JestPromiseTestAdapterSpec.ts | 22 +++--- .../JestTestAdapterFactorySpec.ts | 64 +++++----------- packages/stryker-karma-runner/package.json | 5 +- .../src/KarmaTestRunner.ts | 23 +++--- packages/stryker-karma-runner/src/index.ts | 6 +- .../src/starters/stryker-karma.conf.ts | 47 ++++++++---- .../test/helpers/LoggerStub.ts | 33 -------- .../test/helpers/globals.ts | 6 -- .../test/helpers/initSinon.ts | 8 +- .../test/integration/KarmaTestRunner.it.ts | 64 ++++++++-------- .../test/integration/sample.it.ts | 7 +- .../test/unit/KarmaTestRunnerSpec.ts | 75 ++++++++++--------- .../test/unit/starters/angularStarterSpec.ts | 5 +- .../unit/starters/stryker-karma.confSpec.ts | 21 +++--- packages/stryker-mocha-framework/package.json | 7 +- packages/stryker-mocha-runner/package.json | 5 +- .../src/MochaTestRunner.ts | 20 +++-- packages/stryker-mocha-runner/src/index.ts | 8 +- .../test/helpers/globals.ts | 7 -- .../test/helpers/mockHelpers.ts | 18 ----- ...MochaTestFrameworkIntegrationTestWorker.ts | 24 +++--- .../test/integration/QUnitSampleSpec.ts | 45 ++++++----- .../test/integration/SampleProjectSpec.ts | 62 +++++++-------- .../test/unit/MochaTestRunnerSpec.ts | 43 ++++++----- .../stryker-test-helpers/src/TestInjector.ts | 9 +-- packages/stryker-wct-runner/package.json | 8 +- .../stryker-wct-runner/src/WctTestRunner.ts | 17 +++-- packages/stryker-wct-runner/src/index.ts | 6 +- .../test/integration/WctTestRunnerIT.ts | 23 +++--- .../test/unit/WctTestRunnerSpec.ts | 33 ++++---- .../stryker-wct-runner/tsconfig.test.json | 3 + .../stryker-webpack-transpiler/package.json | 1 - packages/stryker/package.json | 7 +- packages/stryker/src/Sandbox.ts | 16 ++-- packages/stryker/src/SandboxPool.ts | 22 ++++-- packages/stryker/src/Stryker.ts | 19 +++-- packages/stryker/src/di/coreTokens.ts | 2 + .../src/process/MutationTestExecutor.ts | 42 +++++------ .../ChildProcessTestRunnerDecorator.ts | 12 +-- .../ChildProcessTestRunnerWorker.ts | 15 ++-- .../src/test-runner/CommandTestRunner.ts | 7 +- .../test-runner/ResilientTestRunnerFactory.ts | 12 +-- .../src/test-runner/TestRunnerDecorator.ts | 2 +- .../CommandTestRunner.it.ts | 2 +- .../ResilientTestRunnerFactory.it.ts | 31 ++++---- packages/stryker/test/unit/SandboxPoolSpec.ts | 60 +++++++++------ packages/stryker/test/unit/SandboxSpec.ts | 8 +- packages/stryker/test/unit/StrykerSpec.ts | 8 +- .../unit/process/MutationTestExecutorSpec.ts | 72 +++++++----------- .../ChildProcessTestRunnerDecoratorSpec.ts | 21 +++--- .../unit/test-runner/CommandTestRunnerSpec.ts | 2 +- 67 files changed, 643 insertions(+), 750 deletions(-) create mode 100644 packages/stryker-jasmine-runner/test/helpers/initSinon.ts delete mode 100644 packages/stryker-jest-runner/src/jestTestAdapters/JestTestAdapterFactory.ts create mode 100644 packages/stryker-jest-runner/src/jestTestAdapters/index.ts delete mode 100644 packages/stryker-jest-runner/test/helpers/logMock.ts delete mode 100644 packages/stryker-karma-runner/test/helpers/LoggerStub.ts delete mode 100644 packages/stryker-karma-runner/test/helpers/globals.ts delete mode 100644 packages/stryker-mocha-runner/test/helpers/globals.ts diff --git a/packages/stryker-jasmine-runner/package.json b/packages/stryker-jasmine-runner/package.json index e16d90e739..5087c14724 100644 --- a/packages/stryker-jasmine-runner/package.json +++ b/packages/stryker-jasmine-runner/package.json @@ -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" }, diff --git a/packages/stryker-jasmine-runner/src/JasmineTestRunner.ts b/packages/stryker-jasmine-runner/src/JasmineTestRunner.ts index b82d3ac61b..38e335d6c2 100644 --- a/packages/stryker-jasmine-runner/src/JasmineTestRunner.ts +++ b/packages/stryker-jasmine-runner/src/JasmineTestRunner.ts @@ -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; 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, options: StrykerOptions) { + this.jasmineConfigFile = options.jasmineConfigFile; } public run(options: { testHooks?: string }): Promise { @@ -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() { diff --git a/packages/stryker-jasmine-runner/src/index.ts b/packages/stryker-jasmine-runner/src/index.ts index 046a942279..925bc60855 100644 --- a/packages/stryker-jasmine-runner/src/index.ts +++ b/packages/stryker-jasmine-runner/src/index.ts @@ -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) +]; diff --git a/packages/stryker-jasmine-runner/test/helpers/initSinon.ts b/packages/stryker-jasmine-runner/test/helpers/initSinon.ts new file mode 100644 index 0000000000..2e2167eed7 --- /dev/null +++ b/packages/stryker-jasmine-runner/test/helpers/initSinon.ts @@ -0,0 +1,7 @@ +import * as sinon from 'sinon'; +import { testInjector } from '@stryker-mutator/test-helpers'; + +afterEach(() => { + sinon.reset(); + testInjector.reset(); +}); diff --git a/packages/stryker-jasmine-runner/test/integration/JasmineRunner.it.ts b/packages/stryker-jasmine-runner/test/integration/JasmineRunner.it.ts index 8dbb42e6d4..62f64f3107 100644 --- a/packages/stryker-jasmine-runner/test/integration/JasmineRunner.it.ts +++ b/packages/stryker-jasmine-runner/test/integration/JasmineRunner.it.ts @@ -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({}); @@ -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 () => { @@ -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 () => { diff --git a/packages/stryker-jasmine-runner/test/unit/JasmineTestRunnerSpec.ts b/packages/stryker-jasmine-runner/test/unit/JasmineTestRunnerSpec.ts index 02d4aec9e0..08c98e5e19 100644 --- a/packages/stryker-jasmine-runner/test/unit/JasmineTestRunnerSpec.ts +++ b/packages/stryker-jasmine-runner/test/unit/JasmineTestRunnerSpec.ts @@ -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(() => { diff --git a/packages/stryker-jasmine/package.json b/packages/stryker-jasmine/package.json index 41bd390bef..89a9e6c1b5 100644 --- a/packages/stryker-jasmine/package.json +++ b/packages/stryker-jasmine/package.json @@ -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": [] diff --git a/packages/stryker-jest-runner/package.json b/packages/stryker-jest-runner/package.json index 25a0acb3a6..96988d101f 100644 --- a/packages/stryker-jest-runner/package.json +++ b/packages/stryker-jest-runner/package.json @@ -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" - } -} \ No newline at end of file + }, + "initStrykerConfig": { + "coverageAnalysis": "off" + } +} diff --git a/packages/stryker-jest-runner/src/JestTestRunner.ts b/packages/stryker-jest-runner/src/JestTestRunner.ts index 3a246e4c39..5ea5d68bc2 100644 --- a/packages/stryker-jest-runner/src/JestTestRunner.ts +++ b/packages/stryker-jest-runner/src/JestTestRunner.ts @@ -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) { + 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; } @@ -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 { 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 here because we filter the empty error messages const errorMessages = results.testResults.map((testSuite: jest.TestResult) => testSuite.failureMessage).filter(errorMessage => (errorMessage)) as string[]; diff --git a/packages/stryker-jest-runner/src/index.ts b/packages/stryker-jest-runner/src/index.ts index 1375036e10..849ed0c0ec 100644 --- a/packages/stryker-jest-runner/src/index.ts +++ b/packages/stryker-jest-runner/src/index.ts @@ -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); diff --git a/packages/stryker-jest-runner/src/jestTestAdapters/JestPromiseTestAdapter.ts b/packages/stryker-jest-runner/src/jestTestAdapters/JestPromiseTestAdapter.ts index a082b5ce5a..7c438d5d0e 100644 --- a/packages/stryker-jest-runner/src/jestTestAdapters/JestPromiseTestAdapter.ts +++ b/packages/stryker-jest-runner/src/jestTestAdapters/JestPromiseTestAdapter.ts @@ -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 { jestConfig.reporters = []; diff --git a/packages/stryker-jest-runner/src/jestTestAdapters/JestTestAdapterFactory.ts b/packages/stryker-jest-runner/src/jestTestAdapters/JestTestAdapterFactory.ts deleted file mode 100644 index 9d0a8a0a8b..0000000000 --- a/packages/stryker-jest-runner/src/jestTestAdapters/JestTestAdapterFactory.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { getLogger } from 'stryker-api/logging'; - -import JestTestAdapter from './JestTestAdapter'; -import JestPromiseAdapter from './JestPromiseTestAdapter'; -import * as semver from 'semver'; - -export default class JestTestAdapterFactory { - private static readonly log = getLogger(JestTestAdapterFactory.name); - - public static getJestTestAdapter(loader?: NodeRequire): JestTestAdapter { - const jestVersion = this.getJestVersion(loader || /* istanbul ignore next */ require); - - if (semver.satisfies(jestVersion, '<22.0.0')) { - JestTestAdapterFactory.log.debug('Detected Jest below 22.0.0'); - throw new Error('You need Jest version >= 22.0.0 to use Stryker'); - } else { - return new JestPromiseAdapter(); - } - } - - private static getJestVersion(loader: NodeRequire): string { - const packageJson = loader('jest/package.json'); - - return packageJson.version; - } -} diff --git a/packages/stryker-jest-runner/src/jestTestAdapters/index.ts b/packages/stryker-jest-runner/src/jestTestAdapters/index.ts new file mode 100644 index 0000000000..9f15363299 --- /dev/null +++ b/packages/stryker-jest-runner/src/jestTestAdapters/index.ts @@ -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) { + 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 +}; diff --git a/packages/stryker-jest-runner/test/helpers/logMock.ts b/packages/stryker-jest-runner/test/helpers/logMock.ts deleted file mode 100644 index a795a2ddd7..0000000000 --- a/packages/stryker-jest-runner/test/helpers/logMock.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as logging from 'stryker-api/logging'; -import * as sinon from 'sinon'; - -let log: sinon.SinonStubbedInstance; - -beforeEach(() => { - log = createLogger(); - sinon.stub(logging, 'getLogger').returns(log); -}); - -function createLogger(): sinon.SinonStubbedInstance { - return { - debug: sinon.stub(), - error: sinon.stub(), - fatal: sinon.stub(), - info: sinon.stub(), - isDebugEnabled: sinon.stub(), - isErrorEnabled: sinon.stub(), - isFatalEnabled: sinon.stub(), - isInfoEnabled: sinon.stub(), - isTraceEnabled: sinon.stub(), - isWarnEnabled: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub() - }; -} - -export default function currentLogMock() { - return log; -} diff --git a/packages/stryker-jest-runner/test/integration/StrykerJestRunner.it.spec.ts b/packages/stryker-jest-runner/test/integration/StrykerJestRunner.it.spec.ts index 6b3f45de8d..f2bb7573a6 100644 --- a/packages/stryker-jest-runner/test/integration/StrykerJestRunner.it.spec.ts +++ b/packages/stryker-jest-runner/test/integration/StrykerJestRunner.it.spec.ts @@ -1,5 +1,4 @@ -import { Config } from 'stryker-api/config'; -import { RunnerOptions, RunStatus, TestStatus, RunOptions } from 'stryker-api/test_runner'; +import { RunStatus, TestStatus, RunOptions } from 'stryker-api/test_runner'; import * as sinon from 'sinon'; import { expect } from 'chai'; import * as path from 'path'; @@ -9,8 +8,9 @@ const paths = require('react-scripts-ts/config/paths'); paths.appTsTestConfig = require.resolve('../../testResources/reactTsProject/tsconfig.test.json'); import JestConfigEditor from '../../src/JestConfigEditor'; -import JestTestRunner from '../../src/JestTestRunner'; +import { jestTestRunnerFactory } from '../../src/JestTestRunner'; import { testInjector } from '@stryker-mutator/test-helpers'; +import { commonTokens } from 'stryker-api/plugin'; // Get the actual project root, since we will stub process.cwd later on const jestProjectRoot = process.cwd(); @@ -20,9 +20,6 @@ process.env.BABEL_ENV = 'test'; describe('Integration test for Strykers Jest runner', () => { // Set timeout for integration tests to 10 seconds for travis - - let jestConfigEditor: JestConfigEditor; - let runnerOptions: RunnerOptions; let processCwdStub: sinon.SinonStub; const runOptions: RunOptions = { timeout: 0 }; @@ -39,21 +36,22 @@ describe('Integration test for Strykers Jest runner', () => { beforeEach(() => { processCwdStub = sinon.stub(process, 'cwd'); - - jestConfigEditor = testInjector.injector.injectClass(JestConfigEditor); - - runnerOptions = { - fileNames: [], - strykerOptions: new Config() - }; }); + function createSut() { + const jestConfigEditor = testInjector.injector.injectClass(JestConfigEditor); + const config = testInjector.injector.resolve(commonTokens.config); + jestConfigEditor.edit(config); + return testInjector.injector + .provideValue(commonTokens.options, config) + .injectFunction(jestTestRunnerFactory); + } + it('should run tests on the example React + TypeScript project', async () => { processCwdStub.returns(getProjectRoot('reactTsProject')); - runnerOptions.strykerOptions.set({ jest: { projectType: 'react-ts' } }); - jestConfigEditor.edit(runnerOptions.strykerOptions as Config); + testInjector.options.jest = { projectType: 'react-ts' }; - const jestTestRunner = new JestTestRunner(runnerOptions); + const jestTestRunner = createSut(); const result = await jestTestRunner.run(runOptions); expect(result.status).to.equal(RunStatus.Complete); @@ -68,9 +66,7 @@ describe('Integration test for Strykers Jest runner', () => { it('should run tests on the example custom project using package.json', async () => { processCwdStub.returns(getProjectRoot('exampleProject')); - - jestConfigEditor.edit(runnerOptions.strykerOptions as Config); - const jestTestRunner = new JestTestRunner(runnerOptions); + const jestTestRunner = createSut(); const result = await jestTestRunner.run(runOptions); @@ -91,8 +87,7 @@ describe('Integration test for Strykers Jest runner', () => { it('should run tests on the example custom project using jest.config.js', async () => { processCwdStub.returns(getProjectRoot('exampleProjectWithExplicitJestConfig')); - jestConfigEditor.edit(runnerOptions.strykerOptions as Config); - const jestTestRunner = new JestTestRunner(runnerOptions); + const jestTestRunner = createSut(); const result = await jestTestRunner.run(runOptions); diff --git a/packages/stryker-jest-runner/test/unit/JestTestRunnerSpec.ts b/packages/stryker-jest-runner/test/unit/JestTestRunnerSpec.ts index 5a63dd0b38..d0464bd76c 100644 --- a/packages/stryker-jest-runner/test/unit/JestTestRunnerSpec.ts +++ b/packages/stryker-jest-runner/test/unit/JestTestRunnerSpec.ts @@ -1,62 +1,48 @@ -import JestTestAdapterFactory from '../../src/jestTestAdapters/JestTestAdapterFactory'; -import JestTestRunner from '../../src/JestTestRunner'; -import { Config } from 'stryker-api/config'; +import JestTestRunner, { PROCESS_ENV_TOKEN, JEST_TEST_ADAPTER_TOKEN } from '../../src/JestTestRunner'; import * as fakeResults from '../helpers/testResultProducer'; import * as sinon from 'sinon'; -import { assert, expect } from 'chai'; +import { expect } from 'chai'; import { RunStatus, TestStatus, RunOptions } from 'stryker-api/test_runner'; -import currentLogMock from '../helpers/logMock'; +import { testInjector } from '@stryker-mutator/test-helpers'; +import { JestTestAdapter } from '../../src/jestTestAdapters'; describe('JestTestRunner', () => { const basePath = '/path/to/project/root'; const runOptions: RunOptions = { timeout: 0 }; - let jestTestAdapterFactoryStub: sinon.SinonStub; - let runJestStub: sinon.SinonStub; - let strykerOptions: Config; + let jestTestAdapterMock: sinon.SinonStubbedInstance; let jestTestRunner: JestTestRunner; let processEnvMock: NodeJS.ProcessEnv; beforeEach(() => { - runJestStub = sinon.stub(); - runJestStub.resolves({ results: { testResults: [] } }); + jestTestAdapterMock = { run: sinon.stub() }; + jestTestAdapterMock.run.resolves({ results: { testResults: [] } }); - strykerOptions = new Config(); - strykerOptions.set({ jest: { config: { property: 'value' } }, basePath }); + testInjector.options.jest = { config: { property: 'value' } }; + testInjector.options.basePath = basePath; processEnvMock = { NODE_ENV: undefined }; - jestTestRunner = new JestTestRunner({ - fileNames: [], - strykerOptions - }, processEnvMock); - - jestTestAdapterFactoryStub = sinon.stub(JestTestAdapterFactory, 'getJestTestAdapter'); - jestTestAdapterFactoryStub.returns({ - run: runJestStub - }); + jestTestRunner = testInjector.injector + .provideValue(PROCESS_ENV_TOKEN, processEnvMock) + .provideValue(JEST_TEST_ADAPTER_TOKEN, jestTestAdapterMock as unknown as JestTestAdapter) + .injectClass(JestTestRunner); }); it('should log the project root when constructing the JestTestRunner', () => { - assert(currentLogMock().debug.calledWith(`Project root is ${basePath}`)); - }); - - it('should call jestTestAdapterFactory "getJestTestAdapter" method to obtain a testRunner', async () => { - await jestTestRunner.run(runOptions); - - assert(jestTestAdapterFactoryStub.called); + expect(testInjector.logger.debug).calledWith(`Project root is ${basePath}`); }); it('should call the run function with the provided config and the projectRoot', async () => { await jestTestRunner.run(runOptions); - assert(runJestStub.called); + expect(jestTestAdapterMock.run).called; }); it('should call the jestTestRunner run method and return a correct runResult', async () => { - runJestStub.resolves({ results: fakeResults.createSuccessResult() }); + jestTestAdapterMock.run.resolves({ results: fakeResults.createSuccessResult() }); const result = await jestTestRunner.run(runOptions); @@ -75,7 +61,7 @@ describe('JestTestRunner', () => { }); it('should call the jestTestRunner run method and return a skipped runResult', async () => { - runJestStub.resolves({ results: fakeResults.createPendingResult() }); + jestTestAdapterMock.run.resolves({ results: fakeResults.createPendingResult() }); const result = await jestTestRunner.run(runOptions); @@ -94,7 +80,7 @@ describe('JestTestRunner', () => { }); it('should call the jestTestRunner run method and return a negative runResult', async () => { - runJestStub.resolves({ results: fakeResults.createFailResult() }); + jestTestAdapterMock.run.resolves({ results: fakeResults.createFailResult() }); const result = await jestTestRunner.run(runOptions); @@ -129,7 +115,7 @@ describe('JestTestRunner', () => { }); it('should return an error result when a runtime error occurs', async () => { - runJestStub.resolves({ results: { testResults: [], numRuntimeErrorTestSuites: 1 } }); + jestTestAdapterMock.run.resolves({ results: { testResults: [], numRuntimeErrorTestSuites: 1 } }); const result = await jestTestRunner.run(runOptions); diff --git a/packages/stryker-jest-runner/test/unit/jestTestAdapters/JestPromiseTestAdapterSpec.ts b/packages/stryker-jest-runner/test/unit/jestTestAdapters/JestPromiseTestAdapterSpec.ts index af24f3b418..28bc8fa394 100644 --- a/packages/stryker-jest-runner/test/unit/jestTestAdapters/JestPromiseTestAdapterSpec.ts +++ b/packages/stryker-jest-runner/test/unit/jestTestAdapters/JestPromiseTestAdapterSpec.ts @@ -2,10 +2,10 @@ import JestPromiseTestAdapter from '../../../src/jestTestAdapters/JestPromiseTes import * as sinon from 'sinon'; import { expect, assert } from 'chai'; import * as jest from 'jest'; -import currentLogMock from '../../helpers/logMock'; +import { testInjector } from '@stryker-mutator/test-helpers'; -describe('JestPromiseTestAdapter', () => { - let jestPromiseTestAdapter: JestPromiseTestAdapter; +describe(JestPromiseTestAdapter.name, () => { + let sut: JestPromiseTestAdapter; let runCLIStub: sinon.SinonStub; const projectRoot = '/path/to/project'; @@ -19,17 +19,17 @@ describe('JestPromiseTestAdapter', () => { result: 'testResult' })); - jestPromiseTestAdapter = new JestPromiseTestAdapter(); + sut = testInjector.injector.injectClass(JestPromiseTestAdapter); }); it('should set reporters to an empty array', async () => { - await jestPromiseTestAdapter.run(jestConfig, projectRoot); + await sut.run(jestConfig, projectRoot); expect(jestConfig.reporters).to.be.an('array').that.is.empty; }); it('should call the runCLI method with the correct projectRoot', async () => { - await jestPromiseTestAdapter.run(jestConfig, projectRoot); + await sut.run(jestConfig, projectRoot); assert(runCLIStub.calledWith({ config: JSON.stringify({ rootDir: projectRoot, reporters: [] }), @@ -39,7 +39,7 @@ describe('JestPromiseTestAdapter', () => { }); it('should call the runCLI method with the --findRelatedTests flag', async () => { - await jestPromiseTestAdapter.run(jestConfig, projectRoot, fileNameUnderTest); + await sut.run(jestConfig, projectRoot, fileNameUnderTest); assert(runCLIStub.calledWith({ _: [fileNameUnderTest], @@ -51,7 +51,7 @@ describe('JestPromiseTestAdapter', () => { }); it('should call the runCLI method and return the test result', async () => { - const result = await jestPromiseTestAdapter.run(jestConfig, projectRoot); + const result = await sut.run(jestConfig, projectRoot); expect(result).to.deep.equal({ config: { @@ -64,7 +64,7 @@ describe('JestPromiseTestAdapter', () => { }); it('should call the runCLI method and return the test result when run with --findRelatedTests flag', async () => { - const result = await jestPromiseTestAdapter.run(jestConfig, projectRoot, fileNameUnderTest); + const result = await sut.run(jestConfig, projectRoot, fileNameUnderTest); expect(result).to.deep.equal({ config: { @@ -79,11 +79,11 @@ describe('JestPromiseTestAdapter', () => { }); it('should trace log a message when jest is invoked', async () => { - await jestPromiseTestAdapter.run(jestConfig, projectRoot); + await sut.run(jestConfig, projectRoot); const expectedResult: any = JSON.parse(JSON.stringify(jestConfig)); expectedResult.reporters = []; - assert(currentLogMock().trace.calledWithMatch(/Invoking Jest with config\s.*/)); + expect(testInjector.logger.trace).calledWithMatch(/Invoking Jest with config\s.*/); }); }); diff --git a/packages/stryker-jest-runner/test/unit/jestTestAdapters/JestTestAdapterFactorySpec.ts b/packages/stryker-jest-runner/test/unit/jestTestAdapters/JestTestAdapterFactorySpec.ts index 26425a41c1..daa5e22ba5 100644 --- a/packages/stryker-jest-runner/test/unit/jestTestAdapters/JestTestAdapterFactorySpec.ts +++ b/packages/stryker-jest-runner/test/unit/jestTestAdapters/JestTestAdapterFactorySpec.ts @@ -1,59 +1,29 @@ -import JestTestAdapterFactory from '../../../src/jestTestAdapters/JestTestAdapterFactory'; -import JestPromiseTestAdapter, * as jestPromiseTestAdapter from '../../../src/jestTestAdapters/JestPromiseTestAdapter'; -import * as sinon from 'sinon'; -import { expect, assert } from 'chai'; +import { jestTestAdapterFactory, JEST_VERSION_TOKEN, JestTestAdapter } from '../../../src/jestTestAdapters'; +import JestPromiseTestAdapter from '../../../src/jestTestAdapters/JestPromiseTestAdapter'; +import { expect } from 'chai'; +import { testInjector } from '@stryker-mutator/test-helpers'; -const loader: any = { - require: () => {} -}; +describe(jestTestAdapterFactory.name, () => { + let jestVersion: string; -describe('JestTestAdapterFactory', () => { - let jestPromiseTestAdapterStub: TestAdapterStub; - let requireStub: sinon.SinonStub; - let debugLoggerStub: sinon.SinonStub; - - beforeEach(() => { - jestPromiseTestAdapterStub = sinon.createStubInstance(JestPromiseTestAdapter); - sinon.stub(jestPromiseTestAdapter, 'default').returns(jestPromiseTestAdapterStub); - requireStub = sinon.stub(loader, 'require'); - debugLoggerStub = (JestTestAdapterFactory as any).log.debug = sinon.stub(); - }); + function act(): JestTestAdapter { + return testInjector.injector + .provideValue(JEST_VERSION_TOKEN, jestVersion) + .injectFunction(jestTestAdapterFactory); + } it('should return a Promise-based adapter when the Jest version is higher or equal to 22.0.0', () => { - requireStub.returns({ version: '22.0.0' }); - - const testAdapter = JestTestAdapterFactory.getJestTestAdapter(loader.require); + jestVersion = '22.0.0'; + const testAdapter = act(); - expect(testAdapter).to.equal(jestPromiseTestAdapterStub); - }); - - it('should load the Jest package.json with require', () => { - requireStub.returns({ version: '22.0.0' }); - - JestTestAdapterFactory.getJestTestAdapter(loader.require); - - assert(requireStub.calledWith('jest/package.json'), 'require not called with "jest/package.json"'); + expect(testAdapter).instanceOf(JestPromiseTestAdapter); }); it('should throw an error when the Jest version is lower than 22.0.0', () => { - requireStub.returns({ version: '21.0.0' }); + jestVersion = '21.0.0'; - expect(() => JestTestAdapterFactory.getJestTestAdapter(loader.require)).to.throw(Error, 'You need Jest version >= 22.0.0 to use Stryker'); + expect(() => act()).to.throw(Error, 'You need Jest version >= 22.0.0 to use Stryker'); + expect(testInjector.logger.debug).calledWith('Detected Jest below 22.0.0: %s', jestVersion); }); - it('should log a debug message when a Jest verion below 22.0.0 is used', () => { - requireStub.returns({ version: '21.0.0' }); - - try { - JestTestAdapterFactory.getJestTestAdapter(loader.require); - - assert(false, 'We should never reach this part of the script'); - } catch { - assert(debugLoggerStub.calledWith('Detected Jest below 22.0.0')); - } - }); }); - -interface TestAdapterStub { - run: sinon.SinonStub; -} diff --git a/packages/stryker-karma-runner/package.json b/packages/stryker-karma-runner/package.json index 5f5161ef70..4d442e3957 100644 --- a/packages/stryker-karma-runner/package.json +++ b/packages/stryker-karma-runner/package.json @@ -28,9 +28,6 @@ "url": "https://github.com/stryker-mutator/stryker/issues" }, "homepage": "https://github.com/stryker-mutator/stryker/tree/master/packages/stryker-karma-runner#readme", - "peerDependencies": { - "stryker-api": ">=0.18.0 <0.24.0" - }, "devDependencies": { "@stryker-mutator/util": "0.0.3", "@stryker-mutator/test-helpers": "0.0.0", @@ -41,12 +38,12 @@ "karma": "~3.1.1", "karma-jasmine": "~2.0.0", "karma-phantomjs-launcher": "~1.0.4", - "stryker-api": "^0.23.0", "stryker-jasmine": "^0.11.0" }, "dependencies": { "decamelize": "^2.0.0", "semver": "~5.6.0", + "stryker-api": "^0.23.0", "tslib": "~1.9.3" }, "contributors": [ diff --git a/packages/stryker-karma-runner/src/KarmaTestRunner.ts b/packages/stryker-karma-runner/src/KarmaTestRunner.ts index d2e07e5a22..25be0ae8fd 100644 --- a/packages/stryker-karma-runner/src/KarmaTestRunner.ts +++ b/packages/stryker-karma-runner/src/KarmaTestRunner.ts @@ -1,18 +1,19 @@ -import { TestRunner, TestResult, RunStatus, RunResult, RunnerOptions, CoverageCollection, CoveragePerTestResult } from 'stryker-api/test_runner'; -import { getLogger } from 'stryker-api/logging'; +import { TestRunner, TestResult, RunStatus, RunResult, CoverageCollection, CoveragePerTestResult } from 'stryker-api/test_runner'; +import { Logger, LoggerFactoryMethod } from 'stryker-api/logging'; import * as karma from 'karma'; import StrykerKarmaSetup, { DEPRECATED_KARMA_CONFIG, DEPRECATED_KARMA_CONFIG_FILE, KARMA_CONFIG_KEY } from './StrykerKarmaSetup'; import TestHooksMiddleware from './TestHooksMiddleware'; import StrykerReporter from './StrykerReporter'; import strykerKarmaConf = require('./starters/stryker-karma.conf'); import ProjectStarter from './starters/ProjectStarter'; +import { StrykerOptions } from 'stryker-api/core'; +import { tokens, commonTokens } from 'stryker-api/plugin'; export interface ConfigOptions extends karma.ConfigOptions { detached?: boolean; } export default class KarmaTestRunner implements TestRunner { - private readonly log = getLogger(KarmaTestRunner.name); private currentTestResults: TestResult[]; private currentErrorMessages: string[]; private currentCoverageReport?: CoverageCollection | CoveragePerTestResult; @@ -21,14 +22,15 @@ export default class KarmaTestRunner implements TestRunner { private readonly starter: ProjectStarter; public port: undefined | number; - constructor(options: RunnerOptions) { + public static inject = tokens(commonTokens.logger, commonTokens.getLogger, commonTokens.options); + constructor(private readonly log: Logger, getLogger: LoggerFactoryMethod, options: StrykerOptions) { const setup = this.loadSetup(options); if (setup.project !== undefined) { setup.projectType = setup.project; this.log.warn('DEPRECATED: `karma.project` is renamed to `karma.projectType`. Please change it in your stryker configuration.'); } this.starter = new ProjectStarter(setup); - this.setGlobals(setup); + this.setGlobals(setup, getLogger); this.cleanRun(); this.listenToServerStart(); this.listenToRunComplete(); @@ -58,16 +60,16 @@ export default class KarmaTestRunner implements TestRunner { return runResult; } - private loadSetup(settings: RunnerOptions): StrykerKarmaSetup { + private loadSetup(options: StrykerOptions): StrykerKarmaSetup { const defaultKarmaConfig: StrykerKarmaSetup = { projectType: 'custom' }; - const strykerKarmaSetup: StrykerKarmaSetup = Object.assign(defaultKarmaConfig, settings.strykerOptions[KARMA_CONFIG_KEY]); + const strykerKarmaSetup: StrykerKarmaSetup = Object.assign(defaultKarmaConfig, options[KARMA_CONFIG_KEY]); const loadDeprecatedOption = (configKey: keyof StrykerKarmaSetup, deprecatedConfigOption: string) => { - if (!strykerKarmaSetup[configKey] && settings.strykerOptions[deprecatedConfigOption]) { + if (!strykerKarmaSetup[configKey] && options[deprecatedConfigOption]) { this.log.warn(`[deprecated]: config option ${deprecatedConfigOption} is renamed to ${KARMA_CONFIG_KEY}.${configKey}`); - strykerKarmaSetup[configKey] = settings.strykerOptions[deprecatedConfigOption]; + strykerKarmaSetup[configKey] = options[deprecatedConfigOption]; } }; loadDeprecatedOption('configFile', DEPRECATED_KARMA_CONFIG_FILE); @@ -75,8 +77,9 @@ export default class KarmaTestRunner implements TestRunner { return strykerKarmaSetup; } - private setGlobals(setup: StrykerKarmaSetup) { + private setGlobals(setup: StrykerKarmaSetup, getLogger: LoggerFactoryMethod) { strykerKarmaConf.setGlobals({ + getLogger, karmaConfig: setup.config, karmaConfigFile: setup.configFile }); diff --git a/packages/stryker-karma-runner/src/index.ts b/packages/stryker-karma-runner/src/index.ts index 92644dbce4..4839a8bada 100644 --- a/packages/stryker-karma-runner/src/index.ts +++ b/packages/stryker-karma-runner/src/index.ts @@ -1,4 +1,6 @@ -import { TestRunnerFactory } from 'stryker-api/test_runner'; import KarmaTestRunner from './KarmaTestRunner'; +import { declareClassPlugin, PluginKind } from 'stryker-api/plugin'; -TestRunnerFactory.instance().register('karma', KarmaTestRunner); +export const strykerPlugins = [ + declareClassPlugin(PluginKind.TestRunner, 'karma', KarmaTestRunner) +]; diff --git a/packages/stryker-karma-runner/src/starters/stryker-karma.conf.ts b/packages/stryker-karma-runner/src/starters/stryker-karma.conf.ts index 47b032e347..0e6b563c30 100644 --- a/packages/stryker-karma-runner/src/starters/stryker-karma.conf.ts +++ b/packages/stryker-karma-runner/src/starters/stryker-karma.conf.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import { requireModule } from '../utils'; import TestHooksMiddleware, { TEST_HOOKS_FILE_NAME } from '../TestHooksMiddleware'; import StrykerReporter from '../StrykerReporter'; -import { getLogger, Logger } from 'stryker-api/logging'; +import { Logger, LoggerFactoryMethod } from 'stryker-api/logging'; function setDefaultOptions(config: Config) { config.set({ @@ -92,13 +92,33 @@ function configureStrykerReporter(config: Config) { config.reporters.push(StrykerReporter.name); } +const noopLogger = { + isTraceEnabled() { return false; }, + isDebugEnabled() { return false; }, + isInfoEnabled() { return false; }, + isWarnEnabled() { return false; }, + isErrorEnabled() { return false; }, + isFatalEnabled() { return false; }, + trace() { }, + debug() { }, + info() { }, + warn() { }, + error() { }, + fatal() { } +}; + const globalSettings: { karmaConfig?: ConfigOptions; karmaConfigFile?: string; -} = {}; + getLogger: LoggerFactoryMethod; +} = { + getLogger() { + return noopLogger; + } +}; export = Object.assign((config: Config) => { - const log = getLogger(path.basename(__filename)); + const log = globalSettings.getLogger(path.basename(__filename)); setDefaultOptions(config); setUserKarmaConfigFile(config, log); setUserKarmaConfig(config); @@ -107,13 +127,14 @@ export = Object.assign((config: Config) => { configureTestHooksMiddleware(config); configureStrykerReporter(config); }, { - /** - * Provide global settings for next configuration - * This is the only way we can pass through any values between the `KarmaTestRunner` and the stryker-karma.conf file. - * (not counting environment variables) - */ - setGlobals(globals: { karmaConfig?: ConfigOptions; karmaConfigFile?: string; }) { - globalSettings.karmaConfig = globals.karmaConfig; - globalSettings.karmaConfigFile = globals.karmaConfigFile; - } -}); + /** + * Provide global settings for next configuration + * This is the only way we can pass through any values between the `KarmaTestRunner` and the stryker-karma.conf file. + * (not counting environment variables) + */ + setGlobals(globals: { karmaConfig?: ConfigOptions; karmaConfigFile?: string; getLogger?: LoggerFactoryMethod }) { + globalSettings.karmaConfig = globals.karmaConfig; + globalSettings.karmaConfigFile = globals.karmaConfigFile; + globalSettings.getLogger = globals.getLogger || (() => noopLogger); + } + }); diff --git a/packages/stryker-karma-runner/test/helpers/LoggerStub.ts b/packages/stryker-karma-runner/test/helpers/LoggerStub.ts deleted file mode 100644 index 6232295cb3..0000000000 --- a/packages/stryker-karma-runner/test/helpers/LoggerStub.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as sinon from 'sinon'; -import { Logger } from 'stryker-api/logging'; - -export default class LoggerStub implements Logger { - public error: sinon.SinonStub; - public warn: sinon.SinonStub; - public info: sinon.SinonStub; - public debug: sinon.SinonStub; - public trace: sinon.SinonStub; - public fatal: sinon.SinonStub; - public isTraceEnabled: sinon.SinonStub; - public isDebugEnabled: sinon.SinonStub; - public isInfoEnabled: sinon.SinonStub; - public isWarnEnabled: sinon.SinonStub; - public isErrorEnabled: sinon.SinonStub; - public isFatalEnabled: sinon.SinonStub; - - constructor() { - this.error = sinon.stub(); - this.warn = sinon.stub(); - this.info = sinon.stub(); - this.debug = sinon.stub(); - - this.trace = sinon.stub(); - this.fatal = sinon.stub(); - this.isTraceEnabled = sinon.stub(); - this.isDebugEnabled = sinon.stub(); - this.isInfoEnabled = sinon.stub(); - this.isWarnEnabled = sinon.stub(); - this.isErrorEnabled = sinon.stub(); - this.isFatalEnabled = sinon.stub(); - } -} diff --git a/packages/stryker-karma-runner/test/helpers/globals.ts b/packages/stryker-karma-runner/test/helpers/globals.ts deleted file mode 100644 index 8d26910fe6..0000000000 --- a/packages/stryker-karma-runner/test/helpers/globals.ts +++ /dev/null @@ -1,6 +0,0 @@ -declare const sandbox: sinon.SinonSandbox; -namespace NodeJS { - export interface Global { - sandbox: sinon.SinonSandbox; - } -} diff --git a/packages/stryker-karma-runner/test/helpers/initSinon.ts b/packages/stryker-karma-runner/test/helpers/initSinon.ts index 3f17ab7b90..73fd05ba06 100644 --- a/packages/stryker-karma-runner/test/helpers/initSinon.ts +++ b/packages/stryker-karma-runner/test/helpers/initSinon.ts @@ -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(); + testInjector.reset(); + sinon.restore(); }); diff --git a/packages/stryker-karma-runner/test/integration/KarmaTestRunner.it.ts b/packages/stryker-karma-runner/test/integration/KarmaTestRunner.it.ts index 8c5c4a7219..cd8c24a1fd 100644 --- a/packages/stryker-karma-runner/test/integration/KarmaTestRunner.it.ts +++ b/packages/stryker-karma-runner/test/integration/KarmaTestRunner.it.ts @@ -1,12 +1,12 @@ import { expect } from 'chai'; -import { CoverageCollection, RunnerOptions, RunResult, RunStatus, TestStatus } from 'stryker-api/test_runner'; +import { CoverageCollection, RunResult, RunStatus, TestStatus } from 'stryker-api/test_runner'; import KarmaTestRunner from '../../src/KarmaTestRunner'; import JasmineTestFramework from 'stryker-jasmine/src/JasmineTestFramework'; import { expectTestResults } from '../helpers/assertions'; import http = require('http'); import { promisify } from '@stryker-mutator/util'; import { FilePattern } from 'karma'; -import { factory } from '@stryker-mutator/test-helpers'; +import { testInjector } from '@stryker-mutator/test-helpers'; function wrapInClosure(codeFragment: string) { return ` @@ -15,22 +15,21 @@ function wrapInClosure(codeFragment: string) { })((Function('return this'))());`; } -function createRunnerOptions(files: ReadonlyArray, coverageAnalysis: 'all' | 'perTest' | 'off' = 'off'): RunnerOptions { - return { - fileNames: [], - strykerOptions: factory.strykerOptions({ - coverageAnalysis, - karma: { - config: { - files, - logLevel: 'off', - reporters: [] - } - } - }) +function setOptions(files: ReadonlyArray, coverageAnalysis: 'all' | 'perTest' | 'off' = 'off'): void { + testInjector.options.coverageAnalysis = coverageAnalysis; + testInjector.options.karma = { + config: { + files, + logLevel: 'off', + reporters: [] + } }; } +function createSut() { + return testInjector.injector.injectClass(KarmaTestRunner); +} + describe('KarmaTestRunner', () => { let sut: KarmaTestRunner; @@ -52,10 +51,11 @@ describe('KarmaTestRunner', () => { describe('with simple add function to test', () => { before(() => { - sut = new KarmaTestRunner(createRunnerOptions([ + setOptions([ 'testResources/sampleProject/src/Add.js', 'testResources/sampleProject/test/AddSpec.js' - ])); + ]); + sut = createSut(); return sut.init(); }); @@ -90,11 +90,12 @@ describe('KarmaTestRunner', () => { describe('when some tests fail', () => { before(() => { - sut = new KarmaTestRunner(createRunnerOptions([ + setOptions([ 'testResources/sampleProject/src/Add.js', 'testResources/sampleProject/test/AddSpec.js', 'testResources/sampleProject/test/AddFailedSpec.js' - ])); + ]); + sut = createSut(); return sut.init(); }); @@ -111,11 +112,12 @@ describe('KarmaTestRunner', () => { describe('when an error occurs while running tests', () => { before(() => { - sut = new KarmaTestRunner(createRunnerOptions([ + setOptions([ 'testResources/sampleProject/src/Add.js', 'testResources/sampleProject/src/Error.js', 'testResources/sampleProject/test/AddSpec.js' - ])); + ]); + sut = createSut(); return sut.init(); }); @@ -129,10 +131,11 @@ describe('KarmaTestRunner', () => { describe('when no error occurred and no test is performed', () => { before(() => { - sut = new KarmaTestRunner(createRunnerOptions([ + setOptions([ 'testResources/sampleProject/src/Add.js', 'testResources/sampleProject/test/EmptySpec.js' - ])); + ]); + sut = createSut(); return sut.init(); }); @@ -151,11 +154,12 @@ describe('KarmaTestRunner', () => { describe('when adding an error file with included: false', () => { before(() => { - sut = new KarmaTestRunner(createRunnerOptions([ + setOptions([ { pattern: 'testResources/sampleProject/src/Add.js', included: true }, { pattern: 'testResources/sampleProject/test/AddSpec.js', included: true }, { pattern: 'testResources/sampleProject/src/Error.js', included: false } - ])); + ]); + sut = createSut(); return sut.init(); }); @@ -170,10 +174,11 @@ describe('KarmaTestRunner', () => { describe('when coverage data is available', () => { before(() => { - sut = new KarmaTestRunner(createRunnerOptions([ + setOptions([ 'testResources/sampleProject/src-instrumented/Add.js', 'testResources/sampleProject/test/AddSpec.js' - ], 'all')); + ], 'all'); + sut = createSut(); return sut.init(); }); @@ -193,10 +198,7 @@ describe('KarmaTestRunner', () => { before(async () => { dummyServer = await DummyServer.create(); - sut = new KarmaTestRunner(createRunnerOptions([ - 'testResources/sampleProject/src-instrumented/Add.js', - 'testResources/sampleProject/test/AddSpec.js' - ])); + sut = testInjector.injector.injectClass(KarmaTestRunner); return sut.init(); }); diff --git a/packages/stryker-karma-runner/test/integration/sample.it.ts b/packages/stryker-karma-runner/test/integration/sample.it.ts index d24925d82b..ac5b7d4da7 100644 --- a/packages/stryker-karma-runner/test/integration/sample.it.ts +++ b/packages/stryker-karma-runner/test/integration/sample.it.ts @@ -1,15 +1,14 @@ import { TestStatus } from 'stryker-api/test_runner'; -import { Config } from 'stryker-api/config'; import * as path from 'path'; import KarmaTestRunner from '../../src/KarmaTestRunner'; import { expectTestResults } from '../helpers/assertions'; +import { testInjector } from '@stryker-mutator/test-helpers'; describe('Sample project', () => { it('should be able to run karma', async () => { - const options = new Config(); - options.karmaConfigFile = path.resolve(__dirname, '..', '..', 'testResources', 'sampleProject', 'karma.conf.js'); - const runner = new KarmaTestRunner({ strykerOptions: options, fileNames: [] }); + testInjector.options.karmaConfigFile = path.resolve(__dirname, '..', '..', 'testResources', 'sampleProject', 'karma.conf.js'); + const runner = testInjector.injector.injectClass(KarmaTestRunner); await runner.init(); const result = await runner.run({}); expectTestResults(result, [ diff --git a/packages/stryker-karma-runner/test/unit/KarmaTestRunnerSpec.ts b/packages/stryker-karma-runner/test/unit/KarmaTestRunnerSpec.ts index c7e720afc9..d6fa53f070 100644 --- a/packages/stryker-karma-runner/test/unit/KarmaTestRunnerSpec.ts +++ b/packages/stryker-karma-runner/test/unit/KarmaTestRunnerSpec.ts @@ -1,17 +1,15 @@ import { EventEmitter } from 'events'; import { expect } from 'chai'; -import * as logging from 'stryker-api/logging'; import * as karma from 'karma'; +import * as sinon from 'sinon'; import strykerKarmaConf = require('../../src/starters/stryker-karma.conf'); import ProjectStarter, * as projectStarterModule from '../../src/starters/ProjectStarter'; import KarmaTestRunner from '../../src/KarmaTestRunner'; import { - RunnerOptions, TestResult, TestStatus, RunStatus } from 'stryker-api/test_runner'; -import LoggerStub from '../helpers/LoggerStub'; import StrykerKarmaSetup, { DEPRECATED_KARMA_CONFIG, DEPRECATED_KARMA_CONFIG_FILE, @@ -19,35 +17,36 @@ import StrykerKarmaSetup, { } from '../../src/StrykerKarmaSetup'; import StrykerReporter from '../../src/StrykerReporter'; import TestHooksMiddleware from '../../src/TestHooksMiddleware'; -import { factory } from '@stryker-mutator/test-helpers'; +import { testInjector } from '@stryker-mutator/test-helpers'; +import { LoggerFactoryMethod } from 'stryker-api/logging'; +import { commonTokens } from 'stryker-api/plugin'; describe('KarmaTestRunner', () => { let projectStarterMock: sinon.SinonStubbedInstance; - let settings: RunnerOptions; let setGlobalsStub: sinon.SinonStub; - let logMock: LoggerStub; let reporterMock: EventEmitter; let karmaRunStub: sinon.SinonStub; + let getLogger: LoggerFactoryMethod; beforeEach(() => { - settings = { - fileNames: ['foo.js', 'bar.js'], - strykerOptions: factory.strykerOptions() - }; reporterMock = new EventEmitter(); - projectStarterMock = sandbox.createStubInstance(ProjectStarter); - logMock = new LoggerStub(); - sandbox.stub(projectStarterModule, 'default').returns(projectStarterMock); - sandbox.stub(logging, 'getLogger').returns(logMock); - sandbox.stub(StrykerReporter, 'instance').value(reporterMock); - setGlobalsStub = sandbox.stub(strykerKarmaConf, 'setGlobals'); - karmaRunStub = sandbox.stub(karma.runner, 'run'); - sandbox.stub(TestHooksMiddleware, 'instance').value({}); + projectStarterMock = sinon.createStubInstance(ProjectStarter); + sinon.stub(projectStarterModule, 'default').returns(projectStarterMock); + sinon.stub(StrykerReporter, 'instance').value(reporterMock); + setGlobalsStub = sinon.stub(strykerKarmaConf, 'setGlobals'); + karmaRunStub = sinon.stub(karma.runner, 'run'); + sinon.stub(TestHooksMiddleware, 'instance').value({}); + getLogger = testInjector.injector.resolve(commonTokens.getLogger); }); + function createSut() { + return testInjector.injector.injectClass(KarmaTestRunner); + } + it('should load default setup', () => { - new KarmaTestRunner(settings); + createSut(); expect(setGlobalsStub).calledWith({ + getLogger, karmaConfig: undefined, karmaConfigFile: undefined }); @@ -61,14 +60,15 @@ describe('KarmaTestRunner', () => { configFile: 'baz.conf.js', projectType: 'angular-cli' }; - settings.strykerOptions.karma = expectedSetup; - new KarmaTestRunner(settings); + testInjector.options.karma = expectedSetup; + createSut(); expect(setGlobalsStub).calledWith({ + getLogger, karmaConfig: expectedSetup.config, karmaConfigFile: expectedSetup.configFile }); - expect(logMock.warn).not.called; + expect(testInjector.logger.warn).not.called; expect(projectStarterModule.default).calledWith(expectedSetup); }); it('should run ng test with parameters from stryker options', () => { @@ -84,33 +84,35 @@ describe('KarmaTestRunner', () => { ngConfig, projectType: 'angular-cli' }; - settings.strykerOptions.karma = expectedSetup; - new KarmaTestRunner(settings); + testInjector.options.karma = expectedSetup; + createSut(); expect(setGlobalsStub).calledWith({ + getLogger, karmaConfig: expectedSetup.config, karmaConfigFile: expectedSetup.configFile }); - expect(logMock.warn).not.called; + expect(testInjector.logger.warn).not.called; expect(projectStarterModule.default).calledWith(expectedSetup); }); it('should load deprecated karma options', () => { const expectedKarmaConfig = { basePath: 'foobar' }; const expectedKarmaConfigFile = 'karmaConfigFile'; - settings.strykerOptions[DEPRECATED_KARMA_CONFIG] = expectedKarmaConfig; - settings.strykerOptions[ + testInjector.options[DEPRECATED_KARMA_CONFIG] = expectedKarmaConfig; + testInjector.options[ DEPRECATED_KARMA_CONFIG_FILE ] = expectedKarmaConfigFile; - new KarmaTestRunner(settings); + createSut(); expect(setGlobalsStub).calledWith({ + getLogger, karmaConfig: expectedKarmaConfig, karmaConfigFile: expectedKarmaConfigFile }); - expect(logMock.warn).calledTwice; - expect(logMock.warn).calledWith( + expect(testInjector.logger.warn).calledTwice; + expect(testInjector.logger.warn).calledWith( '[deprecated]: config option karmaConfigFile is renamed to karma.configFile' ); - expect(logMock.warn).calledWith( + expect(testInjector.logger.warn).calledWith( '[deprecated]: config option karmaConfig is renamed to karma.config' ); }); @@ -130,13 +132,14 @@ describe('KarmaTestRunner', () => { configFile: 'baz.conf.js', projectType: 'angular-cli' }; - settings.strykerOptions.karma = config; - new KarmaTestRunner(settings); + testInjector.options.karma = config; + createSut(); expect(setGlobalsStub).calledWith({ + getLogger, karmaConfig: expectedSetup.config, karmaConfigFile: expectedSetup.configFile }); - expect(logMock.warn).calledWith( + expect(testInjector.logger.warn).calledWith( 'DEPRECATED: `karma.project` is renamed to `karma.projectType`. Please change it in your stryker configuration.' ); }); @@ -145,7 +148,7 @@ describe('KarmaTestRunner', () => { let sut: KarmaTestRunner; beforeEach(() => { - sut = new KarmaTestRunner(settings); + sut = createSut(); }); it('should start karma', async () => { @@ -167,7 +170,7 @@ describe('KarmaTestRunner', () => { let sut: KarmaTestRunner; beforeEach(() => { - sut = new KarmaTestRunner(settings); + sut = createSut(); karmaRunStub.callsArgOn(1, 0); }); diff --git a/packages/stryker-karma-runner/test/unit/starters/angularStarterSpec.ts b/packages/stryker-karma-runner/test/unit/starters/angularStarterSpec.ts index 6d633f5995..3ccb07209a 100644 --- a/packages/stryker-karma-runner/test/unit/starters/angularStarterSpec.ts +++ b/packages/stryker-karma-runner/test/unit/starters/angularStarterSpec.ts @@ -1,14 +1,15 @@ import { expect } from 'chai'; import * as utils from '../../../src/utils'; import * as sut from '../../../src/starters/angularStarter'; +import * as sinon from 'sinon'; describe('angularStarter', () => { let requireModuleStub: sinon.SinonStub; let cliStub: sinon.SinonStub; beforeEach(() => { - cliStub = sandbox.stub(); - requireModuleStub = sandbox.stub(utils, 'requireModule'); + cliStub = sinon.stub(); + requireModuleStub = sinon.stub(utils, 'requireModule'); requireModuleStub.withArgs('@angular/cli').returns(cliStub); }); diff --git a/packages/stryker-karma-runner/test/unit/starters/stryker-karma.confSpec.ts b/packages/stryker-karma-runner/test/unit/starters/stryker-karma.confSpec.ts index 7dee0975e6..8e62678b06 100644 --- a/packages/stryker-karma-runner/test/unit/starters/stryker-karma.confSpec.ts +++ b/packages/stryker-karma-runner/test/unit/starters/stryker-karma.confSpec.ts @@ -1,24 +1,27 @@ import * as path from 'path'; -import * as logging from 'stryker-api/logging'; import sut = require('../../../src/starters/stryker-karma.conf'); import { Config, ConfigOptions } from 'karma'; import { expect } from 'chai'; -import LoggerStub from '../../helpers/LoggerStub'; import * as utils from '../../../src/utils'; import TestHooksMiddleware, { TEST_HOOKS_FILE_NAME } from '../../../src/TestHooksMiddleware'; import StrykerReporter from '../../../src/StrykerReporter'; +import * as sinon from 'sinon'; +import { testInjector } from '@stryker-mutator/test-helpers'; describe('stryker-karma.conf.js', () => { - let logMock: LoggerStub; + let getLogger: sinon.SinonStub; let requireModuleStub: sinon.SinonStub; let config: Config; beforeEach(() => { config = new KarmaConfigMock(); - logMock = new LoggerStub(); - sandbox.stub(logging, 'getLogger').returns(logMock); - requireModuleStub = sandbox.stub(utils, 'requireModule'); + getLogger = sinon.stub(); + getLogger.returns(testInjector.logger); + requireModuleStub = sinon.stub(utils, 'requireModule'); + sut.setGlobals({ + getLogger + }); }); afterEach(() => { @@ -27,7 +30,7 @@ describe('stryker-karma.conf.js', () => { it('should create the correct logger', () => { sut(config); - expect(logging.getLogger).calledWith('stryker-karma.conf.js'); + expect(getLogger).calledWith('stryker-karma.conf.js'); }); it('should set default options', () => { @@ -60,13 +63,13 @@ describe('stryker-karma.conf.js', () => { actualError.code = 'MODULE_NOT_FOUND'; requireModuleStub.throws(actualError); const expectedKarmaConfigFile = 'foobar.conf.js'; - sut.setGlobals({ karmaConfigFile: expectedKarmaConfigFile }); + sut.setGlobals({ getLogger, karmaConfigFile: expectedKarmaConfigFile }); // Act sut(config); // Assert - expect(logMock.error).calledWithMatch(`Unable to find karma config at "foobar.conf.js" (tried to load from ${path.resolve(expectedKarmaConfigFile)})`); + expect(testInjector.logger.error).calledWithMatch(`Unable to find karma config at "foobar.conf.js" (tried to load from ${path.resolve(expectedKarmaConfigFile)})`); expect(requireModuleStub).calledWith(path.resolve(expectedKarmaConfigFile)); }); diff --git a/packages/stryker-mocha-framework/package.json b/packages/stryker-mocha-framework/package.json index ea09ea09f0..712434bd2a 100644 --- a/packages/stryker-mocha-framework/package.json +++ b/packages/stryker-mocha-framework/package.json @@ -35,11 +35,12 @@ "homepage": "https://github.com/stryker-mutator/stryker/tree/master/packages/stryker-mocha-framework#readme", "license": "Apache-2.0", "devDependencies": { - "stryker-api": "^0.23.0", "tslib": "~1.9.3" }, "peerDependencies": { - "mocha": ">= 2.3.3 < 6", - "stryker-api": ">=0.18.0 <0.24.0" + "mocha": ">= 2.3.3 < 6" + }, + "dependencies": { + "stryker-api": "^0.23.0" } } diff --git a/packages/stryker-mocha-runner/package.json b/packages/stryker-mocha-runner/package.json index eac20649fc..04b4ff3ae7 100644 --- a/packages/stryker-mocha-runner/package.json +++ b/packages/stryker-mocha-runner/package.json @@ -34,16 +34,15 @@ "homepage": "https://github.com/stryker-mutator/stryker/tree/master/packages/stryker-mocha-runner#readme", "dependencies": { "multimatch": "~3.0.0", + "stryker-api": "^0.23.0", "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" }, "peerDependencies": { - "mocha": ">= 2.3.3 < 6", - "stryker-api": ">=0.18.0 <0.24.0" + "mocha": ">= 2.3.3 < 6" } } diff --git a/packages/stryker-mocha-runner/src/MochaTestRunner.ts b/packages/stryker-mocha-runner/src/MochaTestRunner.ts index 52d3275cee..b50132c09b 100644 --- a/packages/stryker-mocha-runner/src/MochaTestRunner.ts +++ b/packages/stryker-mocha-runner/src/MochaTestRunner.ts @@ -1,30 +1,30 @@ -import { getLogger } from 'stryker-api/logging'; +import { Logger } from 'stryker-api/logging'; import * as path from 'path'; -import { TestRunner, RunResult, RunStatus, RunnerOptions } from 'stryker-api/test_runner'; +import { TestRunner, RunResult, RunStatus } from 'stryker-api/test_runner'; import LibWrapper from './LibWrapper'; import StrykerMochaReporter from './StrykerMochaReporter'; import MochaRunnerOptions, { mochaOptionsKey } from './MochaRunnerOptions'; import { evalGlobal } from './utils'; +import { StrykerOptions } from 'stryker-api/core'; +import { tokens, commonTokens } from 'stryker-api/plugin'; const DEFAULT_TEST_PATTERN = 'test/**/*.js'; export default class MochaTestRunner implements TestRunner { private testFileNames: string[]; - private readonly allFileNames: string[]; - private readonly log = getLogger(MochaTestRunner.name); private readonly mochaRunnerOptions: MochaRunnerOptions; - constructor(runnerOptions: RunnerOptions) { - this.mochaRunnerOptions = runnerOptions.strykerOptions[mochaOptionsKey]; - this.allFileNames = runnerOptions.fileNames; + public static inject = tokens(commonTokens.logger, commonTokens.sandboxFileNames, commonTokens.options); + constructor(private readonly log: Logger, private readonly allFileNames: ReadonlyArray, options: StrykerOptions) { + this.mochaRunnerOptions = options[mochaOptionsKey]; this.additionalRequires(); } public init(): void { const globPatterns = this.mochaFileGlobPatterns(); const globPatternsAbsolute = globPatterns.map(glob => path.resolve(glob)); - this.testFileNames = LibWrapper.multimatch(this.allFileNames, globPatternsAbsolute); + this.testFileNames = LibWrapper.multimatch(this.allFileNames.slice(), globPatternsAbsolute); if (this.testFileNames.length) { this.log.debug(`Using files: ${JSON.stringify(this.testFileNames, null, 2)}`); } else { @@ -118,9 +118,7 @@ export default class MochaTestRunner implements TestRunner { private additionalRequires() { if (this.mochaRunnerOptions.require) { const modulesToRequire = this.mochaRunnerOptions.require - .map(module => { - return module.startsWith('.') ? path.resolve(module) : module; - }); + .map(moduleName => moduleName.startsWith('.') ? path.resolve(moduleName) : moduleName); modulesToRequire.forEach(LibWrapper.require); } } diff --git a/packages/stryker-mocha-runner/src/index.ts b/packages/stryker-mocha-runner/src/index.ts index 8246a3b3ed..1da9d2b335 100644 --- a/packages/stryker-mocha-runner/src/index.ts +++ b/packages/stryker-mocha-runner/src/index.ts @@ -1,14 +1,12 @@ -import { TestRunnerFactory } from 'stryker-api/test_runner'; -import { declareFactoryPlugin, PluginKind, BaseContext, tokens, commonTokens, Injector } from 'stryker-api/plugin'; +import { declareFactoryPlugin, PluginKind, BaseContext, tokens, commonTokens, Injector, declareClassPlugin } from 'stryker-api/plugin'; import MochaTestRunner from './MochaTestRunner'; import MochaConfigEditor from './MochaConfigEditor'; import MochaOptionsLoader from './MochaOptionsLoader'; -TestRunnerFactory.instance().register('mocha', MochaTestRunner); - export const strykerPlugins = [ - declareFactoryPlugin(PluginKind.ConfigEditor, 'mocha-runner', mochaConfigEditorFactory) + declareFactoryPlugin(PluginKind.ConfigEditor, 'mocha-runner', mochaConfigEditorFactory), + declareClassPlugin(PluginKind.TestRunner, 'mocha', MochaTestRunner) ]; mochaConfigEditorFactory.inject = tokens(commonTokens.injector); diff --git a/packages/stryker-mocha-runner/test/helpers/globals.ts b/packages/stryker-mocha-runner/test/helpers/globals.ts deleted file mode 100644 index f1c6bb3c54..0000000000 --- a/packages/stryker-mocha-runner/test/helpers/globals.ts +++ /dev/null @@ -1,7 +0,0 @@ -declare const sandbox: sinon.SinonSandbox; - -namespace NodeJS { - export interface Global { - sandbox: sinon.SinonSandbox; - } -} diff --git a/packages/stryker-mocha-runner/test/helpers/mockHelpers.ts b/packages/stryker-mocha-runner/test/helpers/mockHelpers.ts index 2574d6fdcc..9e6b61bb0c 100644 --- a/packages/stryker-mocha-runner/test/helpers/mockHelpers.ts +++ b/packages/stryker-mocha-runner/test/helpers/mockHelpers.ts @@ -1,5 +1,4 @@ import * as sinon from 'sinon'; -import { Logger } from 'stryker-api/logging'; import { RunnerOptions } from 'stryker-api/test_runner'; import { factory } from '@stryker-mutator/test-helpers'; @@ -11,23 +10,6 @@ export function mock(constructorFn: new (...args: any[]) => T): Mock { return sinon.createStubInstance(constructorFn) as Mock; } -export function logger(): Mock { - return { - debug: sinon.stub(), - error: sinon.stub(), - fatal: sinon.stub(), - info: sinon.stub(), - isDebugEnabled: sinon.stub(), - isErrorEnabled: sinon.stub(), - isFatalEnabled: sinon.stub(), - isInfoEnabled: sinon.stub(), - isTraceEnabled: sinon.stub(), - isWarnEnabled: sinon.stub(), - trace: sinon.stub(), - warn: sinon.stub() - }; -} - export const runnerOptions = factoryMethod(() => ({ fileNames: ['src/math.js', 'test/mathSpec.js'], strykerOptions: factory.strykerOptions({ mochaOptions: {} }) diff --git a/packages/stryker-mocha-runner/test/integration/MochaTestFrameworkIntegrationTestWorker.ts b/packages/stryker-mocha-runner/test/integration/MochaTestFrameworkIntegrationTestWorker.ts index ad261dac9a..65360e1eee 100644 --- a/packages/stryker-mocha-runner/test/integration/MochaTestFrameworkIntegrationTestWorker.ts +++ b/packages/stryker-mocha-runner/test/integration/MochaTestFrameworkIntegrationTestWorker.ts @@ -1,7 +1,8 @@ import MochaTestRunner from '../../src/MochaTestRunner'; import * as path from 'path'; import { RunResult } from 'stryker-api/test_runner'; -import { factory } from '../../../stryker-test-helpers/src'; +import { testInjector } from '@stryker-mutator/test-helpers'; +import { commonTokens } from 'stryker-api/plugin'; export const AUTO_START_ARGUMENT = '2e164669-acf1-461c-9c05-2be139614de2'; @@ -16,19 +17,18 @@ export default class MochaTestFrameworkIntegrationTestWorker { private readonly sut: MochaTestRunner; constructor() { - this.sut = new MochaTestRunner({ - fileNames: [ + testInjector.options.mochaOptions = { + files: [ + path.resolve(__dirname, '..', '..', 'testResources', 'sampleProject', 'MyMathSpec.js') + ] + }; + this.sut = testInjector.injector + .provideValue(commonTokens.sandboxFileNames, [ path.resolve(__dirname, '..', '..', 'testResources', 'sampleProject', 'MyMath.js'), path.resolve(__dirname, '..', '..', 'testResources', 'sampleProject', 'MyMathSpec.js') - ], - strykerOptions: factory.strykerOptions({ - mochaOptions: { - files: [ - path.resolve(__dirname, '..', '..', 'testResources', 'sampleProject', 'MyMathSpec.js') - ] - } - }) - }); + ]) + .injectClass(MochaTestRunner); + this.listenForParentProcess(); try { this.sut.init(); diff --git a/packages/stryker-mocha-runner/test/integration/QUnitSampleSpec.ts b/packages/stryker-mocha-runner/test/integration/QUnitSampleSpec.ts index 1b03d30f0a..512fa6dd71 100644 --- a/packages/stryker-mocha-runner/test/integration/QUnitSampleSpec.ts +++ b/packages/stryker-mocha-runner/test/integration/QUnitSampleSpec.ts @@ -2,10 +2,21 @@ import * as path from 'path'; import { expect } from 'chai'; import { RunStatus } from 'stryker-api/test_runner'; import MochaTestRunner from '../../src/MochaTestRunner'; -import { runnerOptions } from '../helpers/mockHelpers'; -import { factory } from '@stryker-mutator/test-helpers'; +import { testInjector } from '@stryker-mutator/test-helpers'; +import { commonTokens } from 'stryker-api/plugin'; describe('QUnit sample', () => { + let files: string[]; + + beforeEach(() => { + files = []; + }); + + function createSut() { + return testInjector.injector + .provideValue(commonTokens.sandboxFileNames, files) + .injectClass(MochaTestRunner); + } it('should work when configured with "qunit" ui', async () => { const mochaOptions = { @@ -13,10 +24,9 @@ describe('QUnit sample', () => { require: [], ui: 'qunit' }; - const sut = new MochaTestRunner(runnerOptions({ - fileNames: mochaOptions.files, - strykerOptions: factory.strykerOptions({ mochaOptions }) - })); + testInjector.options.mochaOptions = mochaOptions; + files = mochaOptions.files; + const sut = createSut(); await sut.init(); const actualResult = await sut.run({}); expect(actualResult.status).eq(RunStatus.Complete); @@ -30,19 +40,16 @@ describe('QUnit sample', () => { }); it('should not run tests when not configured with "qunit" ui', async () => { - const sut = new MochaTestRunner({ - fileNames: [ - resolve('./testResources/qunit-sample/MyMathSpec.js'), - resolve('./testResources/qunit-sample/MyMath.js') - ], - strykerOptions: factory.strykerOptions({ - mochaOptions: { - files: [ - resolve('./testResources/qunit-sample/MyMathSpec.js') - ] - } - }) - }); + files = [ + resolve('./testResources/qunit-sample/MyMathSpec.js'), + resolve('./testResources/qunit-sample/MyMath.js') + ]; + testInjector.options.mochaOptions = { + files: [ + resolve('./testResources/qunit-sample/MyMathSpec.js') + ] + }; + const sut = createSut(); await sut.init(); const actualResult = await sut.run({}); expect(actualResult.status).eq(RunStatus.Complete); diff --git a/packages/stryker-mocha-runner/test/integration/SampleProjectSpec.ts b/packages/stryker-mocha-runner/test/integration/SampleProjectSpec.ts index 03f749eb09..e609173d6f 100644 --- a/packages/stryker-mocha-runner/test/integration/SampleProjectSpec.ts +++ b/packages/stryker-mocha-runner/test/integration/SampleProjectSpec.ts @@ -1,10 +1,11 @@ import * as chai from 'chai'; import MochaTestRunner from '../../src/MochaTestRunner'; -import { TestResult, RunResult, TestStatus, RunStatus, RunnerOptions } from 'stryker-api/test_runner'; +import { TestResult, RunResult, TestStatus, RunStatus } from 'stryker-api/test_runner'; import * as chaiAsPromised from 'chai-as-promised'; import * as path from 'path'; import MochaRunnerOptions from '../../src/MochaRunnerOptions'; -import { factory } from '../../../stryker-test-helpers/src'; +import { testInjector } from '../../../stryker-test-helpers/src'; +import { commonTokens } from 'stryker-api/plugin'; chai.use(chaiAsPromised); const expect = chai.expect; @@ -23,21 +24,23 @@ function resolve(fileName: string) { describe('Running a sample project', () => { let sut: MochaTestRunner; + let files: string[]; + + function createSut() { + return testInjector.injector + .provideValue(commonTokens.sandboxFileNames, files) + .injectClass(MochaTestRunner); + } describe('when tests pass', () => { beforeEach(() => { - const files = [ + files = [ resolve('./testResources/sampleProject/MyMath.js'), resolve('./testResources/sampleProject/MyMathSpec.js') ]; - const testRunnerOptions: RunnerOptions = { - fileNames: files, - strykerOptions: factory.strykerOptions({ - mochaOptions: { files } - }) - }; - sut = new MochaTestRunner(testRunnerOptions); + testInjector.options.mochaOptions = { files }; + sut = createSut(); return sut.init(); }); @@ -59,18 +62,15 @@ describe('Running a sample project', () => { describe('with an error in an un-included input file', () => { beforeEach(() => { - const files = [ + files = [ resolve('testResources/sampleProject/MyMath.js'), resolve('testResources/sampleProject/MyMathSpec.js'), ]; const mochaOptions: MochaRunnerOptions = { files }; - const options: RunnerOptions = { - fileNames: files, - strykerOptions: factory.strykerOptions({ mochaOptions }) - }; - sut = new MochaTestRunner(options); + testInjector.options.mochaOptions = mochaOptions; + sut = createSut(); return sut.init(); }); @@ -83,19 +83,12 @@ describe('Running a sample project', () => { describe('with multiple failed tests', () => { before(() => { - sut = new MochaTestRunner({ - fileNames: [ - resolve('testResources/sampleProject/MyMath.js'), - resolve('testResources/sampleProject/MyMathFailedSpec.js') - ], - strykerOptions: factory.strykerOptions({ - mochaOptions: { - files: [ - resolve('testResources/sampleProject/MyMath.js'), - resolve('testResources/sampleProject/MyMathFailedSpec.js')], - } - }) - }); + files = [ + resolve('testResources/sampleProject/MyMath.js'), + resolve('testResources/sampleProject/MyMathFailedSpec.js') + ]; + testInjector.options.mochaOptions = { files }; + sut = createSut(); return sut.init(); }); @@ -108,14 +101,9 @@ describe('Running a sample project', () => { describe('when no tests are executed', () => { beforeEach(() => { - const files = [resolve('./testResources/sampleProject/MyMath.js')]; - const testRunnerOptions = { - fileNames: files, - strykerOptions: factory.strykerOptions({ - mochaOptions: { files } - }) - }; - sut = new MochaTestRunner(testRunnerOptions); + files = [resolve('./testResources/sampleProject/MyMath.js')]; + testInjector.options.mochaOptions = { files }; + sut = createSut(); return sut.init(); }); diff --git a/packages/stryker-mocha-runner/test/unit/MochaTestRunnerSpec.ts b/packages/stryker-mocha-runner/test/unit/MochaTestRunnerSpec.ts index 9d1957ad37..45b7aff63f 100644 --- a/packages/stryker-mocha-runner/test/unit/MochaTestRunnerSpec.ts +++ b/packages/stryker-mocha-runner/test/unit/MochaTestRunnerSpec.ts @@ -1,25 +1,24 @@ import * as path from 'path'; import { EventEmitter } from 'events'; import * as Mocha from 'mocha'; -import * as logging from 'stryker-api/logging'; import { expect } from 'chai'; import { RunOptions } from 'stryker-api/test_runner'; import MochaTestRunner from '../../src/MochaTestRunner'; import LibWrapper from '../../src/LibWrapper'; import * as utils from '../../src/utils'; -import { Mock, mock, logger, runnerOptions } from '../helpers/mockHelpers'; +import { Mock, mock } from '../helpers/mockHelpers'; import MochaRunnerOptions from '../../src/MochaRunnerOptions'; -import { factory } from '../../../stryker-test-helpers/src'; +import { testInjector } from '../../../stryker-test-helpers/src'; import sinon = require('sinon'); +import { commonTokens } from 'stryker-api/plugin'; -describe('MochaTestRunner', () => { +describe.only(MochaTestRunner.name, () => { let MochaStub: sinon.SinonStub; let mocha: Mock & { suite: Mock }; let sut: MochaTestRunner; let requireStub: sinon.SinonStub; let multimatchStub: sinon.SinonStub; - let log: Mock; beforeEach(() => { MochaStub = sinon.stub(LibWrapper, 'Mocha'); @@ -29,8 +28,6 @@ describe('MochaTestRunner', () => { mocha = mock(Mocha) as any; mocha.suite = mock(EventEmitter); MochaStub.returns(mocha); - log = logger(); - sinon.stub(logging, 'getLogger').returns(log); }); afterEach(() => { @@ -40,11 +37,16 @@ describe('MochaTestRunner', () => { delete require.cache['baz.js']; }); + function createSut(mochaSettings: Partial<{ fileNames: ReadonlyArray, mochaOptions: MochaRunnerOptions }>) { + testInjector.options.mochaOptions = mochaSettings.mochaOptions || {}; + return testInjector.injector + .provideValue(commonTokens.sandboxFileNames, mochaSettings.fileNames || ['src/math.js', 'test/mathSpec.js']) + .injectClass(MochaTestRunner); + } + it('should should add all mocha test files on run()', async () => { multimatchStub.returns(['foo.js', 'bar.js', 'foo2.js']); - sut = new MochaTestRunner(runnerOptions({ - strykerOptions: factory.strykerOptions({ mochaOptions: {} }) - })); + sut = createSut({}); await sut.init(); await actRun(); expect(mocha.addFile).calledThrice; @@ -81,7 +83,7 @@ describe('MochaTestRunner', () => { timeout: 2000, ui: 'assert' }; - sut = new MochaTestRunner(runnerOptions({ strykerOptions: factory.strykerOptions({ mochaOptions }) })); + sut = createSut({ mochaOptions }); await sut.init(); // Act @@ -96,7 +98,7 @@ describe('MochaTestRunner', () => { it('should pass require additional require options when constructed', () => { const mochaOptions: MochaRunnerOptions = { require: ['ts-node', 'babel-register'] }; - new MochaTestRunner(runnerOptions({ strykerOptions: factory.strykerOptions({ mochaOptions }) })); + createSut({ mochaOptions }); expect(requireStub).calledTwice; expect(requireStub).calledWith('ts-node'); expect(requireStub).calledWith('babel-register'); @@ -104,7 +106,7 @@ describe('MochaTestRunner', () => { it('should pass and resolve relative require options when constructed', () => { const mochaOptions: MochaRunnerOptions = { require: ['./setup.js', 'babel-register'] }; - new MochaTestRunner(runnerOptions({ strykerOptions: factory.strykerOptions({ mochaOptions }) })); + createSut({ mochaOptions }); const resolvedRequire = path.resolve('./setup.js'); expect(requireStub).calledTwice; expect(requireStub).calledWith(resolvedRequire); @@ -113,7 +115,7 @@ describe('MochaTestRunner', () => { it('should evaluate additional testHooks if required (in global mocha context)', async () => { multimatchStub.returns(['']); - sut = new MochaTestRunner(runnerOptions()); + sut = createSut({}); await sut.init(); await actRun({ timeout: 0, testHooks: 'foobar();' }); expect(utils.evalGlobal).calledWith('foobar();'); @@ -124,7 +126,7 @@ describe('MochaTestRunner', () => { it('should purge cached sandbox files', async () => { // Arrange - sut = new MochaTestRunner(runnerOptions({ fileNames: ['foo.js', 'bar.js'] })); + sut = createSut({ fileNames: ['foo.js', 'bar.js'] }); multimatchStub.returns(['foo.js']); // should still purge 'bar.js' sut.init(); require.cache['foo.js'] = 'foo'; @@ -149,13 +151,13 @@ describe('MochaTestRunner', () => { const filesStringified = JSON.stringify(files, null, 2); // Act - sut = new MochaTestRunner(runnerOptions({ fileNames: files })); + sut = createSut({ fileNames: files }); const actFn = () => sut.init(); // Assert expect(actFn).throws(`[MochaTestRunner] No files discovered (tried pattern(s) ${relativeGlobbing - }). Please specify the files (glob patterns) containing your tests in mochaOptions.files in your stryker.conf.js file.`); - expect(log.debug).calledWith(`Tried ${absoluteGlobbing} on files: ${filesStringified}.`); + }). Please specify the files (glob patterns) containing your tests in mochaOptions.files in your stryker.conf.js file.`); + expect(testInjector.logger.debug).calledWith(`Tried ${absoluteGlobbing} on files: ${filesStringified}.`); }); async function actRun(options: RunOptions = { timeout: 0 }) { @@ -166,10 +168,7 @@ describe('MochaTestRunner', () => { function actAssertMatchedPatterns(relativeGlobPatterns: string | string[] | undefined, expectedGlobPatterns: string[]) { const expectedFiles = ['foo.js', 'bar.js']; multimatchStub.returns(['foo.js']); - sut = new MochaTestRunner(runnerOptions({ - fileNames: expectedFiles, - strykerOptions: factory.strykerOptions({ mochaOptions: { files: relativeGlobPatterns } }) - })); + sut = createSut({ fileNames: expectedFiles, mochaOptions: { files: relativeGlobPatterns } }); sut.init(); expect(multimatchStub).calledWith(expectedFiles, expectedGlobPatterns); } diff --git a/packages/stryker-test-helpers/src/TestInjector.ts b/packages/stryker-test-helpers/src/TestInjector.ts index 2c749937cf..d59e1ca275 100644 --- a/packages/stryker-test-helpers/src/TestInjector.ts +++ b/packages/stryker-test-helpers/src/TestInjector.ts @@ -16,16 +16,15 @@ class TestInjector { } private readonly provideConfig = () => { const config = new Config(); - config.set(this.provideOptions()); + config.set(this.options); return config; } - private readonly provideOptions = () => { - return factory.strykerOptions(this.options); + return this.options; } public pluginResolver: sinon.SinonStubbedInstance; - public options: Partial; + public options: StrykerOptions; public logger: sinon.SinonStubbedInstance; public injector: Injector = rootInjector .provideValue(commonTokens.getLogger, this.provideLogger) @@ -35,7 +34,7 @@ class TestInjector { .provideFactory(commonTokens.pluginResolver, this.providePluginResolver, Scope.Transient); public reset() { - this.options = {}; + this.options = factory.strykerOptions(); this.logger = factory.logger(); this.pluginResolver = { resolve: sinon.stub(), diff --git a/packages/stryker-wct-runner/package.json b/packages/stryker-wct-runner/package.json index 0ab7ad9fa1..7ebdd276a4 100644 --- a/packages/stryker-wct-runner/package.json +++ b/packages/stryker-wct-runner/package.json @@ -23,12 +23,14 @@ "publishConfig": { "access": "public" }, + "dependencies": { + "stryker-api": "^0.23.0" + }, "devDependencies": { "@types/socket.io": "^2.1.0", - "stryker-api": "^0.21.5" + "@stryker-mutator/test-helpers": "0.0.0" }, "peerDependencies": { - "stryker-api": ">=0.18.0 <0.24.0", "web-component-tester": "^6.9.0" }, "author": "Nico Jansen ", @@ -42,4 +44,4 @@ "maxConcurrentTestRunners": 4, "coverageAnalysis": "off" } -} +} \ No newline at end of file diff --git a/packages/stryker-wct-runner/src/WctTestRunner.ts b/packages/stryker-wct-runner/src/WctTestRunner.ts index 7a4c87edef..34fa5fbc43 100644 --- a/packages/stryker-wct-runner/src/WctTestRunner.ts +++ b/packages/stryker-wct-runner/src/WctTestRunner.ts @@ -4,7 +4,8 @@ import { steps } from 'web-component-tester'; import { StrykerOptions } from 'stryker-api/core'; import WctReporter from './WctReporter'; import WctLogger from './WctLogger'; -import { getLogger } from 'stryker-api/logging'; +import { Logger } from 'stryker-api/logging'; +import { tokens, commonTokens } from 'stryker-api/plugin'; const WCT_PACKAGE = 'web-component-tester'; const FORCED_WCT_OPTIONS = Object.freeze({ persistent: false @@ -15,20 +16,20 @@ export default class WctTestRunner implements TestRunner { private readonly reporter: WctReporter; private readonly context: Context; private readonly logger: WctLogger; - private readonly log = getLogger(WctTestRunner.name); - constructor(runnerOptions: { strykerOptions: StrykerOptions }) { - if (runnerOptions.strykerOptions.coverageAnalysis !== 'off') { - throw new Error(`Coverage analysis "${runnerOptions.strykerOptions.coverageAnalysis}" is not (yet) supported by the WCT test runner plugin. Please set \`coverageAnalysis: "off"\` in your stryker.conf.js file.`); + public static inject = tokens(commonTokens.logger, commonTokens.options); + constructor(private readonly log: Logger, options: StrykerOptions) { + if (options.coverageAnalysis !== 'off') { + throw new Error(`Coverage analysis "${options.coverageAnalysis}" is not (yet) supported by the WCT test runner plugin. Please set \`coverageAnalysis: "off"\` in your stryker.conf.js file.`); } this.log.debug('Running wct version %s from %s', require(`${WCT_PACKAGE}/package.json`).version, require.resolve(WCT_PACKAGE)); - this.context = this.loadContext(runnerOptions); + this.context = this.loadContext(options); this.logger = new WctLogger(this.context, this.context.options.verbose || false); this.reporter = new WctReporter(this.context); } - private loadContext(runnerOptions: { strykerOptions: StrykerOptions }) { - const context = new Context(Object.assign({}, runnerOptions.strykerOptions.wct, FORCED_WCT_OPTIONS)); + private loadContext(options: StrykerOptions) { + const context = new Context(Object.assign({}, options.wct, FORCED_WCT_OPTIONS)); if (this.log.isDebugEnabled()) { this.log.debug(`WCT options: %s`, JSON.stringify(this.context.options)); } diff --git a/packages/stryker-wct-runner/src/index.ts b/packages/stryker-wct-runner/src/index.ts index 67ca92df27..eb6c7a56a7 100644 --- a/packages/stryker-wct-runner/src/index.ts +++ b/packages/stryker-wct-runner/src/index.ts @@ -1,4 +1,6 @@ -import { TestRunnerFactory } from 'stryker-api/test_runner'; import WctTestRunner from './WctTestRunner'; +import { declareClassPlugin, PluginKind } from 'stryker-api/plugin'; -TestRunnerFactory.instance().register('wct', WctTestRunner); +export const strykerPlugins = [ + declareClassPlugin(PluginKind.TestRunner, 'wct', WctTestRunner) +]; diff --git a/packages/stryker-wct-runner/test/integration/WctTestRunnerIT.ts b/packages/stryker-wct-runner/test/integration/WctTestRunnerIT.ts index 22b3621d13..61d1cafd45 100644 --- a/packages/stryker-wct-runner/test/integration/WctTestRunnerIT.ts +++ b/packages/stryker-wct-runner/test/integration/WctTestRunnerIT.ts @@ -2,7 +2,7 @@ import * as path from 'path'; import WctTestRunner from '../../src/WctTestRunner'; import { expect } from 'chai'; import { RunResult, TestStatus, RunStatus, TestResult } from 'stryker-api/test_runner'; -import { StrykerOptions } from 'stryker-api/core'; +import { testInjector } from '@stryker-mutator/test-helpers'; type TimelessRunResult = { [K in keyof RunResult]: RunResult[K] extends TestResult[] ? TimelessTestResult[] : RunResult[K]; @@ -15,9 +15,12 @@ describe('WctTestRunner integration', () => { // The "root" wct configuration option is always loaded from the current directory. // In order to test it properly, we need to grab it before- and reset it after each test. let cwd: string; - let settings: { strykerOptions: StrykerOptions }; const root = path.resolve(__dirname, '..', '..', '..', '..'); + function createSut(): WctTestRunner { + return testInjector.injector.injectClass(WctTestRunner); + } + const expectedHtmlSuiteResult: TimelessRunResult = { status: RunStatus.Complete, tests: [ @@ -30,7 +33,7 @@ describe('WctTestRunner integration', () => { beforeEach(() => { cwd = process.cwd(); - settings = { strykerOptions: { coverageAnalysis: 'off' } }; + testInjector.options.coverageAnalysis = 'off'; }); afterEach(() => { @@ -40,12 +43,12 @@ describe('WctTestRunner integration', () => { it('should run in an html suite with root configuration option', async () => { // Arrange const wctConfigFile = path.resolve(__dirname, '..', '..', 'testResources', 'htmlTestSuite', 'wct.conf.json'); - settings.strykerOptions.wct = { + testInjector.options.wct = { configFile: wctConfigFile, persistent: true, // should be forced to false root }; - const sut = new WctTestRunner(settings); + const sut = createSut(); // Act await sut.init(); @@ -58,7 +61,7 @@ describe('WctTestRunner integration', () => { it('should be able to run twice in quick succession (with cwd)', async () => { // Arrange process.chdir(path.resolve(__dirname, '..', '..', 'testResources', 'htmlTestSuite')); - const sut = new WctTestRunner(settings); + const sut = createSut(); // Act await sut.init(); @@ -72,11 +75,11 @@ describe('WctTestRunner integration', () => { it('should run in a js suite', async () => { // Arrange const wctConfigFile = path.resolve(__dirname, '..', '..', 'testResources', 'jsTestSuite', 'wct.conf.json'); - settings.strykerOptions.wct = { + testInjector.options.wct = { configFile: wctConfigFile, root }; - const sut = new WctTestRunner(settings); + const sut = createSut(); const expectedResult: TimelessRunResult = { status: RunStatus.Complete, tests: [{ name: 'AwesomeLib is awesome', status: TestStatus.Success, failureMessages: undefined }] @@ -93,11 +96,11 @@ describe('WctTestRunner integration', () => { it('should fail with ~~error~~ _failed_ if a suite is garbage', async () => { // Arrange const wctConfigFile = path.resolve(__dirname, '..', '..', 'testResources', 'garbage', 'wct.conf.json'); - settings.strykerOptions.wct = { + testInjector.options.wct = { configFile: wctConfigFile, root }; - const sut = new WctTestRunner(settings); + const sut = createSut(); const expectedResult: TimelessRunResult = { status: RunStatus.Complete, // We want to actually expect an error here, but wct doesn't let is. tests: [{ name: '', status: TestStatus.Failed, failureMessages: ['Random error\n at /components/stryker-parent/packages/stryker-wct-runner/testResources/garbage/test/gargbage-tests.js:1'] }] diff --git a/packages/stryker-wct-runner/test/unit/WctTestRunnerSpec.ts b/packages/stryker-wct-runner/test/unit/WctTestRunnerSpec.ts index bd9b3c5567..ecafce62d9 100644 --- a/packages/stryker-wct-runner/test/unit/WctTestRunnerSpec.ts +++ b/packages/stryker-wct-runner/test/unit/WctTestRunnerSpec.ts @@ -7,7 +7,7 @@ import WctLogger, * as wctLoggerModule from '../../src/WctLogger'; import WctReporter, * as wctReporterModule from '../../src/WctReporter'; import { expect } from 'chai'; import { TestStatus, RunResult, RunStatus } from 'stryker-api/test_runner'; -import { StrykerOptions } from 'stryker-api/core'; +import { testInjector } from '@stryker-mutator/test-helpers'; describe(WctTestRunner.name, () => { @@ -15,7 +15,10 @@ describe(WctTestRunner.name, () => { let wctLoggerMock: sinon.SinonStubbedInstance; let wctReporterMock: sinon.SinonStubbedInstance; let stepsMock: sinon.SinonStubbedInstance; - let options: { strykerOptions: StrykerOptions }; + + function createSut(): WctTestRunner { + return testInjector.injector.injectClass(WctTestRunner); + } beforeEach(() => { contextMock = sinon.createStubInstance(contextModule.Context); @@ -27,7 +30,7 @@ describe(WctTestRunner.name, () => { runTests: sinon.stub(), setupOverrides: sinon.stub() }; - options = { strykerOptions: { coverageAnalysis: 'off' } }; + testInjector.options.coverageAnalysis = 'off'; contextMock.options = {}; wctLoggerMock = sinon.createStubInstance(WctLogger); wctReporterMock = sinon.createStubInstance(WctReporter); @@ -40,34 +43,34 @@ describe(WctTestRunner.name, () => { it('should create a context using wct options, with `persistent = false`', () => { const wctOptions = { foo: 'bar', persistent: true }; const expectedWctOptions = { foo: 'bar', persistent: false }; - options.strykerOptions.wct = wctOptions; - new WctTestRunner(options); + testInjector.options.wct = wctOptions; + createSut(); expect(contextModule.Context).calledWithNew; expect(contextModule.Context).calledWith(expectedWctOptions); }); it('should create a reporter', () => { - new WctTestRunner(options); + createSut(); expect(wctReporterModule.default).calledWithNew; expect(wctReporterModule.default).calledWith(contextMock); }); it('should create a logger', () => { contextMock.options.verbose = true; - new WctTestRunner(options); + createSut(); expect(wctLoggerModule.default).calledWithNew; expect(wctLoggerModule.default).calledWith(contextMock, true); }); it('should throw when coverageAnalysis != "off"', () => { - options.strykerOptions.coverageAnalysis = 'all'; + testInjector.options.coverageAnalysis = 'all'; const expectedError = 'Coverage analysis "all" is not (yet) supported by the WCT test runner plugin. Please set `coverageAnalysis: "off"` in your stryker.conf.js file.'; - expect(() => new WctTestRunner(options)).throws(expectedError); + expect(() => createSut()).throws(expectedError); }); describe('init', () => { it('should run initialization steps', async () => { - const sut = new WctTestRunner(options); + const sut = createSut(); await sut.init(); expect(stepsMock.setupOverrides).calledBefore(stepsMock.loadPlugins); expect(stepsMock.loadPlugins).calledBefore(stepsMock.configure); @@ -81,7 +84,7 @@ describe(WctTestRunner.name, () => { describe('dispose', () => { it('should run dispose the logger and reporter', () => { - const sut = new WctTestRunner(options); + const sut = createSut(); sut.dispose(); expect(wctLoggerMock.dispose).called; expect(wctReporterMock.dispose).called; @@ -92,7 +95,7 @@ describe(WctTestRunner.name, () => { it('should run clear tests', async () => { stepsMock.runTests.resolves(); - const sut = new WctTestRunner(options); + const sut = createSut(); wctReporterMock.results = [{ name: 'foobar', status: TestStatus.Success, timeSpentMs: 4 }]; const actual = await sut.run(); const expectedRunResult: RunResult = { status: RunStatus.Complete, tests: [] }; @@ -101,7 +104,7 @@ describe(WctTestRunner.name, () => { it('should run tests', async () => { stepsMock.runTests.resolves(); - const sut = new WctTestRunner(options); + const sut = createSut(); const runPromise = sut.run(); const expectedTests = wctReporterMock.results = [{ name: 'foobar', status: TestStatus.Success, timeSpentMs: 4 }]; const actual = await runPromise; @@ -111,7 +114,7 @@ describe(WctTestRunner.name, () => { it('should ignore errors from failed tests', async () => { stepsMock.runTests.rejects(new Error('23 failed tests')); - const sut = new WctTestRunner(options); + const sut = createSut(); const runPromise = sut.run(); const expectedTests = wctReporterMock.results = [{ name: 'foobar', status: TestStatus.Failed, timeSpentMs: 4 }]; const actual = await runPromise; @@ -122,7 +125,7 @@ describe(WctTestRunner.name, () => { it('should not ignore other errors', async () => { const expectedError = new Error('Foobar Error'); stepsMock.runTests.rejects(expectedError); - const sut = new WctTestRunner(options); + const sut = createSut(); const actualResult = await sut.run(); expect(actualResult.status).eq(RunStatus.Error); expect(actualResult.errorMessages).deep.eq([expectedError.stack]); diff --git a/packages/stryker-wct-runner/tsconfig.test.json b/packages/stryker-wct-runner/tsconfig.test.json index ae868a7db3..af856805d9 100644 --- a/packages/stryker-wct-runner/tsconfig.test.json +++ b/packages/stryker-wct-runner/tsconfig.test.json @@ -20,6 +20,9 @@ "references": [ { "path": "tsconfig.src.json" + }, + { + "path": "../stryker-test-helpers/tsconfig.src.json" } ] } \ No newline at end of file diff --git a/packages/stryker-webpack-transpiler/package.json b/packages/stryker-webpack-transpiler/package.json index a0e6d88f75..18f6c54cdf 100644 --- a/packages/stryker-webpack-transpiler/package.json +++ b/packages/stryker-webpack-transpiler/package.json @@ -37,7 +37,6 @@ "@types/memory-fs": "~0.3.0", "@types/webpack": "~4.4.12", "raw-loader": "~1.0.0", - "stryker-api": "^0.23.0", "webpack": "~4.29.1" }, "peerDependencies": { diff --git a/packages/stryker/package.json b/packages/stryker/package.json index bee632867d..3fa0c2465a 100644 --- a/packages/stryker/package.json +++ b/packages/stryker/package.json @@ -70,6 +70,7 @@ "rxjs": "~6.3.0", "source-map": "~0.6.1", "surrial": "^0.2.0", + "stryker-api": "^0.23.0", "tree-kill": "~1.2.0", "tslib": "~1.9.3", "typed-inject": "^0.2.0", @@ -82,10 +83,6 @@ "@types/istanbul-lib-instrument": "~1.7.0", "@types/node": "~10.11.4", "@types/prettier": "~1.13.1", - "@types/progress": "~2.0.1", - "stryker-api": "^0.23.0" - }, - "peerDependencies": { - "stryker-api": ">=0.18.0 <0.24.0" + "@types/progress": "~2.0.1" } } diff --git a/packages/stryker/src/Sandbox.ts b/packages/stryker/src/Sandbox.ts index d67b91221a..8e44f744d7 100644 --- a/packages/stryker/src/Sandbox.ts +++ b/packages/stryker/src/Sandbox.ts @@ -2,11 +2,10 @@ import { StrykerOptions } from 'stryker-api/core'; import * as path from 'path'; import { getLogger } from 'stryker-api/logging'; import * as mkdirp from 'mkdirp'; -import { RunResult, RunnerOptions } from 'stryker-api/test_runner'; +import { RunResult, TestRunner } from 'stryker-api/test_runner'; import { File } from 'stryker-api/core'; import { TestFramework } from 'stryker-api/test_framework'; import { wrapInClosure, normalizeWhiteSpaces } from './utils/objectUtils'; -import TestRunnerDecorator from './test-runner/TestRunnerDecorator'; import ResilientTestRunnerFactory from './test-runner/ResilientTestRunnerFactory'; import { TempFolder } from './utils/TempFolder'; import { writeFile, findNodeModules, symlinkJunction } from './utils/fileUtils'; @@ -21,7 +20,7 @@ interface FileMap { export default class Sandbox { private readonly log = getLogger(Sandbox.name); - private testRunner: TestRunnerDecorator; + private testRunner: Required; private fileMap: FileMap; private readonly files: File[]; private readonly workingDirectory: string; @@ -112,14 +111,11 @@ export default class Sandbox { return writeFile(targetFile, file.content); } - private initializeTestRunner(): Promise { - const settings: RunnerOptions = { - fileNames: Object.keys(this.fileMap).map(sourceFileName => this.fileMap[sourceFileName]), - strykerOptions: this.options, - }; + private async initializeTestRunner(): Promise { + const fileNames = Object.keys(this.fileMap).map(sourceFileName => this.fileMap[sourceFileName]); this.log.debug(`Creating test runner %s using settings {port: %s}`, this.index); - this.testRunner = ResilientTestRunnerFactory.create(settings.strykerOptions.testRunner || '', settings, this.workingDirectory, this.loggingContext); - return this.testRunner.init(); + this.testRunner = ResilientTestRunnerFactory.create(this.options, fileNames, this.workingDirectory, this.loggingContext); + await this.testRunner.init(); } private calculateTimeout(mutant: TestableMutant) { diff --git a/packages/stryker/src/SandboxPool.ts b/packages/stryker/src/SandboxPool.ts index cfca076172..216a90c21c 100644 --- a/packages/stryker/src/SandboxPool.ts +++ b/packages/stryker/src/SandboxPool.ts @@ -2,20 +2,30 @@ import { getLogger } from 'stryker-api/logging'; import * as os from 'os'; import { Observable, range } from 'rxjs'; import { flatMap } from 'rxjs/operators'; -import { Config } from 'stryker-api/config'; -import { File } from 'stryker-api/core'; +import { File, StrykerOptions } from 'stryker-api/core'; import { TestFramework } from 'stryker-api/test_framework'; import Sandbox from './Sandbox'; import LoggingClientContext from './logging/LoggingClientContext'; +import { tokens, commonTokens } from 'stryker-api/plugin'; +import { coreTokens } from './di'; +import { InitialTestRunResult } from './process/InitialTestExecutor'; -export default class SandboxPool { +export class SandboxPool { private readonly log = getLogger(SandboxPool.name); private readonly sandboxes: Promise[] = []; + private readonly overheadTimeMS: number; - constructor(private readonly options: Config, private readonly testFramework: TestFramework | null, private readonly initialFiles: ReadonlyArray, private readonly overheadTimeMS: number, private readonly loggingContext: LoggingClientContext) { } + public static inject = tokens(commonTokens.options, coreTokens.testFramework, coreTokens.initialRunResult, coreTokens.loggingContext); + constructor( + private readonly options: StrykerOptions, + private readonly testFramework: TestFramework | null, + initialRunResult: InitialTestRunResult, + private readonly loggingContext: LoggingClientContext) { + this.overheadTimeMS = initialRunResult.overheadTimeMS; + } - public streamSandboxes(): Observable { + public streamSandboxes(initialFiles: ReadonlyArray): Observable { let numConcurrentRunners = os.cpus().length; if (this.options.transpilers.length) { // If transpilers are configured, one core is reserved for the compiler (for now) @@ -32,7 +42,7 @@ export default class SandboxPool { this.log.info(`Creating ${numConcurrentRunners} test runners (based on ${numConcurrentRunnersSource})`); const sandboxes = range(0, numConcurrentRunners) - .pipe(flatMap(n => this.registerSandbox(Sandbox.create(this.options, n, this.initialFiles, this.testFramework, this.overheadTimeMS, this.loggingContext)))); + .pipe(flatMap(n => this.registerSandbox(Sandbox.create(this.options, n, initialFiles, this.testFramework, this.overheadTimeMS, this.loggingContext)))); return sandboxes; } diff --git a/packages/stryker/src/Stryker.ts b/packages/stryker/src/Stryker.ts index 78dcc45156..015da4db50 100644 --- a/packages/stryker/src/Stryker.ts +++ b/packages/stryker/src/Stryker.ts @@ -9,12 +9,14 @@ import { isPromise } from './utils/objectUtils'; import { TempFolder } from './utils/TempFolder'; import { MutatorFacade } from './mutants/MutatorFacade'; import InitialTestExecutor from './process/InitialTestExecutor'; -import MutationTestExecutor from './process/MutationTestExecutor'; +import { MutationTestExecutor } from './process/MutationTestExecutor'; import LogConfigurator from './logging/LogConfigurator'; import { Injector } from 'typed-inject'; import { TranspilerFacade } from './transpiler/TranspilerFacade'; import { coreTokens, MainContext, PluginCreator, buildMainInjector } from './di'; import { commonTokens, PluginKind } from 'stryker-api/plugin'; +import MutantTranspiler from './transpiler/MutantTranspiler'; +import { SandboxPool } from './SandboxPool'; export default class Stryker { @@ -53,27 +55,24 @@ export default class Stryker { if (inputFiles.files.length) { TempFolder.instance().initialize(); const inputFileInjector = this.injector + .provideValue(coreTokens.loggingContext, loggingContext) .provideValue(coreTokens.inputFiles, inputFiles); const initialTestRunProcess = inputFileInjector - .provideValue(coreTokens.loggingContext, loggingContext) .provideValue(commonTokens.produceSourceMaps, this.config.coverageAnalysis !== 'off') .provideFactory(coreTokens.pluginCreatorTranspiler, PluginCreator.createFactory(PluginKind.Transpiler)) .provideClass(coreTokens.transpiler, TranspilerFacade) .injectClass(InitialTestExecutor); const initialRunResult = await initialTestRunProcess.run(); const mutator = inputFileInjector.injectClass(MutatorFacade); - const testableMutants = await inputFileInjector + const mutationTestProcessInjector = inputFileInjector .provideValue(coreTokens.initialRunResult, initialRunResult) + .provideClass(coreTokens.mutantTranspiler, MutantTranspiler) + .provideClass(coreTokens.sandboxPool, SandboxPool); + const testableMutants = await mutationTestProcessInjector .injectClass(MutantTestMatcher) .matchWithMutants(mutator.mutate(inputFiles.filesToMutate)); if (initialRunResult.runResult.tests.length && testableMutants.length) { - const mutationTestExecutor = new MutationTestExecutor( - this.config, - inputFiles.files, - this.injector.resolve(coreTokens.testFramework), - this.reporter, - initialRunResult.overheadTimeMS, - loggingContext); + const mutationTestExecutor = mutationTestProcessInjector.injectClass(MutationTestExecutor); const mutantResults = await mutationTestExecutor.run(testableMutants); this.reportScore(mutantResults); await this.wrapUpReporter(); diff --git a/packages/stryker/src/di/coreTokens.ts b/packages/stryker/src/di/coreTokens.ts index 1ee5c5c24f..40f750c181 100644 --- a/packages/stryker/src/di/coreTokens.ts +++ b/packages/stryker/src/di/coreTokens.ts @@ -4,6 +4,8 @@ export const configReadFromConfigFile = 'configReadFromConfigFile'; export const configEditorApplier = 'configEditorApplier'; export const inputFiles = 'inputFiles'; export const initialRunResult = 'initialRunResult'; +export const mutantTranspiler = 'mutantTranspiler'; +export const sandboxPool = 'sandboxPool'; export const testFramework = 'testFramework'; export const timer = 'timer'; export const loggingContext = 'loggingContext'; diff --git a/packages/stryker/src/process/MutationTestExecutor.ts b/packages/stryker/src/process/MutationTestExecutor.ts index 152db1ac58..6d499b6a6f 100644 --- a/packages/stryker/src/process/MutationTestExecutor.ts +++ b/packages/stryker/src/process/MutationTestExecutor.ts @@ -1,40 +1,40 @@ import { Observable, Observer, merge, zip } from 'rxjs'; import { flatMap, map, tap, toArray } from 'rxjs/operators'; -import { Config } from 'stryker-api/config'; -import { File } from 'stryker-api/core'; import { MutantResult, MutantStatus } from 'stryker-api/report'; -import { TestFramework } from 'stryker-api/test_framework'; import { RunResult, RunStatus, TestStatus } from 'stryker-api/test_runner'; import Sandbox from '../Sandbox'; -import SandboxPool from '../SandboxPool'; +import { SandboxPool } from '../SandboxPool'; import TestableMutant from '../TestableMutant'; import TranspiledMutant from '../TranspiledMutant'; import StrictReporter from '../reporters/StrictReporter'; import MutantTranspiler from '../transpiler/MutantTranspiler'; -import LoggingClientContext from '../logging/LoggingClientContext'; -import { getLogger } from 'stryker-api/logging'; - -export default class MutationTestExecutor { - private readonly log = getLogger(MutationTestExecutor.name); +import { Logger } from 'stryker-api/logging'; +import { tokens, commonTokens } from 'stryker-api/plugin'; +import { coreTokens } from '../di'; +import InputFileCollection from '../input/InputFileCollection'; +export class MutationTestExecutor { + public static inject = tokens( + commonTokens.logger, + coreTokens.inputFiles, + coreTokens.reporter, + coreTokens.mutantTranspiler, + coreTokens.sandboxPool); constructor( - private readonly config: Config, - private readonly inputFiles: ReadonlyArray, - private readonly testFramework: TestFramework | null, + private readonly log: Logger, + private readonly input: InputFileCollection, private readonly reporter: StrictReporter, - private readonly overheadTimeMS: number, - private readonly loggingContext: LoggingClientContext) { + private readonly mutantTranspiler: MutantTranspiler, + private readonly sandboxPool: SandboxPool) { } public async run(allMutants: ReadonlyArray): Promise { - const mutantTranspiler = new MutantTranspiler(this.config, this.loggingContext); - const transpiledFiles = await mutantTranspiler.initialize(this.inputFiles); - const sandboxPool = new SandboxPool(this.config, this.testFramework, transpiledFiles, this.overheadTimeMS, this.loggingContext); + const transpiledFiles = await this.mutantTranspiler.initialize(this.input.files); const result = await this.runInsideSandboxes( - sandboxPool.streamSandboxes(), - mutantTranspiler.transpileMutants(allMutants)); - await sandboxPool.disposeAll(); - mutantTranspiler.dispose(); + this.sandboxPool.streamSandboxes(transpiledFiles), + this.mutantTranspiler.transpileMutants(allMutants)); + await this.sandboxPool.disposeAll(); + this.mutantTranspiler.dispose(); return result; } diff --git a/packages/stryker/src/test-runner/ChildProcessTestRunnerDecorator.ts b/packages/stryker/src/test-runner/ChildProcessTestRunnerDecorator.ts index 707352e1a6..a42888a5c3 100644 --- a/packages/stryker/src/test-runner/ChildProcessTestRunnerDecorator.ts +++ b/packages/stryker/src/test-runner/ChildProcessTestRunnerDecorator.ts @@ -1,30 +1,30 @@ -import { TestRunner, RunResult, RunOptions, RunnerOptions } from 'stryker-api/test_runner'; +import { TestRunner, RunResult, RunOptions } from 'stryker-api/test_runner'; import LoggingClientContext from '../logging/LoggingClientContext'; import ChildProcessProxy from '../child-proxy/ChildProcessProxy'; import { ChildProcessTestRunnerWorker } from './ChildProcessTestRunnerWorker'; import { timeout } from '../utils/objectUtils'; import ChildProcessCrashedError from '../child-proxy/ChildProcessCrashedError'; +import { StrykerOptions } from 'stryker-api/core'; const MAX_WAIT_FOR_DISPOSE = 2000; /** * Runs the given test runner in a child process and forwards reports about test results - * Also implements timeout-mechanism (on timeout, restart the child runner and report timeout) */ export default class ChildProcessTestRunnerDecorator implements TestRunner { private readonly worker: ChildProcessProxy; constructor( - realTestRunnerName: string, - options: RunnerOptions, + options: StrykerOptions, + sandboxFileNames: ReadonlyArray, sandboxWorkingDirectory: string, loggingContext: LoggingClientContext) { this.worker = ChildProcessProxy.create( require.resolve(`./${ChildProcessTestRunnerWorker.name}`), loggingContext, - options.strykerOptions, - { realTestRunnerName, runnerOptions: options }, + options, + { sandboxFileNames }, sandboxWorkingDirectory, ChildProcessTestRunnerWorker); } diff --git a/packages/stryker/src/test-runner/ChildProcessTestRunnerWorker.ts b/packages/stryker/src/test-runner/ChildProcessTestRunnerWorker.ts index 83d932baba..fc239fef0b 100644 --- a/packages/stryker/src/test-runner/ChildProcessTestRunnerWorker.ts +++ b/packages/stryker/src/test-runner/ChildProcessTestRunnerWorker.ts @@ -1,14 +1,19 @@ -import { TestRunner, TestRunnerFactory, RunnerOptions, RunOptions } from 'stryker-api/test_runner'; +import { TestRunner, RunOptions } from 'stryker-api/test_runner'; import { errorToString } from '@stryker-mutator/util'; -import { tokens, commonTokens, PluginResolver } from 'stryker-api/plugin'; +import { tokens, commonTokens, PluginKind, Injector, OptionsContext } from 'stryker-api/plugin'; +import { PluginCreator } from '../di'; +import { StrykerOptions } from 'stryker-api/core'; export class ChildProcessTestRunnerWorker implements TestRunner { private readonly underlyingTestRunner: TestRunner; - public static inject = tokens('realTestRunnerName', 'runnerOptions', commonTokens.pluginResolver); - constructor(realTestRunnerName: string, options: RunnerOptions, _: PluginResolver) { - this.underlyingTestRunner = TestRunnerFactory.instance().create(realTestRunnerName, options); + public static inject = tokens(commonTokens.sandboxFileNames, commonTokens.options, commonTokens.injector); + constructor(sandboxFileNames: ReadonlyArray, { testRunner }: StrykerOptions, injector: Injector) { + this.underlyingTestRunner = injector + .provideValue(commonTokens.sandboxFileNames, sandboxFileNames) + .injectFunction(PluginCreator.createFactory(PluginKind.TestRunner)) + .create(testRunner); } public async init(): Promise { diff --git a/packages/stryker/src/test-runner/CommandTestRunner.ts b/packages/stryker/src/test-runner/CommandTestRunner.ts index 2ae75dfbed..e9a0b79c62 100644 --- a/packages/stryker/src/test-runner/CommandTestRunner.ts +++ b/packages/stryker/src/test-runner/CommandTestRunner.ts @@ -1,9 +1,10 @@ import * as os from 'os'; -import { TestRunner, RunResult, RunStatus, TestStatus, RunnerOptions } from 'stryker-api/test_runner'; +import { TestRunner, RunResult, RunStatus, TestStatus } from 'stryker-api/test_runner'; import { exec } from 'child_process'; import { kill } from '../utils/objectUtils'; import Timer from '../utils/Timer'; import { errorToString } from '@stryker-mutator/util'; +import { StrykerOptions } from 'stryker-api/core'; export interface CommandRunnerSettings { command: string; @@ -34,10 +35,10 @@ export default class CommandTestRunner implements TestRunner { private timeoutHandler: undefined | (() => Promise); - constructor(private readonly workingDir: string, options: RunnerOptions) { + constructor(private readonly workingDir: string, options: StrykerOptions) { this.settings = Object.assign({ command: 'npm test' - }, options.strykerOptions.commandRunner); + }, options.commandRunner); } public run(): Promise { diff --git a/packages/stryker/src/test-runner/ResilientTestRunnerFactory.ts b/packages/stryker/src/test-runner/ResilientTestRunnerFactory.ts index 32f97421d3..da02d1d121 100644 --- a/packages/stryker/src/test-runner/ResilientTestRunnerFactory.ts +++ b/packages/stryker/src/test-runner/ResilientTestRunnerFactory.ts @@ -1,18 +1,18 @@ import ChildProcessTestRunnerDecorator from './ChildProcessTestRunnerDecorator'; import TimeoutDecorator from './TimeoutDecorator'; import RetryDecorator from './RetryDecorator'; -import TestRunnerDecorator from './TestRunnerDecorator'; import LoggingClientContext from '../logging/LoggingClientContext'; -import { RunnerOptions } from 'stryker-api/test_runner'; +import { TestRunner } from 'stryker-api/test_runner'; import CommandTestRunner from '../test-runner/CommandTestRunner'; +import { StrykerOptions } from 'stryker-api/core'; export default { - create(testRunnerName: string, settings: RunnerOptions, sandboxWorkingDirectory: string, loggingContext: LoggingClientContext): TestRunnerDecorator { - if (CommandTestRunner.is(testRunnerName)) { - return new RetryDecorator(() => new TimeoutDecorator(() => new CommandTestRunner(sandboxWorkingDirectory, settings))); + create(options: StrykerOptions, sandboxFileNames: ReadonlyArray, sandboxWorkingDirectory: string, loggingContext: LoggingClientContext): Required { + if (CommandTestRunner.is(options.testRunner)) { + return new RetryDecorator(() => new TimeoutDecorator(() => new CommandTestRunner(sandboxWorkingDirectory, options))); } else { return new RetryDecorator(() => - new TimeoutDecorator(() => new ChildProcessTestRunnerDecorator(testRunnerName, settings, sandboxWorkingDirectory, loggingContext))); + new TimeoutDecorator(() => new ChildProcessTestRunnerDecorator(options, sandboxFileNames, sandboxWorkingDirectory, loggingContext))); } } }; diff --git a/packages/stryker/src/test-runner/TestRunnerDecorator.ts b/packages/stryker/src/test-runner/TestRunnerDecorator.ts index 74600c5cf8..51bef6bbc2 100644 --- a/packages/stryker/src/test-runner/TestRunnerDecorator.ts +++ b/packages/stryker/src/test-runner/TestRunnerDecorator.ts @@ -1,6 +1,6 @@ import { TestRunner, RunOptions, RunResult } from 'stryker-api/test_runner'; -export default class TestRunnerDecorator implements TestRunner { +export default class TestRunnerDecorator implements Required { protected innerRunner: TestRunner; constructor(private readonly testRunnerProducer: () => TestRunner) { diff --git a/packages/stryker/test/integration/command-test-runner/CommandTestRunner.it.ts b/packages/stryker/test/integration/command-test-runner/CommandTestRunner.it.ts index 38d075c3b5..998d0e6f7b 100644 --- a/packages/stryker/test/integration/command-test-runner/CommandTestRunner.it.ts +++ b/packages/stryker/test/integration/command-test-runner/CommandTestRunner.it.ts @@ -54,6 +54,6 @@ describe(`${CommandTestRunner.name} integration`, () => { commandRunner: settings }); } - return new CommandTestRunner(workingDir, { strykerOptions, fileNames: [] }); + return new CommandTestRunner(workingDir, strykerOptions); } }); diff --git a/packages/stryker/test/integration/test-runner/ResilientTestRunnerFactory.it.ts b/packages/stryker/test/integration/test-runner/ResilientTestRunnerFactory.it.ts index 902391dedc..7bd720510d 100644 --- a/packages/stryker/test/integration/test-runner/ResilientTestRunnerFactory.it.ts +++ b/packages/stryker/test/integration/test-runner/ResilientTestRunnerFactory.it.ts @@ -1,11 +1,10 @@ import * as path from 'path'; import { expect } from 'chai'; import getPort = require('get-port'); -import { RunStatus, RunnerOptions } from 'stryker-api/test_runner'; +import { RunStatus, TestRunner } from 'stryker-api/test_runner'; import * as log4js from 'log4js'; import ResilientTestRunnerFactory from '../../../src/test-runner/ResilientTestRunnerFactory'; -import TestRunnerDecorator from '../../../src/test-runner/TestRunnerDecorator'; -import { LogLevel } from 'stryker-api/core'; +import { LogLevel, StrykerOptions } from 'stryker-api/core'; import LoggingServer from '../../helpers/LoggingServer'; import LoggingClientContext from '../../../src/logging/LoggingClientContext'; import { toArray } from 'rxjs/operators'; @@ -14,8 +13,8 @@ import { strykerOptions } from '../../helpers/producers'; describe('ResilientTestRunnerFactory integration', () => { - let sut: TestRunnerDecorator; - let options: RunnerOptions; + let sut: Required; + let options: StrykerOptions; const sandboxWorkingDirectory = path.resolve('./test/integration/test-runner'); let loggingContext: LoggingClientContext; @@ -27,15 +26,12 @@ describe('ResilientTestRunnerFactory integration', () => { const port = await getPort(); loggingServer = new LoggingServer(port); loggingContext = { port, level: LogLevel.Trace }; - options = { - fileNames: [], - strykerOptions: strykerOptions({ - plugins: [require.resolve('./AdditionalTestRunners')], - someRegex: /someRegex/, - testFramework: 'jasmine', - testRunner: 'karma' - }) - }; + options = strykerOptions({ + plugins: [require.resolve('./AdditionalTestRunners')], + someRegex: /someRegex/, + testFramework: 'jasmine', + testRunner: 'karma' + }); alreadyDisposed = false; }); @@ -47,12 +43,13 @@ describe('ResilientTestRunnerFactory integration', () => { }); function createSut(name: string) { - sut = ResilientTestRunnerFactory.create(name, options, sandboxWorkingDirectory, loggingContext); + options.testRunner = name; + sut = ResilientTestRunnerFactory.create(options, [], sandboxWorkingDirectory, loggingContext); } - function arrangeSut(name: string): Promise { + async function arrangeSut(name: string): Promise { createSut(name); - return sut.init(); + await sut.init(); } function actRun(timeout = 4000) { diff --git a/packages/stryker/test/unit/SandboxPoolSpec.ts b/packages/stryker/test/unit/SandboxPoolSpec.ts index fd1759ba55..7ab8beda30 100644 --- a/packages/stryker/test/unit/SandboxPoolSpec.ts +++ b/packages/stryker/test/unit/SandboxPoolSpec.ts @@ -1,16 +1,19 @@ import { expect } from 'chai'; import * as os from 'os'; import { flatMap, toArray } from 'rxjs/operators'; -import { Config } from 'stryker-api/config'; import { File, LogLevel } from 'stryker-api/core'; import { TestFramework } from 'stryker-api/test_framework'; import Sandbox from '../../src/Sandbox'; -import SandboxPool from '../../src/SandboxPool'; +import {SandboxPool} from '../../src/SandboxPool'; import { Task } from '../../src/utils/Task'; -import { Mock, config, file, mock, testFramework } from '../helpers/producers'; +import { Mock, file, mock, testFramework } from '../helpers/producers'; import LoggingClientContext from '../../src/logging/LoggingClientContext'; import { sleep } from '../helpers/testUtils'; import * as sinon from 'sinon'; +import { testInjector } from '@stryker-mutator/test-helpers'; +import { coreTokens } from '../../src/di'; +import { InitialTestRunResult } from '../../src/process/InitialTestExecutor'; +import { RunStatus } from 'stryker-api/test_runner'; const OVERHEAD_TIME_MS = 42; const LOGGING_CONTEXT: LoggingClientContext = Object.freeze({ @@ -18,17 +21,15 @@ const LOGGING_CONTEXT: LoggingClientContext = Object.freeze({ port: 4200 }); -describe('SandboxPool', () => { +describe(SandboxPool.name, () => { let sut: SandboxPool; let firstSandbox: Mock; let secondSandbox: Mock; - let options: Config; let expectedTestFramework: TestFramework; let expectedInputFiles: File[]; let createStub: sinon.SinonStub; beforeEach(() => { - options = config(); expectedTestFramework = testFramework(); firstSandbox = mock(Sandbox as any); firstSandbox.dispose.resolves(); @@ -42,47 +43,64 @@ describe('SandboxPool', () => { .onCall(1).resolves(secondSandbox); expectedInputFiles = [file()]; - sut = new SandboxPool(options, expectedTestFramework, expectedInputFiles, OVERHEAD_TIME_MS, LOGGING_CONTEXT); + sut = createSut(); }); + function createSut(): SandboxPool { + const initialRunResult: InitialTestRunResult = { + coverageMaps: {}, + overheadTimeMS: OVERHEAD_TIME_MS, + runResult: { tests: [], status: RunStatus.Complete }, + sourceMapper: { + transpiledFileNameFor: n => n, + transpiledLocationFor: n => n + } + }; + return testInjector.injector + .provideValue(coreTokens.testFramework, expectedTestFramework as unknown as TestFramework) + .provideValue(coreTokens.initialRunResult, initialRunResult) + .provideValue(coreTokens.loggingContext, LOGGING_CONTEXT) + .injectClass(SandboxPool); + } + describe('streamSandboxes', () => { it('should use maxConcurrentTestRunners when set', async () => { - options.maxConcurrentTestRunners = 1; - await sut.streamSandboxes().pipe(toArray()).toPromise(); + testInjector.options.maxConcurrentTestRunners = 1; + await sut.streamSandboxes(expectedInputFiles).pipe(toArray()).toPromise(); expect(Sandbox.create).to.have.callCount(1); - expect(Sandbox.create).calledWith(options, 0, expectedInputFiles, expectedTestFramework, OVERHEAD_TIME_MS); + expect(Sandbox.create).calledWith(testInjector.options, 0, expectedInputFiles, expectedTestFramework, OVERHEAD_TIME_MS, LOGGING_CONTEXT); }); it('should use cpuCount when maxConcurrentTestRunners is set too high', async () => { sinon.stub(os, 'cpus').returns([1, 2, 3]); // stub 3 cpus - options.maxConcurrentTestRunners = 100; - const actual = await sut.streamSandboxes().pipe(toArray()).toPromise(); + testInjector.options.maxConcurrentTestRunners = 100; + const actual = await sut.streamSandboxes(expectedInputFiles).pipe(toArray()).toPromise(); expect(actual).lengthOf(3); expect(Sandbox.create).to.have.callCount(3); - expect(Sandbox.create).calledWith(options, 0, expectedInputFiles, expectedTestFramework, OVERHEAD_TIME_MS); + expect(Sandbox.create).calledWith(testInjector.options, 0, expectedInputFiles, expectedTestFramework, OVERHEAD_TIME_MS, LOGGING_CONTEXT); }); it('should use the cpuCount when maxConcurrentTestRunners is <= 0', async () => { sinon.stub(os, 'cpus').returns([1, 2, 3]); // stub 3 cpus - options.maxConcurrentTestRunners = 0; - const actual = await sut.streamSandboxes().pipe(toArray()).toPromise(); + testInjector.options.maxConcurrentTestRunners = 0; + const actual = await sut.streamSandboxes(expectedInputFiles).pipe(toArray()).toPromise(); expect(Sandbox.create).to.have.callCount(3); expect(actual).lengthOf(3); - expect(Sandbox.create).calledWith(options, 0, expectedInputFiles, expectedTestFramework, OVERHEAD_TIME_MS); + expect(Sandbox.create).calledWith(testInjector.options, 0, expectedInputFiles, expectedTestFramework, OVERHEAD_TIME_MS, LOGGING_CONTEXT); }); it('should use the cpuCount - 1 when a transpiler is configured', async () => { - options.transpilers = ['a transpiler']; - options.maxConcurrentTestRunners = 2; + testInjector.options.transpilers = ['a transpiler']; + testInjector.options.maxConcurrentTestRunners = 2; sinon.stub(os, 'cpus').returns([1, 2]); // stub 2 cpus - const actual = await sut.streamSandboxes().pipe(toArray()).toPromise(); + const actual = await sut.streamSandboxes(expectedInputFiles).pipe(toArray()).toPromise(); expect(Sandbox.create).to.have.callCount(1); expect(actual).lengthOf(1); }); }); describe('dispose', () => { it('should have disposed all sandboxes', async () => { - await sut.streamSandboxes().pipe(toArray()).toPromise(); + await sut.streamSandboxes(expectedInputFiles).pipe(toArray()).toPromise(); await sut.disposeAll(); expect(firstSandbox.dispose).called; expect(secondSandbox.dispose).called; @@ -101,7 +119,7 @@ describe('SandboxPool', () => { createStub.onCall(2).returns(task.promise); // promise is not yet resolved const registeredSandboxes: Sandbox[] = []; let disposeAllResolved = false; - await sut.streamSandboxes().pipe(flatMap(async sandbox => { + await sut.streamSandboxes(expectedInputFiles).pipe(flatMap(async sandbox => { if (registeredSandboxes.push(sandbox) === 2) { // Act: The last sandbox will take a while to resolve (it is not yet created) const disposeAllPromise = sut.disposeAll().then(_ => disposeAllResolved = true); diff --git a/packages/stryker/test/unit/SandboxSpec.ts b/packages/stryker/test/unit/SandboxSpec.ts index 4c08f73567..bc45b6cb01 100644 --- a/packages/stryker/test/unit/SandboxSpec.ts +++ b/packages/stryker/test/unit/SandboxSpec.ts @@ -18,7 +18,6 @@ import * as fileUtils from '../../src/utils/fileUtils'; import currentLogMock from '../helpers/logMock'; import TestRunnerDecorator from '../../src/test-runner/TestRunnerDecorator'; import LoggingClientContext from '../../src/logging/LoggingClientContext'; -import { RunnerOptions } from 'stryker-api/test_runner'; const OVERHEAD_TIME_MS = 0; const LOGGING_CONTEXT: LoggingClientContext = Object.freeze({ @@ -84,11 +83,8 @@ describe('Sandbox', () => { it('should have created the isolated test runner', async () => { await Sandbox.create(options, SANDBOX_INDEX, files, null, OVERHEAD_TIME_MS, LOGGING_CONTEXT); - const expectedSettings: RunnerOptions = { - fileNames: [path.resolve('random-folder-3', 'file1'), path.resolve('random-folder-3', 'file2')], - strykerOptions: options - }; - expect(ResilientTestRunnerFactory.create).to.have.been.calledWith(options.testRunner, expectedSettings, sandboxDirectory, LOGGING_CONTEXT); + const expectedFileNames = files.map(file => path.resolve(sandboxDirectory, path.basename(file.name))); + expect(ResilientTestRunnerFactory.create).calledWith(options, expectedFileNames, sandboxDirectory, LOGGING_CONTEXT); }); it('should have created a sandbox folder', async () => { diff --git a/packages/stryker/test/unit/StrykerSpec.ts b/packages/stryker/test/unit/StrykerSpec.ts index 71c5ecb661..5d75afe97f 100644 --- a/packages/stryker/test/unit/StrykerSpec.ts +++ b/packages/stryker/test/unit/StrykerSpec.ts @@ -12,7 +12,7 @@ import * as typedInject from 'typed-inject'; import { MutatorFacade } from '../../src/mutants/MutatorFacade'; import { MutantTestMatcher } from '../../src/mutants/MutantTestMatcher'; import InitialTestExecutor from '../../src/process/InitialTestExecutor'; -import MutationTestExecutor, * as mutationTestExecutor from '../../src/process/MutationTestExecutor'; +import { MutationTestExecutor } from '../../src/process/MutationTestExecutor'; import ScoreResultCalculator, * as scoreResultCalculatorModule from '../../src/ScoreResultCalculator'; import { TempFolder } from '../../src/utils/TempFolder'; import currentLogMock from '../helpers/logMock'; @@ -69,7 +69,6 @@ describe(Stryker.name, () => { tempFolderMock.clean.resolves(); scoreResultCalculator = new ScoreResultCalculator(); sinon.stub(di, 'buildMainInjector').returns(injectorMock); - sinon.stub(mutationTestExecutor, 'default').returns(mutationTestExecutorMock); sinon.stub(TempFolder, 'instance').returns(tempFolderMock); sinon.stub(scoreResultCalculator, 'determineExitCode').returns(sinon.stub()); sinon.stub(scoreResultCalculatorModule, 'default').returns(scoreResultCalculator); @@ -78,7 +77,8 @@ describe(Stryker.name, () => { .withArgs(InitialTestExecutor).returns(initialTestExecutorMock) .withArgs(InputFileResolver).returns(inputFileResolverMock) .withArgs(MutatorFacade).returns(mutatorMock) - .withArgs(MutantTestMatcher).returns(mutantTestMatcherMock); + .withArgs(MutantTestMatcher).returns(mutantTestMatcherMock) + .withArgs(MutationTestExecutor).returns(mutationTestExecutorMock); injectorMock.resolve .withArgs(commonTokens.config).returns(strykerConfig) .withArgs(di.coreTokens.timer).returns(timerMock) @@ -220,8 +220,6 @@ describe(Stryker.name, () => { it('should create the mutation test executor', async () => { sut = new Stryker({}); await sut.runMutationTest(); - expect(mutationTestExecutor.default).calledWithNew; - expect(mutationTestExecutor.default).calledWith(strykerConfig, inputFiles.files, testFrameworkMock, reporterMock, undefined, LOGGING_CONTEXT); expect(mutationTestExecutorMock.run).calledWith(mutants); }); diff --git a/packages/stryker/test/unit/process/MutationTestExecutorSpec.ts b/packages/stryker/test/unit/process/MutationTestExecutorSpec.ts index fb3c538ca1..ca883f90ea 100644 --- a/packages/stryker/test/unit/process/MutationTestExecutorSpec.ts +++ b/packages/stryker/test/unit/process/MutationTestExecutorSpec.ts @@ -1,23 +1,20 @@ import { expect } from 'chai'; import * as _ from 'lodash'; import { empty, of } from 'rxjs'; -import { Config } from 'stryker-api/config'; -import { File, LogLevel } from 'stryker-api/core'; +import { File } from 'stryker-api/core'; import { MutantStatus } from 'stryker-api/report'; -import { TestFramework } from 'stryker-api/test_framework'; import { RunStatus, TestStatus } from 'stryker-api/test_runner'; -import { Logger } from 'stryker-api/logging'; import Sandbox from '../../../src/Sandbox'; -import SandboxPool, * as sandboxPool from '../../../src/SandboxPool'; +import { SandboxPool } from '../../../src/SandboxPool'; import TestableMutant from '../../../src/TestableMutant'; import TranspiledMutant from '../../../src/TranspiledMutant'; -import MutantTestExecutor from '../../../src/process/MutationTestExecutor'; +import { MutationTestExecutor } from '../../../src/process/MutationTestExecutor'; import BroadcastReporter from '../../../src/reporters/BroadcastReporter'; -import MutantTranspiler, * as mutantTranspiler from '../../../src/transpiler/MutantTranspiler'; -import { Mock, config, file, mock, mutantResult, testFramework, testResult, testableMutant, transpiledMutant, runResult } from '../../helpers/producers'; -import LoggingClientContext from '../../../src/logging/LoggingClientContext'; -import currentLogMock from '../../helpers/logMock'; -import * as sinon from 'sinon'; +import MutantTranspiler from '../../../src/transpiler/MutantTranspiler'; +import { Mock, file, mock, mutantResult, testResult, testableMutant, transpiledMutant, runResult } from '../../helpers/producers'; +import { testInjector } from '@stryker-mutator/test-helpers'; +import { coreTokens } from '../../../src/di'; +import InputFileCollection from '../../../src/input/InputFileCollection'; const createTranspiledMutants = (...n: number[]) => { return n.map(n => { @@ -29,45 +26,41 @@ const createTranspiledMutants = (...n: number[]) => { }); }; -const LOGGING_CONTEXT: LoggingClientContext = Object.freeze({ - level: LogLevel.Fatal, - port: 4200 -}); - -describe('MutationTestExecutor', () => { +describe(MutationTestExecutor.name, () => { let sandboxPoolMock: Mock; let mutantTranspilerMock: Mock; - let testFrameworkMock: TestFramework; let transpiledMutants: TranspiledMutant[]; - let inputFiles: File[]; + let inputFiles: InputFileCollection; let reporter: Mock; - let expectedConfig: Config; - let sut: MutantTestExecutor; + let sut: MutationTestExecutor; let mutants: TestableMutant[]; let initialTranspiledFiles: File[]; - let logMock: Mock; beforeEach(() => { - logMock = currentLogMock(); sandboxPoolMock = mock(SandboxPool); mutantTranspilerMock = mock(MutantTranspiler); initialTranspiledFiles = [file(), file()]; mutantTranspilerMock.initialize.resolves(initialTranspiledFiles); sandboxPoolMock.disposeAll.resolves(); - testFrameworkMock = testFramework(); - sinon.stub(sandboxPool, 'default').returns(sandboxPoolMock); - sinon.stub(mutantTranspiler, 'default').returns(mutantTranspilerMock); reporter = mock(BroadcastReporter); - inputFiles = [new File('input.ts', '')]; - expectedConfig = config(); + inputFiles = new InputFileCollection([new File('input.ts', '')], []); mutants = [testableMutant()]; }); + function createSut(): MutationTestExecutor { + return testInjector.injector + .provideValue(coreTokens.sandboxPool, sandboxPoolMock as unknown as SandboxPool) + .provideValue(coreTokens.mutantTranspiler, mutantTranspilerMock as unknown as MutantTranspiler) + .provideValue(coreTokens.inputFiles, inputFiles) + .provideValue(coreTokens.reporter, reporter) + .injectClass(MutationTestExecutor); + } + describe('run', () => { beforeEach(async () => { - sut = new MutantTestExecutor(expectedConfig, inputFiles, testFrameworkMock, reporter, 42, LOGGING_CONTEXT); + sut = createSut(); const sandbox = mock(Sandbox as any); sandbox.runMutant.resolves(mutantResult()); sandboxPoolMock.streamSandboxes.returns(of(sandbox)); @@ -75,17 +68,8 @@ describe('MutationTestExecutor', () => { await sut.run(mutants); }); - it('should create the mutant transpiler', () => { - expect(mutantTranspiler.default).calledWith(expectedConfig); - expect(mutantTranspiler.default).calledWithNew; - }); - it('should create the sandbox pool', () => { - expect(sandboxPool.default).calledWith(expectedConfig, testFrameworkMock, initialTranspiledFiles, 42); - expect(sandboxPool.default).calledWithNew; - }); - it('should initialize the mutantTranspiler', () => { - expect(mutantTranspilerMock.initialize).calledWith(inputFiles); + expect(mutantTranspilerMock.initialize).calledWith(inputFiles.files); }); it('should dispose all sandboxes afterwards', () => { @@ -107,7 +91,7 @@ describe('MutationTestExecutor', () => { mutantTranspilerMock.transpileMutants.returns(of(...transpiledMutants)); sandboxPoolMock.streamSandboxes.returns(of(firstSandbox, secondSandbox)); - sut = new MutantTestExecutor(config(), inputFiles, testFrameworkMock, reporter, 42, LOGGING_CONTEXT); + sut = createSut(); // The uncovered, transpile error and changedAnyTranspiledFiles = false should not be ran in a sandbox // Mock first sandbox to return first success, then failed @@ -162,16 +146,16 @@ describe('MutationTestExecutor', () => { }); it('should log error results on debug', async () => { - logMock.isDebugEnabled.returns(true); + testInjector.logger.isDebugEnabled.returns(true); await sut.run(mutants); - expect(logMock.debug).calledWith('A runtime error occurred: %s during execution of mutant: %s', + expect(testInjector.logger.debug).calledWith('A runtime error occurred: %s during execution of mutant: %s', ['foo', 'bar'].toString(), transpiledMutants[5].mutant.toString()); }); it('should log transpile error results on debug', async () => { - logMock.isDebugEnabled.returns(true); + testInjector.logger.isDebugEnabled.returns(true); await sut.run(mutants); - expect(logMock.debug).calledWith(`Transpile error occurred: "Error! Cannot negate a string (or something)" during transpiling of mutant ${ + expect(testInjector.logger.debug).calledWith(`Transpile error occurred: "Error! Cannot negate a string (or something)" during transpiling of mutant ${ transpiledMutants[1].mutant.toString()}`); }); }); diff --git a/packages/stryker/test/unit/test-runner/ChildProcessTestRunnerDecoratorSpec.ts b/packages/stryker/test/unit/test-runner/ChildProcessTestRunnerDecoratorSpec.ts index 842ae528b4..9a48302e54 100644 --- a/packages/stryker/test/unit/test-runner/ChildProcessTestRunnerDecoratorSpec.ts +++ b/packages/stryker/test/unit/test-runner/ChildProcessTestRunnerDecoratorSpec.ts @@ -1,7 +1,7 @@ import * as sinon from 'sinon'; import { expect } from 'chai'; -import { RunnerOptions, RunOptions } from 'stryker-api/test_runner'; -import { LogLevel } from 'stryker-api/core'; +import { RunOptions } from 'stryker-api/test_runner'; +import { LogLevel, StrykerOptions } from 'stryker-api/core'; import ChildProcessTestRunnerDecorator from '../../../src/test-runner/ChildProcessTestRunnerDecorator'; import { Mock, mock, strykerOptions } from '../../helpers/producers'; import ChildProcessProxy from '../../../src/child-proxy/ChildProcessProxy'; @@ -13,7 +13,7 @@ import ChildProcessCrashedError from '../../../src/child-proxy/ChildProcessCrash describe(ChildProcessTestRunnerDecorator.name, () => { let sut: ChildProcessTestRunnerDecorator; - let runnerOptions: RunnerOptions; + let options: StrykerOptions; let childProcessProxyMock: { proxy: Mock; dispose: sinon.SinonStub; @@ -30,22 +30,19 @@ describe(ChildProcessTestRunnerDecorator.name, () => { }; childProcessProxyCreateStub = sinon.stub(ChildProcessProxy, 'create'); childProcessProxyCreateStub.returns(childProcessProxyMock); - runnerOptions = { - fileNames: [], - strykerOptions: strykerOptions({ - plugins: ['foo-plugin', 'bar-plugin'] - }) - }; + options = strykerOptions({ + plugins: ['foo-plugin', 'bar-plugin'] + }); loggingContext = { port: 4200, level: LogLevel.Fatal }; - sut = new ChildProcessTestRunnerDecorator('realRunner', runnerOptions, 'a working directory', loggingContext); + sut = new ChildProcessTestRunnerDecorator(options, [], 'a working directory', loggingContext); }); it('should create the child process proxy', () => { expect(childProcessProxyCreateStub).calledWith( require.resolve('../../../src/test-runner/ChildProcessTestRunnerWorker.js'), loggingContext, - runnerOptions.strykerOptions, - { [ChildProcessTestRunnerWorker.inject[0]]: 'realRunner', [ChildProcessTestRunnerWorker.inject[1]]: runnerOptions }, + options, + { sandboxFileNames: [] }, 'a working directory', ChildProcessTestRunnerWorker ); diff --git a/packages/stryker/test/unit/test-runner/CommandTestRunnerSpec.ts b/packages/stryker/test/unit/test-runner/CommandTestRunnerSpec.ts index 0bf280bde2..e86ee7883d 100644 --- a/packages/stryker/test/unit/test-runner/CommandTestRunnerSpec.ts +++ b/packages/stryker/test/unit/test-runner/CommandTestRunnerSpec.ts @@ -131,7 +131,7 @@ describe(CommandTestRunner.name, () => { commandRunner: settings }); } - return new CommandTestRunner(workingDir, { strykerOptions, fileNames: [] }); + return new CommandTestRunner(workingDir, strykerOptions); } function tick(): Promise {