From 455e170a44cbd8fe5629fc830eeb5df890cd3941 Mon Sep 17 00:00:00 2001 From: Tomasz Nguyen Date: Fri, 5 Mar 2021 20:11:36 +0000 Subject: [PATCH] fix(jest-runner): pass config by path if configPath provided This mimics behaviour of jest - if you want to use projects, you need to pass the config by file. This teaches stryker to do the same - if you set explicit config path in jest config, that gets passed into jest and voila, multi project jest works :) https://github.com/swist/stryker-test is the minimum reproducible example repo for this --- .../jest-greater-than-25-adapter.ts | 7 +++- .../jest-less-than-25-adapter.ts | 6 ++- .../jest-test-adapters/jest-test-adapter.ts | 1 + packages/jest-runner/src/jest-test-runner.ts | 16 ++++++-- .../jest-greater-than-25-adapter.spec.ts | 39 +++++++++++++++++++ 5 files changed, 61 insertions(+), 8 deletions(-) diff --git a/packages/jest-runner/src/jest-test-adapters/jest-greater-than-25-adapter.ts b/packages/jest-runner/src/jest-test-adapters/jest-greater-than-25-adapter.ts index 9a5fc1503b..b264f347da 100644 --- a/packages/jest-runner/src/jest-test-adapters/jest-greater-than-25-adapter.ts +++ b/packages/jest-runner/src/jest-test-adapters/jest-greater-than-25-adapter.ts @@ -1,11 +1,14 @@ +import { join } from 'path'; + import { jestWrapper } from '../utils'; import { JestRunResult } from '../jest-run-result'; import { JestTestAdapter, RunSettings } from './jest-test-adapter'; export class JestGreaterThan25TestAdapter implements JestTestAdapter { - public async run({ jestConfig, projectRoot, fileNameUnderTest, testNamePattern }: RunSettings): Promise { - const config = JSON.stringify(jestConfig); + public async run({ jestConfig, projectRoot, fileNameUnderTest, testNamePattern, jestConfigPath }: RunSettings): Promise { + const config = jestConfigPath ? join(projectRoot, jestConfigPath) : JSON.stringify(jestConfig); + const result = await jestWrapper.runCLI( { $0: 'stryker', diff --git a/packages/jest-runner/src/jest-test-adapters/jest-less-than-25-adapter.ts b/packages/jest-runner/src/jest-test-adapters/jest-less-than-25-adapter.ts index 72a4820022..53baf7f315 100644 --- a/packages/jest-runner/src/jest-test-adapters/jest-less-than-25-adapter.ts +++ b/packages/jest-runner/src/jest-test-adapters/jest-less-than-25-adapter.ts @@ -1,3 +1,5 @@ +import { join } from 'path'; + import jest from 'jest'; import { JestRunResult } from '../jest-run-result'; @@ -9,8 +11,8 @@ import { RunSettings, JestTestAdapter } from './jest-test-adapter'; * It has a lot of `any` typings here, since the installed typings are not in sync. */ export class JestLessThan25TestAdapter implements JestTestAdapter { - public run({ jestConfig, projectRoot, fileNameUnderTest, testNamePattern }: RunSettings): Promise { - const config = JSON.stringify(jestConfig); + public run({ jestConfig, projectRoot, fileNameUnderTest, testNamePattern, jestConfigPath }: RunSettings): Promise { + const config = jestConfigPath ? join(projectRoot, jestConfigPath) : JSON.stringify(jestConfig); return jest.runCLI( { ...(fileNameUnderTest && { _: [fileNameUnderTest], findRelatedTests: true }), diff --git a/packages/jest-runner/src/jest-test-adapters/jest-test-adapter.ts b/packages/jest-runner/src/jest-test-adapters/jest-test-adapter.ts index 73970ee65f..cf3a6034ae 100644 --- a/packages/jest-runner/src/jest-test-adapters/jest-test-adapter.ts +++ b/packages/jest-runner/src/jest-test-adapters/jest-test-adapter.ts @@ -5,6 +5,7 @@ import { JestRunResult } from '../jest-run-result'; export interface RunSettings { jestConfig: Config.InitialOptions; projectRoot: string; + jestConfigPath?: string; testNamePattern?: string; fileNameUnderTest?: string; } diff --git a/packages/jest-runner/src/jest-test-runner.ts b/packages/jest-runner/src/jest-test-runner.ts index a713063cd4..4930f3f46c 100644 --- a/packages/jest-runner/src/jest-test-runner.ts +++ b/packages/jest-runner/src/jest-test-runner.ts @@ -60,6 +60,7 @@ export const jestTestRunnerFactory = createJestTestRunnerFactory(); export class JestTestRunner implements TestRunner { private readonly jestConfig: jest.Config.InitialOptions; private readonly enableFindRelatedTests: boolean; + private readonly jestRunnerOptions: JestRunnerOptionsWithStrykerOptions; public static inject = tokens( commonTokens.logger, @@ -78,13 +79,13 @@ export class JestTestRunner implements TestRunner { configLoader: JestConfigLoader, private readonly globalNamespace: typeof INSTRUMENTER_CONSTANTS.NAMESPACE | '__stryker2__' ) { - const jestOptions = options as JestRunnerOptionsWithStrykerOptions; + this.jestRunnerOptions = options as JestRunnerOptionsWithStrykerOptions; // Get jest configuration from stryker options and assign it to jestConfig const configFromFile = configLoader.loadConfig(); - this.jestConfig = this.mergeConfigSettings(configFromFile, jestOptions.jest || {}); + this.jestConfig = this.mergeConfigSettings(configFromFile, this.jestRunnerOptions.jest || {}); // Get enableFindRelatedTests from stryker jest options or default to true - this.enableFindRelatedTests = jestOptions.jest.enableFindRelatedTests; + this.enableFindRelatedTests = this.jestRunnerOptions.jest.enableFindRelatedTests; if (this.enableFindRelatedTests) { this.log.debug('Running jest with --findRelatedTests flag. Set jest.enableFindRelatedTests to false to run all tests on every mutant.'); @@ -109,6 +110,7 @@ export class JestTestRunner implements TestRunner { const { dryRunResult, jestResult } = await this.run({ jestConfig: withCoverageAnalysis(this.jestConfig, coverageAnalysis), projectRoot: process.cwd(), + jestConfigPath: this.jestRunnerOptions.jest?.configFile, }); if (dryRunResult.status === DryRunStatus.Complete && coverageAnalysis !== 'off') { const errorMessage = verifyAllTestFilesHaveCoverage(jestResult, fileNamesWithMutantCoverage); @@ -136,7 +138,13 @@ export class JestTestRunner implements TestRunner { } process.env[INSTRUMENTER_CONSTANTS.ACTIVE_MUTANT_ENV_VARIABLE] = activeMutant.id.toString(); try { - const { dryRunResult } = await this.run({ fileNameUnderTest, jestConfig: this.jestConfig, projectRoot: process.cwd(), testNamePattern }); + const { dryRunResult } = await this.run({ + fileNameUnderTest, + jestConfig: this.jestConfig, + projectRoot: process.cwd(), + testNamePattern, + jestConfigPath: this.jestRunnerOptions.jest?.configFile, + }); return toMutantRunResult(dryRunResult); } finally { delete process.env[INSTRUMENTER_CONSTANTS.ACTIVE_MUTANT_ENV_VARIABLE]; diff --git a/packages/jest-runner/test/unit/jest-test-adapters/jest-greater-than-25-adapter.spec.ts b/packages/jest-runner/test/unit/jest-test-adapters/jest-greater-than-25-adapter.spec.ts index 3f1a0e5828..0c8f6d0eba 100644 --- a/packages/jest-runner/test/unit/jest-test-adapters/jest-greater-than-25-adapter.spec.ts +++ b/packages/jest-runner/test/unit/jest-test-adapters/jest-greater-than-25-adapter.spec.ts @@ -1,3 +1,5 @@ +import { join } from 'path'; + import { testInjector } from '@stryker-mutator/test-helpers'; import { expect } from 'chai'; import sinon from 'sinon'; @@ -13,6 +15,7 @@ describe(JestGreaterThan25TestAdapter.name, () => { const projectRoot = '/path/to/project'; const fileNameUnderTest = '/path/to/file'; + const jestConfigPath = 'jest.config.js'; const jestConfig: Config.InitialOptions = { rootDir: projectRoot }; beforeEach(() => { @@ -30,6 +33,42 @@ describe(JestGreaterThan25TestAdapter.name, () => { expect(runCLIStub).calledWith(sinon.match.object, [projectRoot]); }); + describe('when jestConfigPath not provided', () => { + it('should call the runCLI method with the stringified jest config flag', async () => { + await sut.run({ jestConfig, projectRoot, fileNameUnderTest }); + + expect(runCLIStub).calledWith( + { + $0: 'stryker', + _: [fileNameUnderTest], + config: JSON.stringify({ rootDir: projectRoot }), + findRelatedTests: true, + runInBand: true, + silent: true, + testNamePattern: undefined, + }, + [projectRoot] + ); + }); + }); + describe('when jestConfigPath provided', () => { + it('should pass the config path instead jest config flag', async () => { + await sut.run({ jestConfig, projectRoot, fileNameUnderTest, jestConfigPath }); + + expect(runCLIStub).calledWith( + { + $0: 'stryker', + _: [fileNameUnderTest], + config: join(projectRoot, jestConfigPath), + findRelatedTests: true, + runInBand: true, + silent: true, + testNamePattern: undefined, + }, + [projectRoot] + ); + }); + }); it('should call the runCLI method with the --findRelatedTests flag', async () => { await sut.run({ jestConfig, projectRoot, fileNameUnderTest });