install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/codex/test-component-react" ~/.claude/skills/intense-visions-harness-engineering-test-component-react-7c1d9f && rm -rf "$T"
manifest:
agents/skills/codex/test-component-react/SKILL.mdsource content
Test Component React
Test React components with Testing Library using user-centric queries and async utilities
When to Use
- Testing React component rendering, interaction, and state changes
- Verifying that components display the correct content
- Simulating user interactions (clicks, typing, form submission)
- Testing async components that fetch data or show loading states
Instructions
- Render and query — use
andrender
:screen
import { render, screen } from '@testing-library/react'; import { UserCard } from './user-card'; it('displays the user name', () => { render(<UserCard user={{ name: 'Alice', email: 'alice@test.com' }} />); expect(screen.getByText('Alice')).toBeInTheDocument(); expect(screen.getByText('alice@test.com')).toBeInTheDocument(); });
- Use accessible queries in priority order:
— buttons, links, headings, form elementsgetByRole
— form inputs by labelgetByLabelText
— inputs by placeholdergetByPlaceholderText
— visible text contentgetByText
— last resort, for elements without accessible namesgetByTestId
it('renders a submit button', () => { render(<LoginForm />); expect(screen.getByRole('button', { name: 'Submit' })).toBeInTheDocument(); expect(screen.getByLabelText('Email')).toBeInTheDocument(); });
- Simulate user interactions with
:userEvent
import userEvent from '@testing-library/user-event'; it('submits the form with entered data', async () => { const onSubmit = vi.fn(); render(<LoginForm onSubmit={onSubmit} />); const user = userEvent.setup(); await user.type(screen.getByLabelText('Email'), 'alice@test.com'); await user.type(screen.getByLabelText('Password'), 'secret123'); await user.click(screen.getByRole('button', { name: 'Log in' })); expect(onSubmit).toHaveBeenCalledWith({ email: 'alice@test.com', password: 'secret123', }); });
- Test async behavior with
andwaitFor
:findBy
it('shows user data after loading', async () => { render(<UserProfile userId="123" />); expect(screen.getByText('Loading...')).toBeInTheDocument(); // findBy waits for the element to appear (default 1000ms timeout) expect(await screen.findByText('Alice')).toBeInTheDocument(); expect(screen.queryByText('Loading...')).not.toBeInTheDocument(); });
- Test conditional rendering:
it('shows error message for invalid input', async () => { render(<EmailInput />); const user = userEvent.setup(); await user.type(screen.getByLabelText('Email'), 'not-an-email'); await user.tab(); // Trigger blur validation expect(screen.getByText('Please enter a valid email')).toBeInTheDocument(); });
- Verify absence with
(returns null instead of throwing):queryBy
it('does not show admin panel for regular users', () => { render(<Dashboard user={{ role: 'user' }} />); expect(screen.queryByText('Admin Panel')).not.toBeInTheDocument(); });
- Test with context providers:
function renderWithProviders(ui: React.ReactElement) { return render( <ThemeProvider theme={defaultTheme}> <AuthProvider value={mockAuth}> {ui} </AuthProvider> </ThemeProvider> ); } it('uses theme colors', () => { renderWithProviders(<Button>Click me</Button>); expect(screen.getByRole('button')).toHaveStyle({ color: 'blue' }); });
- Prefer
overuserEvent
—fireEvent
simulates real browser behavior (focus, keyboard events, click sequence):userEvent
// Good — fires focus, keydown, keypress, input, keyup for each character await user.type(input, 'hello'); // Less realistic — fires only the change event fireEvent.change(input, { target: { value: 'hello' } });
Details
Testing Library's philosophy is "test the way users interact with your app." Tests should not know about component internals (state, props, hooks) — they should only interact through the rendered DOM.
Query types:
— returns element or throws. Use when the element must be presentgetBy
— returns element or null. Use when asserting absencequeryBy
— returns a Promise. Use for elements that appear asynchronouslyfindBy- All have
variants that return arraysAllBy
: Always call userEvent.setup()
userEvent.setup() before interactions. This creates a user session with proper event sequencing. Do not use the older userEvent.click() static methods.
jsdom limitations: Testing Library runs in jsdom, which does not implement layout.
getComputedStyle, getBoundingClientRect, and scroll behavior do not work. Use Playwright for visual and layout testing.
Trade-offs:
- User-centric testing avoids testing implementation details — but some internal states are hard to observe through the DOM
encourages accessible markup — but can be frustrating when role names are not obviousgetByRole
is realistic — but slower thanuserEvent
. UsefireEvent
for simple cases in large test suitesfireEvent
andwaitFor
handle async — but can mask slow components. Set explicit timeouts in CIfindBy
Source
https://testing-library.com/docs/react-testing-library/intro/
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.