@rettangoli/vt
v1.1.1
Published
Rettangoli Visual Testing
Readme
Rettangoli Visual Testing
Visual regression testing for Rettangoli specs using Playwright screenshots.
Commands
rtgl vt generatertgl vt screenshotrtgl vt reportrtgl vt accept
Behavior split:
generatebuilds candidate HTML only (no Playwright capture)screenshotruns generate flow and captures candidate screenshotsreportcompares existing artifacts only (does not run generate/screenshot)
Public Screenshot Options
--headed--concurrency <number>--timeout <ms>--wait-event <name>--isolation <fast|strict>--folder <path>(repeatable)--group <section-key>(repeatable)--item <spec-path>(repeatable)
Public Report Options
--compare-method <method>--color-threshold <number>--diff-threshold <number>--folder <path>(repeatable)--group <section-key>(repeatable)--item <spec-path>(repeatable)
Scoped Runs
Use selectors to run only part of VT in both screenshot and report:
folder: matches specs by folder prefix undervt/specs(example:components/forms)group: matches derived section page key fromvt.sectionstitles (kebab-case(title))item: matches a single spec path relative tovt/specs(with or without extension)
Selector rules:
- selectors are unioned (OR); any matched item is included
- if no selector is provided, all items are included
Examples:
# Only specs under a folder
rtgl vt screenshot --folder components/forms
# Only one section/group key from vt.sections
rtgl vt screenshot --group components-basic
# Only one spec item (extension optional)
rtgl vt screenshot --item components/forms/login
rtgl vt screenshot --item components/forms/login.html
# Combine selectors (union)
rtgl vt screenshot --group components-basic --item pages/home
# Same selectors for report
rtgl vt report --folder components/forms
rtgl vt report --group components-basic
rtgl vt report --item components/forms/loginOther capture tuning remains internal and intentionally not user-configurable.
Config
rettangoli.config.yaml:
vt:
path: ./vt
port: 3001
url: http://127.0.0.1:4173
service:
start: bun run preview
concurrency: 4
timeout: 30000
waitEvent: vt:ready
isolationMode: strict
viewport:
id: desktop
width: 1280
height: 720
sections:
- title: Components Basic
files: componentsNotes:
vt.sectionsis required.vt.serviceis optional. When set, VT starts the command before capture, waits forvt.url, then stops it after capture.- when
vt.serviceis omitted andvt.urlis set, VT expects that URL to already be running. vt.isolationModeis optional:fastorstrict. Default isfast.strictuses a fresh browser context per captured spec and is recommended for real app routes or IndexedDB-backed state.- Section page keys are derived as
kebab-case(title)for flat sections and groupitems[].title. - Derived section page keys must be unique case-insensitively.
vt.viewportsupports object or array; each viewport requiresid,width,height.vt.captureis internal and must be omitted.- Viewport contract details:
docs/viewport-contract.md.
Spec Frontmatter
Supported frontmatter keys per spec file:
titledescriptiontemplateurlwaitEventwaitSelectorwaitStrategy(networkidle|load|event|selector)viewport(object or array of viewport objects)skipScreenshotskipInitialScreenshotspecssteps
Step action reference:
docs/step-actions.md- canonical format is structured action objects (
- action: ...); legacy one-line string steps are not supported. action: selectaccepts exactly one oftestIdorselectorfor interaction targeting.assertsupportsjsdeep-equal checks for object/array values.
Screenshot naming:
- By default, VT takes an immediate first screenshot before running
steps. - Set
skipInitialScreenshot: truein frontmatter to skip that immediate first screenshot. - First captured screenshot is
-01. - Then
-02,-03, up to-99. - When viewport id is configured, filenames include
--<viewportId>before ordinal (for examplepages/home--mobile-01.webp).
Default template behavior:
- The built-in template loads
/public/main.jsexactly once as an ES module. - Put the bundle at
vt/static/public/main.js, or use a custom template when the page has a different entry point or script format. - An uncaught page exception fails the screenshot task instead of silently producing a candidate image.
Docker
Use the Playwright image for browsers and the project's installed CLI for its pinned VT version:
docker pull han4wluc/rtgl:playwright-v1.57.0-rtgl-v1.1.0Install rtgl in the project, then run its CLI inside the browser image:
docker run --rm -v "$(pwd):/workspace" han4wluc/rtgl:playwright-v1.57.0-rtgl-v1.1.0 node /workspace/node_modules/rtgl/cli.js vt screenshot
docker run --rm -v "$(pwd):/workspace" han4wluc/rtgl:playwright-v1.57.0-rtgl-v1.1.0 node /workspace/node_modules/rtgl/cli.js vt report
docker run --rm -v "$(pwd):/workspace" han4wluc/rtgl:playwright-v1.57.0-rtgl-v1.1.0 node /workspace/node_modules/rtgl/cli.js vt acceptNote:
- Run
npm install --save-dev --save-exact [email protected]once and commit the lockfile. - The image supplies browsers;
/workspace/node_modules/rtgl/cli.jssupplies the project CLI and VT. Match the image's Playwright version to the installed VT dependency. - In this monorepo, mount the repository root and use
node /workspace/packages/rettangoli-cli/cli.jsinstead. - Image default working directory is
/workspace. - Use
-w /workspace/<subdir>only when running commands from a subfolder within the mounted project.
Supports linux/amd64 and linux/arm64.
docker/build-and-push.sh derives its tag and build arguments from the CLI and
VT manifests ([email protected], @rettangoli/[email protected], Playwright 1.57.0).
After publishing these packages, the script publishes
han4wluc/rtgl:playwright-v1.57.0-rtgl-v2.1.4. The examples above use an
existing browser image with the project CLI and do not require that new image.
Image builds verify the installed CLI, VT and Playwright versions
and fail on dependency drift. Publishing a matching image remains a release
step; an existing image tag does not imply that it contains the project's VT.
Consumer projects receive source fixes through a published package release and
a normal dependency upgrade; changing a Docker wrapper alone does not install
unreleased library changes.
The local Dockerfile.test includes all workspace packages and loads every CLI
command before the image can be used by the E2E runner.
Development
Run unit tests:
bun run testDefault unit run behavior:
bun run testskips the real-browser smoke tests inspec/e2e-smoke.spec.jsunlessVT_E2E=1.- Skipped smoke tests are:
runs generate, accept, and report with real screenshotssupports waitEvent readiness with real browser screenshotsloads the default main bundle once as an ES modulecompletes the final drain when the page replaces timer globalsfails capture on an uncaught page errorsupports managed service lifecycle with vt.service.start and vt.url
Run real-browser smoke:
VT_E2E=1 bun run test -- spec/e2e-smoke.spec.jsRun Docker E2E tests (requires Docker daemon running):
# Full pipeline: build test image → run all E2E scenarios
bun run test:e2e:full
# Scenarios only (skip image build, assumes image already exists)
bun run test:e2eOptional benchmark fixture:
bun run bench:capture