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,923
Forks
708

安装方式

默认使用会先检查来源的 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 查看