import { describe, expect, it } from "@suaveplan/testing/runner"; import { applyDetents, clamp, fractionForValue, keyboardStep, quantize, resolveSliderValue, } from "./slider-helpers.js"; describe("slider-helpers", () => { it("clamps below min and above max", { timeout: 5000 }, () => { expect(clamp(-1, 0, 1)).toBe(0); expect(clamp(2, 0, 1)).toBe(1); expect(clamp(0.5, 0, 1)).toBe(0.5); }); it("quantize snaps to step", { timeout: 5000 }, () => { expect(quantize(0.42, 0.1, 0)).toBeCloseTo(0.4, 5); expect(quantize(0.48, 0.1, 0)).toBeCloseTo(0.5, 5); }); it("quantize is identity when step <= 0", { timeout: 5000 }, () => { expect(quantize(0.42, 0, 0)).toBe(0.42); expect(quantize(0.42, -1, 0)).toBe(0.42); }); it("applyDetents snaps within threshold", { timeout: 5000 }, () => { const snapped = applyDetents(0.51, [{ at: 0.5 }], 0.05); expect(snapped).toBe(0.5); }); it("applyDetents ignores out-of-threshold detents", { timeout: 5000, }, () => { expect(applyDetents(0.7, [{ at: 0.5 }], 0.05)).toBe(0.7); }); it("resolveSliderValue runs full pipeline", { timeout: 5000 }, () => { const v = resolveSliderValue({ raw: 0.51, min: 0, max: 1, step: 0.01, detents: [{ at: 0.5 }], }); expect(v).toBe(0.5); }); it("resolveSliderValue with no detents skips snap", { timeout: 5000, }, () => { expect( resolveSliderValue({ raw: 0.42, min: 0, max: 1, step: 0.01 }) ).toBeCloseTo(0.42, 5); }); it("resolveSliderValue uses default snapThreshold", { timeout: 5000, }, () => { const v = resolveSliderValue({ raw: 0.505, min: 0, max: 1, step: 0.001, detents: [{ at: 0.5 }], }); expect(v).toBe(0.5); }); it("fractionForValue normalizes", { timeout: 5000 }, () => { expect(fractionForValue(0, 0, 1)).toBe(0); expect(fractionForValue(0.5, 0, 1)).toBeCloseTo(0.5, 5); expect(fractionForValue(1, 0, 1)).toBe(1); }); it("fractionForValue handles degenerate span", { timeout: 5000 }, () => { expect(fractionForValue(5, 5, 5)).toBe(0); }); it("fractionForValue clamps out-of-range", { timeout: 5000 }, () => { expect(fractionForValue(2, 0, 1)).toBe(1); expect(fractionForValue(-1, 0, 1)).toBe(0); }); it("keyboardStep ArrowUp/ArrowDown for vertical", { timeout: 5000, }, () => { const args = { value: 0.5, min: 0, max: 1, step: 0.1, fineFactor: 0.1, shiftKey: false, orientation: "vertical" as const, }; expect(keyboardStep({ ...args, key: "ArrowUp" })).toBeGreaterThan(0.5); expect(keyboardStep({ ...args, key: "ArrowDown" })).toBeLessThan(0.5); }); it("keyboardStep ArrowLeft/ArrowRight for horizontal", { timeout: 5000, }, () => { const args = { value: 0.5, min: 0, max: 1, step: 0.1, fineFactor: 0.1, shiftKey: false, orientation: "horizontal" as const, }; expect(keyboardStep({ ...args, key: "ArrowRight" })).toBeGreaterThan( 0.5 ); expect(keyboardStep({ ...args, key: "ArrowLeft" })).toBeLessThan(0.5); }); it("keyboardStep Home/End", { timeout: 5000 }, () => { const args = { value: 0.5, min: 0, max: 1, step: 0.1, fineFactor: 0.1, shiftKey: false, orientation: "vertical" as const, }; expect(keyboardStep({ ...args, key: "Home" })).toBe(0); expect(keyboardStep({ ...args, key: "End" })).toBe(1); }); it("keyboardStep PageUp/PageDown", { timeout: 5000 }, () => { const args = { value: 0.5, min: 0, max: 1, step: 0.1, fineFactor: 0.1, shiftKey: false, orientation: "vertical" as const, }; expect(keyboardStep({ ...args, key: "PageUp" })).toBeCloseTo(0.6, 5); expect(keyboardStep({ ...args, key: "PageDown" })).toBeCloseTo(0.4, 5); }); it("keyboardStep returns null for unknown keys", { timeout: 5000 }, () => { expect( keyboardStep({ key: "Tab", value: 0.5, min: 0, max: 1, step: 0.1, fineFactor: 0.1, shiftKey: false, orientation: "vertical", }) ).toBeNull(); }); it("keyboardStep applies fineFactor on shift", { timeout: 5000 }, () => { const normal = keyboardStep({ key: "ArrowUp", value: 0.5, min: 0, max: 1, step: 0.1, fineFactor: 0.1, shiftKey: false, orientation: "vertical", }) as number; const fine = keyboardStep({ key: "ArrowUp", value: 0.5, min: 0, max: 1, step: 0.1, fineFactor: 0.1, shiftKey: true, orientation: "vertical", }) as number; expect(fine - 0.5).toBeLessThan(normal - 0.5); }); });