Hone logo
Hone
Problems

Jest AST Transformation: Enhancing Test Coverage Reporting

This challenge involves creating a Jest transformer that modifies Abstract Syntax Trees (ASTs) to automatically inject code for tracking test coverage. This is a powerful technique for improving testing workflows by ensuring that all executed lines within your codebase are accounted for, even in scenarios where explicit coverage tracking might be missed.

Problem Description

Your task is to build a custom Jest transformer that intercepts JavaScript/TypeScript files before they are compiled and executed by Jest. This transformer should analyze the Abstract Syntax Tree (AST) of the input code and inject additional instrumentation code at specific points. The goal is to ensure that every executable line of code is explicitly marked as "covered" when the test suite runs.

Key Requirements:

  • AST Traversal and Modification: You will need to parse the source code into an AST, traverse it to identify relevant nodes (e.g., function declarations, if statements, loops), and insert new AST nodes representing coverage tracking calls.
  • Instrumentation Logic: For each identified code block or executable line, you must insert a call to a hypothetical __coverage__.trackLine(lineNumber) function. This function (which you don't need to implement, just call) would be responsible for recording the coverage.
  • Support for TypeScript: The transformer should be capable of processing both JavaScript and TypeScript files.
  • Jest Integration: The transformer must be correctly configured to work within a Jest testing environment.

Expected Behavior:

Given a source file, the transformer should output a modified version of that file. The modified file will contain the original code plus the injected coverage tracking calls. When this modified code is executed by Jest, these calls should be registered.

Edge Cases to Consider:

  • Code within comments should be ignored.
  • String literals containing line numbers or similar patterns should not be mistaken for executable code.
  • Ensure that the injected code does not introduce syntax errors or break the original logic of the code.
  • Consider how to handle different AST node types representing executable code.

Examples

Example 1:

Input (src/calculator.ts):
function add(a: number, b: number): number {
  return a + b;
}

export { add };
Output (transformed src/calculator.ts):
import { trackLine } from './coverage-tracker'; // Assume this import is added

function add(a: number, b: number): number {
  trackLine(2); // Line number of the function declaration
  return a + b;
  trackLine(3); // Line number of the return statement
}

export { add };

Explanation: The trackLine function is injected before the return statement and potentially at the beginning of the function body to mark those lines as covered.

Example 2:

Input (src/conditional.ts):
function process(value: number): string {
  if (value > 10) {
    return "Large";
  } else {
    return "Small";
  }
}

export { process };
Output (transformed src/conditional.ts):
import { trackLine } from './coverage-tracker';

function process(value: number): string {
  trackLine(2);
  if (value > 10) {
    trackLine(3);
    return "Large";
  } else {
    trackLine(5);
    return "Small";
  }
}

export { process };

Explanation: Coverage tracking calls are injected for the function declaration, the if condition, and the return statements within both the if and else blocks.

Example 3: (Edge Case - Comments)

Input (src/commented.ts):
// This is a comment that should be ignored
function greet(): void {
  /* Another comment
     spanning multiple lines */
  console.log("Hello"); // This line should be tracked
}

export { greet };
Output (transformed src/commented.ts):
import { trackLine } from './coverage-tracker';

// This is a comment that should be ignored
function greet(): void {
  /* Another comment
     spanning multiple lines */
  trackLine(5); // Line number of console.log
  console.log("Hello");
}

export { greet };

Explanation: The comments are preserved, and the coverage tracking call is only injected for the executable console.log line.

Constraints

  • The transformer should add minimal overhead to the build process.
  • The injected code must be valid TypeScript and JavaScript.
  • You should use an AST manipulation library like babel or ts-morph for parsing and transforming the code.
  • The solution should be implemented in TypeScript.
  • The hypothetical __coverage__.trackLine(lineNumber) function will be provided by the Jest environment (or a mock setup). You only need to generate calls to it.

Notes

This challenge will require a good understanding of ASTs, how to traverse them, and how to programmatically create new AST nodes. Familiarity with Babel's plugin API or ts-morph's API will be highly beneficial. You'll need to identify the appropriate AST node types that represent executable code and insert your instrumentation logic within their parent nodes. Consider how to get the correct line numbers for the injected calls.

Loading editor...
typescript