import assert from "node:assert/strict"; import { describe, it } from "node:test"; import { buildVisibleGridRows, filterAndSortCircuitSections, getDistinctFilterValues, } from "../src/frontend/utils/circuit-grid-projection.js"; import type { CircuitTreeCircuitDto, CircuitTreeDeviceRowDto, CircuitTreeSectionDto, } from "../src/frontend/types.js"; function makeDevice( id: string, displayName: string, roomNumberSnapshot: string, sortOrder: number ): CircuitTreeDeviceRowDto { return { id, sortOrder, name: displayName, displayName, roomNumberSnapshot, quantity: 1, powerPerUnit: 0.1, simultaneityFactor: 1, rowTotalPower: 0.1, }; } function makeCircuit( id: string, equipmentIdentifier: string, displayName: string, deviceRows: CircuitTreeDeviceRowDto[], sortOrder: number ): CircuitTreeCircuitDto { return { id, circuitListId: "list-1", sectionId: "section-1", equipmentIdentifier, displayName, sortOrder, isReserve: deviceRows.length === 0, circuitTotalPower: deviceRows.reduce((total, row) => total + row.rowTotalPower, 0), deviceRows, }; } const multiDeviceCircuit = makeCircuit( "circuit-multi", "-1F2", "Office lighting", [makeDevice("device-office", "Office light", "1.01", 10), makeDevice("device-store", "Store light", "1.02", 20)], 20 ); const compactCircuit = makeCircuit( "circuit-compact", "-1F1", "Corridor lighting", [makeDevice("device-corridor", "Corridor light", "1.03", 10)], 10 ); const reserveCircuit = makeCircuit("circuit-reserve", "-1F3", "Reserve", [], 30); const sections: CircuitTreeSectionDto[] = [ { id: "section-1", key: "lighting", displayName: "Lighting", prefix: "-1F", sortOrder: 10, circuits: [compactCircuit, multiDeviceCircuit, reserveCircuit], }, ]; describe("circuit grid projection", () => { it("keeps empty sections and their free placeholders when no filter is active", () => { const emptySections: CircuitTreeSectionDto[] = [ { id: "empty-lighting", key: "lighting", displayName: "Lighting", prefix: "-1F", sortOrder: 10, circuits: [], }, { id: "empty-single-phase", key: "single_phase", displayName: "Single-phase circuits", prefix: "-2F", sortOrder: 20, circuits: [], }, ]; const projectedSections = filterAndSortCircuitSections(emptySections, {}, null); const rows = buildVisibleGridRows(projectedSections); assert.equal(projectedSections.length, 2); assert.deepEqual( rows.map((row) => row.rowKey), [ "section:empty-lighting", "placeholder:empty-lighting", "section:empty-single-phase", "placeholder:empty-single-phase", ] ); }); it("keeps every device row in a circuit block when one device matches a filter", () => { const result = filterAndSortCircuitSections(sections, { roomNumberSnapshot: ["1.01"] }, null); assert.deepEqual(result.map((section) => section.circuits.map((circuit) => circuit.id)), [["circuit-multi"]]); assert.deepEqual(result[0].circuits[0].deviceRows.map((row) => row.id), ["device-office", "device-store"]); }); it("combines column filters across the complete circuit block", () => { const result = filterAndSortCircuitSections( sections, { equipmentIdentifier: ["-1F2"], roomNumberSnapshot: ["1.02"] }, null ); assert.deepEqual(result[0].circuits.map((circuit) => circuit.id), ["circuit-multi"]); }); it("treats missing device values as missing without matching populated circuits", () => { const result = filterAndSortCircuitSections(sections, { roomNumberSnapshot: ["-"] }, null); assert.deepEqual(result[0].circuits.map((circuit) => circuit.id), ["circuit-reserve"]); }); it("sorts complete circuit blocks without changing device row order", () => { const result = filterAndSortCircuitSections(sections, {}, { key: "equipmentIdentifier", direction: "desc" }); assert.deepEqual(result[0].circuits.map((circuit) => circuit.id), [ "circuit-reserve", "circuit-multi", "circuit-compact", ]); assert.deepEqual(result[0].circuits[1].deviceRows.map((row) => row.id), ["device-office", "device-store"]); }); it("builds the compact, grouped, reserve and placeholder row shapes", () => { const rows = buildVisibleGridRows(sections); assert.deepEqual(rows.map((row) => row.rowType), [ "section", "circuitCompact", "circuitSummary", "deviceRow", "deviceRow", "reserveCircuit", "placeholder", ]); assert.equal(rows.at(-1)?.rowKey, "placeholder:section-1"); assert.equal( rows.at(-1)?.cells.find((cell) => cell.cellKey === "equipmentIdentifier")?.value, "-frei-" ); const circuitSummary = rows.find((row) => row.rowType === "circuitSummary"); assert.equal( circuitSummary?.cells.find((cell) => cell.cellKey === "rowTotalPower")?.value, multiDeviceCircuit.circuitTotalPower ); assert.equal( circuitSummary?.cells.some((cell) => cell.cellKey === "circuitTotalPower"), false ); }); it("collects filter options from both circuit and device values", () => { const values = getDistinctFilterValues(sections, [ { key: "displayName", label: "Display name" }, { key: "roomNumberSnapshot", label: "Room number" }, ]); assert.ok(values.displayName.includes("Office lighting")); assert.ok(values.displayName.includes("Store light")); assert.ok(values.roomNumberSnapshot.includes("1.03")); }); });