# How Vite+ Implements Snapshot Testing for CLI Output Accuracy

> Discover how Vite+ implements snapshot testing for CLI output accuracy. Learn about the snapTest runner, output normalization, and deterministic snap.txt files for robust regression detection.

- Repository: [VoidZero/vite-plus](https://github.com/voidzero-dev/vite-plus)
- Tags: internals
- Published: 2026-03-16

---

**Vite+ uses a specialized `snapTest` runner that executes CLI commands in isolated environments, normalizes unstable output (timestamps, paths, ANSI codes) via `replaceUnstableOutput`, and writes deterministic [`snap.txt`](https://github.com/voidzero-dev/vite-plus/blob/main/snap.txt) files for diff-based regression detection.**

Vite+ (located at `voidzero-dev/vite-plus`) maintains CLI reliability through a comprehensive snapshot testing framework designed to verify command-line output accuracy across different platforms and environments. This system captures raw CLI execution, sanitizes non-deterministic elements through aggressive normalization, and compares results against committed baselines to catch unintended behavioral changes.

## The Three-Stage Snapshot Testing Pipeline

The implementation in [`packages/tools/src/snap-test.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/packages/tools/src/snap-test.ts) orchestrates a deterministic pipeline that transforms volatile CLI execution into stable, comparable artifacts suitable for version control.

### Stage 1: Isolated Test Environment Setup

The `snapTest` function begins by scanning the `snap-tests` directory for subdirectories containing a [`steps.json`](https://github.com/voidzero-dev/vite-plus/blob/main/steps.json) file. For each test case, it creates a fresh temporary directory using `tmpdir()` with a `vite-plus-test-<uuid>` pattern to ensure complete filesystem isolation.

The runner establishes a pristine environment by:

- Symlinking node modules into the temporary directory
- Creating a clean `$HOME/.vite-plus` configuration directory
- Injecting stable environment variables: `VITE_PLUS_CLI_TEST=1`, `NO_COLOR=true`, and `CI=true`
- Stripping platform-specific quirks such as Windows `Path` versus `PATH` casing and home-directory path variations

This setup prevents external user configuration from bleeding into test results, as implemented in lines 69-106 of [`packages/tools/src/snap-test.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/packages/tools/src/snap-test.ts).

### Stage 2: Command Execution and Output Capture

Each command defined in [`steps.json`](https://github.com/voidzero-dev/vite-plus/blob/main/steps.json) executes via `@yarnpkg/shell` within the isolated environment. The system redirects both stdout and stderr to a temporary `output.log` file to guarantee stable ordering and prevent race conditions that could alter line sequences.

Key execution parameters include:

- A per-command timeout defaulting to **50 seconds** to prevent hanging processes
- Raw log retrieval prefixed with the command line (`> <cmd>`) for context
- Synchronous reading of the output stream back into memory

This capture mechanism ensures that CLI output is recorded exactly as the user would see it, preserving timing-sensitive interactions while maintaining determinism.

### Stage 3: Output Normalization and Baseline Comparison

Raw captured output passes through `replaceUnstableOutput`, defined in [`packages/tools/src/utils.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/packages/tools/src/utils.ts) (lines 11-71), which rewrites environment-specific data into portable placeholders. This function handles:

- **ANSI escape codes** and color output
- **Timestamps and dates** (converted to `<date>`)
- **Semantic versions** (converted to `<semver>`)
- **File system paths** (replaced with `<cwd>`, `<homedir>`, or `<vite-plus-home>`)
- **Hash strings** and UUIDs
- **Windows back-slashes** normalized to forward slashes
- **npm/pnpm progress indicators** and warning messages

The sanitized output array concatenates into a single [`snap.txt`](https://github.com/voidzero-dev/vite-plus/blob/main/snap.txt) file per test case. Continuous integration diffs this file against the repository-tracked baseline; any mismatch indicates a regression requiring investigation.

## Key Implementation Files

The snapshot testing architecture spans two primary source files and a declarative configuration format:

- **[`packages/tools/src/snap-test.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/packages/tools/src/snap-test.ts)**: Orchestrates test discovery, environment isolation, command execution via `@yarnpkg/shell`, and the final [`snap.txt`](https://github.com/voidzero-dev/vite-plus/blob/main/snap.txt) generation.
- **[`packages/tools/src/utils.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/packages/tools/src/utils.ts)**: Houses `replaceUnstableOutput` and `isPassThroughEnv`, handling normalization logic and environment variable filtering.
- **`snap-tests/<case>/steps.json`**: Declarative configuration defining CLI commands, timeouts, environment overrides, and serial execution flags.
- **`snap-tests/<case>/snap.txt`**: The committed baseline file containing normalized output for diff-based regression detection.

## Running Snapshot Tests Locally

Execute the snapshot suite using pnpm from the repository root:

```bash

# Run all default snap-tests

pnpm -F vite-plus snap-test

# Execute tests from a custom directory

pnpm -F vite-plus snap-test --dir=cli/snap-tests

```

These commands invoke the `snapTest()` function, which processes each test case directory and reports deviations from established baselines.

## Creating a New Snapshot Test Case

To add coverage for a new CLI scenario, create a [`steps.json`](https://github.com/voidzero-dev/vite-plus/blob/main/steps.json) file inside a new subdirectory under `snap-tests/`:

```json
{
  "env": { "CUSTOM_VAR": "value" },
  "commands": [
    "vp --version",
    { "command": "vp build", "timeout": 60000 }
  ],
  "after": [
    "git checkout ."
  ],
  "serial": false
}

```

After placing the configuration, run the snap-test command to generate the initial [`snap.txt`](https://github.com/voidzero-dev/vite-plus/blob/main/snap.txt) baseline. The framework automatically detects whether a test case modifies global state via the `serial` flag, forcing such tests to run sequentially while allowing others to execute in parallel limited by CPU count.

## Summary

- Vite+ implements CLI snapshot testing through a dedicated `snapTest` runner that creates isolated temporary environments for each test case.
- The system captures raw output using `@yarnpkg/shell` with redirected streams to prevent race conditions, then normalizes content via `replaceUnstableOutput` to remove timestamps, paths, and version numbers.
- Portable placeholders (`<cwd>`, `<homedir>`, `<semver>`) ensure snapshots remain consistent across macOS, Linux, and Windows environments.
- Test cases are defined declaratively in [`steps.json`](https://github.com/voidzero-dev/vite-plus/blob/main/steps.json) files, with support for serial execution when global state modification occurs.
- CI validates CLI behavior by diffing generated [`snap.txt`](https://github.com/voidzero-dev/vite-plus/blob/main/snap.txt) files against committed baselines, immediately flagging output regressions.

## Frequently Asked Questions

### What makes Vite+ snapshot testing different from standard Jest snapshots?

Unlike Jest's component or object serialization, Vite+ snapshot testing specifically targets **CLI output accuracy** by executing real shell commands in isolated environments. The system normalizes platform-specific differences—Windows paths, ANSI codes, timestamps—through the `replaceUnstableOutput` function in [`packages/tools/src/utils.ts`](https://github.com/voidzero-dev/vite-plus/blob/main/packages/tools/src/utils.ts), creating portable text baselines rather than serialized JavaScript objects.

### How does the snap-test runner handle environment variables?

The runner injects stable variables including `VITE_PLUS_CLI_TEST=1`, `NO_COLOR=true`, and `CI=true` to ensure deterministic output across platforms. It also creates a fresh `$HOME/.vite-plus` directory for each test case and uses `isPassThroughEnv` to filter which variables propagate from the host environment, preventing local configuration from affecting results.

### Can snapshot tests run in parallel?

Yes, by default the runner executes test cases in parallel up to the CPU count limit. However, setting `"serial": true` in a [`steps.json`](https://github.com/voidzero-dev/vite-plus/blob/main/steps.json) file forces sequential execution for tests that modify global state or shared resources, maintaining isolation without sacrificing speed for independent test cases.

### What happens when CLI output changes intentionally?

When output changes are intentional, developers delete the existing [`snap.txt`](https://github.com/voidzero-dev/vite-plus/blob/main/snap.txt) file in the relevant `snap-tests/<case>/` directory and rerun the suite. The `snapTest` function regenerates the baseline, which can then be committed as the new reference. CI will use this updated file for future regression detection.