Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
af2817e
add proper keyboard navigation to combobox
LFDanLu Apr 27, 2026
4e1c651
mutiple select for combobox, api rename for consitency, table keyboar…
LFDanLu Apr 27, 2026
d828424
add tests for deselect
LFDanLu Apr 27, 2026
9cc80d8
get rid of timer warnings cuz user gives a default if not provided
LFDanLu Apr 27, 2026
2556c18
change getters into method calls for consistency
LFDanLu Apr 27, 2026
0f0bd7d
update instances of old getters to new function calls
LFDanLu Apr 27, 2026
c694bb4
update instances of old getters to new function calls
LFDanLu Apr 27, 2026
c9125d3
switch to dom testing library
LFDanLu Apr 27, 2026
1beb706
support missing RTL for expand and keyboard nav
LFDanLu Apr 28, 2026
59994d3
standardize indexOrText option naming
LFDanLu Apr 28, 2026
c86baaf
throw when attempting to interact with disabled rows and make error m…
LFDanLu Apr 28, 2026
53187c3
update readmes and docs to reflect change to testing-library/dom
LFDanLu Apr 28, 2026
8a871db
more audit items
LFDanLu Apr 28, 2026
a20ca65
add tests to cover error messages
LFDanLu Apr 28, 2026
348c5f7
cleanup todos that we are punting on/not doing
LFDanLu Apr 29, 2026
3c4d45e
add grid navigation to listbox test util and fix browser tests
LFDanLu Apr 29, 2026
b1ca6f4
add grid nav to gridlist too
LFDanLu Apr 29, 2026
226f5d9
add browser tests for each pattern to make sure utils work with it
LFDanLu Apr 30, 2026
3e45ed4
adding midding dialog testing pages to RAC
LFDanLu Apr 30, 2026
1687d0d
add rough skills for the test utils
LFDanLu Apr 30, 2026
3e6d64a
forgot to save
LFDanLu Apr 30, 2026
1dc99d1
improvements to skill guidence and utils from attempt to use skill to…
LFDanLu Apr 30, 2026
3fac945
more improvements from second pass
LFDanLu May 1, 2026
bc32b85
add getter for table footer
LFDanLu May 1, 2026
cf0038a
Merge branch 'main' of github.com:adobe/react-spectrum into test-util…
LFDanLu May 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/@adobe/react-spectrum/docs/combobox/ComboBox.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1020,12 +1020,12 @@ it('ComboBox can select an option via keyboard', async function () {
let comboboxTester = testUtilUser.createTester('ComboBox', {root: getByTestId('test-combobox'), interactionType: 'keyboard'});

await comboboxTester.open();
expect(comboboxTester.listbox).toBeInTheDocument();
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();
await comboboxTester.toggleOptionSelection({option: options[0]});
expect(comboboxTester.combobox().value).toBe('One');
expect(comboboxTester.listbox()).not.toBeInTheDocument();
});
```

Expand Down
8 changes: 4 additions & 4 deletions packages/@adobe/react-spectrum/docs/list/ListView.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1218,17 +1218,17 @@ it('ListView can select a row via keyboard', async function () {
);
let gridListTester = testUtilUser.createTester('GridList', {root: getByTestId('test-gridlist'), interactionType: 'keyboard'});

let row = gridListTester.rows[0];
let row = gridListTester.rows()[0];
expect(within(row).getByRole('checkbox')).not.toBeChecked();
expect(gridListTester.selectedRows).toHaveLength(0);
expect(gridListTester.selectedRows()).toHaveLength(0);

await gridListTester.toggleRowSelection({row: 0});
expect(within(row).getByRole('checkbox')).toBeChecked();
expect(gridListTester.selectedRows).toHaveLength(1);
expect(gridListTester.selectedRows()).toHaveLength(1);

await gridListTester.toggleRowSelection({row: 0});
expect(within(row).getByRole('checkbox')).not.toBeChecked();
expect(gridListTester.selectedRows).toHaveLength(0);
expect(gridListTester.selectedRows()).toHaveLength(0);
});
```

