@@ -56,6 +56,17 @@ describe('useComposerDefaultFocus', () => {
5656 return container . querySelector ( '[data-testid="composer"]' ) as HTMLDivElement ;
5757 }
5858
59+ function pressKey ( key : string , init : KeyboardEventInit = { } ) : KeyboardEvent {
60+ const event = new KeyboardEvent ( 'keydown' , {
61+ key,
62+ bubbles : true ,
63+ cancelable : true ,
64+ ...init ,
65+ } ) ;
66+ act ( ( ) => document . body . dispatchEvent ( event ) ) ;
67+ return event ;
68+ }
69+
5970 it ( 'focuses the composer when the active session has no input owner' , ( ) => {
6071 const composer = renderProbe ( { sessionId : 'session-a' , isSceneActive : true } ) ;
6172
@@ -126,8 +137,84 @@ describe('useComposerDefaultFocus', () => {
126137 button . focus ( ) ;
127138
128139 const composer = renderProbe ( { sessionId : 'session-a' , isSceneActive : true } ) ;
140+ pressKey ( 'a' ) ;
129141
130142 expect ( document . activeElement ) . toBe ( button ) ;
131143 expect ( document . activeElement ) . not . toBe ( composer ) ;
132144 } ) ;
145+
146+ it ( 'moves focus to the composer for a printable character without consuming it' , ( ) => {
147+ const composer = renderProbe ( { sessionId : 'session-a' , isSceneActive : true } ) ;
148+ const button = document . createElement ( 'button' ) ;
149+ document . body . appendChild ( button ) ;
150+ button . focus ( ) ;
151+
152+ const event = pressKey ( 'a' ) ;
153+
154+ expect ( document . activeElement ) . toBe ( composer ) ;
155+ expect ( event . defaultPrevented ) . toBe ( false ) ;
156+ } ) ;
157+
158+ it ( 'moves focus to the composer for Space without consuming it' , ( ) => {
159+ const composer = renderProbe ( { sessionId : 'session-a' , isSceneActive : true } ) ;
160+ const button = document . createElement ( 'button' ) ;
161+ document . body . appendChild ( button ) ;
162+ button . focus ( ) ;
163+
164+ const event = pressKey ( ' ' ) ;
165+
166+ expect ( document . activeElement ) . toBe ( composer ) ;
167+ expect ( event . defaultPrevented ) . toBe ( false ) ;
168+ } ) ;
169+
170+ it . each ( [ 'Dead' , 'Process' ] ) ( 'focuses the composer for %s text entry' , key => {
171+ const composer = renderProbe ( { sessionId : 'session-a' , isSceneActive : true } ) ;
172+ const button = document . createElement ( 'button' ) ;
173+ document . body . appendChild ( button ) ;
174+ button . focus ( ) ;
175+
176+ pressKey ( key ) ;
177+
178+ expect ( document . activeElement ) . toBe ( composer ) ;
179+ } ) ;
180+
181+ it ( 'does not steal keyboard shortcuts or navigation keys' , ( ) => {
182+ const composer = renderProbe ( { sessionId : 'session-a' , isSceneActive : true } ) ;
183+ const button = document . createElement ( 'button' ) ;
184+ document . body . appendChild ( button ) ;
185+
186+ for ( const [ key , init ] of [
187+ [ 'k' , { ctrlKey : true } ] ,
188+ [ 'k' , { metaKey : true } ] ,
189+ [ 'k' , { altKey : true } ] ,
190+ [ 'Enter' , { } ] ,
191+ [ 'ArrowDown' , { } ] ,
192+ ] satisfies Array < [ string , KeyboardEventInit ] > ) {
193+ button . focus ( ) ;
194+ pressKey ( key , init ) ;
195+ expect ( document . activeElement ) . toBe ( button ) ;
196+ expect ( document . activeElement ) . not . toBe ( composer ) ;
197+ }
198+ } ) ;
199+
200+ it ( 'preserves another visible text input while typing' , ( ) => {
201+ const alternateInput = document . createElement ( 'textarea' ) ;
202+ document . body . appendChild ( alternateInput ) ;
203+ markRendered ( alternateInput ) ;
204+ alternateInput . focus ( ) ;
205+
206+ const composer = renderProbe ( { sessionId : 'session-a' , isSceneActive : true } ) ;
207+ pressKey ( 'a' ) ;
208+
209+ expect ( document . activeElement ) . toBe ( alternateInput ) ;
210+ expect ( document . activeElement ) . not . toBe ( composer ) ;
211+ } ) ;
212+
213+ it ( 'does not focus an inactive scene while typing' , ( ) => {
214+ const composer = renderProbe ( { sessionId : 'session-a' , isSceneActive : false } ) ;
215+
216+ pressKey ( 'a' ) ;
217+
218+ expect ( document . activeElement ) . not . toBe ( composer ) ;
219+ } ) ;
133220} ) ;
0 commit comments