Skip to content

Mearman/test-related

Repository files navigation

test-related

npm version GitHub

Run tests related to changed source files using AST-based dependency analysis (tree shaking).

Why

I wanted to run only the relevant tests for changed files with lint-staged.

Existing tools like Jest's --findRelatedTests don't work with Node.js's built-in node:test runner.

This tool uses TypeScript's AST to build a precise dependency graph, tracking which specific exports from source files are used by tests. When a source file changes, only tests that actually use its exports are triggered.

Installation

npm install -D test-related
yarn add -D test-related
pnpm add -D test-related

Usage

With lint-staged

{
  "lint-staged": {
    "src/**/*.ts": [
      "eslint --fix",
      "typecheck-files",
      "test-related"
    ],
    "test/**/*.ts": [
      "eslint --fix",
      "test-related"
    ]
  }
}

Tip: typecheck-files type-checks only changed files while respecting your tsconfig.json—perfect for fast pre-commit workflows.

CLI

test-related src/index.ts src/utils.ts

Programmatic API

import { findRelatedTests } from "test-related"

const result = findRelatedTests(["src/index.ts", "src/utils.ts"])

console.log(`Running ${result.testFiles.length} related tests...`)

// Run with node:test
const { execSync } = await import("node:child_process")
execSync(`node --test ${result.testFiles.join(" ")}`, { stdio: "inherit" })

How it works

  1. Parse test files - Extracts all imports using TypeScript AST
  2. Parse source files - Extracts all exports (functions, classes, types, interfaces, enums)
  3. Build dependency graph - Maps which tests use which exports
  4. Match changed files - When a source file changes, finds tests that import its exports

Tree-shaking precision

// Source file: src/math.ts
export function add(a: number, b: number): number { return a + b }
export function subtract(a: number, b: number): number { return a - b }

// Test file: test/math.test.ts
import { add } from "../src/math.js"  // Only uses 'add'

test("add works", () => { ... })  // This test runs
// No test for 'subtract' exists

If you change subtract() but not add(), the test won't run—because no test uses subtract.

Options

interface Options {
  /** Working directory (defaults to process.cwd()) */
  cwd?: string
  /** Test directory patterns (defaults to ["test", "src"]) */
  testDirs?: string[]
  /** Source directory patterns (defaults to ["src"]) */
  sourceDirs?: string[]
  /** Always run tests matching this pattern when source files change */
  alwaysRunPatterns?: string[]
  /** Test runner command (defaults to "node --test") */
  testRunner?: string
}

Limitations

  • TypeScript/JavaScript only - Requires .ts or .js files
  • Static analysis only - Runtime dependencies (dynamic imports) are not tracked
  • Export precision - Namespace imports (import * as x) always trigger tests (can't determine specific usage)

For full coverage, run all tests in CI:

node --test

License

MIT

About

Runs only the tests affected by changed source files by analysing dependencies so that irrelevant tests are skipped.

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors