npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

affected-tests-runner

v0.0.3

Published

Run only the tests affected by changed files in your PR. Uses dependency analysis to find test files that depend on modified code.

Readme

Affected Tests Runner

Run only the tests affected by your code changes. Uses dependency analysis to find test files that import (directly or transitively) any modified files in your PR.

Features

  • Smart dependency analysis - Uses madge to build a dependency graph and find all test files affected by changes
  • Works with any test runner - Cypress, Jest, Vitest, or any CLI-based test runner
  • Parallel CI support - Automatically split tests into groups for parallel execution
  • Monorepo friendly - Configurable path prefixes and source directories
  • GitHub Actions integration - Detects PR context and base branches automatically
  • Zero config - Sensible defaults work out of the box

Compatible Test Runners

Works with any test runner that accepts spec files as CLI arguments:

| Test Runner | Compatible | Example Command | | -------------------------------- | ---------- | --------------------------------------------------- | | Jest | ✅ | jest {specs} | | Vitest | ✅ | vitest run {specs} | | Cypress Component Testing | ✅ | cypress run --component --spec "{specs}" | | Mocha | ✅ | mocha {specs} | | Playwright Component Testing | ✅ | playwright test {specs} | | AVA | ✅ | ava {specs} | | Cypress E2E | ❌ | Not supported - E2E tests don't import source files |

Note: This tool relies on analyzing import/dependency chains between your source code and test files. It works with unit tests and component tests that import the code they're testing. E2E tests typically don't import source files directly, so dependency analysis won't find them.

Installation

npm install affected-tests
# or
pnpm add affected-tests
# or
yarn add affected-tests

Quick Start

# Run all affected tests
npx affected-tests run

# See what would run without executing
npx affected-tests run --dry-run

# Get optimal group count for CI matrix
npx affected-tests groups

CLI Usage

affected-tests [command] [options]

Commands

| Command | Description | | --------- | --------------------------------------------- | | run | Run affected tests (default) | | analyze | Analyze affected tests without running | | groups | Output optimal number of groups for CI matrix |

Options

| Option | Description | Default | | -------------------- | ------------------------------------------------- | ----------------------------- | | --config <path> | Path to config file | Auto-detected | | --src-dir <path> | Source directory to analyze | ./src | | --base-dir <path> | Base directory (where tsconfig.json is) | cwd | | --path-prefix | Path prefix for monorepos (e.g., apps/web/) | "" | | --base-branch | Base branch to compare against | master | | --test-command | Test command template (use {specs} placeholder) | npx cypress run... | | --test-pattern | Regex pattern for test files | \.spec\.(ts\|tsx\|js\|jsx)$ | | --max-tests <n> | Max tests per group | 20 | | --group <n> | Group index (0-based) for parallel runs | - | | --total-groups <n> | Total number of groups | - | | --verbose | Enable verbose output | false | | --dry-run | Show what would run without executing | false | | --json | Output results as JSON (for analyze command) | false |

Examples

# Run tests with custom command
affected-tests run --test-command 'npx jest {specs}'

# Run tests in parallel (group 1 of 3)
affected-tests run --group 0 --total-groups 3

# Analyze changes for a feature branch
affected-tests analyze --base-branch main --json

# Monorepo usage
affected-tests run --path-prefix 'packages/app/' --src-dir './src'

# Run all groups in parallel locally
GROUPS=$(npx affected-tests groups) && \
  if (( GROUPS > 0 )); then \
    seq 0 $((GROUPS-1)) | xargs -P "$GROUPS" -I {} \
      npx affected-tests run --group {} --total-groups "$GROUPS"; \
  else \
    echo "No affected tests to run"; \
  fi

Configuration File

Create affected-tests.config.js in your project root:

