CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-stryker-mutator--jest-runner

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

Pending
Overview
Eval results
Files

environment-integration.mddocs/

Environment Integration

Jest environment enhancement utilities that provide Stryker-specific functionality including instrumenter context sharing and test event handling for coverage analysis.

Capabilities

Jest Environment Mixin

Enhances Jest environment classes with Stryker integration for mutation testing and coverage analysis.

/**
 * Mixin function that enhances a Jest environment class with Stryker functionality
 * Adds instrumenter context sharing and test event handling for coverage analysis
 * @param JestEnvironmentClass - Jest environment class to enhance
 * @returns Enhanced Jest environment class with Stryker integration
 */
export function mixinJestEnvironment<T extends typeof JestEnvironment>(
  JestEnvironmentClass: T & { [STRYKER_JEST_ENV]?: true }
): T;

Usage Examples:

import { mixinJestEnvironment } from "@stryker-mutator/jest-runner";
import NodeEnvironment from "jest-environment-node";
import JSDOMEnvironment from "jest-environment-jsdom";

// Enhance Node.js environment
const StrykerNodeEnvironment = mixinJestEnvironment(NodeEnvironment);

// Enhance JSDOM environment  
const StrykerJSDOMEnvironment = mixinJestEnvironment(JSDOMEnvironment);

// Use in Jest configuration
module.exports = {
  testEnvironment: StrykerNodeEnvironment,
  // or
  testEnvironment: "./custom-stryker-environment.js"
};
// custom-stryker-environment.js
const { mixinJestEnvironment } = require("@stryker-mutator/jest-runner");
const NodeEnvironment = require("jest-environment-node").default;

module.exports = mixinJestEnvironment(NodeEnvironment);

Pre-built Environment Exports

The package provides pre-built Jest environments with Stryker integration for common use cases.

// Available as CommonJS exports through package.json exports map

// Node.js environment with Stryker integration
"@stryker-mutator/jest-runner/jest-env/node"

// JSDOM environment with Stryker integration  
"@stryker-mutator/jest-runner/jest-env/jsdom"

// JSDOM v16 environment with Stryker integration
"@stryker-mutator/jest-runner/jest-env/jsdom-sixteen"

Usage Examples:

// Jest configuration using pre-built environments
module.exports = {
  // Use Node.js environment
  testEnvironment: "@stryker-mutator/jest-runner/jest-env/node",
  
  // Use JSDOM environment
  testEnvironment: "@stryker-mutator/jest-runner/jest-env/jsdom",
  
  // Use JSDOM v16 environment
  testEnvironment: "@stryker-mutator/jest-runner/jest-env/jsdom-sixteen"
};
// Import for programmatic use
const StrykerNodeEnv = require("@stryker-mutator/jest-runner/jest-env/node");
const StrykerJSDOMEnv = require("@stryker-mutator/jest-runner/jest-env/jsdom");

Types

Jest Environment Types

import type {
  JestEnvironment,
  EnvironmentContext,
  JestEnvironmentConfig,
} from '@jest/environment';
import type { Circus } from '@jest/types';

interface StrykerJestEnvironment extends JestEnvironment {
  readonly [STRYKER_JEST_ENV]: true;
  handleTestEvent: Circus.EventHandler;
}

interface EnvironmentContext {
  testPath: string;
  docblockPragmas: Record<string, string | string[]>;
  console: Console;
}

interface JestEnvironmentConfig {
  projectConfig: Config.ProjectConfig;
  globalConfig: Config.GlobalConfig;
}

Test Event Types

namespace Circus {
  interface Event {
    name: string;
    test?: TestEntry;
  }
  
  interface TestEntry {
    name: string;
    parent: DescribeBlock;
  }
  
  interface DescribeBlock {
    name: string;
    parent?: DescribeBlock;
  }
  
  interface State {
    currentDescribeBlock: DescribeBlock;
    currentlyRunningTest?: TestEntry;
  }
  
  type EventHandler = (event: Event, state: State) => Promise<void> | void;
}

Instrumenter Context

interface InstrumenterContext {
  currentTestId?: string;
  hitCount?: number;
  hitLimit?: number;
  activeMutant?: Mutant;
  mutantCoverage?: MutantCoverage;
}

Implementation Details

Instrumenter Context Sharing

The mixin creates a shared context between the test environment and Stryker's instrumenter:

  • Global Access: Available as global.__stryker__ or custom namespace
  • Test Tracking: Tracks current test ID for per-test coverage
  • Hit Limiting: Monitors execution counts to prevent infinite loops
  • Mutant State: Shares active mutant information during mutation testing

Test Event Handling

The enhanced environment intercepts Jest test events for coverage analysis:

  • test_start: Sets current test ID for coverage tracking
  • test_done: Clears current test ID when test completes
  • Async Support: Handles both synchronous and asynchronous test events
  • Event Forwarding: Preserves original environment's event handling

Coverage Analysis Integration

Per-test coverage analysis requires the environment mixin to:

  1. Track Test Execution: Identify which test is currently running
  2. Associate Coverage: Link code execution to specific tests
  3. Aggregate Results: Collect coverage data across all tests
  4. Report Coverage: Provide coverage information to Stryker

Environment Compatibility

The mixin is designed to work with any Jest environment:

  • Node.js Environment: Server-side testing with full Node.js APIs
  • JSDOM Environment: Browser simulation with DOM APIs
  • Custom Environments: Any environment extending Jest's base environment
  • Backward Compatibility: Preserves existing environment functionality

Test File Registration

The environment automatically registers test files with Stryker:

// Tracks which test files have Stryker environment integration
state.testFilesWithStrykerEnvironment.add(context.testPath);

This information is used for:

  • Coverage verification
  • Test file validation
  • Mutation testing optimization
  • Error reporting and debugging

Global Namespace Configuration

The environment respects custom global namespaces:

// Uses configured namespace or defaults to '__stryker__'
const namespace = this.global.__strykerGlobalNamespace__ ?? '__stryker__';
this.#strykerContext = this.global[namespace] = state.instrumenterContext;

This allows for:

  • Custom instrumenter configurations
  • Avoiding naming conflicts
  • Supporting multiple Stryker instances
  • Compatibility with different Stryker versions

Install with Tessl CLI

npx tessl i tessl/npm-stryker-mutator--jest-runner

docs

configuration.md

environment-integration.md

index.md

plugin-integration.md

test-runner.md

utilities.md

tile.json