11import { describe , it , expect , vi , beforeEach } from "vitest" ;
2- import { render , screen , fireEvent , within , waitFor } from "@testing-library/react" ;
2+ import { render , screen , fireEvent , within , waitFor , act } from "@testing-library/react" ;
33import React , { useState } from "react" ;
44import { WorkflowSplitView } from "./WorkflowSplitView" ;
55
@@ -47,7 +47,7 @@ describe("WorkflowSplitView (desktop keyboard navigation)", () => {
4747 expect ( gridcells . length ) . toBeGreaterThanOrEqual ( 2 ) ;
4848 } ) ;
4949
50- it ( "roves focus across containers with ArrowRight/Left and Home/End" , ( ) => {
50+ it ( "roves focus across containers with ArrowRight/Left and Home/End" , async ( ) => {
5151 render (
5252 < WorkflowSplitView
5353 steps = { steps }
@@ -65,20 +65,30 @@ describe("WorkflowSplitView (desktop keyboard navigation)", () => {
6565 const grid = document . querySelector ( ".nhsfdp-workflow-grid.panes-3" ) as HTMLElement ;
6666 const cells = Array . from ( grid . querySelectorAll ( '[role="gridcell"]' ) ) as HTMLElement [ ] ;
6767 // focus first container
68- cells [ 0 ] . focus ( ) ;
69-
70- fireEvent . keyDown ( cells [ 0 ] , { key : "ArrowRight" } ) ;
71- expect ( document . activeElement ) . toBe ( cells [ 1 ] ) ;
72-
73- fireEvent . keyDown ( document . activeElement as Element , { key : "End" } ) ;
74- expect ( document . activeElement ) . toBe ( cells [ cells . length - 1 ] ) ;
75-
76- fireEvent . keyDown ( document . activeElement as Element , { key : "Home" } ) ;
77- expect ( document . activeElement ) . toBe ( cells [ 0 ] ) ;
78-
79- fireEvent . keyDown ( document . activeElement as Element , { key : "ArrowLeft" } ) ;
68+ act ( ( ) => {
69+ cells [ 0 ] . focus ( ) ;
70+ } ) ;
71+
72+ act ( ( ) => {
73+ fireEvent . keyDown ( cells [ 0 ] , { key : "ArrowRight" } ) ;
74+ } ) ;
75+ await waitFor ( ( ) => expect ( document . activeElement ) . toBe ( cells [ 1 ] ) ) ;
76+
77+ act ( ( ) => {
78+ fireEvent . keyDown ( document . activeElement as Element , { key : "End" } ) ;
79+ } ) ;
80+ await waitFor ( ( ) => expect ( document . activeElement ) . toBe ( cells [ cells . length - 1 ] ) ) ;
81+
82+ act ( ( ) => {
83+ fireEvent . keyDown ( document . activeElement as Element , { key : "Home" } ) ;
84+ } ) ;
85+ await waitFor ( ( ) => expect ( document . activeElement ) . toBe ( cells [ 0 ] ) ) ;
86+
87+ act ( ( ) => {
88+ fireEvent . keyDown ( document . activeElement as Element , { key : "ArrowLeft" } ) ;
89+ } ) ;
8090 // Should remain on first
81- expect ( document . activeElement ) . toBe ( cells [ 0 ] ) ;
91+ await waitFor ( ( ) => expect ( document . activeElement ) . toBe ( cells [ 0 ] ) ) ;
8292 } ) ;
8393
8494 it ( "Enter enters nav container and focuses listbox active option; Escape ascends back to container" , ( ) => {
@@ -97,9 +107,13 @@ describe("WorkflowSplitView (desktop keyboard navigation)", () => {
97107
98108 const grid = document . querySelector ( ".nhsfdp-workflow-grid.panes-3" ) as HTMLElement ;
99109 const navCell = grid . querySelector ( '[role="gridcell"][aria-label="Primary navigation"]' ) as HTMLElement ;
100- navCell . focus ( ) ;
110+ act ( ( ) => {
111+ navCell . focus ( ) ;
112+ } ) ;
101113
102- fireEvent . keyDown ( navCell , { key : "Enter" } ) ;
114+ act ( ( ) => {
115+ fireEvent . keyDown ( navCell , { key : "Enter" } ) ;
116+ } ) ;
103117
104118 const listbox = within ( navCell ) . getByRole ( "listbox" ) ;
105119 expect ( listbox ) . toBeTruthy ( ) ;
@@ -112,7 +126,9 @@ describe("WorkflowSplitView (desktop keyboard navigation)", () => {
112126 }
113127
114128 // Escape should move focus back to the nav container
115- fireEvent . keyDown ( listbox , { key : "Escape" } ) ;
129+ act ( ( ) => {
130+ fireEvent . keyDown ( listbox , { key : "Escape" } ) ;
131+ } ) ;
116132 expect ( document . activeElement ) . toBe ( navCell ) ;
117133 } ) ;
118134
@@ -134,16 +150,22 @@ describe("WorkflowSplitView (desktop keyboard navigation)", () => {
134150 const listbox = within ( navCell ) . getByRole ( "listbox" ) ;
135151
136152 // Focus listbox and move through options (use ArrowDown twice to reach "Three")
137- ( listbox as HTMLElement ) . focus ( ) ;
138- fireEvent . keyDown ( listbox , { key : "End" } ) ;
153+ act ( ( ) => {
154+ ( listbox as HTMLElement ) . focus ( ) ;
155+ } ) ;
156+ act ( ( ) => {
157+ fireEvent . keyDown ( listbox , { key : "End" } ) ;
158+ } ) ;
139159
140160 const options = within ( listbox ) . getAllByRole ( "option" ) ;
141161 expect ( options . length ) . toBe ( steps . length ) ;
142162
143163 // Re-query the listbox to ensure we have a fresh live node, then activate via Enter
144164 const freshListbox = within ( navCell ) . getByRole ( "listbox" ) ;
145- fireEvent . keyDown ( freshListbox , { key : "Enter" , code : "Enter" , keyCode : 13 , charCode : 13 } ) ;
146- fireEvent . keyUp ( freshListbox , { key : "Enter" , code : "Enter" , keyCode : 13 , charCode : 13 } ) ;
165+ act ( ( ) => {
166+ fireEvent . keyDown ( freshListbox , { key : "Enter" , code : "Enter" , keyCode : 13 , charCode : 13 } ) ;
167+ fireEvent . keyUp ( freshListbox , { key : "Enter" , code : "Enter" , keyCode : 13 , charCode : 13 } ) ;
168+ } ) ;
147169 // Re-query listbox post-activation to avoid stale references and assert aria-activedescendant
148170 await waitFor ( ( ) => {
149171 const freshListbox = within ( navCell ) . getByRole ( "listbox" ) ;
@@ -173,8 +195,12 @@ describe("WorkflowSplitView (desktop keyboard navigation)", () => {
173195 const contentAside = screen . getByRole ( "gridcell" , { name : / B r e a d c r u m b s | N a m e | T w o | O n e / } ) ;
174196 const input = within ( contentAside ) . getByLabelText ( "Name" ) ;
175197
176- ( input as HTMLElement ) . focus ( ) ;
177- fireEvent . keyDown ( input , { key : "ArrowLeft" } ) ;
198+ act ( ( ) => {
199+ ( input as HTMLElement ) . focus ( ) ;
200+ } ) ;
201+ act ( ( ) => {
202+ fireEvent . keyDown ( input , { key : "ArrowLeft" } ) ;
203+ } ) ;
178204
179205 // Focus remains in input
180206 expect ( document . activeElement ) . toBe ( input ) ;
@@ -205,9 +231,13 @@ describe("WorkflowSplitView (desktop keyboard navigation)", () => {
205231 const secondaryCell = cells [ 2 ] ;
206232
207233 // Focus content container and move right
208- contentCell . focus ( ) ;
234+ act ( ( ) => {
235+ contentCell . focus ( ) ;
236+ } ) ;
209237 expect ( document . activeElement ) . toBe ( contentCell ) ;
210- fireEvent . keyDown ( contentCell , { key : "ArrowRight" } ) ;
238+ act ( ( ) => {
239+ fireEvent . keyDown ( contentCell , { key : "ArrowRight" } ) ;
240+ } ) ;
211241
212242 // Focus should now be on the secondary container (content loses focus)
213243 await waitFor ( ( ) => expect ( document . activeElement ) . toBe ( secondaryCell ) ) ;
0 commit comments