diff --git a/packages/react/src/ActionBar/ActionBar.test.tsx b/packages/react/src/ActionBar/ActionBar.test.tsx
index 9afa538368f..23aa52a96ca 100644
--- a/packages/react/src/ActionBar/ActionBar.test.tsx
+++ b/packages/react/src/ActionBar/ActionBar.test.tsx
@@ -1,6 +1,6 @@
+import {page, userEvent} from 'vitest/browser'
import {describe, expect, it, afterEach, vi} from 'vitest'
-import {render, screen, act, within} from '@testing-library/react'
-import userEvent from '@testing-library/user-event'
+import {render, act, fireEvent} from '@testing-library/react'
import React, {createRef, useState} from 'react'
import ActionBar from './'
import {BoldIcon, ItalicIcon, CodeIcon} from '@primer/octicons-react'
@@ -14,9 +14,8 @@ describe('ActionBar', () => {
})
it('renders icon elements in the overflow menu', async () => {
- const user = userEvent.setup()
render(
-
+
,
)
- await user.click(await screen.findByRole('button', {name: 'More items'}))
+ await expect.element(page.getByRole('toolbar')).toHaveAttribute('data-has-overflow', 'true')
+ await userEvent.click(page.getByRole('button', {name: 'More items', exact: true}))
- expect(within(screen.getByRole('menuitem', {name: 'Bold'})).getByTestId('action-icon')).toBeInTheDocument()
- expect(within(screen.getByRole('menuitem', {name: 'Formatting'})).getByTestId('menu-icon')).toBeInTheDocument()
+ await expect
+ .element(page.getByRole('menuitem', {name: 'Bold', exact: true}).getByTestId('action-icon'))
+ .toBeInTheDocument()
+ await expect
+ .element(page.getByRole('menuitem', {name: 'Formatting', exact: true}).getByTestId('menu-icon'))
+ .toBeInTheDocument()
})
it('renders icon elements in menu items and submenu triggers', async () => {
- const user = userEvent.setup()
render(
{
,
)
- await user.click(screen.getByRole('button', {name: 'Formatting'}))
+ await userEvent.click(page.getByRole('button', {name: 'Formatting', exact: true}))
- const item = within(screen.getByRole('menuitem', {name: 'Bold'}))
- expect(item.getByTestId('item-leading-icon')).toBeInTheDocument()
- expect(item.getByTestId('item-trailing-icon')).toBeInTheDocument()
- const submenu = within(screen.getByRole('menuitem', {name: 'More formatting'}))
- expect(submenu.getByTestId('submenu-leading-icon')).toBeInTheDocument()
- expect(submenu.getByTestId('submenu-trailing-icon')).toBeInTheDocument()
+ const item = page.getByRole('menuitem', {name: 'Bold', exact: true})
+ await expect.element(item.getByTestId('item-leading-icon')).toBeInTheDocument()
+ await expect.element(item.getByTestId('item-trailing-icon')).toBeInTheDocument()
+ const submenu = page.getByRole('menuitem', {name: 'More formatting', exact: true})
+ await expect.element(submenu.getByTestId('submenu-leading-icon')).toBeInTheDocument()
+ await expect.element(submenu.getByTestId('submenu-trailing-icon')).toBeInTheDocument()
})
it('should not trigger disabled button', () => {
@@ -77,13 +80,13 @@ describe('ActionBar', () => {
,
)
- const button = screen.getByRole('button')
- button.click()
+ const button = page.getByRole('button')
+ fireEvent.click(button.element())
expect(onClick).not.toHaveBeenCalled()
})
- it('should trigger non-disabled button', () => {
+ it('should trigger non-disabled button', async () => {
const onClick = vi.fn()
render(
@@ -91,14 +94,13 @@ describe('ActionBar', () => {
,
)
- const button = screen.getByRole('button')
- button.click()
+ const button = page.getByRole('button')
+ await userEvent.click(button)
expect(onClick).toHaveBeenCalled()
})
it('should not trigger disabled button with spacebar or enter', async () => {
- const user = userEvent.setup()
const onClick = vi.fn()
render(
@@ -106,19 +108,18 @@ describe('ActionBar', () => {
,
)
- const button = screen.getByRole('button')
+ const button = page.getByRole('button')
act(() => {
- button.focus()
+ button.element().focus()
})
- await user.keyboard('{Enter}')
+ await userEvent.keyboard('{Enter}')
expect(onClick).not.toHaveBeenCalled()
})
it('should trigger non-disabled button with spacebar or enter', async () => {
- const user = userEvent.setup()
const onClick = vi.fn()
render(
@@ -126,13 +127,13 @@ describe('ActionBar', () => {
,
)
- const button = screen.getByRole('button')
+ const button = page.getByRole('button')
act(() => {
- button.focus()
+ button.element().focus()
})
- await user.keyboard('{Enter}')
+ await userEvent.keyboard('{Enter}')
expect(onClick).toHaveBeenCalled()
})
@@ -143,17 +144,17 @@ describe('ActionBar.Button', () => {
vi.clearAllMocks()
})
- it('renders a text button with its children as the accessible name', () => {
+ it('renders a text button with its children as the accessible name', async () => {
render(
Save
,
)
- expect(screen.getByRole('button', {name: 'Save'})).toBeInTheDocument()
+ await expect.element(page.getByRole('button', {name: 'Save', exact: true})).toBeInTheDocument()
})
- it('should trigger non-disabled button', () => {
+ it('should trigger non-disabled button', async () => {
const onClick = vi.fn()
render(
@@ -161,7 +162,7 @@ describe('ActionBar.Button', () => {
,
)
- screen.getByRole('button', {name: 'Save'}).click()
+ await userEvent.click(page.getByRole('button', {name: 'Save', exact: true}))
expect(onClick).toHaveBeenCalled()
})
@@ -176,13 +177,12 @@ describe('ActionBar.Button', () => {
,
)
- screen.getByRole('button', {name: 'Save'}).click()
+ fireEvent.click(page.getByRole('button', {name: 'Save', exact: true}).element())
expect(onClick).not.toHaveBeenCalled()
})
it('should not trigger disabled button with spacebar or enter', async () => {
- const user = userEvent.setup()
const onClick = vi.fn()
render(
@@ -192,20 +192,20 @@ describe('ActionBar.Button', () => {
,
)
- const button = screen.getByRole('button', {name: 'Save'})
+ const button = page.getByRole('button', {name: 'Save', exact: true})
act(() => {
- button.focus()
+ button.element().focus()
})
- await user.keyboard('{Enter}')
+ await userEvent.keyboard('{Enter}')
expect(onClick).not.toHaveBeenCalled()
})
})
describe('ActionBar Registry System', () => {
- it('should preserve order with deep nesting', () => {
+ it('should preserve order with deep nesting', async () => {
render(
@@ -218,14 +218,14 @@ describe('ActionBar Registry System', () => {
,
)
- const buttons = screen.getAllByRole('button')
+ const buttons = page.getByRole('button').all()
expect(buttons).toHaveLength(3)
- expect(buttons[0]).toHaveAccessibleName('First')
- expect(buttons[1]).toHaveAccessibleName('Second')
- expect(buttons[2]).toHaveAccessibleName('Third')
+ await expect.element(buttons[0]).toHaveAccessibleName('First')
+ await expect.element(buttons[1]).toHaveAccessibleName('Second')
+ await expect.element(buttons[2]).toHaveAccessibleName('Third')
})
- it('should preserve group order with deep nesting', () => {
+ it('should preserve group order with deep nesting', async () => {
render(
@@ -244,11 +244,11 @@ describe('ActionBar Registry System', () => {
,
)
- const buttons = screen.getAllByRole('button')
+ const buttons = page.getByRole('button').all()
expect(buttons).toHaveLength(3)
- expect(buttons[0]).toHaveAccessibleName('First')
- expect(buttons[1]).toHaveAccessibleName('Second')
- expect(buttons[2]).toHaveAccessibleName('Third')
+ await expect.element(buttons[0]).toHaveAccessibleName('First')
+ await expect.element(buttons[1]).toHaveAccessibleName('Second')
+ await expect.element(buttons[2]).toHaveAccessibleName('Third')
})
it('should handle conditional rendering without breaking order', async () => {
@@ -274,31 +274,30 @@ describe('ActionBar Registry System', () => {
)
}
- const user = userEvent.setup()
render(
)
// Initially should have 3 buttons
- expect(screen.getAllByRole('button', {name: /First|Second|Third/})).toHaveLength(3)
+ expect(page.getByRole('button', {name: /First|Second|Third/}).all()).toHaveLength(3)
// Hide first button
- await user.click(screen.getByText('Hide first'))
+ await userEvent.click(page.getByText('Hide first', {exact: true}))
- const buttonsAfterHide = screen.getAllByRole('button', {name: /Second|Third/})
+ const buttonsAfterHide = page.getByRole('button', {name: /Second|Third/}).all()
expect(buttonsAfterHide).toHaveLength(2)
- expect(buttonsAfterHide[0]).toHaveAccessibleName('Second')
- expect(buttonsAfterHide[1]).toHaveAccessibleName('Third')
+ await expect.element(buttonsAfterHide[0]).toHaveAccessibleName('Second')
+ await expect.element(buttonsAfterHide[1]).toHaveAccessibleName('Third')
// Show first button again
- await user.click(screen.getByText('Show all'))
+ await userEvent.click(page.getByText('Show all', {exact: true}))
- const buttonsAfterShow = screen.getAllByRole('button', {name: /First|Second|Third/})
+ const buttonsAfterShow = page.getByRole('button', {name: /First|Second|Third/}).all()
expect(buttonsAfterShow).toHaveLength(3)
- expect(buttonsAfterShow[0]).toHaveAccessibleName('First')
- expect(buttonsAfterShow[1]).toHaveAccessibleName('Second')
- expect(buttonsAfterShow[2]).toHaveAccessibleName('Third')
+ await expect.element(buttonsAfterShow[0]).toHaveAccessibleName('First')
+ await expect.element(buttonsAfterShow[1]).toHaveAccessibleName('Second')
+ await expect.element(buttonsAfterShow[2]).toHaveAccessibleName('Third')
})
- it('should handle fragments and array mapping', () => {
+ it('should handle fragments and array mapping', async () => {
render(
<>
@@ -311,12 +310,12 @@ describe('ActionBar Registry System', () => {
,
)
- const buttons = screen.getAllByRole('button')
+ const buttons = page.getByRole('button').all()
expect(buttons).toHaveLength(4)
- expect(buttons[0]).toHaveAccessibleName('In fragment')
- expect(buttons[1]).toHaveAccessibleName('Mapped 1')
- expect(buttons[2]).toHaveAccessibleName('Mapped 2')
- expect(buttons[3]).toHaveAccessibleName('After fragment')
+ await expect.element(buttons[0]).toHaveAccessibleName('In fragment')
+ await expect.element(buttons[1]).toHaveAccessibleName('Mapped 1')
+ await expect.element(buttons[2]).toHaveAccessibleName('Mapped 2')
+ await expect.element(buttons[3]).toHaveAccessibleName('After fragment')
})
it('should handle rapid re-renders without losing registry data', async () => {
@@ -335,18 +334,17 @@ describe('ActionBar Registry System', () => {
)
}
- const user = userEvent.setup()
render(
)
// Rapidly trigger re-renders
for (let i = 0; i < 10; i++) {
- await user.click(screen.getByText('Increment'))
+ await userEvent.click(page.getByText('Increment', {exact: true}))
}
- expect(screen.getByRole('button', {name: 'Button 10'})).toBeInTheDocument()
+ await expect.element(page.getByRole('button', {name: 'Button 10', exact: true})).toBeInTheDocument()
})
- it('should handle zero-width scenarios gracefully', () => {
+ it('should handle zero-width scenarios gracefully', async () => {
render(
@@ -357,7 +355,7 @@ describe('ActionBar Registry System', () => {
// Component should still render even with zero width
// Button is unlabeled because the label is hidden, so we select by test id instead
- expect(screen.getByTestId('zero-width-button')).toBeInTheDocument()
+ await expect.element(page.getByTestId('zero-width-button')).toBeInTheDocument()
})
it('should clean up registry on unmount', async () => {
@@ -378,54 +376,53 @@ describe('ActionBar Registry System', () => {
)
}
- const user = userEvent.setup()
render()
- expect(screen.getByRole('button', {name: 'Will unmount'})).toBeInTheDocument()
+ await expect.element(page.getByRole('button', {name: 'Will unmount', exact: true})).toBeInTheDocument()
- await user.click(screen.getByText('Unmount'))
+ await userEvent.click(page.getByText('Unmount', {exact: true}))
- expect(screen.queryByRole('button', {name: 'Will unmount'})).not.toBeInTheDocument()
+ await expect.element(page.getByRole('button', {name: 'Will unmount', exact: true})).not.toBeInTheDocument()
})
})
describe('ActionBar gap prop', () => {
- it('defaults to condensed', () => {
+ it('defaults to condensed', async () => {
render(
,
)
- const toolbar = screen.getByRole('toolbar')
- expect(toolbar).toHaveAttribute('data-gap', 'condensed')
+ const toolbar = page.getByRole('toolbar')
+ await expect.element(toolbar).toHaveAttribute('data-gap', 'condensed')
})
- it('applies provided gap scale (none)', () => {
+ it('applies provided gap scale (none)', async () => {
render(
,
)
- const toolbar = screen.getByRole('toolbar')
- expect(toolbar).toHaveAttribute('data-gap', 'none')
+ const toolbar = page.getByRole('toolbar')
+ await expect.element(toolbar).toHaveAttribute('data-gap', 'none')
})
- it('applies provided gap scale (condensed)', () => {
+ it('applies provided gap scale (condensed)', async () => {
render(
,
)
- const toolbar = screen.getByRole('toolbar')
- expect(toolbar).toHaveAttribute('data-gap', 'condensed')
+ const toolbar = page.getByRole('toolbar')
+ await expect.element(toolbar).toHaveAttribute('data-gap', 'condensed')
})
})
describe('ActionBar.Menu returnFocusRef', () => {
- it('accepts returnFocusRef prop', () => {
+ it('accepts returnFocusRef prop', async () => {
const returnFocusRef = createRef()
render(
@@ -443,11 +440,10 @@ describe('ActionBar.Menu returnFocusRef', () => {
,
)
- expect(screen.getByRole('button', {name: 'More options'})).toBeInTheDocument()
+ await expect.element(page.getByRole('button', {name: 'More options', exact: true})).toBeInTheDocument()
})
it('returns focus to returnFocusRef when menu is closed', async () => {
- const user = userEvent.setup()
const returnFocusRef = createRef()
render(
@@ -466,24 +462,23 @@ describe('ActionBar.Menu returnFocusRef', () => {
,
)
- const menuButton = screen.getByRole('button', {name: 'More options'})
+ const menuButton = page.getByRole('button', {name: 'More options', exact: true})
// Open the menu
- await user.click(menuButton)
+ await userEvent.click(menuButton)
// Verify menu is open
- expect(screen.getByRole('menu')).toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
// Close the menu by pressing Escape
- await user.keyboard('{Escape}')
+ await userEvent.keyboard('{Escape}')
// Verify focus is returned to the returnFocusRef element
- const returnFocusTarget = screen.getByTestId('return-focus-target')
- expect(document.activeElement).toEqual(returnFocusTarget)
+ const returnFocusTarget = page.getByTestId('return-focus-target')
+ await expect.element(returnFocusTarget).toHaveFocus()
})
it('returns focus to returnFocusRef when menu item is selected', async () => {
- const user = userEvent.setup()
const returnFocusRef = createRef
()
const onClick = vi.fn()
@@ -503,41 +498,39 @@ describe('ActionBar.Menu returnFocusRef', () => {
,
)
- const menuButton = screen.getByRole('button', {name: 'More options'})
+ const menuButton = page.getByRole('button', {name: 'More options', exact: true})
// Open the menu
- await user.click(menuButton)
+ await userEvent.click(menuButton)
// Click a menu item
- await user.click(screen.getByRole('menuitem', {name: 'Option 1'}))
+ await userEvent.click(page.getByRole('menuitem', {name: 'Option 1', exact: true}))
// Verify focus is returned to the returnFocusRef element
- const returnFocusTarget = screen.getByTestId('return-focus-target')
- expect(document.activeElement).toEqual(returnFocusTarget)
+ const returnFocusTarget = page.getByTestId('return-focus-target')
+ await expect.element(returnFocusTarget).toHaveFocus()
})
it('returns focus to anchor button when returnFocusRef is not provided', async () => {
- const user = userEvent.setup()
-
render(
,
)
- const menuButton = screen.getByRole('button', {name: 'More options'})
+ const menuButton = page.getByRole('button', {name: 'More options', exact: true})
// Open the menu
- await user.click(menuButton)
+ await userEvent.click(menuButton)
// Verify menu is open
- expect(screen.getByRole('menu')).toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
// Close the menu by pressing Escape
- await user.keyboard('{Escape}')
+ await userEvent.keyboard('{Escape}')
// Verify focus returns to the menu button (default behavior)
- expect(document.activeElement).toEqual(menuButton)
+ await expect.element(menuButton).toHaveFocus()
})
})
@@ -564,15 +557,15 @@ describe('ActionBar data-component attributes', () => {
expect(iconButton).toBeInTheDocument()
})
- it('renders ActionBar.Button with a text label', () => {
+ it('renders ActionBar.Button with a text label', async () => {
render(
Save
,
)
- const button = screen.getByRole('button', {name: 'Save'})
- expect(button).toBeInTheDocument()
+ const button = page.getByRole('button', {name: 'Save', exact: true})
+ await expect.element(button).toBeInTheDocument()
})
it('renders ActionBar.VerticalDivider with data-component attribute', () => {
@@ -602,14 +595,14 @@ describe('ActionBar data-component attributes', () => {
expect(group).toBeInTheDocument()
})
- it('renders ActionBar.Menu.IconButton with data-component attribute', () => {
+ it('renders ActionBar.Menu.IconButton with data-component attribute', async () => {
render(
,
)
- const menuButton = screen.getByRole('button', {name: 'More options'})
- expect(menuButton).toHaveAttribute('data-component', 'ActionBar.Menu.IconButton')
+ const menuButton = page.getByRole('button', {name: 'More options', exact: true})
+ await expect.element(menuButton).toHaveAttribute('data-component', 'ActionBar.Menu.IconButton')
})
})
diff --git a/packages/react/src/ActionList/ActionList.test.tsx b/packages/react/src/ActionList/ActionList.test.tsx
index e12b21c5bb6..74da5f666a1 100644
--- a/packages/react/src/ActionList/ActionList.test.tsx
+++ b/packages/react/src/ActionList/ActionList.test.tsx
@@ -1,6 +1,6 @@
+import {page, userEvent} from 'vitest/browser'
import {describe, it, expect, vi} from 'vitest'
-import {render as HTMLRender} from '@testing-library/react'
-import userEvent from '@testing-library/user-event'
+import {render as HTMLRender, act, fireEvent} from '@testing-library/react'
import {ActionList} from '.'
import {createRef} from 'react'
import {ActionListContainerContext} from './ActionListContainerContext'
@@ -50,23 +50,25 @@ describe('ActionList', () => {
)
await userEvent.tab() // tab into the story, this should focus on the first button
- expect(document.activeElement).toHaveTextContent('Option 1')
+ await expect.element(page.getByRole('option', {name: 'Option 1', exact: true})).toHaveFocus()
await userEvent.keyboard('{ArrowDown}')
- expect(document.activeElement).toHaveTextContent('Option 2')
+ await expect.element(page.getByRole('option', {name: 'Option 2', exact: true})).toHaveFocus()
await userEvent.keyboard('{ArrowDown}')
- expect(document.activeElement).toHaveTextContent('Option 3')
+ await expect.element(page.getByRole('option', {name: 'Option 3', exact: true})).toHaveFocus()
await userEvent.keyboard('{ArrowDown}')
- expect(document.activeElement).toHaveTextContent('Option 4')
+ await expect.element(page.getByRole('option', {name: 'Option 4', exact: true})).toHaveFocus()
await userEvent.keyboard('{ArrowDown}')
- expect(document.activeElement).toHaveAccessibleName('Option 5')
- expect(document.activeElement).toHaveAccessibleDescription('Unavailable due to an outage')
+ const inactiveOption = page.getByRole('option', {name: 'Option 5', exact: true})
+ await expect.element(inactiveOption).toHaveFocus()
+ await expect.element(inactiveOption).toHaveAccessibleName('Option 5')
+ await expect.element(inactiveOption).toHaveAccessibleDescription('Unavailable due to an outage')
await userEvent.keyboard('{ArrowUp}')
- expect(document.activeElement).toHaveTextContent('Option 4')
+ await expect.element(page.getByRole('option', {name: 'Option 4', exact: true})).toHaveFocus()
})
it('divider should support a custom `className`', () => {
@@ -139,10 +141,10 @@ describe('ActionList', () => {
)
await userEvent.tab() // tab into the story, this should focus on the first button
- expect(document.activeElement).toHaveTextContent('Option 1')
+ await expect.element(page.getByRole('option', {name: 'Option 1', exact: true})).toHaveFocus()
await userEvent.keyboard('{ArrowDown}')
- expect(document.activeElement).toHaveTextContent('Option 1')
+ await expect.element(page.getByRole('option', {name: 'Option 1', exact: true})).toHaveFocus()
expect(container.querySelector('li[aria-disabled="true"]')?.nextElementSibling).toHaveTextContent('Option 4')
expect(container.querySelector('li[aria-disabled="true"]')?.nextElementSibling).toHaveAttribute('tabindex', '0')
@@ -540,7 +542,7 @@ describe('ActionList with role="tree"', () => {
,
)
- const item = document.querySelector('[role="treeitem"]')!
+ const item = page.getByRole('treeitem')
await userEvent.click(item)
expect(onSelect).toHaveBeenCalledTimes(1)
})
@@ -555,8 +557,10 @@ describe('ActionList with role="tree"', () => {
,
)
- const item = document.querySelector('[role="treeitem"]')!
- ;(item as HTMLElement).focus()
+ const item = page.getByRole('treeitem')
+ act(() => {
+ item.element().focus()
+ })
await userEvent.keyboard('{Enter}')
expect(onSelect).toHaveBeenCalledTimes(1)
})
@@ -571,8 +575,8 @@ describe('ActionList with role="tree"', () => {
,
)
- const item = document.querySelector('[role="treeitem"]')!
- await userEvent.click(item)
+ const item = page.getByRole('treeitem')
+ fireEvent.click(item.element())
expect(onSelect).not.toHaveBeenCalled()
})
diff --git a/packages/react/src/ActionList/Description.test.tsx b/packages/react/src/ActionList/Description.test.tsx
index 5312364912e..2f92d1ac41a 100644
--- a/packages/react/src/ActionList/Description.test.tsx
+++ b/packages/react/src/ActionList/Description.test.tsx
@@ -1,3 +1,4 @@
+import {page} from 'vitest/browser'
import {expect, it, describe} from 'vitest'
import {render as HTMLRender} from '@testing-library/react'
import {ActionList} from '.'
@@ -6,8 +7,8 @@ import classes from './ActionList.module.css'
describe('ActionList.Description', () => {
implementsClassName(ActionList.Description, classes.Description)
- it('should render the description as inline without truncation by default', () => {
- const {getByText} = HTMLRender(
+ it('should render the description as inline without truncation by default', async () => {
+ HTMLRender(
Item 1Item 1 description
@@ -15,14 +16,14 @@ describe('ActionList.Description', () => {
,
)
- const description = getByText('Item 1 description')
- expect(description.tagName).toBe('SPAN')
- expect(description).toHaveStyle('flex-basis: auto')
- expect(description).not.toHaveStyle('overflow: ellipsis')
- expect(description).not.toHaveStyle('white-space: nowrap')
+ const description = page.getByText('Item 1 description', {exact: true})
+ expect(description.element().tagName).toBe('SPAN')
+ await expect.element(description).toHaveStyle('flex-basis: auto')
+ await expect.element(description).not.toHaveStyle('overflow: ellipsis')
+ await expect.element(description).not.toHaveStyle('white-space: nowrap')
})
- it('should render the description as `Truncate` when truncate is true', () => {
- const {getByText} = HTMLRender(
+ it('should render the description as `Truncate` when truncate is true', async () => {
+ HTMLRender(
Item 1Item 1 description
@@ -30,18 +31,18 @@ describe('ActionList.Description', () => {
,
)
- const description = getByText('Item 1 description')
- expect(description.tagName).toBe('DIV')
+ const description = page.getByText('Item 1 description', {exact: true})
+ expect(description.element().tagName).toBe('DIV')
// For button-semantic items, the native title is suppressed in favor of
// a keyboard-accessible Tooltip rendered by the parent Item.
- expect(description).toHaveAttribute('title', '')
- expect(description).toHaveStyle('flex-basis: auto')
- expect(description).toHaveStyle('text-overflow: ellipsis')
- expect(description).toHaveStyle('overflow: hidden')
- expect(description).toHaveStyle('white-space: nowrap')
+ await expect.element(description).toHaveAttribute('title', '')
+ await expect.element(description).toHaveStyle('flex-basis: auto')
+ await expect.element(description).toHaveStyle('text-overflow: ellipsis')
+ await expect.element(description).toHaveStyle('overflow: hidden')
+ await expect.element(description).toHaveStyle('white-space: nowrap')
})
it('should render the description in a new line when variant is block', () => {
- const {getByText} = HTMLRender(
+ HTMLRender(
Item 1Item 1 description
@@ -49,9 +50,9 @@ describe('ActionList.Description', () => {
,
)
- const description = getByText('Item 1 description')
- expect(description.tagName).toBe('SPAN')
- expect(description.parentElement?.parentElement).toHaveAttribute(
+ const description = page.getByText('Item 1 description', {exact: true})
+ expect(description.element().tagName).toBe('SPAN')
+ expect(description.element().parentElement?.parentElement).toHaveAttribute(
'data-component',
'ActionList.Item--DividerContainer',
)
diff --git a/packages/react/src/ActionList/Group.test.tsx b/packages/react/src/ActionList/Group.test.tsx
index 46d43d28923..034c26f8a98 100644
--- a/packages/react/src/ActionList/Group.test.tsx
+++ b/packages/react/src/ActionList/Group.test.tsx
@@ -1,3 +1,4 @@
+import {page} from 'vitest/browser'
import {describe, it, expect} from 'vitest'
import {render as HTMLRender} from '@testing-library/react'
import {PlusIcon} from '@primer/octicons-react'
@@ -45,7 +46,7 @@ describe('ActionList.Group', () => {
})
it('should render the ActionList.GroupHeading component as a heading with the given heading level', async () => {
- const container = HTMLRender(
+ HTMLRender(
Heading
@@ -53,9 +54,9 @@ describe('ActionList.Group', () => {
,
)
- const heading = container.getByRole('heading', {level: 2})
- expect(heading).toBeInTheDocument()
- expect(heading).toHaveTextContent('Group Heading')
+ const heading = page.getByRole('heading', {level: 2})
+ await expect.element(heading).toBeInTheDocument()
+ await expect.element(heading).toHaveTextContent('Group Heading')
})
it('should throw an error if ActionList.GroupHeading is used without an `as` prop when no role is specified (for list role)', async () => {
withExpectedConsoleError(() => {
@@ -75,7 +76,7 @@ describe('ActionList.Group', () => {
})
})
it('should render the ActionList.GroupHeading component as a span (not a heading tag) when role is specified as listbox', async () => {
- const container = HTMLRender(
+ HTMLRender(
Heading
@@ -83,12 +84,12 @@ describe('ActionList.Group', () => {
,
)
- const label = container.getByText('Group Heading')
- expect(label).toBeInTheDocument()
- expect(label.tagName).toEqual('SPAN')
+ const label = page.getByText('Group Heading', {exact: true})
+ await expect.element(label).toBeInTheDocument()
+ expect(label.element().tagName).toEqual('SPAN')
})
it('should render the ActionList.GroupHeading component as a span with role="presentation" and aria-hidden="true" when role is specified as listbox', async () => {
- const container = HTMLRender(
+ HTMLRender(
Heading
@@ -96,13 +97,13 @@ describe('ActionList.Group', () => {
,
)
- const label = container.getByText('Group Heading')
- const wrapper = label.parentElement
+ const label = page.getByText('Group Heading', {exact: true})
+ const wrapper = label.element().parentElement
expect(wrapper).toHaveAttribute('role', 'presentation')
expect(wrapper).toHaveAttribute('aria-hidden', 'true')
})
it('should label the list with the group heading id', async () => {
- const {container, getByText} = HTMLRender(
+ const {container} = HTMLRender(
Heading
@@ -112,11 +113,11 @@ describe('ActionList.Group', () => {
,
)
const list = container.querySelector(`li[data-test-id='ActionList.Group'] > ul`)
- const heading = getByText('Group Heading')
- expect(list).toHaveAttribute('aria-labelledby', heading.id)
+ const heading = page.getByText('Group Heading', {exact: true})
+ expect(list).toHaveAttribute('aria-labelledby', heading.element().id)
})
it('should NOT label the list with the group heading id when role is specified', async () => {
- const {container, getByText} = HTMLRender(
+ const {container} = HTMLRender(
Heading
@@ -126,8 +127,8 @@ describe('ActionList.Group', () => {
,
)
const list = container.querySelector(`li[data-test-id='ActionList.Group'] > ul`)
- const heading = getByText('Group Heading')
- expect(list).not.toHaveAttribute('aria-labelledby', heading.id)
+ const heading = page.getByText('Group Heading', {exact: true})
+ expect(list).not.toHaveAttribute('aria-labelledby', heading.element().id)
})
it('should label the list with aria-label if it is specified', async () => {
@@ -144,8 +145,8 @@ describe('ActionList.Group', () => {
})
describe('GroupHeading.TrailingAction (behind feature flag)', () => {
- it('renders GroupHeading.TrailingAction as a sibling of the heading element when the feature flag is enabled', () => {
- const {getByRole} = HTMLRender(
+ it('renders GroupHeading.TrailingAction as a sibling of the heading element when the feature flag is enabled', async () => {
+ HTMLRender(
Heading
@@ -160,20 +161,20 @@ describe('ActionList.Group', () => {
,
)
- const heading = getByRole('heading', {level: 2})
- const button = getByRole('button', {name: 'New field'})
+ const heading = page.getByRole('heading', {level: 2})
+ const button = page.getByRole('button', {name: 'New field', exact: true})
// The button must NOT be inside the heading element
- expect(heading).not.toContainElement(button)
+ await expect.element(heading).not.toContainElement(button.element())
// The heading text should not include the button's accessible name
- expect(heading).toHaveTextContent('Group Heading')
- expect(heading.textContent).not.toContain('New field')
+ await expect.element(heading).toHaveTextContent('Group Heading')
+ expect(heading.element().textContent).not.toContain('New field')
// The button should be inside the same wrapper as the heading
- expect(heading.parentElement).toContainElement(button)
+ expect(heading.element().parentElement).toContainElement(button.element())
})
- it('passes GroupHeading.TrailingAction through as a child of the heading when the feature flag is disabled', () => {
- const {getByRole} = HTMLRender(
+ it('passes GroupHeading.TrailingAction through as a child of the heading when the feature flag is disabled', async () => {
+ HTMLRender(
Heading
@@ -186,12 +187,12 @@ describe('ActionList.Group', () => {
,
)
- const heading = getByRole('heading', {level: 2})
- const button = getByRole('button', {name: 'New field'})
+ const heading = page.getByRole('heading', {level: 2})
+ const button = page.getByRole('button', {name: 'New field', exact: true})
// Without the flag, the slot is not consumed: the button passes
// through and still renders inside the heading element.
- expect(heading).toContainElement(button)
+ await expect.element(heading).toContainElement(button.element())
})
it('throws when GroupHeading.TrailingAction is used inside an ActionMenu (menu role) and the feature flag is enabled', () => {
diff --git a/packages/react/src/ActionList/Heading.test.tsx b/packages/react/src/ActionList/Heading.test.tsx
index 2bd6b5f72c5..a4b98765384 100644
--- a/packages/react/src/ActionList/Heading.test.tsx
+++ b/packages/react/src/ActionList/Heading.test.tsx
@@ -1,3 +1,4 @@
+import {page} from 'vitest/browser'
import {describe, it, expect} from 'vitest'
import {render as HTMLRender} from '@testing-library/react'
import BaseStyles from '../BaseStyles'
@@ -20,61 +21,61 @@ describe('ActionList.Heading', () => {
)
it('should render the ActionList.Heading component as a heading with the given heading level', async () => {
- const container = HTMLRender(
+ HTMLRender(
Heading
,
)
- const heading = container.getByRole('heading', {level: 1})
- expect(heading).toBeInTheDocument()
- expect(heading).toHaveTextContent('Heading')
+ const heading = page.getByRole('heading', {level: 1})
+ await expect.element(heading).toBeInTheDocument()
+ await expect.element(heading).toHaveTextContent('Heading')
})
it('should not wrap the heading in a span', async () => {
- const {getByRole} = HTMLRender(
+ HTMLRender(
Heading
,
)
- const heading = getByRole('heading', {level: 1})
- expect(heading.parentElement?.tagName).not.toBe('SPAN')
- expect(heading).toHaveClass(classes.ActionListHeader)
+ const heading = page.getByRole('heading', {level: 1})
+ expect(heading.element().parentElement?.tagName).not.toBe('SPAN')
+ await expect.element(heading).toHaveClass(classes.ActionListHeader)
})
it('should apply the visually-hidden class to the heading when visuallyHidden is set', async () => {
- const {getByRole} = HTMLRender(
+ HTMLRender(
Heading
,
)
- const heading = getByRole('heading', {level: 1})
- expect(heading).toHaveClass(visuallyHiddenClasses.InternalVisuallyHidden)
- expect(heading).toHaveClass(classes.ActionListHeader)
+ const heading = page.getByRole('heading', {level: 1})
+ await expect.element(heading).toHaveClass(visuallyHiddenClasses.InternalVisuallyHidden)
+ await expect.element(heading).toHaveClass(classes.ActionListHeader)
})
it('should not apply the visually-hidden class to the heading by default', async () => {
- const {getByRole} = HTMLRender(
+ HTMLRender(
Heading
,
)
- const heading = getByRole('heading', {level: 1})
- expect(heading).not.toHaveClass(visuallyHiddenClasses.InternalVisuallyHidden)
- expect(heading).toHaveClass(classes.ActionListHeader)
+ const heading = page.getByRole('heading', {level: 1})
+ await expect.element(heading).not.toHaveClass(visuallyHiddenClasses.InternalVisuallyHidden)
+ await expect.element(heading).toHaveClass(classes.ActionListHeader)
})
it('should label the action list with the heading id', async () => {
- const {container, getByRole} = HTMLRender(
+ const {container} = HTMLRender(
Heading
Item
,
)
const list = container.querySelector('ul')
- const heading = getByRole('heading', {level: 1})
- expect(list).toHaveAttribute('aria-labelledby', heading.id)
+ const heading = page.getByRole('heading', {level: 1})
+ expect(list).toHaveAttribute('aria-labelledby', heading.element().id)
})
it('should throw an error when ActionList.Heading is used within ActionMenu context', async () => {
diff --git a/packages/react/src/ActionList/Item.test.tsx b/packages/react/src/ActionList/Item.test.tsx
index 0a34bacba76..83af185a381 100644
--- a/packages/react/src/ActionList/Item.test.tsx
+++ b/packages/react/src/ActionList/Item.test.tsx
@@ -1,6 +1,6 @@
+import {page, userEvent} from 'vitest/browser'
import {describe, it, expect, vi} from 'vitest'
-import {render as HTMLRender, waitFor, fireEvent} from '@testing-library/react'
-import userEvent from '@testing-library/user-event'
+import {render as HTMLRender, fireEvent, act} from '@testing-library/react'
import React, {type JSX} from 'react'
import {ActionList} from '.'
import {AnchoredOverlay} from '../AnchoredOverlay'
@@ -67,102 +67,113 @@ describe('ActionList.Item', () => {
implementsClassName(ActionList.LinkItem)
it('should have aria-keyshortcuts applied to the correct element', async () => {
const {container} = HTMLRender()
- const linkOptions = await waitFor(() => container.querySelectorAll('a'))
+ const linkOptions = container.querySelectorAll('a')
expect(linkOptions[0]).toHaveAttribute('aria-keyshortcuts', 'd')
expect(linkOptions[0].parentElement).not.toHaveAttribute('aria-keyshortcuts', 'd')
})
it('should fire onSelect on click and keypress', async () => {
- const component = HTMLRender()
- const options = await waitFor(() => component.getAllByRole('option'))
- expect(options[0]).toHaveAttribute('aria-selected', 'true')
- expect(options[1]).toHaveAttribute('aria-selected', 'false')
- fireEvent.click(options[1])
- expect(options[0]).toHaveAttribute('aria-selected', 'false')
- expect(options[1]).toHaveAttribute('aria-selected', 'true')
- // We pass keycode here to navigate a implementation detail in react-testing-library
- // https://github.com/testing-library/react-testing-library/issues/269#issuecomment-455854112
- fireEvent.keyPress(options[0], {key: 'Enter', charCode: 13})
- expect(options[0]).toHaveAttribute('aria-selected', 'true')
- expect(options[1]).toHaveAttribute('aria-selected', 'false')
- fireEvent.keyPress(options[1], {key: ' ', charCode: 32})
- expect(options[0]).toHaveAttribute('aria-selected', 'false')
- expect(options[1]).toHaveAttribute('aria-selected', 'true')
+ HTMLRender()
+ const options = page.getByRole('option').all()
+ await expect.element(options[0]).toHaveAttribute('aria-selected', 'true')
+ await expect.element(options[1]).toHaveAttribute('aria-selected', 'false')
+ await userEvent.click(options[1])
+ await expect.element(options[0]).toHaveAttribute('aria-selected', 'false')
+ await expect.element(options[1]).toHaveAttribute('aria-selected', 'true')
+ act(() => {
+ options[0].element().focus()
+ })
+ await userEvent.keyboard('{Enter}')
+ await expect.element(options[0]).toHaveAttribute('aria-selected', 'true')
+ await expect.element(options[1]).toHaveAttribute('aria-selected', 'false')
+ act(() => {
+ options[1].element().focus()
+ })
+ await userEvent.keyboard(' ')
+ await expect.element(options[0]).toHaveAttribute('aria-selected', 'false')
+ await expect.element(options[1]).toHaveAttribute('aria-selected', 'true')
})
it('should skip onSelect on disabled items', async () => {
- const component = HTMLRender()
- const options = await waitFor(() => component.getAllByRole('option'))
- expect(options[0]).toHaveAttribute('aria-selected', 'true')
- expect(options[2]).toHaveAttribute('aria-selected', 'false')
- fireEvent.click(options[2])
- expect(options[0]).toHaveAttribute('aria-selected', 'true')
- expect(options[2]).toHaveAttribute('aria-selected', 'false')
- fireEvent.keyPress(options[2], {key: 'Enter', charCode: 13})
- expect(options[0]).toHaveAttribute('aria-selected', 'true')
- expect(options[2]).toHaveAttribute('aria-selected', 'false')
+ HTMLRender()
+ const options = page.getByRole('option').all()
+ await expect.element(options[0]).toHaveAttribute('aria-selected', 'true')
+ await expect.element(options[2]).toHaveAttribute('aria-selected', 'false')
+ fireEvent.click(options[2].element())
+ await expect.element(options[0]).toHaveAttribute('aria-selected', 'true')
+ await expect.element(options[2]).toHaveAttribute('aria-selected', 'false')
+ act(() => {
+ options[2].element().focus()
+ })
+ await userEvent.keyboard('{Enter}')
+ await expect.element(options[0]).toHaveAttribute('aria-selected', 'true')
+ await expect.element(options[2]).toHaveAttribute('aria-selected', 'false')
})
it('should skip onSelect on inactive items', async () => {
- const component = HTMLRender()
- const options = await waitFor(() => component.getAllByRole('option'))
- expect(options[0]).toHaveAttribute('aria-selected', 'true')
- expect(options[3]).toHaveAttribute('aria-selected', 'false')
- fireEvent.click(options[3])
- expect(options[0]).toHaveAttribute('aria-selected', 'true')
- expect(options[3]).toHaveAttribute('aria-selected', 'false')
- fireEvent.keyPress(options[3], {key: 'Enter', charCode: 13})
- expect(options[0]).toHaveAttribute('aria-selected', 'true')
- expect(options[3]).toHaveAttribute('aria-selected', 'false')
+ HTMLRender()
+ const options = page.getByRole('option').all()
+ await expect.element(options[0]).toHaveAttribute('aria-selected', 'true')
+ await expect.element(options[3]).toHaveAttribute('aria-selected', 'false')
+ fireEvent.click(options[3].element())
+ await expect.element(options[0]).toHaveAttribute('aria-selected', 'true')
+ await expect.element(options[3]).toHaveAttribute('aria-selected', 'false')
+ act(() => {
+ options[3].element().focus()
+ })
+ await userEvent.keyboard('{Enter}')
+ await expect.element(options[0]).toHaveAttribute('aria-selected', 'true')
+ await expect.element(options[3]).toHaveAttribute('aria-selected', 'false')
})
it('should skip onSelect on loading items', async () => {
- const component = HTMLRender()
- const options = await waitFor(() => component.getAllByRole('option'))
- expect(options[0]).toHaveAttribute('aria-selected', 'true')
- expect(options[4]).toHaveAttribute('aria-selected', 'false')
- fireEvent.click(options[4])
- expect(options[0]).toHaveAttribute('aria-selected', 'true')
- expect(options[4]).toHaveAttribute('aria-selected', 'false')
- fireEvent.keyPress(options[3], {key: 'Enter', charCode: 13})
- expect(options[0]).toHaveAttribute('aria-selected', 'true')
- expect(options[4]).toHaveAttribute('aria-selected', 'false')
+ HTMLRender()
+ const options = page.getByRole('option').all()
+ await expect.element(options[0]).toHaveAttribute('aria-selected', 'true')
+ await expect.element(options[4]).toHaveAttribute('aria-selected', 'false')
+ fireEvent.click(options[4].element())
+ await expect.element(options[0]).toHaveAttribute('aria-selected', 'true')
+ await expect.element(options[4]).toHaveAttribute('aria-selected', 'false')
+ act(() => {
+ options[3].element().focus()
+ })
+ await userEvent.keyboard('{Enter}')
+ await expect.element(options[0]).toHaveAttribute('aria-selected', 'true')
+ await expect.element(options[4]).toHaveAttribute('aria-selected', 'false')
})
it('should not crash when clicking an item without an onSelect', async () => {
- const component = HTMLRender(
+ HTMLRender(
Primer React
,
)
- const option = await waitFor(() => component.getByRole('option'))
- expect(option).toBeInTheDocument()
- fireEvent.click(option)
- fireEvent.keyPress(option, {key: 'Enter', charCode: 13})
- expect(option).toBeInTheDocument()
+ const option = page.getByRole('option')
+ await expect.element(option).toBeInTheDocument()
+ await userEvent.click(option)
+ await userEvent.keyboard('{Enter}')
+ await expect.element(option).toBeInTheDocument()
})
it('should focus the button around the alert icon when tabbing to an inactive item', async () => {
- const component = HTMLRender()
- const inactiveIndicatorButton = await waitFor(() => component.getByRole('button', {name: 'Inactive item'}))
+ HTMLRender()
+ const inactiveIndicatorButton = page.getByRole('button', {name: 'Inactive item', exact: true})
await userEvent.tab()
await userEvent.tab()
await userEvent.tab()
await userEvent.tab()
await userEvent.tab()
await userEvent.tab() // focuses 6th element, which is inactive
- expect(inactiveIndicatorButton).toHaveFocus()
- expect(document.activeElement).toHaveAccessibleDescription('Unavailable due to an outage')
+ await expect.element(inactiveIndicatorButton).toHaveFocus()
+ await expect.element(inactiveIndicatorButton).toHaveAccessibleDescription('Unavailable due to an outage')
})
it('should focus the option or menu item when moving focus to an inactive item **in a listbox**', async () => {
- const component = HTMLRender()
- const inactiveOption = await waitFor(() => component.getByRole('option', {name: projects[3].name}))
+ HTMLRender()
+ const inactiveOption = page.getByRole('option', {name: projects[3].name})
await userEvent.tab() // get focus on first element
await userEvent.keyboard('{ArrowDown}')
await userEvent.keyboard('{ArrowDown}')
await userEvent.keyboard('{ArrowDown}')
- expect(inactiveOption).toHaveFocus()
- expect(document.activeElement).toHaveAccessibleDescription(projects[3].inactiveText as string)
+ await expect.element(inactiveOption).toHaveFocus()
+ await expect.element(inactiveOption).toHaveAccessibleDescription(projects[3].inactiveText as string)
})
it('should behave as inactive if both inactiveText and loading props are passed', async () => {
- const component = HTMLRender()
- const inactiveIndicatorButton = await waitFor(() =>
- component.getByRole('button', {name: 'Loading and inactive item'}),
- )
+ HTMLRender()
+ const inactiveIndicatorButton = page.getByRole('button', {name: 'Loading and inactive item', exact: true})
await userEvent.tab()
await userEvent.tab()
await userEvent.tab()
@@ -170,33 +181,33 @@ describe('ActionList.Item', () => {
await userEvent.tab()
await userEvent.tab()
await userEvent.tab() // focuses 7th element, which is inactive AND has a loading prop
- expect(inactiveIndicatorButton).toHaveFocus()
- expect(document.activeElement).toHaveAccessibleDescription('Unavailable due to an outage')
+ await expect.element(inactiveIndicatorButton).toHaveFocus()
+ await expect.element(inactiveIndicatorButton).toHaveAccessibleDescription('Unavailable due to an outage')
})
it('should behave as inactive if both inactiveText and loading props are passed **in a listbox**', async () => {
- const component = HTMLRender()
- const inactiveOption = await waitFor(() => component.getByRole('option', {name: projects[5].name}))
+ HTMLRender()
+ const inactiveOption = page.getByRole('option', {name: projects[5].name})
await userEvent.tab() // get focus on first element
await userEvent.keyboard('{ArrowDown}')
await userEvent.keyboard('{ArrowDown}')
await userEvent.keyboard('{ArrowDown}')
await userEvent.keyboard('{ArrowDown}')
await userEvent.keyboard('{ArrowDown}')
- expect(inactiveOption).toHaveFocus()
- expect(document.activeElement).toHaveAccessibleDescription(projects[5].inactiveText as string)
+ await expect.element(inactiveOption).toHaveFocus()
+ await expect.element(inactiveOption).toHaveAccessibleDescription(projects[5].inactiveText as string)
})
it('should call onClick for a link item', async () => {
const onClick = vi.fn()
- const component = HTMLRender(
+ HTMLRender(
Primer React
,
)
- const link = await waitFor(() => component.getByRole('link'))
- fireEvent.click(link)
+ const link = page.getByRole('link')
+ await userEvent.click(link)
expect(onClick).toHaveBeenCalled()
})
it('should render ActionList.Item as button', async () => {
@@ -207,9 +218,9 @@ describe('ActionList.Item', () => {
,
)
const button = container.querySelector('button')
- expect(button).toHaveTextContent('Item 1')
+ await expect.element(button).toHaveTextContent('Item 1')
// Ensure passed prop "disabled" is applied to the button
- expect(button).toHaveAttribute('aria-disabled', 'true')
+ await expect.element(button).toHaveAttribute('aria-disabled', 'true')
const listItems = container.querySelectorAll('li')
expect(listItems.length).toBe(2)
})
@@ -254,7 +265,7 @@ describe('ActionList.Item', () => {
})
it('should do action when trailing action is clicked', async () => {
const onClick = vi.fn()
- const component = HTMLRender(
+ HTMLRender(
Item 1
@@ -262,8 +273,8 @@ describe('ActionList.Item', () => {
,
)
- const trailingAction = await waitFor(() => component.getByRole('button', {name: 'Action'}))
- fireEvent.click(trailingAction)
+ const trailingAction = page.getByRole('button', {name: 'Action', exact: true})
+ await userEvent.click(trailingAction)
expect(onClick).toHaveBeenCalled()
})
it('should focus the trailing action', async () => {
@@ -276,26 +287,27 @@ describe('ActionList.Item', () => {
,
)
await userEvent.tab()
- expect(document.activeElement).toHaveTextContent('Item 1')
+ await expect.element(page.getByRole('button', {name: 'Item 1', exact: true})).toHaveFocus()
await userEvent.tab()
- expect(document.activeElement).toHaveAccessibleName('Action')
+ await expect.element(page.getByRole('button', {name: 'Action', exact: true})).toHaveFocus()
})
it('should only trigger a key event once', async () => {
const mockOnSelect = vi.fn()
- const user = userEvent.setup()
- const {getByRole} = HTMLRender(
+ HTMLRender(
Item 1
,
)
- const item = getByRole('button')
- item.focus()
- expect(document.activeElement).toBe(item)
- await user.keyboard('{Enter}')
+ const item = page.getByRole('button')
+ act(() => {
+ item.element().focus()
+ })
+ await expect.element(item).toHaveFocus()
+ await userEvent.keyboard('{Enter}')
expect(mockOnSelect).toHaveBeenCalledTimes(1)
})
it('should not render buttons when role is specified', async () => {
- const {getByRole} = HTMLRender(
+ HTMLRender(
Item 1
Item 2
@@ -304,22 +316,22 @@ describe('ActionList.Item', () => {
Item 5
,
)
- const option = getByRole('option')
- expect(option.tagName).toBe('LI')
- expect(option.textContent).toBe('Item 1')
- const menuItem = getByRole('menuitem')
- expect(menuItem.tagName).toBe('LI')
- const menuItemCheckbox = getByRole('menuitemcheckbox')
- expect(menuItemCheckbox.tagName).toBe('LI')
- const menuItemRadio = getByRole('menuitemradio')
- expect(menuItemRadio.tagName).toBe('LI')
- const button = getByRole('button')
- expect(button.parentElement?.tagName).toBe('LI')
- expect(button.textContent).toBe('Item 5')
+ const option = page.getByRole('option')
+ expect(option.element().tagName).toBe('LI')
+ expect(option.element().textContent).toBe('Item 1')
+ const menuItem = page.getByRole('menuitem')
+ expect(menuItem.element().tagName).toBe('LI')
+ const menuItemCheckbox = page.getByRole('menuitemcheckbox')
+ expect(menuItemCheckbox.element().tagName).toBe('LI')
+ const menuItemRadio = page.getByRole('menuitemradio')
+ expect(menuItemRadio.element().tagName).toBe('LI')
+ const button = page.getByRole('button')
+ expect(button.element().parentElement?.tagName).toBe('LI')
+ expect(button.element().textContent).toBe('Item 5')
})
it('should add `role="option"` if `role="listbox"` and `selectionVariant` is present', async () => {
- const {getAllByRole} = HTMLRender(
+ HTMLRender(
Item 1
Item 2
@@ -327,84 +339,82 @@ describe('ActionList.Item', () => {
Item 4
,
)
- const options = getAllByRole('option')
- expect(options[0]).toBeInTheDocument()
+ const options = page.getByRole('option').all()
+ await expect.element(options[0]).toBeInTheDocument()
expect(options).toHaveLength(4)
})
- it('should add `aria-describedby` to items with a description', () => {
- const {getByRole} = HTMLRender(
+ it('should add `aria-describedby` to items with a description', async () => {
+ HTMLRender(
Item, Description
,
)
- const item = getByRole('button')
- expect(item).toHaveAttribute('aria-describedby')
- expect(item).toHaveTextContent('Item, Description')
- expect(item).toHaveAccessibleDescription('Description')
+ const item = page.getByRole('button')
+ await expect.element(item).toHaveAttribute('aria-describedby')
+ await expect.element(item).toHaveTextContent('Item, Description')
+ await expect.element(item).toHaveAccessibleDescription('Description')
})
- it('should add `aria-describedby` to items with a description when `role=listbox` is applied', () => {
- const {getByRole} = HTMLRender(
+ it('should add `aria-describedby` to items with a description when `role=listbox` is applied', async () => {
+ HTMLRender(
Item, Description
,
)
- const item = getByRole('option')
- expect(item).toHaveAttribute('aria-describedby')
- expect(item).toHaveTextContent('Item, Description')
- expect(item).toHaveAccessibleDescription('Description')
+ const item = page.getByRole('option')
+ await expect.element(item).toHaveAttribute('aria-describedby')
+ await expect.element(item).toHaveTextContent('Item, Description')
+ await expect.element(item).toHaveAccessibleDescription('Description')
})
- it('should add role="tab" when ActionList has role="tablist"', () => {
- const {getAllByRole} = HTMLRender(
+ it('should add role="tab" when ActionList has role="tablist"', async () => {
+ HTMLRender(
Tab 1
Tab 2
Tab 3
,
)
- const tabs = getAllByRole('tab')
- expect(tabs[0]).toBeInTheDocument()
+ const tabs = page.getByRole('tab').all()
+ await expect.element(tabs[0]).toBeInTheDocument()
expect(tabs).toHaveLength(3)
})
- it('should allow role="tab" on the li element', () => {
- const {getAllByRole} = HTMLRender(
+ it('should allow role="tab" on the li element', async () => {
+ HTMLRender(
Tab 1
Tab 2
Tab 3
,
)
- const tabs = getAllByRole('tab')
- expect(tabs[0]).toBeInTheDocument()
- expect(tabs[0].nodeType).toBe(Node.ELEMENT_NODE)
+ const tabs = page.getByRole('tab').all()
+ await expect.element(tabs[0]).toBeInTheDocument()
+ expect(tabs[0].element().nodeType).toBe(Node.ELEMENT_NODE)
expect(tabs).toHaveLength(3)
})
it('should forward a list-semantic item ref only to the outer list item', () => {
const ref = vi.fn<(node: HTMLLIElement | null) => void>()
- const {getByRole} = HTMLRender(
+ HTMLRender(
Primer React
,
)
- const option = getByRole('option')
+ const option = page.getByRole('option')
- expect(ref.mock.calls.filter(([node]) => node !== null)).toEqual([[option]])
- expect(option.tagName).toBe('LI')
+ expect(ref.mock.calls.filter(([node]) => node !== null)).toEqual([[option.element()]])
+ expect(option.element().tagName).toBe('LI')
})
it('should preserve consumer ref when tooltip wraps trigger', async () => {
- const user = userEvent.setup()
-
function TestComponent() {
const anchorRef = React.useRef(null)
const [open, setOpen] = React.useState(false)
@@ -433,18 +443,16 @@ describe('ActionList.Item', () => {
)
}
- const {getByText, queryByRole} = HTMLRender()
+ HTMLRender()
// Overlay should not be visible initially
- expect(queryByRole('menu')).not.toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).not.toBeInTheDocument()
// Click the item to open the anchored overlay
- await user.click(getByText('Convert to issue'))
+ await userEvent.click(page.getByText('Convert to issue', {exact: true}))
// The overlay should open — this fails if Tooltip overwrites the consumer ref
- await waitFor(() => {
- expect(queryByRole('menu')).toBeInTheDocument()
- })
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
})
describe('data attributes derived from slots', () => {
diff --git a/packages/react/src/ActionList/LinkItem.test.tsx b/packages/react/src/ActionList/LinkItem.test.tsx
index 3c2536ab531..4ff5a0de035 100644
--- a/packages/react/src/ActionList/LinkItem.test.tsx
+++ b/packages/react/src/ActionList/LinkItem.test.tsx
@@ -1,5 +1,6 @@
+import {page, userEvent} from 'vitest/browser'
import {describe, it, expect, vi} from 'vitest'
-import {render, screen, fireEvent} from '@testing-library/react'
+import {render} from '@testing-library/react'
import React from 'react'
import {ActionList} from '.'
import Link from '../Link'
@@ -8,16 +9,16 @@ import {implementsClassName} from '../utils/testing'
describe('ActionList.LinkItem', () => {
implementsClassName(ActionList.LinkItem)
- it('renders as an anchor by default', () => {
+ it('renders as an anchor by default', async () => {
render(
Home
,
)
- const link = screen.getByRole('link', {name: 'Home'})
- expect(link).toHaveAttribute('href', '#home')
- expect(link.tagName).toBe('A')
+ const link = page.getByRole('link', {name: 'Home', exact: true})
+ await expect.element(link).toHaveAttribute('href', '#home')
+ expect(link.element().tagName).toBe('A')
})
it('forwards its ref to the link when wrapped in a tooltip', () => {
@@ -31,10 +32,10 @@ describe('ActionList.LinkItem', () => {
,
)
- expect(ref.current).toBe(screen.getByRole('link', {name: 'Home'}))
+ expect(ref.current).toBe(page.getByRole('link', {name: 'Home', exact: true}).element())
})
- it('calls onClick handler', () => {
+ it('calls onClick handler', async () => {
const onClick = vi.fn()
render(
@@ -45,12 +46,12 @@ describe('ActionList.LinkItem', () => {
,
)
- fireEvent.click(screen.getByRole('link', {name: 'Home'}))
+ await userEvent.click(page.getByRole('link', {name: 'Home', exact: true}))
expect(onClick).toHaveBeenCalledTimes(1)
})
describe('as prop', () => {
- it('supports polymorphic LinkItem with as prop', () => {
+ it('supports polymorphic LinkItem with as prop', async () => {
const CustomLink = React.forwardRef<
HTMLAnchorElement,
React.AnchorHTMLAttributes & {custom: boolean}
@@ -69,12 +70,12 @@ describe('ActionList.LinkItem', () => {
,
)
- const link = screen.getByRole('link', {name: 'Docs'})
- expect(link).toHaveAttribute('data-custom-link', 'true')
- expect(link).toHaveAttribute('href', '#docs')
+ const link = page.getByRole('link', {name: 'Docs', exact: true})
+ await expect.element(link).toHaveAttribute('data-custom-link', 'true')
+ await expect.element(link).toHaveAttribute('href', '#docs')
})
- it('passes through additional props to the element specified by as', () => {
+ it('passes through additional props to the element specified by as', async () => {
render(
@@ -83,13 +84,13 @@ describe('ActionList.LinkItem', () => {
,
)
- const homeLink = screen.getByTestId('home-link')
- expect(homeLink).toHaveAttribute('href', '#home')
- expect(homeLink).toHaveAttribute('data-inline', 'true')
+ const homeLink = page.getByTestId('home-link')
+ await expect.element(homeLink).toHaveAttribute('href', '#home')
+ await expect.element(homeLink).toHaveAttribute('data-inline', 'true')
})
})
- it('renders inactive text when inactiveText is provided', () => {
+ it('renders inactive text when inactiveText is provided', async () => {
render(
@@ -98,6 +99,6 @@ describe('ActionList.LinkItem', () => {
,
)
- expect(screen.getByText('Unavailable due to an outage')).toBeInTheDocument()
+ await expect.element(page.getByText('Unavailable due to an outage', {exact: true})).toBeInTheDocument()
})
})
diff --git a/packages/react/src/ActionMenu/ActionMenu.test.tsx b/packages/react/src/ActionMenu/ActionMenu.test.tsx
index bec8b1b18a3..62168df2fdb 100644
--- a/packages/react/src/ActionMenu/ActionMenu.test.tsx
+++ b/packages/react/src/ActionMenu/ActionMenu.test.tsx
@@ -1,6 +1,6 @@
+import {page, userEvent} from 'vitest/browser'
import {describe, expect, it, vi, beforeEach} from 'vitest'
-import {render as HTMLRender, waitFor, act, within} from '@testing-library/react'
-import userEvent from '@testing-library/user-event'
+import {render as HTMLRender, waitFor, act} from '@testing-library/react'
import type React from 'react'
import {useRef, useState} from 'react'
import BaseStyles from '../BaseStyles'
@@ -185,63 +185,62 @@ describe('ActionMenu', () => {
it('renders data-component attributes for ActionMenu parts', async () => {
const component = HTMLRender()
- const user = userEvent.setup()
- const trigger = component.getByRole('button', {name: 'Toggle Menu'})
- expect(trigger).toHaveAttribute('data-component', 'ActionMenu.Button')
+ const trigger = page.getByRole('button', {name: 'Toggle Menu', exact: true})
+ await expect.element(trigger).toHaveAttribute('data-component', 'ActionMenu.Button')
- await user.click(trigger)
+ await userEvent.click(trigger)
expect(component.baseElement.querySelector('[data-component="ActionMenu.Overlay"]')).not.toBeNull()
expect(component.baseElement.querySelector('[data-component="AnchoredOverlay"]')).toBeNull()
})
it('should open Menu on MenuButton click', async () => {
- const component = HTMLRender()
- const button = component.getByRole('button')
+ HTMLRender()
+ const button = page.getByRole('button')
- const user = userEvent.setup()
- await user.click(button)
+ await userEvent.click(button)
- expect(component.getByRole('menu')).toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
})
it('should open Menu on MenuButton keypress', async () => {
- const component = HTMLRender()
- const button = component.getByRole('button')
+ HTMLRender()
+ const button = page.getByRole('button')
- const user = userEvent.setup()
- button.focus()
- await user.keyboard('{Enter}')
+ act(() => {
+ button.element().focus()
+ })
+ await userEvent.keyboard('{Enter}')
- expect(component.getByRole('menu')).toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
})
it('should close Menu on selecting an action with click', async () => {
- const component = HTMLRender()
- const button = component.getByRole('button')
+ HTMLRender()
+ const button = page.getByRole('button')
- const user = userEvent.setup()
- await user.click(button)
+ await userEvent.click(button)
- const menuItems = component.getAllByRole('menuitem')
- await user.click(menuItems[0])
+ const menuItems = page.getByRole('menuitem').all()
+ await userEvent.click(menuItems[0])
- expect(component.queryByRole('menu')).toBeNull()
+ await expect.element(page.getByRole('menu')).not.toBeInTheDocument()
})
it('should close Menu on selecting an action with Enter', async () => {
- const component = HTMLRender()
- const button = component.getByRole('button')
+ HTMLRender()
+ const button = page.getByRole('button')
- const user = userEvent.setup()
- await user.click(button)
+ await userEvent.click(button)
- const menuItems = component.getAllByRole('menuitem')
- menuItems[0].focus()
- await user.keyboard('{Enter}')
+ const menuItems = page.getByRole('menuitem').all()
+ act(() => {
+ menuItems[0].element().focus()
+ })
+ await userEvent.keyboard('{Enter}')
- expect(component.queryByRole('menu')).toBeNull()
+ await expect.element(page.getByRole('menu')).not.toBeInTheDocument()
})
describe('preventFocusOnClose', () => {
@@ -256,37 +255,39 @@ describe('ActionMenu', () => {
{gesture: 'Escape', preventFocusOnClose: true},
{gesture: 'outside-click', preventFocusOnClose: true},
])('handles $gesture with preventFocusOnClose=$preventFocusOnClose', async ({gesture, preventFocusOnClose}) => {
- const component = HTMLRender(
+ HTMLRender(
<>
>,
)
- const user = userEvent.setup()
- const trigger = component.getByRole('button', {name: 'Toggle Menu'})
- await user.click(trigger)
- const item = component.getByRole('menuitem', {name: 'New file'})
- item.focus()
+ const trigger = page.getByRole('button', {name: 'Toggle Menu', exact: true})
+ const triggerElement = trigger.element()
+ await userEvent.click(trigger)
+ const item = page.getByRole('menuitem', {name: 'New file', exact: true})
+ act(() => {
+ item.element().focus()
+ })
- const focus = vi.spyOn(trigger, 'focus')
+ const focus = vi.spyOn(triggerElement, 'focus')
if (gesture === 'item-click') {
- await user.click(item)
+ await userEvent.click(item)
} else if (gesture === 'outside-click') {
- await user.click(component.getByRole('button', {name: 'Outside'}))
+ await userEvent.click(page.getByRole('button', {name: 'Outside', exact: true}))
} else {
- await user.keyboard(gesture === 'Space' ? ' ' : `{${gesture}}`)
+ await userEvent.keyboard(gesture === 'Space' ? ' ' : `{${gesture}}`)
}
- expect(component.queryByRole('menu')).not.toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).not.toBeInTheDocument()
if (preventFocusOnClose) {
expect(focus).not.toHaveBeenCalled()
- expect(trigger).not.toHaveFocus()
+ await expect.element(trigger).not.toHaveFocus()
} else {
expect(focus).toHaveBeenCalled()
- expect(trigger).toHaveFocus()
+ await expect.element(trigger).toHaveFocus()
}
if (gesture === 'outside-click') {
- expect(component.getByRole('button', {name: 'Outside'})).toHaveFocus()
+ await expect.element(page.getByRole('button', {name: 'Outside', exact: true})).toHaveFocus()
}
})
@@ -294,7 +295,7 @@ describe('ActionMenu', () => {
'handles anchor-click closure with preventFocusOnClose=%s',
async preventFocusOnClose => {
const returnFocusRef = {current: null as HTMLButtonElement | null}
- const component = HTMLRender(
+ HTMLRender(
@@ -307,21 +308,20 @@ describe('ActionMenu', () => {
,
)
- const user = userEvent.setup()
- const trigger = component.getByRole('button', {name: 'Toggle Menu'})
- const returnFocusTarget = component.getByRole('button', {name: 'Return focus'})
+ const trigger = page.getByRole('button', {name: 'Toggle Menu', exact: true})
+ const returnFocusTarget = page.getByRole('button', {name: 'Return focus', exact: true})
- await user.click(trigger)
- const focus = vi.spyOn(returnFocusTarget, 'focus')
- await user.click(trigger)
+ await userEvent.click(trigger)
+ const focus = vi.spyOn(returnFocusTarget.element(), 'focus')
+ await userEvent.click(trigger)
- expect(component.queryByRole('menu')).not.toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).not.toBeInTheDocument()
if (preventFocusOnClose) {
expect(focus).not.toHaveBeenCalled()
- expect(returnFocusTarget).not.toHaveFocus()
+ await expect.element(returnFocusTarget).not.toHaveFocus()
} else {
expect(focus).toHaveBeenCalled()
- expect(returnFocusTarget).toHaveFocus()
+ await expect.element(returnFocusTarget).toHaveFocus()
}
},
)
@@ -343,12 +343,12 @@ describe('ActionMenu', () => {
const component = HTMLRender(example(true))
const focus = vi.spyOn(returnFocusRef.current, 'focus')
- await waitFor(() => expect(component.getByRole('menuitem', {name: 'New file'})).toHaveFocus())
+ await expect.element(page.getByRole('menuitem', {name: 'New file', exact: true})).toHaveFocus()
component.rerender(example(false))
- expect(component.queryByRole('menu')).not.toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).not.toBeInTheDocument()
expect(focus).not.toHaveBeenCalled()
- expect(component.getByRole('button', {name: 'Toggle Menu'})).not.toHaveFocus()
+ await expect.element(page.getByRole('button', {name: 'Toggle Menu', exact: true})).not.toHaveFocus()
})
it('does not focus a captured anchor that an action replaces', async () => {
@@ -370,208 +370,199 @@ describe('ActionMenu', () => {
)
}
- const component = HTMLRender()
- const user = userEvent.setup()
- const trigger = component.getByRole('button', {name: 'Link'})
- await user.click(trigger)
- const focus = vi.spyOn(trigger, 'focus')
+ HTMLRender()
+ const trigger = page.getByRole('button', {name: 'Link', exact: true})
+ const triggerElement = trigger.element()
+ await userEvent.click(trigger)
+ const focus = vi.spyOn(triggerElement, 'focus')
- await user.click(component.getByRole('menuitem', {name: 'Convert to preview'}))
+ await userEvent.click(page.getByRole('menuitem', {name: 'Convert to preview', exact: true}))
- expect(component.queryByRole('menu')).not.toBeInTheDocument()
- expect(trigger).not.toBeInTheDocument()
- expect(component.getByRole('button', {name: 'Preview'})).not.toHaveFocus()
+ await expect.element(page.getByRole('menu')).not.toBeInTheDocument()
+ expect(triggerElement).not.toBeInTheDocument()
+ await expect.element(page.getByRole('button', {name: 'Preview', exact: true})).not.toHaveFocus()
expect(focus).not.toHaveBeenCalled()
})
})
it('should not close Menu if event is prevented', async () => {
- const component = HTMLRender()
- const button = component.getByRole('button')
+ HTMLRender()
+ const button = page.getByRole('button')
- const user = userEvent.setup()
- await user.click(button)
+ await userEvent.click(button)
- const menuItems = component.getAllByRole('menuitem')
- await user.click(menuItems[3])
+ const menuItems = page.getByRole('menuitem').all()
+ await userEvent.click(menuItems[3])
// menu should still be open
- expect(component.getByRole('menu')).toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
})
it('should be able to select an Item with selectionVariant', async () => {
- const component = HTMLRender()
- const button = component.getByRole('button', {name: /^options/i})
+ HTMLRender()
+ const button = page.getByRole('button', {name: /^options/i})
- const user = userEvent.setup()
- await user.click(button)
+ await userEvent.click(button)
// select first item by role, that would close the menu
- await user.click(component.getAllByRole('menuitemradio')[0])
- expect(component.queryByRole('menu')).not.toBeInTheDocument()
+ await userEvent.click(page.getByRole('menuitemradio').all()[0])
+ await expect.element(page.getByRole('menu')).not.toBeInTheDocument()
// open menu again and check if the first option is checked
- await user.click(button)
- expect(component.getAllByRole('menuitemradio')[0]).toHaveAttribute('aria-checked', 'true')
+ await userEvent.click(button)
+ await expect.element(page.getByRole('menuitemradio').all()[0]).toHaveAttribute('aria-checked', 'true')
})
it('should assign the right roles with groups & mixed selectionVariant', async () => {
- const component = HTMLRender()
+ HTMLRender()
- const button = component.getByRole('button', {
+ const button = page.getByRole('button', {
name: 'Group by Stage',
+ exact: true,
})
- const user = userEvent.setup()
- await user.click(button)
+ await userEvent.click(button)
- expect(component.getByLabelText('Status')).toHaveAttribute('role', 'menuitemradio')
- expect(component.getByLabelText('Clear Group by')).toHaveAttribute('role', 'menuitem')
+ await expect.element(page.getByLabelText('Status', {exact: true})).toHaveAttribute('role', 'menuitemradio')
+ await expect.element(page.getByLabelText('Clear Group by', {exact: true})).toHaveAttribute('role', 'menuitem')
})
it('should be able to select an Item with aria-keyshortcuts after opening Menu with click', async () => {
- const component = HTMLRender()
- const button = component.getByRole('button')
+ HTMLRender()
+ const button = page.getByRole('button')
- const user = userEvent.setup()
- await user.click(button)
+ await userEvent.click(button)
- expect(component.queryByRole('menu')).toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
// linkItem button is the active element at this point
- await user.keyboard('{ArrowDown}{s}')
+ await userEvent.keyboard('{ArrowDown}{s}')
- expect(component.getAllByRole('menuitem')[4]).toEqual(document.activeElement)
- await user.keyboard('{ArrowUp}')
- expect(component.getAllByRole('menuitem')[3]).toEqual(document.activeElement)
+ await expect.element(page.getByRole('menuitem').all()[4]).toHaveFocus()
+ await userEvent.keyboard('{ArrowUp}')
+ await expect.element(page.getByRole('menuitem').all()[3]).toHaveFocus()
// assumes mnemonics aria-keyshortcuts are ignored
- await user.keyboard('{g}')
- expect(component.getAllByRole('menuitem')[3]).toEqual(document.activeElement)
+ await userEvent.keyboard('{g}')
+ await expect.element(page.getByRole('menuitem').all()[3]).toHaveFocus()
})
it('should select last element when ArrowUp is pressed after opening Menu with click', async () => {
- const component = HTMLRender()
+ HTMLRender()
- const button = component.getByRole('button')
+ const button = page.getByRole('button')
- const user = userEvent.setup()
- await user.click(button)
+ await userEvent.click(button)
- expect(component.queryByRole('menu')).toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
// button should be the active element
// assumes button is the active element at this point
- await user.keyboard('{ArrowUp}')
+ await userEvent.keyboard('{ArrowUp}')
- expect(component.getAllByRole('menuitem').pop()).toEqual(document.activeElement)
+ await expect.element(page.getByRole('menuitem').last()).toHaveFocus()
})
it('should close the menu if Tab is pressed and move to next element', async () => {
- const component = HTMLRender(
+ HTMLRender(
<>
>,
)
- const anchor = component.getByRole('button')
+ const anchor = page.getByRole('button')
- const user = userEvent.setup()
- await user.tab() // tab into the story, this should focus on the first button
- expect(anchor).toEqual(document.activeElement) // trust, but verify
+ await userEvent.tab() // tab into the story, this should focus on the first button
+ await expect.element(anchor).toHaveFocus() // trust, but verify
- await user.keyboard('{Enter}')
- expect(component.queryByRole('menu')).toBeInTheDocument()
- expect(component.getAllByRole('menuitem')[0]).toEqual(document.activeElement)
+ await userEvent.keyboard('{Enter}')
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
+ await expect.element(page.getByRole('menuitem').all()[0]).toHaveFocus()
- // TODO: revisit why we need this waitFor
- await waitFor(async () => {
- await user.tab()
- expect(document.activeElement).toEqual(component.getByPlaceholderText('next focusable element'))
- expect(component.queryByRole('menu')).not.toBeInTheDocument()
- })
+ await userEvent.tab()
+ await expect.element(page.getByPlaceholder('next focusable element')).toHaveFocus()
+ await expect.element(page.getByRole('menu')).not.toBeInTheDocument()
})
it('should wrap focus when ArrowDown is pressed on the last element', async () => {
- const component = HTMLRender()
- const button = component.getByRole('button')
+ HTMLRender()
+ const button = page.getByRole('button')
- const user = userEvent.setup()
- await user.click(button)
+ await userEvent.click(button)
- expect(component.queryByRole('menu')).toBeInTheDocument()
- const menuItems = component.getAllByRole('menuitem')
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
+ const menuItems = page.getByRole('menuitem').all()
// TODO: Fix the focus trap from taking over focus control
// https://github.com/primer/react/issues/6434
// expect(menuItems[0]).toEqual(document.activeElement)
- await user.keyboard('{ArrowDown}')
- expect(menuItems[1]).toEqual(document.activeElement)
+ await userEvent.keyboard('{ArrowDown}')
+ await expect.element(menuItems[1]).toHaveFocus()
// TODO: Removed one ArrowDown to account for the focus trap starting at the second element
- // await user.keyboard('{ArrowDown}')
- await user.keyboard('{ArrowDown}')
- await user.keyboard('{ArrowDown}')
- await user.keyboard('{ArrowDown}')
- expect(menuItems[menuItems.length - 1]).toEqual(document.activeElement) // last elememt
-
- await user.keyboard('{ArrowDown}')
- expect(menuItems[0]).toEqual(document.activeElement) // wrap to first
+ // await userEvent.keyboard('{ArrowDown}')
+ await userEvent.keyboard('{ArrowDown}')
+ await userEvent.keyboard('{ArrowDown}')
+ await userEvent.keyboard('{ArrowDown}')
+ await expect.element(menuItems[menuItems.length - 1]).toHaveFocus() // last elememt
+
+ await userEvent.keyboard('{ArrowDown}')
+ await expect.element(menuItems[0]).toHaveFocus() // wrap to first
})
it('should open menu on menu button click and it is wrapped with tooltip', async () => {
- const component = HTMLRender()
- const button = component.getByRole('button')
+ HTMLRender()
+ const button = page.getByRole('button')
- const user = userEvent.setup()
- await user.click(button)
+ await userEvent.click(button)
- expect(component.getByRole('menu')).toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
})
it('should open menu on menu button click and it is wrapped with tooltip v2', async () => {
- const component = HTMLRender(
+ HTMLRender(
ExampleWithTooltipV2(
Toggle Menu
,
),
)
- const button = component.getByRole('button')
+ const button = page.getByRole('button')
- const user = userEvent.setup()
- await user.click(button)
+ await userEvent.click(button)
- expect(component.getByRole('menu')).toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
})
it('should display tooltip when menu button is focused', async () => {
- const component = HTMLRender()
- const button = component.getByRole('button')
- button.focus()
- expect(component.getByRole('tooltip')).toBeInTheDocument()
+ HTMLRender()
+ const button = page.getByRole('button')
+ act(() => {
+ button.element().focus()
+ })
+ await expect.element(page.getByRole('tooltip')).toBeInTheDocument()
})
it('should display tooltip v2 when menu button is focused', async () => {
- const component = HTMLRender(
+ HTMLRender(
ExampleWithTooltipV2(
Toggle Menu
,
),
)
- const button = component.getByRole('button')
+ const button = page.getByRole('button')
act(() => {
- button.focus()
+ button.element().focus()
})
- expect(component.getByRole('tooltip', {hidden: true})).toBeInTheDocument()
+ await expect.element(page.getByRole('tooltip', {includeHidden: true})).toBeInTheDocument()
})
it('should open menu on menu anchor click and it is wrapped with tooltip v2', async () => {
- const component = HTMLRender(
+ HTMLRender(
ExampleWithTooltipV2(
@@ -580,16 +571,15 @@ describe('ActionMenu', () => {
,
),
)
- const button = component.getByRole('button')
+ const button = page.getByRole('button')
- const user = userEvent.setup()
- await user.click(button)
+ await userEvent.click(button)
- expect(component.getByRole('menu')).toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
})
it('should display tooltip v2 and menu anchor is focused', async () => {
- const component = HTMLRender(
+ HTMLRender(
ExampleWithTooltipV2(
@@ -598,17 +588,17 @@ describe('ActionMenu', () => {
,
),
)
- const button = component.getByRole('button')
+ const button = page.getByRole('button')
act(() => {
- button.focus()
+ button.element().focus()
})
- expect(component.getByRole('tooltip', {hidden: true})).toBeInTheDocument()
+ await expect.element(page.getByRole('tooltip', {includeHidden: true})).toBeInTheDocument()
})
it('should pass the "id" prop from ActionMenu.Button to the HTML button', async () => {
const buttonId = 'toggle-menu-custom-id'
- const component = HTMLRender(
+ HTMLRender(
Toggle Menu
@@ -629,13 +619,13 @@ describe('ActionMenu', () => {
,
)
- const button = component.getByRole('button')
+ const button = page.getByRole('button')
- expect(button.id).toBe(buttonId)
+ expect(button.element().id).toBe(buttonId)
})
it('should pass the "id" prop from ActionMenu.Anchor to anchor child', async () => {
const buttonId = 'toggle-menu-custom-id'
- const component = HTMLRender(
+ HTMLRender(
@@ -658,13 +648,13 @@ describe('ActionMenu', () => {
,
)
- const button = component.getByRole('button')
+ const button = page.getByRole('button')
- expect(button.id).toBe(buttonId)
+ expect(button.element().id).toBe(buttonId)
})
it('should use the tooltip id to name the menu when the anchor is icon button', async () => {
- const component = HTMLRender(
+ HTMLRender(
@@ -683,133 +673,131 @@ describe('ActionMenu', () => {
,
)
- const toggleButton = component.getByRole('button', {name: 'More actions'})
+ const toggleButton = page.getByRole('button', {name: 'More actions', exact: true})
await userEvent.click(toggleButton)
- expect(toggleButton).toHaveAttribute('aria-labelledby')
- expect(component.getByRole('menu')).toHaveAttribute('aria-labelledby', toggleButton.getAttribute('aria-labelledby'))
+ await expect.element(toggleButton).toHaveAttribute('aria-labelledby')
+ await expect
+ .element(page.getByRole('menu'))
+ .toHaveAttribute('aria-labelledby', toggleButton.element().getAttribute('aria-labelledby'))
})
describe('submenus', () => {
it('sets `aria-haspopup` and `aria-expanded` on submenu anchors', async () => {
- const component = HTMLRender()
- const user = userEvent.setup()
+ HTMLRender()
- const baseAnchor = component.getByRole('button', {name: 'Toggle Menu'})
- await user.click(baseAnchor)
+ const baseAnchor = page.getByRole('button', {name: 'Toggle Menu', exact: true})
+ await userEvent.click(baseAnchor)
- const submenuAnchor = component.getByRole('menuitem', {name: 'Paste special'})
- expect(submenuAnchor).toHaveAttribute('aria-haspopup')
- await user.click(submenuAnchor)
- expect(submenuAnchor).toHaveAttribute('aria-expanded')
+ const submenuAnchor = page.getByRole('menuitem', {name: 'Paste special', exact: true})
+ await expect.element(submenuAnchor).toHaveAttribute('aria-haspopup')
+ await userEvent.click(submenuAnchor)
+ await expect.element(submenuAnchor).toHaveAttribute('aria-expanded')
- const subSubmenuAnchor = component.getByRole('menuitem', {name: 'Paste from'})
- expect(subSubmenuAnchor).toHaveAttribute('aria-haspopup')
- await user.click(subSubmenuAnchor)
- expect(subSubmenuAnchor).toHaveAttribute('aria-expanded')
+ const subSubmenuAnchor = page.getByRole('menuitem', {name: 'Paste from', exact: true})
+ await expect.element(subSubmenuAnchor).toHaveAttribute('aria-haspopup')
+ await userEvent.click(subSubmenuAnchor)
+ await expect.element(subSubmenuAnchor).toHaveAttribute('aria-expanded')
})
it('sets labels on submenus', async () => {
- const component = HTMLRender()
- const user = userEvent.setup()
+ HTMLRender()
- const baseAnchor = component.getByRole('button', {name: 'Toggle Menu'})
- await user.click(baseAnchor)
+ const baseAnchor = page.getByRole('button', {name: 'Toggle Menu', exact: true})
+ await userEvent.click(baseAnchor)
- const submenuAnchor = component.getByRole('menuitem', {name: 'Paste special'})
- await user.click(submenuAnchor)
+ const submenuAnchor = page.getByRole('menuitem', {name: 'Paste special', exact: true})
+ await userEvent.click(submenuAnchor)
- const submenu = component.getByRole('menu', {name: 'Paste special'})
- await waitFor(() => {
- expect(submenu).toBeVisible()
- })
+ const submenu = page.getByRole('menu', {name: 'Paste special', exact: true})
+ await expect.element(submenu).toBeVisible()
- const subSubmenuAnchor = within(submenu).getByRole('menuitem', {name: 'Paste from'})
- await user.click(subSubmenuAnchor)
+ const subSubmenuAnchor = submenu.getByRole('menuitem', {name: 'Paste from', exact: true})
+ await userEvent.click(subSubmenuAnchor)
- const subSubmenu = component.getByRole('menu', {name: 'Paste from'})
+ const subSubmenu = page.getByRole('menu', {name: 'Paste from', exact: true})
- await waitFor(() => {
- expect(subSubmenu).toBeVisible()
- })
+ await expect.element(subSubmenu).toBeVisible()
})
it('does not open top-level menu on right arrow key press', async () => {
- const component = HTMLRender()
- const user = userEvent.setup()
+ HTMLRender()
- const baseAnchor = component.getByRole('button', {name: 'Toggle Menu'})
- baseAnchor.focus()
+ const baseAnchor = page.getByRole('button', {name: 'Toggle Menu', exact: true})
+ act(() => {
+ baseAnchor.element().focus()
+ })
- await user.keyboard('{ArrowRight}')
- expect(component.queryByRole('menu')).not.toBeInTheDocument()
- expect(baseAnchor).not.toHaveAttribute('aria-expanded', 'true')
+ await userEvent.keyboard('{ArrowRight}')
+ await expect.element(page.getByRole('menu')).not.toBeInTheDocument()
+ await expect.element(baseAnchor).not.toHaveAttribute('aria-expanded', 'true')
})
it('opens submenus on enter or right arrow key press', async () => {
- const component = HTMLRender()
- const user = userEvent.setup()
-
- const baseAnchor = component.getByRole('button', {name: 'Toggle Menu'})
- await user.click(baseAnchor)
-
- const submenuAnchor = component.getByRole('menuitem', {name: 'Paste special'})
- expect(submenuAnchor).toHaveAttribute('aria-haspopup', 'true')
- submenuAnchor.focus()
- await user.keyboard('{Enter}')
- expect(submenuAnchor).toHaveAttribute('aria-expanded', 'true')
-
- const subSubmenuAnchor = component.getByRole('menuitem', {name: 'Paste from'})
- subSubmenuAnchor.focus()
- await user.keyboard('{ArrowRight}')
- expect(subSubmenuAnchor).toHaveAttribute('aria-expanded', 'true')
+ HTMLRender()
+
+ const baseAnchor = page.getByRole('button', {name: 'Toggle Menu', exact: true})
+ await userEvent.click(baseAnchor)
+
+ const submenuAnchor = page.getByRole('menuitem', {name: 'Paste special', exact: true})
+ await expect.element(submenuAnchor).toHaveAttribute('aria-haspopup', 'true')
+ act(() => {
+ submenuAnchor.element().focus()
+ })
+ await userEvent.keyboard('{Enter}')
+ await expect.element(submenuAnchor).toHaveAttribute('aria-expanded', 'true')
+
+ const subSubmenuAnchor = page.getByRole('menuitem', {name: 'Paste from', exact: true})
+ act(() => {
+ subSubmenuAnchor.element().focus()
+ })
+ await userEvent.keyboard('{ArrowRight}')
+ await expect.element(subSubmenuAnchor).toHaveAttribute('aria-expanded', 'true')
})
it('closes top menu on escape or left arrow key press', async () => {
- const component = HTMLRender()
- const user = userEvent.setup()
+ HTMLRender()
- const baseAnchor = component.getByRole('button', {name: 'Toggle Menu'})
- await user.click(baseAnchor)
+ const baseAnchor = page.getByRole('button', {name: 'Toggle Menu', exact: true})
+ await userEvent.click(baseAnchor)
- const submenuAnchor = component.getByRole('menuitem', {name: 'Paste special'})
- await user.click(submenuAnchor)
+ const submenuAnchor = page.getByRole('menuitem', {name: 'Paste special', exact: true})
+ await userEvent.click(submenuAnchor)
- const subSubmenuAnchor = component.getByRole('menuitem', {name: 'Paste from'})
- await user.click(subSubmenuAnchor)
+ const subSubmenuAnchor = page.getByRole('menuitem', {name: 'Paste from', exact: true})
+ await userEvent.click(subSubmenuAnchor)
- expect(subSubmenuAnchor).toHaveAttribute('aria-expanded', 'true')
+ await expect.element(subSubmenuAnchor).toHaveAttribute('aria-expanded', 'true')
- await user.keyboard('{Escape}')
- expect(subSubmenuAnchor).not.toHaveAttribute('aria-expanded', 'true')
- expect(submenuAnchor).toHaveAttribute('aria-expanded', 'true')
+ await userEvent.keyboard('{Escape}')
+ await expect.element(subSubmenuAnchor).not.toHaveAttribute('aria-expanded', 'true')
+ await expect.element(submenuAnchor).toHaveAttribute('aria-expanded', 'true')
- await user.keyboard('{ArrowLeft}')
- expect(submenuAnchor).not.toHaveAttribute('aria-expanded', 'true')
+ await userEvent.keyboard('{ArrowLeft}')
+ await expect.element(submenuAnchor).not.toHaveAttribute('aria-expanded', 'true')
- expect(baseAnchor).toHaveAttribute('aria-expanded', 'true')
+ await expect.element(baseAnchor).toHaveAttribute('aria-expanded', 'true')
})
it('closes all menus when an item is selected', async () => {
- const component = HTMLRender()
- const user = userEvent.setup()
+ HTMLRender()
- const baseAnchor = component.getByRole('button', {name: 'Toggle Menu'})
- await user.click(baseAnchor)
+ const baseAnchor = page.getByRole('button', {name: 'Toggle Menu', exact: true})
+ await userEvent.click(baseAnchor)
- const submenuAnchor = component.getByRole('menuitem', {name: 'Paste special'})
- await user.click(submenuAnchor)
+ const submenuAnchor = page.getByRole('menuitem', {name: 'Paste special', exact: true})
+ await userEvent.click(submenuAnchor)
- const subSubmenuAnchor = component.getByRole('menuitem', {name: 'Paste from'})
- await user.click(subSubmenuAnchor)
+ const subSubmenuAnchor = page.getByRole('menuitem', {name: 'Paste from', exact: true})
+ await userEvent.click(subSubmenuAnchor)
- const subSubmenuItem = component.getByRole('menuitem', {name: 'Current clipboard'})
- await user.click(subSubmenuItem)
+ const subSubmenuItem = page.getByRole('menuitem', {name: 'Current clipboard', exact: true})
+ await userEvent.click(subSubmenuItem)
- expect(baseAnchor).not.toHaveAttribute('aria-expanded', 'true')
+ await expect.element(baseAnchor).not.toHaveAttribute('aria-expanded', 'true')
})
it('supports className prop on ActionMenu.Anchor with css anchor positioning flag', async () => {
- const component = HTMLRender(
+ HTMLRender(
@@ -834,12 +822,12 @@ describe('ActionMenu', () => {
,
)
- const anchor = component.getByRole('button', {name: 'Toggle Menu'})
- expect(anchor).toHaveClass('test-class')
+ const anchor = page.getByRole('button', {name: 'Toggle Menu', exact: true})
+ await expect.element(anchor).toHaveClass('test-class')
})
it('supports className prop on ActionMenu.Button with css anchor positioning flag', async () => {
- const component = HTMLRender(
+ HTMLRender(
@@ -862,12 +850,12 @@ describe('ActionMenu', () => {
,
)
- const button = component.getByRole('button', {name: 'Toggle Menu'})
- expect(button).toHaveClass('test-class')
+ const button = page.getByRole('button', {name: 'Toggle Menu', exact: true})
+ await expect.element(button).toHaveClass('test-class')
})
it('supports className prop on ActionMenu.Anchor', async () => {
- const component = HTMLRender(
+ HTMLRender(
@@ -892,12 +880,12 @@ describe('ActionMenu', () => {
,
)
- const anchor = component.getByRole('button', {name: 'Toggle Menu'})
- expect(anchor).toHaveClass('test-class')
+ const anchor = page.getByRole('button', {name: 'Toggle Menu', exact: true})
+ await expect.element(anchor).toHaveClass('test-class')
})
it('supports className prop on ActionMenu.Button', async () => {
- const component = HTMLRender(
+ HTMLRender(
@@ -920,36 +908,36 @@ describe('ActionMenu', () => {
,
)
- const button = component.getByRole('button', {name: 'Toggle Menu'})
- expect(button).toHaveClass('test-class')
+ const button = page.getByRole('button', {name: 'Toggle Menu', exact: true})
+ await expect.element(button).toHaveClass('test-class')
})
it('keeps anchor-name and position-anchor linked when the anchor is replaced while the menu is open', async () => {
- const user = userEvent.setup()
const component = HTMLRender()
- const initialAnchor = component.getByRole('button', {name: 'Open menu'})
- await user.click(initialAnchor)
+ const initialAnchor = page.getByRole('button', {name: 'Open menu', exact: true})
+ const initialAnchorElement = initialAnchor.element()
+ await userEvent.click(initialAnchor)
const overlay = component.baseElement.querySelector('[data-component="ActionMenu.Overlay"]') as HTMLElement
expect(overlay).not.toBeNull()
- const initialAnchorName = initialAnchor.style.getPropertyValue('anchor-name')
+ const initialAnchorName = initialAnchor.element().style.getPropertyValue('anchor-name')
const initialPositionAnchor = overlay.style.getPropertyValue('position-anchor')
expect(initialAnchorName).not.toBe('')
expect(initialPositionAnchor).not.toBe('')
expect(initialPositionAnchor).toBe(initialAnchorName)
// Click the item that remounts the anchor while keeping the menu open
- const switchItem = component.getByRole('menuitem', {name: 'Switch anchor'})
- await user.click(switchItem)
+ const switchItem = page.getByRole('menuitem', {name: 'Switch anchor', exact: true})
+ await userEvent.click(switchItem)
- const newAnchor = component.getByRole('button', {name: 'Open menu'})
- expect(newAnchor).not.toBe(initialAnchor)
+ const newAnchor = page.getByRole('button', {name: 'Open menu', exact: true})
+ expect(newAnchor.element()).not.toBe(initialAnchorElement)
// The new anchor should have the same anchor-name re-applied, and the
// overlay should still reference it via position-anchor.
- expect(newAnchor.style.getPropertyValue('anchor-name')).toBe(initialAnchorName)
+ expect(newAnchor.element().style.getPropertyValue('anchor-name')).toBe(initialAnchorName)
expect(overlay.style.getPropertyValue('position-anchor')).toBe(initialPositionAnchor)
})
})
@@ -959,7 +947,7 @@ describe('ActionMenu', () => {
const mockOnClick = vi.fn()
const mockOnKeyDown = vi.fn()
- const component = HTMLRender(
+ HTMLRender(
Open menu
@@ -973,21 +961,20 @@ describe('ActionMenu', () => {
,
)
- const user = userEvent.setup()
- const button = component.getByRole('button')
- await user.click(button)
+ const button = page.getByRole('button')
+ await userEvent.click(button)
- expect(component.getByRole('menu')).toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
expect(mockOnClick).toHaveBeenCalledTimes(1)
// select and close menu
- const menuItems = component.getAllByRole('menuitem')
- await user.click(menuItems[0])
- expect(component.queryByRole('menu')).toBeNull()
+ const menuItems = page.getByRole('menuitem').all()
+ await userEvent.click(menuItems[0])
+ await expect.element(page.getByRole('menu')).not.toBeInTheDocument()
- expect(button).toEqual(document.activeElement)
- await user.keyboard('{Enter}')
- expect(component.queryByRole('menu')).toBeInTheDocument()
+ await expect.element(button).toHaveFocus()
+ await userEvent.keyboard('{Enter}')
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
expect(mockOnKeyDown).toHaveBeenCalledTimes(1)
})
@@ -995,7 +982,7 @@ describe('ActionMenu', () => {
const mockOnClick = vi.fn()
const mockOnKeyDown = vi.fn()
- const component = HTMLRender(
+ HTMLRender(
{
,
)
- const user = userEvent.setup()
- const button = component.getByRole('button')
- await user.click(button)
+ const button = page.getByRole('button')
+ await userEvent.click(button)
- expect(component.getByRole('menu')).toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
expect(mockOnClick).toHaveBeenCalledTimes(1)
// select and close menu
- const menuItems = component.getAllByRole('menuitem')
- await user.click(menuItems[0])
- expect(component.queryByRole('menu')).toBeNull()
+ const menuItems = page.getByRole('menuitem').all()
+ await userEvent.click(menuItems[0])
+ await expect.element(page.getByRole('menu')).not.toBeInTheDocument()
- expect(button).toEqual(document.activeElement)
- await user.keyboard('{Enter}')
- expect(component.queryByRole('menu')).toBeInTheDocument()
+ await expect.element(button).toHaveFocus()
+ await userEvent.keyboard('{Enter}')
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
expect(mockOnKeyDown).toHaveBeenCalledTimes(1)
})
@@ -1036,7 +1022,7 @@ describe('ActionMenu', () => {
const mockOnClick = vi.fn()
const mockOnKeyDown = vi.fn()
- const component = HTMLRender(
+ HTMLRender(
@@ -1052,21 +1038,20 @@ describe('ActionMenu', () => {
,
)
- const user = userEvent.setup()
- const button = component.getByRole('button')
- await user.click(button)
+ const button = page.getByRole('button')
+ await userEvent.click(button)
- expect(component.getByRole('menu')).toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
expect(mockOnClick).toHaveBeenCalledTimes(1)
// select and close menu
- const menuItems = component.getAllByRole('menuitem')
- await user.click(menuItems[0])
- expect(component.queryByRole('menu')).toBeNull()
+ const menuItems = page.getByRole('menuitem').all()
+ await userEvent.click(menuItems[0])
+ await expect.element(page.getByRole('menu')).not.toBeInTheDocument()
- expect(button).toEqual(document.activeElement)
- await user.keyboard('{Enter}')
- expect(component.queryByRole('menu')).toBeInTheDocument()
+ await expect.element(button).toHaveFocus()
+ await userEvent.keyboard('{Enter}')
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
expect(mockOnKeyDown).toHaveBeenCalledTimes(1)
})
@@ -1074,7 +1059,7 @@ describe('ActionMenu', () => {
const mockOnClick = vi.fn()
const mockOnKeyDown = vi.fn()
- const component = HTMLRender(
+ HTMLRender(
@@ -1095,21 +1080,22 @@ describe('ActionMenu', () => {
,
)
- const user = userEvent.setup()
- const button = component.getByRole('button')
- await user.click(button)
+ const button = page.getByRole('button')
+ await userEvent.click(button)
- expect(component.getByRole('menu')).toBeInTheDocument()
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
expect(mockOnClick).toHaveBeenCalledTimes(1)
+ await userEvent.unhover(button)
+
// select and close menu
- const menuItems = component.getAllByRole('menuitem')
- await user.click(menuItems[0])
- expect(component.queryByRole('menu')).toBeNull()
+ const menuItems = page.getByRole('menuitem').all()
+ await userEvent.click(menuItems[0])
+ await expect.element(page.getByRole('menu')).not.toBeInTheDocument()
- expect(button).toEqual(document.activeElement)
- await user.keyboard('{Enter}')
- expect(component.queryByRole('menu')).toBeInTheDocument()
+ await expect.element(button).toHaveFocus()
+ await userEvent.keyboard('{Enter}')
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
expect(mockOnKeyDown).toHaveBeenCalledTimes(1)
})
})
@@ -1125,7 +1111,7 @@ describe('ActionMenu', () => {
it('should enable displayInViewport when ActionMenu is inside a dialog', async () => {
// When the ActionMenu is wrapped in a Dialog, it's inside a dialog context.
// displayInViewport should be automatically enabled.
- const component = HTMLRender(
+ HTMLRender(
,
)
- const user = userEvent.setup()
- const button = component.getByRole('button', {name: 'Toggle Menu'})
- await user.click(button)
+ const button = page.getByRole('button', {name: 'Toggle Menu', exact: true})
+ await userEvent.click(button)
- await waitFor(() => {
- expect(component.queryByRole('menu')).toBeInTheDocument()
- })
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
// Verify getAnchoredPosition was called with displayInViewport: true
await waitFor(() => {
@@ -1159,7 +1142,7 @@ describe('ActionMenu', () => {
it('should not enable displayInViewport when ActionMenu is NOT inside a dialog', async () => {
// Without being wrapped in a Dialog, the ActionMenu is not in a dialog context.
// displayInViewport should remain at its default (false/undefined).
- const component = HTMLRender(
+ HTMLRender(
Toggle Menu
@@ -1170,13 +1153,10 @@ describe('ActionMenu', () => {
,
)
- const user = userEvent.setup()
- const button = component.getByRole('button')
- await user.click(button)
+ const button = page.getByRole('button')
+ await userEvent.click(button)
- await waitFor(() => {
- expect(component.queryByRole('menu')).toBeInTheDocument()
- })
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
// Verify getAnchoredPosition was called without displayInViewport enabled
await waitFor(() => {
@@ -1191,7 +1171,7 @@ describe('ActionMenu', () => {
it('should respect explicit displayInViewport prop over default logic', async () => {
// Test that an explicit displayInViewport=false prop overrides the automatic
// detection, even when the ActionMenu is inside a dialog.
- const component = HTMLRender(
+ HTMLRender(
,
)
- const user = userEvent.setup()
- const button = component.getByRole('button', {name: 'Toggle Menu'})
- await user.click(button)
+ const button = page.getByRole('button', {name: 'Toggle Menu', exact: true})
+ await userEvent.click(button)
- await waitFor(() => {
- expect(component.queryByRole('menu')).toBeInTheDocument()
- })
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
// Verify getAnchoredPosition was called with displayInViewport: false (explicit override)
await waitFor(() => {
@@ -1225,7 +1202,7 @@ describe('ActionMenu', () => {
it('should respect explicit displayInViewport=true prop', async () => {
// Test that an explicit displayInViewport=true prop works regardless of
// the dialog context.
- const component = HTMLRender(
+ HTMLRender(
Toggle Menu
@@ -1236,13 +1213,10 @@ describe('ActionMenu', () => {
,
)
- const user = userEvent.setup()
- const button = component.getByRole('button')
- await user.click(button)
+ const button = page.getByRole('button')
+ await userEvent.click(button)
- await waitFor(() => {
- expect(component.queryByRole('menu')).toBeInTheDocument()
- })
+ await expect.element(page.getByRole('menu')).toBeInTheDocument()
// Verify getAnchoredPosition was called with displayInViewport: true (explicit override)
await waitFor(() => {
diff --git a/packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx b/packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx
index 557cc896ee6..2ac3942e87d 100644
--- a/packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx
+++ b/packages/react/src/AnchoredOverlay/AnchoredOverlay.test.tsx
@@ -1,7 +1,7 @@
+import {page, userEvent} from 'vitest/browser'
import {createRef, useCallback, useRef, useState} from 'react'
import {describe, expect, it, vi} from 'vitest'
-import {act, fireEvent, render} from '@testing-library/react'
-import userEvent from '@testing-library/user-event'
+import {act, render} from '@testing-library/react'
import {AnchoredOverlay} from '../AnchoredOverlay'
import {Button} from '../Button'
import BaseStyles from '../BaseStyles'
@@ -97,14 +97,14 @@ describe.each([true, false])(
it('should call onOpen when the anchor is clicked', async () => {
const mockOpenCallback = vi.fn()
const mockCloseCallback = vi.fn()
- const anchoredOverlay = render(
+ render(
,
)
- const anchor = anchoredOverlay.baseElement.querySelector('[aria-haspopup="true"]')!
+ const anchor = page.getByRole('button', {name: 'Anchor Button', exact: true})
await userEvent.click(anchor)
expect(mockOpenCallback).toHaveBeenCalledTimes(1)
@@ -115,15 +115,18 @@ describe.each([true, false])(
it('should call onOpen when the anchor activated by a key press', async () => {
const mockOpenCallback = vi.fn()
const mockCloseCallback = vi.fn()
- const anchoredOverlay = render(
+ render(
,
)
- const anchor = anchoredOverlay.baseElement.querySelector('[aria-haspopup="true"]')!
- fireEvent.keyDown(anchor, {key: ' '})
+ const anchor = page.getByRole('button', {name: 'Anchor Button', exact: true})
+ act(() => {
+ anchor.element().focus()
+ })
+ await userEvent.keyboard(' ')
expect(mockOpenCallback).toHaveBeenCalledTimes(1)
expect(mockOpenCallback).toHaveBeenCalledWith('anchor-key-press')
@@ -133,15 +136,20 @@ describe.each([true, false])(
it('should call onClose when the user clicks off of the overlay', async () => {
const mockOpenCallback = vi.fn()
const mockCloseCallback = vi.fn()
- const anchoredOverlay = render(
- ,
+ render(
+ <>
+
+
+ >,
)
- await userEvent.click(anchoredOverlay.baseElement)
+ await userEvent.click(page.getByRole('button', {name: 'Outside overlay', exact: true}))
expect(mockOpenCallback).toHaveBeenCalledTimes(0)
expect(mockCloseCallback).toHaveBeenCalledTimes(1)
@@ -277,7 +285,7 @@ describe('AnchoredOverlay scroll/resize cascade', () => {
)
// Open then close the overlay so position has a non-undefined last value.
- await userEvent.click(document.body.querySelector('button')!)
+ await userEvent.click(page.getByRole('button', {name: 'Toggle', exact: true}))
await userEvent.keyboard('{Escape}')
// Let any pending close-time effects flush.
@@ -706,7 +714,7 @@ describe('AnchoredOverlay CSS anchor positioning viewport handling', () => {
})
describe('AnchoredOverlay anchor element replacement', () => {
- it('should re-apply anchor-name to a new anchor DOM element when the overlay reopens', () => {
+ it('should re-apply anchor-name to a new anchor DOM element when the overlay reopens', async () => {
function TestComponent() {
const anchorRef = useRef(null)
const [open, setOpen] = useState(true)
@@ -736,31 +744,25 @@ describe('AnchoredOverlay anchor element replacement', () => {
)
}
- const {baseElement} = render()
+ render()
// Verify anchor-name is set on the initial anchor element
- const initialAnchor = baseElement.querySelector('[data-testid="anchor"]') as HTMLElement
+ const initialAnchor = page.getByTestId('anchor').element()
expect(initialAnchor.style.getPropertyValue('anchor-name')).not.toBe('')
const anchorName = initialAnchor.style.getPropertyValue('anchor-name')
// Close the overlay
- const toggleButton = baseElement.querySelector('[data-testid="toggle"]') as HTMLElement
- act(() => {
- toggleButton.click()
- })
+ const toggleButton = page.getByTestId('toggle')
+ await userEvent.click(toggleButton)
// Replace the anchor DOM element by changing its key while overlay is closed
- const switchButton = baseElement.querySelector('[data-testid="switch"]') as HTMLElement
- act(() => {
- switchButton.click()
- })
+ const switchButton = page.getByTestId('switch')
+ await userEvent.click(switchButton)
// Reopen the overlay — the new anchor should get anchor-name before paint
- act(() => {
- toggleButton.click()
- })
+ await userEvent.click(toggleButton)
- const newAnchor = baseElement.querySelector('[data-testid="anchor"]') as HTMLElement
+ const newAnchor = page.getByTestId('anchor').element()
expect(newAnchor).not.toBe(initialAnchor)
expect(newAnchor.style.getPropertyValue('anchor-name')).toBe(anchorName)
})