Agent skill

Testing Web

by ericrisco in ericrisco/rsc-harness

A skill your agent uses when writing or fixing frontend unit, component or custom-hook tests with Vitest or Jest plus Testing Library — rendering a component in jsdom, testing a hook in isolation…

MITAuto-check passedTesting & QA

Install Testing Web

skills CLI
$ npx skills add ericrisco/rsc-harness --skill testing-web -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install ericrisco/rsc-harness testing-web --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/ericrisco/rsc-harness.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/testing-web .claude/skills/testing-web && rm -rf skills-src

Use ~/.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/

Facts

Skill name
testing-web
GitHub stars
156
Token cost
~3.3k tokens
SKILL.md length
1,365 words
Files
6 (incl. scripts, references)
Skills in repo
229
Repo updated
First seen
Licence
MIT

At a glance

A skill your agent uses when writing or fixing frontend unit, component or custom-hook tests with Vitest or Jest plus Testing Library — rendering a component in jsdom, testing a hook in isolation…

  • Fixing frontend unit
  • SKILL.md covers What this owns / what it doesn't, Pick the runner (do this once,…, Minimal Vitest setup that works and The one rule: test what the…, plus 9 more sections
  • Runs Shell scripts from its folder; calls npx and vitest
  • Custom-hook tests with Vitest

What it does

Testing Web is an agent skill from ericrisco/rsc-harness. Use when writing or fixing frontend unit, component or custom-hook tests with Vitest or Jest plus Testing Library — rendering a component in jsdom, testing a hook in isolation, choosing between sync and async queries, silencing act warnings, mocking fetch, or migrating a Jest suite to Vitest. NOT real-browser multi-page journeys (that is e2e-testing), NOT pytest suites (that is testing-py), NOT accessibility auditing (that is accessibility).

Its SKILL.md is about 3.3k tokens, which your agent loads only when the skill is triggered. The skill folder holds 8 other files, including scripts and reference files (for example `evals/README.md`, `evals/cases.yaml` and `references/jest-setup.md`).

It sits in Testing & QA, covering Unit testing. It works with Vitest, Jest, pytest and Testing Library. The repository describes itself as: Your agent invents things because it has no memory, and can't touch your database because it has no arms. rsc is the meta-harness that gives it both, plus the trade to know the… The licence is MIT.

When your agent uses it

  • Fixing frontend unit
  • Custom-hook tests with Vitest
  • Jest plus Testing Library — rendering a component in jsdom
  • Testing a hook in isolation

Example prompts

  • “/testing-web”

Requirements

  • Python 3
  • Node.js
  • A Bash shell

What it can do on your machine

Read from SKILL.md and the folder at commit 92fde8f. It shows what the files ask for, not the result of running them.

  • Tool permissions

    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.

  • Runs code

    Ships 1 file in scripts/ (Shell), which the agent can run.

    Shell commands in SKILL.md call:

    • npx
    • vitest

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use npx, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Testing Web loads about 3.3k tokens when it runs, and up to ~5k if it reads all its reference files. Until then it costs about 116 tokens; SKILL.md has 1,365 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~116
When it runs · the whole SKILL.md, loaded when a task matches
~3.3k
With references · SKILL.md plus every file in references/, read only if the agent opens them
~5k

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.

Safety

Auto-check passed

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); the scripts in this folder are not scanned.

SKILL.md

The full file from ericrisco/rsc-harness at commit 92fde8f, republished under its MIT licence (© ericrisco). 1,365 words, ~3,313 tokens.

