Skip to content
Back to skills

Test Discovery

ASecurity

Explains the prescribed __test__/ directory layout, test file naming conventions, and how source coverage is derived from the adjacent src/ folder. Applies to files matching: **/__test__/**/*, **/__fixtures__/**/*, **/__snapshots__/**/*

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
testingtypescriptgo

Works with

  • cli

Security analysis

A100/100

Scanned November 7, 2026

npx -y skills add spencerbeggs/vitest-agent --skill test-discovery --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Test Discovery?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Test Discovery
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/spencerbeggs-test-discovery-vitest-agent/badge)](https://www.skillsdirectory.com/skills/spencerbeggs-test-discovery-vitest-agent)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: test-discovery
description: "Explains the prescribed __test__/ directory layout, test file naming conventions, and how source coverage is derived from the adjacent src/ folder. Applies to files matching: **/__test__/**/*, **/__fixtures__/**/*, **/__snapshots__/**/*"
---

# Test Layout — `__test__/` Conventions

Projects using this layout place tests in a `__test__/` directory that sits adjacent to `src/`. Coverage is calculated from that `src/` directory. Auto-discovery tools (like `AgentPlugin.discover()`) may generate Vitest projects from this structure automatically, but custom project configurations are also possible.

```text
<project-root>/
├── src/            source code — coverage is calculated from here
└── __test__/       tests and test support files
```

Discovery recognises these two locations and no others. A `__test__/` directory anywhere else in the package tree — `lib/scripts/__test__/`, for example — is not a valid test location: Vitest will not collect it, and the tests inside it will never run. The walker also stops at a nested `package.json` boundary, so a sibling package's tests are never double-counted in a monorepo scan.

## `__test__/` Directory Structure

Test files sit flat at the top level of `__test__/`; helper files go in reserved subdirectories organised by test kind.

```text
__test__/
├── *.test.ts | *.unit.test.ts  unit test files
├── fixtures/                   static files needed by unit tests
├── snapshots/                  Vitest snapshot files for unit tests
├── utils/                      shared helpers for unit tests
├── integration/
│   ├── *.int.test.ts           integration test files
│   ├── fixtures/               static files needed by integration tests
│   ├── snapshots/              Vitest snapshot files for integration tests
│   └── utils/                  shared helpers for integration tests
└── e2e/
    ├── *.e2e.test.ts           e2e test files
    ├── fixtures/               static files needed by e2e tests
    ├── snapshots/              Vitest snapshot files for e2e tests
    └── utils/                  shared helpers for e2e tests
```

## Subdirectory Rules

| Subdirectory | Coverage | Lint | Typecheck | Purpose |
| --- | :---: | :---: | :---: | --- |
| `fixtures/` | excluded | excluded | excluded | Static files consumed by tests (JSON, TOML, binary, etc.) |
| `snapshots/` | excluded | excluded | excluded | Vitest snapshot output (managed automatically) |
| `utils/` | excluded | required | required | Shared helpers — mocks, extended `expect`, shared types |

Fixtures and snapshots are fully excluded; place arbitrary static content there without concern for linting or types. Utils files must be valid TypeScript and pass the linter — treat them as first-class support code, just not counted toward coverage.

These three columns describe coverage, linting, and typechecking — not Vitest
discovery, which is a separate mechanism. Discovery only ever collects
`*.test.ts` files, so a helper in any of these directories is never collected
wherever it sits. What discovery does exclude is a *test* file under a helper
directory named directly beneath `__test__/` — `__test__/utils/a.test.ts` is
not collected, while `__test__/integration/utils/a.test.ts` is an ordinary
suite. That boundary is anchored at the test root deliberately: matching the
helper name at any depth silently swallowed real suites (issue #251).

## Escape Hatch — `__fixtures__` and `__snapshots__` Anywhere

Directories named `__fixtures__` or `__snapshots__` (double-underscore wrapping) are recognised and excluded from coverage, linting, and typechecking wherever they appear inside a project — not just under `__test__/`. This can be useful when a fixture needs to live close to the source file it supports.

Prefer the prescribed locations (`__test__/fixtures/`, `__test__/integration/fixtures/`, etc.) over this escape hatch. Scattering `__fixtures__` directories through `src/` makes test support files harder to find and the project harder to navigate.

## Test File Classification

The scanner classifies files by suffix; first match wins:

| Suffix | Kind | Lives in |
| --- | --- | --- |
| `*.e2e.test.ts` | e2e | `__test__/e2e/` |
| `*.int.test.ts` | int | `__test__/integration/` |
| `*.test.ts` or `*.unit.test.ts` | unit | `__test__/` (top level) |

## Limitation — the PreToolUse Hook Only Knows the Default Layout

The `test-location.sh` PreToolUse hook and its backing `vitest-agent agent
check-test-path` CLI subcommand classify a test path against the **default**
layout described above — `classifyTestPath` is purely lexical and never loads
a project's actual Vitest config. Most workspaces use the default layout
`AgentPlugin.discover()` produces, so this is right almost always. It is
**not** aware of a consumer who changes discovery via:

- a custom `discoverStrategy` passed to `AgentPlugin({ discoverStrategy })`
  (including `discoverStrategy: false` to disable discovery entirely),
- `AgentPlugin.discover().addProject({ name, path })` registering a
  non-package folder as an extra project, or
- a class extending `DefaultDiscoverStrategy` / implementing
  `DiscoverStrategy` directly.

For such a workspace, the default-layout rule can be wrong about a path the
consumer's actual config collects fine. Because a denial is the strongest
action the hook can take, `check-test-path` **fails open** (exits 1 with no
stdout — the hook then no-ops) whenever it detects one of those markers in
the workspace's `vitest.config.*` / `vitest.workspace.*` / `vite.config.*`
source text, or whenever that config file cannot be found or read at all. A
missing or unreadable config is treated exactly like a detected marker: no
verdict is safer than a confidently wrong one. The detector
(`detectNonDefaultDiscoverStrategy` in `@vitest-agent/sdk`) is a lexical scan
with a best-effort comment strip, so a marker string that only appears inside
a comment can still register as a false positive — that direction (failing
open) is the safe one, so it is accepted rather than built out further.

If you hit a wrong deny anyway — or just want the check off — set
`VITEST_AGENT_TEST_LOCATION_HOOK=off` (`0` and `false` also work) in your
environment. The hook checks this before doing anything else and emits a
silent noop without ever invoking the CLI.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.