React Testing
getsentry/sentry
Write and review React/TypeScript tests for Sentry's frontend using Jest and React Testing Library.
Jest + React Testing Library best practices for Wonder Blocks unit tests.
$ npx skills add Khan/wonder-blocks --skill unit-tests -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install Khan/wonder-blocks unit-tests --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/Khan/wonder-blocks.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/unit-tests .claude/skills/unit-tests && 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 "unit-tests" agent skill from https://github.com/Khan/wonder-blocks/tree/main/.agents/skills/unit-tests into .claude/skills/unit-tests/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "unit-tests", 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/Khan/wonder-blocks/tree/main/.agents/skills/unit-testsType 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 Khan/wonder-blocks --skill unit-tests -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install Khan/wonder-blocks unit-tests --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Khan/wonder-blocks.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/unit-tests .agents/skills/unit-tests && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "unit-tests" agent skill from https://github.com/Khan/wonder-blocks/tree/main/.agents/skills/unit-tests into .agents/skills/unit-tests/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "unit-tests", 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 Khan/wonder-blocks --skill unit-tests -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install Khan/wonder-blocks unit-tests --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Khan/wonder-blocks.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/unit-tests .cursor/skills/unit-tests && 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 "unit-tests" agent skill from https://github.com/Khan/wonder-blocks/tree/main/.agents/skills/unit-tests into .cursor/skills/unit-tests/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "unit-tests", 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/Khan/wonder-blocks.git --path .agents/skills/unit-tests--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 Khan/wonder-blocks --skill unit-tests -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install Khan/wonder-blocks unit-tests --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Khan/wonder-blocks.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/unit-tests .gemini/skills/unit-tests && 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 "unit-tests" agent skill from https://github.com/Khan/wonder-blocks/tree/main/.agents/skills/unit-tests into .gemini/skills/unit-tests/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "unit-tests", 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 Khan/wonder-blocks unit-testsInstalls 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 Khan/wonder-blocks --skill unit-tests -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/Khan/wonder-blocks.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/unit-tests .github/skills/unit-tests && 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 "unit-tests" agent skill from https://github.com/Khan/wonder-blocks/tree/main/.agents/skills/unit-tests into .github/skills/unit-tests/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "unit-tests", 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 Khan/wonder-blocks --skill unit-tests -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install Khan/wonder-blocks unit-tests --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Khan/wonder-blocks.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/unit-tests .opencode/skills/unit-tests && 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 "unit-tests" agent skill from https://github.com/Khan/wonder-blocks/tree/main/.agents/skills/unit-tests into .opencode/skills/unit-tests/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "unit-tests", 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.
unit-testsJest + React Testing Library best practices for Wonder Blocks unit tests.
Unit Tests is an agent skill from Khan/wonder-blocks. Jest + React Testing Library best practices for Wonder Blocks unit tests. Use when creating or editing .test.ts / .test.tsx files.
Its SKILL.md is about 4.2k 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 React components. It works with Jest, Testing Library and React. The repository describes itself as: React components for Wonder Blocks design system. The licence is MIT.
4 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 1ba546c. 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.
Shell commands in SKILL.md call:
pnpmFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use pnpm, which can reach the network depending on how they are called.
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.
Unit Tests loads about 4.2k tokens when it runs. Until then it costs about 36 tokens; SKILL.md has 1,290 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 Khan/wonder-blocks at commit 1ba546c, republished under its MIT licence (© Khan). 1,290 words, ~4,180 tokens.
.claude/skills/unit-tests/SKILL.md (or your agent's skills folder).This guide covers testing patterns and best practices for Jest and React Testing Library in the Wonder Blocks codebase.
Test Workflow Priority:
Unhandled console.error call messagesUnhandled console.error call, look for the root cause error (e.g., ReferenceError: window is not defined)File Structure:
.test.ts or .test.tsx suffix__tests__/ directory OR colocate with source files (follow local conventions)Test Framework Setup:
jest-extended are availabledescribe/it pattern for test organizationglobalThis prefix when accessing global objects⚠️ ALWAYS use this three-section structure:
describe("Calculator", () => {
it("should add two numbers correctly", () => {
// Arrange
const a = 5;
const b = 3;
// Act
const result = add(a, b);
// Assert
expect(result).toBe(8);
});
});Rules:
// Arrange, // Act, // Assert) — no additional comments within a section// Act & Assert)Exception - Testing Thrown Errors:
When testing errors, use an underTest variable in the Act section:
it("should throw an error when input is invalid", () => {
// Arrange
const invalidInput = "invalid";
// Act
const underTest = () => {
processInput(invalidInput);
};
// Assert
expect(underTest).toThrow("Invalid input");
});⚠️ Focus on what matters - don't overdo it:
DO Test:
DON'T Test:
// ❌ DON'T: Testing style-only props (use visual regression tests instead)
it("should apply primary color when kind is primary", () => {
render(<Button kind="primary" />);
expect(screen.getByRole("button")).toHaveStyle({ backgroundColor: "blue" });
});
// ✅ DO: Test meaningful behavior
it("should call onClick when clicked", async () => {
// Arrange
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
// Act
await userEvent.click(screen.getByRole("button"));
// Assert
expect(handleClick).toHaveBeenCalledTimes(1);
});
// ✅ DO: Test non-trivial logic
it("should validate email format and return error message", () => {
// Arrange
const invalidEmail = "not-an-email";
// Act
const result = validateEmail(invalidEmail);
// Assert
expect(result).toBe("Please enter a valid email address");
});Key Principles:
Best Practices:
toBe, toEqual, toHaveBeenCalledWith)toBeInTheDocument(), toBeVisible(), toHaveAttribute().toMatchSnapshot(), .toMatchInlineSnapshot()) - use Chromatic + Storybook for visual regression tests, or use specific attribute assertions instead⚠️ Each test should have exactly one expect. If you need to assert multiple things, split them into separate tests. Multiple assertions hide which behavior actually broke when the test fails.
it.eachWhen to use: Testing the same logic with multiple input/output combinations
✅ DO: Use it.each for data-driven tests
describe("Calculator", () => {
it.each([
[2, 3, 5],
[0, 0, 0],
[-1, 1, 0],
[10, -5, 5],
])("should add %i and %i to equal %i", (a, b, expected) => {
// Arrange
// (inputs come from it.each)
// Act
const result = add(a, b);
// Assert
expect(result).toBe(expected);
});
});Benefits:
console.error - This hides real implementation issues and errorsjest.spyOn() to create spies - Never treat the original function as though it were a spy✅ DO: Use jest.spyOn and store the result when asserting
import * as SomeFile from "./some-file.ts";
describe("MyComponent", () => {
it("should call someMethod with correct args", () => {
// Arrange
// Store spy because we'll assert on it later
const spy = jest.spyOn(SomeFile, "someMethod").mockReturnValue(mockValue);
// Act
myFunction();
// Assert
expect(spy).toHaveBeenCalledWith(expectedArgs);
});
});❌ DON'T: Treat the original function as a spy without jest.spyOn()
// ❌ WRONG - This will fail because someMethod is not a spy
import * as SomeFile from "./some-file.ts";
describe("MyComponent", () => {
it("should call someMethod", () => {
// Act
myFunction();
// Assert
expect(SomeFile.someMethod).toHaveBeenCalled(); // ❌ ERROR! Not a spy
});
});Spies serve two purposes:
✅ Mocking only (no variable needed):
it("should process user data", () => {
// Arrange
// Mock the API call to return test data, but don't store it
jest.spyOn(API, "fetchUser").mockResolvedValue(mockUserData);
// Act
const result = processUserProfile();
// Assert
// We're testing processUserProfile's logic, not that fetchUser was called
expect(result.displayName).toBe("John Doe");
// No spy variable = no unused variable linter error
});✅ Mocking AND verification (store in variable):
it("should call analytics when button is clicked", () => {
// Arrange
// Store the spy because we'll assert on it
const trackEventSpy = jest
.spyOn(Analytics, "trackEvent")
.mockReturnValue(undefined);
// Act
userEvent.click(screen.getByRole("button"));
// Assert
// We're testing that the analytics call happens correctly
expect(trackEventSpy).toHaveBeenCalledWith("button_click", {
buttonId: "submit",
});
});Key point: Only store the spy in a variable if you're going to assert on it. This avoids unused variable linter errors while still allowing you to verify calls when needed.
Mock only (no variable):
// When you only need to control the return value
jest.spyOn(module, "functionName").mockReturnValue(mockValue);
jest.spyOn(module, "asyncFunction").mockResolvedValue(mockValue);
jest.spyOn(module, "asyncFunction").mockRejectedValue(new Error("Test error"));Mock and verify (store in variable):
// When you need to assert the spy was called
const spy = jest.spyOn(module, "functionName").mockReturnValue(mockValue);
// ... later in Assert section:
expect(spy).toHaveBeenCalledWith(expectedArgs);Spy with mock implementation:
// Store only if you'll verify it was called
const spy = jest.spyOn(module, "functionName").mockImplementation((arg) => {
return processedValue;
});DO:
jest.spyOn() for mocking functions and tracking calls.mockReturnValue() or similar directly on jest.spyOn() when only mocking behaviorclearAllMocks)DON'T:
console.error - this hides real implementation issuesjest.spyOn()✅ Use renderHook:
import {renderHook} from "@testing-library/react";
// Direct for simple hooks
const {result} = renderHook(() => useMyHook(params));✅ ALWAYS use userEvent for interactions:
import userEvent from "@testing-library/user-event";
// ✅ DO: Use userEvent (realistic, includes focus/blur/typing)
await userEvent.click(screen.getByRole("button"));
await userEvent.type(screen.getByRole("textbox"), "hello");
// ❌ DON'T: Use fireEvent (low-level, less realistic)
fireEvent.click(button);⚠️ jsdom does not fully implement all browser behaviors. Common limitations include: getBoundingClientRect(), scroll positions, offsetWidth/offsetHeight, clipboard API, CSS animations, and Intersection/Resize Observers.
✅ Mock browser APIs when testing in unit tests:
// Mock scrollIntoView
const scrollIntoViewMock = jest.fn();
Element.prototype.scrollIntoView = scrollIntoViewMock;
// Mock getBoundingClientRect
jest.spyOn(Element.prototype, "getBoundingClientRect").mockReturnValue({
top: 100, left: 100, bottom: 200, right: 200,
width: 100, height: 100, x: 100, y: 100, toJSON: () => {},
});✅ Use Storybook interaction tests for behavior that's difficult to mock accurately (scroll, layout, clipboard, complex focus management). See .agents/skills/storybook/SKILL.md for details.
Query priority (use in this order):
getByRole, getByLabelText, getByTextgetByTestId with data-testid attribute❌ NEVER access DOM nodes directly. Always use Testing Library queries. Direct node access couples tests to implementation structure, not behavior.
// ✅ DO: Testing Library queries
screen.getByRole("button", {name: /submit/i});
screen.getByLabelText("Email address");
screen.findByText("Welcome back");
screen.getByTestId("custom-widget");
// ❌ DON'T: CSS selectors
container.querySelector(".my-class");
container.querySelector("#my-id");
// ❌ DON'T: Direct node traversal
element.parentElement;
element.children[0];
element.firstChild;
element.nextSibling;✅ Group related tests using describe blocks:
describe("MyComponent", () => {
describe("Props", () => { /* prop tests */ });
describe("Event Handlers", () => { /* onClick, onChange tests */ });
describe("Accessibility", () => {
describe("axe", () => { /* toHaveNoA11yViolations tests */ });
describe("ARIA", () => { /* aria attribute tests */ });
describe("Focus", () => { /* focus management tests */ });
describe("Keyboard Interactions", () => { /* keyboard nav tests */ });
});
});Unit tests for a component should cover:
it.each when there are multiple combinations of things you want to test together.toHaveNoA11yViolations jest matcher to confirm that a component doesn't have accessibility warningsaria-disabled="true" for determining disabled state (not the disabled attribute)# Run all tests
pnpm jest
# Run tests in watch mode
pnpm jest --watch
# Update snapshots
pnpm jest -u
# Run tests with coverage
pnpm jest --coverage
# Run specific test file
pnpm jest path/to/test-file.test.ts
# Debug with verbose output
pnpm jest --verbose --runInBandTerminal Commands
console.log statements for debugging--verbose flag for detailed output--runInBand for sequential execution (easier to debug)debugger statementsgetByRole, getByLabelText) over test IDsuserEvent instead of fireEventjest.spyOn() and only store in variables when assertingtoHaveNoA11yViolations testsdescribe blocksexpect per test — split into separate tests if you need moreit.each for testing multiple input/output combinationsconsole.error, over-testing trivial code, adding logic to tests© Khan, MIT. 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/unit-tests of Khan/wonder-blocks.
Open the folder on GitHubat commit 1ba546c
Unit Tests 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 |
|---|---|---|---|---|---|---|
| Unit Tests this skillKhan/wonder-blocks | 163 | — | ~4.2k | Automated safety check: Pass | MIT | |
| React Testinggetsentry/sentry | 45k | — | ~2.2k | Automated safety check: Pass | Custom licence | |
| React Testingaffaan-m/ECC | 274k | 1 repos | ~3.3k | Automated safety check: Pass | MIT | |
| React TestingHoangNguyen0403/agent-skills-standard | 570 | — | ~1.3k | Automated safety check: Pass | MIT | |
| Write Testsgrafana/synthetic-monitoring-app | 171 | — | ~1.2k | Automated safety check: Pass | AGPL-3.0 | |
| Testingredis/RedisInsight | 8.9k | — | ~3.3k | Automated safety check: Pass | Custom licence |
getsentry/sentry
Write and review React/TypeScript tests for Sentry's frontend using Jest and React Testing Library.
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…
HoangNguyen0403/agent-skills-standard
Test React components with RTL and Jest/Vitest. An agent skill from HoangNguyen0403/agent-skills-standard.
grafana/synthetic-monitoring-app
Write Jest integration and unit tests for the Grafana Synthetic Monitoring app using React Testing Library, MSW, and src/test helpers.
redis/RedisInsight
Unit/integration testing standards for RedisInsight using Jest and Testing Library: test structure, the renderComponent helper, faker for test data, mocking patterns, and waitFor instead of fixed…
epam/UUI
Helps write unit tests for UUI components using Jest, jsdom, and @epam/uui-test-utils.
Khan/wonder-blocks
Implements user interfaces using the Wonder Blocks (WB) design system — Khan Academy's React component library.
Khan/wonder-blocks
Storybook best practices for Wonder Blocks component stories.
Works with
Categories
Jest + React Testing Library best practices for Wonder Blocks unit tests. Unit Tests is an agent skill from Khan/wonder-blocks. Jest + React Testing Library best practices for Wonder Blocks unit tests.
Unit Tests fits situations like: editing .test.ts / .test.tsx files; tasks that involve Unit testing; tasks that involve React components.
Run `npx skills add Khan/wonder-blocks --skill unit-tests -a claude-code`. Or copy the skill folder (.agents/skills/unit-tests in Khan/wonder-blocks) into .claude/skills/unit-tests in your project. Claude Code loads it when a task matches its description.
Run `npx skills add Khan/wonder-blocks --skill unit-tests -a codex`. Or copy the skill folder (.agents/skills/unit-tests in Khan/wonder-blocks) into .agents/skills/unit-tests 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 Khan/wonder-blocks --skill unit-tests -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/unit-tests, .gemini/skills/unit-tests, .github/skills/unit-tests and .opencode/skills/unit-tests in your project.
Going by SKILL.md and its folder, Unit Tests needs the command-line tools its instructions call (pnpm).
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.
Unit Tests is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 4.2k tokens (SKILL.md is roughly 17k 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 Unit Tests: React Testing (getsentry/sentry, 45k stars), React Testing (affaan-m/ECC, 274k stars), React Testing (HoangNguyen0403/agent-skills-standard, 570 stars) and Write Tests (grafana/synthetic-monitoring-app, 171 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
Khan (a GitHub organization) maintains it in Khan/wonder-blocks, which has 163 GitHub stars. The repository holds 3 skills in this directory. The repository was last updated on October 6, 2026.
Source: Khan/wonder-blocks on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.