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

@unblessed/vrt

v1.0.0-alpha.23

Published

Visual Regression Testing tools for @unblessed terminal UI applications

Readme

@unblessed/vrt

Visual Regression Testing tools for @unblessed terminal UI applications.

Overview

@unblessed/vrt provides infrastructure for recording, playing back, and comparing terminal UI screenshots to detect visual regressions. It captures the raw SGR-encoded terminal output from screen.screenshot() and allows you to verify that UI rendering remains consistent across code changes.

Installation

npm install --save-dev @unblessed/vrt
# or
pnpm add -D @unblessed/vrt

CLI Tools

The vrt command provides tools for working with VRT recordings directly from the terminal.

Available Commands

vrt play - Play back a recording

vrt play recording.vrt.json
vrt play recording.vrt.json --speed 2.0  # 2x speed

Plays the recording to your terminal, showing exactly what was captured.

vrt info - Show recording information

vrt info recording.vrt.json

Displays metadata about the recording including dimensions, duration, frame count, and timestamps.

vrt compare - Compare two recordings

# Basic comparison
vrt compare expected.vrt.json actual.vrt.json

# With options
vrt compare expected.vrt.json actual.vrt.json \
  --threshold 5 \
  --ignore-colors \
  --verbose

Options:

  • -t, --threshold <number> - Allow N character differences (default: 0)
  • --ignore-colors - Ignore ANSI color code differences
  • --ignore-whitespace - Ignore whitespace differences
  • -v, --verbose - Show detailed diff information

vrt export - Export to image formats

vrt export recording.vrt.json output.gif        # Animated GIF
vrt export recording.vrt.json output.png --frame 0  # Single frame PNG

Note: Export functionality coming soon

Usage in Scripts

Add CLI commands to your package.json scripts:

{
  "scripts": {
    "vrt:info": "vrt info tests/fixtures/golden.vrt.json",
    "vrt:compare": "vrt compare tests/fixtures/golden.vrt.json tests/fixtures/current.vrt.json"
  }
}

Quick Start - Golden Snapshot Testing

The most common use case is golden snapshot testing with the compareWithGolden utility:

import { compareWithGolden } from "@unblessed/vrt";
import { Screen, Box } from "@unblessed/node";

it("box renders correctly", async () => {
  const screen = new Screen();

  const box = new Box({
    parent: screen,
    content: "Hello",
    border: { type: "line" },
  });
  screen.render();

  // Capture screenshot
  const recording = {
    version: "1.0.0",
    dimensions: { cols: screen.cols, rows: screen.rows },
    metadata: {
      createdAt: new Date().toISOString(),
      duration: 0,
      frameCount: 1,
      description: "box test",
    },
    frames: [{ screenshot: screen.screenshot(), timestamp: 0 }],
  };

  // Compare with golden snapshot
  const result = compareWithGolden(
    "__tests__/fixtures/box.vrt.json",
    recording,
    "box renders correctly",
  );

  if (!result.pass) {
    throw new Error(result.errorMessage);
  }

  screen.destroy();
});

Commands:

# Normal - compare with golden
pnpm test

# Update golden snapshots
UPDATE_SNAPSHOTS=1 pnpm test

API Reference

Golden Snapshot Utilities

compareWithGolden(fixturePath, recording, testName)

Complete golden snapshot workflow that handles:

  • Creating golden on first run
  • Updating golden when UPDATE_SNAPSHOTS=1
  • Comparing with golden on normal runs
  • Formatting detailed error messages

Returns: GoldenComparisonResult

  • pass: boolean - Whether test should pass
  • action: 'created' | 'updated' | 'matched' | 'failed'
  • errorMessage?: string - Formatted error (on failure)

saveGoldenSnapshot(path, recording)

Save a VRT recording as a golden snapshot file.

Recording & Playback

Recording a Session

import { VRTRecorder } from "@unblessed/vrt";
import { Screen, Box } from "@unblessed/node";

const screen = new Screen({ smartCSR: true });
const recorder = new VRTRecorder(screen, {
  interval: 100, // Capture every 100ms
  outputPath: "./recording.vrt.json",
});

// Start recording
recorder.start();

// Create UI and interact
const box = new Box({
  parent: screen,
  content: "Hello World",
  border: { type: "line" },
});
screen.render();

// Stop and save
const recording = recorder.stop();
// Saved to ./recording.vrt.json

Playing Back a Recording

import { VRTPlayer } from "@unblessed/vrt";

const player = new VRTPlayer("./recording.vrt.json");

// Play to stdout
await player.play({ writeToStdout: true });

