UNPKG

@stryker-mutator/jest-runner

Version:

A plugin to use the jest test runner and framework in Stryker, the JavaScript mutation testing framework

97 lines (86 loc) 3.91 kB
import path from 'path'; import { fileURLToPath, URL } from 'url'; import type { Config } from '@jest/types'; import { CoverageAnalysis, StrykerOptions } from '@stryker-mutator/api/core'; import { propertyPath } from '@stryker-mutator/util'; import semver from 'semver'; import { JestWrapper } from '../utils/jest-wrapper.js'; import { state } from './messaging.cjs'; const jestEnvironmentGenericFileName = fileURLToPath(new URL('./jest-environment-generic.cjs', import.meta.url)); /** * Jest's defaults. * @see https://jestjs.io/docs/en/configuration */ function getJestDefaults(jestWrapper: JestWrapper) { // New defaults since 27: https://jestjs.io/blog/2021/05/25/jest-27 if (semver.satisfies(jestWrapper.getVersion(), '>=27')) { return { testRunner: 'jest-circus/runner', testEnvironment: 'node', }; } else { return { // the defaults before v27 testRunner: 'jest-jasmine2', testEnvironment: 'jsdom', }; } } export function withCoverageAnalysis( jestConfig: Config.InitialOptions, coverageAnalysis: CoverageAnalysis, jestWrapper: JestWrapper, ): Config.InitialOptions { // Override with Stryker specific test environment to capture coverage analysis if (coverageAnalysis === 'off') { return jestConfig; } else { const overrides: Config.InitialOptions = {}; overrideEnvironment(jestConfig, overrides, jestWrapper); if (coverageAnalysis === 'perTest') { setupFramework(jestConfig, overrides, jestWrapper); } return { ...jestConfig, ...overrides }; } } export function withHitLimit(jestConfig: Config.InitialOptions, hitLimit: number | undefined, jestWrapper: JestWrapper): Config.InitialOptions { // Override with Stryker specific test environment to capture coverage analysis if (typeof hitLimit === 'number') { const overrides: Config.InitialOptions = {}; overrideEnvironment(jestConfig, overrides, jestWrapper); return { ...jestConfig, ...overrides }; } else { return jestConfig; } } /** * Setup the test framework (aka "runner" in jest terms) for "perTest" coverage analysis. * Will use monkey patching for framework "jest-jasmine2", and will assume the test environment handles events when "jest-circus" */ function setupFramework(jestConfig: Config.InitialOptions, overrides: Config.InitialOptions, jestWrapper: JestWrapper) { const testRunner = jestConfig.testRunner ?? getJestDefaults(jestWrapper).testRunner; if (testRunner === 'jest-jasmine2') { overrides.setupFilesAfterEnv = [ path.resolve(path.dirname(fileURLToPath(import.meta.url)), './jasmine2-setup-coverage-analysis.cjs'), ...(jestConfig.setupFilesAfterEnv ?? []), ]; } else if (!testRunner.includes('jest-circus')) { // 'jest-circus/runner' is supported, via handleTestEvent, see https://jestjs.io/docs/en/configuration#testenvironment-string // Use includes here, since "react-scripts" will specify the full path to `jest-circus`, see https://github.com/stryker-mutator/stryker-js/issues/2789 throw new Error( `The @stryker-mutator/jest-runner doesn't support ${propertyPath<StrykerOptions>()( 'coverageAnalysis', )} "perTest" with "jestConfig.testRunner": "${ jestConfig.testRunner }". Please open an issue if you want support for this: https://github.com/stryker-mutator/stryker-js/issues`, ); } } function overrideEnvironment(jestConfig: Config.InitialOptions, overrides: Config.InitialOptions, jestWrapper: JestWrapper): void { const originalJestEnvironment = jestConfig.testEnvironment ?? getJestDefaults(jestWrapper).testEnvironment; state.jestEnvironment = nameEnvironment(originalJestEnvironment); overrides.testEnvironment = jestEnvironmentGenericFileName; } function nameEnvironment(shortName: string): string { return ['node', 'jsdom'].includes(shortName) ? `jest-environment-${shortName}` : shortName; }