Playwright Testing
chongdashu/vibejam-starter-pack
Plan, implement, and debug frontend tests: unit/integration/E2E/visual/a11y.
Make @uiverify/vitest (Vitest browser-mode) captures deterministic so component visual tests stop coming back "changed" without a real change (flaky diffs).
$ npx skills add FranciscoMoretti/chat-js --skill vitest-visual-testing -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install FranciscoMoretti/chat-js vitest-visual-testing --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/FranciscoMoretti/chat-js.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/vitest-visual-testing .claude/skills/vitest-visual-testing && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "vitest-visual-testing" agent skill from https://github.com/FranciscoMoretti/chat-js/tree/main/.agents/skills/vitest-visual-testing into .claude/skills/vitest-visual-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vitest-visual-testing", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/FranciscoMoretti/chat-js/tree/main/.agents/skills/vitest-visual-testingType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add FranciscoMoretti/chat-js --skill vitest-visual-testing -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install FranciscoMoretti/chat-js vitest-visual-testing --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/FranciscoMoretti/chat-js.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/vitest-visual-testing .agents/skills/vitest-visual-testing && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "vitest-visual-testing" agent skill from https://github.com/FranciscoMoretti/chat-js/tree/main/.agents/skills/vitest-visual-testing into .agents/skills/vitest-visual-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vitest-visual-testing", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add FranciscoMoretti/chat-js --skill vitest-visual-testing -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install FranciscoMoretti/chat-js vitest-visual-testing --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/FranciscoMoretti/chat-js.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/vitest-visual-testing .cursor/skills/vitest-visual-testing && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "vitest-visual-testing" agent skill from https://github.com/FranciscoMoretti/chat-js/tree/main/.agents/skills/vitest-visual-testing into .cursor/skills/vitest-visual-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vitest-visual-testing", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/FranciscoMoretti/chat-js.git --path .agents/skills/vitest-visual-testing--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add FranciscoMoretti/chat-js --skill vitest-visual-testing -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install FranciscoMoretti/chat-js vitest-visual-testing --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/FranciscoMoretti/chat-js.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/vitest-visual-testing .gemini/skills/vitest-visual-testing && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "vitest-visual-testing" agent skill from https://github.com/FranciscoMoretti/chat-js/tree/main/.agents/skills/vitest-visual-testing into .gemini/skills/vitest-visual-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vitest-visual-testing", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install FranciscoMoretti/chat-js vitest-visual-testingInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add FranciscoMoretti/chat-js --skill vitest-visual-testing -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/FranciscoMoretti/chat-js.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/vitest-visual-testing .github/skills/vitest-visual-testing && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "vitest-visual-testing" agent skill from https://github.com/FranciscoMoretti/chat-js/tree/main/.agents/skills/vitest-visual-testing into .github/skills/vitest-visual-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vitest-visual-testing", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add FranciscoMoretti/chat-js --skill vitest-visual-testing -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install FranciscoMoretti/chat-js vitest-visual-testing --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/FranciscoMoretti/chat-js.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/vitest-visual-testing .opencode/skills/vitest-visual-testing && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "vitest-visual-testing" agent skill from https://github.com/FranciscoMoretti/chat-js/tree/main/.agents/skills/vitest-visual-testing into .opencode/skills/vitest-visual-testing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "vitest-visual-testing", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
vitest-visual-testingMake @uiverify/vitest (Vitest browser-mode) captures deterministic so component visual tests stop coming back "changed" without a real change (flaky diffs).
Vitest Visual Testing is an agent skill from FranciscoMoretti/chat-js. Make @uiverify/vitest (Vitest browser-mode) captures deterministic so component visual tests stop coming back "changed" without a real change (flaky diffs). Use when setting up or debugging visual tests over Vitest browser-mode component tests. Focuses only on the run-to-run variation the capturer can't neutralize from outside your app — above all live/dynamic data, the highest-value step (freeze it with static fixtures and the whole content-noise class disappears), plus the clock, infinite JS animations, and…
Its SKILL.md is about 2.5k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Testing & QA, covering Unit testing and Visual regression testing. It works with Vitest, OpenAI and Google Gemini. The repository describes itself as: Production-ready AI chat. Start here and make it your own. Formerly Sparka AI. The licence is Apache-2.0.
5 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 7da0efb. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are typescript).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Vitest Visual Testing loads about 2.5k tokens when it runs. Until then it costs about 161 tokens; SKILL.md has 1,145 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from FranciscoMoretti/chat-js at commit 7da0efb, republished under its Apache-2.0 licence (© FranciscoMoretti). 1,145 words, ~2,526 tokens.
.claude/skills/vitest-visual-testing/SKILL.md (or your agent's skills folder).@uiverify/vitest archives each browser-mode test's final DOM + every resource the page loaded; UI Verify re-renders and pixel-diffs that archive server-side. Because a browser-mode component test renders an isolated component (no page scroll, no A/B / analytics / chat / consent scripts, no lazy-load-on-scroll races), you get the same head start Storybook gives you: the determinism work here is narrow. If you reach for scroll-settling or third-party stubbing, you're fighting a problem component isolation already removed (that's a real-page concern — see playwright-visual-testing).
Whatever the component is at the end of the test (or at your
takeSnapshot()call) is baked into the archive forever. Your job is to drive it to one canonical state before capture.
Integration is one plugin (no per-test code):
// vitest.config.ts
import { playwright } from "@vitest/browser-playwright";
import { uiverifyPlugin } from "@uiverify/vitest/plugin";
export default defineConfig({
plugins: [uiverifyPlugin()],
test: {
browser: {
enabled: true,
provider: playwright(),
instances: [{ browser: "chromium" }],
},
},
});Every browser-mode test then archives its final DOM automatically; takeSnapshot('name') adds an intermediate checkpoint.
UI Verify's capturer neutralizes these automatically — do NOT hand-fix them: CSS animations & transitions (killed at render) and the Web Animations API (disabled); prefers-reduced-motion: reduce (emulated); Math.random (seeded before your app code runs); web fonts and <img> loading (waited for); finite JS animations (captured at their settled final frame). And unlike a real page (playwright-visual-testing), a browser-mode test has no SSR — no server-rendered random pick to reconcile. So the checklist below is only the remainder — what lives inside your app.
The headline: freeze the data. Once the list above is off the table, the one thing left that floods a component suite with false "changes" is live/dynamic data — star counts, follower counts, contributor lists, tiles, timestamps. Feed every component static fixtures and that entire class disappears at the source: static data can't churn run-to-run, so there is nothing to diff. This is the single highest-value determinism step here — do it first (step 1), and most components need nothing else.
The auto-snapshot fires at the end of a passing test, and takeSnapshot() fires the moment you call it. If the component is still resolving a promise, running a transition, or hasn't rendered its data yet, you archive a half-rendered frame. Drive it to its final state first — await your render helper, wait for the content to appear, then let the test end (or call takeSnapshot()):
import { expect } from 'vitest';
import { render } from 'vitest-browser-react'; // or your framework's browser render helper
import { takeSnapshot } from '@uiverify/vitest';
test('user card', async () => {
const screen = await render(<UserCard id="u_1" />); // render() is async - await it, or `screen` is a Promise
await expect.element(screen.getByText('Ada Lovelace')).toBeVisible(); // wait for settled content
await takeSnapshot();
});This is the analog of a Playwright test's navigation + assertions: your render + waits are the determinism surface.
A component fed live/dynamic data is flaky by construction: the stars, followers, contributor list, tile order, and timestamps move between runs, so the diff lights up with no code change. Give every component static fixtures and the whole class is gone. Never let a test hit a real backend.
Concrete — feed fixed, ordered, complete fixtures:
Two ways to inject them, both fine:
// (a) pass fixtures as props — the simplest, when the component takes its data as props
await render(<LibraryTile name="ktor" stars={12873} platforms={['jvm', 'js', 'native']} />);
// (b) mock the data module the component imports — when it fetches internally
vi.mock('../api/library', () => ({ getLibrary: () => fixtures.ktor }));If a page is a server component that fetches, render its client presentational subtree with fixture props instead of the fetching wrapper — a browser-mode test has no server to run the fetch anyway. MSW in a setup file also works for fetch-based components; the rule is only no real request.
Copy the dogfood — it's the reference implementation. apps/docs/e2e/docs-visual.browser.test.ts is the in-repo example: call takeSnapshot('name') after the page settles, with static content.
One canvas per component, not N stories. Render every variant × state of a component (a Button's sizes/states, every tile kind) in a single grid and take one snapshot — cheaper (one screenshot), and you eyeball the whole component's surface at once. Keep each page/component in its own test file so --only-changed carries the untouched ones forward, and add a path filter so the visual job only runs on UI PRs — both keep the suite cheap at scale.
The one thing the capturer deliberately does not do. Any component that reads the clock — a relative timestamp, a date defaulting to "today", a chart's day axis — drifts every run. Pin it with Vitest's fake timers before you render:
import { beforeEach, afterEach, vi } from "vitest";
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2020-01-01T00:00:00Z"));
});
afterEach(() => vi.useRealTimers());If a component animates on mount and fake timers freeze it half-way, advance to the end (vi.runAllTimers()) or set the time after the render settles.
A CSS/WAAPI/finite animation is handled for you (above). What's left is an infinite JS loop with no final frame — framer-motion pulsing dots, a Lottie loop, an autoplay spinner, or a <canvas> / requestAnimationFrame loop (which no media query can reach). Two fixes:
prefers-reduced-motion: reduce, so make the component respect it (framer-motion: <MotionConfig reducedMotion="user">, or gate the loop with useReducedMotion()). One line, and it's good app behavior anyway.<canvas> rAF loop: if (isUIVerify()) drawOneStaticFrame(); else startRaf(); in the effect). The canonical helper reads two signals — a UIVerify navigator.userAgent marker and a window.__UI_VERIFY__ global:export const isUIVerify = () =>
(typeof navigator !== "undefined" &&
navigator.userAgent.includes("UIVerify")) ||
(typeof window !== "undefined" && "__UI_VERIFY__" in window);<RadarChart isAnimationActive={!isUIVerify()} />window.__UI_VERIFY__ global (the browser provider owns the context, so the SDK sets the global, not the UA marker) — use the helper as-is; the global is what fires here.Math.random randomnessMath.random is seeded for you, but crypto.randomUUID(), a uuid library, or faker are not. Use fixed fixtures for anything that reaches the DOM (an id, a faker name), or set a fixed faker seed.
A JS-measured layout that reflows or reorders on its own (packing driven by measured size, a shuffled list) can vary run-to-run even with identical content. Force a deterministic variant (a fixed order/size), or mask the region.
One measured-layout case the SDK already handles: a component that measures text width on mount (a sliding tab or switch highlight) can bake a 1px-shifted position if the font wasn't ready at that first layout. The SDK preloads fonts before each test so the first measurement uses real metrics. If your CSS or a font registers late (an unusual setup) and you still see a sub-pixel shift, call preloadFonts() from @uiverify/vitest before render() to force it - settling after render can't undo a measurement already taken.
vi.mock / MSW).takeSnapshot() (or letting the test end) before the component committed or rendered its data → archives a half-rendered or blank frame; await render(...) (it is async), then await the settled state.Math.random / waiting on fonts by hand → wasted effort; the capturer already does all three. Spend the effort on the clock, settling, and infinite loops.crypto/uuid/faker or a bare Date.now() → fixtures + freeze the clock.© FranciscoMoretti, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .agents/skills/vitest-visual-testing of FranciscoMoretti/chat-js.
Open the folder on GitHubat commit 7da0efb
Vitest Visual Testing next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Vitest Visual Testing this skillFranciscoMoretti/chat-js | 1.2k | — | ~2.5k | Automated safety check: Pass | Apache-2.0 | |
| Playwright Testingchongdashu/vibejam-starter-pack | 149 | — | ~2.2k | Automated safety check: Pass | None | |
| Testingradix-ng/primitives | 274 | — | ~3.3k | Automated safety check: Pass | MIT | |
| Web Testing with Playwright and Vitestwithkynam/vibecode-pro-max-kit | 1.1k | — | ~892 | Automated safety check: Pass | Apache-2.0 | |
| Sanity Visual Regressionsanity-io/sanity | 6.4k | — | ~3.4k | Automated safety check: Pass | MIT | |
| Test CommanderEliasOulkadi/shokunin | 114 | — | ~3k | Automated safety check: Notes | MIT |
chongdashu/vibejam-starter-pack
Plan, implement, and debug frontend tests: unit/integration/E2E/visual/a11y.
radix-ng/primitives
Test Radix NG primitives across every layer and pick the RIGHT one for a change: Vitest unit (zoneless), jest-axe a11y, Playwright browser regression (apps/visual-regression), SSR…
withkynam/vibecode-pro-max-kit
Covers web testing from unit to E2E, load, visual, accessibility and security checks, with Playwright, Vitest and k6 guides plus a Playwright setup script.
sanity-io/sanity
Add, review, and maintain Chromatic visual regression coverage in the Sanity monorepo via dev/storybook stories, the vitest browser-mode suite, and Playwright e2e snapshots.
EliasOulkadi/shokunin
Generate unit, integration, E2E, and visual regression tests following the Testing Trophy methodology (80% integration).
yonatangross/orchestkit
Storybook 10 testing patterns with Vitest integration, ESM-only distribution, CSF3 typesafe factories, play() interaction tests, Chromatic TurboSnap visual regression, module automocking…
FranciscoMoretti/chat-js
Implement ChatJS tRPC routers, TanStack Query hooks, mutations, and cache invalidation.
FranciscoMoretti/chat-js
Discover and use stable per-app ports assigned to a local monorepo worktree.
FranciscoMoretti/chat-js
Generate a concise, shareable code snippet URL for code.franciscomoretti.com.
FranciscoMoretti/chat-js
Use the ChatJS React Query v5 lazy-prefetch pattern without blocking server rendering.
FranciscoMoretti/chat-js
Create a well-formed GitHub issue in vercel/ai using the gh CLI.
FranciscoMoretti/chat-js
Authenticate a browser against a local ChatJS app without OAuth.
Works with
Categories
Make @uiverify/vitest (Vitest browser-mode) captures deterministic so component visual tests stop coming back "changed" without a real change (flaky diffs). Vitest Visual Testing is an agent skill from FranciscoMoretti/chat-js. Make @uiverify/vitest (Vitest browser-mode) captures deterministic so component visual tests stop coming back "changed" without a real change (flaky diffs).
Vitest Visual Testing fits situations like: debugging visual tests over Vitest browser-mode component tests; tasks that involve Unit testing; tasks that involve Visual regression testing.
Run `npx skills add FranciscoMoretti/chat-js --skill vitest-visual-testing -a claude-code`. Or copy the skill folder (.agents/skills/vitest-visual-testing in FranciscoMoretti/chat-js) into .claude/skills/vitest-visual-testing in your project. Claude Code loads it when a task matches its description.
Run `npx skills add FranciscoMoretti/chat-js --skill vitest-visual-testing -a codex`. Or copy the skill folder (.agents/skills/vitest-visual-testing in FranciscoMoretti/chat-js) into .agents/skills/vitest-visual-testing in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add FranciscoMoretti/chat-js --skill vitest-visual-testing -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/vitest-visual-testing, .gemini/skills/vitest-visual-testing, .github/skills/vitest-visual-testing and .opencode/skills/vitest-visual-testing in your project.
SKILL.md names no scripts, command-line tools or credentials: Vitest Visual Testing is instructions for the agent only.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Vitest Visual Testing is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 2.5k tokens (SKILL.md is roughly 10k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Vitest Visual Testing: Playwright Testing (chongdashu/vibejam-starter-pack, 149 stars), Testing (radix-ng/primitives, 274 stars), Web Testing with Playwright and Vitest (withkynam/vibecode-pro-max-kit, 1.1k stars) and Sanity Visual Regression (sanity-io/sanity, 6.4k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
FranciscoMoretti (a GitHub user) maintains it in FranciscoMoretti/chat-js, which has 1,201 GitHub stars. The repository holds 9 skills in this directory. The repository was last updated on October 7, 2026.
Source: FranciscoMoretti/chat-js on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.