@@ -14,19 +14,26 @@ import { createInteractionDevice } from './__tests__/test-utils/index.ts';
1414
1515const TAB_BAR_RECT : Rect = { x : 148 , y : 791 , width : 104 , height : 83 } ;
1616
17- /** The contract fixture with its app-owned rects moved: the variants stay one tree, not copies. */
18- function keyboardTree ( params : { tabRect ?: Rect ; keyboardOwnedDy ?: number } = { } ) : SnapshotState {
19- if ( ! params . tabRect && params . keyboardOwnedDy === undefined )
20- return keyboardCoveredTabBarSnapshot ( ) ;
17+ /** The contract fixture with its app-owned button moved, so the variants stay one tree, not copies. */
18+ function keyboardTree ( params : { tabRect ?: Rect } = { } ) : SnapshotState {
19+ const tabRect = params . tabRect ;
20+ if ( ! tabRect ) return keyboardCoveredTabBarSnapshot ( ) ;
2121 return makeSnapshotState (
22- keyboardCoveredTabBarSnapshot ( ) . nodes . map ( ( node ) => {
23- if ( params . tabRect && node . index === 1 ) return { ...node , rect : params . tabRect } ;
24- const keyboardOwned = node . type === 'Keyboard' || node . type === 'Key' ;
25- if ( params . keyboardOwnedDy !== undefined && keyboardOwned && node . rect ) {
26- return { ...node , rect : { ...node . rect , y : node . rect . y + params . keyboardOwnedDy } } ;
27- }
28- return node ;
29- } ) ,
22+ keyboardCoveredTabBarSnapshot ( ) . nodes . map ( ( node ) =>
23+ node . index === 1 ? { ...node , rect : tabRect } : node ,
24+ ) ,
25+ ) ;
26+ }
27+
28+ /**
29+ * Keyboard-owned rects hauled above the docking budget: the tree stops describing a keyboard the
30+ * bottom of the screen belongs to, which is what the app-drawn-keypad and aim cases measure against.
31+ */
32+ function liftKeyboardOffBottomEdge ( nodes : SnapshotState [ 'nodes' ] ) : SnapshotState [ 'nodes' ] {
33+ return nodes . map ( ( node ) =>
34+ node . rect && ( node . type === 'Keyboard' || node . type === 'Key' )
35+ ? { ...node , rect : { ...node . rect , y : node . rect . y - 560 } }
36+ : node ,
3037 ) ;
3138}
3239
@@ -119,7 +126,8 @@ test('no keyboard in the tree means nothing to refuse', async () => {
119126
120127test ( 'an app-drawn keypad that stops short of the bottom edge is not the system keyboard' , async ( ) => {
121128 const calls : Point [ ] = [ ] ;
122- const device = tappedDevice ( keyboardTree ( { keyboardOwnedDy : - 560 } ) , calls ) ;
129+ const appOwnedKeypad = makeSnapshotState ( liftKeyboardOffBottomEdge ( keyboardTree ( ) . nodes ) ) ;
130+ const device = tappedDevice ( appOwnedKeypad , calls ) ;
123131
124132 await device . interactions . click ( ref ( '@e2' ) , { session : 'default' } ) ;
125133
@@ -143,9 +151,8 @@ test('a coordinate behind the keyboard taps anyway and discloses the reason', as
143151} ) ;
144152
145153test ( 'the guard reads the point the interaction dispatches, not the rect center' , async ( ) => {
146- const calls : Point [ ] = [ ] ;
147- // The Form button's own center sits 3 pt above the key plane, but its interactive child owns that
148- // upper region, so the aim point the tap dispatches is pushed down into the keyboard's band.
154+ // The Form button's own center sits 3 pt above the key plane, and its interactive child owns that
155+ // upper region, so the point the tap dispatches is pushed down into the keyboard's band.
149156 const aimShifted = makeSnapshotState ( [
150157 ...keyboardCoveredTabBarSnapshot ( ) . nodes ,
151158 {
@@ -157,41 +164,29 @@ test('the guard reads the point the interaction dispatches, not the rect center'
157164 rect : { x : 148 , y : 500 , width : 104 , height : 83 } ,
158165 hittable : true ,
159166 } ,
160- ] ) ;
161- const shifted = aimShifted . nodes . map ( ( node ) =>
167+ ] ) . nodes . map ( ( node ) =>
162168 node . index === 1 ? { ...node , rect : { x : 148 , y : 500 , width : 104 , height : 160 } } : node ,
163169 ) ;
164- const device = tappedDevice ( makeSnapshotState ( shifted ) , calls ) ;
165170
171+ const calls : Point [ ] = [ ] ;
166172 await assert . rejects (
167- ( ) => device . interactions . click ( ref ( '@e2' ) , { session : 'default' } ) ,
168- ( error : unknown ) => {
169- assert . ok ( error instanceof Error ) ;
170- assert . match ( error . message , / R e f @ e 2 i s b e h i n d t h e v i s i b l e k e y b o a r d / ) ;
171- return true ;
172- } ,
173+ ( ) =>
174+ tappedDevice ( makeSnapshotState ( aimShifted ) , calls ) . interactions . click ( ref ( '@e2' ) , {
175+ session : 'default' ,
176+ } ) ,
177+ / R e f @ e 2 i s b e h i n d t h e v i s i b l e k e y b o a r d / ,
173178 ) ;
174179 assert . deepEqual ( calls , [ ] ) ;
175180
176- // The same tree with its keypad lifted off the bottom edge shows where that tap was aiming: below
177- // the key plane, which is what makes this the aim point's refusal rather than the center's.
178- const aimCalls : Point [ ] = [ ] ;
179- const clearDevice = tappedDevice (
180- makeSnapshotState (
181- shifted . map ( ( node ) =>
182- node . type === 'Keyboard' || node . type === 'Key'
183- ? { ...node , rect : { ...node . rect ! , y : node . rect ! . y - 560 } }
184- : node ,
185- ) ,
186- ) ,
187- aimCalls ,
188- ) ;
189- await clearDevice . interactions . click ( ref ( '@e2' ) , { session : 'default' } ) ;
190- assert . equal ( aimCalls . length , 1 ) ;
191- assert . ok (
192- ( aimCalls [ 0 ] ?. y ?? 0 ) > 583 ,
193- `expected the dispatched aim below the key plane at 583, got ${ aimCalls [ 0 ] ?. y } ` ,
194- ) ;
181+ // The same tree with its keyboard hauled off the bottom edge shows where that tap was aiming: below
182+ // the key plane, which is what makes this the dispatched point's refusal rather than the center's.
183+ const aim : Point [ ] = [ ] ;
184+ await tappedDevice (
185+ makeSnapshotState ( liftKeyboardOffBottomEdge ( aimShifted ) ) ,
186+ aim ,
187+ ) . interactions . click ( ref ( '@e2' ) , { session : 'default' } ) ;
188+ assert . equal ( aim . length , 1 ) ;
189+ assert . ok ( ( aim [ 0 ] ?. y ?? 0 ) > 583 , `expected the dispatched point below 583, got ${ aim [ 0 ] ?. y } ` ) ;
195190} ) ;
196191
197192test ( 'a coordinate on a reported key is the keyboard the caller asked for' , async ( ) => {
0 commit comments