@@ -29,6 +29,13 @@ afterEach(() => {
2929 localStorage . clear ( ) ;
3030} ) ;
3131
32+ async function waitForFocus ( element : HTMLElement , message : string ) {
33+ // Comparing DOM objects makes assertion formatting block the RAF we await.
34+ await waitFor ( ( ) => assert . equal ( document . activeElement === element , true , message ) , {
35+ onTimeout : ( error ) => error ,
36+ } ) ;
37+ }
38+
3239function mount ( ) {
3340 let closeCount = 0 ;
3441 let abortCount = 0 ;
@@ -56,11 +63,11 @@ test('Settings has a named modal and close button, autofocuses inside, and resto
5663 const dialog = screen . getByRole ( 'dialog' , { name : 'Settings' } ) ;
5764 assert . equal ( dialog . getAttribute ( 'aria-modal' ) , 'true' ) ;
5865 const close = within ( dialog ) . getByRole ( 'button' , { name : 'Close settings' } ) ;
59- await waitFor ( ( ) => assert . equal ( document . activeElement , close ) ) ;
60- fireEvent . click ( close ) ;
66+ await waitForFocus ( close , 'Settings close button receives initial focus' ) ;
67+ await act ( async ( ) => { fireEvent . click ( close ) ; } ) ;
6168 assert . equal ( view . closeCount ( ) , 1 ) ;
62- assert . equal ( screen . queryByRole ( 'dialog' ) , null ) ;
63- assert . equal ( document . activeElement , view . opener ) ;
69+ assert . equal ( screen . queryByRole ( 'dialog' ) === null , true , 'Settings closes after clicking its close button' ) ;
70+ await waitForFocus ( view . opener , 'Closing Settings restores focus to its opener' ) ;
6471} ) ;
6572
6673test ( 'Escape on either Appearance navigation button closes Settings once and restores focus and scrolling' , async ( ) => {
@@ -69,14 +76,14 @@ test('Escape on either Appearance navigation button closes Settings once and res
6976 for ( const index of [ 0 , 1 ] ) {
7077 view . open ( ) ;
7178 const appearance = screen . getAllByRole ( 'button' , { name : 'Appearance' } ) [ index ] ;
72- await waitFor ( ( ) => assert . notEqual ( document . activeElement , view . opener ) ) ;
79+ await waitForFocus ( screen . getByRole ( 'button' , { name : 'Close settings' } ) , 'Settings receives focus before Escape' ) ;
7380 act ( ( ) => appearance . focus ( ) ) ;
7481 assert . equal ( document . body . style . overflow , 'hidden' ) ;
75- fireEvent . keyDown ( appearance , { key : 'Escape' } ) ;
82+ await act ( async ( ) => { fireEvent . keyDown ( appearance , { key : 'Escape' } ) ; } ) ;
7683 assert . equal ( view . closeCount ( ) , index + 1 ) ;
7784 assert . equal ( view . abortCount ( ) , 0 , 'closing Settings must not abort the background run' ) ;
78- assert . equal ( screen . queryByRole ( 'button' , { name : 'Close settings' } ) , null ) ;
79- assert . equal ( document . activeElement , view . opener ) ;
85+ assert . equal ( screen . queryByRole ( 'button' , { name : 'Close settings' } ) === null , true , 'Escape closes Settings' ) ;
86+ await waitForFocus ( view . opener , 'Escape restores focus to the Settings opener' ) ;
8087 assert . equal ( document . body . style . overflow , 'scroll' ) ;
8188 }
8289 fireEvent . keyDown ( view . opener , { key : 'Escape' } ) ;
@@ -89,11 +96,11 @@ test('Tab and Shift+Tab stay inside Settings at both ends of the Appearance cont
8996 const dialog = screen . getByRole ( 'dialog' , { name : 'Settings' } ) ;
9097 const first = within ( dialog ) . getByRole ( 'button' , { name : 'Close settings' } ) ;
9198 const last = within ( dialog ) . getByDisplayValue ( 'Alphabetical' ) ;
92- await waitFor ( ( ) => assert . equal ( document . activeElement , first ) ) ;
99+ await waitForFocus ( first , 'Settings close button receives initial focus' ) ;
93100 act ( ( ) => last . focus ( ) ) ;
94- fireEvent . keyDown ( last , { key : 'Tab' } ) ;
95- assert . equal ( document . activeElement , first ) ;
96- fireEvent . keyDown ( first , { key : 'Tab' , shiftKey : true } ) ;
97- assert . equal ( document . activeElement , last ) ;
101+ await act ( async ( ) => { fireEvent . keyDown ( last , { key : 'Tab' } ) ; } ) ;
102+ await waitForFocus ( first , 'Tab wraps focus to the first Settings control' ) ;
103+ await act ( async ( ) => { fireEvent . keyDown ( first , { key : 'Tab' , shiftKey : true } ) ; } ) ;
104+ await waitForFocus ( last , 'Shift+Tab wraps focus to the last Settings control' ) ;
98105 assert . equal ( view . closeCount ( ) , 0 ) ;
99106} ) ;
0 commit comments