Skip to main content

component-testing

Use when authoring, modifying, reviewing, or debugging Primer React component and hook tests in Vitest browser mode. Do not use for Node/SSR tests, standalone Playwright visual or accessibility tests, or component implementation without test changes.

소스 정보

저장소
primer/react
최근 소스 활동
2026년 10월 23일 21:01
감지된 SKILL.md 언어
영어
스타
3,927
포크
711

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
component-testing
description
Use when authoring, modifying, reviewing, or debugging Primer React component and hook tests in Vitest browser mode. Do not use for Node/SSR tests, standalone Playwright visual or accessibility tests, or component implementation without test changes.
# Component Testing Use these conventions for Primer React component and hook tests in Vitest browser mode. Test observable behavior and the component's public contract rather than its implementation details. Read [the testing guide](../../../contributor-docs/testing.md) for strategy, environment setup, and commands. Keep Node/SSR tests and the separate Playwright visual and accessibility suites on the APIs available in their own environments. ## Start with RTL rendering and browser interactions Use React Testing Library (RTL) for `render`, `rerender`, `unmount`, `renderHook`, cleanup, and manual `act` boundaries. Use `page` and `userEvent` from `vitest/browser` for browser queries and interactions. ```tsx import {render} from '@testing-library/react' import {expect, it, vi} from 'vitest' import {page, userEvent} from 'vitest/browser' import {Button} from '@primer/react' it('calls onClick', async () => { const onClick = vi.fn() render(<Button onClick={onClick}>Submit</Button>) const button = page.getByRole('button', {name: 'Submit', exact: true}) await expect.element(button).toBeVisible() await userEvent.click(button) expect(onClick).toHaveBeenCalledTimes(1) }) ``` Await browser interactions directly, without wrapping them in `act`. ## Choose assertions based on when the result should be available | Contract | Assertion | | :---------------------------------------- | :------------------------------------------------------ | | Eventual DOM state | `await expect.element(locator).toHaveAttribute(...)` | | Eventual computed value | `await expect.poll(() => readCurrentValue()).toBe(...)` | | Immediate result or synchronous guarantee | `expect(value).toBe(...)` | Prefer assertions on observable conditions over fixed sleeps. This lets the test continue as soon as the expected state is available. ```tsx // Avoid await new Promise(resolve => setTimeout(resolve, 100)) expect(document.getElementById(announcementId)?.textContent).toBe('Loading') // Prefer await expect.poll(() => document.getElementById(announcementId)?.textContent).toBe('Loading') ``` Read the current value inside the polling callback so each attempt checks the latest state. Use ordinary `expect` when the result must be available immediately, such as after synchronous focus redirection, event cancellation, or explicit timer advancement. Retrying these assertions can hide a regression. When testing timed behavior, keep fake timers and advance them explicitly inside RTL's `act`. Assert the state at the required boundaries, including before the deadline when relevant, so the test verifies when the change occurs rather than only that it eventually occurs. ## Use RTL's act for manual React updates Import `act` from `@testing-library/react`, not `react`. Use it for manual updates that can trigger React work, including programmatic focus, timer advancement, hook updates, and hydration. ```tsx import {act} from '@testing-library/react' const button = page.getByRole('button', {name: 'Submit', exact: true}) act(() => { button.element().focus() }) expect(button.element()).toHaveFocus() ``` RTL scopes the React act environment to the operation. When an act warning occurs, check that manual updates use RTL's `act` and browser interactions are awaited directly. Keep that scoped behavior rather than changing `IS_REACT_ACT_ENVIRONMENT` globally or suppressing the warning. ## Use locators for current DOM state Prefer role and accessible-name queries. A locator resolves again each time it is used, so it must stay unambiguous after interactions. For example, expanding a tree can make a partial name match both a parent and its descendants. Use an exact accessible name or scope the query to the intended region. Prefer an exact accessible name or a scoped query over `.first()`. This keeps the test tied to the intended element when the DOM changes. Use positional selection when position is part of the behavior being tested. Use `.element()` when the assertion needs a DOM node, such as for computed styles, structural relationships, existing snapshots, or node identity. Capture the node before removal when checking that a particular node was detached: ```tsx const {rerender} = render(<Button>Submit</Button>) const originalButton = page.getByRole('button', {name: 'Submit', exact: true}).element() rerender(<></>) expect(originalButton).not.toBeInTheDocument() ``` ## Assert presence and visibility separately DOM presence and visibility describe different behavior. Assert removal when a node must be removed, and visibility when it may remain mounted but hidden. Role queries normally exclude inaccessible elements, so a missing role match alone doesn't prove that a hidden node was removed. For accessibility relationships, check the actual ID reference and the referenced node's content. A generic text match can select the wrong node, such as a spinner's "Loading" text instead of the live-region announcement. Precise DOM access is appropriate when testing that relationship. ## Keep intentional low-level event tests Use browser interactions for user behavior, but keep synthetic events when testing disabled-click suppression, event cancellation, or a specific event payload. Browser actions wait for actionable elements and don't expose the boolean result of `dispatchEvent`. If a browser action unexpectedly fails because an element cannot be interacted with, investigate its visibility, enabled state, and other interaction requirements before choosing a synthetic event.
GitHub에서 보기