From 0cb2e49483e166b2e9152c9b58a45adcb67c08cc Mon Sep 17 00:00:00 2001 From: Taylor Jones Date: Mon, 19 Oct 2026 12:02:33 -0500 Subject: [PATCH 1/2] add component testing guidance to the style-guide skill Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a1e411c7-7f69-4ce9-9e3f-6f7ddd87b2bc --- .github/skills/style-guide/SKILL.md | 9 +- .../style-guide/docs/component-testing.md | 134 ++++++++++++++++++ 2 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 .github/skills/style-guide/docs/component-testing.md diff --git a/.github/skills/style-guide/SKILL.md b/.github/skills/style-guide/SKILL.md index 1edc5de484f..7b74f206b13 100644 --- a/.github/skills/style-guide/SKILL.md +++ b/.github/skills/style-guide/SKILL.md @@ -1,18 +1,22 @@ --- name: style-guide -description: 'Use when: authoring, modifying, or reviewing Primer React components, hooks, utilities, or component APIs.' +description: 'Use when: authoring, modifying, or reviewing Primer React components, hooks, utilities, or component APIs; or authoring, modifying, or debugging their tests.' --- # Primer React Style Guide Use this skill when creating, modifying, or reviewing Primer React components, -hooks, utilities, or component APIs. +hooks, utilities, or component APIs, and when authoring, modifying, or debugging +their tests. Before authoring component code, read `contributor-docs/style.md` and apply the guidelines that are relevant to the change. The style guide is the source of truth for component API design and implementation conventions in this repository. +Before authoring or changing component tests, read the Component testing topic +below and `contributor-docs/testing.md` for testing strategy, setup, and commands. + When proposing, implementing, or reviewing a component change, explicitly apply the relevant style-guide principles to the design. If a change intentionally deviates from the style guide, call out the reason. @@ -24,6 +28,7 @@ specific topic. Consult this to see if any apply to the task at hand. | :-------------------- | :-------------------------------------------------------------------------------- | :------------------------------------------------------- | | Component prop naming | Use when deciding or evaluating the name for a prop in a React component. | [component-prop-naming](./docs/component-prop-naming.md) | | Component prop types | Use when designing or reviewing component prop types, including slot-based props. | [component-prop-types](./docs/component-prop-types.md) | +| Component testing | Use when authoring, modifying, or debugging component and hook tests. | [component-testing](./docs/component-testing.md) | ## Updating this skill diff --git a/.github/skills/style-guide/docs/component-testing.md b/.github/skills/style-guide/docs/component-testing.md new file mode 100644 index 00000000000..b4f038d14c0 --- /dev/null +++ b/.github/skills/style-guide/docs/component-testing.md @@ -0,0 +1,134 @@ +# Component Testing + +Use these conventions when authoring, modifying, or debugging component and hook +tests. 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. The conventions below apply to Vitest browser +tests. Node/SSR tests and the separate Playwright visual and accessibility suites +have their own environments and use the APIs available in those 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() + + 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() +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. From 0171203c992b4478215ba3b6a7bb56ce73b0abb6 Mon Sep 17 00:00:00 2001 From: Taylor Jones Date: Thu, 22 Oct 2026 09:45:39 -0500 Subject: [PATCH 2/2] extract component testing guidance into a standalone skill Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a1e411c7-7f69-4ce9-9e3f-6f7ddd87b2bc --- .../skills/component-testing/SKILL.md | 16 ++++++++++------ .github/skills/style-guide/SKILL.md | 9 ++------- 2 files changed, 12 insertions(+), 13 deletions(-) rename .github/skills/style-guide/docs/component-testing.md => .agents/skills/component-testing/SKILL.md (88%) diff --git a/.github/skills/style-guide/docs/component-testing.md b/.agents/skills/component-testing/SKILL.md similarity index 88% rename from .github/skills/style-guide/docs/component-testing.md rename to .agents/skills/component-testing/SKILL.md index b4f038d14c0..9196a93df86 100644 --- a/.github/skills/style-guide/docs/component-testing.md +++ b/.agents/skills/component-testing/SKILL.md @@ -1,13 +1,17 @@ +--- +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 when authoring, modifying, or debugging component and hook -tests. Test observable behavior and the component's public contract rather than +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. The conventions below apply to Vitest browser -tests. Node/SSR tests and the separate Playwright visual and accessibility suites -have their own environments and use the APIs available in those environments. +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 diff --git a/.github/skills/style-guide/SKILL.md b/.github/skills/style-guide/SKILL.md index 7b74f206b13..1edc5de484f 100644 --- a/.github/skills/style-guide/SKILL.md +++ b/.github/skills/style-guide/SKILL.md @@ -1,22 +1,18 @@ --- name: style-guide -description: 'Use when: authoring, modifying, or reviewing Primer React components, hooks, utilities, or component APIs; or authoring, modifying, or debugging their tests.' +description: 'Use when: authoring, modifying, or reviewing Primer React components, hooks, utilities, or component APIs.' --- # Primer React Style Guide Use this skill when creating, modifying, or reviewing Primer React components, -hooks, utilities, or component APIs, and when authoring, modifying, or debugging -their tests. +hooks, utilities, or component APIs. Before authoring component code, read `contributor-docs/style.md` and apply the guidelines that are relevant to the change. The style guide is the source of truth for component API design and implementation conventions in this repository. -Before authoring or changing component tests, read the Component testing topic -below and `contributor-docs/testing.md` for testing strategy, setup, and commands. - When proposing, implementing, or reviewing a component change, explicitly apply the relevant style-guide principles to the design. If a change intentionally deviates from the style guide, call out the reason. @@ -28,7 +24,6 @@ specific topic. Consult this to see if any apply to the task at hand. | :-------------------- | :-------------------------------------------------------------------------------- | :------------------------------------------------------- | | Component prop naming | Use when deciding or evaluating the name for a prop in a React component. | [component-prop-naming](./docs/component-prop-naming.md) | | Component prop types | Use when designing or reviewing component prop types, including slot-based props. | [component-prop-types](./docs/component-prop-types.md) | -| Component testing | Use when authoring, modifying, or debugging component and hook tests. | [component-testing](./docs/component-testing.md) | ## Updating this skill