Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: jest prerelease support (#5015) #5016

Merged
merged 4 commits into from
Sep 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,52 @@
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,
jestWrapper: JestWrapper,
options: StrykerOptions,
injector: Injector<JestPluginContext>,
): 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);
}
}
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".`,
);
}
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,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,45 @@ 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');

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';
Expand Down
12 changes: 11 additions & 1 deletion packages/jest-runner/test/unit/jest-test-runner.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();
Expand Down