UNPKG

@stryker-mutator/jest-runner

Version:

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

110 lines (96 loc) 4.39 kB
import path from 'path'; import fs from 'fs'; import { Logger } from '@stryker-mutator/api/logging'; import { tokens, commonTokens } from '@stryker-mutator/api/plugin'; import { StrykerOptions } from '@stryker-mutator/api/core'; import type { Config } from '@jest/types'; import { I, requireResolve } from '@stryker-mutator/util'; import { JestRunnerOptionsWithStrykerOptions } from '../jest-runner-options-with-stryker-options.js'; import { JestConfigWrapper } from '../utils/jest-config-wrapper.js'; import { pluginTokens } from '../plugin-di.js'; import { JestConfigLoader } from './jest-config-loader.js'; /** * The Default config loader will load the Jest configuration using the package.json in the package root */ export class CustomJestConfigLoader implements JestConfigLoader { private readonly options: JestRunnerOptionsWithStrykerOptions; public static inject = tokens(commonTokens.logger, commonTokens.options, pluginTokens.requireFromCwd, pluginTokens.jestConfigWrapper); constructor( private readonly log: Logger, options: StrykerOptions, private readonly requireFromCwd: typeof requireResolve, private readonly jestConfig: I<JestConfigWrapper>, ) { this.options = options as JestRunnerOptionsWithStrykerOptions; } public async loadConfig(): Promise<Config.InitialOptions> { try { return await this.readConfigNative(); } catch { return this.readConfigManually(); } } /** * Tries to read the jest config via the native jest api, available since jest@>=29.3 */ private async readConfigNative(): Promise<Config.InitialOptions> { const { config, configPath } = await this.jestConfig.readInitialOptions(this.options.jest.configFile, { skipMultipleConfigError: true }); const hint = '(used native `readInitialOptions` from jest-config)'; if (configPath) { this.log.debug(`Read config from "${path.relative(process.cwd(), configPath)}" ${hint}.`); } else { this.log.debug(`No config file read ${hint}.`); } return config; } /** * The legacy readConfig functionality */ public async readConfigManually(): Promise<Config.InitialOptions> { const jestConfig = (await this.readConfigFromJestConfigFile()) ?? (await this.readConfigFromPackageJson()) ?? {}; this.log.debug("Read config: %s (used stryker's own config reading functionality)", jestConfig); return jestConfig; } private async readConfigFromJestConfigFile(): Promise<Config.InitialOptions | undefined> { const configFilePath = this.resolveJestConfigFilePath(); if (configFilePath) { let config = this.requireFromCwd(configFilePath) as Config.InitialOptions | (() => Promise<Config.InitialOptions>); if (typeof config === 'function') { config = await config(); } this.log.debug(`Read Jest config from ${configFilePath}`); this.setRootDir(config, configFilePath); return config; } return undefined; } private async readConfigFromPackageJson(): Promise<Config.InitialOptions | undefined> { const pkgJsonFilePath = this.resolvePackageJsonFilePath(); if (pkgJsonFilePath) { const config: Config.InitialOptions = JSON.parse(await fs.promises.readFile(pkgJsonFilePath, 'utf8')).jest ?? {}; this.log.debug(`Read Jest config from ${pkgJsonFilePath}`); this.setRootDir(config, pkgJsonFilePath); return config; } return undefined; } private resolvePackageJsonFilePath(): string | undefined { const jestOptions = this.options; const packageJsonCandidate = path.resolve(jestOptions.jest.configFile ?? 'package.json'); if (packageJsonCandidate.endsWith('package.json') && (jestOptions.jest.configFile ?? fs.existsSync(packageJsonCandidate))) { return packageJsonCandidate; } return undefined; } private setRootDir(config: Config.InitialOptions, configFilePath: string) { config.rootDir = path.resolve(path.dirname(configFilePath), config.rootDir ?? '.'); } private resolveJestConfigFilePath(): string | undefined { const jestOptions = this.options; const configFileCandidate = path.resolve(jestOptions.jest.configFile ?? 'jest.config.js'); if (!configFileCandidate.endsWith('package.json') && (jestOptions.jest.configFile ?? fs.existsSync(configFileCandidate))) { return configFileCandidate; } return undefined; } }