Download SKILL.mdSave it as .claude/skills/testing-web/SKILL.md (or your agent's skills folder). This skill also uses 5 other files; get the full folder from GitHub.
name
testing-web
description
Use when writing or fixing frontend unit, component or custom-hook tests with Vitest or Jest plus Testing Library — rendering a component in jsdom, testing a hook in isolation, choosing between sync and async queries, silencing act warnings, mocking fetch, or migrating a Jest suite to Vitest. NOT real-browser multi-page journeys (that is `e2e-testing`), NOT pytest suites (that is `testing-py`), NOT accessibility auditing (that is `accessibility`).
tags
testing, frontend, vitest, jest, testing-library, react, hooks, component-testing, jsdom
recommends
e2e-testing, accessibility, testing-py, react, nextjs, debug
origin
risco

testing-web — fast, trustworthy component and hook tests

A frontend test is only worth keeping if it survives a refactor and fails for the right reason. The way to get there is boring and non-negotiable: render the thing, query it the way a user finds it, drive it with real events, and assert on what the user can see. Everything in this skill bends toward that. Tests that reach into className, state, props, or instance methods pass while the UI is broken and break while the UI is fine — delete that instinct.

What this owns / what it doesn't

This skill owns unit, component, and custom-hook tests that run in a simulated DOM (jsdom) or Vitest Browser Mode at component granularity. The moment scope crosses a boundary, switch skills:

Pick the runner (do this once, never run both)

Project shapeRunnerWhy
New Vite / React 19 / Next 16 repoVitest 4Shares your vite.config, zero second transform pipeline, Browser Mode is stable as of v4.0 (Oct 2025).
Established Jest / CRA / React Native repoJest 30Migration cost outweighs the win; Jest 30 is current (min Node 18.x, min TS 5.4).
Both installedpick one and rip the other outTwo runners means two configs, two mock APIs, doubled CI — and tests that pass in one, fail in the other.

Vitest is the de-facto default for new frontend projects in 2026; Jest stays where it already lives. Jest 30 specifics (ts-jest vs babel, the jsdom v26 window.location break) live in references/jest-setup.md.

Minimal Vitest setup that works

Pin current majors: vitest ^4.0, @testing-library/react ^16.3, @testing-library/jest-dom ^6.9, @testing-library/user-event ^14.6, jsdom, @vitejs/plugin-react.

ts
// vitest.config.ts
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  test: {
    environment: "jsdom",   // give the test a DOM; default 'node' has no document
    globals: true,          // describe/it/expect without imports; jest-dom matchers register globally
    setupFiles: ["./vitest.setup.ts"],
  },
});
ts
// vitest.setup.ts
import "@testing-library/jest-dom/vitest"; // the /vitest entry — NOT the bare import (that is Jest's)

The /vitest import path matters: the bare @testing-library/jest-dom registers against Jest's expect. Wrong path = toBeInTheDocument is not a function.

The one rule: test what the user sees

Query and assert on the rendered output a human perceives, never the mechanism. This is what makes a test outlive a refactor — rename a state variable, swap a class library, restructure the tree, and a behavioral test still passes.

tsx
// Bad — coupled to internals; passes when broken, breaks when fine
expect(wrapper.find(".btn--loading")).toHaveLength(1);
expect(component.state.isOpen).toBe(true);

// Good — coupled to user-observable behavior
expect(screen.getByRole("button", { name: /saving/i })).toBeDisabled();
expect(screen.getByRole("dialog")).toBeVisible();

Query priority ladder

Reach for the highest query that fits. getByTestId is the fire escape, not the front door — it asserts nothing about accessibility or labels.

PriorityQueryUse for
1getByRole(name)Almost everything: buttons, headings, inputs, dialogs, links.
2getByLabelTextForm fields tied to a <label>.
3getByPlaceholderTextInputs with only a placeholder (prefer a real label).
4getByTextNon-interactive copy, paragraphs, list items.
5getByDisplayValueAsserting a filled-in input's current value.
lastgetByTestIdOnly when no role/label/text identifies the node.

Pick the right variant by what you expect:

VariantReturnsThrows if absent?Use when
getBy*element nowyeselement must already be there
queryBy*element or nullno (returns null)asserting absence (expect(...).toBeNull())
findBy*Promise of elementrejects after timeoutelement appears later (after fetch/async)

Never getBy something that arrives asynchronously — it throws before the element mounts. That is what findBy is for.

Driving interactions

Set up user-event once per test and await every interaction. It dispatches the full realistic event sequence (pointerdown -> mousedown -> focus -> mouseup -> click), so it catches handlers fireEvent silently skips.

tsx
import userEvent from "@testing-library/user-event";

it("submits the typed name", async () => {
  const user = userEvent.setup();           // call setup() before interacting
  render(<Greeter />);
  await user.type(screen.getByLabelText(/name/i), "Ada"); // await — these are async
  await user.click(screen.getByRole("button", { name: /greet/i }));
  expect(screen.getByText(/hello, ada/i)).toBeInTheDocument();
});

Reach for fireEvent only for events user-event has no verb for (e.g. scroll). A missing await is the single most common source of "passes locally, flakes in CI."

Async and the act() warning

"An update to X was not wrapped in act(...)" means state updated after your assertion ran — the test finished, the component kept working, React complained. The fix in a component test is almost never a manual act(). It is to wait for the observable result:

