diff --git a/packages/jest-runner/src/jest-plugins/with-coverage-analysis.ts b/packages/jest-runner/src/jest-plugins/with-coverage-analysis.ts index 22b45be647..798aa115c9 100644 --- a/packages/jest-runner/src/jest-plugins/with-coverage-analysis.ts +++ b/packages/jest-runner/src/jest-plugins/with-coverage-analysis.ts @@ -18,7 +18,7 @@ const jestEnvironmentGenericFileName = fileURLToPath(new URL('./jest-environment */ function getJestDefaults(jestWrapper: JestWrapper) { // New defaults since 27: https://jestjs.io/blog/2021/05/25/jest-27 - if (semver.satisfies(jestWrapper.getVersion(), '>=27')) { + if (semver.satisfies(semver.coerce(jestWrapper.getVersion())!, '>=27')) { return { testRunner: 'jest-circus/runner', testEnvironment: 'node', diff --git a/packages/jest-runner/src/jest-test-adapters/jest-test-adapter-factory.ts b/packages/jest-runner/src/jest-test-adapters/jest-test-adapter-factory.ts index 5724298aa1..da1c1fd38d 100644 --- a/packages/jest-runner/src/jest-test-adapters/jest-test-adapter-factory.ts +++ b/packages/jest-runner/src/jest-test-adapters/jest-test-adapter-factory.ts @@ -1,13 +1,21 @@ import { Logger } from '@stryker-mutator/api/logging'; import { StrykerOptions } from '@stryker-mutator/api/core'; import { commonTokens, Injector, tokens } from '@stryker-mutator/api/plugin'; -import semver from 'semver'; +import semver, { SemVer } from 'semver'; import { JestPluginContext, pluginTokens } from '../plugin-di.js'; import { JestWrapper } from '../utils/jest-wrapper.js'; import { JestLessThan25TestAdapter } from './jest-less-than-25-adapter.js'; import { JestGreaterThan25TestAdapter } from './jest-greater-than-25-adapter.js'; +interface CoercedVersion { + version: SemVer; + raw: string; +} + +function coerceVersion(version: string): CoercedVersion { + return { version: semver.coerce(version)!, raw: version }; +} export function jestTestAdapterFactory( log: Logger, @@ -15,11 +23,11 @@ export function jestTestAdapterFactory( options: StrykerOptions, injector: Injector, ): JestGreaterThan25TestAdapter | JestLessThan25TestAdapter { - const version = jestWrapper.getVersion(); - log.debug('Detected Jest version %s', version); - guardJestVersion(version, options, log); + const coercedVersion = coerceVersion(jestWrapper.getVersion()); + log.debug('Detected Jest version %s', coercedVersion.raw); + guardJestVersion(coercedVersion, options, log); - if (semver.satisfies(version, '<25.0.0')) { + if (semver.satisfies(coercedVersion.version, '<25.0.0')) { return injector.injectClass(JestLessThan25TestAdapter); } else { return injector.injectClass(JestGreaterThan25TestAdapter); @@ -27,10 +35,10 @@ export function jestTestAdapterFactory( } jestTestAdapterFactory.inject = tokens(commonTokens.logger, pluginTokens.jestWrapper, commonTokens.options, commonTokens.injector); -function guardJestVersion(jest: string, options: StrykerOptions, log: Logger) { - if (semver.satisfies(jest, '<22.0.0')) { - throw new Error(`You need Jest version >= 22.0.0 to use the @stryker-mutator/jest-runner plugin, found ${jest}`); - } else if (semver.satisfies(jest, '<24')) { +function guardJestVersion({ version, raw }: CoercedVersion, options: StrykerOptions, log: Logger) { + if (semver.satisfies(version, '<22.0.0')) { + throw new Error(`You need Jest version >= 22.0.0 to use the @stryker-mutator/jest-runner plugin, found ${raw}`); + } else if (semver.satisfies(version, '<24')) { if (options.coverageAnalysis !== 'off') { throw new Error( `You need Jest version >= 24.0.0 to use the @stryker-mutator/jest-runner with "coverageAnalysis": "${options.coverageAnalysis}", you're currently using version 23.0.0. Please upgrade your jest version, or set "coverageAnalysis": "off".`, @@ -38,7 +46,7 @@ function guardJestVersion(jest: string, options: StrykerOptions, log: Logger) { } log.warn( '[DEPRECATED] Support for Jest version < 24 is deprecated and will be removed in the next major version of Stryker, please upgrade your jest version (your current version is %s).', - jest, + raw, ); } } diff --git a/packages/jest-runner/test/unit/jest-test-adapters/jest-test-adapter-factory.spec.ts b/packages/jest-runner/test/unit/jest-test-adapters/jest-test-adapter-factory.spec.ts index 6690523532..e58e8648b6 100644 --- a/packages/jest-runner/test/unit/jest-test-adapters/jest-test-adapter-factory.spec.ts +++ b/packages/jest-runner/test/unit/jest-test-adapters/jest-test-adapter-factory.spec.ts @@ -37,19 +37,32 @@ describe(jestTestAdapterFactory.name, () => { expect(testInjector.logger.debug).calledWith('Detected Jest version %s', '25.0.0'); }); - it('should return a JestGreaterThan25Adapter when the Jest version is higher or equal to 25.0.0', () => { + it('should return a JestGreaterThan25Adapter when the Jest version is greater or equal to 25.0.0', () => { jestWrapperMock.getVersion.returns('25.0.0'); const testAdapter = act(); expect(testAdapter).instanceOf(JestGreaterThan25TestAdapter); }); - it('should return a JestLessThan25Adapter when the Jest version is higher or equal to 22.0.0, but less then 25 and coverage analysis is disabled', () => { + it('should return a JestGreaterThan25Adapter when the Jest version is an alpha version greater than 25.0.0', () => { + jestWrapperMock.getVersion.returns('30.0.0-alpha.6'); + const testAdapter = act(); + + expect(testAdapter).instanceOf(JestGreaterThan25TestAdapter); + }); + it('should return a JestLessThan25Adapter when the Jest version is greater or equal to 22.0.0, but less then 25 and coverage analysis is disabled', () => { testInjector.options.coverageAnalysis = 'off'; jestWrapperMock.getVersion.returns('22.0.0'); const testAdapter = act(); expect(testAdapter).instanceOf(JestLessThan25TestAdapter); }); + it('should return a JestLessThan25Adapter when the Jest version is an alpha version greater than 22.0.0, lower than 25', () => { + testInjector.options.coverageAnalysis = 'off'; + jestWrapperMock.getVersion.returns('23.0.0-alpha.6'); + const testAdapter = act(); + + expect(testAdapter).instanceOf(JestLessThan25TestAdapter); + }); it('should throw an error when the Jest version is lower than 22.0.0', () => { jestWrapperMock.getVersion.returns('21.0.0'); @@ -57,6 +70,12 @@ describe(jestTestAdapterFactory.name, () => { expect(act).to.throw(Error, 'You need Jest version >= 22.0.0 to use the @stryker-mutator/jest-runner plugin, found 21.0.0'); }); + it('should throw an error when the Jest version is an alpha version lower than 22.0.0', () => { + jestWrapperMock.getVersion.returns('21.0.0-alpha.6'); + + expect(act).to.throw(Error, 'You need Jest version >= 22.0.0 to use the @stryker-mutator/jest-runner plugin, found 21.0.0-alpha.6'); + }); + it('should throw an error when the Jest version is between 22 and 24, but coverage analysis is enabled', () => { jestWrapperMock.getVersion.returns('23.0.0'); testInjector.options.coverageAnalysis = 'all'; diff --git a/packages/jest-runner/test/unit/jest-test-runner.spec.ts b/packages/jest-runner/test/unit/jest-test-runner.spec.ts index d077c11a1a..5e77a0dd65 100644 --- a/packages/jest-runner/test/unit/jest-test-runner.spec.ts +++ b/packages/jest-runner/test/unit/jest-test-runner.spec.ts @@ -376,7 +376,7 @@ describe(JestTestRunner.name, () => { }); }); - it('should not add a set setupFile if testRunner is not specified and jest version >= 27 (circus test runner)', async () => { + it('should not add a setupFile if testRunner is not specified and jest version >= 27 (circus test runner)', async () => { jestWrapperMock.getVersion.returns('27.0.0'); options.jest.config = { testRunner: undefined }; const sut = await arrangeInitializedSut(); @@ -386,6 +386,16 @@ describe(JestTestRunner.name, () => { }); }); + it('should not add a setupFile if testRunner is not specified and jest is an alpha version >= 27 (circus test runner)', async () => { + jestWrapperMock.getVersion.returns('30.0.0-alpha.6'); + options.jest.config = { testRunner: undefined }; + const sut = await arrangeInitializedSut(); + await sut.dryRun(factory.dryRunOptions({ coverageAnalysis: 'perTest' })); + expect(jestTestAdapterMock.run).calledWithMatch({ + jestConfig: sinon.match({ setupFilesAfterEnv: undefined }), + }); + }); + it('should not allow the circus test runner for coverage analysis "perTest"', async () => { options.jest.config = { testRunner: 'jest-circus/runner' }; const sut = await arrangeInitializedSut();