agentsclimarketplace

React testing

Skill Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack/plugins/devtools-pack/skills/react-testing

When to activate: React Testing Library, vitest, jest, component tests, user-event, MSW, accessibility queries, async testingFrom its SKILL.md

Install
npx -y skills add Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack --skill react-testing

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

5.8 KB, ~1.4k tokens by cl100k_base, as published. Nobody here has run it

React Testing Patterns

Setup

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

export default defineConfig({
  plugins: [react()],
  test: {
    environment: 'jsdom',
    globals: true,
    setupFiles: ['./src/test/setup.ts'],
    coverage: { thresholds: { lines: 80, functions: 80, branches: 80 } },
  },
})

// src/test/setup.ts
import '@testing-library/jest-dom'
import { cleanup } from '@testing-library/react'
import { afterEach } from 'vitest'

afterEach(() => { cleanup() })

Component Rendering

import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

// Wrap with providers
function renderWithProviders(ui: React.ReactElement) {
  const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } })
  return render(
    <QueryClientProvider client={queryClient}>
      <RouterProvider router={createMemoryRouter([{ path: '/', element: ui }])} />
    </QueryClientProvider>
  )
}

Accessible Queries (priority order)

// 1. By role — best (mirrors how users/AT perceive the element)
screen.getByRole('button', { name: /submit/i })
screen.getByRole('textbox', { name: /email/i })
screen.getByRole('heading', { level: 1 })
screen.getByRole('link', { name: /home/i })

// 2. By label text
screen.getByLabelText(/email address/i)

// 3. By placeholder text
screen.getByPlaceholderText(/search/i)

// 4. By text
screen.getByText(/welcome back/i)

// 5. By test id (last resort)
screen.getByTestId('hero-section')

User Interactions

describe('LoginForm', () => {
  it('submits with valid credentials', async () => {
    const user = userEvent.setup()
    const onSubmit = vi.fn()
    render(<LoginForm onSubmit={onSubmit} />)

    await user.type(screen.getByLabelText(/email/i), '[email protected]')
    await user.type(screen.getByLabelText(/password/i), 'secret123')
    await user.click(screen.getByRole('button', { name: /log in/i }))

    expect(onSubmit).toHaveBeenCalledWith({
      email: '[email protected]',
      password: 'secret123',
    })
  })

  it('shows error for invalid email', async () => {
    const user = userEvent.setup()
    render(<LoginForm onSubmit={vi.fn()} />)

    await user.type(screen.getByLabelText(/email/i), 'not-an-email')
    await user.click(screen.getByRole('button', { name: /log in/i }))

    expect(screen.getByRole('alert')).toHaveTextContent(/invalid email/i)
  })
})

Async & API Testing with MSW

// src/test/mocks/handlers.ts
import { http, HttpResponse } from 'msw'

export const handlers = [
  http.get('/api/users', () => {
    return HttpResponse.json([
      { id: '1', name: 'Alice', email: '[email protected]' },
    ])
  }),

  http.post('/api/users', async ({ request }) => {
    const body = await request.json() as any
    return HttpResponse.json({ id: '2', ...body }, { status: 201 })
  }),
]

// src/test/setup.ts (add to existing)
import { setupServer } from 'msw/node'
import { handlers } from './mocks/handlers'

const server = setupServer(...handlers)
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
// Component test with data fetching
it('renders user list', async () => {
  renderWithProviders(<UserList />)

  // Wait for async content
  expect(await screen.findByText('Alice')).toBeInTheDocument()
})

// Override handler for specific test
it('shows error on API failure', async () => {
  server.use(
    http.get('/api/users', () => HttpResponse.json({ error: 'Server error' }, { status: 500 }))
  )
  renderWithProviders(<UserList />)
  expect(await screen.findByRole('alert')).toBeInTheDocument()
})

Custom Hook Testing

import { renderHook, act } from '@testing-library/react'

describe('useCounter', () => {
  it('increments count', () => {
    const { result } = renderHook(() => useCounter(0))

    act(() => { result.current.increment() })

    expect(result.current.count).toBe(1)
  })

  it('resets to initial value', () => {
    const { result } = renderHook(() => useCounter(5))

    act(() => {
      result.current.increment()
      result.current.reset()
    })

    expect(result.current.count).toBe(5)
  })
})

Snapshot Testing (selective use)

// Use for stable, purely-presentational components
it('renders loading skeleton', () => {
  const { container } = render(<UserSkeleton />)
  expect(container.firstChild).toMatchSnapshot()
})

Accessibility Testing

import { axe, toHaveNoViolations } from 'jest-axe'
expect.extend(toHaveNoViolations)

it('has no accessibility violations', async () => {
  const { container } = render(<LoginForm onSubmit={vi.fn()} />)
  const results = await axe(container)
  expect(results).toHaveNoViolations()
})

Testing Common Patterns

Modal / Dialog

it('opens and closes modal', async () => {
  const user = userEvent.setup()
  render(<ConfirmDialog />)

  await user.click(screen.getByRole('button', { name: /delete/i }))
  expect(screen.getByRole('dialog')).toBeInTheDocument()

  await user.click(screen.getByRole('button', { name: /cancel/i }))
  expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
})

Form Validation

it('disables submit button while loading', async () => {
  const user = userEvent.setup()
  render(<CreatePostForm />)

  await user.type(screen.getByLabelText(/title/i), 'My Post')
  await user.click(screen.getByRole('button', { name: /create/i }))

  expect(screen.getByRole('button', { name: /creating/i })).toBeDisabled()
})

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.