tsx
// Bad — asserts before the fetch resolves; state lands "outside act"
render(<Profile id="1" />);
expect(screen.getByText("Ada")).toBeInTheDocument(); // throws / act warning

// Good — findBy retries until the node appears, inside RTL's act wrapper
render(<Profile id="1" />);
expect(await screen.findByText("Ada")).toBeInTheDocument();

For a transition you can't pin to a single element, wrap the assertion in waitFor. Bare act() in a component test is a code smell — it belongs to hook tests (next section).

Testing hooks

renderHook ships inside @testing-library/react itself. Do not install or import the long-deprecated @testing-library/react-hooks. Read live values off result.current; wrap any setter call you trigger yourself in act(); re-run with new props via rerender; await async settle with waitFor.

tsx
import { renderHook, act, waitFor } from "@testing-library/react";

it("counts down then stops at zero", async () => {
  const { result, rerender } = renderHook(({ from }) => useCountdown(from), {
    initialProps: { from: 3 },
  });
  expect(result.current.value).toBe(3);

  act(() => result.current.start());     // a setter YOU invoke -> wrap in act
  await waitFor(() => expect(result.current.value).toBe(0)); // async settle -> waitFor

  rerender({ from: 10 });                // feed new props
  expect(result.current.value).toBe(10);
});

Mocking the boundary

Mock at the edge your code talks to the outside world — the network or the imported module — never the internal function you are trying to verify. Mock the unit under test and the test proves nothing.

  • Network: prefer MSW (http.get(...) handlers) so components hit a real fetch/axios path. It survives client-library swaps.
  • A whole module: vi.mock("./api") (Vitest) / jest.mock("./api") (Jest) for non-network collaborators.
  • Time: vi.useFakeTimers() for timers/debounce; advance with vi.advanceTimersByTime(ms), then restore in cleanup.
ts
import { vi } from "vitest";
vi.mock("./flags", () => ({ isEnabled: () => true })); // a boundary module, not the component

Runnable copy-paste recipes — form submit, controlled input, MSW async data, a provider-wrapping custom render, fake timers, a hook with an effect + cleanup, an error-boundary test — live in references/recipes.md.

Show full SKILL.md (543 more words)Show less

Snapshots vs assertions

Default to explicit behavioral assertions. A snapshot proves nothing about correctness — it proves output didn't change, and a giant DOM snapshot gets blindly --updated the first time it breaks. Snapshot only small, stable, serializable output (a formatted currency string, a normalized config object). Never snapshot a full component tree as your primary assertion.

Mutation: does the suite actually notice?

Coverage tells you which code ran. It cannot tell you whether any test would have noticed if that code were wrong — and a render() with no assertion, or a snapshot nobody reads, raises coverage while detecting nothing. Mutation testing plants bugs on purpose: if the suite still passes, the mutant survived and you have found a test that asserts nothing.

jsonc
// stryker.config.json — scope is not optional here
{ "testRunner": "vitest", "mutate": ["src/cart/total.ts", "src/cart/discount.ts"] }
bash
npx stryker run

Reach for the real tool over a hand-written mutant list: Stryker generates mutants from the syntax tree, so it cannot apply one to code that moved and cannot report one it never ran.

  • Always scope mutate to the files you changed. A whole-project Stryker run on a real front-end does not finish in a useful amount of time; that is the main reason teams try it once and abandon it.
  • Scale it to risk. Not a default toll. Run it on the logic where a bug is expensive — pricing, totals, permissions, anything money- or auth-shaped — not on presentational components, where a surviving mutant usually just means the DOM detail genuinely does not matter.
  • A survivor is not automatically a failure. Some mutants are semantically equivalent to the original and cannot be killed; classify those with the reason. Never add an assertion about non-behaviour just to kill one — that is coverage-chasing wearing a different hat.
  • A survivor that is a real bug gets an assertion, not an excuse. Write it, then rerun.

Anti-patterns

