Files
callstack__agent-device/packages/contracts/src/gesture-input.test.ts
Thiago Brezinski a13a6832ee feat: add selector-targeted drag gestures (#1567)
* feat: add selector-targeted drag gestures

* fix: address drag gesture review feedback

* fix: satisfy drag review quality gates

* fix(android): lower drag trajectories piecewise

* test(replay): validate drag fixture selectors

* fix(ios): ignore full-viewport chrome containers

* test(drag): prove destination on live devices
2026-08-05 12:37:02 +02:00

112 lines
2.9 KiB
TypeScript

import assert from 'node:assert/strict';
import { test } from 'vitest';
import { readGesturePayload } from './gesture-input.ts';
test('structured gesture input rejects durations outside the planner range', () => {
const cases = [
{
kind: 'pan',
origin: { x: 10, y: 20 },
delta: { x: 30, y: 40 },
durationMs: 0,
},
{
kind: 'transform',
origin: { x: 10, y: 20 },
delta: { x: 30, y: 40 },
scale: 1.2,
degrees: 20,
durationMs: 10_001,
},
];
for (const input of cases) {
assert.throws(
() => readGesturePayload(input),
(error: unknown) =>
typeof error === 'object' &&
error !== null &&
'code' in error &&
error.code === 'INVALID_ARGS' &&
'message' in error &&
typeof error.message === 'string' &&
error.message.includes('16') &&
error.message.includes('10000'),
);
}
});
test('rotate does not accept velocity', () => {
assert.deepEqual(readGesturePayload({ kind: 'rotate', degrees: 45 }), {
kind: 'rotate',
degrees: 45,
origin: undefined,
});
assert.throws(() => readGesturePayload({ kind: 'rotate', degrees: 45, velocity: 1 }), {
code: 'INVALID_ARGS',
});
});
test('drag requires two non-empty targets and validates each timing phase', () => {
for (const input of [
{ kind: 'drag', source: '', destination: 'id="drop-target"' },
{ kind: 'drag', source: 'id="drag-source"', destination: ' ' },
{ kind: 'drag', source: 'id="drag-source"', destination: 'id="drop-target"', sourceHoldMs: 0 },
{ kind: 'drag', source: 'id="drag-source"', destination: 'id="drop-target"', moveMs: 15 },
{
kind: 'drag',
source: 'id="drag-source"',
destination: 'id="drop-target"',
destinationHoldMs: -1,
},
]) {
assert.throws(() => readGesturePayload(input), { code: 'INVALID_ARGS' });
}
assert.deepEqual(
readGesturePayload({
kind: 'drag',
source: 'id="drag-source"',
destination: '@e2~s42',
destinationHoldMs: 0,
}),
{
kind: 'drag',
source: 'id="drag-source"',
destination: '@e2~s42',
sourceHoldMs: undefined,
moveMs: undefined,
destinationHoldMs: 0,
},
);
assert.deepEqual(
readGesturePayload({
kind: 'drag',
source: ' id="drag-source" ',
destination: ' id="drop-target" ',
}),
{
kind: 'drag',
source: 'id="drag-source"',
destination: 'id="drop-target"',
sourceHoldMs: undefined,
moveMs: undefined,
destinationHoldMs: undefined,
},
);
assert.throws(
() =>
readGesturePayload({
kind: 'drag',
source: 'id="drag-source"',
destination: 'id="drop-target"',
sourceHoldMs: 4_000,
moveMs: 4_000,
destinationHoldMs: 4_000,
}),
{ code: 'INVALID_ARGS', message: 'gesture drag total duration must be at most 10000' },
);
});