From 68a66e04df0a3fffb4e6619b00e623e60816e2f3 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Mon, 13 Oct 2025 14:17:39 -0700 Subject: [PATCH 01/14] initial react aria testing docs --- packages/@react-aria/test-utils/src/index.ts | 10 + .../dev/s2-docs/pages/react-aria/testing.mdx | 440 ++++++++++++++++++ packages/dev/s2-docs/src/ClassAPI.tsx | 22 +- packages/dev/s2-docs/src/CodeBlock.tsx | 4 +- 4 files changed, 471 insertions(+), 5 deletions(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/testing.mdx diff --git a/packages/@react-aria/test-utils/src/index.ts b/packages/@react-aria/test-utils/src/index.ts index b5b7da34492..ddad90b9a57 100644 --- a/packages/@react-aria/test-utils/src/index.ts +++ b/packages/@react-aria/test-utils/src/index.ts @@ -14,5 +14,15 @@ export {triggerLongPress} from './events'; export {installMouseEvent, installPointerEvent} from './testSetup'; export {pointerMap} from './userEventMaps'; export {User} from './user'; +// TODO: had to export these for the docs, but not sure why I didn't have to do +// so for the v3 docs? +export {ComboBoxTester} from './combobox'; +export {GridListTester} from './gridlist'; +export {ListBoxTester} from './listbox'; +export {MenuTester} from './menu'; +export {SelectTester} from './select'; +export {TableTester} from './table'; +export {TabsTester} from './tabs'; +export {TreeTester} from './tree'; export type {UserOpts} from './types'; diff --git a/packages/dev/s2-docs/pages/react-aria/testing.mdx b/packages/dev/s2-docs/pages/react-aria/testing.mdx new file mode 100644 index 00000000000..397acaa215f --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/testing.mdx @@ -0,0 +1,440 @@ +import {VersionBadge} from '../../src/VersionBadge'; +import {InstallCommand} from '../../src/InstallCommand'; +import {Layout} from '../../src/Layout'; +export default Layout; + +import docs from 'docs:react-aria-components'; +import testUtilDocs from 'docs:@react-aria/test-utils'; + +export const section = 'Guides'; +export const description = 'Writing tests for apps built with React Aria'; + +# Testing + +This page describes how to test an application built with React Aria. It documents the available testing utilities available for each aria pattern and how they can be used to simulate common user interactions. + +## Introduction + +Running automated tests on your application helps ensure that it continues to work as expected over time. +You can use testing tools like [React Testing Library](https://testing-library.com/docs/react-testing-library/intro) along with test runners like +[Jest](https://jestjs.io) or [Mocha](https://mochajs.org) to test applications built with React Aria Components or hooks. These generally +work quite well out of the box but there are a few things to consider to ensure your tests are the best they +can be. + +The information below covers best practices when writing tests, and be sure to checkout our [test utils](./react-aria-test-utils) that incorporate these +strategies under the hood, helping streamline the test writing practice for you. + +## Testing semantics + +Many testing libraries expect you to query for elements in the DOM tree. For example, you might have a test +that renders a login page, finds the username and password fields, and simulates filling them out and +submitting the form. + +The recommended way to query for React Aria Components and their internals is by semantics. React Aria +Components implement [ARIA patterns](https://www.w3.org/TR/wai-aria-practices-1.2/). ARIA is a W3C standard +that specifies the semantics for many UI components. This is used to expose them to assistive technology +such as screen readers, but can also be used in tests to operate the application programmatically. These semantics +are much less likely to change over time, and while other DOM nodes may be added or removed, the semantics are more likely to stay stable. + +The main attribute to look for when querying is the [role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques). +This attribute represents the type of element a DOM node represents, e.g. a button, list option, or tab. + +### React Testing Library + +[React Testing Library](https://testing-library.com/docs/react-testing-library/intro) is useful because it +enforces that you write tests using semantics instead of implementation details. We use React Testing Library +to test React Aria itself, and it's quite easy to [query](https://testing-library.com/docs/dom-testing-library/api-queries) +elements by role, text, label, etc. + +```tsx +import {render} from '@testing-library/react'; + +let tree = render(); +let option = tree.getByRole('button'); +``` + +## Test ids + +Querying by semantics covers many scenarios, but what if you have many buttons on a page? How do you find the specific button +you're looking for in a test? In many cases this could be done by querying by the text in the button or an +accessibility label associated with it, but sometimes this might change over time or may be affected by things like +translations in different languages. In these cases, you may need a way to identify specific elements in tests, and that's +where test ids come in. + +React Aria Components pass all [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes) +through to their underlying DOM nodes, which allows you to use an attribute like `data-testid` to identify +a particular instance of a component. For example, you could add test ids to the two input elements +in a login form and use them to find the username and password fields. + +This example uses React Testing Library, but the idea could be applied in a similar way with other +testing libraries. + +```tsx +import {render} from '@testing-library/react'; +import {Input, Label, TextField} from 'react-aria-components'; + +function LoginForm() { + return ( + <> + + + + + + + + + + ); +} + +let tree = render(); +let username = tree.getByTestId('username'); +let password = tree.getByTestId('password'); +``` + +## Triggering events + +Most testing libraries include a way to simulate events on an element. React Aria Components rely on +many different browser events to support different devices and platforms, so it's important to simulate +these correctly in your tests. For example, rather than only simulating a click event, the tests should +simulate all of the events that would occur if a real user were interacting with the component. + +For example, a click is really a `mousemove` and `mouseover` the target, followed +by `mousedown`, `focus`, and `mouseup` events, and finally a `click` event. If you only simulated the `click` +event, you would be missing all of these other preceding events that occur in real-world situations and this +may make your test not work correctly. The implementation of the component may also change in the future to +expect these events, making your test brittle. In addition, browsers have default behavior that occurs on +certain events which would be missing, like focusing elements on mouse down, and toggling checkboxes on click. + +The best way to handle this is with the [user-event](https://github.com/testing-library/user-event) library. +This lets you trigger high level interactions like a user would, and the library handles firing all of the individual +events that make up that interaction. If you use this library rather than firing events manually, your tests +will simulate real-world behavior much better and work as expected. + +`user-event` can handle many types of interactions, e.g. clicks, tabbing, typing, etc. This example shows how you could +use it to render a login form and enter text in each field and click the submit button, just as a real user would. + +```tsx +import {render} from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +let tree = render(); + +// Click on the username field to focus it, and enter the value. +userEvent.click(tree.getByLabelText('Username')); +userEvent.type(document.activeElement, 'devon'); + +// Tab to the password field, and enter the value. +userEvent.tab(); +userEvent.type(document.activeElement, 'Pas$w0rd'); + +// Tab to the submit button and click it. +userEvent.tab(); +userEvent.click(document.activeElement); +``` + +## React Aria test utils + +TODO can't place this next to the header here + + +[@react-aria/test-utils](https://www.npmjs.com/package/@react-aria/test-utils) is a set of testing utilities that aims to make writing unit tests easier for consumers of React Aria +or for users who have built their own components following the respective ARIA pattern specification. By using the ARIA specification for any given component pattern as a source of truth, +we can make assumptions about the existence of various aria attributes in a component. This allows us to navigate the component's DOM structure, simulate common interactions, and verify the +the resulting state of the component. + +### Installation + + + +Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need +to be on React 18+ in order for these utilities to work. + +### Setup + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +specific ARIA pattern tester in your test cases. This gives you access to that pattern's specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +See [below](#patterns) for what patterns are currently supported. + +```ts +// YourTest.test.ts +import {screen} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +// Provide whatever method of advancing timers you use in your test, this example assumes Jest with fake timers. +// 'interactionType' specifies what mode of interaction should be simulated by the tester +// 'advanceTimer' is used by the tester to advance the timers in the tests for specific interactions (e.g. long press) +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('my test case', async function () { + // Render your test component/app + render(); + // Initialize the table tester via providing the 'Table' pattern name and the root element of said table + let table = testUtilUser.createTester('Table', {root: screen.getByTestId('test_table')}); + + // ... +}); +``` + +See below for the full definition of the `User` object. + + + +### Patterns + + + +<> + ```ts isInSwitcher + // Combobox.test.ts + import {render} from '@testing-library/react'; + import {User} from '@react-aria/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse'}); + // ... + + it('ComboBox can select an option via keyboard', async function () { + // Render your test component/app and initialize the combobox tester + let {getByTestId} = render( + + ... + + ); + let comboboxTester = testUtilUser.createTester('ComboBox', {root: getByTestId('test-combobox'), interactionType: 'keyboard'}); + + await comboboxTester.open(); + expect(comboboxTester.listbox).toBeInTheDocument(); + + let options = comboboxTester.options(); + await comboboxTester.selectOption({option: options[0]}); + expect(comboboxTester.combobox.value).toBe('One'); + expect(comboboxTester.listbox).not.toBeInTheDocument(); + }); + ``` + + + +<> + ```ts isInSwitcher + // GridList.test.ts + import {render, within} from '@testing-library/react'; + import {User} from '@react-aria/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse'}); + // ... + + it('GridList can select a row via keyboard', async function () { + // Render your test component/app and initialize the gridlist tester + let {getByTestId} = render( + + ... + + ); + let gridListTester = testUtilUser.createTester('GridList', {root: getByTestId('test-gridlist'), interactionType: 'keyboard'}); + + let row = gridListTester.rows[0]; + expect(within(row).getByRole('checkbox')).not.toBeChecked(); + expect(gridListTester.selectedRows).toHaveLength(0); + + await gridListTester.toggleRowSelection({row: 0}); + expect(within(row).getByRole('checkbox')).toBeChecked(); + expect(gridListTester.selectedRows).toHaveLength(1); + + await gridListTester.toggleRowSelection({row: 0}); + expect(within(row).getByRole('checkbox')).not.toBeChecked(); + expect(gridListTester.selectedRows).toHaveLength(0); + }); + ``` + + + +<> + ```ts isInSwitcher + // ListBox.test.ts + import {render} from '@testing-library/react'; + import {User} from '@react-aria/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse'}); + // ... + + it('ListBox can select an option via keyboard', async function () { + // Render your test component/app and initialize the listbox tester + let {getByTestId} = render( + + ... + + ); + let listboxTester = testUtilUser.createTester('ListBox', {root: getByTestId('test-listbox'), interactionType: 'keyboard'}); + + await listboxTester.toggleOptionSelection({option: 4}); + expect(listboxTester.options()[4]).toHaveAttribute('aria-selected', 'true'); + }); + ``` + + + +<> + ```ts isInSwitcher + // Menu.test.ts + import {render} from '@testing-library/react'; + import {User} from '@react-aria/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse'}); + // ... + + it('Menu can open its submenu via keyboard', async function () { + // Render your test component/app and initialize the menu tester + let {getByTestId} = render( + + + ... + + ); + let menuTester = testUtilUser.createTester('Menu', {root: getByTestId('test-menutrigger'), interactionType: 'keyboard'}); + + await menuTester.open(); + expect(menuTester.menu).toBeInTheDocument(); + let submenuTriggers = menuTester.submenuTriggers; + expect(submenuTriggers).toHaveLength(1); + + let submenuTester = await menuTester.openSubmenu({submenuTrigger: 'Share…'}); + expect(submenuTester.menu).toBeInTheDocument(); + + await submenuTester.selectOption({option: submenuTester.options()[0]}); + expect(submenuTester.menu).not.toBeInTheDocument(); + expect(menuTester.menu).not.toBeInTheDocument(); + }); + ``` + + + +<> + ```ts isInSwitcher + // Select.test.ts + import {render} from '@testing-library/react'; + import {User} from '@react-aria/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse'}); + // ... + + it('Select can select an option via keyboard', async function () { + // Render your test component/app and initialize the select tester + let {getByTestId} = render( + + ); + let selectTester = testUtilUser.createTester('Select', {root: getByTestId('test-select'), interactionType: 'keyboard'}); + let trigger = selectTester.trigger; + expect(trigger).toHaveTextContent('Select an item'); + + await selectTester.selectOption({option: 'Cat'}); + expect(trigger).toHaveTextContent('Cat'); + }); + ``` + + + +<> + ```ts isInSwitcher + // Table.test.ts + import {render, within} from '@testing-library/react'; + import {User} from '@react-aria/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); + // ... + + it('Table can toggle row selection', async function () { + // Render your test component/app and initialize the table tester + let {getByTestId} = render( + + ... +
+ ); + let tableTester = testUtilUser.createTester('Table', {root: getByTestId('test-table')}); + expect(tableTester.selectedRows).toHaveLength(0); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(10); + + await tableTester.toggleRowSelection({row: 2}); + expect(tableTester.selectedRows).toHaveLength(9); + let checkbox = within(tableTester.rows[2]).getByRole('checkbox'); + expect(checkbox).not.toBeChecked(); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(10); + expect(checkbox).toBeChecked(); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(0); + }); + ``` + + + +<> + ```ts isInSwitcher + // Tabs.test.ts + import {render} from '@testing-library/react'; + import {User} from '@react-aria/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse'}); + // ... + + it('Tabs can change selection via keyboard', async function () { + // Render your test component/app and initialize the listbox tester + let {getByTestId} = render( + + ... + + ); + let tabsTester = testUtilUser.createTester('Tabs', {root: getByTestId('test-tabs'), interactionType: 'keyboard'}); + + let tabs = tabsTester.tabs; + expect(tabsTester.selectedTab).toBe(tabs[0]); + + await tabsTester.triggerTab({tab: 1}); + expect(tabsTester.selectedTab).toBe(tabs[1]); + }); + ``` + + + +<> + ```ts isInSwitcher + // Tree.test.ts + import {render, within} from '@testing-library/react'; + import {User} from '@react-aria/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse'}); + // ... + + it('Tree can select a item via keyboard', async function () { + // Render your test component/app and initialize the Tree tester + let {getByTestId} = render( + + ... + + ); + let treeTester = testUtilUser.createTester('Tree', {root: getByTestId('test-tree'), interactionType: 'keyboard'}); + + await treeTester.toggleRowSelection({row: 0}); + expect(treeTester.selectedRows).toHaveLength(1); + expect(within(treeTester.rows[0]).getByRole('checkbox')).toBeChecked(); + + await treeTester.toggleRowSelection({row: 1}); + expect(treeTester.selectedRows).toHaveLength(2); + expect(within(treeTester.rows[1]).getByRole('checkbox')).toBeChecked(); + + await treeTester.toggleRowSelection({row: 0}); + expect(treeTester.selectedRows).toHaveLength(1); + expect(within(treeTester.rows[0]).getByRole('checkbox')).not.toBeChecked(); + }); + ``` + + + +
diff --git a/packages/dev/s2-docs/src/ClassAPI.tsx b/packages/dev/s2-docs/src/ClassAPI.tsx index 8d837a3e283..e4a560c5846 100644 --- a/packages/dev/s2-docs/src/ClassAPI.tsx +++ b/packages/dev/s2-docs/src/ClassAPI.tsx @@ -1,14 +1,30 @@ import {InterfaceType, setLinks, TInterface} from './types'; import React from 'react'; +import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; + +const wrapper = style({ + paddingX: { + default: 0, + isInSwitcher: 32 + }, + paddingBottom: { + default: 0, + isInSwitcher: 32 + } +}); interface ClassAPIProps { class: TInterface, - links: any + links: any, + // TODO: replace by making this a client component if we can do that + isInSwitcher?: boolean } -export function ClassAPI({class: c, links}: ClassAPIProps) { +export function ClassAPI({class: c, links, isInSwitcher}: ClassAPIProps) { setLinks(links); return ( - +
+ +
); } diff --git a/packages/dev/s2-docs/src/CodeBlock.tsx b/packages/dev/s2-docs/src/CodeBlock.tsx index a66d5ebf011..5c75ff7adc9 100644 --- a/packages/dev/s2-docs/src/CodeBlock.tsx +++ b/packages/dev/s2-docs/src/CodeBlock.tsx @@ -101,7 +101,7 @@ export function CodeBlock({render, children, files, expanded, hidden, hideExampl component={render} align={props.align} />
- {files + {files ? {content} : content}
@@ -179,7 +179,7 @@ export function getFiles(files: string[]) { let contents = fs.readFileSync(file, 'utf8'); fileContents[name] = contents.replace(/(vanilla-starter|tailwind-starter)\//g, './'); } - + return fileContents; } From 432f70272f8d0ebe4f1361e85bb30a56c58dc5eb Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Mon, 13 Oct 2025 14:55:51 -0700 Subject: [PATCH 02/14] pare down content for readability --- .../dev/s2-docs/pages/react-aria/testing.mdx | 65 +++++-------------- 1 file changed, 16 insertions(+), 49 deletions(-) diff --git a/packages/dev/s2-docs/pages/react-aria/testing.mdx b/packages/dev/s2-docs/pages/react-aria/testing.mdx index 397acaa215f..fceb68060b4 100644 --- a/packages/dev/s2-docs/pages/react-aria/testing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/testing.mdx @@ -5,6 +5,7 @@ export default Layout; import docs from 'docs:react-aria-components'; import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const section = 'Guides'; export const description = 'Writing tests for apps built with React Aria'; @@ -13,28 +14,12 @@ export const description = 'Writing tests for apps built with React Aria'; This page describes how to test an application built with React Aria. It documents the available testing utilities available for each aria pattern and how they can be used to simulate common user interactions. -## Introduction - -Running automated tests on your application helps ensure that it continues to work as expected over time. -You can use testing tools like [React Testing Library](https://testing-library.com/docs/react-testing-library/intro) along with test runners like -[Jest](https://jestjs.io) or [Mocha](https://mochajs.org) to test applications built with React Aria Components or hooks. These generally -work quite well out of the box but there are a few things to consider to ensure your tests are the best they -can be. - -The information below covers best practices when writing tests, and be sure to checkout our [test utils](./react-aria-test-utils) that incorporate these -strategies under the hood, helping streamline the test writing practice for you. - ## Testing semantics -Many testing libraries expect you to query for elements in the DOM tree. For example, you might have a test -that renders a login page, finds the username and password fields, and simulates filling them out and -submitting the form. - The recommended way to query for React Aria Components and their internals is by semantics. React Aria Components implement [ARIA patterns](https://www.w3.org/TR/wai-aria-practices-1.2/). ARIA is a W3C standard -that specifies the semantics for many UI components. This is used to expose them to assistive technology -such as screen readers, but can also be used in tests to operate the application programmatically. These semantics -are much less likely to change over time, and while other DOM nodes may be added or removed, the semantics are more likely to stay stable. +that specifies the semantics for many UI components. Unlike the DOM structure of the component, these semantics are much less likely to change over time, +making them ideal to query for. The main attribute to look for when querying is the [role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques). This attribute represents the type of element a DOM node represents, e.g. a button, list option, or tab. @@ -55,19 +40,12 @@ let option = tree.getByRole('button'); ## Test ids -Querying by semantics covers many scenarios, but what if you have many buttons on a page? How do you find the specific button -you're looking for in a test? In many cases this could be done by querying by the text in the button or an -accessibility label associated with it, but sometimes this might change over time or may be affected by things like -translations in different languages. In these cases, you may need a way to identify specific elements in tests, and that's -where test ids come in. +Querying by semantics covers many scenarios, but what if you have many buttons on a page or its text changes due to translations based on locale? +In these cases, you may need a way to identify specific elements in tests, and that's where test ids come in. React Aria Components pass all [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes) through to their underlying DOM nodes, which allows you to use an attribute like `data-testid` to identify -a particular instance of a component. For example, you could add test ids to the two input elements -in a login form and use them to find the username and password fields. - -This example uses React Testing Library, but the idea could be applied in a similar way with other -testing libraries. +a particular instance of a component. ```tsx import {render} from '@testing-library/react'; @@ -95,25 +73,13 @@ let password = tree.getByTestId('password'); ## Triggering events -Most testing libraries include a way to simulate events on an element. React Aria Components rely on -many different browser events to support different devices and platforms, so it's important to simulate -these correctly in your tests. For example, rather than only simulating a click event, the tests should -simulate all of the events that would occur if a real user were interacting with the component. - -For example, a click is really a `mousemove` and `mouseover` the target, followed -by `mousedown`, `focus`, and `mouseup` events, and finally a `click` event. If you only simulated the `click` -event, you would be missing all of these other preceding events that occur in real-world situations and this -may make your test not work correctly. The implementation of the component may also change in the future to -expect these events, making your test brittle. In addition, browsers have default behavior that occurs on -certain events which would be missing, like focusing elements on mouse down, and toggling checkboxes on click. +React Aria Components rely on many different browser events to support different devices and platforms, so it's important to simulate +these correctly in your tests. For example, a click is really a `mousemove` and `mouseover` the target, followed +by `mousedown`, `focus`, and `mouseup` events, and finally a `click` event. The best way to handle this is with the [user-event](https://github.com/testing-library/user-event) library. This lets you trigger high level interactions like a user would, and the library handles firing all of the individual -events that make up that interaction. If you use this library rather than firing events manually, your tests -will simulate real-world behavior much better and work as expected. - -`user-event` can handle many types of interactions, e.g. clicks, tabbing, typing, etc. This example shows how you could -use it to render a login form and enter text in each field and click the submit button, just as a real user would. +events that make up that interaction. ```tsx import {render} from '@testing-library/react'; @@ -140,16 +106,17 @@ TODO can't place this next to the header here [@react-aria/test-utils](https://www.npmjs.com/package/@react-aria/test-utils) is a set of testing utilities that aims to make writing unit tests easier for consumers of React Aria -or for users who have built their own components following the respective ARIA pattern specification. By using the ARIA specification for any given component pattern as a source of truth, -we can make assumptions about the existence of various aria attributes in a component. This allows us to navigate the component's DOM structure, simulate common interactions, and verify the -the resulting state of the component. +or for users who have built their own components following the respective ARIA pattern specification. ### Installation -Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need -to be on React 18+ in order for these utilities to work. + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + ### Setup From 78fb92bfd241b5face7e632589687bbadbd0028d Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Mon, 13 Oct 2025 16:05:11 -0700 Subject: [PATCH 03/14] adding S2 testing docs --- .../dev/s2-docs/pages/react-aria/testing.mdx | 1 - packages/dev/s2-docs/pages/s2/testing.mdx | 421 ++++++++++++++++++ 2 files changed, 421 insertions(+), 1 deletion(-) create mode 100644 packages/dev/s2-docs/pages/s2/testing.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/testing.mdx b/packages/dev/s2-docs/pages/react-aria/testing.mdx index fceb68060b4..eb821119f6c 100644 --- a/packages/dev/s2-docs/pages/react-aria/testing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/testing.mdx @@ -3,7 +3,6 @@ import {InstallCommand} from '../../src/InstallCommand'; import {Layout} from '../../src/Layout'; export default Layout; -import docs from 'docs:react-aria-components'; import testUtilDocs from 'docs:@react-aria/test-utils'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' diff --git a/packages/dev/s2-docs/pages/s2/testing.mdx b/packages/dev/s2-docs/pages/s2/testing.mdx new file mode 100644 index 00000000000..f56c8b4abd9 --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/testing.mdx @@ -0,0 +1,421 @@ +import {VersionBadge} from '../../src/VersionBadge'; +import {InstallCommand} from '../../src/InstallCommand'; +import {Layout} from '../../src/Layout'; +export default Layout; + +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' + +export const section = 'Guides'; +export const description = 'Writing tests for apps built with React Spectrum'; + +# Testing + +This page describes how to test an application built with React Spectrum. It documents the available testing utilities available for each aria pattern and how they can be used to simulate common user interactions. + +## Testing semantics + +The recommended way to query for React Spectrum components and their internals is by semantics. React Spectrum +Components implement [ARIA patterns](https://www.w3.org/TR/wai-aria-practices-1.2/). ARIA is a W3C standard +that specifies the semantics for many UI components. Unlike the class names and DOM structure of the component, these +semantics are much less likely to change over time, making them ideal to query for. + +The main attribute to look for when querying is the [role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques). +This attribute represents the type of element a DOM node represents, e.g. a button, list option, or tab. + +### React Testing Library + +[React Testing Library](https://testing-library.com/docs/react-testing-library/intro) is useful because it +enforces that you write tests using semantics instead of implementation details. We use React Testing Library +to test React Spectrum itself, and it's quite easy to [query](https://testing-library.com/docs/dom-testing-library/api-queries) +elements by role, text, label, etc. + +```tsx +import {render} from '@testing-library/react'; + +let tree = render(); +let option = tree.getByRole('button'); +``` + +## Test ids + +Querying by semantics covers many scenarios, but what if you have many buttons on a page or its text changes due to translations based on locale? +In these cases, you may need a way to identify specific elements in tests, and that's where test ids come in. + +React Spectrum components pass all [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes) +through to their underlying DOM nodes, which allows you to use an attribute like `data-testid` to identify +a particular instance of a component. + +```tsx +import {render} from '@testing-library/react'; +import {TextField} from '@react-spectrum/s2'; + +function LoginForm() { + return ( + <> + + + + ); +} + +let tree = render(); +let username = tree.getByTestId('username'); +let password = tree.getByTestId('password'); +``` + +## Triggering events + +React Spectrum components rely on many different browser events to support different devices and platforms, so it's important to simulate +these correctly in your tests. For example, a click is really a `mousemove` and `mouseover` the target, followed +by `mousedown`, `focus`, and `mouseup` events, and finally a `click` event. + +The best way to handle this is with the [user-event](https://github.com/testing-library/user-event) library. +This lets you trigger high level interactions like a user would, and the library handles firing all of the individual +events that make up that interaction. + +```tsx +import {render} from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +let tree = render(); + +// Click on the username field to focus it, and enter the value. +userEvent.click(tree.getByLabelText('Username')); +userEvent.type(document.activeElement, 'devon'); + +// Tab to the password field, and enter the value. +userEvent.tab(); +userEvent.type(document.activeElement, 'Pas$w0rd'); + +// Tab to the submit button and click it. +userEvent.tab(); +userEvent.click(document.activeElement); +``` + +## Test setup and common gotchas + +### Timers + +If you are using fake timers in your test suite, be aware that you may need to advance your timers after various interactions. We have `requestAnimationFrame` calls in various underlying hooks that you will need to also handle by advancing your timers in the tests. +This happens most prominently in our collection components after selection. In Jest, this can be handled by calling `act(() => jest.runAllTimers());` but you may require more precise control +depending on the other time-sensitive behavior you are testing. Please see [Jest's timer docs](https://jestjs.io/docs/timer-mocks) or the equivalent docs of your test frameworks for more information on how to do so. +It is also a good idea to run all timers to completion after each test case to avoid any left over transitions or timeouts that a component may have setup during its lifecycle. + +```tsx +afterEach(() => { + act(() => jest.runAllTimers()); +}); +``` + +Consider adding a `act(() => jest.runAllTimers());` after your simulated user interaction if you run into a test failure that looks like the following: + +``` +TestingLibraryElementError: Unable to find an accessible element with the role "listbox" +``` + +If you are using real timers instead, you can await a particular state of your app to be reached. If you are using React Testing Library, you can perform a `waitFor` query +to wait for a dialog to appear: + +```tsx +await waitFor(() => { + expect(getByRole('dialog')).toBeInTheDocument(); +}); +``` + +### Simulating user long press + +Some components like Menu support long press operations. Unfortunately, the approach of using the userEvent library to simulate a press event and running timers to hit the +long press internal timer threshold isn't sufficient due to `useLongPress`'s usage of `PointerEvent` and our own detection of `virtual` vs `mouse`/`touch` pointer types. Mock [PointerEvent](https://github.com/adobe/react-spectrum/blob/16ff0efac57eebeb1cd601ab376ce7c58a4e4efd/packages/dev/test-utils/src/events.ts#L70-L103) +globally and use `fireEvent` from `@testing-library/react` to properly simulate these long press events in your tests. +If you are using Jest, you can call our utility to automatically set up and tear down this mock in your test. +Additionally, if you are using fake timers and don't need to control the specific timings around the long press interaction, feel free to use our utility as shown below. + +```tsx +import {fireEvent} from '@testing-library/react'; +import {installPointerEvent, triggerLongPress} from '@react-spectrum/test-utils'; +installPointerEvent(); + +// In test case +let button = getByRole('button'); + +// With fireEvent and specific timing control +fireEvent.pointerDown(el, {pointerType: 'touch'}); +act(() => jest.advanceTimersByTime(800)); +fireEvent.up(el, {pointerType: 'touch'}); + +// With triggerLongPress +triggerLongPress(button); +``` + +### Simulating move event + +Components like ColorArea, ColorSlider, ColorWheel, and Slider each feature a draggable handle that a user can interact with to change the component's value. Similar to long press, the interactions offered by userEvent library aren't sufficient to trigger +the underlying event handlers governing these drag/move operations. [Mock MouseEvent globally](https://github.com/adobe/react-spectrum/blob/16ff0efac57eebeb1cd601ab376ce7c58a4e4efd/packages/dev/test-utils/src/events.ts#L44-L68) and `fireEvent` from `@testing-library/react` to simulate these drag/move events in your tests. +If you are using Jest, you can call our utility to automatically set up and tear down this mock in your test. Additionally, the track dimensions +for the draggable handle should be mocked so that the move operation calculations can be properly computed. + +```tsx +import {fireEvent} from '@testing-library/react'; +import {installMouseEvent} from '@react-spectrum/test-utils'; +installMouseEvent(); + +beforeAll(() => { + jest.spyOn(window.HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(() => ({top: 0, left: 0, width: 100, height: 10})); +}) + +// In test case +let sliderThumb = getByRole('slider').parentElement; + +// With fireEvent, move thumb from 0 to 50 +fireEvent.mouseDown(thumb, {clientX: 0, pageX: 0}); +fireEvent.mouseMove(thumb, {pageX: 50}); +fireEvent.mouseUp(thumb, {pageX: 50}); +``` + +## React Spectrum test utils + +TODO can't place this next to the header here + + +In addition to the test utilities mentioned above, [@react-spectrum/test-utils](https://www.npmjs.com/package/@react-spectrum/test-utils) re-exports the same test utils available in `@react-aria/test-utils`, including +the ARIA pattern testers. These testers are set of testing utilities that aims to make writing unit tests easier for consumers of React Spectrum. + +### Installation + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + + +### Setup + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +specific ARIA pattern tester in your test cases. This gives you access to that pattern's specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +See [below](#patterns) for what patterns are currently supported. + +```ts +// YourTest.test.ts +import {screen} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +// Provide whatever method of advancing timers you use in your test, this example assumes Jest with fake timers. +// 'interactionType' specifies what mode of interaction should be simulated by the tester +// 'advanceTimer' is used by the tester to advance the timers in the tests for specific interactions (e.g. long press) +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('my test case', async function () { + // Render your test component/app + render(); + // Initialize the table tester via providing the 'Table' pattern name and the root element of said table + let table = testUtilUser.createTester('Table', {root: screen.getByTestId('test_table')}); + + // ... +}); +``` + +See below for the full definition of the `User` object. + + + +### Patterns + + + +<> + ```ts isInSwitcher + // Combobox.test.ts + import {render} from '@testing-library/react'; + import {User} from '@react-spectrum/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse'}); + // ... + + it('ComboBox can select an option via keyboard', async function () { + // Render your test component/app and initialize the combobox tester + let {getByTestId} = render( + + ... + + ); + let comboboxTester = testUtilUser.createTester('ComboBox', {root: getByTestId('test-combobox'), interactionType: 'keyboard'}); + + await comboboxTester.open(); + expect(comboboxTester.listbox).toBeInTheDocument(); + + let options = comboboxTester.options(); + await comboboxTester.selectOption({option: options[0]}); + expect(comboboxTester.combobox.value).toBe('One'); + expect(comboboxTester.listbox).not.toBeInTheDocument(); + }); + ``` + + + +<> + ```ts isInSwitcher + // Menu.test.ts + import {render} from '@testing-library/react'; + import {User} from '@react-spectrum/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse'}); + // ... + + it('Menu can open its submenu via keyboard', async function () { + // Render your test component/app and initialize the menu tester + let {getByTestId} = render( + + + ... + + ); + let menuTester = testUtilUser.createTester('Menu', {root: getByTestId('test-menutrigger'), interactionType: 'keyboard'}); + + await menuTester.open(); + expect(menuTester.menu).toBeInTheDocument(); + let submenuTriggers = menuTester.submenuTriggers; + expect(submenuTriggers).toHaveLength(1); + + let submenuTester = await menuTester.openSubmenu({submenuTrigger: 'Share…'}); + expect(submenuTester.menu).toBeInTheDocument(); + + await submenuTester.selectOption({option: submenuTester.options()[0]}); + expect(submenuTester.menu).not.toBeInTheDocument(); + expect(menuTester.menu).not.toBeInTheDocument(); + }); + ``` + + + +<> + ```ts isInSwitcher + // Picker.test.ts + import {render} from '@testing-library/react'; + import {User} from '@react-spectrum/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse'}); + // ... + + it('Picker can select an option via keyboard', async function () { + // Render your test component/app and initialize the select tester + let {getByTestId} = render( + + ... + + ); + let selectTester = testUtilUser.createTester('Select', {root: getByTestId('test-select'), interactionType: 'keyboard'}); + let trigger = selectTester.trigger; + expect(trigger).toHaveTextContent('Select an item'); + + await selectTester.selectOption({option: 'Cat'}); + expect(trigger).toHaveTextContent('Cat'); + }); + ``` + + + +<> + ```ts isInSwitcher + // Table.test.ts + import {render, within} from '@testing-library/react'; + import {User} from '@react-spectrum/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); + // ... + + it('TableView can toggle row selection', async function () { + // Render your test component/app and initialize the table tester + let {getByTestId} = render( + + ... + + ); + let tableTester = testUtilUser.createTester('Table', {root: getByTestId('test-table')}); + expect(tableTester.selectedRows).toHaveLength(0); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(10); + + await tableTester.toggleRowSelection({row: 2}); + expect(tableTester.selectedRows).toHaveLength(9); + let checkbox = within(tableTester.rows[2]).getByRole('checkbox'); + expect(checkbox).not.toBeChecked(); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(10); + expect(checkbox).toBeChecked(); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(0); + }); + ``` + + + +<> + ```ts isInSwitcher + // Tabs.test.ts + import {render} from '@testing-library/react'; + import {User} from '@react-spectrum/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse'}); + // ... + + it('Tabs can change selection via keyboard', async function () { + // Render your test component/app and initialize the listbox tester + let {getByTestId} = render( + + ... + + ); + let tabsTester = testUtilUser.createTester('Tabs', {root: getByTestId('test-tabs'), interactionType: 'keyboard'}); + + let tabs = tabsTester.tabs; + expect(tabsTester.selectedTab).toBe(tabs[0]); + + await tabsTester.triggerTab({tab: 1}); + expect(tabsTester.selectedTab).toBe(tabs[1]); + }); + ``` + + + +<> + ```ts isInSwitcher + // Tree.test.ts + import {render, within} from '@testing-library/react'; + import {User} from '@react-spectrum/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse'}); + // ... + + it('TreeView can select a item via keyboard', async function () { + // Render your test component/app and initialize the Tree tester + let {getByTestId} = render( + + ... + + ); + let treeTester = testUtilUser.createTester('Tree', {root: getByTestId('test-tree'), interactionType: 'keyboard'}); + + await treeTester.toggleRowSelection({row: 0}); + expect(treeTester.selectedRows).toHaveLength(1); + expect(within(treeTester.rows[0]).getByRole('checkbox')).toBeChecked(); + + await treeTester.toggleRowSelection({row: 1}); + expect(treeTester.selectedRows).toHaveLength(2); + expect(within(treeTester.rows[1]).getByRole('checkbox')).toBeChecked(); + + await treeTester.toggleRowSelection({row: 0}); + expect(treeTester.selectedRows).toHaveLength(1); + expect(within(treeTester.rows[0]).getByRole('checkbox')).not.toBeChecked(); + }); + ``` + + + + From 620ebd738c2b0045faeeea5d23770ed843e66555 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 21 Oct 2025 14:15:44 -0700 Subject: [PATCH 04/14] add sample subpage content --- .../dev/s2-docs/pages/react-aria/Table.mdx | 89 ++++++++++++++++++- .../dev/s2-docs/pages/react-aria/testing.mdx | 80 +++++++++++++++++ 2 files changed, 168 insertions(+), 1 deletion(-) diff --git a/packages/dev/s2-docs/pages/react-aria/Table.mdx b/packages/dev/s2-docs/pages/react-aria/Table.mdx index c60bf737500..57ff052bfd9 100644 --- a/packages/dev/s2-docs/pages/react-aria/Table.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Table.mdx @@ -1,3 +1,4 @@ +import {InstallCommand} from '../../src/InstallCommand'; import {Layout} from '../../src/Layout'; export default Layout; @@ -6,6 +7,7 @@ import vanillaDocs from 'docs:vanilla-starter/Table'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/TableAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import testUtilDocs from 'docs:@react-aria/test-utils'; export const tags = ['data', 'grid']; @@ -89,7 +91,7 @@ export const tags = ['data', 'grid']; ## Content -`Table` follows the [Collection Components API](collections.html?component=Table), accepting both static and dynamic collections. +`Table` follows the [Collection Components API](collections.html?component=Table), accepting both static and dynamic collections. In this example, both the columns and the rows are provided to the table via a render function, enabling the user to hide and show columns and add additional rows. ```tsx render @@ -707,3 +709,88 @@ function ReorderableTable() { ### TableLoadMoreItem + + +## Testing + +TODO make this a subpage + + +### General setup + +Table features long press interactions on its rows depending on the row actions provided and if the user is interacting with the table on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](testing.html#timers) + +[Long press](testing.html#simulating-user-long-press) + +### Test utils + +`@react-aria/test-utils` offers common table interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Table` tester in your test cases. This gives you access to `Table` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Table` tester, use it to simulate row selection, and verify the table's state after each interaction. + +```ts +// Table.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('Table can toggle row selection', async function () { + // Render your test component/app and initialize the table tester + let {getByTestId} = render( + + ... +
+ ); + let tableTester = testUtilUser.createTester('Table', {root: getByTestId('test-table')}); + expect(tableTester.selectedRows).toHaveLength(0); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(10); + + await tableTester.toggleRowSelection({row: 2}); + expect(tableTester.selectedRows).toHaveLength(9); + let checkbox = within(tableTester.rows[2]).getByRole('checkbox'); + expect(checkbox).not.toBeChecked(); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(10); + expect(checkbox).toBeChecked(); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(0); +}); +``` + +See below for the full definition of the `User` and the `Table` tester. + + + + +### Testing FAQ + +- When using the test utils, what if a certain interaction errors or doesn't seem to result in the expected state? + +In cases like this, first double check your test setup and make sure that your test is rendering your table in its expected +state before the test util interaction call. If everything looks correct, you can always fall back to simulating interactions manually, +and using the test util to query your table's state post interaction. + +- The tester doesn't offer a specific interaction flow, what should I do? + +Whenever the table tester queries its rows/cells/etc or triggers a user flow, it does so against the current state of the table. Therefore the table test can be used alongside +whatever simulated user flow you add. diff --git a/packages/dev/s2-docs/pages/react-aria/testing.mdx b/packages/dev/s2-docs/pages/react-aria/testing.mdx index eb821119f6c..803a9c23f76 100644 --- a/packages/dev/s2-docs/pages/react-aria/testing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/testing.mdx @@ -99,6 +99,86 @@ userEvent.tab(); userEvent.click(document.activeElement); ``` +## Test setup and common gotchas + +### Timers + +If you are using fake timers in your test suite, be aware that you may need to advance your timers after various interactions. We have `requestAnimationFrame` calls in various underlying hooks that you will need to also handle by advancing your timers in the tests. +This happens most prominently in our collection components after selection. In Jest, this can be handled by calling `act(() => jest.runAllTimers());` but you may require more precise control +depending on the other time-sensitive behavior you are testing. Please see [Jest's timer docs](https://jestjs.io/docs/timer-mocks) or the equivalent docs of your test frameworks for more information on how to do so. +It is also a good idea to run all timers to completion after each test case to avoid any left over transitions or timeouts that a component may have setup during its lifecycle. + +```tsx +afterEach(() => { + act(() => jest.runAllTimers()); +}); +``` + +Consider adding a `act(() => jest.runAllTimers());` after your simulated user interaction if you run into a test failure that looks like the following: + +``` +TestingLibraryElementError: Unable to find an accessible element with the role "listbox" +``` + +If you are using real timers instead, you can await a particular state of your app to be reached. If you are using React Testing Library, you can perform a `waitFor` query +to wait for a dialog to appear: + +```tsx +await waitFor(() => { + expect(getByRole('dialog')).toBeInTheDocument(); +}); +``` + +### Simulating user long press + +Some components like Menu support long press operations. Unfortunately, the approach of using the userEvent library to simulate a press event and running timers to hit the +long press internal timer threshold isn't sufficient due to `useLongPress`'s usage of `PointerEvent` and our own detection of `virtual` vs `mouse`/`touch` pointer types. Mock [PointerEvent](https://github.com/adobe/react-spectrum/blob/16ff0efac57eebeb1cd601ab376ce7c58a4e4efd/packages/dev/test-utils/src/events.ts#L70-L103) +globally and use `fireEvent` from `@testing-library/react` to properly simulate these long press events in your tests. +If you are using Jest, you can call our utility to automatically set up and tear down this mock in your test. +Additionally, if you are using fake timers and don't need to control the specific timings around the long press interaction, feel free to use our utility as shown below. + +```tsx +import {fireEvent} from '@testing-library/react'; +import {installPointerEvent, triggerLongPress} from '@react-aria/test-utils'; +installPointerEvent(); + +// In test case +let button = getByRole('button'); + +// With fireEvent and specific timing control +fireEvent.pointerDown(el, {pointerType: 'touch'}); +act(() => jest.advanceTimersByTime(800)); +fireEvent.up(el, {pointerType: 'touch'}); + +// With triggerLongPress +triggerLongPress(button); +``` + +### Simulating move event + +Components like ColorArea, ColorSlider, ColorWheel, and Slider each feature a draggable handle that a user can interact with to change the component's value. Similar to long press, the interactions offered by userEvent library aren't sufficient to trigger +the underlying event handlers governing these drag/move operations. [Mock MouseEvent globally](https://github.com/adobe/react-spectrum/blob/f40b575e38837e1aa7cabf0431406e81275d118a/packages/%40react-aria/test-utils/src/testSetup.ts#L16-L36) and `fireEvent` from `@testing-library/react` to simulate these drag/move events in your tests. +If you are using Jest, you can call our utility to automatically set up and tear down this mock in your test. Additionally, the track dimensions +for the draggable handle should be mocked so that the move operation calculations can be properly computed. + +```tsx +import {fireEvent} from '@testing-library/react'; +import {installMouseEvent} from '@react-aria/test-utils'; +installMouseEvent(); + +beforeAll(() => { + jest.spyOn(window.HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(() => ({top: 0, left: 0, width: 100, height: 10})); +}) + +// In test case +let sliderThumb = getByRole('slider').parentElement; + +// With fireEvent, move thumb from 0 to 50 +fireEvent.mouseDown(thumb, {clientX: 0, pageX: 0}); +fireEvent.mouseMove(thumb, {pageX: 50}); +fireEvent.mouseUp(thumb, {pageX: 50}); +``` + ## React Aria test utils TODO can't place this next to the header here From 6fdf671e3176f52774879bef00e3e031ae196b46 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 21 Oct 2025 14:35:17 -0700 Subject: [PATCH 05/14] add style for blockquote --- packages/dev/s2-docs/pages/react-aria/Table.mdx | 4 ++-- packages/dev/s2-docs/src/Layout.tsx | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/dev/s2-docs/pages/react-aria/Table.mdx b/packages/dev/s2-docs/pages/react-aria/Table.mdx index 57ff052bfd9..f624a0cb59b 100644 --- a/packages/dev/s2-docs/pages/react-aria/Table.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Table.mdx @@ -784,13 +784,13 @@ See below for the full definition of the `User` and the `Table` tester. ### Testing FAQ -- When using the test utils, what if a certain interaction errors or doesn't seem to result in the expected state? +> When using the test utils, what if a certain interaction errors or doesn't seem to result in the expected state? In cases like this, first double check your test setup and make sure that your test is rendering your table in its expected state before the test util interaction call. If everything looks correct, you can always fall back to simulating interactions manually, and using the test util to query your table's state post interaction. -- The tester doesn't offer a specific interaction flow, what should I do? +> The tester doesn't offer a specific interaction flow, what should I do? Whenever the table tester queries its rows/cells/etc or triggers a user flow, it does so against the current state of the table. Therefore the table test can be used alongside whatever simulated user flow you add. diff --git a/packages/dev/s2-docs/src/Layout.tsx b/packages/dev/s2-docs/src/Layout.tsx index af2772eef66..e31c45bdddd 100644 --- a/packages/dev/s2-docs/src/Layout.tsx +++ b/packages/dev/s2-docs/src/Layout.tsx @@ -46,6 +46,23 @@ const components = { p: ({children, ...props}) =>

{children}

, ul: (props) =>
    , li: ({children, ...props}) =>
  • {children}
  • , + blockquote: ({children, ...props}) => ( +
    + {children} +
    + ), Figure: (props) =>
    , Caption: (props) =>
    , CodeBlock: CodeBlock, From c051a8c7024c7fa5effbf18f54932afdae684215 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 24 Oct 2025 16:41:17 -0700 Subject: [PATCH 06/14] add new test util docs to old website --- packages/@react-aria/test-utils/src/index.ts | 3 ++ .../checkbox/docs/CheckboxGroup.mdx | 43 ++++++++++++++++++- .../combobox/docs/ComboBox.mdx | 2 +- .../@react-spectrum/dialog/docs/Dialog.mdx | 43 ++++++++++++++++++- .../@react-spectrum/list/docs/ListView.mdx | 2 +- .../@react-spectrum/listbox/docs/ListBox.mdx | 2 +- .../@react-spectrum/menu/docs/MenuTrigger.mdx | 2 +- .../@react-spectrum/picker/docs/Picker.mdx | 2 +- .../@react-spectrum/radio/docs/RadioGroup.mdx | 43 ++++++++++++++++++- .../@react-spectrum/table/docs/TableView.mdx | 2 +- packages/@react-spectrum/tabs/docs/Tabs.mdx | 2 +- .../@react-spectrum/tree/docs/TreeView.mdx | 2 +- .../dev/docs/pages/react-aria/testing.mdx | 6 +++ .../dev/docs/pages/react-spectrum/testing.mdx | 6 +++ .../docs/CheckboxGroup.mdx | 40 ++++++++++++++++- .../react-aria-components/docs/Dialog.mdx | 42 +++++++++++++++++- .../react-aria-components/docs/RadioGroup.mdx | 40 ++++++++++++++++- packages/react-aria-components/docs/Tabs.mdx | 4 +- packages/react-aria-components/docs/Tree.mdx | 2 +- 19 files changed, 271 insertions(+), 17 deletions(-) diff --git a/packages/@react-aria/test-utils/src/index.ts b/packages/@react-aria/test-utils/src/index.ts index ddad90b9a57..885fdc94c2c 100644 --- a/packages/@react-aria/test-utils/src/index.ts +++ b/packages/@react-aria/test-utils/src/index.ts @@ -16,10 +16,13 @@ export {pointerMap} from './userEventMaps'; export {User} from './user'; // TODO: had to export these for the docs, but not sure why I didn't have to do // so for the v3 docs? +export {CheckboxGroupTester} from './checkboxgroup'; export {ComboBoxTester} from './combobox'; +export {DialogTester} from './dialog'; export {GridListTester} from './gridlist'; export {ListBoxTester} from './listbox'; export {MenuTester} from './menu'; +export {RadioGroupTester} from './radiogroup'; export {SelectTester} from './select'; export {TableTester} from './table'; export {TabsTester} from './tabs'; diff --git a/packages/@react-spectrum/checkbox/docs/CheckboxGroup.mdx b/packages/@react-spectrum/checkbox/docs/CheckboxGroup.mdx index 67dd8eba6b7..fcefad3a544 100644 --- a/packages/@react-spectrum/checkbox/docs/CheckboxGroup.mdx +++ b/packages/@react-spectrum/checkbox/docs/CheckboxGroup.mdx @@ -11,8 +11,9 @@ import {Layout} from '@react-spectrum/docs'; export default Layout; import docs from 'docs:@react-spectrum/checkbox'; +import checkboxgroupUtil from 'docs:@react-aria/test-utils/src/checkboxgroup.ts'; import packageData from '@react-spectrum/checkbox/package.json'; -import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs'; +import {HeaderInfo, PropTable, PageDescription, VersionBadge, ClassAPI} from '@react-spectrum/docs'; ```jsx import import {Checkbox, CheckboxGroup} from '@react-spectrum/checkbox'; @@ -332,3 +333,43 @@ See the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/ Basketball ``` + +## Testing + +### Test utils + +`@react-spectrum/test-utils` offers common checkbox group interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities +in your tests. Below is the full definition of the checkbox group tester and a sample of how you could use it in your test suite. + +```ts +// CheckboxGroup.test.ts +import {render, within} from '@testing-library/react'; +import {theme} from '@react-spectrum/theme-default'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('CheckboxGroup can select multiple checkboxes', async function () { + // Render your test component/app and initialize the checkbox group tester + let {getByTestId} = render( + + + ... + + + ); + let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByTestId('test-checkboxgroup')}); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(0); + + await checkboxGroupTester.toggleCheckbox({checkbox: 0}); + expect(checkboxGroupTester.checkboxes[0]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1); + + await checkboxGroupTester.toggleCheckbox({checkbox: 4}); + expect(checkboxGroupTester.checkboxes[4]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2); +}); +``` + + diff --git a/packages/@react-spectrum/combobox/docs/ComboBox.mdx b/packages/@react-spectrum/combobox/docs/ComboBox.mdx index 81ccd82d6a4..ff582b53d73 100644 --- a/packages/@react-spectrum/combobox/docs/ComboBox.mdx +++ b/packages/@react-spectrum/combobox/docs/ComboBox.mdx @@ -996,7 +996,7 @@ isn't sufficient when resolving issues in your own test cases. ### Test utils -`@react-spectrum/test-utils` offers common combobox interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-spectrum/test-utils` offers common combobox interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the combobox tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/@react-spectrum/dialog/docs/Dialog.mdx b/packages/@react-spectrum/dialog/docs/Dialog.mdx index 6bfd237b82e..4b85a4788f6 100644 --- a/packages/@react-spectrum/dialog/docs/Dialog.mdx +++ b/packages/@react-spectrum/dialog/docs/Dialog.mdx @@ -12,7 +12,8 @@ export default Layout; import DialogAnatomy from './images/DialogAnatomy.svg'; import docs from 'docs:@react-spectrum/dialog'; -import {Image, HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs'; +import dialogUtil from 'docs:@react-aria/test-utils/src/dialog.ts'; +import {Image, HeaderInfo, PropTable, PageDescription, VersionBadge, ClassAPI} from '@react-spectrum/docs'; import packageData from '@react-spectrum/dialog/package.json'; import styles from '@react-spectrum/docs/src/docs.css'; @@ -398,3 +399,43 @@ respectively for container sizing considerations. Modal sizes on mobile devices )} ``` + +## Testing + +### Test utils + +`@react-spectrum/test-utils` offers common dialog interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities +in your tests. Below is the full definition of the dialog tester and a sample of how you could use it in your test suite. + +```ts +// Dialog.test.ts +import {render, within} from '@testing-library/react'; +import {theme} from '@react-spectrum/theme-default'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('Dialog can be opened and closed', async function () { + // Render your test component/app and initialize the dialog tester + let {getByTestId, getByRole} = render( + + + Trigger + + ... + + + + ); + let button = getByRole('button'); + let dialogTester = testUtilUser.createTester('Dialog', {root: button, overlayType: 'modal'}); + await dialogTester.open(); + let dialog = dialogTester.dialog; + expect(dialog).toBeVisible(); + await dialogTester.close(); + expect(dialog).not.toBeInTheDocument(); +}); +``` + + diff --git a/packages/@react-spectrum/list/docs/ListView.mdx b/packages/@react-spectrum/list/docs/ListView.mdx index ae791b43f22..2c64058f70e 100644 --- a/packages/@react-spectrum/list/docs/ListView.mdx +++ b/packages/@react-spectrum/list/docs/ListView.mdx @@ -1195,7 +1195,7 @@ isn't sufficient when resolving issues in your own test cases. ### Test utils -`@react-spectrum/test-utils` offers common gridlist interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-spectrum/test-utils` offers common gridlist interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the gridlist tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/@react-spectrum/listbox/docs/ListBox.mdx b/packages/@react-spectrum/listbox/docs/ListBox.mdx index 336386f4572..2ef9cf0a17b 100644 --- a/packages/@react-spectrum/listbox/docs/ListBox.mdx +++ b/packages/@react-spectrum/listbox/docs/ListBox.mdx @@ -413,7 +413,7 @@ isn't sufficient when resolving issues in your own test cases. ### Test utils -`@react-spectrum/test-utils` offers common listbox interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-spectrum/test-utils` offers common listbox interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the listbox tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/@react-spectrum/menu/docs/MenuTrigger.mdx b/packages/@react-spectrum/menu/docs/MenuTrigger.mdx index 251bddaec2f..b3b72eb44c8 100644 --- a/packages/@react-spectrum/menu/docs/MenuTrigger.mdx +++ b/packages/@react-spectrum/menu/docs/MenuTrigger.mdx @@ -260,7 +260,7 @@ isn't sufficient when resolving issues in your own test cases. ### Test utils -`@react-spectrum/test-utils` offers common menu interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-spectrum/test-utils` offers common menu interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the menu tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/@react-spectrum/picker/docs/Picker.mdx b/packages/@react-spectrum/picker/docs/Picker.mdx index 6c327e3166b..d1eb7f56fc5 100644 --- a/packages/@react-spectrum/picker/docs/Picker.mdx +++ b/packages/@react-spectrum/picker/docs/Picker.mdx @@ -592,7 +592,7 @@ isn't sufficient when resolving issues in your own test cases. ### Test utils -`@react-spectrum/test-utils` offers common select interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-spectrum/test-utils` offers common select interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the select tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/@react-spectrum/radio/docs/RadioGroup.mdx b/packages/@react-spectrum/radio/docs/RadioGroup.mdx index 050672f1517..d8b1c61ff7c 100644 --- a/packages/@react-spectrum/radio/docs/RadioGroup.mdx +++ b/packages/@react-spectrum/radio/docs/RadioGroup.mdx @@ -11,8 +11,9 @@ import {Layout} from '@react-spectrum/docs'; export default Layout; import docs from 'docs:@react-spectrum/radio'; +import radiogroupUtil from 'docs:@react-aria/test-utils/src/radiogroup.ts'; import packageData from '@react-spectrum/radio/package.json'; -import {HeaderInfo, PropTable, PageDescription} from '@react-spectrum/docs'; +import {HeaderInfo, PropTable, PageDescription, VersionBadge, ClassAPI} from '@react-spectrum/docs'; ```jsx import import {Radio, RadioGroup} from '@react-spectrum/radio'; @@ -306,3 +307,43 @@ See the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/ Dragon ``` + +## Testing + +### Test utils + +`@react-spectrum/test-utils` offers common radio group interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities +in your tests. Below is the full definition of the radio group tester and a sample of how you could use it in your test suite. + +```ts +// RadioGroup.test.ts +import {render, within} from '@testing-library/react'; +import {theme} from '@react-spectrum/theme-default'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('RadioGroup can switch the selected radio', async function () { + // Render your test component/app and initialize the radiogroup tester + let {getByRole} = render( + + + ... + + + ); + + let radioGroupTester = testUtilUser.createTester('RadioGroup', {root: getByRole('radiogroup')}); + let radios = radioGroupTester.radios; + expect(radioGroupTester.selectedRadio).toBeFalsy(); + + await radioGroupTester.triggerRadio({radio: radios[0]}); + expect(radioGroupTester.selectedRadio).toBe(radios[0]); + + await radioGroupTester.triggerRadio({radio: radios[1]}); + expect(radioGroupTester.selectedRadio).toBe(radios[1]); +}); +``` + + diff --git a/packages/@react-spectrum/table/docs/TableView.mdx b/packages/@react-spectrum/table/docs/TableView.mdx index ae4e9909584..1740998ce5e 100644 --- a/packages/@react-spectrum/table/docs/TableView.mdx +++ b/packages/@react-spectrum/table/docs/TableView.mdx @@ -1964,7 +1964,7 @@ isn't sufficient when resolving issues in your own test cases. ### Test utils -`@react-spectrum/test-utils` offers common table interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-spectrum/test-utils` offers common table interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the table tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/@react-spectrum/tabs/docs/Tabs.mdx b/packages/@react-spectrum/tabs/docs/Tabs.mdx index 3c81dd2e12e..74eb54adbf2 100644 --- a/packages/@react-spectrum/tabs/docs/Tabs.mdx +++ b/packages/@react-spectrum/tabs/docs/Tabs.mdx @@ -639,7 +639,7 @@ Tabs features automatic tab collapse behavior and may need specific mocks to tes [React Spectrum's test suite](https://github.com/adobe/react-spectrum/blob/326f48154e301edab425c8198c5c3af72422462b/packages/%40react-spectrum/tabs/test/Tabs.test.js#L58-L62) if you run into any issues with your tests. -`@react-spectrum/test-utils` offers common tabs interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-spectrum/test-utils` offers common tabs interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the tabs tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/@react-spectrum/tree/docs/TreeView.mdx b/packages/@react-spectrum/tree/docs/TreeView.mdx index 4dc29a886d7..7d2915fe3ea 100644 --- a/packages/@react-spectrum/tree/docs/TreeView.mdx +++ b/packages/@react-spectrum/tree/docs/TreeView.mdx @@ -533,7 +533,7 @@ isn't sufficient when resolving issues in your own test cases. ### Test utils -`@react-spectrum/test-utils` offers common tree interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-spectrum/test-utils` offers common tree interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-spectrum-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the tree tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/dev/docs/pages/react-aria/testing.mdx b/packages/dev/docs/pages/react-aria/testing.mdx index aab35a898ec..c649d45ea4d 100644 --- a/packages/dev/docs/pages/react-aria/testing.mdx +++ b/packages/dev/docs/pages/react-aria/testing.mdx @@ -201,14 +201,20 @@ See below for the full definition of the `User` object. Below is a list of the ARIA patterns testers currently supported by `createTester`. See the accompanying component testing docs pages for a sample of how to use the testers in your test suite. +- [CheckboxGroup](CheckboxGroup.html#test-utils) + - [ComboBox](ComboBox.html#test-utils) +- [Dialog](Dialog.html#test-utils) + - [GridList](GridList.html#test-utils) - [ListBox](ListBox.html#test-utils) - [Menu](Menu.html#test-utils) +- [RadioGroup](RadioGroup.html#test-utils) + - [Select](Select.html#test-utils) - [Table](Table.html#test-utils) diff --git a/packages/dev/docs/pages/react-spectrum/testing.mdx b/packages/dev/docs/pages/react-spectrum/testing.mdx index 99c179e3d61..90dd1e8050d 100644 --- a/packages/dev/docs/pages/react-spectrum/testing.mdx +++ b/packages/dev/docs/pages/react-spectrum/testing.mdx @@ -395,14 +395,20 @@ See below for the full definition of the `User` object. Below is a list of the ARIA patterns testers currently supported by `createTester`. See the accompanying component testing docs pages for a sample of how to use the testers in your test suite. +- [CheckboxGroup](CheckboxGroup.html#test-utils) + - [ComboBox](ComboBox.html#test-utils) +- [Dialog](Dialog.html#test-utils) + - [ListView](ListView.html#test-utils) - [ListBox](ListBox.html#test-utils) - [MenuTrigger](MenuTrigger.html#test-utils) +- [RadioGroup](RadioGroup.html#test-utils) + - [Picker](Picker.html#test-utils) - [TableView](TableView.html#test-utils) diff --git a/packages/react-aria-components/docs/CheckboxGroup.mdx b/packages/react-aria-components/docs/CheckboxGroup.mdx index b4d5082e6cb..31611bf05c1 100644 --- a/packages/react-aria-components/docs/CheckboxGroup.mdx +++ b/packages/react-aria-components/docs/CheckboxGroup.mdx @@ -13,7 +13,8 @@ export default Layout; import docs from 'docs:react-aria-components'; import statelyDocs from 'docs:@react-stately/checkbox'; import typesDocs from 'docs:@react-types/shared/src/events.d.ts'; -import {PropTable, HeaderInfo, TypeLink, PageDescription, StateTable, ContextTable} from '@react-spectrum/docs'; +import checkboxgroupUtil from 'docs:@react-aria/test-utils/src/checkboxgroup.ts'; +import {PropTable, HeaderInfo, TypeLink, PageDescription, StateTable, ContextTable, VersionBadge, ClassAPI} from '@react-spectrum/docs'; import styles from '@react-spectrum/docs/src/docs.css'; import packageData from 'react-aria-components/package.json'; import Anatomy from '@react-aria/checkbox/docs/checkboxgroup-anatomy.svg'; @@ -609,3 +610,40 @@ function SelectionCount() { ### Hooks If you need to customize things further, such as accessing internal state or customizing DOM structure, you can drop down to the lower level Hook-based API. See [useCheckboxGroup](useCheckboxGroup.html) for more details. + +## Testing + +### Test utils + +`@react-aria/test-utils` offers common checkbox group interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-aria-test-utils) for more information on how to setup these utilities +in your tests. Below is the full definition of the checkbox group tester and a sample of how you could use it in your test suite. + +```ts +// CheckboxGroup.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('CheckboxGroup can select multiple checkboxes', async function () { + // Render your test component/app and initialize the checkbox group tester + let {getByTestId} = render( + + ... + + ); + let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByTestId('test-checkboxgroup')}); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(0); + + await checkboxGroupTester.toggleCheckbox({checkbox: 0}); + expect(checkboxGroupTester.checkboxes[0]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1); + + await checkboxGroupTester.toggleCheckbox({checkbox: 4}); + expect(checkboxGroupTester.checkboxes[4]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2); +}); +``` + + diff --git a/packages/react-aria-components/docs/Dialog.mdx b/packages/react-aria-components/docs/Dialog.mdx index ddf239cc4be..2e47022c069 100644 --- a/packages/react-aria-components/docs/Dialog.mdx +++ b/packages/react-aria-components/docs/Dialog.mdx @@ -12,8 +12,9 @@ export default Layout; import docs from 'docs:react-aria-components'; import typesDocs from 'docs:@react-types/overlays'; +import dialogUtil from 'docs:@react-aria/test-utils/src/dialog.ts'; import overlayStatelyDocs from 'docs:@react-stately/overlays'; -import {PropTable, HeaderInfo, TypeLink, PageDescription, StateTable, ContextTable} from '@react-spectrum/docs'; +import {PropTable, HeaderInfo, TypeLink, PageDescription, StateTable, ContextTable, VersionBadge, ClassAPI} from '@react-spectrum/docs'; import styles from '@react-spectrum/docs/src/docs.css'; import packageData from 'react-aria-components/package.json'; import Anatomy from '@react-aria/dialog/docs/anatomy.svg'; @@ -349,3 +350,42 @@ function CloseButton() { ### Hooks If you need to customize things further, such as accessing internal state or customizing DOM structure, you can drop down to the lower level Hook-based API. See [useDialog](useDialog.html) for more details. + +## Testing + +### Test utils + +`@react-aria/test-utils` offers common dialog interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-aria-test-utils) for more information on how to setup these utilities +in your tests. Below is the full definition of the dialog tester and a sample of how you could use it in your test suite. + +```ts +// Dialog.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('Dialog can be opened and closed', async function () { + // Render your test component/app and initialize the dialog tester + let {getByTestId, getByRole} = render( + + + + + ... + + + + ); + let button = getByRole('button'); + let dialogTester = testUtilUser.createTester('Dialog', {root: button, overlayType: 'modal'}); + await dialogTester.open(); + let dialog = dialogTester.dialog; + expect(dialog).toHaveAttribute('role', 'alertdialog'); + await dialogTester.close(); + expect(dialog).not.toBeInTheDocument(); +}); +``` + + diff --git a/packages/react-aria-components/docs/RadioGroup.mdx b/packages/react-aria-components/docs/RadioGroup.mdx index ac492baff71..213d7177b6b 100644 --- a/packages/react-aria-components/docs/RadioGroup.mdx +++ b/packages/react-aria-components/docs/RadioGroup.mdx @@ -13,7 +13,8 @@ export default Layout; import docs from 'docs:react-aria-components'; import statelyDocs from 'docs:@react-stately/radio'; import typesDocs from 'docs:@react-types/shared/src/events.d.ts'; -import {PropTable, HeaderInfo, TypeLink, PageDescription, StateTable, ContextTable} from '@react-spectrum/docs'; +import radiogroupUtil from 'docs:@react-aria/test-utils/src/radiogroup.ts'; +import {PropTable, HeaderInfo, TypeLink, PageDescription, StateTable, ContextTable, VersionBadge, ClassAPI} from '@react-spectrum/docs'; import styles from '@react-spectrum/docs/src/docs.css'; import packageData from 'react-aria-components/package.json'; import Anatomy from '@react-aria/radio/docs/anatomy.svg'; @@ -630,3 +631,40 @@ RadioGroup provides a + +`@react-aria/test-utils` offers common radio group interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-aria-test-utils) for more information on how to setup these utilities +in your tests. Below is the full definition of the radio group tester and a sample of how you could use it in your test suite. + +```ts +// RadioGroup.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('RadioGroup can switch the selected radio', async function () { + // Render your test component/app and initialize the radiogroup tester + let {getByRole} = render( + + ... + + ); + + let radioGroupTester = testUtilUser.createTester('RadioGroup', {root: getByRole('radiogroup')}); + let radios = radioGroupTester.radios; + expect(radioGroupTester.selectedRadio).toBeFalsy(); + + await radioGroupTester.triggerRadio({radio: radios[0]}); + expect(radioGroupTester.selectedRadio).toBe(radios[0]); + + await radioGroupTester.triggerRadio({radio: radios[1]}); + expect(radioGroupTester.selectedRadio).toBe(radios[1]); +}); +``` + + diff --git a/packages/react-aria-components/docs/Tabs.mdx b/packages/react-aria-components/docs/Tabs.mdx index a1a73fde486..fedeb18bed2 100644 --- a/packages/react-aria-components/docs/Tabs.mdx +++ b/packages/react-aria-components/docs/Tabs.mdx @@ -123,7 +123,7 @@ import {Tabs, TabList, Tab, TabPanel, SelectionIndicator} from 'react-aria-compo position: absolute; transition-property: translate, width, height; transition-duration: 200ms; - + @media (prefers-reduced-motion: reduce) { transition: none; } @@ -815,7 +815,7 @@ If you need to customize things even further, such as accessing internal state o ### Test utils -`@react-aria/test-utils` offers common tabs interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-aria/test-utils` offers common tabs interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-aria-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the tabs tester and a sample of how you could use it in your test suite. ```ts diff --git a/packages/react-aria-components/docs/Tree.mdx b/packages/react-aria-components/docs/Tree.mdx index 3309a3d57e5..de7f7bdc4e1 100644 --- a/packages/react-aria-components/docs/Tree.mdx +++ b/packages/react-aria-components/docs/Tree.mdx @@ -2163,7 +2163,7 @@ If you need to customize things even further, such as accessing internal state o ### Test utils -`@react-aria/test-utils` offers common tree interaction utilities which you may find helpful when writing tests. See [here](../react-aria/testing.html#react-aria-test-utils) for more information on how to setup these utilities +`@react-aria/test-utils` offers common tree interaction utilities which you may find helpful when writing tests. See [here](./testing.html#react-aria-test-utils) for more information on how to setup these utilities in your tests. Below is the full definition of the tree tester and a sample of how you could use it in your test suite. ```ts From 159e76bfdd65cf0ea471d666aafe49bd5a0e258d Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 24 Oct 2025 16:58:21 -0700 Subject: [PATCH 07/14] add examples to new docs --- .../checkbox/docs/CheckboxGroup.mdx | 2 +- .../@react-spectrum/dialog/docs/Dialog.mdx | 2 +- .../@react-spectrum/radio/docs/RadioGroup.mdx | 2 +- .../dev/s2-docs/pages/react-aria/testing.mdx | 97 ++++++++++++++++++- packages/dev/s2-docs/pages/s2/testing.mdx | 95 +++++++++++++++++- .../docs/CheckboxGroup.mdx | 2 +- .../react-aria-components/docs/Dialog.mdx | 2 +- .../react-aria-components/docs/RadioGroup.mdx | 2 +- 8 files changed, 196 insertions(+), 8 deletions(-) diff --git a/packages/@react-spectrum/checkbox/docs/CheckboxGroup.mdx b/packages/@react-spectrum/checkbox/docs/CheckboxGroup.mdx index fcefad3a544..91e27b8197e 100644 --- a/packages/@react-spectrum/checkbox/docs/CheckboxGroup.mdx +++ b/packages/@react-spectrum/checkbox/docs/CheckboxGroup.mdx @@ -343,7 +343,7 @@ in your tests. Below is the full definition of the checkbox group tester and a s ```ts // CheckboxGroup.test.ts -import {render, within} from '@testing-library/react'; +import {render} from '@testing-library/react'; import {theme} from '@react-spectrum/theme-default'; import {User} from '@react-spectrum/test-utils'; diff --git a/packages/@react-spectrum/dialog/docs/Dialog.mdx b/packages/@react-spectrum/dialog/docs/Dialog.mdx index 4b85a4788f6..3353600dec6 100644 --- a/packages/@react-spectrum/dialog/docs/Dialog.mdx +++ b/packages/@react-spectrum/dialog/docs/Dialog.mdx @@ -409,7 +409,7 @@ in your tests. Below is the full definition of the dialog tester and a sample of ```ts // Dialog.test.ts -import {render, within} from '@testing-library/react'; +import {render} from '@testing-library/react'; import {theme} from '@react-spectrum/theme-default'; import {User} from '@react-spectrum/test-utils'; diff --git a/packages/@react-spectrum/radio/docs/RadioGroup.mdx b/packages/@react-spectrum/radio/docs/RadioGroup.mdx index d8b1c61ff7c..e82b7c5d428 100644 --- a/packages/@react-spectrum/radio/docs/RadioGroup.mdx +++ b/packages/@react-spectrum/radio/docs/RadioGroup.mdx @@ -317,7 +317,7 @@ in your tests. Below is the full definition of the radio group tester and a samp ```ts // RadioGroup.test.ts -import {render, within} from '@testing-library/react'; +import {render} from '@testing-library/react'; import {theme} from '@react-spectrum/theme-default'; import {User} from '@react-spectrum/test-utils'; diff --git a/packages/dev/s2-docs/pages/react-aria/testing.mdx b/packages/dev/s2-docs/pages/react-aria/testing.mdx index 803a9c23f76..12aac831e63 100644 --- a/packages/dev/s2-docs/pages/react-aria/testing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/testing.mdx @@ -230,7 +230,38 @@ See below for the full definition of the `User` object. ### Patterns - + + +<> + ```ts isInSwitcher + // CheckboxGroup.test.ts + import {render} from '@testing-library/react'; + import {User} from '@react-aria/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); + // ... + + it('CheckboxGroup can select multiple checkboxes', async function () { + // Render your test component/app and initialize the checkbox group tester + let {getByTestId} = render( + + ... + + ); + let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByTestId('test-checkboxgroup')}); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(0); + + await checkboxGroupTester.toggleCheckbox({checkbox: 0}); + expect(checkboxGroupTester.checkboxes[0]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1); + + await checkboxGroupTester.toggleCheckbox({checkbox: 4}); + expect(checkboxGroupTester.checkboxes[4]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2); + }); + ``` + + <> ```ts isInSwitcher @@ -262,6 +293,39 @@ See below for the full definition of the `User` object. +<> + ```ts isInSwitcher + // Dialog.test.ts + import {render} from '@testing-library/react'; + import {User} from '@react-aria/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); + // ... + + it('Dialog can be opened and closed', async function () { + // Render your test component/app and initialize the dialog tester + let {getByTestId, getByRole} = render( + + + + + ... + + + + ); + let button = getByRole('button'); + let dialogTester = testUtilUser.createTester('Dialog', {root: button, overlayType: 'modal'}); + await dialogTester.open(); + let dialog = dialogTester.dialog; + expect(dialog).toHaveAttribute('role', 'alertdialog'); + await dialogTester.close(); + expect(dialog).not.toBeInTheDocument(); + }); + ``` + + + <> ```ts isInSwitcher // GridList.test.ts @@ -356,6 +420,37 @@ See below for the full definition of the `User` object. +<> + ```ts isInSwitcher + // RadioGroup.test.ts + import {render} from '@testing-library/react'; + import {User} from '@react-aria/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); + // ... + + it('RadioGroup can switch the selected radio', async function () { + // Render your test component/app and initialize the radiogroup tester + let {getByRole} = render( + + ... + + ); + + let radioGroupTester = testUtilUser.createTester('RadioGroup', {root: getByRole('radiogroup')}); + let radios = radioGroupTester.radios; + expect(radioGroupTester.selectedRadio).toBeFalsy(); + + await radioGroupTester.triggerRadio({radio: radios[0]}); + expect(radioGroupTester.selectedRadio).toBe(radios[0]); + + await radioGroupTester.triggerRadio({radio: radios[1]}); + expect(radioGroupTester.selectedRadio).toBe(radios[1]); + }); + ``` + + + <> ```ts isInSwitcher // Select.test.ts diff --git a/packages/dev/s2-docs/pages/s2/testing.mdx b/packages/dev/s2-docs/pages/s2/testing.mdx index f56c8b4abd9..0ed335b341e 100644 --- a/packages/dev/s2-docs/pages/s2/testing.mdx +++ b/packages/dev/s2-docs/pages/s2/testing.mdx @@ -224,7 +224,38 @@ See below for the full definition of the `User` object. ### Patterns - + + +<> + ```ts isInSwitcher + // CheckboxGroup.test.ts + import {render} from '@testing-library/react'; + import {User} from '@react-spectrum/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); + // ... + + it('CheckboxGroup can select multiple checkboxes', async function () { + // Render your test component/app and initialize the checkbox group tester + let {getByTestId} = render( + + ... + + ); + let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByTestId('test-checkboxgroup')}); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(0); + + await checkboxGroupTester.toggleCheckbox({checkbox: 0}); + expect(checkboxGroupTester.checkboxes[0]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1); + + await checkboxGroupTester.toggleCheckbox({checkbox: 4}); + expect(checkboxGroupTester.checkboxes[4]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2); + }); + ``` + + <> ```ts isInSwitcher @@ -256,6 +287,37 @@ See below for the full definition of the `User` object. +<> + ```ts isInSwitcher + // Dialog.test.ts + import {render} from '@testing-library/react'; + import {User} from '@react-spectrum/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); + // ... + + it('Dialog can be opened and closed', async function () { + // Render your test component/app and initialize the dialog tester + let {getByTestId, getByRole} = render( + + Trigger + + ... + + + ); + let button = getByRole('button'); + let dialogTester = testUtilUser.createTester('Dialog', {root: button, overlayType: 'modal'}); + await dialogTester.open(); + let dialog = dialogTester.dialog; + expect(dialog).toBeVisible(); + await dialogTester.close(); + expect(dialog).not.toBeInTheDocument(); + }); + ``` + + + <> ```ts isInSwitcher // Menu.test.ts @@ -318,6 +380,37 @@ See below for the full definition of the `User` object. +<> + ```ts isInSwitcher + // RadioGroup.test.ts + import {render} from '@testing-library/react'; + import {User} from '@react-spectrum/test-utils'; + + let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); + // ... + + it('RadioGroup can switch the selected radio', async function () { + // Render your test component/app and initialize the radiogroup tester + let {getByRole} = render( + + ... + + ); + + let radioGroupTester = testUtilUser.createTester('RadioGroup', {root: getByRole('radiogroup')}); + let radios = radioGroupTester.radios; + expect(radioGroupTester.selectedRadio).toBeFalsy(); + + await radioGroupTester.triggerRadio({radio: radios[0]}); + expect(radioGroupTester.selectedRadio).toBe(radios[0]); + + await radioGroupTester.triggerRadio({radio: radios[1]}); + expect(radioGroupTester.selectedRadio).toBe(radios[1]); + }); + ``` + + + <> ```ts isInSwitcher // Table.test.ts diff --git a/packages/react-aria-components/docs/CheckboxGroup.mdx b/packages/react-aria-components/docs/CheckboxGroup.mdx index 31611bf05c1..1ecece634b0 100644 --- a/packages/react-aria-components/docs/CheckboxGroup.mdx +++ b/packages/react-aria-components/docs/CheckboxGroup.mdx @@ -620,7 +620,7 @@ in your tests. Below is the full definition of the checkbox group tester and a s ```ts // CheckboxGroup.test.ts -import {render, within} from '@testing-library/react'; +import {render} from '@testing-library/react'; import {User} from '@react-aria/test-utils'; let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); diff --git a/packages/react-aria-components/docs/Dialog.mdx b/packages/react-aria-components/docs/Dialog.mdx index 2e47022c069..55768ae9f51 100644 --- a/packages/react-aria-components/docs/Dialog.mdx +++ b/packages/react-aria-components/docs/Dialog.mdx @@ -360,7 +360,7 @@ in your tests. Below is the full definition of the dialog tester and a sample of ```ts // Dialog.test.ts -import {render, within} from '@testing-library/react'; +import {render} from '@testing-library/react'; import {User} from '@react-aria/test-utils'; let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); diff --git a/packages/react-aria-components/docs/RadioGroup.mdx b/packages/react-aria-components/docs/RadioGroup.mdx index 213d7177b6b..3bafc1670e0 100644 --- a/packages/react-aria-components/docs/RadioGroup.mdx +++ b/packages/react-aria-components/docs/RadioGroup.mdx @@ -641,7 +641,7 @@ in your tests. Below is the full definition of the radio group tester and a samp ```ts // RadioGroup.test.ts -import {render, within} from '@testing-library/react'; +import {render} from '@testing-library/react'; import {User} from '@react-aria/test-utils'; let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); From 50c9e075dc2af6e8619b251a85b841b188cba80c Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 31 Oct 2025 11:46:06 -0700 Subject: [PATCH 08/14] clean up old code, no more switcher --- packages/dev/s2-docs/src/ClassAPI.tsx | 22 +++------------------- packages/dev/s2-docs/src/Layout.tsx | 17 ----------------- 2 files changed, 3 insertions(+), 36 deletions(-) diff --git a/packages/dev/s2-docs/src/ClassAPI.tsx b/packages/dev/s2-docs/src/ClassAPI.tsx index e4a560c5846..8d837a3e283 100644 --- a/packages/dev/s2-docs/src/ClassAPI.tsx +++ b/packages/dev/s2-docs/src/ClassAPI.tsx @@ -1,30 +1,14 @@ import {InterfaceType, setLinks, TInterface} from './types'; import React from 'react'; -import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; - -const wrapper = style({ - paddingX: { - default: 0, - isInSwitcher: 32 - }, - paddingBottom: { - default: 0, - isInSwitcher: 32 - } -}); interface ClassAPIProps { class: TInterface, - links: any, - // TODO: replace by making this a client component if we can do that - isInSwitcher?: boolean + links: any } -export function ClassAPI({class: c, links, isInSwitcher}: ClassAPIProps) { +export function ClassAPI({class: c, links}: ClassAPIProps) { setLinks(links); return ( -
    - -
    + ); } diff --git a/packages/dev/s2-docs/src/Layout.tsx b/packages/dev/s2-docs/src/Layout.tsx index e31c45bdddd..af2772eef66 100644 --- a/packages/dev/s2-docs/src/Layout.tsx +++ b/packages/dev/s2-docs/src/Layout.tsx @@ -46,23 +46,6 @@ const components = { p: ({children, ...props}) =>

    {children}

    , ul: (props) =>
      , li: ({children, ...props}) =>
    • {children}
    • , - blockquote: ({children, ...props}) => ( -
      - {children} -
      - ), Figure: (props) =>
      , Caption: (props) =>
      , CodeBlock: CodeBlock, From 5a10ff09cae32b9036014487667d1bf70f0adb39 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 31 Oct 2025 15:47:57 -0700 Subject: [PATCH 09/14] refactor test util content so it is in each page --- .../pages/react-aria/CheckboxGroup.mdx | 59 +++ .../dev/s2-docs/pages/react-aria/ComboBox.mdx | 58 +++ .../dev/s2-docs/pages/react-aria/GridList.mdx | 72 ++++ .../dev/s2-docs/pages/react-aria/ListBox.mdx | 62 +++ .../dev/s2-docs/pages/react-aria/Menu.mdx | 72 ++++ .../s2-docs/pages/react-aria/RadioGroup.mdx | 59 +++ .../dev/s2-docs/pages/react-aria/Select.mdx | 57 ++- .../dev/s2-docs/pages/react-aria/Table.mdx | 17 +- .../dev/s2-docs/pages/react-aria/Tabs.mdx | 58 ++- .../dev/s2-docs/pages/react-aria/Tree.mdx | 75 ++++ .../dev/s2-docs/pages/react-aria/testing.mdx | 365 +----------------- .../dev/s2-docs/pages/s2/CheckboxGroup.mdx | 60 ++- packages/dev/s2-docs/pages/s2/ComboBox.mdx | 59 ++- packages/dev/s2-docs/pages/s2/Dialog.mdx | 62 ++- packages/dev/s2-docs/pages/s2/Menu.mdx | 72 ++++ packages/dev/s2-docs/pages/s2/Picker.mdx | 56 ++- packages/dev/s2-docs/pages/s2/RadioGroup.mdx | 60 ++- packages/dev/s2-docs/pages/s2/TableView.mdx | 79 +++- packages/dev/s2-docs/pages/s2/Tabs.mdx | 64 ++- packages/dev/s2-docs/pages/s2/TreeView.mdx | 76 +++- packages/dev/s2-docs/pages/s2/testing.mdx | 303 +-------------- .../dev/s2-docs/src/PatternTestingFAQ.tsx | 24 ++ 22 files changed, 1201 insertions(+), 668 deletions(-) create mode 100644 packages/dev/s2-docs/src/PatternTestingFAQ.tsx diff --git a/packages/dev/s2-docs/pages/react-aria/CheckboxGroup.mdx b/packages/dev/s2-docs/pages/react-aria/CheckboxGroup.mdx index 95c6e3260b5..baf2656c7c1 100644 --- a/packages/dev/s2-docs/pages/react-aria/CheckboxGroup.mdx +++ b/packages/dev/s2-docs/pages/react-aria/CheckboxGroup.mdx @@ -5,6 +5,10 @@ import docs from 'docs:react-aria-components'; import vanillaDocs from 'docs:vanilla-starter/CheckboxGroup'; import '../../tailwind/tailwind.css'; import Anatomy from '@react-aria/checkbox/docs/checkboxgroup-anatomy.svg'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['input']; @@ -120,3 +124,58 @@ import {Form} from 'vanilla-starter/Form';; ### CheckboxGroup + +## Testing + +### Test utils + +`@react-aria/test-utils` offers common checkbox group interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `CheckboxGroup` tester in your test cases. This gives you access to `CheckboxGroup` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `CheckboxGroup` tester, use it to simulate checkbox selection, and verify the checkbox group's state after each interaction. + +```ts +// CheckboxGroup.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('CheckboxGroup can select multiple checkboxes', async function () { + // Render your test component/app and initialize the checkbox group tester + let {getByTestId} = render( + + ... + + ); + let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByTestId('test-checkboxgroup')}); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(0); + + await checkboxGroupTester.toggleCheckbox({checkbox: 0}); + expect(checkboxGroupTester.checkboxes[0]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1); + + await checkboxGroupTester.toggleCheckbox({checkbox: 4}); + expect(checkboxGroupTester.checkboxes[4]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2); +}); +``` + +See below for the full definition of the `User` and the `CheckboxGroup` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/ComboBox.mdx b/packages/dev/s2-docs/pages/react-aria/ComboBox.mdx index 0efd57fda82..a1bca66bed4 100644 --- a/packages/dev/s2-docs/pages/react-aria/ComboBox.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ComboBox.mdx @@ -6,6 +6,10 @@ import {ComboBox as VanillaComboBox, ComboBoxItem} from 'vanilla-starter/ComboBo import vanillaDocs from 'docs:vanilla-starter/ComboBox'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/ComboBoxAnatomy.svg'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['autocomplete', 'search', 'typeahead', 'input']; @@ -353,3 +357,57 @@ import {ComboBox, ComboBoxItem} from 'vanilla-starter/ComboBox'; ### ComboBox + +## Testing + +### Test utils + +`@react-aria/test-utils` offers common combobox interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `ComboBox` tester in your test cases. This gives you access to `ComboBox` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `ComboBox` tester, use it to simulate option selection, and verify the combobox's state after each interaction. + +```ts +// Combobox.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('ComboBox can select an option via keyboard', async function () { + // Render your test component/app and initialize the combobox tester + let {getByTestId} = render( + + ... + + ); + let comboboxTester = testUtilUser.createTester('ComboBox', {root: getByTestId('test-combobox'), interactionType: 'keyboard'}); + + await comboboxTester.open(); + expect(comboboxTester.listbox).toBeInTheDocument(); + + let options = comboboxTester.options(); + await comboboxTester.selectOption({option: options[0]}); + expect(comboboxTester.combobox.value).toBe('One'); + expect(comboboxTester.listbox).not.toBeInTheDocument(); +}); +``` + +See below for the full definition of the `User` and the `ComboBox` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/GridList.mdx b/packages/dev/s2-docs/pages/react-aria/GridList.mdx index 5f86d9bf1dd..9bed73d59f9 100644 --- a/packages/dev/s2-docs/pages/react-aria/GridList.mdx +++ b/packages/dev/s2-docs/pages/react-aria/GridList.mdx @@ -6,6 +6,10 @@ import {GridList as VanillaGridList, GridListItem} from 'vanilla-starter/GridLis import vanillaDocs from 'docs:vanilla-starter/GridList'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/GridListAnatomy.svg'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['list view']; @@ -923,3 +927,71 @@ function Example() { ### GridListLoadMoreItem + +## Testing + +### General setup + +GridList features long press interactions on its items depending on the item actions provided and if the user is interacting with the gridlist on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](testing.html#timers) + +[Long press](testing.html#simulating-user-long-press) + +### Test utils + +`@react-aria/test-utils` offers common gridlist interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `GridList` tester in your test cases. This gives you access to `GridList` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `GridList` tester, use it to simulate item selection, and verify the gridlist's state after each interaction. + +```ts +// GridList.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('GridList can select a row via keyboard', async function () { + // Render your test component/app and initialize the gridlist tester + let {getByTestId} = render( + + ... + + ); + let gridListTester = testUtilUser.createTester('GridList', {root: getByTestId('test-gridlist'), interactionType: 'keyboard'}); + + let row = gridListTester.rows[0]; + expect(within(row).getByRole('checkbox')).not.toBeChecked(); + expect(gridListTester.selectedRows).toHaveLength(0); + + await gridListTester.toggleRowSelection({row: 0}); + expect(within(row).getByRole('checkbox')).toBeChecked(); + expect(gridListTester.selectedRows).toHaveLength(1); + + await gridListTester.toggleRowSelection({row: 0}); + expect(within(row).getByRole('checkbox')).not.toBeChecked(); + expect(gridListTester.selectedRows).toHaveLength(0); +}); +``` + +See below for the full definition of the `User` and the `GridList` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/ListBox.mdx b/packages/dev/s2-docs/pages/react-aria/ListBox.mdx index 93fbcd78ccf..36abf9516fd 100644 --- a/packages/dev/s2-docs/pages/react-aria/ListBox.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ListBox.mdx @@ -7,6 +7,9 @@ import vanillaDocs from 'docs:vanilla-starter/ListBox'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/ListBoxAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['options']; @@ -415,3 +418,62 @@ function Example() { ### ListBoxLoadMoreItem + +## Testing + +### General setup + +ListBox features long press interactions on its options depending on the option actions provided and if the user is interacting with the listbox on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](testing.html#timers) + +[Long press](testing.html#simulating-user-long-press) + +### Test utils + +`@react-aria/test-utils` offers common listbox interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `ListBox` tester in your test cases. This gives you access to `ListBox` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `ListBox` tester, use it to simulate option selection, and verify the listbox's state after each interaction. + +```ts +// ListBox.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('ListBox can select an option via keyboard', async function () { + // Render your test component/app and initialize the listbox tester + let {getByTestId} = render( + + ... + + ); + let listboxTester = testUtilUser.createTester('ListBox', {root: getByTestId('test-listbox'), interactionType: 'keyboard'}); + + await listboxTester.toggleOptionSelection({option: 4}); + expect(listboxTester.options()[4]).toHaveAttribute('aria-selected', 'true'); +}); +``` + +See below for the full definition of the `User` and the `ListBox` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/Menu.mdx b/packages/dev/s2-docs/pages/react-aria/Menu.mdx index d20963fb7a2..ad72fb57130 100644 --- a/packages/dev/s2-docs/pages/react-aria/Menu.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Menu.mdx @@ -5,6 +5,9 @@ import docs from 'docs:react-aria-components'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/MenuAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['dropdown']; @@ -577,3 +580,72 @@ import {ChevronDown} from 'lucide-react'; ### SubmenuTrigger + +## Testing + +### General setup + +Menu features long press interactions depending on the actions provided and if the user is interacting with the menu on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](testing.html#timers) + +[Long press](testing.html#simulating-user-long-press) + +### Test utils + +`@react-aria/test-utils` offers common menu interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Menu` tester in your test cases. This gives you access to `Menu` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Menu` tester, use it to find and open a submenu, and verify the menu's state after each interaction. + +```ts +// Menu.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Menu can open its submenu via keyboard', async function () { + // Render your test component/app and initialize the menu tester + let {getByTestId} = render( + + + ... + + ); + let menuTester = testUtilUser.createTester('Menu', {root: getByTestId('test-menutrigger'), interactionType: 'keyboard'}); + + await menuTester.open(); + expect(menuTester.menu).toBeInTheDocument(); + let submenuTriggers = menuTester.submenuTriggers; + expect(submenuTriggers).toHaveLength(1); + + let submenuTester = await menuTester.openSubmenu({submenuTrigger: 'Share…'}); + expect(submenuTester.menu).toBeInTheDocument(); + + await submenuTester.selectOption({option: submenuTester.options()[0]}); + expect(submenuTester.menu).not.toBeInTheDocument(); + expect(menuTester.menu).not.toBeInTheDocument(); +}); +``` + +See below for the full definition of the `User` and the `Menu` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/RadioGroup.mdx b/packages/dev/s2-docs/pages/react-aria/RadioGroup.mdx index c412c6dd29a..d1720ae47a8 100644 --- a/packages/dev/s2-docs/pages/react-aria/RadioGroup.mdx +++ b/packages/dev/s2-docs/pages/react-aria/RadioGroup.mdx @@ -5,6 +5,10 @@ import docs from 'docs:react-aria-components'; import vanillaDocs from 'docs:vanilla-starter/RadioGroup'; import '../../tailwind/tailwind.css'; import Anatomy from '@react-aria/radio/docs/anatomy.svg'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['input']; @@ -113,3 +117,58 @@ import {Form} from 'vanilla-starter/Form';; ### Radio + +## Testing + +### Test utils + +`@react-aria/test-utils` offers common radio group interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `RadioGroup` tester in your test cases. This gives you access to `RadioGroup` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `RadioGroup` tester, use it to change radio selection, and verify the radio group's state after each interaction. + +```ts +// RadioGroup.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('RadioGroup can switch the selected radio', async function () { + // Render your test component/app and initialize the radiogroup tester + let {getByRole} = render( + + ... + + ); + + let radioGroupTester = testUtilUser.createTester('RadioGroup', {root: getByRole('radiogroup')}); + let radios = radioGroupTester.radios; + expect(radioGroupTester.selectedRadio).toBeFalsy(); + + await radioGroupTester.triggerRadio({radio: radios[0]}); + expect(radioGroupTester.selectedRadio).toBe(radios[0]); + + await radioGroupTester.triggerRadio({radio: radios[1]}); + expect(radioGroupTester.selectedRadio).toBe(radios[1]); +}); +``` + +See below for the full definition of the `User` and the `RadioGroup` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/Select.mdx b/packages/dev/s2-docs/pages/react-aria/Select.mdx index 1f60d9f4d2f..ee1565114d6 100644 --- a/packages/dev/s2-docs/pages/react-aria/Select.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Select.mdx @@ -5,6 +5,10 @@ import docs from 'docs:react-aria-components'; import vanillaDocs from 'docs:vanilla-starter/Select'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/SelectAnatomy.svg'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['picker', 'dropdown', 'menu', 'input']; @@ -242,7 +246,7 @@ function SelectWithTagGroup() { {/*- end highlight -*/} - + +## Testing + +### Test utils + +`@react-aria/test-utils` offers common select interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Select` tester in your test cases. This gives you access to `Select` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Select` tester, use it to simulate option selection, and verify the select's state after each interaction. + +```ts +// Select.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Select can select an option via keyboard', async function () { + // Render your test component/app and initialize the select tester + let {getByTestId} = render( + + ); + let selectTester = testUtilUser.createTester('Select', {root: getByTestId('test-select'), interactionType: 'keyboard'}); + let trigger = selectTester.trigger; + expect(trigger).toHaveTextContent('Select an item'); + + await selectTester.selectOption({option: 'Cat'}); + expect(trigger).toHaveTextContent('Cat'); +}); +``` + +See below for the full definition of the `User` and the `Select` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/Table.mdx b/packages/dev/s2-docs/pages/react-aria/Table.mdx index f624a0cb59b..89510917f17 100644 --- a/packages/dev/s2-docs/pages/react-aria/Table.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Table.mdx @@ -6,8 +6,9 @@ import docs from 'docs:react-aria-components'; import vanillaDocs from 'docs:vanilla-starter/Table'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/TableAnatomy.svg'; -import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; import testUtilDocs from 'docs:@react-aria/test-utils'; +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['data', 'grid']; @@ -713,9 +714,6 @@ function ReorderableTable() { ## Testing -TODO make this a subpage - - ### General setup Table features long press interactions on its rows depending on the row actions provided and if the user is interacting with the table on @@ -784,13 +782,4 @@ See below for the full definition of the `User` and the `Table` tester. ### Testing FAQ -> When using the test utils, what if a certain interaction errors or doesn't seem to result in the expected state? - -In cases like this, first double check your test setup and make sure that your test is rendering your table in its expected -state before the test util interaction call. If everything looks correct, you can always fall back to simulating interactions manually, -and using the test util to query your table's state post interaction. - -> The tester doesn't offer a specific interaction flow, what should I do? - -Whenever the table tester queries its rows/cells/etc or triggers a user flow, it does so against the current state of the table. Therefore the table test can be used alongside -whatever simulated user flow you add. + diff --git a/packages/dev/s2-docs/pages/react-aria/Tabs.mdx b/packages/dev/s2-docs/pages/react-aria/Tabs.mdx index 6ca184cd397..6b1e421bdb4 100644 --- a/packages/dev/s2-docs/pages/react-aria/Tabs.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Tabs.mdx @@ -6,6 +6,10 @@ import {Tabs as VanillaTabs, TabsItem} from 'vanilla-starter/Tabs'; import vanillaDocs from 'docs:vanilla-starter/Tabs'; import '../../tailwind/tailwind.css'; import Anatomy from '@react-aria/tabs/docs/anatomy.svg'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['navigation']; @@ -143,7 +147,7 @@ function Example() { return (
      - + +## Testing + +### Test utils + +`@react-aria/test-utils` offers common tabs interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Tabs` tester in your test cases. This gives you access to `Tabs` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Tabs` tester, use it to change tab selection, and verify the tabs' state after each interaction. + +```ts +// Tabs.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Tabs can change selection via keyboard', async function () { + // Render your test component/app and initialize the listbox tester + let {getByTestId} = render( + + ... + + ); + let tabsTester = testUtilUser.createTester('Tabs', {root: getByTestId('test-tabs'), interactionType: 'keyboard'}); + + let tabs = tabsTester.tabs; + expect(tabsTester.selectedTab).toBe(tabs[0]); + + await tabsTester.triggerTab({tab: 1}); + expect(tabsTester.selectedTab).toBe(tabs[1]); +}); +``` + +See below for the full definition of the `User` and the `Tabs` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/Tree.mdx b/packages/dev/s2-docs/pages/react-aria/Tree.mdx index dca4d8bb346..1dbc0093516 100644 --- a/packages/dev/s2-docs/pages/react-aria/Tree.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Tree.mdx @@ -6,6 +6,10 @@ import {Tree as VanillaTree, TreeItem} from 'vanilla-starter/Tree'; import vanillaDocs from 'docs:vanilla-starter/Tree'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/TreeAnatomy.svg'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['data', 'tree', 'nested', 'hierarchy']; @@ -403,3 +407,74 @@ function Example() { cssVariables={{ '--tree-item-level': "The depth of the item within the tree. Useful to calculate indentation." }} /> + +## Testing + +### General setup + +Tree features long press interactions on its rows depending on the row actions provided and if the user is interacting with the tree on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](testing.html#timers) + +[Long press](testing.html#simulating-user-long-press) + +### Test utils + +`@react-aria/test-utils` offers common tree interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Tree` tester in your test cases. This gives you access to `Tree` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Tree` tester, use it to simulate row expansion and selection, and verify the tree's state after each interaction. + +```ts +// Tree.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Tree can select and expand a item via keyboard', async function () { + // Render your test component/app and initialize the Tree tester + let {getByTestId} = render( + + ... + + ); + let treeTester = testUtilUser.createTester('Tree', {root: getByTestId('test-tree'), interactionType: 'keyboard'}); + + await treeTester.toggleRowSelection({row: 0}); + expect(treeTester.selectedRows).toHaveLength(1); + expect(within(treeTester.rows[0]).getByRole('checkbox')).toBeChecked(); + + await treeTester.toggleRowSelection({row: 1}); + expect(treeTester.selectedRows).toHaveLength(2); + expect(within(treeTester.rows[1]).getByRole('checkbox')).toBeChecked(); + + await treeTester.toggleRowSelection({row: 0}); + expect(treeTester.selectedRows).toHaveLength(1); + expect(within(treeTester.rows[0]).getByRole('checkbox')).not.toBeChecked(); + + await treeTester.toggleRowExpansion({index: 0}); + expect(treeTester.rows[0]).toHaveAttribute('aria-expanded', 'true'); +}); +``` + +See below for the full definition of the `User` and the `Tree` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/testing.mdx b/packages/dev/s2-docs/pages/react-aria/testing.mdx index 12aac831e63..52c8a580ff0 100644 --- a/packages/dev/s2-docs/pages/react-aria/testing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/testing.mdx @@ -180,8 +180,6 @@ fireEvent.mouseUp(thumb, {pageX: 50}); ``` ## React Aria test utils - -TODO can't place this next to the header here [@react-aria/test-utils](https://www.npmjs.com/package/@react-aria/test-utils) is a set of testing utilities that aims to make writing unit tests easier for consumers of React Aria @@ -228,354 +226,19 @@ See below for the full definition of the `User` object. -### Patterns - - - -<> - ```ts isInSwitcher - // CheckboxGroup.test.ts - import {render} from '@testing-library/react'; - import {User} from '@react-aria/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); - // ... - - it('CheckboxGroup can select multiple checkboxes', async function () { - // Render your test component/app and initialize the checkbox group tester - let {getByTestId} = render( - - ... - - ); - let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByTestId('test-checkboxgroup')}); - expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(0); - - await checkboxGroupTester.toggleCheckbox({checkbox: 0}); - expect(checkboxGroupTester.checkboxes[0]).toBeChecked(); - expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1); - - await checkboxGroupTester.toggleCheckbox({checkbox: 4}); - expect(checkboxGroupTester.checkboxes[4]).toBeChecked(); - expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2); - }); - ``` - - - -<> - ```ts isInSwitcher - // Combobox.test.ts - import {render} from '@testing-library/react'; - import {User} from '@react-aria/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse'}); - // ... - - it('ComboBox can select an option via keyboard', async function () { - // Render your test component/app and initialize the combobox tester - let {getByTestId} = render( - - ... - - ); - let comboboxTester = testUtilUser.createTester('ComboBox', {root: getByTestId('test-combobox'), interactionType: 'keyboard'}); - - await comboboxTester.open(); - expect(comboboxTester.listbox).toBeInTheDocument(); - - let options = comboboxTester.options(); - await comboboxTester.selectOption({option: options[0]}); - expect(comboboxTester.combobox.value).toBe('One'); - expect(comboboxTester.listbox).not.toBeInTheDocument(); - }); - ``` - - - -<> - ```ts isInSwitcher - // Dialog.test.ts - import {render} from '@testing-library/react'; - import {User} from '@react-aria/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); - // ... - - it('Dialog can be opened and closed', async function () { - // Render your test component/app and initialize the dialog tester - let {getByTestId, getByRole} = render( - - - - - ... - - - - ); - let button = getByRole('button'); - let dialogTester = testUtilUser.createTester('Dialog', {root: button, overlayType: 'modal'}); - await dialogTester.open(); - let dialog = dialogTester.dialog; - expect(dialog).toHaveAttribute('role', 'alertdialog'); - await dialogTester.close(); - expect(dialog).not.toBeInTheDocument(); - }); - ``` - - - -<> - ```ts isInSwitcher - // GridList.test.ts - import {render, within} from '@testing-library/react'; - import {User} from '@react-aria/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse'}); - // ... - - it('GridList can select a row via keyboard', async function () { - // Render your test component/app and initialize the gridlist tester - let {getByTestId} = render( - - ... - - ); - let gridListTester = testUtilUser.createTester('GridList', {root: getByTestId('test-gridlist'), interactionType: 'keyboard'}); - - let row = gridListTester.rows[0]; - expect(within(row).getByRole('checkbox')).not.toBeChecked(); - expect(gridListTester.selectedRows).toHaveLength(0); - - await gridListTester.toggleRowSelection({row: 0}); - expect(within(row).getByRole('checkbox')).toBeChecked(); - expect(gridListTester.selectedRows).toHaveLength(1); - - await gridListTester.toggleRowSelection({row: 0}); - expect(within(row).getByRole('checkbox')).not.toBeChecked(); - expect(gridListTester.selectedRows).toHaveLength(0); - }); - ``` - - - -<> - ```ts isInSwitcher - // ListBox.test.ts - import {render} from '@testing-library/react'; - import {User} from '@react-aria/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse'}); - // ... - - it('ListBox can select an option via keyboard', async function () { - // Render your test component/app and initialize the listbox tester - let {getByTestId} = render( - - ... - - ); - let listboxTester = testUtilUser.createTester('ListBox', {root: getByTestId('test-listbox'), interactionType: 'keyboard'}); - - await listboxTester.toggleOptionSelection({option: 4}); - expect(listboxTester.options()[4]).toHaveAttribute('aria-selected', 'true'); - }); - ``` - - - -<> - ```ts isInSwitcher - // Menu.test.ts - import {render} from '@testing-library/react'; - import {User} from '@react-aria/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse'}); - // ... - - it('Menu can open its submenu via keyboard', async function () { - // Render your test component/app and initialize the menu tester - let {getByTestId} = render( - - - ... - - ); - let menuTester = testUtilUser.createTester('Menu', {root: getByTestId('test-menutrigger'), interactionType: 'keyboard'}); - - await menuTester.open(); - expect(menuTester.menu).toBeInTheDocument(); - let submenuTriggers = menuTester.submenuTriggers; - expect(submenuTriggers).toHaveLength(1); - - let submenuTester = await menuTester.openSubmenu({submenuTrigger: 'Share…'}); - expect(submenuTester.menu).toBeInTheDocument(); - - await submenuTester.selectOption({option: submenuTester.options()[0]}); - expect(submenuTester.menu).not.toBeInTheDocument(); - expect(menuTester.menu).not.toBeInTheDocument(); - }); - ``` - - - -<> - ```ts isInSwitcher - // RadioGroup.test.ts - import {render} from '@testing-library/react'; - import {User} from '@react-aria/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); - // ... - - it('RadioGroup can switch the selected radio', async function () { - // Render your test component/app and initialize the radiogroup tester - let {getByRole} = render( - - ... - - ); - - let radioGroupTester = testUtilUser.createTester('RadioGroup', {root: getByRole('radiogroup')}); - let radios = radioGroupTester.radios; - expect(radioGroupTester.selectedRadio).toBeFalsy(); - - await radioGroupTester.triggerRadio({radio: radios[0]}); - expect(radioGroupTester.selectedRadio).toBe(radios[0]); - - await radioGroupTester.triggerRadio({radio: radios[1]}); - expect(radioGroupTester.selectedRadio).toBe(radios[1]); - }); - ``` - - - -<> - ```ts isInSwitcher - // Select.test.ts - import {render} from '@testing-library/react'; - import {User} from '@react-aria/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse'}); - // ... - it('Select can select an option via keyboard', async function () { - // Render your test component/app and initialize the select tester - let {getByTestId} = render( - - ); - let selectTester = testUtilUser.createTester('Select', {root: getByTestId('test-select'), interactionType: 'keyboard'}); - let trigger = selectTester.trigger; - expect(trigger).toHaveTextContent('Select an item'); - - await selectTester.selectOption({option: 'Cat'}); - expect(trigger).toHaveTextContent('Cat'); - }); - ``` - - - -<> - ```ts isInSwitcher - // Table.test.ts - import {render, within} from '@testing-library/react'; - import {User} from '@react-aria/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); - // ... - - it('Table can toggle row selection', async function () { - // Render your test component/app and initialize the table tester - let {getByTestId} = render( - - ... -
      - ); - let tableTester = testUtilUser.createTester('Table', {root: getByTestId('test-table')}); - expect(tableTester.selectedRows).toHaveLength(0); - - await tableTester.toggleSelectAll(); - expect(tableTester.selectedRows).toHaveLength(10); - - await tableTester.toggleRowSelection({row: 2}); - expect(tableTester.selectedRows).toHaveLength(9); - let checkbox = within(tableTester.rows[2]).getByRole('checkbox'); - expect(checkbox).not.toBeChecked(); - - await tableTester.toggleSelectAll(); - expect(tableTester.selectedRows).toHaveLength(10); - expect(checkbox).toBeChecked(); - - await tableTester.toggleSelectAll(); - expect(tableTester.selectedRows).toHaveLength(0); - }); - ``` - - - -<> - ```ts isInSwitcher - // Tabs.test.ts - import {render} from '@testing-library/react'; - import {User} from '@react-aria/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse'}); - // ... - - it('Tabs can change selection via keyboard', async function () { - // Render your test component/app and initialize the listbox tester - let {getByTestId} = render( - - ... - - ); - let tabsTester = testUtilUser.createTester('Tabs', {root: getByTestId('test-tabs'), interactionType: 'keyboard'}); - - let tabs = tabsTester.tabs; - expect(tabsTester.selectedTab).toBe(tabs[0]); - - await tabsTester.triggerTab({tab: 1}); - expect(tabsTester.selectedTab).toBe(tabs[1]); - }); - ``` - - - -<> - ```ts isInSwitcher - // Tree.test.ts - import {render, within} from '@testing-library/react'; - import {User} from '@react-aria/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse'}); - // ... +### Patterns - it('Tree can select a item via keyboard', async function () { - // Render your test component/app and initialize the Tree tester - let {getByTestId} = render( - - ... - - ); - let treeTester = testUtilUser.createTester('Tree', {root: getByTestId('test-tree'), interactionType: 'keyboard'}); - - await treeTester.toggleRowSelection({row: 0}); - expect(treeTester.selectedRows).toHaveLength(1); - expect(within(treeTester.rows[0]).getByRole('checkbox')).toBeChecked(); - - await treeTester.toggleRowSelection({row: 1}); - expect(treeTester.selectedRows).toHaveLength(2); - expect(within(treeTester.rows[1]).getByRole('checkbox')).toBeChecked(); - - await treeTester.toggleRowSelection({row: 0}); - expect(treeTester.selectedRows).toHaveLength(1); - expect(within(treeTester.rows[0]).getByRole('checkbox')).not.toBeChecked(); - }); - ``` - - - -
      +Below is a list of the ARIA patterns testers currently supported by `createTester`. See the accompanying component testing docs pages for a sample of how to use +the testers in your test suite. + +- [CheckboxGroup](./CheckboxGroup.html#testing) +- [ComboBox](./ComboBox.html#testing) +- [GridList](./GridList.html#testing) +- [ListBox](./ListBox.html#testing) +- [Menu](./Menu.html#testing) +- [RadioGroup](./RadioGroup.html#testing) +- [Select](./Select.html#testing) +- [Table](./Table.html#testing) +- [Tabs](./Tabs.html#testing) +- [Tree](./Tree.html#testing) diff --git a/packages/dev/s2-docs/pages/s2/CheckboxGroup.mdx b/packages/dev/s2-docs/pages/s2/CheckboxGroup.mdx index 7ecc1ad5288..28e89f82ed2 100644 --- a/packages/dev/s2-docs/pages/s2/CheckboxGroup.mdx +++ b/packages/dev/s2-docs/pages/s2/CheckboxGroup.mdx @@ -1,8 +1,11 @@ import {Layout} from '../../src/Layout'; export default Layout; -import {CheckboxGroup} from '@react-spectrum/s2'; +import {CheckboxGroup, InlineAlert, Heading, Content} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = []; @@ -93,3 +96,58 @@ import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; ### CheckboxGroup + +## Testing + +### Test utils + +`@react-spectrum/test-utils` offers common checkbox group interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `CheckboxGroup` tester in your test cases. This gives you access to `CheckboxGroup` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `CheckboxGroup` tester, use it to simulate checkbox selection, and verify the checkbox group's state after each interaction. + +```ts +// CheckboxGroup.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('CheckboxGroup can select multiple checkboxes', async function () { + // Render your test component/app and initialize the checkbox group tester + let {getByTestId} = render( + + ... + + ); + let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByTestId('test-checkboxgroup')}); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(0); + + await checkboxGroupTester.toggleCheckbox({checkbox: 0}); + expect(checkboxGroupTester.checkboxes[0]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1); + + await checkboxGroupTester.toggleCheckbox({checkbox: 4}); + expect(checkboxGroupTester.checkboxes[4]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2); +}); +``` + +See below for the full definition of the `User` and the `CheckboxGroup` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/ComboBox.mdx b/packages/dev/s2-docs/pages/s2/ComboBox.mdx index 45a7218308a..1719da2b8c6 100644 --- a/packages/dev/s2-docs/pages/s2/ComboBox.mdx +++ b/packages/dev/s2-docs/pages/s2/ComboBox.mdx @@ -1,8 +1,11 @@ import {Layout} from '../../src/Layout'; export default Layout; -import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['autocomplete', 'search', 'typeahead']; @@ -467,3 +470,57 @@ import {ComboBox, ComboBoxItem} from '@react-spectrum/s2'; ### ComboBoxSection + +## Testing + +### Test utils + +`@react-spectrum/test-utils` offers common combobox interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `ComboBox` tester in your test cases. This gives you access to `ComboBox` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `ComboBox` tester, use it to simulate option selection, and verify the combobox's state after each interaction. + +```ts +// Combobox.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('ComboBox can select an option via keyboard', async function () { + // Render your test component/app and initialize the combobox tester + let {getByTestId} = render( + + ... + + ); + let comboboxTester = testUtilUser.createTester('ComboBox', {root: getByTestId('test-combobox'), interactionType: 'keyboard'}); + + await comboboxTester.open(); + expect(comboboxTester.listbox).toBeInTheDocument(); + + let options = comboboxTester.options(); + await comboboxTester.selectOption({option: options[0]}); + expect(comboboxTester.combobox.value).toBe('One'); + expect(comboboxTester.listbox).not.toBeInTheDocument(); +}); +``` + +See below for the full definition of the `User` and the `ComboBox` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/Dialog.mdx b/packages/dev/s2-docs/pages/s2/Dialog.mdx index dfc3e176a13..5fca00ec740 100644 --- a/packages/dev/s2-docs/pages/s2/Dialog.mdx +++ b/packages/dev/s2-docs/pages/s2/Dialog.mdx @@ -1,8 +1,11 @@ import {Layout} from '../../src/Layout'; export default Layout; -import {Dialog, FullscreenDialog, CustomDialog, DialogTrigger, DialogContainer, Button, ButtonGroup, Heading, Header, Content, Footer, Image, TextField, Checkbox, CloseButton, ActionButton, IllustratedMessage} from '@react-spectrum/s2'; +import {Dialog, FullscreenDialog, CustomDialog, DialogTrigger, DialogContainer, Button, ButtonGroup, Heading, Header, Content, Footer, Image, TextField, Checkbox, CloseButton, ActionButton, IllustratedMessage, InlineAlert} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['modal', 'popup', 'overlay']; @@ -200,7 +203,7 @@ function DialogContainerExample() { Delete Item Share Item - + {/*- begin highlight -*/} setDialogType(null)}> {/*- end highlight -*/} @@ -310,3 +313,58 @@ function DialogContainerExample() { ``` + +## Testing + +### Test utils + +`@react-spectrum/test-utils` offers common dialog interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Dialog` tester in your test cases. This gives you access to `Dialog` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Dialog` tester, use it to open and close the dialog, and verify the dialog's state after each interaction. + +```ts +// Dialog.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('Dialog can be opened and closed', async function () { + // Render your test component/app and initialize the dialog tester + let {getByTestId, getByRole} = render( + + Trigger + + ... + + + ); + let button = getByRole('button'); + let dialogTester = testUtilUser.createTester('Dialog', {root: button, overlayType: 'modal'}); + await dialogTester.open(); + let dialog = dialogTester.dialog; + expect(dialog).toBeVisible(); + await dialogTester.close(); + expect(dialog).not.toBeInTheDocument(); +}); +``` + +See below for the full definition of the `User` and the `Dialog` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/Menu.mdx b/packages/dev/s2-docs/pages/s2/Menu.mdx index 248d3471389..e0a1c2bd836 100644 --- a/packages/dev/s2-docs/pages/s2/Menu.mdx +++ b/packages/dev/s2-docs/pages/s2/Menu.mdx @@ -3,6 +3,9 @@ export default Layout; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' import docs from 'docs:@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['dropdown']; @@ -518,3 +521,72 @@ import {MenuTrigger, Menu, MenuItem, ActionButton} from '@react-spectrum/s2'; ### SubmenuTrigger + +## Testing + +### General setup + +Menu features long press interactions depending on the actions provided and if the user is interacting with the menu on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](testing.html#timers) + +[Long press](testing.html#simulating-user-long-press) + +### Test utils + +`@react-spectrum/test-utils` offers common menu interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Menu` tester in your test cases. This gives you access to `Menu` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Menu` tester, use it to find and open a submenu, and verify the menu's state after each interaction. + +```ts +// Menu.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Menu can open its submenu via keyboard', async function () { + // Render your test component/app and initialize the menu tester + let {getByTestId} = render( + + + ... + + ); + let menuTester = testUtilUser.createTester('Menu', {root: getByTestId('test-menutrigger'), interactionType: 'keyboard'}); + + await menuTester.open(); + expect(menuTester.menu).toBeInTheDocument(); + let submenuTriggers = menuTester.submenuTriggers; + expect(submenuTriggers).toHaveLength(1); + + let submenuTester = await menuTester.openSubmenu({submenuTrigger: 'Share…'}); + expect(submenuTester.menu).toBeInTheDocument(); + + await submenuTester.selectOption({option: submenuTester.options()[0]}); + expect(submenuTester.menu).not.toBeInTheDocument(); + expect(menuTester.menu).not.toBeInTheDocument(); +}); +``` + +See below for the full definition of the `User` and the `Menu` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/Picker.mdx b/packages/dev/s2-docs/pages/s2/Picker.mdx index 88c956a12fb..dbde971c6bd 100644 --- a/packages/dev/s2-docs/pages/s2/Picker.mdx +++ b/packages/dev/s2-docs/pages/s2/Picker.mdx @@ -1,8 +1,11 @@ import {Layout} from '../../src/Layout'; export default Layout; -import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['select', 'dropdown']; @@ -333,3 +336,54 @@ function Example(props) { ### PickerSection + +## Testing + +### Test utils + +`@react-spectrum/test-utils` offers common picker interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Picker` tester in your test cases. This gives you access to `Picker` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Picker` tester, use it to simulate option selection, and verify the picker's state after each interaction. + +```ts +// Picker.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Picker can select an option via keyboard', async function () { + // Render your test component/app and initialize the select tester + let {getByTestId} = render( + + ... + + ); + let selectTester = testUtilUser.createTester('Select', {root: getByTestId('test-select'), interactionType: 'keyboard'}); + let trigger = selectTester.trigger; + expect(trigger).toHaveTextContent('Select an item'); + + await selectTester.selectOption({option: 'Cat'}); + expect(trigger).toHaveTextContent('Cat'); +}); +``` + +See below for the full definition of the `User` and the `Picker` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/RadioGroup.mdx b/packages/dev/s2-docs/pages/s2/RadioGroup.mdx index 30ae82b98eb..eb11de11e14 100644 --- a/packages/dev/s2-docs/pages/s2/RadioGroup.mdx +++ b/packages/dev/s2-docs/pages/s2/RadioGroup.mdx @@ -1,8 +1,11 @@ import {Layout} from '../../src/Layout'; export default Layout; -import {RadioGroup, Radio} from '@react-spectrum/s2'; +import {RadioGroup, Radio, InlineAlert, Heading, Content} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['input']; @@ -94,3 +97,58 @@ function Example(props) { ### Radio + +## Testing + +### Test utils + +`@react-spectrum/test-utils` offers common radio group interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `RadioGroup` tester in your test cases. This gives you access to `RadioGroup` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `RadioGroup` tester, use it to changing radio selection, and verify the radio group's state after each interaction. + +```ts +// RadioGroup.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('RadioGroup can switch the selected radio', async function () { + // Render your test component/app and initialize the radiogroup tester + let {getByRole} = render( + + ... + + ); + + let radioGroupTester = testUtilUser.createTester('RadioGroup', {root: getByRole('radiogroup')}); + let radios = radioGroupTester.radios; + expect(radioGroupTester.selectedRadio).toBeFalsy(); + + await radioGroupTester.triggerRadio({radio: radios[0]}); + expect(radioGroupTester.selectedRadio).toBe(radios[0]); + + await radioGroupTester.triggerRadio({radio: radios[1]}); + expect(radioGroupTester.selectedRadio).toBe(radios[1]); +}); +``` + +See below for the full definition of the `User` and the `RadioGroup` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/TableView.mdx b/packages/dev/s2-docs/pages/s2/TableView.mdx index 1d884e3c76f..1ee9b8c7618 100644 --- a/packages/dev/s2-docs/pages/s2/TableView.mdx +++ b/packages/dev/s2-docs/pages/s2/TableView.mdx @@ -2,7 +2,10 @@ import {Layout} from '../../src/Layout'; export default Layout; import docs from 'docs:@react-spectrum/s2'; -import {InlineAlert, Heading, Content} from '@react-spectrum/s2' +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['table', 'data', 'grid']; @@ -895,4 +898,76 @@ export default function EditableTable(props) { ### EditableCell - \ No newline at end of file + + +## Testing + +### General setup + +TableView features long press interactions on its rows depending on the row actions provided and if the user is interacting with the table on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](testing.html#timers) + +[Long press](testing.html#simulating-user-long-press) + +### Test utils + +`@react-spectrum/test-utils` offers common table interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `TableView` tester in your test cases. This gives you access to `TableView` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `TableView` tester, use it to simulate row selection, and verify the table's state after each interaction. + +```ts +// Table.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('TableView can toggle row selection', async function () { + // Render your test component/app and initialize the table tester + let {getByTestId} = render( + + ... + + ); + let tableTester = testUtilUser.createTester('Table', {root: getByTestId('test-table')}); + expect(tableTester.selectedRows).toHaveLength(0); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(10); + + await tableTester.toggleRowSelection({row: 2}); + expect(tableTester.selectedRows).toHaveLength(9); + let checkbox = within(tableTester.rows[2]).getByRole('checkbox'); + expect(checkbox).not.toBeChecked(); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(10); + expect(checkbox).toBeChecked(); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(0); +}); +``` + +See below for the full definition of the `User` and the `TableView` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/Tabs.mdx b/packages/dev/s2-docs/pages/s2/Tabs.mdx index 3bd7a1013e6..de486dd62b1 100644 --- a/packages/dev/s2-docs/pages/s2/Tabs.mdx +++ b/packages/dev/s2-docs/pages/s2/Tabs.mdx @@ -3,6 +3,10 @@ export default Layout; import docs from 'docs:@react-spectrum/s2'; export const tags = ['navigation']; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; # Tabs @@ -161,7 +165,7 @@ import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; width: 320, padding: 16, borderWidth: 1, - borderStyle: 'solid', + borderStyle: 'solid', borderColor: 'gray-300', borderRadius: 'default', overflow: 'hidden', @@ -275,4 +279,60 @@ function Example() { ### TabPanel - \ No newline at end of file + +## Testing + +### General setup + +Tabs features automatic tab collapse behavior and may need specific mocks to test said behavior. +TODO: update this with what mocks are required + +### Test utils + +`@react-spectrum/test-utils` offers common tabs interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Tabs` tester in your test cases. This gives you access to `Tabs` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Tabs` tester, use it to change tab selection, and verify the tabs' state after each interaction. + +```ts +// Tabs.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Tabs can change selection via keyboard', async function () { + // Render your test component/app and initialize the listbox tester + let {getByTestId} = render( + + ... + + ); + let tabsTester = testUtilUser.createTester('Tabs', {root: getByTestId('test-tabs'), interactionType: 'keyboard'}); + + let tabs = tabsTester.tabs; + expect(tabsTester.selectedTab).toBe(tabs[0]); + + await tabsTester.triggerTab({tab: 1}); + expect(tabsTester.selectedTab).toBe(tabs[1]); +}); +``` + +See below for the full definition of the `User` and the `Tabs` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/TreeView.mdx b/packages/dev/s2-docs/pages/s2/TreeView.mdx index e754da1a821..577299edf38 100644 --- a/packages/dev/s2-docs/pages/s2/TreeView.mdx +++ b/packages/dev/s2-docs/pages/s2/TreeView.mdx @@ -1,8 +1,11 @@ import {Layout} from '../../src/Layout'; export default Layout; -import {TreeView, TreeViewItem, TreeViewItemContent, Collection, Text, ActionMenu, MenuItem} from '@react-spectrum/s2'; +import {TreeView, TreeViewItem, TreeViewItemContent, Collection, Text, ActionMenu, MenuItem, InlineAlert, Heading, Content} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['hierarchy', 'data', 'nested']; @@ -378,3 +381,74 @@ function Example(props) { ### TreeViewLoadMoreItem + +## Testing + +### General setup + +TreeView features long press interactions on its rows depending on the row actions provided and if the user is interacting with the tree on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](testing.html#timers) + +[Long press](testing.html#simulating-user-long-press) + +### Test utils + +`@react-spectrum/test-utils` offers common tree interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `TreeView` tester in your test cases. This gives you access to `TreeView` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `TreeView` tester, use it to simulate row expansion and selection, and verify the tree's state after each interaction. + +```ts +// Tree.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('TreeView can select a item via keyboard', async function () { + // Render your test component/app and initialize the Tree tester + let {getByTestId} = render( + + ... + + ); + let treeTester = testUtilUser.createTester('Tree', {root: getByTestId('test-tree'), interactionType: 'keyboard'}); + + await treeTester.toggleRowSelection({row: 0}); + expect(treeTester.selectedRows).toHaveLength(1); + expect(within(treeTester.rows[0]).getByRole('checkbox')).toBeChecked(); + + await treeTester.toggleRowSelection({row: 1}); + expect(treeTester.selectedRows).toHaveLength(2); + expect(within(treeTester.rows[1]).getByRole('checkbox')).toBeChecked(); + + await treeTester.toggleRowSelection({row: 0}); + expect(treeTester.selectedRows).toHaveLength(1); + expect(within(treeTester.rows[0]).getByRole('checkbox')).not.toBeChecked(); + + await treeTester.toggleRowExpansion({index: 0}); + expect(treeTester.rows[0]).toHaveAttribute('aria-expanded', 'true'); +}); +``` + +See below for the full definition of the `User` and the `TreeView` tester. + + + + +### Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/testing.mdx b/packages/dev/s2-docs/pages/s2/testing.mdx index 0ed335b341e..254624c746d 100644 --- a/packages/dev/s2-docs/pages/s2/testing.mdx +++ b/packages/dev/s2-docs/pages/s2/testing.mdx @@ -173,9 +173,8 @@ fireEvent.mouseMove(thumb, {pageX: 50}); fireEvent.mouseUp(thumb, {pageX: 50}); ``` -## React Spectrum test utils -TODO can't place this next to the header here +## React Spectrum test utils In addition to the test utilities mentioned above, [@react-spectrum/test-utils](https://www.npmjs.com/package/@react-spectrum/test-utils) re-exports the same test utils available in `@react-aria/test-utils`, including @@ -222,293 +221,17 @@ See below for the full definition of the `User` object. -### Patterns - - - -<> - ```ts isInSwitcher - // CheckboxGroup.test.ts - import {render} from '@testing-library/react'; - import {User} from '@react-spectrum/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); - // ... - - it('CheckboxGroup can select multiple checkboxes', async function () { - // Render your test component/app and initialize the checkbox group tester - let {getByTestId} = render( - - ... - - ); - let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByTestId('test-checkboxgroup')}); - expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(0); - - await checkboxGroupTester.toggleCheckbox({checkbox: 0}); - expect(checkboxGroupTester.checkboxes[0]).toBeChecked(); - expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1); - - await checkboxGroupTester.toggleCheckbox({checkbox: 4}); - expect(checkboxGroupTester.checkboxes[4]).toBeChecked(); - expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2); - }); - ``` - - - -<> - ```ts isInSwitcher - // Combobox.test.ts - import {render} from '@testing-library/react'; - import {User} from '@react-spectrum/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse'}); - // ... - it('ComboBox can select an option via keyboard', async function () { - // Render your test component/app and initialize the combobox tester - let {getByTestId} = render( - - ... - - ); - let comboboxTester = testUtilUser.createTester('ComboBox', {root: getByTestId('test-combobox'), interactionType: 'keyboard'}); - - await comboboxTester.open(); - expect(comboboxTester.listbox).toBeInTheDocument(); - - let options = comboboxTester.options(); - await comboboxTester.selectOption({option: options[0]}); - expect(comboboxTester.combobox.value).toBe('One'); - expect(comboboxTester.listbox).not.toBeInTheDocument(); - }); - ``` - - - -<> - ```ts isInSwitcher - // Dialog.test.ts - import {render} from '@testing-library/react'; - import {User} from '@react-spectrum/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); - // ... - - it('Dialog can be opened and closed', async function () { - // Render your test component/app and initialize the dialog tester - let {getByTestId, getByRole} = render( - - Trigger - - ... - - - ); - let button = getByRole('button'); - let dialogTester = testUtilUser.createTester('Dialog', {root: button, overlayType: 'modal'}); - await dialogTester.open(); - let dialog = dialogTester.dialog; - expect(dialog).toBeVisible(); - await dialogTester.close(); - expect(dialog).not.toBeInTheDocument(); - }); - ``` - - - -<> - ```ts isInSwitcher - // Menu.test.ts - import {render} from '@testing-library/react'; - import {User} from '@react-spectrum/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse'}); - // ... - - it('Menu can open its submenu via keyboard', async function () { - // Render your test component/app and initialize the menu tester - let {getByTestId} = render( - - - ... - - ); - let menuTester = testUtilUser.createTester('Menu', {root: getByTestId('test-menutrigger'), interactionType: 'keyboard'}); - - await menuTester.open(); - expect(menuTester.menu).toBeInTheDocument(); - let submenuTriggers = menuTester.submenuTriggers; - expect(submenuTriggers).toHaveLength(1); - - let submenuTester = await menuTester.openSubmenu({submenuTrigger: 'Share…'}); - expect(submenuTester.menu).toBeInTheDocument(); - - await submenuTester.selectOption({option: submenuTester.options()[0]}); - expect(submenuTester.menu).not.toBeInTheDocument(); - expect(menuTester.menu).not.toBeInTheDocument(); - }); - ``` - - - -<> - ```ts isInSwitcher - // Picker.test.ts - import {render} from '@testing-library/react'; - import {User} from '@react-spectrum/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse'}); - // ... - - it('Picker can select an option via keyboard', async function () { - // Render your test component/app and initialize the select tester - let {getByTestId} = render( - - ... - - ); - let selectTester = testUtilUser.createTester('Select', {root: getByTestId('test-select'), interactionType: 'keyboard'}); - let trigger = selectTester.trigger; - expect(trigger).toHaveTextContent('Select an item'); - - await selectTester.selectOption({option: 'Cat'}); - expect(trigger).toHaveTextContent('Cat'); - }); - ``` - - - -<> - ```ts isInSwitcher - // RadioGroup.test.ts - import {render} from '@testing-library/react'; - import {User} from '@react-spectrum/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); - // ... - - it('RadioGroup can switch the selected radio', async function () { - // Render your test component/app and initialize the radiogroup tester - let {getByRole} = render( - - ... - - ); - - let radioGroupTester = testUtilUser.createTester('RadioGroup', {root: getByRole('radiogroup')}); - let radios = radioGroupTester.radios; - expect(radioGroupTester.selectedRadio).toBeFalsy(); - - await radioGroupTester.triggerRadio({radio: radios[0]}); - expect(radioGroupTester.selectedRadio).toBe(radios[0]); - - await radioGroupTester.triggerRadio({radio: radios[1]}); - expect(radioGroupTester.selectedRadio).toBe(radios[1]); - }); - ``` - - - -<> - ```ts isInSwitcher - // Table.test.ts - import {render, within} from '@testing-library/react'; - import {User} from '@react-spectrum/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); - // ... - - it('TableView can toggle row selection', async function () { - // Render your test component/app and initialize the table tester - let {getByTestId} = render( - - ... - - ); - let tableTester = testUtilUser.createTester('Table', {root: getByTestId('test-table')}); - expect(tableTester.selectedRows).toHaveLength(0); - - await tableTester.toggleSelectAll(); - expect(tableTester.selectedRows).toHaveLength(10); - - await tableTester.toggleRowSelection({row: 2}); - expect(tableTester.selectedRows).toHaveLength(9); - let checkbox = within(tableTester.rows[2]).getByRole('checkbox'); - expect(checkbox).not.toBeChecked(); - - await tableTester.toggleSelectAll(); - expect(tableTester.selectedRows).toHaveLength(10); - expect(checkbox).toBeChecked(); - - await tableTester.toggleSelectAll(); - expect(tableTester.selectedRows).toHaveLength(0); - }); - ``` - - - -<> - ```ts isInSwitcher - // Tabs.test.ts - import {render} from '@testing-library/react'; - import {User} from '@react-spectrum/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse'}); - // ... - - it('Tabs can change selection via keyboard', async function () { - // Render your test component/app and initialize the listbox tester - let {getByTestId} = render( - - ... - - ); - let tabsTester = testUtilUser.createTester('Tabs', {root: getByTestId('test-tabs'), interactionType: 'keyboard'}); - - let tabs = tabsTester.tabs; - expect(tabsTester.selectedTab).toBe(tabs[0]); - - await tabsTester.triggerTab({tab: 1}); - expect(tabsTester.selectedTab).toBe(tabs[1]); - }); - ``` - - - -<> - ```ts isInSwitcher - // Tree.test.ts - import {render, within} from '@testing-library/react'; - import {User} from '@react-spectrum/test-utils'; - - let testUtilUser = new User({interactionType: 'mouse'}); - // ... +### Patterns - it('TreeView can select a item via keyboard', async function () { - // Render your test component/app and initialize the Tree tester - let {getByTestId} = render( - - ... - - ); - let treeTester = testUtilUser.createTester('Tree', {root: getByTestId('test-tree'), interactionType: 'keyboard'}); - - await treeTester.toggleRowSelection({row: 0}); - expect(treeTester.selectedRows).toHaveLength(1); - expect(within(treeTester.rows[0]).getByRole('checkbox')).toBeChecked(); - - await treeTester.toggleRowSelection({row: 1}); - expect(treeTester.selectedRows).toHaveLength(2); - expect(within(treeTester.rows[1]).getByRole('checkbox')).toBeChecked(); - - await treeTester.toggleRowSelection({row: 0}); - expect(treeTester.selectedRows).toHaveLength(1); - expect(within(treeTester.rows[0]).getByRole('checkbox')).not.toBeChecked(); - }); - ``` - - - - +Below is a list of the ARIA patterns testers currently supported by createTester. See the accompanying component testing docs pages for a sample of how to use the testers in your test suite. + +- [CheckboxGroup](./CheckboxGroup.html#testing) +- [ComboBox](./ComboBox.html#testing) +- [Dialog](./Dialog.html#testing) +- [Menu](./Menu.html#testing) +- [Picker](./Picker.html#testing) +- [RadioGroup](./RadioGroup.html#testing) +- [TableView](./TableView.html#testing) +- [Tabs](./Tabs.html#testing) +- [TreeView](./TreeView.html#testing) diff --git a/packages/dev/s2-docs/src/PatternTestingFAQ.tsx b/packages/dev/s2-docs/src/PatternTestingFAQ.tsx new file mode 100644 index 00000000000..6ef7b742f81 --- /dev/null +++ b/packages/dev/s2-docs/src/PatternTestingFAQ.tsx @@ -0,0 +1,24 @@ +import {Disclosure, DisclosurePanel, DisclosureTitle} from '@react-spectrum/s2'; +import React from 'react'; + +export function PatternTestingFAQ({patternName}: {patternName: string}) { + return ( + <> + + When using the test utils, what if a certain interaction errors or doesn't seem to result in the expected state? + + In cases like this, first double check your test setup and make sure that your test is rendering your {patternName} in its expected + state before the test util interaction call. If everything looks correct, you can always fall back to simulating interactions manually, + and using the test util to query your {patternName}'s state post interaction. + + + + The tester doesn't offer a specific interaction flow, what should I do? + + Whenever the {patternName} tester queries its elements or triggers a user flow, it does so against the current state of the {patternName}. Therefore the {patternName} tester can be used alongside + whatever simulated user flow you add. + + + + ); +} From 5b9fda588bbc0d0fa537967a5c29e371d72dc8a7 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 31 Oct 2025 16:32:14 -0700 Subject: [PATCH 10/14] debug why subpage why failing --- .../dev/s2-docs/pages/react-aria/Table.mdx | 78 +---------------- .../pages/react-aria/Table/testing.mdx | 83 +++++++++++++++++++ 2 files changed, 86 insertions(+), 75 deletions(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/Table/testing.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/Table.mdx b/packages/dev/s2-docs/pages/react-aria/Table.mdx index 89510917f17..bffe5a8593a 100644 --- a/packages/dev/s2-docs/pages/react-aria/Table.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Table.mdx @@ -7,10 +7,11 @@ import vanillaDocs from 'docs:vanilla-starter/Table'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/TableAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['data', 'grid']; +export const relatedPages = [ + {title: 'Testing', url: './Table/testing.html'} +]; # Table @@ -710,76 +711,3 @@ function ReorderableTable() { ### TableLoadMoreItem - - -## Testing - -### General setup - -Table features long press interactions on its rows depending on the row actions provided and if the user is interacting with the table on -a touch device. Please see the following sections in the general testing documentation for more information on how to handle these -behaviors in your test suite. - -[Timers](testing.html#timers) - -[Long press](testing.html#simulating-user-long-press) - -### Test utils - -`@react-aria/test-utils` offers common table interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `Table` tester in your test cases. This gives you access to `Table` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `Table` tester, use it to simulate row selection, and verify the table's state after each interaction. - -```ts -// Table.test.ts -import {render, within} from '@testing-library/react'; -import {User} from '@react-aria/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); -// ... - -it('Table can toggle row selection', async function () { - // Render your test component/app and initialize the table tester - let {getByTestId} = render( - - ... -
      - ); - let tableTester = testUtilUser.createTester('Table', {root: getByTestId('test-table')}); - expect(tableTester.selectedRows).toHaveLength(0); - - await tableTester.toggleSelectAll(); - expect(tableTester.selectedRows).toHaveLength(10); - - await tableTester.toggleRowSelection({row: 2}); - expect(tableTester.selectedRows).toHaveLength(9); - let checkbox = within(tableTester.rows[2]).getByRole('checkbox'); - expect(checkbox).not.toBeChecked(); - - await tableTester.toggleSelectAll(); - expect(tableTester.selectedRows).toHaveLength(10); - expect(checkbox).toBeChecked(); - - await tableTester.toggleSelectAll(); - expect(tableTester.selectedRows).toHaveLength(0); -}); -``` - -See below for the full definition of the `User` and the `Table` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/react-aria/Table/testing.mdx b/packages/dev/s2-docs/pages/react-aria/Table/testing.mdx new file mode 100644 index 00000000000..c9a298e19b1 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Table/testing.mdx @@ -0,0 +1,83 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'table', 'test-utils']; +export const description = 'Testing Table with React Aria test utils'; + +# Testing Table + +## General setup + +Table features long press interactions on its rows depending on the row actions provided and if the user is interacting with the table on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](../testing.html#timers) + +[Long press](../testing.html#simulating-user-long-press) + +## Test utils + +`@react-aria/test-utils` offers common table interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Table` tester in your test cases. This gives you access to `Table` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Table` tester, use it to simulate row selection, and verify the table's state after each interaction. + +```ts +// Table.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('Table can toggle row selection', async function () { + // Render your test component/app and initialize the table tester + let {getByTestId} = render( + + ... +
      + ); + let tableTester = testUtilUser.createTester('Table', {root: getByTestId('test-table')}); + expect(tableTester.selectedRows).toHaveLength(0); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(10); + + await tableTester.toggleRowSelection({row: 2}); + expect(tableTester.selectedRows).toHaveLength(9); + let checkbox = within(tableTester.rows[2]).getByRole('checkbox'); + expect(checkbox).not.toBeChecked(); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(10); + expect(checkbox).toBeChecked(); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(0); +}); +``` + +See below for the full definition of the `User` and the `Table` tester. + + + + +## Testing FAQ + + From 6db86ef24ffb75b933a7e24e9a9696afa72fe397 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Fri, 31 Oct 2025 17:08:57 -0700 Subject: [PATCH 11/14] move to subpages --- .../pages/react-aria/CheckboxGroup.mdx | 61 +------------- .../react-aria/CheckboxGroup/testing.mdx | 66 +++++++++++++++ .../dev/s2-docs/pages/react-aria/ComboBox.mdx | 60 +------------- .../pages/react-aria/ComboBox/testing.mdx | 65 +++++++++++++++ .../dev/s2-docs/pages/react-aria/GridList.mdx | 74 +---------------- .../pages/react-aria/GridList/testing.mdx | 79 ++++++++++++++++++ .../dev/s2-docs/pages/react-aria/ListBox.mdx | 65 +-------------- .../pages/react-aria/ListBox/testing.mdx | 70 ++++++++++++++++ .../dev/s2-docs/pages/react-aria/Menu.mdx | 75 +---------------- .../s2-docs/pages/react-aria/Menu/testing.mdx | 80 ++++++++++++++++++ .../s2-docs/pages/react-aria/RadioGroup.mdx | 61 +------------- .../pages/react-aria/RadioGroup/testing.mdx | 66 +++++++++++++++ .../dev/s2-docs/pages/react-aria/Select.mdx | 57 +------------ .../pages/react-aria/Select/testing.mdx | 62 ++++++++++++++ .../dev/s2-docs/pages/react-aria/Tabs.mdx | 58 +------------ .../s2-docs/pages/react-aria/Tabs/testing.mdx | 63 ++++++++++++++ .../dev/s2-docs/pages/react-aria/Tree.mdx | 77 +---------------- .../s2-docs/pages/react-aria/Tree/testing.mdx | 82 ++++++++++++++++++ .../dev/s2-docs/pages/react-aria/testing.mdx | 20 ++--- .../dev/s2-docs/pages/s2/CheckboxGroup.mdx | 61 +------------- .../pages/s2/CheckboxGroup/testing.mdx | 66 +++++++++++++++ packages/dev/s2-docs/pages/s2/ComboBox.mdx | 60 +------------- .../dev/s2-docs/pages/s2/ComboBox/testing.mdx | 65 +++++++++++++++ packages/dev/s2-docs/pages/s2/Dialog.mdx | 61 +------------- .../dev/s2-docs/pages/s2/Dialog/testing.mdx | 66 +++++++++++++++ packages/dev/s2-docs/pages/s2/Menu.mdx | 75 +---------------- .../dev/s2-docs/pages/s2/Menu/testing.mdx | 80 ++++++++++++++++++ packages/dev/s2-docs/pages/s2/Picker.mdx | 57 +------------ .../dev/s2-docs/pages/s2/Picker/testing.mdx | 62 ++++++++++++++ packages/dev/s2-docs/pages/s2/RadioGroup.mdx | 61 +------------- .../s2-docs/pages/s2/RadioGroup/testing.mdx | 66 +++++++++++++++ packages/dev/s2-docs/pages/s2/TableView.mdx | 78 +---------------- .../s2-docs/pages/s2/TableView/testing.mdx | 83 +++++++++++++++++++ packages/dev/s2-docs/pages/s2/Tabs.mdx | 64 +------------- .../dev/s2-docs/pages/s2/Tabs/testing.mdx | 68 +++++++++++++++ packages/dev/s2-docs/pages/s2/TreeView.mdx | 77 +---------------- .../dev/s2-docs/pages/s2/TreeView/testing.mdx | 82 ++++++++++++++++++ packages/dev/s2-docs/pages/s2/testing.mdx | 18 ++-- 38 files changed, 1345 insertions(+), 1146 deletions(-) create mode 100644 packages/dev/s2-docs/pages/react-aria/CheckboxGroup/testing.mdx create mode 100644 packages/dev/s2-docs/pages/react-aria/ComboBox/testing.mdx create mode 100644 packages/dev/s2-docs/pages/react-aria/GridList/testing.mdx create mode 100644 packages/dev/s2-docs/pages/react-aria/ListBox/testing.mdx create mode 100644 packages/dev/s2-docs/pages/react-aria/Menu/testing.mdx create mode 100644 packages/dev/s2-docs/pages/react-aria/RadioGroup/testing.mdx create mode 100644 packages/dev/s2-docs/pages/react-aria/Select/testing.mdx create mode 100644 packages/dev/s2-docs/pages/react-aria/Tabs/testing.mdx create mode 100644 packages/dev/s2-docs/pages/react-aria/Tree/testing.mdx create mode 100644 packages/dev/s2-docs/pages/s2/CheckboxGroup/testing.mdx create mode 100644 packages/dev/s2-docs/pages/s2/ComboBox/testing.mdx create mode 100644 packages/dev/s2-docs/pages/s2/Dialog/testing.mdx create mode 100644 packages/dev/s2-docs/pages/s2/Menu/testing.mdx create mode 100644 packages/dev/s2-docs/pages/s2/Picker/testing.mdx create mode 100644 packages/dev/s2-docs/pages/s2/RadioGroup/testing.mdx create mode 100644 packages/dev/s2-docs/pages/s2/TableView/testing.mdx create mode 100644 packages/dev/s2-docs/pages/s2/Tabs/testing.mdx create mode 100644 packages/dev/s2-docs/pages/s2/TreeView/testing.mdx diff --git a/packages/dev/s2-docs/pages/react-aria/CheckboxGroup.mdx b/packages/dev/s2-docs/pages/react-aria/CheckboxGroup.mdx index baf2656c7c1..2c6d39d70c0 100644 --- a/packages/dev/s2-docs/pages/react-aria/CheckboxGroup.mdx +++ b/packages/dev/s2-docs/pages/react-aria/CheckboxGroup.mdx @@ -5,12 +5,12 @@ import docs from 'docs:react-aria-components'; import vanillaDocs from 'docs:vanilla-starter/CheckboxGroup'; import '../../tailwind/tailwind.css'; import Anatomy from '@react-aria/checkbox/docs/checkboxgroup-anatomy.svg'; -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['input']; +export const relatedPages = [ + {title: 'Testing', url: './CheckboxGroup/testing.html'} +]; # CheckboxGroup @@ -124,58 +124,3 @@ import {Form} from 'vanilla-starter/Form';; ### CheckboxGroup - -## Testing - -### Test utils - -`@react-aria/test-utils` offers common checkbox group interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `CheckboxGroup` tester in your test cases. This gives you access to `CheckboxGroup` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `CheckboxGroup` tester, use it to simulate checkbox selection, and verify the checkbox group's state after each interaction. - -```ts -// CheckboxGroup.test.ts -import {render} from '@testing-library/react'; -import {User} from '@react-aria/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); -// ... - -it('CheckboxGroup can select multiple checkboxes', async function () { - // Render your test component/app and initialize the checkbox group tester - let {getByTestId} = render( - - ... - - ); - let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByTestId('test-checkboxgroup')}); - expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(0); - - await checkboxGroupTester.toggleCheckbox({checkbox: 0}); - expect(checkboxGroupTester.checkboxes[0]).toBeChecked(); - expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1); - - await checkboxGroupTester.toggleCheckbox({checkbox: 4}); - expect(checkboxGroupTester.checkboxes[4]).toBeChecked(); - expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2); -}); -``` - -See below for the full definition of the `User` and the `CheckboxGroup` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/react-aria/CheckboxGroup/testing.mdx b/packages/dev/s2-docs/pages/react-aria/CheckboxGroup/testing.mdx new file mode 100644 index 00000000000..d57c2b9d969 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/CheckboxGroup/testing.mdx @@ -0,0 +1,66 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'checkboxgroup', 'test-utils']; +export const description = 'Testing CheckboxGroup with React Aria test utils'; + +# Testing CheckboxGroup + +## Test utils + +`@react-aria/test-utils` offers common checkbox group interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `CheckboxGroup` tester in your test cases. This gives you access to `CheckboxGroup` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `CheckboxGroup` tester, use it to simulate checkbox selection, and verify the checkbox group's state after each interaction. + +```ts +// CheckboxGroup.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('CheckboxGroup can select multiple checkboxes', async function () { + // Render your test component/app and initialize the checkbox group tester + let {getByTestId} = render( + + ... + + ); + let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByTestId('test-checkboxgroup')}); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(0); + + await checkboxGroupTester.toggleCheckbox({checkbox: 0}); + expect(checkboxGroupTester.checkboxes[0]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1); + + await checkboxGroupTester.toggleCheckbox({checkbox: 4}); + expect(checkboxGroupTester.checkboxes[4]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2); +}); +``` + +See below for the full definition of the `User` and the `CheckboxGroup` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/ComboBox.mdx b/packages/dev/s2-docs/pages/react-aria/ComboBox.mdx index a1bca66bed4..6fabb1fa95f 100644 --- a/packages/dev/s2-docs/pages/react-aria/ComboBox.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ComboBox.mdx @@ -6,12 +6,12 @@ import {ComboBox as VanillaComboBox, ComboBoxItem} from 'vanilla-starter/ComboBo import vanillaDocs from 'docs:vanilla-starter/ComboBox'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/ComboBoxAnatomy.svg'; -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['autocomplete', 'search', 'typeahead', 'input']; +export const relatedPages = [ + {title: 'Testing', url: './ComboBox/testing.html'} +]; # ComboBox @@ -357,57 +357,3 @@ import {ComboBox, ComboBoxItem} from 'vanilla-starter/ComboBox'; ### ComboBox - -## Testing - -### Test utils - -`@react-aria/test-utils` offers common combobox interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `ComboBox` tester in your test cases. This gives you access to `ComboBox` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `ComboBox` tester, use it to simulate option selection, and verify the combobox's state after each interaction. - -```ts -// Combobox.test.ts -import {render} from '@testing-library/react'; -import {User} from '@react-aria/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse'}); -// ... - -it('ComboBox can select an option via keyboard', async function () { - // Render your test component/app and initialize the combobox tester - let {getByTestId} = render( - - ... - - ); - let comboboxTester = testUtilUser.createTester('ComboBox', {root: getByTestId('test-combobox'), interactionType: 'keyboard'}); - - await comboboxTester.open(); - expect(comboboxTester.listbox).toBeInTheDocument(); - - let options = comboboxTester.options(); - await comboboxTester.selectOption({option: options[0]}); - expect(comboboxTester.combobox.value).toBe('One'); - expect(comboboxTester.listbox).not.toBeInTheDocument(); -}); -``` - -See below for the full definition of the `User` and the `ComboBox` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/react-aria/ComboBox/testing.mdx b/packages/dev/s2-docs/pages/react-aria/ComboBox/testing.mdx new file mode 100644 index 00000000000..ded69a0c50a --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/ComboBox/testing.mdx @@ -0,0 +1,65 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'combobox', 'test-utils']; +export const description = 'Testing ComboBox with React Aria test utils'; + +# Testing ComboBox + +## Test utils + +`@react-aria/test-utils` offers common combobox interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `ComboBox` tester in your test cases. This gives you access to `ComboBox` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `ComboBox` tester, use it to simulate option selection, and verify the combobox's state after each interaction. + +```ts +// Combobox.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('ComboBox can select an option via keyboard', async function () { + // Render your test component/app and initialize the combobox tester + let {getByTestId} = render( + + ... + + ); + let comboboxTester = testUtilUser.createTester('ComboBox', {root: getByTestId('test-combobox'), interactionType: 'keyboard'}); + + await comboboxTester.open(); + expect(comboboxTester.listbox).toBeInTheDocument(); + + let options = comboboxTester.options(); + await comboboxTester.selectOption({option: options[0]}); + expect(comboboxTester.combobox.value).toBe('One'); + expect(comboboxTester.listbox).not.toBeInTheDocument(); +}); +``` + +See below for the full definition of the `User` and the `ComboBox` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/GridList.mdx b/packages/dev/s2-docs/pages/react-aria/GridList.mdx index 9bed73d59f9..46b04a7bb9a 100644 --- a/packages/dev/s2-docs/pages/react-aria/GridList.mdx +++ b/packages/dev/s2-docs/pages/react-aria/GridList.mdx @@ -6,12 +6,12 @@ import {GridList as VanillaGridList, GridListItem} from 'vanilla-starter/GridLis import vanillaDocs from 'docs:vanilla-starter/GridList'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/GridListAnatomy.svg'; -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['list view']; +export const relatedPages = [ + {title: 'Testing', url: './GridList/testing.html'} +]; # GridList @@ -927,71 +927,3 @@ function Example() { ### GridListLoadMoreItem - -## Testing - -### General setup - -GridList features long press interactions on its items depending on the item actions provided and if the user is interacting with the gridlist on -a touch device. Please see the following sections in the general testing documentation for more information on how to handle these -behaviors in your test suite. - -[Timers](testing.html#timers) - -[Long press](testing.html#simulating-user-long-press) - -### Test utils - -`@react-aria/test-utils` offers common gridlist interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `GridList` tester in your test cases. This gives you access to `GridList` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `GridList` tester, use it to simulate item selection, and verify the gridlist's state after each interaction. - -```ts -// GridList.test.ts -import {render, within} from '@testing-library/react'; -import {User} from '@react-aria/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse'}); -// ... - -it('GridList can select a row via keyboard', async function () { - // Render your test component/app and initialize the gridlist tester - let {getByTestId} = render( - - ... - - ); - let gridListTester = testUtilUser.createTester('GridList', {root: getByTestId('test-gridlist'), interactionType: 'keyboard'}); - - let row = gridListTester.rows[0]; - expect(within(row).getByRole('checkbox')).not.toBeChecked(); - expect(gridListTester.selectedRows).toHaveLength(0); - - await gridListTester.toggleRowSelection({row: 0}); - expect(within(row).getByRole('checkbox')).toBeChecked(); - expect(gridListTester.selectedRows).toHaveLength(1); - - await gridListTester.toggleRowSelection({row: 0}); - expect(within(row).getByRole('checkbox')).not.toBeChecked(); - expect(gridListTester.selectedRows).toHaveLength(0); -}); -``` - -See below for the full definition of the `User` and the `GridList` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/react-aria/GridList/testing.mdx b/packages/dev/s2-docs/pages/react-aria/GridList/testing.mdx new file mode 100644 index 00000000000..c50f4e75c3a --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/GridList/testing.mdx @@ -0,0 +1,79 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'gridlist', 'test-utils']; +export const description = 'Testing GridList with React Aria test utils'; + +# Testing GridList + +## General setup + +GridList features long press interactions on its items depending on the item actions provided and if the user is interacting with the gridlist on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](../testing.html#timers) + +[Long press](../testing.html#simulating-user-long-press) + +## Test utils + +`@react-aria/test-utils` offers common gridlist interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `GridList` tester in your test cases. This gives you access to `GridList` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `GridList` tester, use it to simulate item selection, and verify the gridlist's state after each interaction. + +```ts +// GridList.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('GridList can select a row via keyboard', async function () { + // Render your test component/app and initialize the gridlist tester + let {getByTestId} = render( + + ... + + ); + let gridListTester = testUtilUser.createTester('GridList', {root: getByTestId('test-gridlist'), interactionType: 'keyboard'}); + + let row = gridListTester.rows[0]; + expect(within(row).getByRole('checkbox')).not.toBeChecked(); + expect(gridListTester.selectedRows).toHaveLength(0); + + await gridListTester.toggleRowSelection({row: 0}); + expect(within(row).getByRole('checkbox')).toBeChecked(); + expect(gridListTester.selectedRows).toHaveLength(1); + + await gridListTester.toggleRowSelection({row: 0}); + expect(within(row).getByRole('checkbox')).not.toBeChecked(); + expect(gridListTester.selectedRows).toHaveLength(0); +}); +``` + +See below for the full definition of the `User` and the `GridList` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/ListBox.mdx b/packages/dev/s2-docs/pages/react-aria/ListBox.mdx index 36abf9516fd..5c8cc4e3109 100644 --- a/packages/dev/s2-docs/pages/react-aria/ListBox.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ListBox.mdx @@ -7,11 +7,11 @@ import vanillaDocs from 'docs:vanilla-starter/ListBox'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/ListBoxAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['options']; +export const relatedPages = [ + {title: 'Testing', url: './ListBox/testing.html'} +]; # ListBox @@ -418,62 +418,3 @@ function Example() { ### ListBoxLoadMoreItem - -## Testing - -### General setup - -ListBox features long press interactions on its options depending on the option actions provided and if the user is interacting with the listbox on -a touch device. Please see the following sections in the general testing documentation for more information on how to handle these -behaviors in your test suite. - -[Timers](testing.html#timers) - -[Long press](testing.html#simulating-user-long-press) - -### Test utils - -`@react-aria/test-utils` offers common listbox interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `ListBox` tester in your test cases. This gives you access to `ListBox` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `ListBox` tester, use it to simulate option selection, and verify the listbox's state after each interaction. - -```ts -// ListBox.test.ts -import {render} from '@testing-library/react'; -import {User} from '@react-aria/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse'}); -// ... - -it('ListBox can select an option via keyboard', async function () { - // Render your test component/app and initialize the listbox tester - let {getByTestId} = render( - - ... - - ); - let listboxTester = testUtilUser.createTester('ListBox', {root: getByTestId('test-listbox'), interactionType: 'keyboard'}); - - await listboxTester.toggleOptionSelection({option: 4}); - expect(listboxTester.options()[4]).toHaveAttribute('aria-selected', 'true'); -}); -``` - -See below for the full definition of the `User` and the `ListBox` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/react-aria/ListBox/testing.mdx b/packages/dev/s2-docs/pages/react-aria/ListBox/testing.mdx new file mode 100644 index 00000000000..58d6cc72537 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/ListBox/testing.mdx @@ -0,0 +1,70 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'listbox', 'test-utils']; +export const description = 'Testing ListBox with React Aria test utils'; + +# Testing ListBox + +## General setup + +ListBox features long press interactions on its options depending on the option actions provided and if the user is interacting with the listbox on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](../testing.html#timers) + +[Long press](../testing.html#simulating-user-long-press) + +## Test utils + +`@react-aria/test-utils` offers common listbox interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `ListBox` tester in your test cases. This gives you access to `ListBox` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `ListBox` tester, use it to simulate option selection, and verify the listbox's state after each interaction. + +```ts +// ListBox.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('ListBox can select an option via keyboard', async function () { + // Render your test component/app and initialize the listbox tester + let {getByTestId} = render( + + ... + + ); + let listboxTester = testUtilUser.createTester('ListBox', {root: getByTestId('test-listbox'), interactionType: 'keyboard'}); + + await listboxTester.toggleOptionSelection({option: 4}); + expect(listboxTester.options()[4]).toHaveAttribute('aria-selected', 'true'); +}); +``` + +See below for the full definition of the `User` and the `ListBox` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/Menu.mdx b/packages/dev/s2-docs/pages/react-aria/Menu.mdx index ad72fb57130..8489a45d7d3 100644 --- a/packages/dev/s2-docs/pages/react-aria/Menu.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Menu.mdx @@ -5,11 +5,11 @@ import docs from 'docs:react-aria-components'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/MenuAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['dropdown']; +export const relatedPages = [ + {title: 'Testing', url: './Menu/testing.html'} +]; # Menu @@ -580,72 +580,3 @@ import {ChevronDown} from 'lucide-react'; ### SubmenuTrigger - -## Testing - -### General setup - -Menu features long press interactions depending on the actions provided and if the user is interacting with the menu on -a touch device. Please see the following sections in the general testing documentation for more information on how to handle these -behaviors in your test suite. - -[Timers](testing.html#timers) - -[Long press](testing.html#simulating-user-long-press) - -### Test utils - -`@react-aria/test-utils` offers common menu interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `Menu` tester in your test cases. This gives you access to `Menu` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `Menu` tester, use it to find and open a submenu, and verify the menu's state after each interaction. - -```ts -// Menu.test.ts -import {render} from '@testing-library/react'; -import {User} from '@react-aria/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse'}); -// ... - -it('Menu can open its submenu via keyboard', async function () { - // Render your test component/app and initialize the menu tester - let {getByTestId} = render( - - - ... - - ); - let menuTester = testUtilUser.createTester('Menu', {root: getByTestId('test-menutrigger'), interactionType: 'keyboard'}); - - await menuTester.open(); - expect(menuTester.menu).toBeInTheDocument(); - let submenuTriggers = menuTester.submenuTriggers; - expect(submenuTriggers).toHaveLength(1); - - let submenuTester = await menuTester.openSubmenu({submenuTrigger: 'Share…'}); - expect(submenuTester.menu).toBeInTheDocument(); - - await submenuTester.selectOption({option: submenuTester.options()[0]}); - expect(submenuTester.menu).not.toBeInTheDocument(); - expect(menuTester.menu).not.toBeInTheDocument(); -}); -``` - -See below for the full definition of the `User` and the `Menu` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/react-aria/Menu/testing.mdx b/packages/dev/s2-docs/pages/react-aria/Menu/testing.mdx new file mode 100644 index 00000000000..f9339cda983 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Menu/testing.mdx @@ -0,0 +1,80 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'menu', 'test-utils']; +export const description = 'Testing Menu with React Aria test utils'; + +# Testing Menu + +## General setup + +Menu features long press interactions depending on the actions provided and if the user is interacting with the menu on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](../testing.html#timers) + +[Long press](../testing.html#simulating-user-long-press) + +## Test utils + +`@react-aria/test-utils` offers common menu interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Menu` tester in your test cases. This gives you access to `Menu` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Menu` tester, use it to find and open a submenu, and verify the menu's state after each interaction. + +```ts +// Menu.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Menu can open its submenu via keyboard', async function () { + // Render your test component/app and initialize the menu tester + let {getByTestId} = render( + + + ... + + ); + let menuTester = testUtilUser.createTester('Menu', {root: getByTestId('test-menutrigger'), interactionType: 'keyboard'}); + + await menuTester.open(); + expect(menuTester.menu).toBeInTheDocument(); + let submenuTriggers = menuTester.submenuTriggers; + expect(submenuTriggers).toHaveLength(1); + + let submenuTester = await menuTester.openSubmenu({submenuTrigger: 'Share…'}); + expect(submenuTester.menu).toBeInTheDocument(); + + await submenuTester.selectOption({option: submenuTester.options()[0]}); + expect(submenuTester.menu).not.toBeInTheDocument(); + expect(menuTester.menu).not.toBeInTheDocument(); +}); +``` + +See below for the full definition of the `User` and the `Menu` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/RadioGroup.mdx b/packages/dev/s2-docs/pages/react-aria/RadioGroup.mdx index d1720ae47a8..2750f2cebe3 100644 --- a/packages/dev/s2-docs/pages/react-aria/RadioGroup.mdx +++ b/packages/dev/s2-docs/pages/react-aria/RadioGroup.mdx @@ -6,11 +6,11 @@ import vanillaDocs from 'docs:vanilla-starter/RadioGroup'; import '../../tailwind/tailwind.css'; import Anatomy from '@react-aria/radio/docs/anatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['input']; +export const relatedPages = [ + {title: 'Testing', url: './RadioGroup/testing.html'} +]; # RadioGroup @@ -117,58 +117,3 @@ import {Form} from 'vanilla-starter/Form';; ### Radio - -## Testing - -### Test utils - -`@react-aria/test-utils` offers common radio group interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `RadioGroup` tester in your test cases. This gives you access to `RadioGroup` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `RadioGroup` tester, use it to change radio selection, and verify the radio group's state after each interaction. - -```ts -// RadioGroup.test.ts -import {render} from '@testing-library/react'; -import {User} from '@react-aria/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); -// ... - -it('RadioGroup can switch the selected radio', async function () { - // Render your test component/app and initialize the radiogroup tester - let {getByRole} = render( - - ... - - ); - - let radioGroupTester = testUtilUser.createTester('RadioGroup', {root: getByRole('radiogroup')}); - let radios = radioGroupTester.radios; - expect(radioGroupTester.selectedRadio).toBeFalsy(); - - await radioGroupTester.triggerRadio({radio: radios[0]}); - expect(radioGroupTester.selectedRadio).toBe(radios[0]); - - await radioGroupTester.triggerRadio({radio: radios[1]}); - expect(radioGroupTester.selectedRadio).toBe(radios[1]); -}); -``` - -See below for the full definition of the `User` and the `RadioGroup` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/react-aria/RadioGroup/testing.mdx b/packages/dev/s2-docs/pages/react-aria/RadioGroup/testing.mdx new file mode 100644 index 00000000000..8b172a7c815 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/RadioGroup/testing.mdx @@ -0,0 +1,66 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'radiogroup', 'test-utils']; +export const description = 'Testing RadioGroup with React Aria test utils'; + +# Testing RadioGroup + +## Test utils + +`@react-aria/test-utils` offers common radio group interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `RadioGroup` tester in your test cases. This gives you access to `RadioGroup` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `RadioGroup` tester, use it to change radio selection, and verify the radio group's state after each interaction. + +```ts +// RadioGroup.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('RadioGroup can switch the selected radio', async function () { + // Render your test component/app and initialize the radiogroup tester + let {getByRole} = render( + + ... + + ); + + let radioGroupTester = testUtilUser.createTester('RadioGroup', {root: getByRole('radiogroup')}); + let radios = radioGroupTester.radios; + expect(radioGroupTester.selectedRadio).toBeFalsy(); + + await radioGroupTester.triggerRadio({radio: radios[0]}); + expect(radioGroupTester.selectedRadio).toBe(radios[0]); + + await radioGroupTester.triggerRadio({radio: radios[1]}); + expect(radioGroupTester.selectedRadio).toBe(radios[1]); +}); +``` + +See below for the full definition of the `User` and the `RadioGroup` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/Select.mdx b/packages/dev/s2-docs/pages/react-aria/Select.mdx index ee1565114d6..d784307b475 100644 --- a/packages/dev/s2-docs/pages/react-aria/Select.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Select.mdx @@ -6,11 +6,11 @@ import vanillaDocs from 'docs:vanilla-starter/Select'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/SelectAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['picker', 'dropdown', 'menu', 'input']; +export const relatedPages = [ + {title: 'Testing', url: './Select/testing.html'} +]; # Select @@ -354,54 +354,3 @@ import {Form} from 'vanilla-starter/Form'; ### SelectValue - -## Testing - -### Test utils - -`@react-aria/test-utils` offers common select interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `Select` tester in your test cases. This gives you access to `Select` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `Select` tester, use it to simulate option selection, and verify the select's state after each interaction. - -```ts -// Select.test.ts -import {render} from '@testing-library/react'; -import {User} from '@react-aria/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse'}); -// ... - -it('Select can select an option via keyboard', async function () { - // Render your test component/app and initialize the select tester - let {getByTestId} = render( - - ); - let selectTester = testUtilUser.createTester('Select', {root: getByTestId('test-select'), interactionType: 'keyboard'}); - let trigger = selectTester.trigger; - expect(trigger).toHaveTextContent('Select an item'); - - await selectTester.selectOption({option: 'Cat'}); - expect(trigger).toHaveTextContent('Cat'); -}); -``` - -See below for the full definition of the `User` and the `Select` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/react-aria/Select/testing.mdx b/packages/dev/s2-docs/pages/react-aria/Select/testing.mdx new file mode 100644 index 00000000000..1a3fa450416 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Select/testing.mdx @@ -0,0 +1,62 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'select', 'test-utils']; +export const description = 'Testing Select with React Aria test utils'; + +# Testing Select + +## Test utils + +`@react-aria/test-utils` offers common select interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Select` tester in your test cases. This gives you access to `Select` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Select` tester, use it to simulate option selection, and verify the select's state after each interaction. + +```ts +// Select.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Select can select an option via keyboard', async function () { + // Render your test component/app and initialize the select tester + let {getByTestId} = render( + + ); + let selectTester = testUtilUser.createTester('Select', {root: getByTestId('test-select'), interactionType: 'keyboard'}); + let trigger = selectTester.trigger; + expect(trigger).toHaveTextContent('Select an item'); + + await selectTester.selectOption({option: 'Cat'}); + expect(trigger).toHaveTextContent('Cat'); +}); +``` + +See below for the full definition of the `User` and the `Select` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/Tabs.mdx b/packages/dev/s2-docs/pages/react-aria/Tabs.mdx index 6b1e421bdb4..1e7bd64c08e 100644 --- a/packages/dev/s2-docs/pages/react-aria/Tabs.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Tabs.mdx @@ -7,11 +7,11 @@ import vanillaDocs from 'docs:vanilla-starter/Tabs'; import '../../tailwind/tailwind.css'; import Anatomy from '@react-aria/tabs/docs/anatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['navigation']; +export const relatedPages = [ + {title: 'Testing', url: './Tabs/testing.html'} +]; # Tabs @@ -320,55 +320,3 @@ function Example() { ### TabPanel - -## Testing - -### Test utils - -`@react-aria/test-utils` offers common tabs interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `Tabs` tester in your test cases. This gives you access to `Tabs` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `Tabs` tester, use it to change tab selection, and verify the tabs' state after each interaction. - -```ts -// Tabs.test.ts -import {render} from '@testing-library/react'; -import {User} from '@react-aria/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse'}); -// ... - -it('Tabs can change selection via keyboard', async function () { - // Render your test component/app and initialize the listbox tester - let {getByTestId} = render( - - ... - - ); - let tabsTester = testUtilUser.createTester('Tabs', {root: getByTestId('test-tabs'), interactionType: 'keyboard'}); - - let tabs = tabsTester.tabs; - expect(tabsTester.selectedTab).toBe(tabs[0]); - - await tabsTester.triggerTab({tab: 1}); - expect(tabsTester.selectedTab).toBe(tabs[1]); -}); -``` - -See below for the full definition of the `User` and the `Tabs` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/react-aria/Tabs/testing.mdx b/packages/dev/s2-docs/pages/react-aria/Tabs/testing.mdx new file mode 100644 index 00000000000..1f48351eda5 --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Tabs/testing.mdx @@ -0,0 +1,63 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'tabs', 'test-utils']; +export const description = 'Testing Tabs with React Aria test utils'; + +# Testing Tabs + +## Test utils + +`@react-aria/test-utils` offers common tabs interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Tabs` tester in your test cases. This gives you access to `Tabs` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Tabs` tester, use it to change tab selection, and verify the tabs' state after each interaction. + +```ts +// Tabs.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Tabs can change selection via keyboard', async function () { + // Render your test component/app and initialize the listbox tester + let {getByTestId} = render( + + ... + + ); + let tabsTester = testUtilUser.createTester('Tabs', {root: getByTestId('test-tabs'), interactionType: 'keyboard'}); + + let tabs = tabsTester.tabs; + expect(tabsTester.selectedTab).toBe(tabs[0]); + + await tabsTester.triggerTab({tab: 1}); + expect(tabsTester.selectedTab).toBe(tabs[1]); +}); +``` + +See below for the full definition of the `User` and the `Tabs` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/Tree.mdx b/packages/dev/s2-docs/pages/react-aria/Tree.mdx index 1dbc0093516..8e23f026743 100644 --- a/packages/dev/s2-docs/pages/react-aria/Tree.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Tree.mdx @@ -6,12 +6,12 @@ import {Tree as VanillaTree, TreeItem} from 'vanilla-starter/Tree'; import vanillaDocs from 'docs:vanilla-starter/Tree'; import '../../tailwind/tailwind.css'; import Anatomy from 'react-aria-components/docs/TreeAnatomy.svg'; -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['data', 'tree', 'nested', 'hierarchy']; +export const relatedPages = [ + {title: 'Testing', url: './Tree/testing.html'} +]; # Tree @@ -407,74 +407,3 @@ function Example() { cssVariables={{ '--tree-item-level': "The depth of the item within the tree. Useful to calculate indentation." }} /> - -## Testing - -### General setup - -Tree features long press interactions on its rows depending on the row actions provided and if the user is interacting with the tree on -a touch device. Please see the following sections in the general testing documentation for more information on how to handle these -behaviors in your test suite. - -[Timers](testing.html#timers) - -[Long press](testing.html#simulating-user-long-press) - -### Test utils - -`@react-aria/test-utils` offers common tree interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `Tree` tester in your test cases. This gives you access to `Tree` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `Tree` tester, use it to simulate row expansion and selection, and verify the tree's state after each interaction. - -```ts -// Tree.test.ts -import {render, within} from '@testing-library/react'; -import {User} from '@react-aria/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse'}); -// ... - -it('Tree can select and expand a item via keyboard', async function () { - // Render your test component/app and initialize the Tree tester - let {getByTestId} = render( - - ... - - ); - let treeTester = testUtilUser.createTester('Tree', {root: getByTestId('test-tree'), interactionType: 'keyboard'}); - - await treeTester.toggleRowSelection({row: 0}); - expect(treeTester.selectedRows).toHaveLength(1); - expect(within(treeTester.rows[0]).getByRole('checkbox')).toBeChecked(); - - await treeTester.toggleRowSelection({row: 1}); - expect(treeTester.selectedRows).toHaveLength(2); - expect(within(treeTester.rows[1]).getByRole('checkbox')).toBeChecked(); - - await treeTester.toggleRowSelection({row: 0}); - expect(treeTester.selectedRows).toHaveLength(1); - expect(within(treeTester.rows[0]).getByRole('checkbox')).not.toBeChecked(); - - await treeTester.toggleRowExpansion({index: 0}); - expect(treeTester.rows[0]).toHaveAttribute('aria-expanded', 'true'); -}); -``` - -See below for the full definition of the `User` and the `Tree` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/react-aria/Tree/testing.mdx b/packages/dev/s2-docs/pages/react-aria/Tree/testing.mdx new file mode 100644 index 00000000000..7383f558bdf --- /dev/null +++ b/packages/dev/s2-docs/pages/react-aria/Tree/testing.mdx @@ -0,0 +1,82 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-aria/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'tree', 'test-utils']; +export const description = 'Testing Tree with React Aria test utils'; + +# Testing Tree + +## General setup + +Tree features long press interactions on its rows depending on the row actions provided and if the user is interacting with the tree on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](../testing.html#timers) + +[Long press](../testing.html#simulating-user-long-press) + +## Test utils + +`@react-aria/test-utils` offers common tree interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-aria/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Tree` tester in your test cases. This gives you access to `Tree` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Tree` tester, use it to simulate row expansion and selection, and verify the tree's state after each interaction. + +```ts +// Tree.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-aria/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Tree can select and expand a item via keyboard', async function () { + // Render your test component/app and initialize the Tree tester + let {getByTestId} = render( + + ... + + ); + let treeTester = testUtilUser.createTester('Tree', {root: getByTestId('test-tree'), interactionType: 'keyboard'}); + + await treeTester.toggleRowSelection({row: 0}); + expect(treeTester.selectedRows).toHaveLength(1); + expect(within(treeTester.rows[0]).getByRole('checkbox')).toBeChecked(); + + await treeTester.toggleRowSelection({row: 1}); + expect(treeTester.selectedRows).toHaveLength(2); + expect(within(treeTester.rows[1]).getByRole('checkbox')).toBeChecked(); + + await treeTester.toggleRowSelection({row: 0}); + expect(treeTester.selectedRows).toHaveLength(1); + expect(within(treeTester.rows[0]).getByRole('checkbox')).not.toBeChecked(); + + await treeTester.toggleRowExpansion({index: 0}); + expect(treeTester.rows[0]).toHaveAttribute('aria-expanded', 'true'); +}); +``` + +See below for the full definition of the `User` and the `Tree` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/react-aria/testing.mdx b/packages/dev/s2-docs/pages/react-aria/testing.mdx index 52c8a580ff0..e128d757363 100644 --- a/packages/dev/s2-docs/pages/react-aria/testing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/testing.mdx @@ -232,13 +232,13 @@ See below for the full definition of the `User` object. Below is a list of the ARIA patterns testers currently supported by `createTester`. See the accompanying component testing docs pages for a sample of how to use the testers in your test suite. -- [CheckboxGroup](./CheckboxGroup.html#testing) -- [ComboBox](./ComboBox.html#testing) -- [GridList](./GridList.html#testing) -- [ListBox](./ListBox.html#testing) -- [Menu](./Menu.html#testing) -- [RadioGroup](./RadioGroup.html#testing) -- [Select](./Select.html#testing) -- [Table](./Table.html#testing) -- [Tabs](./Tabs.html#testing) -- [Tree](./Tree.html#testing) +- [CheckboxGroup](./CheckboxGroup/testing.html) +- [ComboBox](./ComboBox/testing.html) +- [GridList](./GridList/testing.html) +- [ListBox](./ListBox/testing.html) +- [Menu](./Menu/testing.html) +- [RadioGroup](./RadioGroup/testing.html) +- [Select](./Select/testing.html) +- [Table](./Table/testing.html) +- [Tabs](./Tabs/testing.html) +- [Tree](./Tree/testing.html) diff --git a/packages/dev/s2-docs/pages/s2/CheckboxGroup.mdx b/packages/dev/s2-docs/pages/s2/CheckboxGroup.mdx index 28e89f82ed2..a680f8ce2ab 100644 --- a/packages/dev/s2-docs/pages/s2/CheckboxGroup.mdx +++ b/packages/dev/s2-docs/pages/s2/CheckboxGroup.mdx @@ -3,11 +3,11 @@ export default Layout; import {CheckboxGroup, InlineAlert, Heading, Content} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = []; +export const relatedPages = [ + {title: 'Testing', url: './CheckboxGroup/testing.html'} +]; # CheckboxGroup @@ -96,58 +96,3 @@ import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; ### CheckboxGroup - -## Testing - -### Test utils - -`@react-spectrum/test-utils` offers common checkbox group interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `CheckboxGroup` tester in your test cases. This gives you access to `CheckboxGroup` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `CheckboxGroup` tester, use it to simulate checkbox selection, and verify the checkbox group's state after each interaction. - -```ts -// CheckboxGroup.test.ts -import {render} from '@testing-library/react'; -import {User} from '@react-spectrum/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); -// ... - -it('CheckboxGroup can select multiple checkboxes', async function () { - // Render your test component/app and initialize the checkbox group tester - let {getByTestId} = render( - - ... - - ); - let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByTestId('test-checkboxgroup')}); - expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(0); - - await checkboxGroupTester.toggleCheckbox({checkbox: 0}); - expect(checkboxGroupTester.checkboxes[0]).toBeChecked(); - expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1); - - await checkboxGroupTester.toggleCheckbox({checkbox: 4}); - expect(checkboxGroupTester.checkboxes[4]).toBeChecked(); - expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2); -}); -``` - -See below for the full definition of the `User` and the `CheckboxGroup` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/s2/CheckboxGroup/testing.mdx b/packages/dev/s2-docs/pages/s2/CheckboxGroup/testing.mdx new file mode 100644 index 00000000000..834690ffe4b --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/CheckboxGroup/testing.mdx @@ -0,0 +1,66 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'checkboxgroup', 'test-utils']; +export const description = 'Testing CheckboxGroup with React Spectrum test utils'; + +# Testing CheckboxGroup + +## Test utils + +`@react-spectrum/test-utils` offers common checkbox group interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `CheckboxGroup` tester in your test cases. This gives you access to `CheckboxGroup` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `CheckboxGroup` tester, use it to simulate checkbox selection, and verify the checkbox group's state after each interaction. + +```ts +// CheckboxGroup.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('CheckboxGroup can select multiple checkboxes', async function () { + // Render your test component/app and initialize the checkbox group tester + let {getByTestId} = render( + + ... + + ); + let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByTestId('test-checkboxgroup')}); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(0); + + await checkboxGroupTester.toggleCheckbox({checkbox: 0}); + expect(checkboxGroupTester.checkboxes[0]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1); + + await checkboxGroupTester.toggleCheckbox({checkbox: 4}); + expect(checkboxGroupTester.checkboxes[4]).toBeChecked(); + expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2); +}); +``` + +See below for the full definition of the `User` and the `CheckboxGroup` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/ComboBox.mdx b/packages/dev/s2-docs/pages/s2/ComboBox.mdx index 1719da2b8c6..54a9e2e47e4 100644 --- a/packages/dev/s2-docs/pages/s2/ComboBox.mdx +++ b/packages/dev/s2-docs/pages/s2/ComboBox.mdx @@ -3,11 +3,11 @@ export default Layout; import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['autocomplete', 'search', 'typeahead']; +export const relatedPages = [ + {title: 'Testing', url: './ComboBox/testing.html'} +]; # ComboBox @@ -470,57 +470,3 @@ import {ComboBox, ComboBoxItem} from '@react-spectrum/s2'; ### ComboBoxSection - -## Testing - -### Test utils - -`@react-spectrum/test-utils` offers common combobox interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `ComboBox` tester in your test cases. This gives you access to `ComboBox` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `ComboBox` tester, use it to simulate option selection, and verify the combobox's state after each interaction. - -```ts -// Combobox.test.ts -import {render} from '@testing-library/react'; -import {User} from '@react-spectrum/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse'}); -// ... - -it('ComboBox can select an option via keyboard', async function () { - // Render your test component/app and initialize the combobox tester - let {getByTestId} = render( - - ... - - ); - let comboboxTester = testUtilUser.createTester('ComboBox', {root: getByTestId('test-combobox'), interactionType: 'keyboard'}); - - await comboboxTester.open(); - expect(comboboxTester.listbox).toBeInTheDocument(); - - let options = comboboxTester.options(); - await comboboxTester.selectOption({option: options[0]}); - expect(comboboxTester.combobox.value).toBe('One'); - expect(comboboxTester.listbox).not.toBeInTheDocument(); -}); -``` - -See below for the full definition of the `User` and the `ComboBox` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/s2/ComboBox/testing.mdx b/packages/dev/s2-docs/pages/s2/ComboBox/testing.mdx new file mode 100644 index 00000000000..4899a904938 --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/ComboBox/testing.mdx @@ -0,0 +1,65 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'combobox', 'test-utils']; +export const description = 'Testing ComboBox with React Spectrum test utils'; + +# Testing ComboBox + +## Test utils + +`@react-spectrum/test-utils` offers common combobox interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `ComboBox` tester in your test cases. This gives you access to `ComboBox` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `ComboBox` tester, use it to simulate option selection, and verify the combobox's state after each interaction. + +```ts +// Combobox.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('ComboBox can select an option via keyboard', async function () { + // Render your test component/app and initialize the combobox tester + let {getByTestId} = render( + + ... + + ); + let comboboxTester = testUtilUser.createTester('ComboBox', {root: getByTestId('test-combobox'), interactionType: 'keyboard'}); + + await comboboxTester.open(); + expect(comboboxTester.listbox).toBeInTheDocument(); + + let options = comboboxTester.options(); + await comboboxTester.selectOption({option: options[0]}); + expect(comboboxTester.combobox.value).toBe('One'); + expect(comboboxTester.listbox).not.toBeInTheDocument(); +}); +``` + +See below for the full definition of the `User` and the `ComboBox` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/Dialog.mdx b/packages/dev/s2-docs/pages/s2/Dialog.mdx index 5fca00ec740..6a3f3af9e92 100644 --- a/packages/dev/s2-docs/pages/s2/Dialog.mdx +++ b/packages/dev/s2-docs/pages/s2/Dialog.mdx @@ -3,11 +3,11 @@ export default Layout; import {Dialog, FullscreenDialog, CustomDialog, DialogTrigger, DialogContainer, Button, ButtonGroup, Heading, Header, Content, Footer, Image, TextField, Checkbox, CloseButton, ActionButton, IllustratedMessage, InlineAlert} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['modal', 'popup', 'overlay']; +export const relatedPages = [ + {title: 'Testing', url: './Dialog/testing.html'} +]; # Dialog @@ -313,58 +313,3 @@ function DialogContainerExample() { ``` - -## Testing - -### Test utils - -`@react-spectrum/test-utils` offers common dialog interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `Dialog` tester in your test cases. This gives you access to `Dialog` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `Dialog` tester, use it to open and close the dialog, and verify the dialog's state after each interaction. - -```ts -// Dialog.test.ts -import {render} from '@testing-library/react'; -import {User} from '@react-spectrum/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); -// ... - -it('Dialog can be opened and closed', async function () { - // Render your test component/app and initialize the dialog tester - let {getByTestId, getByRole} = render( - - Trigger - - ... - - - ); - let button = getByRole('button'); - let dialogTester = testUtilUser.createTester('Dialog', {root: button, overlayType: 'modal'}); - await dialogTester.open(); - let dialog = dialogTester.dialog; - expect(dialog).toBeVisible(); - await dialogTester.close(); - expect(dialog).not.toBeInTheDocument(); -}); -``` - -See below for the full definition of the `User` and the `Dialog` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/s2/Dialog/testing.mdx b/packages/dev/s2-docs/pages/s2/Dialog/testing.mdx new file mode 100644 index 00000000000..8e0927a296a --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/Dialog/testing.mdx @@ -0,0 +1,66 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'dialog', 'test-utils']; +export const description = 'Testing Dialog with React Spectrum test utils'; + +# Testing Dialog + +## Test utils + +`@react-spectrum/test-utils` offers common dialog interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Dialog` tester in your test cases. This gives you access to `Dialog` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Dialog` tester, use it to open and close the dialog, and verify the dialog's state after each interaction. + +```ts +// Dialog.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('Dialog can be opened and closed', async function () { + // Render your test component/app and initialize the dialog tester + let {getByTestId, getByRole} = render( + + Trigger + + ... + + + ); + let button = getByRole('button'); + let dialogTester = testUtilUser.createTester('Dialog', {root: button, overlayType: 'modal'}); + await dialogTester.open(); + let dialog = dialogTester.dialog; + expect(dialog).toBeVisible(); + await dialogTester.close(); + expect(dialog).not.toBeInTheDocument(); +}); +``` + +See below for the full definition of the `User` and the `Dialog` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/Menu.mdx b/packages/dev/s2-docs/pages/s2/Menu.mdx index e0a1c2bd836..b2164057f53 100644 --- a/packages/dev/s2-docs/pages/s2/Menu.mdx +++ b/packages/dev/s2-docs/pages/s2/Menu.mdx @@ -3,11 +3,11 @@ export default Layout; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' import docs from 'docs:@react-spectrum/s2'; -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['dropdown']; +export const relatedPages = [ + {title: 'Testing', url: './Menu/testing.html'} +]; # Menu @@ -521,72 +521,3 @@ import {MenuTrigger, Menu, MenuItem, ActionButton} from '@react-spectrum/s2'; ### SubmenuTrigger - -## Testing - -### General setup - -Menu features long press interactions depending on the actions provided and if the user is interacting with the menu on -a touch device. Please see the following sections in the general testing documentation for more information on how to handle these -behaviors in your test suite. - -[Timers](testing.html#timers) - -[Long press](testing.html#simulating-user-long-press) - -### Test utils - -`@react-spectrum/test-utils` offers common menu interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `Menu` tester in your test cases. This gives you access to `Menu` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `Menu` tester, use it to find and open a submenu, and verify the menu's state after each interaction. - -```ts -// Menu.test.ts -import {render} from '@testing-library/react'; -import {User} from '@react-spectrum/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse'}); -// ... - -it('Menu can open its submenu via keyboard', async function () { - // Render your test component/app and initialize the menu tester - let {getByTestId} = render( - - - ... - - ); - let menuTester = testUtilUser.createTester('Menu', {root: getByTestId('test-menutrigger'), interactionType: 'keyboard'}); - - await menuTester.open(); - expect(menuTester.menu).toBeInTheDocument(); - let submenuTriggers = menuTester.submenuTriggers; - expect(submenuTriggers).toHaveLength(1); - - let submenuTester = await menuTester.openSubmenu({submenuTrigger: 'Share…'}); - expect(submenuTester.menu).toBeInTheDocument(); - - await submenuTester.selectOption({option: submenuTester.options()[0]}); - expect(submenuTester.menu).not.toBeInTheDocument(); - expect(menuTester.menu).not.toBeInTheDocument(); -}); -``` - -See below for the full definition of the `User` and the `Menu` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/s2/Menu/testing.mdx b/packages/dev/s2-docs/pages/s2/Menu/testing.mdx new file mode 100644 index 00000000000..fcbec79a316 --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/Menu/testing.mdx @@ -0,0 +1,80 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'menu', 'test-utils']; +export const description = 'Testing Menu with React Spectrum test utils'; + +# Testing Menu + +## General setup + +Menu features long press interactions depending on the actions provided and if the user is interacting with the menu on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](../testing.html#timers) + +[Long press](../testing.html#simulating-user-long-press) + +## Test utils + +`@react-spectrum/test-utils` offers common menu interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Menu` tester in your test cases. This gives you access to `Menu` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Menu` tester, use it to find and open a submenu, and verify the menu's state after each interaction. + +```ts +// Menu.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Menu can open its submenu via keyboard', async function () { + // Render your test component/app and initialize the menu tester + let {getByTestId} = render( + + + ... + + ); + let menuTester = testUtilUser.createTester('Menu', {root: getByTestId('test-menutrigger'), interactionType: 'keyboard'}); + + await menuTester.open(); + expect(menuTester.menu).toBeInTheDocument(); + let submenuTriggers = menuTester.submenuTriggers; + expect(submenuTriggers).toHaveLength(1); + + let submenuTester = await menuTester.openSubmenu({submenuTrigger: 'Share…'}); + expect(submenuTester.menu).toBeInTheDocument(); + + await submenuTester.selectOption({option: submenuTester.options()[0]}); + expect(submenuTester.menu).not.toBeInTheDocument(); + expect(menuTester.menu).not.toBeInTheDocument(); +}); +``` + +See below for the full definition of the `User` and the `Menu` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/Picker.mdx b/packages/dev/s2-docs/pages/s2/Picker.mdx index dbde971c6bd..4c695a9ce22 100644 --- a/packages/dev/s2-docs/pages/s2/Picker.mdx +++ b/packages/dev/s2-docs/pages/s2/Picker.mdx @@ -3,11 +3,11 @@ export default Layout; import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['select', 'dropdown']; +export const relatedPages = [ + {title: 'Testing', url: './Picker/testing.html'} +]; # Picker @@ -336,54 +336,3 @@ function Example(props) { ### PickerSection - -## Testing - -### Test utils - -`@react-spectrum/test-utils` offers common picker interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `Picker` tester in your test cases. This gives you access to `Picker` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `Picker` tester, use it to simulate option selection, and verify the picker's state after each interaction. - -```ts -// Picker.test.ts -import {render} from '@testing-library/react'; -import {User} from '@react-spectrum/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse'}); -// ... - -it('Picker can select an option via keyboard', async function () { - // Render your test component/app and initialize the select tester - let {getByTestId} = render( - - ... - - ); - let selectTester = testUtilUser.createTester('Select', {root: getByTestId('test-select'), interactionType: 'keyboard'}); - let trigger = selectTester.trigger; - expect(trigger).toHaveTextContent('Select an item'); - - await selectTester.selectOption({option: 'Cat'}); - expect(trigger).toHaveTextContent('Cat'); -}); -``` - -See below for the full definition of the `User` and the `Picker` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/s2/Picker/testing.mdx b/packages/dev/s2-docs/pages/s2/Picker/testing.mdx new file mode 100644 index 00000000000..a98e57797b0 --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/Picker/testing.mdx @@ -0,0 +1,62 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'picker', 'test-utils']; +export const description = 'Testing Picker with React Spectrum test utils'; + +# Testing Picker + +## Test utils + +`@react-spectrum/test-utils` offers common picker interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Picker` tester in your test cases. This gives you access to `Picker` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Picker` tester, use it to simulate option selection, and verify the picker's state after each interaction. + +```ts +// Picker.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Picker can select an option via keyboard', async function () { + // Render your test component/app and initialize the select tester + let {getByTestId} = render( + + ... + + ); + let selectTester = testUtilUser.createTester('Select', {root: getByTestId('test-select'), interactionType: 'keyboard'}); + let trigger = selectTester.trigger; + expect(trigger).toHaveTextContent('Select an item'); + + await selectTester.selectOption({option: 'Cat'}); + expect(trigger).toHaveTextContent('Cat'); +}); +``` + +See below for the full definition of the `User` and the `Picker` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/RadioGroup.mdx b/packages/dev/s2-docs/pages/s2/RadioGroup.mdx index eb11de11e14..dc86d41a0bc 100644 --- a/packages/dev/s2-docs/pages/s2/RadioGroup.mdx +++ b/packages/dev/s2-docs/pages/s2/RadioGroup.mdx @@ -3,11 +3,11 @@ export default Layout; import {RadioGroup, Radio, InlineAlert, Heading, Content} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['input']; +export const relatedPages = [ + {title: 'Testing', url: './RadioGroup/testing.html'} +]; # RadioGroup @@ -97,58 +97,3 @@ function Example(props) { ### Radio - -## Testing - -### Test utils - -`@react-spectrum/test-utils` offers common radio group interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `RadioGroup` tester in your test cases. This gives you access to `RadioGroup` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `RadioGroup` tester, use it to changing radio selection, and verify the radio group's state after each interaction. - -```ts -// RadioGroup.test.ts -import {render} from '@testing-library/react'; -import {User} from '@react-spectrum/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); -// ... - -it('RadioGroup can switch the selected radio', async function () { - // Render your test component/app and initialize the radiogroup tester - let {getByRole} = render( - - ... - - ); - - let radioGroupTester = testUtilUser.createTester('RadioGroup', {root: getByRole('radiogroup')}); - let radios = radioGroupTester.radios; - expect(radioGroupTester.selectedRadio).toBeFalsy(); - - await radioGroupTester.triggerRadio({radio: radios[0]}); - expect(radioGroupTester.selectedRadio).toBe(radios[0]); - - await radioGroupTester.triggerRadio({radio: radios[1]}); - expect(radioGroupTester.selectedRadio).toBe(radios[1]); -}); -``` - -See below for the full definition of the `User` and the `RadioGroup` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/s2/RadioGroup/testing.mdx b/packages/dev/s2-docs/pages/s2/RadioGroup/testing.mdx new file mode 100644 index 00000000000..d68d42cb54a --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/RadioGroup/testing.mdx @@ -0,0 +1,66 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'radiogroup', 'test-utils']; +export const description = 'Testing RadioGroup with React Spectrum test utils'; + +# Testing RadioGroup + +## Test utils + +`@react-spectrum/test-utils` offers common radio group interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `RadioGroup` tester in your test cases. This gives you access to `RadioGroup` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `RadioGroup` tester, use it to changing radio selection, and verify the radio group's state after each interaction. + +```ts +// RadioGroup.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('RadioGroup can switch the selected radio', async function () { + // Render your test component/app and initialize the radiogroup tester + let {getByRole} = render( + + ... + + ); + + let radioGroupTester = testUtilUser.createTester('RadioGroup', {root: getByRole('radiogroup')}); + let radios = radioGroupTester.radios; + expect(radioGroupTester.selectedRadio).toBeFalsy(); + + await radioGroupTester.triggerRadio({radio: radios[0]}); + expect(radioGroupTester.selectedRadio).toBe(radios[0]); + + await radioGroupTester.triggerRadio({radio: radios[1]}); + expect(radioGroupTester.selectedRadio).toBe(radios[1]); +}); +``` + +See below for the full definition of the `User` and the `RadioGroup` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/TableView.mdx b/packages/dev/s2-docs/pages/s2/TableView.mdx index 1ee9b8c7618..2a67bba4e89 100644 --- a/packages/dev/s2-docs/pages/s2/TableView.mdx +++ b/packages/dev/s2-docs/pages/s2/TableView.mdx @@ -3,11 +3,11 @@ export default Layout; import docs from 'docs:@react-spectrum/s2'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['table', 'data', 'grid']; +export const relatedPages = [ + {title: 'Testing', url: './TableView/testing.html'} +]; # TableView @@ -899,75 +899,3 @@ export default function EditableTable(props) { ### EditableCell - -## Testing - -### General setup - -TableView features long press interactions on its rows depending on the row actions provided and if the user is interacting with the table on -a touch device. Please see the following sections in the general testing documentation for more information on how to handle these -behaviors in your test suite. - -[Timers](testing.html#timers) - -[Long press](testing.html#simulating-user-long-press) - -### Test utils - -`@react-spectrum/test-utils` offers common table interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `TableView` tester in your test cases. This gives you access to `TableView` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `TableView` tester, use it to simulate row selection, and verify the table's state after each interaction. - -```ts -// Table.test.ts -import {render, within} from '@testing-library/react'; -import {User} from '@react-spectrum/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); -// ... - -it('TableView can toggle row selection', async function () { - // Render your test component/app and initialize the table tester - let {getByTestId} = render( - - ... - - ); - let tableTester = testUtilUser.createTester('Table', {root: getByTestId('test-table')}); - expect(tableTester.selectedRows).toHaveLength(0); - - await tableTester.toggleSelectAll(); - expect(tableTester.selectedRows).toHaveLength(10); - - await tableTester.toggleRowSelection({row: 2}); - expect(tableTester.selectedRows).toHaveLength(9); - let checkbox = within(tableTester.rows[2]).getByRole('checkbox'); - expect(checkbox).not.toBeChecked(); - - await tableTester.toggleSelectAll(); - expect(tableTester.selectedRows).toHaveLength(10); - expect(checkbox).toBeChecked(); - - await tableTester.toggleSelectAll(); - expect(tableTester.selectedRows).toHaveLength(0); -}); -``` - -See below for the full definition of the `User` and the `TableView` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/s2/TableView/testing.mdx b/packages/dev/s2-docs/pages/s2/TableView/testing.mdx new file mode 100644 index 00000000000..ee62ab79a88 --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/TableView/testing.mdx @@ -0,0 +1,83 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'tableview', 'test-utils']; +export const description = 'Testing TableView with React Spectrum test utils'; + +# Testing TableView + +## General setup + +TableView features long press interactions on its rows depending on the row actions provided and if the user is interacting with the table on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](../testing.html#timers) + +[Long press](../testing.html#simulating-user-long-press) + +## Test utils + +`@react-spectrum/test-utils` offers common table interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `TableView` tester in your test cases. This gives you access to `TableView` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `TableView` tester, use it to simulate row selection, and verify the table's state after each interaction. + +```ts +// Table.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime}); +// ... + +it('TableView can toggle row selection', async function () { + // Render your test component/app and initialize the table tester + let {getByTestId} = render( + + ... + + ); + let tableTester = testUtilUser.createTester('Table', {root: getByTestId('test-table')}); + expect(tableTester.selectedRows).toHaveLength(0); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(10); + + await tableTester.toggleRowSelection({row: 2}); + expect(tableTester.selectedRows).toHaveLength(9); + let checkbox = within(tableTester.rows[2]).getByRole('checkbox'); + expect(checkbox).not.toBeChecked(); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(10); + expect(checkbox).toBeChecked(); + + await tableTester.toggleSelectAll(); + expect(tableTester.selectedRows).toHaveLength(0); +}); +``` + +See below for the full definition of the `User` and the `TableView` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/Tabs.mdx b/packages/dev/s2-docs/pages/s2/Tabs.mdx index de486dd62b1..04bcd1031b3 100644 --- a/packages/dev/s2-docs/pages/s2/Tabs.mdx +++ b/packages/dev/s2-docs/pages/s2/Tabs.mdx @@ -1,12 +1,12 @@ import {Layout} from '../../src/Layout'; export default Layout; import docs from 'docs:@react-spectrum/s2'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; export const tags = ['navigation']; -import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; +export const relatedPages = [ + {title: 'Testing', url: './Tabs/testing.html'} +]; # Tabs @@ -280,59 +280,3 @@ function Example() { ### TabPanel -## Testing - -### General setup - -Tabs features automatic tab collapse behavior and may need specific mocks to test said behavior. -TODO: update this with what mocks are required - -### Test utils - -`@react-spectrum/test-utils` offers common tabs interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `Tabs` tester in your test cases. This gives you access to `Tabs` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `Tabs` tester, use it to change tab selection, and verify the tabs' state after each interaction. - -```ts -// Tabs.test.ts -import {render} from '@testing-library/react'; -import {User} from '@react-spectrum/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse'}); -// ... - -it('Tabs can change selection via keyboard', async function () { - // Render your test component/app and initialize the listbox tester - let {getByTestId} = render( - - ... - - ); - let tabsTester = testUtilUser.createTester('Tabs', {root: getByTestId('test-tabs'), interactionType: 'keyboard'}); - - let tabs = tabsTester.tabs; - expect(tabsTester.selectedTab).toBe(tabs[0]); - - await tabsTester.triggerTab({tab: 1}); - expect(tabsTester.selectedTab).toBe(tabs[1]); -}); -``` - -See below for the full definition of the `User` and the `Tabs` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/s2/Tabs/testing.mdx b/packages/dev/s2-docs/pages/s2/Tabs/testing.mdx new file mode 100644 index 00000000000..c17cb88b870 --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/Tabs/testing.mdx @@ -0,0 +1,68 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'tabs', 'test-utils']; +export const description = 'Testing Tabs with React Spectrum test utils'; + +# Testing Tabs + +## General setup + +Tabs features automatic tab collapse behavior and may need specific mocks to test said behavior. +TODO: update this with what mocks are required + +## Test utils + +`@react-spectrum/test-utils` offers common tabs interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `Tabs` tester in your test cases. This gives you access to `Tabs` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `Tabs` tester, use it to change tab selection, and verify the tabs' state after each interaction. + +```ts +// Tabs.test.ts +import {render} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('Tabs can change selection via keyboard', async function () { + // Render your test component/app and initialize the listbox tester + let {getByTestId} = render( + + ... + + ); + let tabsTester = testUtilUser.createTester('Tabs', {root: getByTestId('test-tabs'), interactionType: 'keyboard'}); + + let tabs = tabsTester.tabs; + expect(tabsTester.selectedTab).toBe(tabs[0]); + + await tabsTester.triggerTab({tab: 1}); + expect(tabsTester.selectedTab).toBe(tabs[1]); +}); +``` + +See below for the full definition of the `User` and the `Tabs` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/TreeView.mdx b/packages/dev/s2-docs/pages/s2/TreeView.mdx index 577299edf38..4d1e72006cd 100644 --- a/packages/dev/s2-docs/pages/s2/TreeView.mdx +++ b/packages/dev/s2-docs/pages/s2/TreeView.mdx @@ -3,11 +3,11 @@ export default Layout; import {TreeView, TreeViewItem, TreeViewItemContent, Collection, Text, ActionMenu, MenuItem, InlineAlert, Heading, Content} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; -import testUtilDocs from 'docs:@react-aria/test-utils'; -import {InstallCommand} from '../../src/InstallCommand'; -import {PatternTestingFAQ} from '../../src/PatternTestingFAQ'; export const tags = ['hierarchy', 'data', 'nested']; +export const relatedPages = [ + {title: 'Testing', url: './TreeView/testing.html'} +]; # TreeView @@ -381,74 +381,3 @@ function Example(props) { ### TreeViewLoadMoreItem - -## Testing - -### General setup - -TreeView features long press interactions on its rows depending on the row actions provided and if the user is interacting with the tree on -a touch device. Please see the following sections in the general testing documentation for more information on how to handle these -behaviors in your test suite. - -[Timers](testing.html#timers) - -[Long press](testing.html#simulating-user-long-press) - -### Test utils - -`@react-spectrum/test-utils` offers common tree interaction utilities which you may find helpful when writing tests. To install, simply -add it to your dev dependencies via your preferred package manager. - - - - - Requirements - Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. - - -Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate -the `TreeView` tester in your test cases. This gives you access to `TreeView` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. -The example test case below shows how you might go about setting up the `TreeView` tester, use it to simulate row expansion and selection, and verify the tree's state after each interaction. - -```ts -// Tree.test.ts -import {render, within} from '@testing-library/react'; -import {User} from '@react-spectrum/test-utils'; - -let testUtilUser = new User({interactionType: 'mouse'}); -// ... - -it('TreeView can select a item via keyboard', async function () { - // Render your test component/app and initialize the Tree tester - let {getByTestId} = render( - - ... - - ); - let treeTester = testUtilUser.createTester('Tree', {root: getByTestId('test-tree'), interactionType: 'keyboard'}); - - await treeTester.toggleRowSelection({row: 0}); - expect(treeTester.selectedRows).toHaveLength(1); - expect(within(treeTester.rows[0]).getByRole('checkbox')).toBeChecked(); - - await treeTester.toggleRowSelection({row: 1}); - expect(treeTester.selectedRows).toHaveLength(2); - expect(within(treeTester.rows[1]).getByRole('checkbox')).toBeChecked(); - - await treeTester.toggleRowSelection({row: 0}); - expect(treeTester.selectedRows).toHaveLength(1); - expect(within(treeTester.rows[0]).getByRole('checkbox')).not.toBeChecked(); - - await treeTester.toggleRowExpansion({index: 0}); - expect(treeTester.rows[0]).toHaveAttribute('aria-expanded', 'true'); -}); -``` - -See below for the full definition of the `User` and the `TreeView` tester. - - - - -### Testing FAQ - - diff --git a/packages/dev/s2-docs/pages/s2/TreeView/testing.mdx b/packages/dev/s2-docs/pages/s2/TreeView/testing.mdx new file mode 100644 index 00000000000..0c709b63cc3 --- /dev/null +++ b/packages/dev/s2-docs/pages/s2/TreeView/testing.mdx @@ -0,0 +1,82 @@ +import {Layout} from '../../../src/Layout'; +export default Layout; + +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; +import testUtilDocs from 'docs:@react-spectrum/test-utils'; +import {InstallCommand} from '../../../src/InstallCommand'; +import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; + +export const omitFromNav = true; +export const tags = ['testing', 'treeview', 'test-utils']; +export const description = 'Testing TreeView with React Spectrum test utils'; + +# Testing TreeView + +## General setup + +TreeView features long press interactions on its rows depending on the row actions provided and if the user is interacting with the tree on +a touch device. Please see the following sections in the general testing documentation for more information on how to handle these +behaviors in your test suite. + +[Timers](../testing.html#timers) + +[Long press](../testing.html#simulating-user-long-press) + +## Test utils + +`@react-spectrum/test-utils` offers common tree interaction utilities which you may find helpful when writing tests. To install, simply +add it to your dev dependencies via your preferred package manager. + + + + + Requirements + Please note that this library uses [@testing-library/react@16](https://www.npmjs.com/package/@testing-library/react) and [@testing-library/user-event@14](https://www.npmjs.com/package/@testing-library/user-event). This means that you need to be on React 18+ in order for these utilities to work. + + +Once installed, you can access the `User` that `@react-spectrum/test-utils` provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate +the `TreeView` tester in your test cases. This gives you access to `TreeView` specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. +The example test case below shows how you might go about setting up the `TreeView` tester, use it to simulate row expansion and selection, and verify the tree's state after each interaction. + +```ts +// Tree.test.ts +import {render, within} from '@testing-library/react'; +import {User} from '@react-spectrum/test-utils'; + +let testUtilUser = new User({interactionType: 'mouse'}); +// ... + +it('TreeView can select a item via keyboard', async function () { + // Render your test component/app and initialize the Tree tester + let {getByTestId} = render( + + ... + + ); + let treeTester = testUtilUser.createTester('Tree', {root: getByTestId('test-tree'), interactionType: 'keyboard'}); + + await treeTester.toggleRowSelection({row: 0}); + expect(treeTester.selectedRows).toHaveLength(1); + expect(within(treeTester.rows[0]).getByRole('checkbox')).toBeChecked(); + + await treeTester.toggleRowSelection({row: 1}); + expect(treeTester.selectedRows).toHaveLength(2); + expect(within(treeTester.rows[1]).getByRole('checkbox')).toBeChecked(); + + await treeTester.toggleRowSelection({row: 0}); + expect(treeTester.selectedRows).toHaveLength(1); + expect(within(treeTester.rows[0]).getByRole('checkbox')).not.toBeChecked(); + + await treeTester.toggleRowExpansion({index: 0}); + expect(treeTester.rows[0]).toHaveAttribute('aria-expanded', 'true'); +}); +``` + +See below for the full definition of the `User` and the `TreeView` tester. + + + + +## Testing FAQ + + diff --git a/packages/dev/s2-docs/pages/s2/testing.mdx b/packages/dev/s2-docs/pages/s2/testing.mdx index 254624c746d..b07b3e838f1 100644 --- a/packages/dev/s2-docs/pages/s2/testing.mdx +++ b/packages/dev/s2-docs/pages/s2/testing.mdx @@ -226,12 +226,12 @@ See below for the full definition of the `User` object. Below is a list of the ARIA patterns testers currently supported by createTester. See the accompanying component testing docs pages for a sample of how to use the testers in your test suite. -- [CheckboxGroup](./CheckboxGroup.html#testing) -- [ComboBox](./ComboBox.html#testing) -- [Dialog](./Dialog.html#testing) -- [Menu](./Menu.html#testing) -- [Picker](./Picker.html#testing) -- [RadioGroup](./RadioGroup.html#testing) -- [TableView](./TableView.html#testing) -- [Tabs](./Tabs.html#testing) -- [TreeView](./TreeView.html#testing) +- [CheckboxGroup](./CheckboxGroup/testing.html) +- [ComboBox](./ComboBox/testing.html) +- [Dialog](./Dialog/testing.html) +- [Menu](./Menu/testing.html) +- [Picker](./Picker/testing.html) +- [RadioGroup](./RadioGroup/testing.html) +- [TableView](./TableView/testing.html) +- [Tabs](./Tabs/testing.html) +- [TreeView](./TreeView/testing.html) From e799a043ac3937c74ced75c58de50064b5bd0fa6 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 4 Nov 2025 11:08:41 -0800 Subject: [PATCH 12/14] use isSubPage and update to detect parent link --- .../react-aria/CheckboxGroup/testing.mdx | 2 +- .../pages/react-aria/ComboBox/testing.mdx | 2 +- .../pages/react-aria/GridList/testing.mdx | 2 +- .../pages/react-aria/ListBox/testing.mdx | 2 +- .../s2-docs/pages/react-aria/Menu/testing.mdx | 2 +- .../pages/react-aria/RadioGroup/testing.mdx | 2 +- .../pages/react-aria/Select/testing.mdx | 2 +- .../pages/react-aria/Table/testing.mdx | 2 +- .../s2-docs/pages/react-aria/Tabs/testing.mdx | 2 +- .../s2-docs/pages/react-aria/Tree/testing.mdx | 2 +- .../pages/s2/CheckboxGroup/testing.mdx | 2 +- .../dev/s2-docs/pages/s2/ComboBox/testing.mdx | 2 +- .../dev/s2-docs/pages/s2/Dialog/testing.mdx | 2 +- .../dev/s2-docs/pages/s2/Menu/testing.mdx | 2 +- .../dev/s2-docs/pages/s2/Picker/testing.mdx | 2 +- .../s2-docs/pages/s2/RadioGroup/testing.mdx | 2 +- .../s2-docs/pages/s2/TableView/testing.mdx | 2 +- .../dev/s2-docs/pages/s2/Tabs/testing.mdx | 2 +- .../dev/s2-docs/pages/s2/TreeView/testing.mdx | 2 +- packages/dev/s2-docs/src/GoUpOneLink.tsx | 12 ++++++--- packages/dev/s2-docs/src/Layout.tsx | 26 ++++++++++++++++--- packages/dev/s2-docs/src/Nav.tsx | 4 +-- 22 files changed, 52 insertions(+), 28 deletions(-) diff --git a/packages/dev/s2-docs/pages/react-aria/CheckboxGroup/testing.mdx b/packages/dev/s2-docs/pages/react-aria/CheckboxGroup/testing.mdx index d57c2b9d969..2dc6269b891 100644 --- a/packages/dev/s2-docs/pages/react-aria/CheckboxGroup/testing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/CheckboxGroup/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-aria/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'checkboxgroup', 'test-utils']; export const description = 'Testing CheckboxGroup with React Aria test utils'; diff --git a/packages/dev/s2-docs/pages/react-aria/ComboBox/testing.mdx b/packages/dev/s2-docs/pages/react-aria/ComboBox/testing.mdx index ded69a0c50a..bd41496956f 100644 --- a/packages/dev/s2-docs/pages/react-aria/ComboBox/testing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ComboBox/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-aria/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'combobox', 'test-utils']; export const description = 'Testing ComboBox with React Aria test utils'; diff --git a/packages/dev/s2-docs/pages/react-aria/GridList/testing.mdx b/packages/dev/s2-docs/pages/react-aria/GridList/testing.mdx index c50f4e75c3a..f90f7f4a8cb 100644 --- a/packages/dev/s2-docs/pages/react-aria/GridList/testing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/GridList/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-aria/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'gridlist', 'test-utils']; export const description = 'Testing GridList with React Aria test utils'; diff --git a/packages/dev/s2-docs/pages/react-aria/ListBox/testing.mdx b/packages/dev/s2-docs/pages/react-aria/ListBox/testing.mdx index 58d6cc72537..93e0f3c4cbe 100644 --- a/packages/dev/s2-docs/pages/react-aria/ListBox/testing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ListBox/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-aria/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'listbox', 'test-utils']; export const description = 'Testing ListBox with React Aria test utils'; diff --git a/packages/dev/s2-docs/pages/react-aria/Menu/testing.mdx b/packages/dev/s2-docs/pages/react-aria/Menu/testing.mdx index f9339cda983..b1ac772f170 100644 --- a/packages/dev/s2-docs/pages/react-aria/Menu/testing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Menu/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-aria/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'menu', 'test-utils']; export const description = 'Testing Menu with React Aria test utils'; diff --git a/packages/dev/s2-docs/pages/react-aria/RadioGroup/testing.mdx b/packages/dev/s2-docs/pages/react-aria/RadioGroup/testing.mdx index 8b172a7c815..ea61d6933a4 100644 --- a/packages/dev/s2-docs/pages/react-aria/RadioGroup/testing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/RadioGroup/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-aria/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'radiogroup', 'test-utils']; export const description = 'Testing RadioGroup with React Aria test utils'; diff --git a/packages/dev/s2-docs/pages/react-aria/Select/testing.mdx b/packages/dev/s2-docs/pages/react-aria/Select/testing.mdx index 1a3fa450416..71f099e9153 100644 --- a/packages/dev/s2-docs/pages/react-aria/Select/testing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Select/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-aria/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'select', 'test-utils']; export const description = 'Testing Select with React Aria test utils'; diff --git a/packages/dev/s2-docs/pages/react-aria/Table/testing.mdx b/packages/dev/s2-docs/pages/react-aria/Table/testing.mdx index c9a298e19b1..f3eb7623b9b 100644 --- a/packages/dev/s2-docs/pages/react-aria/Table/testing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Table/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-aria/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'table', 'test-utils']; export const description = 'Testing Table with React Aria test utils'; diff --git a/packages/dev/s2-docs/pages/react-aria/Tabs/testing.mdx b/packages/dev/s2-docs/pages/react-aria/Tabs/testing.mdx index 1f48351eda5..1ebf61700fa 100644 --- a/packages/dev/s2-docs/pages/react-aria/Tabs/testing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Tabs/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-aria/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'tabs', 'test-utils']; export const description = 'Testing Tabs with React Aria test utils'; diff --git a/packages/dev/s2-docs/pages/react-aria/Tree/testing.mdx b/packages/dev/s2-docs/pages/react-aria/Tree/testing.mdx index 7383f558bdf..0bbd11cea7b 100644 --- a/packages/dev/s2-docs/pages/react-aria/Tree/testing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Tree/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-aria/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'tree', 'test-utils']; export const description = 'Testing Tree with React Aria test utils'; diff --git a/packages/dev/s2-docs/pages/s2/CheckboxGroup/testing.mdx b/packages/dev/s2-docs/pages/s2/CheckboxGroup/testing.mdx index 834690ffe4b..58d51ec077a 100644 --- a/packages/dev/s2-docs/pages/s2/CheckboxGroup/testing.mdx +++ b/packages/dev/s2-docs/pages/s2/CheckboxGroup/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-spectrum/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'checkboxgroup', 'test-utils']; export const description = 'Testing CheckboxGroup with React Spectrum test utils'; diff --git a/packages/dev/s2-docs/pages/s2/ComboBox/testing.mdx b/packages/dev/s2-docs/pages/s2/ComboBox/testing.mdx index 4899a904938..ce899b38df9 100644 --- a/packages/dev/s2-docs/pages/s2/ComboBox/testing.mdx +++ b/packages/dev/s2-docs/pages/s2/ComboBox/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-spectrum/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'combobox', 'test-utils']; export const description = 'Testing ComboBox with React Spectrum test utils'; diff --git a/packages/dev/s2-docs/pages/s2/Dialog/testing.mdx b/packages/dev/s2-docs/pages/s2/Dialog/testing.mdx index 8e0927a296a..ea5e8e36e71 100644 --- a/packages/dev/s2-docs/pages/s2/Dialog/testing.mdx +++ b/packages/dev/s2-docs/pages/s2/Dialog/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-spectrum/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'dialog', 'test-utils']; export const description = 'Testing Dialog with React Spectrum test utils'; diff --git a/packages/dev/s2-docs/pages/s2/Menu/testing.mdx b/packages/dev/s2-docs/pages/s2/Menu/testing.mdx index fcbec79a316..83f962a3694 100644 --- a/packages/dev/s2-docs/pages/s2/Menu/testing.mdx +++ b/packages/dev/s2-docs/pages/s2/Menu/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-spectrum/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'menu', 'test-utils']; export const description = 'Testing Menu with React Spectrum test utils'; diff --git a/packages/dev/s2-docs/pages/s2/Picker/testing.mdx b/packages/dev/s2-docs/pages/s2/Picker/testing.mdx index a98e57797b0..92c4036e5a5 100644 --- a/packages/dev/s2-docs/pages/s2/Picker/testing.mdx +++ b/packages/dev/s2-docs/pages/s2/Picker/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-spectrum/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'picker', 'test-utils']; export const description = 'Testing Picker with React Spectrum test utils'; diff --git a/packages/dev/s2-docs/pages/s2/RadioGroup/testing.mdx b/packages/dev/s2-docs/pages/s2/RadioGroup/testing.mdx index d68d42cb54a..c07838d9230 100644 --- a/packages/dev/s2-docs/pages/s2/RadioGroup/testing.mdx +++ b/packages/dev/s2-docs/pages/s2/RadioGroup/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-spectrum/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'radiogroup', 'test-utils']; export const description = 'Testing RadioGroup with React Spectrum test utils'; diff --git a/packages/dev/s2-docs/pages/s2/TableView/testing.mdx b/packages/dev/s2-docs/pages/s2/TableView/testing.mdx index ee62ab79a88..1dd9ebdd349 100644 --- a/packages/dev/s2-docs/pages/s2/TableView/testing.mdx +++ b/packages/dev/s2-docs/pages/s2/TableView/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-spectrum/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'tableview', 'test-utils']; export const description = 'Testing TableView with React Spectrum test utils'; diff --git a/packages/dev/s2-docs/pages/s2/Tabs/testing.mdx b/packages/dev/s2-docs/pages/s2/Tabs/testing.mdx index c17cb88b870..ce630d611a5 100644 --- a/packages/dev/s2-docs/pages/s2/Tabs/testing.mdx +++ b/packages/dev/s2-docs/pages/s2/Tabs/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-spectrum/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'tabs', 'test-utils']; export const description = 'Testing Tabs with React Spectrum test utils'; diff --git a/packages/dev/s2-docs/pages/s2/TreeView/testing.mdx b/packages/dev/s2-docs/pages/s2/TreeView/testing.mdx index 0c709b63cc3..f1e713d1ded 100644 --- a/packages/dev/s2-docs/pages/s2/TreeView/testing.mdx +++ b/packages/dev/s2-docs/pages/s2/TreeView/testing.mdx @@ -6,7 +6,7 @@ import testUtilDocs from 'docs:@react-spectrum/test-utils'; import {InstallCommand} from '../../../src/InstallCommand'; import {PatternTestingFAQ} from '../../../src/PatternTestingFAQ'; -export const omitFromNav = true; +export const isSubpage = true; export const tags = ['testing', 'treeview', 'test-utils']; export const description = 'Testing TreeView with React Spectrum test utils'; diff --git a/packages/dev/s2-docs/src/GoUpOneLink.tsx b/packages/dev/s2-docs/src/GoUpOneLink.tsx index ed439e732f3..7b4cf804a4f 100644 --- a/packages/dev/s2-docs/src/GoUpOneLink.tsx +++ b/packages/dev/s2-docs/src/GoUpOneLink.tsx @@ -51,11 +51,17 @@ export const btnStyles = style>({ } }); -export function GoUpOneLink() { +interface GoUpOneLinkProps { + href?: string, + 'aria-label'?: string +} + +export function GoUpOneLink(props: GoUpOneLinkProps = {}) { let ref = useRef(null); - // How to add aria-label to the Link component + let {href = './index.html'} = props; + return ( - + ({ ...components, h1: ({children, ...props}) => (
      - +

      {children}

      ) -}; +}); function anchorId(children) { return children.replace(/\s/g, '-').replace(/[^a-zA-Z0-9-_]/g, '').toLowerCase(); @@ -145,6 +145,24 @@ export function Layout(props: PageProps & {children: ReactElement}) { let title = getTitle(currentPage); let description = getDescription(currentPage); let isSubpage = currentPage.exports?.isSubpage; + let parentUrl; + let subPageReturnLabel; + if (isSubpage) { + // for testing pages, parent should be ../COMPONENT_NAME.html + // for releases, parent should be ./index.html + let pathParts = currentPage.url.split('/'); + let fileName = pathParts.pop(); + + if (fileName === 'testing.html') { + let parentDir = pathParts.pop(); + parentUrl = `../${parentDir}.html`; + subPageReturnLabel = `Go to ${parentDir} parent page.`; + } else { + parentUrl = './index.html'; + subPageReturnLabel = 'Go to all releases.'; + } + } + return ( @@ -255,7 +273,7 @@ export function Layout(props: PageProps & {children: ReactElement}) {
      {currentPage.exports?.version && } - {React.cloneElement(children, {components: isSubpage ? subPageComponents : components, pages})} + {React.cloneElement(children, {components: isSubpage ? getSubPageComponents(parentUrl, subPageReturnLabel) : components, pages})} {currentPage.exports?.relatedPages && ( )} diff --git a/packages/dev/s2-docs/src/Nav.tsx b/packages/dev/s2-docs/src/Nav.tsx index 12986f9006f..ecf19d9446c 100644 --- a/packages/dev/s2-docs/src/Nav.tsx +++ b/packages/dev/s2-docs/src/Nav.tsx @@ -11,7 +11,7 @@ export function Nav({pages, currentPage}: PageProps) { let currentLibrary = getLibraryFromPage(currentPage); let sections = new Map(); for (let page of pages) { - if (page.exports?.hideNav || page.exports?.omitFromNav) { + if (page.exports?.hideNav) { continue; } @@ -21,7 +21,7 @@ export function Nav({pages, currentPage}: PageProps) { } let section = page.exports?.section ?? 'Components'; - if (section === '') { + if (section === '' || page.exports?.isSubpage) { continue; } let sectionPages = sections.get(section) ?? []; From f8ec2fb32e6437d1951c4bbbf7665e433bac0dce Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 4 Nov 2025 11:28:21 -0800 Subject: [PATCH 13/14] export only the type --- packages/@react-aria/test-utils/src/index.ts | 24 +++++++++----------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/@react-aria/test-utils/src/index.ts b/packages/@react-aria/test-utils/src/index.ts index 885fdc94c2c..7ad297d0245 100644 --- a/packages/@react-aria/test-utils/src/index.ts +++ b/packages/@react-aria/test-utils/src/index.ts @@ -14,18 +14,16 @@ export {triggerLongPress} from './events'; export {installMouseEvent, installPointerEvent} from './testSetup'; export {pointerMap} from './userEventMaps'; export {User} from './user'; -// TODO: had to export these for the docs, but not sure why I didn't have to do -// so for the v3 docs? -export {CheckboxGroupTester} from './checkboxgroup'; -export {ComboBoxTester} from './combobox'; -export {DialogTester} from './dialog'; -export {GridListTester} from './gridlist'; -export {ListBoxTester} from './listbox'; -export {MenuTester} from './menu'; -export {RadioGroupTester} from './radiogroup'; -export {SelectTester} from './select'; -export {TableTester} from './table'; -export {TabsTester} from './tabs'; -export {TreeTester} from './tree'; +export type {CheckboxGroupTester} from './checkboxgroup'; +export type {ComboBoxTester} from './combobox'; +export type {DialogTester} from './dialog'; +export type {GridListTester} from './gridlist'; +export type {ListBoxTester} from './listbox'; +export type {MenuTester} from './menu'; +export type {RadioGroupTester} from './radiogroup'; +export type {SelectTester} from './select'; +export type {TableTester} from './table'; +export type {TabsTester} from './tabs'; +export type {TreeTester} from './tree'; export type {UserOpts} from './types'; From cad6874b6af09f6065814a5c198af90648afe240 Mon Sep 17 00:00:00 2001 From: Daniel Lu Date: Tue, 4 Nov 2025 15:47:27 -0800 Subject: [PATCH 14/14] update subpage back button logic --- packages/dev/s2-docs/src/Layout.tsx | 31 +++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/packages/dev/s2-docs/src/Layout.tsx b/packages/dev/s2-docs/src/Layout.tsx index 6bc31b406e3..65d528f0313 100644 --- a/packages/dev/s2-docs/src/Layout.tsx +++ b/packages/dev/s2-docs/src/Layout.tsx @@ -64,12 +64,13 @@ const components = { ExampleList }; -const subPageComponents = (previousPage?: Page) => ({ +const subPageComponents = (previousPage?: Page, href?: string) => ({ ...components, h1: ({children, ...props}) => (
      - {previousPage?.exports?.title} + {/* see title in Layout.tsx for this logic for extracting the title of a page */} + {previousPage?.exports?.title ?? previousPage?.tableOfContents?.[0]?.title ?? previousPage?.name}

      {children}

      @@ -148,9 +149,27 @@ export function Layout(props: PageProps & {children: ReactElement}) { let title = getTitle(currentPage); let description = getDescription(currentPage); let isSubpage = currentPage.exports?.isSubpage; - let parentPage = pages.find(p => { - return p.url === currentPage.url.replace(/\/[^/]+\.html$/, '/index.html'); - }); + + let parentUrl; + let parentPage; + if (isSubpage) { + let pathParts = currentPage.url.split('/'); + let fileName = pathParts.pop(); + + if (fileName === 'testing.html') { + // for testing pages like /CheckboxGroup/testing.html, parent is /CheckboxGroup.html + let parentDir = pathParts.pop(); + parentUrl = `../${parentDir}.html`; + + let parentPageUrl = pathParts.join('/') + `/${parentDir}.html`; + parentPage = pages.find(p => p.url === parentPageUrl); + } else { + // for release subpages like releases/2024-01-15.html, parent is just the same but with the end replaced with index.html + parentUrl = './index.html'; + let parentIndexUrl = pathParts.join('/') + '/index.html'; + parentPage = pages.find(p => p.url === parentIndexUrl); + } + } return ( @@ -264,7 +283,7 @@ export function Layout(props: PageProps & {children: ReactElement}) { {currentPage.exports?.version && } {React.cloneElement(children, { components: isSubpage ? - subPageComponents(parentPage) : + subPageComponents(parentPage, parentUrl) : components, pages })}