Anti-patternWhy it's wrongDo instead
getByTestId as first choiceAsserts nothing about a11y or labels; survives broken markupClimb the ladder: role > label > text first
getBy* for async contentThrows before the element mountsawait findBy* / await waitFor(...)
Interaction without awaitAssertion runs before the event settles; flakes in CIawait user.click(...) every time
fireEvent.click by defaultSkips the realistic pointer/focus sequenceuserEvent.setup() then await user.click
Manual act() in a component testMasks the real fix (waiting for output)Await findBy/waitFor instead
Asserting on state/props/classNameCouples the test to internals; breaks on refactorAssert on rendered role/text the user sees
setTimeout/sleep to waitArbitrary delay = slow + still flakyfindBy/waitFor retries until ready
Mocking the unit under testThe test verifies the mock, not the codeMock the network/module boundary only
Importing @testing-library/react-hooksDeprecated; folded into @testing-library/reactImport renderHook from @testing-library/react
Bare @testing-library/jest-dom in VitestRegisters against Jest's expect -> matcher missingImport @testing-library/jest-dom/vitest
Running Jest and Vitest in one suiteTwo configs/mock APIs; passes in one, fails in otherPick one runner, remove the other
A test file with zero expect(...)Renders but verifies nothing; green by accidentEvery test asserts an observable outcome

Verify your suite

Run the linter against a test file or directory to catch these shape violations before review:

bash
scripts/verify.sh src/components/__tests__

It hard-fails on tests with no assertion and on un-awaited interactions, and warns on testid-first queries, raw fireEvent, stray act() in component files, and setTimeout-based waiting. It checks artifact shape, not whether your assertions are true.

© ericrisco, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 5 other files (scripts, references) in skills/testing-web of ericrisco/rsc-harness.

  • SKILL.md
  • evals/README.md
  • evals/cases.yaml
  • references/jest-setup.md
  • references/recipes.md
  • scripts/verify.sh

Open the folder on GitHubat commit 92fde8f

Compare with similar skills

Testing Web 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.

Testing Web compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Testing Web this skillericrisco/rsc-harness156—~3.3kAutomated safety check: PassMIT
React Testingaffaan-m/ECC274k1 repos~3.3kAutomated safety check: PassMIT
React TestingHoangNguyen0403/agent-skills-standard570—~1.3kAutomated safety check: PassMIT
Test GuardamElnagdy/guard-skills1.3k2 repos~2.1kAutomated safety check: PassMIT
Designing TestsCloudAI-X/claude-workflow-v21.4k1 repos~1.5kAutomated safety check: PassMIT
Playwright Testingchongdashu/vibejam-starter-pack149—~2.1kAutomated safety check: PassNone

Similar skills

  • React Testing

    affaan-m/ECC

    React component testing with React Testing Library, Vitest/Jest, MSW for network mocking, accessibility assertions with axe, and the decision boundary between component tests and Playwright/Cypress…

    274k GitHub starsUsed in 1 repo~3.3k tokens
    Testing & QAAuto-check passed
  • React Testing

    HoangNguyen0403/agent-skills-standard

    Test React components with RTL and Jest/Vitest. An agent skill from HoangNguyen0403/agent-skills-standard.

    570 GitHub stars~1.3k tokensUpdated today
    Testing & QAAuto-check passed
  • Test Guard

    amElnagdy/guard-skills

    Reviews newly written or edited tests against nine rules that cut test bloat, such as mock-heavy checks and near-duplicate cases, before they are committed.

    1.3k GitHub starsUsed in 2 repos~2.1k tokens
    Testing & QAAuto-check passed
  • Designing Tests

    CloudAI-X/claude-workflow-v2

    Designs and implements testing strategies for any codebase. An agent skill from CloudAI-X/claude-workflow-v2.

    1.4k GitHub starsUsed in 1 repo~1.5k tokens
    Testing & QAAuto-check passed
  • Playwright Testing

    chongdashu/vibejam-starter-pack

    Plan, implement, and debug frontend tests: unit/integration/E2E/visual/a11y.

    149 GitHub stars~2.1k tokensUpdated 5 mo ago
    Testing & QAAuto-check passed
  • MoAI TDD Workflow

    modu-ai/moai-adk

    Drives test-first development through the RED, GREEN, REFACTOR cycle, with a config switch that selects between TDD and a DDD workflow for existing code.

    1.2k GitHub stars~3.1k tokensUpdated today
    Testing & QAAuto-check passed

More from ericrisco/rsc-harness

