|
| 1 | +import assert from 'node:assert/strict'; |
1 | 2 | import { describe, expect, test } from 'vitest'; |
| 3 | +import { buildGesturePlan } from '../../../contracts/gesture-plan.ts'; |
| 4 | +import { readGesturePayload } from '../../../contracts/gesture-input.ts'; |
| 5 | +import { normalizePublicGesture } from '../../../contracts/gesture-normalization.ts'; |
2 | 6 | import { |
3 | 7 | projectMaestroPublicOperation, |
4 | 8 | type MaestroPublicOperation, |
@@ -104,7 +108,10 @@ describe('Maestro public operation projection', () => { |
104 | 108 | durationMs: 400, |
105 | 109 | }, |
106 | 110 | flags: { postGestureStabilization: false }, |
107 | | - internal: { gestureViewport: { x: 0, y: 0, width: 100, height: 200 } }, |
| 111 | + internal: { |
| 112 | + gestureExecutionProfile: 'endpoint-hold', |
| 113 | + gestureViewport: { x: 0, y: 0, width: 100, height: 200 }, |
| 114 | + }, |
108 | 115 | }, |
109 | 116 | }, |
110 | 117 | { |
@@ -168,15 +175,40 @@ describe('Maestro public operation projection', () => { |
168 | 175 | expect(projectMaestroPublicOperation(operation)).toEqual(expected); |
169 | 176 | }); |
170 | 177 |
|
171 | | - test('omits optional swipe and scroll fields', () => { |
| 178 | + test('preserves endpoint-hold for swipes without a viewport and omits scroll duration', () => { |
172 | 179 | expect( |
173 | 180 | projectMaestroPublicOperation({ |
174 | 181 | kind: 'swipe', |
175 | 182 | gesture: { from: { x: 1, y: 2 }, to: { x: 3, y: 4 }, durationMs: 5 }, |
176 | 183 | }), |
177 | | - ).not.toHaveProperty('internal'); |
| 184 | + ).toMatchObject({ |
| 185 | + command: 'gesture', |
| 186 | + internal: { gestureExecutionProfile: 'endpoint-hold' }, |
| 187 | + }); |
178 | 188 | expect(projectMaestroPublicOperation({ kind: 'scroll', direction: 'up' })).not.toHaveProperty( |
179 | 189 | 'input', |
180 | 190 | ); |
181 | 191 | }); |
| 192 | + |
| 193 | + test('a 400 ms Maestro swipe reaches plan execution with endpoint-hold profile', () => { |
| 194 | + const viewport = { x: 0, y: 0, width: 400, height: 800 }; |
| 195 | + const projected = projectMaestroPublicOperation({ |
| 196 | + kind: 'swipe', |
| 197 | + gesture: { from: { x: 360, y: 430 }, to: { x: 40, y: 430 }, durationMs: 400 }, |
| 198 | + viewport, |
| 199 | + }); |
| 200 | + const input = readGesturePayload(projected.input); |
| 201 | + const normalized = normalizePublicGesture(input); |
| 202 | + if (normalized.gesture.intent === 'pan' && projected.internal?.gestureExecutionProfile) { |
| 203 | + normalized.gesture.executionProfile = projected.internal.gestureExecutionProfile; |
| 204 | + } |
| 205 | + const plan = buildGesturePlan( |
| 206 | + normalized.gesture, |
| 207 | + projected.internal?.gestureViewport ?? viewport, |
| 208 | + ); |
| 209 | + assert.equal(plan.topology, 'single'); |
| 210 | + assert.equal(plan.intent, 'pan'); |
| 211 | + assert.equal(plan.executionProfile, 'endpoint-hold'); |
| 212 | + assert.equal(plan.durationMs, 400); |
| 213 | + }); |
182 | 214 | }); |
0 commit comments