用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/joabgonzalez/ai-agents-skills --skill react-testing-library命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Markdown docs with Mermaid diagrams and tree layouts. Trigger: When writing architecture, spike, or implementation docs.
Paste-ready session summary for context transfer to a new chat. Trigger: User says 'context handoff', 'start fresh', or session needs to continue.
One-at-a-time questioning to fully profile a goal before acting. Trigger: User says 'grill me', goal is vague, or clarification is needed first.
正在显示 SKILL.md
基于 SOC 职业分类
| name | react-testing-library |
| description | User-centric React component testing. Trigger: When testing React components with RTL. |
| license | Apache 2.0 |
| metadata | {"version":"1.1","type":"tooling","skills":["react","jest","unit-testing"],"dependencies":{"@testing-library/react":">=14.0.0 <15.0.0"}} |
Tests components the way users interact with them -- querying by accessible roles and text, not implementation details.
Don't use for:
// CORRECT: use screen for all queries
render(<LoginForm />);
const button = screen.getByRole('button', { name: /submit/i });
// WRONG: destructuring queries from render
const { getByRole } = render(<LoginForm />);
import userEvent from '@testing-library/user-event';
// CORRECT: realistic user simulation
const user = userEvent.setup();
await user.type(screen.getByRole('textbox', { name: /email/i }), 'ada@test.com');
// WRONG: skips intermediate events
fireEvent.change(input, { target: { value: 'ada@test.com' } });
Prefer: getByRole > getByLabelText > getByText > getByTestId.
// CORRECT: role query with accessible name
screen.getByRole('heading', { name: /welcome/i });
// WRONG: test-id as first resort
screen.getByTestId('welcome-heading');
// CORRECT: findByRole waits for element to appear
await screen.findByRole('alert', { name: /success/i });
// WRONG: getByRole throws immediately if not in DOM
screen.getByRole('alert', { name: /success/i });
// CORRECT: assert on visible output
await user.click(screen.getByRole('button', { name: /add to cart/i }));
expect(screen.getByText(/1 item in cart/i)).toBeInTheDocument();
// WRONG: reaching into component internals
expect(wrapper.state('cartCount')).toBe(1);
Use queryBy* (never getBy*) for negative DOM assertions — getBy* throws if absent, making .not assertions unreliable.
// ✅ CORRECT: queryBy* returns null when absent
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
expect(screen.queryByText(/error/i)).toBeNull();
// After dismissing a modal:
await user.click(screen.getByRole('button', { name: /close/i }));
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
// ❌ WRONG: getBy* throws before .not can evaluate
expect(screen.getByRole('alert')).not.toBeInTheDocument(); // always throws
See unit-testing skill for the broader strategy of testing both presence and absence.
Element present now?
→ getByRole / getByText
Appears after async?
→ findByRole / findByText
Should NOT exist?
→ queryByRole (returns null)
User input?
→ userEvent.setup() then user.type(), user.click()
No accessible query?
→ Add aria-label; getByTestId last resort
Custom hook?
→ renderHook(() => useMyHook())
Side effects?
→ waitFor(() => expect(...))
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ContactForm } from './ContactForm';
describe('ContactForm', () => {
it('should submit and show success', async () => {
const onSubmit = jest.fn().mockResolvedValue({ ok: true });
const user = userEvent.setup();
render(<ContactForm onSubmit={onSubmit} />);
await user.type(screen.getByRole('textbox', { name: /name/i }), 'Ada');
await user.type(screen.getByRole('textbox', { name: /email/i }), 'ada@test.com');
await user.click(screen.getByRole('button', { name: /send/i }));
expect(onSubmit).toHaveBeenCalledWith({ : , : });
( screen.()).();
});
});
screen queries since portals render outside parent DOMwaitFor when state updates after await or setTimeoutfindBy* handles automaticallyrenderWithProviders wrapper for context (theme, router, store)cleanup automatically with Jest; do not call manuallyuserEvent.setup() used instead of fireEventfindBy* or waitFor, never manual delaysrenderWithProviders wraps components needing context