All 229 skills in this repo
  • Ab Testing

    ericrisco/rsc-harness

    A skill your agent uses when designing or analyzing a controlled experiment — falsifiable hypothesis, sample size from an MDE, reading significance/CI/power, CUPED, or rescuing tests that won't go…

    156 GitHub stars~2.4k tokensUpdated today
    Auto-check passed
  • Accessibility

    ericrisco/rsc-harness

    A skill your agent uses when making a web UI conform to WCAG 2.2 Level AA — axe-core or Lighthouse a11y violations, keyboard operability, focus management, ARIA roles/names/live regions, contrast…

    156 GitHub stars~3.4k tokensUpdated today
    Auto-check passed
  • Ads

    ericrisco/rsc-harness

    A skill your agent uses when running or fixing paid acquisition on Google or Meta — campaign structure (Performance Max, Demand Gen, Search, Advantage+), platform-fit creative, budget/scaling rules…

    156 GitHub stars~2.2k tokensUpdated today
    Auto-check passed
  • Agent Eval

    ericrisco/rsc-harness

    A skill your agent uses when measuring whether an LLM or agent system actually got better and gating merges on it: golden sets, fixing an inflated LLM-as-judge, scoring RAG (faithfulness, contextual…

    156 GitHub stars~3.2k tokensUpdated today
    Auto-check passed
  • AI Media

    ericrisco/rsc-harness

    A skill your agent uses when a creative goal must become a finished media file: pick and order generative-media models per modality — AI voiceover, image-to-video clips, score — then glue them with…

    156 GitHub stars~3.3k tokensUpdated today
    Auto-check passed
  • Analytics

    ericrisco/rsc-harness

    A skill your agent uses when instrumenting product or web analytics — GA4/PostHog SDK wiring, event taxonomy, funnels, double-counted events, consent gating, PII scrubbing.

    156 GitHub stars~2.8k tokensUpdated today
    Auto-check passed

Categories

Questions about Testing Web

What does Testing Web do?

A skill your agent uses when writing or fixing frontend unit, component or custom-hook tests with Vitest or Jest plus Testing Library — rendering a component in jsdom, testing a hook in isolation…. Testing Web is an agent skill from ericrisco/rsc-harness. Use when writing or fixing frontend unit, component or custom-hook tests with Vitest or Jest plus Testing Library — rendering a component in jsdom, testing a hook in isolation, choosing between sync and async queries, silencing act warnings, mocking fetch, or migrating a Jest suite to Vitest.

When should I use Testing Web?

Testing Web fits situations like: fixing frontend unit; custom-hook tests with Vitest; jest plus Testing Library — rendering a component in jsdom; testing a hook in isolation.

How do I install Testing Web in Claude Code?

Run `npx skills add ericrisco/rsc-harness --skill testing-web -a claude-code`. Or copy the skill folder (skills/testing-web in ericrisco/rsc-harness) into .claude/skills/testing-web in your project. Claude Code loads it when a task matches its description.

How do I install Testing Web in Codex?

Run `npx skills add ericrisco/rsc-harness --skill testing-web -a codex`. Or copy the skill folder (skills/testing-web in ericrisco/rsc-harness) into .agents/skills/testing-web in your project. Codex loads it when a task matches its description.

Can I use Testing Web in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add ericrisco/rsc-harness --skill testing-web -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/testing-web, .gemini/skills/testing-web, .github/skills/testing-web and .opencode/skills/testing-web in your project.

What does Testing Web need to run?

Going by SKILL.md and its folder, Testing Web needs a shell for the scripts in its folder and the command-line tools its instructions call (npx and vitest). Our summary lists: Python 3; Node.js; A Bash shell.

Does Testing Web access the network?

SKILL.md contains no URLs. Its commands use npx, which can reach the network depending on how they are called. This is read from the text; nothing was executed.

Is Testing Web safe to install?

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. The check reads SKILL.md only: the scripts in the folder are not scanned, so read them before running anything.

What licence does Testing Web use?

Testing Web is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Testing Web use?

About 3.3k tokens (SKILL.md is roughly 13k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 1.7k tokens, read only when the agent opens those files.

What are the alternatives to Testing Web?

Skills that share tags, products or a category with Testing Web: React Testing (affaan-m/ECC, 274k stars), React Testing (HoangNguyen0403/agent-skills-standard, 570 stars), Test Guard (amElnagdy/guard-skills, 1.3k stars) and Designing Tests (CloudAI-X/claude-workflow-v2, 1.4k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Testing Web?

ericrisco (a GitHub user) maintains it in ericrisco/rsc-harness, which has 156 GitHub stars. The repository holds 229 skills in this directory. The repository was last updated on October 6, 2026.

Source: ericrisco/rsc-harness on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.