Expand Down
12 changes: 6 additions & 6 deletions packages/@adobe/react-spectrum/docs/menu/MenuTrigger.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -285,16 +285,16 @@ it('Menu can open its submenu via keyboard', async function () {
let menuTester = testUtilUser.createTester('Menu', {root: getByTestId('test-menutrigger'), interactionType: 'keyboard'});

await menuTester.open();
expect(menuTester.menu).toBeInTheDocument();
let submenuTriggers = menuTester.submenuTriggers;
expect(menuTester.menu()).toBeInTheDocument();
let submenuTriggers = menuTester.submenuTriggers();
expect(submenuTriggers).toHaveLength(1);

let submenuTester = await menuTester.openSubmenu({submenuTrigger: 'Share…'});
expect(submenuTester.menu).toBeInTheDocument();
expect(submenuTester.menu()).toBeInTheDocument();

await submenuTester.selectOption({option: submenuTester.options()[0]});
expect(submenuTester.menu).not.toBeInTheDocument();
expect(menuTester.menu).not.toBeInTheDocument();
await submenuTester.toggleOptionSelection({option: submenuTester.options()[0]});
expect(submenuTester.menu()).not.toBeInTheDocument();
expect(menuTester.menu()).not.toBeInTheDocument();
});
```

Expand Down
4 changes: 2 additions & 2 deletions packages/@adobe/react-spectrum/docs/picker/Picker.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -600,10 +600,10 @@ it('Picker can select an option via keyboard', async function () {
</Provider>
);
let selectTester = testUtilUser.createTester('Select', {root: getByTestId('test-select'), interactionType: 'keyboard'});
let trigger = selectTester.trigger;
let trigger = selectTester.trigger();
expect(trigger).toHaveTextContent('Select…');

await selectTester.selectOption({option: 'Cat'});
await selectTester.toggleOptionSelection({option: 'Cat'});
expect(trigger).toHaveTextContent('Cat');
});
```
Expand Down
12 changes: 6 additions & 6 deletions packages/@adobe/react-spectrum/docs/table/TableView.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1986,22 +1986,22 @@ it('TableView can toggle row selection', async function () {
</Provider>
);
let tableTester = testUtilUser.createTester('Table', {root: getByTestId('test-table')});
expect(tableTester.selectedRows).toHaveLength(0);
expect(tableTester.selectedRows()).toHaveLength(0);

await tableTester.toggleSelectAll();
expect(tableTester.selectedRows).toHaveLength(10);
expect(tableTester.selectedRows()).toHaveLength(10);

await tableTester.toggleRowSelection({row: 2});
expect(tableTester.selectedRows).toHaveLength(9);
let checkbox = within(tableTester.rows[2]).getByRole('checkbox');
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(tableTester.selectedRows()).toHaveLength(10);
expect(checkbox).toBeChecked();

await tableTester.toggleSelectAll();
expect(tableTester.selectedRows).toHaveLength(0);
expect(tableTester.selectedRows()).toHaveLength(0);
});
```

Expand Down
6 changes: 3 additions & 3 deletions packages/@adobe/react-spectrum/docs/tabs/Tabs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -662,11 +662,11 @@ it('Tabs can change selection via keyboard', async function () {
);
let tabsTester = testUtilUser.createTester('Tabs', {root: getByTestId('test-tabs'), interactionType: 'keyboard'});

let tabs = tabsTester.tabs;
expect(tabsTester.selectedTab).toBe(tabs[0]);
let tabs = tabsTester.tabs();
expect(tabsTester.selectedTab()).toBe(tabs[0]);

await tabsTester.triggerTab({tab: 1});
expect(tabsTester.selectedTab).toBe(tabs[1]);
expect(tabsTester.selectedTab()).toBe(tabs[1]);
});
```

