|
|
|
@@ -276,7 +276,8 @@ test("crossing a detent commits and locks the destination clip", async ({ page }
|
|
|
|
|
const box = (await page.locator("#dial").boundingBox())!;
|
|
|
|
|
const cx = box.x + box.width / 2, cy = box.y + box.height / 2;
|
|
|
|
|
// Drag clockwise across one full detent (top -> right ~= 90deg = 1.25 detents),
|
|
|
|
|
// crossing integer 1 (orbit) so it commits, then hold past it (no auto-complete).
|
|
|
|
|
// crossing integer 1 (orbit) so it commits, then hold past it WHILE STILL DRAGGING
|
|
|
|
|
// (the live blend holds wherever the knob is held; the snap happens only on release).
|
|
|
|
|
await page.mouse.move(cx, cy - 30);
|
|
|
|
|
await page.mouse.down();
|
|
|
|
|
await page.mouse.move(cx + 30, cy + 6, { steps: 14 });
|
|
|
|
@@ -293,3 +294,30 @@ test("crossing a detent commits and locks the destination clip", async ({ page }
|
|
|
|
|
expect(st2.activeClipId).toBe(st.activeClipId);
|
|
|
|
|
await page.mouse.up();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("releasing a partial drag snaps to the closest altitude (no held mid-blend)", async ({ page }) => {
|
|
|
|
|
// The operator can grab the dial, turn it PART of a detent, and let go between two
|
|
|
|
|
// altitudes. On release the knob must auto-scrub to the CLOSEST altitude and settle
|
|
|
|
|
// there (integer position, frac 0) — landing on a discrete altitude exactly as if
|
|
|
|
|
// they had clicked it, rather than holding the half-finished blend.
|
|
|
|
|
await boot(page);
|
|
|
|
|
const box = (await page.locator("#dial").boundingBox())!;
|
|
|
|
|
const cx = box.x + box.width / 2, cy = box.y + box.height / 2;
|
|
|
|
|
await page.mouse.move(cx, cy - 30);
|
|
|
|
|
await page.mouse.down();
|
|
|
|
|
await page.mouse.move(cx + 18, cy - 24, { steps: 8 }); // ~0.5 detent — parks BETWEEN altitudes
|
|
|
|
|
// While held we are mid-blend: a fractional knob position.
|
|
|
|
|
await page.waitForFunction(
|
|
|
|
|
() => { const p = (window as any).__hefState().pos; return Math.abs(p - Math.round(p)) > 0.05; },
|
|
|
|
|
null, { timeout: 5000 },
|
|
|
|
|
);
|
|
|
|
|
await page.mouse.up();
|
|
|
|
|
// After release the position settles on an integer altitude (the snap).
|
|
|
|
|
await page.waitForFunction(
|
|
|
|
|
() => Number.isInteger((window as any).__hefState().pos),
|
|
|
|
|
null, { timeout: 10000 },
|
|
|
|
|
);
|
|
|
|
|
const st = await page.evaluate(() => (window as any).__hefState());
|
|
|
|
|
expect(Number.isInteger(st.pos)).toBe(true); // landed on an altitude, not held mid-blend
|
|
|
|
|
expect(st.ringIndex).toBe(st.pos); // committed altitude == the position it landed on
|
|
|
|
|
});
|
|
|
|
|