feat: Test utils API audit, additional feature coverage (RTL, grid nav, etc) in prep for RC/1.0#9998
feat: Test utils API audit, additional feature coverage (RTL, grid nav, etc) in prep for RC/1.0#9998
Conversation
also gives us the option of adding args in the future
this is a lower level than using react testing library of which we wernt really using anything unique from that library. trade off is that we need to wrap act so it works for other react versions, still not 100% sure if we wanna do this
…essages more descriptive
|
Build successful! 🎉 |
|
Build successful! 🎉 |
|
Build successful! 🎉 |
|
Build successful! 🎉 |
## API Changes
@react-aria/test-utils/@react-aria/test-utils:CheckboxGroupTester CheckboxGroupTester {
- checkboxes: Array<HTMLElement>
- checkboxgroup: HTMLElement
+ checkboxes: () => Array<HTMLElement>
+ checkboxgroup: () => HTMLElement
constructor: (CheckboxGroupTesterOpts) => void
findCheckbox: ({
- checkboxIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- selectedCheckboxes: Array<HTMLElement>
+ selectedCheckboxes: () => Array<HTMLElement>
setInteractionType: (UserOpts['interactionType']) => void
toggleCheckbox: (TriggerCheckboxOptions) => Promise<void>
}/@react-aria/test-utils:ComboBoxTester ComboBoxTester {
close: () => Promise<void>
- combobox: HTMLElement
+ combobox: () => HTMLElement
constructor: (ComboBoxTesterOpts) => void
findOption: ({
- optionIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- focusedOption: HTMLElement | null
- listbox: HTMLElement | null
+ focusedOption: () => HTMLElement | null
+ listbox: () => HTMLElement | null
open: (ComboBoxOpenOpts) => Promise<void>
options: ({
element?: HTMLElement
}) => Array<HTMLElement>
- sections: Array<HTMLElement>
- selectOption: (ComboBoxSelectOpts) => Promise<void>
+ sections: () => Array<HTMLElement>
setInteractionType: (UserOpts['interactionType']) => void
- trigger: HTMLElement
+ toggleOptionSelection: (ComboBoxSelectOpts) => Promise<void>
+ trigger: () => HTMLElement
}/@react-aria/test-utils:DialogTester DialogTester {
close: () => Promise<void>
constructor: (DialogTesterOpts) => void
- dialog: HTMLElement | null
+ dialog: () => HTMLElement | null
open: (DialogOpenOpts) => Promise<void>
setInteractionType: (UserOpts['interactionType']) => void
- trigger: HTMLElement
+ trigger: () => HTMLElement
}/@react-aria/test-utils:GridListTester GridListTester {
cells: ({
element?: HTMLElement
}) => Array<HTMLElement>
constructor: (GridListTesterOpts) => void
findRow: ({
- rowIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- gridlist: HTMLElement
- rows: Array<HTMLElement>
- selectedRows: Array<HTMLElement>
+ gridlist: () => HTMLElement
+ rows: () => Array<HTMLElement>
+ selectedRows: () => Array<HTMLElement>
setInteractionType: (UserOpts['interactionType']) => void
toggleRowSelection: (GridListToggleRowOpts) => Promise<void>
triggerRowAction: (GridListRowActionOpts) => Promise<void>
}/@react-aria/test-utils:ListBoxTester ListBoxTester {
constructor: (ListBoxTesterOpts) => void
findOption: ({
- optionIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- listbox: HTMLElement
+ listbox: () => HTMLElement
options: ({
element?: HTMLElement
}) => Array<HTMLElement>
- sections: Array<HTMLElement>
- selectedOptions: Array<HTMLElement>
+ sections: () => Array<HTMLElement>
+ selectedOptions: () => Array<HTMLElement>
setInteractionType: (UserOpts['interactionType']) => void
toggleOptionSelection: (ListBoxToggleOptionOpts) => Promise<void>
triggerOptionAction: (ListBoxOptionActionOpts) => Promise<void>
}/@react-aria/test-utils:MenuTester MenuTester {
close: () => Promise<void>
constructor: (MenuTesterOpts) => void
findOption: ({
- optionIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- menu: HTMLElement | null
+ menu: () => HTMLElement | null
open: (MenuOpenOpts) => Promise<void>
- openSubmenu: (MenuOpenSubmenuOpts) => Promise<MenuTester | null>
+ openSubmenu: (MenuOpenSubmenuOpts) => Promise<MenuTester>
options: ({
element?: HTMLElement
}) => Array<HTMLElement>
- sections: Array<HTMLElement>
- selectOption: (MenuSelectOpts) => Promise<void>
+ sections: () => Array<HTMLElement>
setInteractionType: (UserOpts['interactionType']) => void
- submenuTriggers: Array<HTMLElement>
- trigger: HTMLElement
+ submenuTriggers: () => Array<HTMLElement>
+ toggleOptionSelection: (MenuSelectOpts) => Promise<void>
+ trigger: () => HTMLElement
}/@react-aria/test-utils:RadioGroupTester RadioGroupTester {
constructor: (RadioGroupTesterOpts) => void
findRadio: ({
- radioIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- radiogroup: HTMLElement
- radios: Array<HTMLElement>
- selectedRadio: HTMLElement | null
+ radiogroup: () => HTMLElement
+ radios: () => Array<HTMLElement>
+ selectedRadio: () => HTMLElement | null
setInteractionType: (UserOpts['interactionType']) => void
triggerRadio: (TriggerRadioOptions) => Promise<void>
}/@react-aria/test-utils:SelectTester SelectTester {
close: () => Promise<void>
constructor: (SelectTesterOpts) => void
findOption: ({
- optionIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- listbox: HTMLElement | null
+ listbox: () => HTMLElement | null
open: (SelectOpenOpts) => Promise<void>
options: ({
element?: HTMLElement
}) => Array<HTMLElement>
- sections: Array<HTMLElement>
- selectOption: (SelectTriggerOptionOpts) => Promise<void>
+ sections: () => Array<HTMLElement>
setInteractionType: (UserOpts['interactionType']) => void
- trigger: HTMLElement
+ toggleOptionSelection: (SelectTriggerOptionOpts) => Promise<void>
+ trigger: () => HTMLElement
}/@react-aria/test-utils:TableTester TableTester {
cells: ({
element?: HTMLElement
}) => Array<HTMLElement>
- columns: Array<HTMLElement>
+ columns: () => Array<HTMLElement>
constructor: (TableTesterOpts) => void
findCell: ({
text: string
}) => HTMLElement
findRow: ({
- rowIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- rowGroups: Array<HTMLElement>
- rowHeaders: Array<HTMLElement>
- rows: Array<HTMLElement>
- selectedRows: Array<HTMLElement>
+ footerRows: () => Array<HTMLElement>
+ rowGroups: () => Array<HTMLElement>
+ rowHeaders: () => Array<HTMLElement>
+ rows: () => Array<HTMLElement>
+ selectedRows: () => Array<HTMLElement>
setInteractionType: (UserOpts['interactionType']) => void
- table: HTMLElement
+ table: () => HTMLElement
toggleRowExpansion: (TableToggleExpansionOpts) => Promise<void>
toggleRowSelection: (TableToggleRowOpts) => Promise<void>
toggleSelectAll: ({
interactionType?: UserOpts['interactionType']
toggleSort: (TableToggleSortOpts) => Promise<void>
triggerColumnHeaderAction: (TableColumnHeaderActionOpts) => Promise<void>
triggerRowAction: (TableRowActionOpts) => Promise<void>
}/@react-aria/test-utils:TabsTester TabsTester {
- activeTabpanel: HTMLElement | null
+ activeTabpanel: () => HTMLElement | null
constructor: (TabsTesterOpts) => void
findTab: ({
- tabIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- selectedTab: HTMLElement | null
+ selectedTab: () => HTMLElement | null
setInteractionType: (UserOpts['interactionType']) => void
- tablist: HTMLElement
- tabpanels: Array<HTMLElement>
- tabs: Array<HTMLElement>
+ tablist: () => HTMLElement
+ tabpanels: () => Array<HTMLElement>
+ tabs: () => Array<HTMLElement>
triggerTab: (TriggerTabOptions) => Promise<void>
}/@react-aria/test-utils:TreeTester TreeTester {
cells: ({
element?: HTMLElement
}) => Array<HTMLElement>
constructor: (TreeTesterOpts) => void
findRow: ({
- rowIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- rows: Array<HTMLElement>
- selectedRows: Array<HTMLElement>
+ rows: () => Array<HTMLElement>
+ selectedRows: () => Array<HTMLElement>
setInteractionType: (UserOpts['interactionType']) => void
toggleRowExpansion: (TreeToggleExpansionOpts) => Promise<void>
toggleRowSelection: (TreeToggleRowOpts) => Promise<void>
- tree: HTMLElement
+ tree: () => HTMLElement
triggerRowAction: (TreeRowActionOpts) => Promise<void>
}@react-spectrum/test-utils/@react-spectrum/test-utils:CheckboxGroupTester CheckboxGroupTester {
- checkboxes: Array<HTMLElement>
- checkboxgroup: HTMLElement
+ checkboxes: () => Array<HTMLElement>
+ checkboxgroup: () => HTMLElement
constructor: (CheckboxGroupTesterOpts) => void
findCheckbox: ({
- checkboxIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- selectedCheckboxes: Array<HTMLElement>
+ selectedCheckboxes: () => Array<HTMLElement>
setInteractionType: (UserOpts['interactionType']) => void
toggleCheckbox: (TriggerCheckboxOptions) => Promise<void>
}/@react-spectrum/test-utils:ComboBoxTester ComboBoxTester {
close: () => Promise<void>
- combobox: HTMLElement
+ combobox: () => HTMLElement
constructor: (ComboBoxTesterOpts) => void
findOption: ({
- optionIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- focusedOption: HTMLElement | null
- listbox: HTMLElement | null
+ focusedOption: () => HTMLElement | null
+ listbox: () => HTMLElement | null
open: (ComboBoxOpenOpts) => Promise<void>
options: ({
element?: HTMLElement
}) => Array<HTMLElement>
- sections: Array<HTMLElement>
- selectOption: (ComboBoxSelectOpts) => Promise<void>
+ sections: () => Array<HTMLElement>
setInteractionType: (UserOpts['interactionType']) => void
- trigger: HTMLElement
+ toggleOptionSelection: (ComboBoxSelectOpts) => Promise<void>
+ trigger: () => HTMLElement
}/@react-spectrum/test-utils:DialogTester DialogTester {
close: () => Promise<void>
constructor: (DialogTesterOpts) => void
- dialog: HTMLElement | null
+ dialog: () => HTMLElement | null
open: (DialogOpenOpts) => Promise<void>
setInteractionType: (UserOpts['interactionType']) => void
- trigger: HTMLElement
+ trigger: () => HTMLElement
}/@react-spectrum/test-utils:GridListTester GridListTester {
cells: ({
element?: HTMLElement
}) => Array<HTMLElement>
constructor: (GridListTesterOpts) => void
findRow: ({
- rowIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- gridlist: HTMLElement
- rows: Array<HTMLElement>
- selectedRows: Array<HTMLElement>
+ gridlist: () => HTMLElement
+ rows: () => Array<HTMLElement>
+ selectedRows: () => Array<HTMLElement>
setInteractionType: (UserOpts['interactionType']) => void
toggleRowSelection: (GridListToggleRowOpts) => Promise<void>
triggerRowAction: (GridListRowActionOpts) => Promise<void>
}/@react-spectrum/test-utils:ListBoxTester ListBoxTester {
constructor: (ListBoxTesterOpts) => void
findOption: ({
- optionIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- listbox: HTMLElement
+ listbox: () => HTMLElement
options: ({
element?: HTMLElement
}) => Array<HTMLElement>
- sections: Array<HTMLElement>
- selectedOptions: Array<HTMLElement>
+ sections: () => Array<HTMLElement>
+ selectedOptions: () => Array<HTMLElement>
setInteractionType: (UserOpts['interactionType']) => void
toggleOptionSelection: (ListBoxToggleOptionOpts) => Promise<void>
triggerOptionAction: (ListBoxOptionActionOpts) => Promise<void>
}/@react-spectrum/test-utils:MenuTester MenuTester {
close: () => Promise<void>
constructor: (MenuTesterOpts) => void
findOption: ({
- optionIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- menu: HTMLElement | null
+ menu: () => HTMLElement | null
open: (MenuOpenOpts) => Promise<void>
- openSubmenu: (MenuOpenSubmenuOpts) => Promise<MenuTester | null>
+ openSubmenu: (MenuOpenSubmenuOpts) => Promise<MenuTester>
options: ({
element?: HTMLElement
}) => Array<HTMLElement>
- sections: Array<HTMLElement>
- selectOption: (MenuSelectOpts) => Promise<void>
+ sections: () => Array<HTMLElement>
setInteractionType: (UserOpts['interactionType']) => void
- submenuTriggers: Array<HTMLElement>
- trigger: HTMLElement
+ submenuTriggers: () => Array<HTMLElement>
+ toggleOptionSelection: (MenuSelectOpts) => Promise<void>
+ trigger: () => HTMLElement
}/@react-spectrum/test-utils:RadioGroupTester RadioGroupTester {
constructor: (RadioGroupTesterOpts) => void
findRadio: ({
- radioIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- radiogroup: HTMLElement
- radios: Array<HTMLElement>
- selectedRadio: HTMLElement | null
+ radiogroup: () => HTMLElement
+ radios: () => Array<HTMLElement>
+ selectedRadio: () => HTMLElement | null
setInteractionType: (UserOpts['interactionType']) => void
triggerRadio: (TriggerRadioOptions) => Promise<void>
}/@react-spectrum/test-utils:SelectTester SelectTester {
close: () => Promise<void>
constructor: (SelectTesterOpts) => void
findOption: ({
- optionIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- listbox: HTMLElement | null
+ listbox: () => HTMLElement | null
open: (SelectOpenOpts) => Promise<void>
options: ({
element?: HTMLElement
}) => Array<HTMLElement>
- sections: Array<HTMLElement>
- selectOption: (SelectTriggerOptionOpts) => Promise<void>
+ sections: () => Array<HTMLElement>
setInteractionType: (UserOpts['interactionType']) => void
- trigger: HTMLElement
+ toggleOptionSelection: (SelectTriggerOptionOpts) => Promise<void>
+ trigger: () => HTMLElement
}/@react-spectrum/test-utils:TableTester TableTester {
cells: ({
element?: HTMLElement
}) => Array<HTMLElement>
- columns: Array<HTMLElement>
+ columns: () => Array<HTMLElement>
constructor: (TableTesterOpts) => void
findCell: ({
text: string
}) => HTMLElement
findRow: ({
- rowIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- rowGroups: Array<HTMLElement>
- rowHeaders: Array<HTMLElement>
- rows: Array<HTMLElement>
- selectedRows: Array<HTMLElement>
+ footerRows: () => Array<HTMLElement>
+ rowGroups: () => Array<HTMLElement>
+ rowHeaders: () => Array<HTMLElement>
+ rows: () => Array<HTMLElement>
+ selectedRows: () => Array<HTMLElement>
setInteractionType: (UserOpts['interactionType']) => void
- table: HTMLElement
+ table: () => HTMLElement
toggleRowExpansion: (TableToggleExpansionOpts) => Promise<void>
toggleRowSelection: (TableToggleRowOpts) => Promise<void>
toggleSelectAll: ({
interactionType?: UserOpts['interactionType']
toggleSort: (TableToggleSortOpts) => Promise<void>
triggerColumnHeaderAction: (TableColumnHeaderActionOpts) => Promise<void>
triggerRowAction: (TableRowActionOpts) => Promise<void>
}/@react-spectrum/test-utils:TabsTester TabsTester {
- activeTabpanel: HTMLElement | null
+ activeTabpanel: () => HTMLElement | null
constructor: (TabsTesterOpts) => void
findTab: ({
- tabIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- selectedTab: HTMLElement | null
+ selectedTab: () => HTMLElement | null
setInteractionType: (UserOpts['interactionType']) => void
- tablist: HTMLElement
- tabpanels: Array<HTMLElement>
- tabs: Array<HTMLElement>
+ tablist: () => HTMLElement
+ tabpanels: () => Array<HTMLElement>
+ tabs: () => Array<HTMLElement>
triggerTab: (TriggerTabOptions) => Promise<void>
}/@react-spectrum/test-utils:TreeTester TreeTester {
cells: ({
element?: HTMLElement
}) => Array<HTMLElement>
constructor: (TreeTesterOpts) => void
findRow: ({
- rowIndexOrText: number | string
+ indexOrText: number | string
}) => HTMLElement
- rows: Array<HTMLElement>
- selectedRows: Array<HTMLElement>
+ rows: () => Array<HTMLElement>
+ selectedRows: () => Array<HTMLElement>
setInteractionType: (UserOpts['interactionType']) => void
toggleRowExpansion: (TreeToggleExpansionOpts) => Promise<void>
toggleRowSelection: (TreeToggleRowOpts) => Promise<void>
- tree: HTMLElement
+ tree: () => HTMLElement
triggerRowAction: (TreeRowActionOpts) => Promise<void>
} |
| } | ||
|
|
||
| if (!this.checkboxgroup.contains(document.activeElement)) { | ||
| if (!this.checkboxgroup().contains(document.activeElement)) { |
There was a problem hiding this comment.
if it's a function, then I think I'd prefer the naming scheme getCheckboxGroup
| // util before first render. Will need to document it well | ||
| let {element, advanceTimer, pointerOpts = {}} = opts; | ||
| let pointerType = pointerOpts.pointerType ?? 'mouse'; | ||
| let shouldFireCompatibilityEvents = fireEvent.pointerDown(element, {pointerType, ...pointerOpts}); |
There was a problem hiding this comment.
instead of this, we should do what react testing library does and configure the dom testing library to wrap events https://github.com/testing-library/react-testing-library/blob/be9d81d91314c9f0bafaa363f70b409b4b31989c/src/pure.js#L60
However, there's a bigger issue I'm worried about, react testing library actually modifies fireEvent pretty substantially with React specific things, so we'd need to probably copy this as well
https://github.com/testing-library/react-testing-library/blob/main/src/fire-event.js
There was a problem hiding this comment.
I imagine we need the other things in that configuration as well
There was a problem hiding this comment.
just realised this is in our testing tools, not a test
I'm not sure we need to include the act wrapper, the users environment should handle it
how did you determine you needed these?
|
|
||
| export function formatTargetNode(value: number | string | HTMLElement): string { | ||
| if (typeof HTMLElement !== 'undefined' && value instanceof HTMLElement) { | ||
| return value.outerHTML; |
There was a problem hiding this comment.
could be very large, how would you feel about something like
value.cloneNode(false).outerHTML
this will remove all the children and just give you the tag + attributes
| } | ||
| let menu = this.menu(); | ||
| if (!menu) { | ||
| throw new Error('Cannot open submenu, parent menu didn\'t open on trigger "${formatTargetNode(trigger)}" press.'); |
There was a problem hiding this comment.
missing the interpolation backticks
| expect(within(row).getByRole('checkbox')).toBeDisabled(); | ||
|
|
||
| let gridListTester = testUtilUser.createTester('GridList', {root: getByRole('grid')}); | ||
| await expect(gridListTester.toggleRowSelection({row: 0})).rejects.toThrow(); |
There was a problem hiding this comment.
something to remember, expect.toThrow is extremely slow
| } | ||
|
|
||
| export const act = actImpl; | ||
| export const act: typeof actImpl = ((fn: any) => { |
| let curr = (document.activeElement as HTMLElement).getBoundingClientRect(); | ||
| let target = option.getBoundingClientRect(); | ||
| let key: string; | ||
| // basically compare current position with desired position to determine if we need to go up/down/left/right |
There was a problem hiding this comment.
basically compare current position with desired position to determine if we need to go up/down/left/right
See commits for what changed
Some additional pattern support to be opened in a different PR maybe, to discuss if we think they are worth it or not.
✅ Pull Request Checklist:
📝 Test Instructions:
🧢 Your Project:
RSP