Expand Down
12 changes: 6 additions & 6 deletions packages/@adobe/react-spectrum/docs/tree/TreeView.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -557,16 +557,16 @@ it('TreeView can select a row via keyboard', async function () {
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();
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();
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();
expect(treeTester.selectedRows()).toHaveLength(1);
expect(within(treeTester.rows()[0]).getByRole('checkbox')).not.toBeChecked();
});
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -800,29 +800,29 @@ describe('CheckboxGroup', () => {
);

let checkboxGroupTester = testUtilUser.createTester('CheckboxGroup', {root: getByRole('group')});
expect(checkboxGroupTester.checkboxgroup).toHaveAttribute('role');
let checkboxes = checkboxGroupTester.checkboxes;
expect(checkboxGroupTester.checkboxgroup()).toHaveAttribute('role');
let checkboxes = checkboxGroupTester.checkboxes();
await checkboxGroupTester.toggleCheckbox({checkbox: checkboxes[0]});
expect(checkboxes[0]).toBeChecked();
expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1);
expect(checkboxGroupTester.selectedCheckboxes()).toHaveLength(1);

await checkboxGroupTester.toggleCheckbox({checkbox: 4, interactionType: 'keyboard'});
expect(checkboxes[4]).toBeChecked();
expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2);
expect(checkboxGroupTester.selectedCheckboxes()).toHaveLength(2);

let checkbox4 = checkboxGroupTester.findCheckbox({checkboxIndexOrText: 3});
let checkbox4 = checkboxGroupTester.findCheckbox({indexOrText: 3});
await checkboxGroupTester.toggleCheckbox({checkbox: checkbox4, interactionType: 'keyboard'});
expect(checkboxes[3]).toBeChecked();
expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(3);
expect(checkboxGroupTester.selectedCheckboxes()).toHaveLength(3);

await checkboxGroupTester.toggleCheckbox({checkbox: 'Soccer', interactionType: 'keyboard'});
expect(checkboxes[0]).not.toBeChecked();
expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(2);
expect(checkboxGroupTester.selectedCheckboxes()).toHaveLength(2);

let checkbox5 = checkboxGroupTester.findCheckbox({checkboxIndexOrText: 'Rugby'});
let checkbox5 = checkboxGroupTester.findCheckbox({indexOrText: 'Rugby'});
await checkboxGroupTester.toggleCheckbox({checkbox: checkbox5, interactionType: 'mouse'});
expect(checkboxes[4]).not.toBeChecked();
expect(checkboxGroupTester.selectedCheckboxes).toHaveLength(1);
expect(checkboxGroupTester.selectedCheckboxes()).toHaveLength(1);
});
});
});
70 changes: 35 additions & 35 deletions packages/@adobe/react-spectrum/test/combobox/ComboBox.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -315,23 +315,23 @@ describe('ComboBox', function () {
let comboboxTester = testUtilUser.createTester('ComboBox', {root: tree.container});

act(() => {
comboboxTester.combobox.focus();
comboboxTester.combobox().focus();
});
await user.keyboard('One');

expect(comboboxTester.listbox).toBeFalsy();
expect(comboboxTester.listbox()).toBeFalsy();
expect(onOpenChange).not.toHaveBeenCalled();
expect(onFocus).not.toHaveBeenCalled();

comboboxTester.setInteractionType('keyboard');
await comboboxTester.open();

expect(comboboxTester.listbox).toBeFalsy();
expect(comboboxTester.listbox()).toBeFalsy();
expect(onOpenChange).not.toHaveBeenCalled();

comboboxTester.setInteractionType('mouse');
await comboboxTester.open();
expect(comboboxTester.listbox).toBeFalsy();
expect(comboboxTester.listbox()).toBeFalsy();
expect(onOpenChange).not.toHaveBeenCalled();
expect(onInputChange).not.toHaveBeenCalled();
});
Expand All @@ -341,34 +341,34 @@ describe('ComboBox', function () {
let comboboxTester = testUtilUser.createTester('ComboBox', {root: tree.container});

act(() => {
comboboxTester.combobox.focus();
comboboxTester.combobox().focus();
});
await user.keyboard('One');
expect(comboboxTester.listbox).toBeFalsy();
expect(comboboxTester.combobox.value).toBe('Blargh');
expect(comboboxTester.listbox()).toBeFalsy();
expect(comboboxTester.combobox().value).toBe('Blargh');
expect(onOpenChange).not.toHaveBeenCalled();
expect(onFocus).toHaveBeenCalled();
expect(onInputChange).not.toHaveBeenCalled();

comboboxTester.setInteractionType('keyboard');
await comboboxTester.open();

expect(comboboxTester.listbox).toBeFalsy();
expect(comboboxTester.listbox()).toBeFalsy();
expect(onOpenChange).not.toHaveBeenCalled();
expect(onInputChange).not.toHaveBeenCalled();

comboboxTester.setInteractionType('mouse');
await comboboxTester.open();

expect(comboboxTester.listbox).toBeFalsy();
expect(comboboxTester.listbox()).toBeFalsy();
expect(onOpenChange).not.toHaveBeenCalled();
});

