testing-library
Test UI components the way users interact with them using Testing Library — query by role, text, and label instead of implementation details. Use when someone asks to "test React components", "Testing Library", "user-centric testing", "test accessibility", "test without implementation details", or "render and query components in tests". Covers React Testing Library, queries, user events, async testing, and accessibility assertions.
Best use case
testing-library is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Test UI components the way users interact with them using Testing Library — query by role, text, and label instead of implementation details. Use when someone asks to "test React components", "Testing Library", "user-centric testing", "test accessibility", "test without implementation details", or "render and query components in tests". Covers React Testing Library, queries, user events, async testing, and accessibility assertions.
Teams using testing-library should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/testing-library/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How testing-library Compares
| Feature / Agent | testing-library | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Test UI components the way users interact with them using Testing Library — query by role, text, and label instead of implementation details. Use when someone asks to "test React components", "Testing Library", "user-centric testing", "test accessibility", "test without implementation details", or "render and query components in tests". Covers React Testing Library, queries, user events, async testing, and accessibility assertions.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Testing Library
## Overview
Testing Library tests UI components from the user's perspective — find elements by their accessible role, text content, or label, not by CSS class or test ID. If a user can't find a button, your test shouldn't find it either. This approach catches accessibility issues by default, survives refactors (rename a CSS class and tests still pass), and produces tests that actually prove the UI works.
## When to Use
- Testing React/Vue/Svelte components
- Want tests that survive refactoring (no CSS selectors or internal state checks)
- Need to verify accessibility (ARIA roles, labels)
- Testing user interactions (clicks, typing, form submission)
- Integration testing of component behavior (not snapshot testing)
## Instructions
### Setup
```bash
npm install -D @testing-library/react @testing-library/jest-dom @testing-library/user-event
# For Vitest:
npm install -D @testing-library/react vitest happy-dom
```
```typescript
// vitest.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
environment: "happy-dom",
setupFiles: ["./tests/setup.ts"],
},
});
```
```typescript
// tests/setup.ts — Global test setup
import "@testing-library/jest-dom/vitest";
```
### Query Priority
Testing Library has a priority order for queries — use the highest priority that works:
```typescript
// Priority 1: Accessible roles (best — tests accessibility too)
screen.getByRole("button", { name: "Submit" });
screen.getByRole("textbox", { name: "Email" });
screen.getByRole("heading", { level: 1 });
// Priority 2: Label text (forms)
screen.getByLabelText("Email address");
// Priority 3: Placeholder text
screen.getByPlaceholderText("Search...");
// Priority 4: Text content
screen.getByText("Welcome back!");
// Priority 5: Display value
screen.getByDisplayValue("user@example.com");
// Priority 6: Alt text (images)
screen.getByAltText("Company logo");
// Last resort: test IDs (avoid if possible)
screen.getByTestId("complex-widget");
```
### Testing Component Behavior
```tsx
// LoginForm.test.tsx — Test a login form from the user's perspective
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { LoginForm } from "./LoginForm";
describe("LoginForm", () => {
it("submits the form with email and password", async () => {
const onSubmit = vi.fn();
const user = userEvent.setup();
render(<LoginForm onSubmit={onSubmit} />);
// Find elements by their accessible role/label
await user.type(screen.getByLabelText("Email"), "kai@example.com");
await user.type(screen.getByLabelText("Password"), "secret123");
await user.click(screen.getByRole("button", { name: "Sign in" }));
expect(onSubmit).toHaveBeenCalledWith({
email: "kai@example.com",
password: "secret123",
});
});
it("shows validation error for invalid email", async () => {
const user = userEvent.setup();
render(<LoginForm onSubmit={vi.fn()} />);
await user.type(screen.getByLabelText("Email"), "not-an-email");
await user.click(screen.getByRole("button", { name: "Sign in" }));
expect(screen.getByRole("alert")).toHaveTextContent("Invalid email address");
});
it("disables submit while loading", async () => {
render(<LoginForm onSubmit={() => new Promise(() => {})} />);
const user = userEvent.setup();
await user.type(screen.getByLabelText("Email"), "kai@example.com");
await user.type(screen.getByLabelText("Password"), "secret123");
await user.click(screen.getByRole("button", { name: "Sign in" }));
expect(screen.getByRole("button", { name: /signing in/i })).toBeDisabled();
});
});
```
### Async Testing
```tsx
// UserProfile.test.tsx — Testing async data fetching
import { render, screen, waitFor } from "@testing-library/react";
import { UserProfile } from "./UserProfile";
it("loads and displays user data", async () => {
render(<UserProfile userId="123" />);
// Loading state
expect(screen.getByText("Loading...")).toBeInTheDocument();
// Wait for data to load
await waitFor(() => {
expect(screen.getByRole("heading")).toHaveTextContent("Kai Chen");
});
expect(screen.getByText("kai@example.com")).toBeInTheDocument();
});
it("shows error state on failure", async () => {
// Mock API failure
server.use(http.get("/api/users/123", () => HttpResponse.error()));
render(<UserProfile userId="123" />);
await waitFor(() => {
expect(screen.getByRole("alert")).toHaveTextContent("Failed to load");
});
});
```
## Examples
### Example 1: Test a complex form with validation
**User prompt:** "Write tests for a multi-step registration form with validation."
The agent will write tests that fill each step from the user's perspective, verify validation errors appear on invalid input, and confirm successful submission.
### Example 2: Test an accessible data table
**User prompt:** "Test a sortable data table — verify sorting, filtering, and pagination work."
The agent will query table headers by role, click to sort, verify row order changes, type into filter input, and navigate pages.
## Guidelines
- **Query by role first** — `getByRole` tests accessibility for free
- **`userEvent` over `fireEvent`** — simulates real user behavior (focus, type, blur)
- **`userEvent.setup()` at test start** — creates a user instance for the test
- **`waitFor` for async** — wait for elements to appear after data fetching
- **Avoid `getByTestId`** — if you need it, the component might have accessibility issues
- **`screen` is global** — no need to destructure render result
- **`toBeInTheDocument()` from jest-dom** — readable assertions
- **Don't test implementation** — state values, effect triggers, re-render counts
- **Test behavior, not structure** — "user sees error" not "error div has class active"Related Skills
webapp-testing
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
playwright-testing
Write and maintain end-to-end tests with Playwright. Use when someone asks to "add e2e tests", "test my web app", "set up Playwright", "write browser tests", "test login flow", "visual regression testing", "test across browsers", or "automate UI testing". Covers test setup, page objects, authentication, API mocking, visual comparisons, and CI integration.
wxt
Build cross-browser extensions with WXT — the modern framework for Chrome, Firefox, Safari, and Edge extensions. Use when someone asks to "build a browser extension", "Chrome extension with React", "WXT framework", "cross- browser extension", "manifest v3 extension", "build Firefox extension", or "browser extension with TypeScript". Covers content scripts, background workers, popup/options pages, storage, messaging, and publishing.
web-design-guidelines
Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices".
vitest
Assists with unit and integration testing using Vitest, a Vite-native test runner. Use when writing tests, configuring mocks, setting up coverage, or migrating from Jest. Trigger words: vitest, unit testing, test runner, vi.fn, vi.mock, test coverage, jest replacement.
vite
Assists with configuring and using Vite as a frontend build tool for modern web applications. Use when setting up dev servers, optimizing production builds, configuring plugins, migrating from Webpack or CRA, or building component libraries. Trigger words: vite, build tool, HMR, hot module replacement, vite config, rollup, bundling.
vercel-react-best-practices
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
vercel-composition-patterns
React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.
tailwindcss
Build UIs with Tailwind CSS — utility classes, responsive design, dark mode, custom configuration, component patterns, animations, plugins, and design system setup. Use when tasks involve styling web applications, configuring design tokens, building responsive layouts, or migrating from other CSS approaches.
skill-creator
Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.
shadcn-ui
Expert guidance for integrating and building applications with shadcn/ui components, including component discovery, installation, customization, and best practices.
security-audit
Scan code for security vulnerabilities, misconfigurations, and exposed secrets. Use when a user asks to audit security, find vulnerabilities, check for OWASP issues, scan for secrets, review dependencies for CVEs, detect SQL injection, find XSS vulnerabilities, or harden an application. Covers OWASP Top 10, dependency auditing, secrets detection, and generates fix recommendations with severity ratings.