// Process each frame
await player.play({
  onFrame: (frame, index) => {
    console.log(`Frame ${index}: ${frame.screenshot.length} bytes`);
  },
  speed: 2.0, // 2x speed
});

Comparing Recordings

import { VRTComparator } from "@unblessed/vrt";

const result = VRTComparator.compare(
  "./golden.vrt.json", // Expected
  "./current.vrt.json", // Actual
  {
    threshold: 5, // Allow up to 5 char differences
    ignoreColors: false, // Compare colors too
  },
);

if (!result.match) {
  console.error(`Visual regression detected!`);
  console.error(
    `  ${result.differentFrames} of ${result.totalFrames} frames differ`,
  );
  result.differences?.forEach((diff) => {
    console.error(
      `  Frame ${diff.frameIndex}: ${diff.diffCount} chars different`,
    );
  });
}

Testing Integration

Use VRT in your Vitest tests:

import { describe, it, expect } from "vitest";
import { Screen, Box } from "@unblessed/node";
import { VRTRecorder, VRTComparator } from "@unblessed/vrt";

describe("Box rendering", () => {
  it("renders with border correctly", async () => {
    const screen = new Screen();
    const recorder = new VRTRecorder(screen);

    recorder.start();

    const box = new Box({
      parent: screen,
      top: 0,
      left: 0,
      width: 20,
      height: 5,
      content: "Test",
      border: { type: "line" },
    });
    screen.render();

    await new Promise((resolve) => setTimeout(resolve, 100));

    const recording = recorder.stop();
    screen.destroy();

    // Compare with golden snapshot
    const result = VRTComparator.compare(
      "./__tests__/fixtures/box-golden.vrt.json",
      recording,
    );

    expect(result.match).toBe(true);
  });
});

API Reference

VRTRecorder

Records screen screenshots at regular intervals.

Constructor:

new VRTRecorder(screen: Screen, options?: VRTRecorderOptions)

Methods:

  • start() - Start recording
  • stop() - Stop recording and return VRTRecording
  • isRecording() - Check if recording is in progress
  • getFrameCount() - Get current frame count

VRTPlayer

Plays back VRT recordings.

Constructor:

new VRTPlayer(source: string | VRTRecording)

Methods:

  • play(options?: VRTPlayerOptions) - Play the recording
  • getFrames() - Get all frames
  • getFrame(index) - Get specific frame
  • getMetadata() - Get recording metadata
  • getDimensions() - Get terminal dimensions

VRTComparator

Compares two VRT recordings.

Static Methods:

  • compare(expected, actual, options?) - Compare two recordings
  • compareRecordings(expected, actual, options?) - Compare VRTRecording objects

Types

interface VRTRecorderOptions {
  interval?: number;
  outputPath?: string;
  description?: string;
  metadata?: Record<string, any>;
}

interface VRTPlayerOptions {
  speed?: number;
  onFrame?: (frame: VRTFrame, index: number) => void;
  writeToStdout?: boolean;
}

interface VRTComparatorOptions {
  threshold?: number;
  ignoreColors?: boolean;
  ignoreWhitespace?: boolean;
}

interface VRTComparisonResult {
  match: boolean;
  totalFrames: number;
  matchedFrames: number;
  differentFrames: number;
  differentFrameIndices: number[];
  differences?: VRTFrameDifference[];
}

Recording Format

VRT recordings are JSON files with this structure:

{
  "version": "1.0.0",
  "dimensions": {
    "cols": 80,
    "rows": 24
  },
  "metadata": {
    "createdAt": "2025-10-21T...",
    "duration": 1000,
    "frameCount": 10,
    "description": "Test recording"
  },
  "frames": [
    {
      "screenshot": "\u001b[H\u001b[2J...",
      "timestamp": 0
    }
  ]
}

Future Work

Image Export

The vrt export command is planned to support exporting VRT recordings to image formats:

Planned Features:

  • PNG Export: Export individual frames as static PNG images

    • Useful for documentation, GitHub PRs, and CI reports
    • Renders ANSI escape codes to pixel buffers
    • Uses pngjs library (already included)
  • GIF Export: Export entire recordings as animated GIFs

    • Perfect for showcasing UI interactions
    • Configurable frame rate and speed
    • Uses omggif library (already included)

Implementation Requirements:

  • ANSI-to-image renderer that converts terminal escape codes to pixel buffers
  • Font rendering (monospace font required)
  • Color palette mapping (256-color and true color support)
  • Text rendering with proper character spacing

Potential Extensions:

  • SVG export for scalable terminal output
  • HTML export with interactive playback controls
  • Video export (MP4/WebM) for high-quality recordings
  • Side-by-side diff visualization

License

MIT