it('features default behavior of completionMode suggest and menuTrigger input', async function () {
let tree = renderComboBox();
let comboboxTester = testUtilUser.createTester('ComboBox', {root: tree.container});

let combobox = comboboxTester.combobox;
let combobox = comboboxTester.combobox();
expect(combobox).not.toHaveAttribute('aria-controls');
expect(combobox).not.toHaveAttribute('aria-activedescendant');
expect(combobox).toHaveAttribute('aria-autocomplete', 'list');
Expand All @@ -386,15 +386,15 @@ describe('ComboBox', function () {

expect(combobox.value).toBe('On');
expect(items[0]).toHaveTextContent('One');
expect(combobox).toHaveAttribute('aria-controls', comboboxTester.listbox.id);
expect(combobox).toHaveAttribute('aria-controls', comboboxTester.listbox().id);
expect(combobox).not.toHaveAttribute('aria-activedescendant');

await user.keyboard('{ArrowDown}');
act(() => {
jest.runAllTimers();
});

expect(combobox).toHaveAttribute('aria-activedescendant', comboboxTester.focusedOption.id);
expect(combobox).toHaveAttribute('aria-activedescendant', comboboxTester.focusedOption().id);
});

describe('refs', function () {
Expand Down Expand Up @@ -426,11 +426,11 @@ describe('ComboBox', function () {
let tree = renderComboBox({menuTrigger: 'focus'});
let comboboxTester = testUtilUser.createTester('ComboBox', {root: tree.container});

let button = comboboxTester.trigger;
let combobox = comboboxTester.combobox;
let button = comboboxTester.trigger();
let combobox = comboboxTester.combobox();
await comboboxTester.open({triggerBehavior: 'focus'});

let listbox = comboboxTester.listbox;
let listbox = comboboxTester.listbox();
expect(onOpenChange).toBeCalledTimes(1);
expect(onOpenChange).toHaveBeenCalledWith(true, 'focus');
await testComboBoxOpen(combobox, button, listbox);
Expand All @@ -440,11 +440,11 @@ describe('ComboBox', function () {
let tree = renderComboBox({menuTrigger: 'focus'});
let comboboxTester = testUtilUser.createTester('ComboBox', {root: tree.container});

let button = comboboxTester.trigger;
let combobox = comboboxTester.combobox;
let button = comboboxTester.trigger();
let combobox = comboboxTester.combobox();
await comboboxTester.open({triggerBehavior: 'manual'});

let listbox = comboboxTester.listbox;
let listbox = comboboxTester.listbox();
expect(onOpenChange).toBeCalledTimes(1);
expect(onOpenChange).toHaveBeenCalledWith(true, 'focus');
await testComboBoxOpen(combobox, button, listbox);
Expand All @@ -458,18 +458,18 @@ describe('ComboBox', function () {
let combobox = getByRole('combobox');
let comboboxTester = testUtilUser.createTester('ComboBox', {root: combobox, trigger: button});

expect(comboboxTester.listbox).toBeFalsy();
expect(comboboxTester.listbox()).toBeFalsy();
await comboboxTester.open();

expect(comboboxTester.listbox).toBeInTheDocument();
expect(document.activeElement).toBe(comboboxTester.combobox);
expect(comboboxTester.listbox()).toBeInTheDocument();
expect(document.activeElement).toBe(comboboxTester.combobox());

await user.click(comboboxTester.trigger);
await user.click(comboboxTester.trigger());
act(() => {
jest.runAllTimers();
});

expect(comboboxTester.listbox).toBeFalsy();
expect(comboboxTester.listbox()).toBeFalsy();
});

it('doesn\'t focus first item if there are items loaded', async function () {
Expand All @@ -492,37 +492,37 @@ describe('ComboBox', function () {
let tree = renderComboBox({});
let comboboxTester = testUtilUser.createTester('ComboBox', {root: tree.container});

let combobox = comboboxTester.combobox;
let combobox = comboboxTester.combobox();
expect(document.activeElement).not.toBe(combobox);

comboboxTester.setInteractionType('touch');
await comboboxTester.open();
expect(document.activeElement).toBe(comboboxTester.combobox);
expect(comboboxTester.listbox).toBeInTheDocument();
expect(document.activeElement).toBe(comboboxTester.combobox());
expect(comboboxTester.listbox()).toBeInTheDocument();

let button = comboboxTester.trigger;
let button = comboboxTester.trigger();
fireEvent.touchStart(button, {targetTouches: [{identifier: 1}]});
fireEvent.touchEnd(button, {changedTouches: [{identifier: 1, clientX: 0, clientY: 0}]});
act(() => {
jest.runAllTimers();
});
expect(comboboxTester.listbox).toBeFalsy();
expect(comboboxTester.listbox()).toBeFalsy();
});

it('it doesn\'t reset the focused item when re-opening the menu', async function () {
let tree = renderComboBox({});
let comboboxTester = testUtilUser.createTester('ComboBox', {root: tree.container});

await comboboxTester.open();
expect(comboboxTester.combobox).not.toHaveAttribute('aria-activedescendant');
expect(comboboxTester.combobox()).not.toHaveAttribute('aria-activedescendant');

let options = comboboxTester.options();
await comboboxTester.selectOption({option: options[0]});
await comboboxTester.toggleOptionSelection({option: options[0]});

expect(comboboxTester.combobox.value).toBe('One');
expect(comboboxTester.combobox().value).toBe('One');

await comboboxTester.open();
expect(comboboxTester.combobox).toHaveAttribute('aria-activedescendant', options[0].id);
expect(comboboxTester.combobox()).toHaveAttribute('aria-activedescendant', options[0].id);
});

it('shows all items', async function () {
Expand Down Expand Up @@ -865,7 +865,7 @@ describe('ComboBox', function () {
let tree = renderComboBox({defaultSelectedKey: '2'});
let comboboxTester = testUtilUser.createTester('ComboBox', {root: tree.container});

let combobox = comboboxTester.combobox;
let combobox = comboboxTester.combobox();
expect(combobox.value).toBe('Two');

act(() => combobox.focus());
Expand All @@ -876,8 +876,8 @@ describe('ComboBox', function () {
expect(combobox.value).toBe('Tw');
expect(comboboxTester.options().length).toBe(1);

await comboboxTester.selectOption({option: 'Two'});
expect(comboboxTester.listbox).toBeFalsy();
await comboboxTester.toggleOptionSelection({option: 'Two'});
expect(comboboxTester.listbox()).toBeFalsy();
expect(combobox.value).toBe('Two');
// selectionManager.select from useSingleSelectListState always calls onSelectionChange even if the key is the same
expect(onSelectionChange).toHaveBeenCalledTimes(1);
Expand Down
Loading
Loading