diff --git a/apps/web/src/components/timeline/timelineProjectRows.test.ts b/apps/web/src/components/timeline/timelineProjectRows.test.ts new file mode 100644 index 0000000..e4fc72b --- /dev/null +++ b/apps/web/src/components/timeline/timelineProjectRows.test.ts @@ -0,0 +1,110 @@ +import { describe, expect, it } from "vitest"; +import { PROJECT_HEADER_HEIGHT, ROW_HEIGHT, SUB_LANE_HEIGHT } from "./timelineConstants.js"; +import { estimateProjectRowHeight, buildProjectFlatRows } from "./timelineProjectRows.js"; + +describe("timelineProjectRows", () => { + it("falls back to the default row height when no row is provided", () => { + expect(estimateProjectRowHeight(undefined)).toBe(ROW_HEIGHT); + }); + + it("returns header and open-demand row heights from the row type", () => { + expect( + estimateProjectRowHeight({ + type: "header", + key: "header-project_1", + project: {} as never, + }), + ).toBe(PROJECT_HEADER_HEIGHT); + + expect( + estimateProjectRowHeight({ + type: "open-demand", + key: "open-demand-project_1", + projectId: "project_1", + openDemandCount: 2, + layout: { + visibleOpenDemands: [], + laneMap: new Map(), + laneCount: 2, + rowHeight: SUB_LANE_HEIGHT * 2 + 16, + }, + }), + ).toBe(SUB_LANE_HEIGHT * 2 + 16); + }); + + it("skips open-demand rows when a project has no open demands", () => { + const rows = buildProjectFlatRows( + [ + { + id: "project_1", + resourceRows: [ + { + resource: { id: "resource_1" }, + allocs: [{ id: "alloc_1" }], + }, + ], + }, + ] as never, + new Map(), + new Map(), + ); + + expect(rows.map((row) => row.type)).toEqual(["header", "resource"]); + expect(rows[1]).toMatchObject({ + key: "project_1-resource_1", + metricsKey: "project_1:resource_1", + }); + }); + + it("uses optimistic open-demand overrides when computing lane layout", () => { + const rows = buildProjectFlatRows( + [ + { + id: "project_1", + resourceRows: [], + }, + ] as never, + new Map([ + [ + "project_1", + [ + { + id: "demand_1", + startDate: "2026-04-10", + endDate: "2026-04-10", + }, + { + id: "demand_2", + startDate: "2026-04-11", + endDate: "2026-04-11", + }, + ], + ], + ]) as never, + new Map([ + [ + "demand_2", + { + startDate: new Date("2026-04-10T00:00:00.000Z"), + endDate: new Date("2026-04-12T00:00:00.000Z"), + }, + ], + ]), + ); + + const openDemandRow = rows.find((row) => row.type === "open-demand"); + expect(openDemandRow?.type).toBe("open-demand"); + + if (openDemandRow?.type !== "open-demand") { + throw new Error("expected open-demand row"); + } + + expect(openDemandRow.layout.visibleOpenDemands[1]?.startDate).toEqual( + new Date("2026-04-10T00:00:00.000Z"), + ); + expect(openDemandRow.layout.laneCount).toBe(2); + expect(openDemandRow.layout.laneMap.get("demand_1")).toBe(0); + expect(openDemandRow.layout.laneMap.get("demand_2")).toBe(1); + expect(openDemandRow.layout.rowHeight).toBe(SUB_LANE_HEIGHT * 2 + 16); + }); +});