Files
leistungsbilanz-ts/tests/circuit-grid-projection.test.ts
T

189 lines
5.6 KiB
TypeScript

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"));
});
});