102 lines
4.6 KiB
TypeScript
102 lines
4.6 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import {
|
|
buildPhysicalDriveProtocol,
|
|
buildTargetActivation,
|
|
buildVolumeKeySequence,
|
|
buildSwipeCycle,
|
|
chooseCycleDirection,
|
|
computeSafeViewport,
|
|
physicalCycleOffsets,
|
|
roundHalfUp,
|
|
} from './physical-drive.protocol';
|
|
|
|
describe('physical-drive.v1 protocol', () => {
|
|
it('uses round-half-up safe viewport coordinates', () => {
|
|
expect(roundHalfUp(1.5)).toBe(2);
|
|
expect(computeSafeViewport({ offsetLeft: 2, offsetTop: 3, width: 390, height: 844 }, { left: 10, right: 12, top: 20, bottom: 22 })).toEqual({
|
|
safeX: 12,
|
|
safeY: 23,
|
|
width: 368,
|
|
height: 802,
|
|
});
|
|
expect(() => computeSafeViewport({ offsetLeft: 0, offsetTop: 0, width: 10, height: 10 }, { left: 5, right: 5, top: 0, bottom: 0 })).toThrow(/positive/u);
|
|
});
|
|
|
|
it('generates the literal 15-step synchronized touch cycle', () => {
|
|
const cycle = buildSwipeCycle('forward', { safeX: 0, safeY: 0, width: 100, height: 200 });
|
|
expect(cycle.pointer.id).toBe('driveTouch');
|
|
expect(cycle.keyboard.id).toBe('driveKeys');
|
|
expect(cycle.pointer.actions).toHaveLength(19);
|
|
expect(cycle.pointer.actions[0]).toEqual({ type: 'pointerMove', duration: 0, origin: 'viewport', x: 50, y: 160 });
|
|
expect(cycle.pointer.actions[2]).toEqual({ type: 'pointerMove', duration: 40, origin: 'viewport', x: 50, y: 151 });
|
|
expect(cycle.pointer.actions[16]).toEqual({ type: 'pointerMove', duration: 40, origin: 'viewport', x: 50, y: 20 });
|
|
expect(cycle.pointer.actions.at(-1)).toEqual({ type: 'pause', duration: 400 });
|
|
expect(cycle.keyboard.actions).toEqual([{ type: 'pause', duration: 1_000 }]);
|
|
});
|
|
|
|
it('switches direction only at the two-pixel boundaries', () => {
|
|
expect(chooseCycleDirection('forward', 98, 100)).toBe('reverse');
|
|
expect(chooseCycleDirection('forward', 97.99, 100)).toBe('forward');
|
|
expect(chooseCycleDirection('reverse', 2, 100)).toBe('forward');
|
|
expect(chooseCycleDirection('reverse', 2.01, 100)).toBe('reverse');
|
|
});
|
|
|
|
it('builds exact stable-box activation and synchronized volume input sources', () => {
|
|
const activation = buildTargetActivation(
|
|
{ left: 10, top: 20, right: 110, bottom: 70 },
|
|
{ left: 10.5, top: 20.5, right: 110.5, bottom: 70.5 },
|
|
{ safeX: 0, safeY: 0, width: 100, height: 100 },
|
|
);
|
|
expect(activation.pointer.actions).toEqual([
|
|
{ type: 'pointerMove', duration: 0, origin: 'viewport', x: 55, y: 46 },
|
|
{ type: 'pause', duration: 60 },
|
|
{ type: 'pointerDown', button: 0 },
|
|
{ type: 'pause', duration: 60 },
|
|
{ type: 'pointerUp', button: 0 },
|
|
]);
|
|
expect(activation.keyboard.actions).toEqual([{ type: 'pause', duration: 120 }]);
|
|
expect(() => buildTargetActivation(
|
|
{ left: 10, top: 20, right: 110, bottom: 70 },
|
|
{ left: 11, top: 20, right: 110, bottom: 70 },
|
|
{ safeX: 0, safeY: 0, width: 100, height: 100 },
|
|
)).toThrow(/stable/u);
|
|
|
|
const volume = buildVolumeKeySequence();
|
|
expect(volume.keyboard.actions).toEqual([
|
|
{ type: 'pause', duration: 15_900 },
|
|
{ type: 'keyDown', value: '\uE004' },
|
|
{ type: 'pause', duration: 20 },
|
|
{ type: 'keyUp', value: '\uE004' },
|
|
{ type: 'pause', duration: 80 },
|
|
{ type: 'keyDown', value: '\uE012' },
|
|
{ type: 'pause', duration: 40 },
|
|
{ type: 'keyUp', value: '\uE012' },
|
|
]);
|
|
expect(volume.pointer.actions).toEqual([{ type: 'pause', duration: 16_040 }]);
|
|
});
|
|
|
|
it('uses every specified cycle except the excluded 389/390-second cycles', () => {
|
|
const offsets = physicalCycleOffsets();
|
|
expect(offsets).toContain(388);
|
|
expect(offsets).not.toContain(389);
|
|
expect(offsets).not.toContain(390);
|
|
expect(offsets).toContain(391);
|
|
expect(offsets).toContain(509);
|
|
expect(offsets).not.toContain(510);
|
|
});
|
|
|
|
it('preserves normative event order and terminal-Static branches', () => {
|
|
const protocol = buildPhysicalDriveProtocol();
|
|
expect(protocol.version).toBe('physical-drive.v1');
|
|
expect(protocol.durationMs).toBe(600_000);
|
|
expect(protocol.events.filter((event) => event.offsetMs === 390_000).map((event) => event.action)).toEqual([
|
|
'arm-navigation-settlement-barrier',
|
|
'navigate-infrastructure',
|
|
]);
|
|
expect(protocol.events.find((event) => event.offsetMs === 391_000)?.assertions).toContain('cancel-exact-pending-navigation-epoch');
|
|
expect(protocol.branches.live.find((event) => event.offsetMs === 550_000)?.action).toBe('pause-motion');
|
|
expect(protocol.branches.static.find((event) => event.offsetMs === 550_000)?.action).toBe('assert-static-without-input');
|
|
expect(protocol.events.at(-1)).toMatchObject({ offsetMs: 600_000, action: 'finish' });
|
|
});
|
|
});
|