module.exports = {
  // Source directory to analyze for dependencies
  srcDir: "./src",

  // Base directory (where tsconfig.json is located)
  baseDir: process.cwd(),

  // Path prefix for monorepos
  pathPrefix: "apps/web/",

  // Branch to compare against
  baseBranch: "main",

  // Test file pattern (as string or RegExp)
  testFilePattern: "\\.spec\\.(ts|tsx)$",

  // File extensions to analyze
  fileExtensions: ["ts", "tsx", "js", "jsx"],

  // Path to tsconfig.json relative to baseDir
  tsConfigPath: "tsconfig.json",

  // Patterns to exclude from analysis
  excludePatterns: [/node_modules/, /\.generated\./],

  // Command to run tests. Use {specs} as placeholder
  testCommand: 'npx cypress run --component --spec "{specs}"',

  // Max tests per group for parallel execution
  maxTestsPerGroup: 20,

  // Skip TypeScript type-only imports
  skipTypeImports: true,

  // Enable verbose logging
  verbose: false,
};

Supported config file names (in order of priority):

  • affected-tests.config.js
  • affected-tests.config.mjs
  • affected-tests.config.json
  • .affected-testsrc
  • .affected-testsrc.json

Programmatic API

import {
  runAffectedTests,
  analyzeAffectedTests,
  getOptimalGroupCount,
} from "affected-tests";

// Analyze affected tests
const analysis = await analyzeAffectedTests({
  srcDir: "./src",
  pathPrefix: "apps/web/",
});

console.log(analysis);
// {
//   changedFiles: ['src/utils/format.ts'],
//   changedTestFiles: [],
//   dependentTestFiles: [{ file: 'src/utils/format.spec.ts', chain: [...] }],
//   allTestFiles: ['src/utils/format.spec.ts'],
//   optimalGroups: 1,
// }

// Run affected tests
await runAffectedTests({ srcDir: "./src" }, { groupIndex: 0, totalGroups: 3 });

// Get optimal group count for CI
const groups = await getOptimalGroupCount({ maxTestsPerGroup: 10 });

GitHub Actions Integration

Basic Usage

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # Required for git diff

      - uses: actions/setup-node@v4
        with:
          node-version: "20"

      - run: npm ci
      - run: npx affected-tests run

Parallel Execution with Dynamic Matrix

jobs:
  calculate-groups:
    runs-on: ubuntu-latest
    outputs:
      groups: ${{ steps.groups.outputs.groups }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v4
      - run: npm ci

      - id: groups
        run: |
          GROUPS=$(npx affected-tests groups)
          if [ "$GROUPS" -eq "0" ]; then
            echo "groups=[]" >> $GITHUB_OUTPUT
          else
            echo "groups=$(seq 0 $((GROUPS-1)) | jq -s -c '.')" >> $GITHUB_OUTPUT
          fi

  test:
    needs: calculate-groups
    if: needs.calculate-groups.outputs.groups != '[]'
    runs-on: ubuntu-latest
    strategy:
      matrix:
        group: ${{ fromJson(needs.calculate-groups.outputs.groups) }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v4
      - run: npm ci

      - run: |
          npx affected-tests run \
            --group ${{ matrix.group }} \
            --total-groups ${{ strategy.job-total }}

How It Works

  1. Get changed files - Compares current branch against base branch using git diff
  2. Build dependency graph - Uses madge to analyze imports
  3. Find affected tests - Walks the dependency graph to find test files that import changed code
  4. Split into groups - Optionally distributes tests across parallel jobs using round-robin
  5. Run tests - Executes your test command with the affected spec files

Limitations

This tool uses file-level dependency analysis, which may produce some false positives:

  • Named imports not tracked - If a file exports multiple functions and only one changes, all importers are considered affected
  • Barrel files - Re-exports through index.ts files cause all downstream imports to be considered affected, even if they import unrelated exports
  • Dynamic imports - import() expressions may not be detected

Recommendation: Prefer direct imports over barrel files when possible (e.g., import { Button } from './Button/Button' instead of import { Button } from './components').

This is an acceptable trade-off: running a few extra tests is safer than missing genuinely affected ones, and still far more efficient than running your entire test suite.

License

MIT