Skip to content

callstackincubator/sniffler

Repository files navigation

Sniffler

Sniffler sniffs out the E2E tests your changes actually wake up.

mit licence npm downloads Chat PRs Welcome

Sniffler turns changed source files into the E2E tests they can affect so you can run a smaller, more relevant set in CI or locally without tracing the whole graph by hand.

Features

  • Impact Selection: Resolve which E2E tests are affected by changed files across TypeScript and JavaScript projects.

  • Fast Development Workflow: Use impact to inspect the affected tests and run to append them to your existing runner command.

  • Workspace Awareness: Understand package and workspace layouts so impacted tests are found across real-world monorepos.

  • React Native Platform Resolution: Pass a runtime platform such as android to resolve .android.*, .native.*, and generic source files in Metro-style order.

  • Configurable Cache: Store graph data in .sniffler/cache.json to speed up repeated runs.

  • Type-Safe & Developer-Friendly: The CLI, graph, cache, and fixture-backed tests are all written in TypeScript so the behavior stays easy to evolve.

  • Works With Your Setup: Works with Git diffs, explicit file lists, package.json workspaces, pnpm-workspace.yaml, TSConfig path aliases, and package exports.

Getting started

Install Sniffler in your project:

pnpm add -D sniffler
# or
npm install -D sniffler

Add Sniffler's project files to your repo:

.sniffler/config.json
.sniffler/test-map.json

Start with a focused config that points Sniffler at your source roots and test map:

{
  "source": {
    "roots": ["apps", "packages"],
    "ignore": ["**/*.test.*", "**/*.spec.*", "**/__tests__/**"]
  },
  "tests": {
    "manifest": ".sniffler/test-map.json"
  }
}

Sniffler fills in defaults for source extensions, workspace discovery, TSConfig paths, cache behavior, test selection rules, and output format. See docs/config.md for the full configuration reference.

If you have a shared setup module that reaches into the rest of the app, add it under tests.sharedTargets so Sniffler still walks the dependency graph from that module into its imports. For example, when src/global.ts imports src/some-other.ts, a change to src/some-other.ts can still select every test that shares src/global.ts.

If a lockfile or other repo-level file should force every test, add tests.runAllWhenChanged:

{
  "tests": {
    "manifest": ".sniffler/test-map.json",
    "runAllWhenChanged": ["pnpm-lock.yaml"]
  }
}

Sniffler checks this before dependency analysis, so a match skips workspace discovery, source scanning, graph build, and cache work.

Then run the CLI from the project root:

pnpm exec sniffler impact --base origin/main --head HEAD
pnpm exec sniffler run --base origin/main --head HEAD -- pnpm vitest run

You can also pass files directly when you are not starting from a Git diff:

sniffler impact src/components/Button.tsx
sniffler run src/components/Button.tsx -- pnpm vitest run

For React Native projects, pass a runtime platform to resolve extensionless imports with Metro-style platform priority:

sniffler impact --platform android src/components/Button.android.tsx
sniffler run --platform android src/components/Button.android.tsx -- pnpm vitest run

sniffler run appends impacted test files to the runner command and exits with the runner's exit code.

Quick example

sniffler impact --base origin/main --head HEAD
sniffler run --base origin/main --head HEAD -- pnpm vitest run

Use impact when you want to inspect the affected set first. Use run when you want Sniffler to pass that set directly into your test runner.

Project compatibility

Sniffler currently supports:

  • Git-based workflows: Diff-based and explicit file-based impact selection.
  • JavaScript and TypeScript: Source graphs built from JS/TS files.
  • Monorepos: Workspace package discovery through package.json and pnpm-workspace.yaml.
  • TypeScript path aliases: Resolution through configured TSConfig path mappings.
  • React Native platforms: Optional runtime platform probing for extensionless relative imports and TSConfig aliases.
  • Package exports: Workspace package exports and package-name imports in supported setups.

Made with ❤️ at Callstack

sniffler is open source and free to use. If it helps your workflow, please star it 🌟. Callstack is a team of React and React Native engineers, contact us at hello@callstack.com if you need help or just want to say hi.

Like the project? ⚛️ Join the team who does amazing stuff for clients and drives React Native Open Source! 🔥

About

Sniffler sniffs out the E2E tests your changes actually wake up.

Resources

License

Contributing

Stars

Watchers

Forks

Contributors