1- import { fireEvent , render } from '@testing-library/react'
1+ import { act , render } from '@testing-library/react'
2+ import { userEvent } from '@testing-library/user-event'
23import { describe , expect , it , vi } from 'vitest'
34import Dropdown from './Dropdown'
45import styles from './Dropdown.module.css'
@@ -14,7 +15,7 @@ describe('Dropdown Component', () => {
1415 expect ( div ?. classList ) . toContain ( styles . dropdownLeft )
1516 } )
1617
17- it ( 'toggles dropdown content on button click' , ( ) => {
18+ it ( 'toggles dropdown content on button click' , async ( ) => {
1819 const { container : { children : [ div ] } , getByRole } = render (
1920 < Dropdown label = 'go' >
2021 < div > Child 1</ div >
@@ -24,15 +25,16 @@ describe('Dropdown Component', () => {
2425 const dropdownButton = getByRole ( 'button' )
2526
2627 // open menu with click
27- fireEvent . click ( dropdownButton )
28+ const user = userEvent . setup ( )
29+ await user . click ( dropdownButton )
2830 expect ( div ?. children [ 0 ] ?. getAttribute ( 'aria-expanded' ) ) . toBe ( 'true' )
2931
3032 // click again to close
31- fireEvent . click ( dropdownButton )
33+ await user . click ( dropdownButton )
3234 expect ( div ?. children [ 0 ] ?. getAttribute ( 'aria-expanded' ) ) . toBe ( 'false' )
3335 } )
3436
35- it ( 'closes dropdown when clicking outside' , ( ) => {
37+ it ( 'closes dropdown when clicking outside' , async ( ) => {
3638 const { container : { children : [ div ] } , getByRole } = render (
3739 < Dropdown >
3840 < div > Child 1</ div >
@@ -41,15 +43,16 @@ describe('Dropdown Component', () => {
4143 )
4244
4345 const dropdownButton = getByRole ( 'button' )
44- fireEvent . click ( dropdownButton ) // open dropdown
46+ const user = userEvent . setup ( )
47+ await user . click ( dropdownButton ) // open dropdown
4548 expect ( div ?. children [ 0 ] ?. getAttribute ( 'aria-expanded' ) ) . toBe ( 'true' )
4649
4750 // Simulate a click outside
48- fireEvent . mouseDown ( document )
51+ await user . click ( document . body )
4952 expect ( div ?. children [ 0 ] ?. getAttribute ( 'aria-expanded' ) ) . toBe ( 'false' )
5053 } )
5154
52- it ( 'does not close dropdown when clicking inside' , ( ) => {
55+ it ( 'closes dropdown when clicking inside' , async ( ) => {
5356 const { container : { children : [ div ] } , getByRole, getByText } = render (
5457 < Dropdown >
5558 < div > Child 1</ div >
@@ -58,16 +61,17 @@ describe('Dropdown Component', () => {
5861 )
5962
6063 const dropdownButton = getByRole ( 'button' )
61- fireEvent . click ( dropdownButton ) // open dropdown
64+ const user = userEvent . setup ( )
65+ await user . click ( dropdownButton ) // open dropdown
6266 expect ( div ?. children [ 0 ] ?. getAttribute ( 'aria-expanded' ) ) . toBe ( 'true' )
6367
6468 const dropdownContent = getByText ( 'Child 1' ) . parentElement
6569 if ( ! dropdownContent ) throw new Error ( 'Dropdown content not found' )
66- fireEvent . mouseDown ( dropdownContent )
67- expect ( div ?. children [ 0 ] ?. getAttribute ( 'aria-expanded' ) ) . toBe ( 'true ' )
70+ await user . click ( dropdownContent )
71+ expect ( div ?. children [ 0 ] ?. getAttribute ( 'aria-expanded' ) ) . toBe ( 'false ' )
6872 } )
6973
70- it ( 'closes dropdown on escape key press' , ( ) => {
74+ it ( 'closes dropdown on escape key press' , async ( ) => {
7175 const { container : { children : [ div ] } , getByRole } = render (
7276 < Dropdown >
7377 < div > Child 1</ div >
@@ -76,11 +80,12 @@ describe('Dropdown Component', () => {
7680 )
7781
7882 const dropdownButton = getByRole ( 'button' )
79- fireEvent . click ( dropdownButton ) // open dropdown
83+ const user = userEvent . setup ( )
84+ await user . click ( dropdownButton ) // open dropdown
8085 expect ( div ?. children [ 0 ] ?. getAttribute ( 'aria-expanded' ) ) . toBe ( 'true' )
8186
8287 // Press escape key
83- fireEvent . keyDown ( document , { key : ' Escape' , code : 'Escape' } )
88+ await user . keyboard ( '{ Escape}' )
8489 expect ( div ?. children [ 0 ] ?. getAttribute ( 'aria-expanded' ) ) . toBe ( 'false' )
8590 } )
8691
@@ -107,7 +112,7 @@ describe('Dropdown Component', () => {
107112 } )
108113
109114 // Keyboard navigation tests
110- it ( 'opens dropdown and focuses first item on ArrowDown when closed' , ( ) => {
115+ it ( 'opens dropdown and focuses first item on ArrowDown when closed' , async ( ) => {
111116 const { getByRole, getAllByRole } = render (
112117 < Dropdown label = "Menu" >
113118 < button role = "menuitem" > Item 1</ button >
@@ -120,15 +125,20 @@ describe('Dropdown Component', () => {
120125 // initially closed
121126 expect ( dropdownButton . getAttribute ( 'aria-expanded' ) ) . toBe ( 'false' )
122127
128+ // focus the button
129+ act ( ( ) => {
130+ dropdownButton . focus ( )
131+ } )
123132 // down arrow to open menu
124- fireEvent . keyDown ( dropdownButton , { key : 'ArrowDown' , code : 'ArrowDown' } )
133+ const user = userEvent . setup ( )
134+ await user . keyboard ( '{ArrowDown}' )
125135 expect ( dropdownButton . getAttribute ( 'aria-expanded' ) ) . toBe ( 'true' )
126136
127137 // first menu item should be focused
128138 expect ( document . activeElement ) . toBe ( menuItems [ 0 ] )
129139 } )
130140
131- it ( 'focuses the next item on ArrowDown and wraps to first item if at the end' , ( ) => {
141+ it ( 'focuses the next item on ArrowDown and wraps to first item if at the end' , async ( ) => {
132142 const { getByRole, getAllByRole } = render (
133143 < Dropdown label = "Menu" >
134144 < button role = "menuitem" > Item 1</ button >
@@ -139,19 +149,20 @@ describe('Dropdown Component', () => {
139149 const dropdownButton = getByRole ( 'button' )
140150
141151 // open menu, first item has focus
142- fireEvent . click ( dropdownButton )
152+ const user = userEvent . setup ( )
153+ await user . click ( dropdownButton )
143154 expect ( document . activeElement ) . toBe ( menuItems [ 0 ] )
144155
145156 // second item should be focused
146- fireEvent . keyDown ( menuItems [ 0 ] , { key : ' ArrowDown' , code : 'ArrowDown' } )
157+ await user . keyboard ( '{ ArrowDown}' )
147158 expect ( document . activeElement ) . toBe ( menuItems [ 1 ] )
148159
149160 // wrap back to first item
150- fireEvent . keyDown ( menuItems [ 1 ] , { key : ' ArrowDown' , code : 'ArrowDown' } )
161+ await user . keyboard ( '{ ArrowDown}' )
151162 expect ( document . activeElement ) . toBe ( menuItems [ 0 ] )
152163 } )
153164
154- it ( 'focuses the previous item on ArrowUp and wraps to the last item if at the top' , ( ) => {
165+ it ( 'focuses the previous item on ArrowUp and wraps to the last item if at the top' , async ( ) => {
155166 const { getByRole, getAllByRole } = render (
156167 < Dropdown label = "Menu" >
157168 < button role = "menuitem" > Item 1</ button >
@@ -162,15 +173,16 @@ describe('Dropdown Component', () => {
162173 const dropdownButton = getByRole ( 'button' )
163174
164175 // open menu, first item has focus
165- fireEvent . click ( dropdownButton )
176+ const user = userEvent . setup ( )
177+ await user . click ( dropdownButton )
166178 expect ( document . activeElement ) . toBe ( menuItems [ 0 ] )
167179
168180 // ArrowUp -> should wrap to last item
169- fireEvent . keyDown ( menuItems [ 0 ] , { key : ' ArrowUp' , code : 'ArrowUp' } )
181+ await user . keyboard ( '{ ArrowUp}' )
170182 expect ( document . activeElement ) . toBe ( menuItems [ 1 ] )
171183 } )
172184
173- it ( 'focuses first item on Home key press' , ( ) => {
185+ it ( 'focuses first item on Home key press' , async ( ) => {
174186 const { getByRole, getAllByRole } = render (
175187 < Dropdown label = "Menu" >
176188 < button role = "menuitem" > Item 1</ button >
@@ -182,19 +194,20 @@ describe('Dropdown Component', () => {
182194 const dropdownButton = getByRole ( 'button' )
183195
184196 // open menu, first item has focus
185- fireEvent . click ( dropdownButton )
197+ const user = userEvent . setup ( )
198+ await user . click ( dropdownButton )
186199 expect ( document . activeElement ) . toBe ( menuItems [ 0 ] )
187200
188201 // move to the second item
189- fireEvent . keyDown ( menuItems [ 0 ] , { key : ' ArrowDown' , code : 'ArrowDown' } )
202+ await user . keyboard ( '{ ArrowDown}' )
190203 expect ( document . activeElement ) . toBe ( menuItems [ 1 ] )
191204
192205 // Home key should focus first item
193- fireEvent . keyDown ( menuItems [ 1 ] , { key : ' Home' , code : 'Home' } )
206+ await user . keyboard ( '{ Home}' )
194207 expect ( document . activeElement ) . toBe ( menuItems [ 0 ] )
195208 } )
196209
197- it ( 'focuses last item on End key press' , ( ) => {
210+ it ( 'focuses last item on End key press' , async ( ) => {
198211 const { getByRole, getAllByRole } = render (
199212 < Dropdown label = "Menu" >
200213 < button role = "menuitem" > Item 1</ button >
@@ -206,15 +219,16 @@ describe('Dropdown Component', () => {
206219 const dropdownButton = getByRole ( 'button' )
207220
208221 // open menu, first item has focus
209- fireEvent . click ( dropdownButton )
222+ const user = userEvent . setup ( )
223+ await user . click ( dropdownButton )
210224 expect ( document . activeElement ) . toBe ( menuItems [ 0 ] )
211225
212226 // End key should focus the last item
213- fireEvent . keyDown ( menuItems [ 0 ] , { key : ' End' , code : 'End' } )
227+ await user . keyboard ( '{ End}' )
214228 expect ( document . activeElement ) . toBe ( menuItems [ 2 ] )
215229 } )
216230
217- it ( 'closes the menu and puts focus back on the button on Escape' , ( ) => {
231+ it ( 'closes the menu and puts focus back on the button on Escape' , async ( ) => {
218232 const { getByRole, getAllByRole } = render (
219233 < Dropdown label = "Menu" >
220234 < button role = "menuitem" > Item 1</ button >
@@ -225,12 +239,13 @@ describe('Dropdown Component', () => {
225239 const dropdownButton = getByRole ( 'button' )
226240
227241 // open menu, first item has focus
228- fireEvent . click ( dropdownButton )
242+ const user = userEvent . setup ( )
243+ await user . click ( dropdownButton )
229244 expect ( document . activeElement ) . toBe ( menuItems [ 0 ] )
230245 expect ( dropdownButton . getAttribute ( 'aria-expanded' ) ) . toBe ( 'true' )
231246
232247 // escape closes menu
233- fireEvent . keyDown ( menuItems [ 0 ] , { key : ' Escape' , code : 'Escape' } )
248+ await user . keyboard ( '{ Escape}' )
234249 expect ( dropdownButton . getAttribute ( 'aria-expanded' ) ) . toBe ( 'false' )
235250
236251 // focus returns to the button
0 commit comments