kanban-dev-webapp/src/lib/roadmap/bar-math.test.ts

90 lines
3 KiB
TypeScript
Raw Normal View History

import { describe, it, expect } from "vitest";
import {
fractionToDate,
shiftDatesByFraction,
resizeEndToFraction,
moveCardByFraction,
} from "@/lib/roadmap/bar-math";
import type { TimelineRange } from "@/lib/roadmap/timeline";
const RANGE: TimelineRange = { from: "2026-02-01", to: "2026-02-22" };
describe("fractionToDate", () => {
it("maps 0 to range.from", () => {
expect(fractionToDate(0, RANGE)).toBe("2026-02-01");
});
it("maps 1 to range.to (rounded)", () => {
expect(fractionToDate(1, RANGE)).toBe("2026-02-22");
});
it("maps 0.5 to the midpoint", () => {
expect(fractionToDate(0.5, RANGE)).toBe("2026-02-11");
// 21 days span (Feb 1 to Feb 22 inclusive diff = 21); half = ~10.5 → 11
});
it("clamps out-of-range inputs", () => {
expect(fractionToDate(-0.5, RANGE)).toBe("2026-02-01");
expect(fractionToDate(1.5, RANGE)).toBe("2026-02-22");
});
});
describe("shiftDatesByFraction — moving a bar", () => {
it("shifts both dates by the same number of days when both are set", () => {
const next = shiftDatesByFraction(
"2026-02-05",
"2026-02-10",
21 / 21 / 2, // half the visible range ≈ 10.5 days → 11
RANGE
);
expect(next.startDate).toBe("2026-02-16");
expect(next.targetDate).toBe("2026-02-21");
});
it("returns nulls when neither date is set", () => {
const next = shiftDatesByFraction("", "", 0.5, RANGE);
expect(next).toEqual({ startDate: null, targetDate: null });
});
it("moves only the target when only the target is set", () => {
const next = shiftDatesByFraction("", "2026-02-10", 0.1, RANGE);
expect(next.startDate).toBeNull();
expect(next.targetDate).toBe("2026-02-12");
});
it("clamps the target to range.to when dragged past the right edge", () => {
const next = shiftDatesByFraction(
"2026-02-18",
"2026-02-20",
10, // way past the right edge
RANGE
);
// Both dates would clamp to range.to and collide; start is dropped
// so the bar still renders as a point at the right edge.
expect(next.startDate).toBeNull();
expect(next.targetDate).toBe("2026-02-22");
});
it("keeps a 1-day span when only the target clamps", () => {
// start=18 target=20 (2-day span). Drag 0.2 of the range (~4 days) right.
// Target=20+4 = 24 → clamps to 22. Start=18+4 = 22, equal to target → drop.
const next = shiftDatesByFraction("2026-02-18", "2026-02-20", 0.2, RANGE);
expect(next.targetDate).toBe("2026-02-22");
});
});
describe("resizeEndToFraction — right-edge resize", () => {
it("returns the date at the new fraction", () => {
expect(resizeEndToFraction("2026-02-05", 0.5, RANGE)).toBe("2026-02-11");
});
});
describe("moveCardByFraction — action shape", () => {
it("mirrors shiftDatesByFraction so the action gets the same shape", () => {
const a = shiftDatesByFraction("2026-02-05", "2026-02-10", 0.1, RANGE);
const b = moveCardByFraction("2026-02-05", "2026-02-10", 0.1, RANGE);
expect(a).toEqual(b);
});
});