import assert from "node:assert/strict"; import { describe, it } from "node:test"; import { buildCostGroupFilterOptions, buildFloorFilterOptions, buildSupplyTypeFilterOptions, emptyDistributionBoardFilters, emptyProjectDeviceFilters, filterDistributionBoards, filterProjectDevices, isDistributionBoardFilterActive, isProjectDeviceFilterActive, retainAvailableFilterValues, toggleFilterValue, withoutCostGroupFilterId, withoutFloorFilterId, withoutSupplyTypeFilterId, } from "../src/frontend/utils/overview-filters.js"; import type { DistributionBoardDto, FloorDto, ProjectDeviceDto, } from "../src/frontend/types.js"; type TestBoard = Pick< DistributionBoardDto, "id" | "name" | "floorId" | "supplyType" >; function board( id: string, name: string, floorId: string | null, supplyType: DistributionBoardDto["supplyType"] ): TestBoard { return { id, name, floorId, supplyType }; } function device( id: string, overrides: Partial ): ProjectDeviceDto { return { id, projectId: "project-1", name: `device-${id}`, displayName: `Gerät ${id}`, phaseType: "single_phase", connectionKind: null, costGroup: null, category: "single_phase", quantity: 1, powerPerUnit: 1, simultaneityFactor: 1, totalPower: 1, cosPhi: null, remark: null, voltageV: null, ...overrides, }; } const floors: FloorDto[] = [ { id: "floor-og", projectId: "project-1", name: "1. OG", sortOrder: 2 }, { id: "floor-eg", projectId: "project-1", name: "EG", sortOrder: 1 }, ]; const floorNameById = new Map(floors.map((floor) => [floor.id, floor.name])); const boards = [ board("b1", "UV-EG-01", "floor-eg", "AV"), board("b2", "UV-OG-01", "floor-og", "SV"), board("b3", "HV", null, "AV"), board("b4", "SiBe-Zentrale", null, null), ]; describe("overview filters: distribution boards", () => { it("returns every board without an active filter", () => { assert.equal(isDistributionBoardFilterActive(emptyDistributionBoardFilters), false); assert.deepEqual( filterDistributionBoards(boards, emptyDistributionBoardFilters, floorNameById), boards ); }); it("also searches board number and location when the boards carry them", () => { const withInfo = [ { ...boards[0]!, boardNumber: "UV 0.1", location: "Raum 003 Technik" }, { ...boards[1]!, boardNumber: null, location: null }, ]; assert.deepEqual( filterDistributionBoards( withInfo, { ...emptyDistributionBoardFilters, query: "uv 0.1" }, floorNameById ).map((entry) => entry.id), [withInfo[0]!.id] ); assert.deepEqual( filterDistributionBoards( withInfo, { ...emptyDistributionBoardFilters, query: "technik" }, floorNameById ).map((entry) => entry.id), [withInfo[0]!.id] ); }); it("searches board and floor names case-insensitively and ignores blank queries", () => { const byFloorName = filterDistributionBoards( boards, { ...emptyDistributionBoardFilters, query: " og " }, floorNameById ); assert.deepEqual(byFloorName.map((entry) => entry.id), ["b2"]); const byBoardName = filterDistributionBoards( boards, { ...emptyDistributionBoardFilters, query: "sibe" }, floorNameById ); assert.deepEqual(byBoardName.map((entry) => entry.id), ["b4"]); assert.equal( isDistributionBoardFilterActive({ ...emptyDistributionBoardFilters, query: " " }), false ); }); it("filters floors with the dedicated option for boards without floor", () => { const result = filterDistributionBoards( boards, { ...emptyDistributionBoardFilters, floorIds: ["floor-eg", withoutFloorFilterId] }, floorNameById ); assert.deepEqual(result.map((entry) => entry.id), ["b1", "b3", "b4"]); }); it("combines supply type with the other criteria", () => { const filters = { query: "", floorIds: [withoutFloorFilterId], supplyTypes: ["AV"], }; assert.equal(isDistributionBoardFilterActive(filters), true); assert.deepEqual( filterDistributionBoards(boards, filters, floorNameById).map((entry) => entry.id), ["b3"] ); assert.deepEqual( filterDistributionBoards( boards, { ...emptyDistributionBoardFilters, supplyTypes: [withoutSupplyTypeFilterId] }, floorNameById ).map((entry) => entry.id), ["b4"] ); }); it("offers floors in configured order followed by 'Ohne Etage'", () => { assert.deepEqual(buildFloorFilterOptions(floors), [ { id: "floor-eg", label: "EG" }, { id: "floor-og", label: "1. OG" }, { id: withoutFloorFilterId, label: "Ohne Etage" }, ]); }); it("offers enabled and used supply types in catalog order", () => { const options = buildSupplyTypeFilterOptions( ["AV", "SV", "EV", "USV"], ["USV", "AV"], boards, { AV: "AV – Allgemein", SV: "SV – Sicherheit" } ); assert.deepEqual(options, [ { id: "AV", label: "AV – Allgemein" }, { id: "SV", label: "SV – Sicherheit" }, { id: "USV", label: "USV" }, { id: withoutSupplyTypeFilterId, label: "Nicht festgelegt" }, ]); }); }); describe("overview filters: project devices", () => { const devices = [ device("d1", { displayName: "Steckdose Büro", costGroup: "444", connectionKind: "Steckdose", }), device("d2", { displayName: "Durchlauferhitzer", phaseType: "three_phase", category: "three_phase", costGroup: " 445 ", }), device("d3", { displayName: "Downlight", category: "lighting", costGroup: " ", }), device("d4", { displayName: "Kaffeemaschine", costGroup: null }), ]; it("searches names, connection kind, cost group and the German category label", () => { assert.deepEqual( filterProjectDevices(devices, { ...emptyProjectDeviceFilters, query: "STECK" }).map( (entry) => entry.id ), ["d1"] ); assert.deepEqual( filterProjectDevices( devices, { ...emptyProjectDeviceFilters, query: "beleucht" }, { lighting: "Beleuchtung" } ).map((entry) => entry.id), ["d3"] ); }); it("filters category, phase type and cost group together", () => { const filters = { query: "", categories: ["single_phase", "three_phase"], phaseTypes: ["three_phase"], costGroups: ["445"], }; assert.equal(isProjectDeviceFilterActive(filters), true); assert.deepEqual( filterProjectDevices(devices, filters).map((entry) => entry.id), ["d2"] ); }); it("treats blank cost groups as 'Ohne Kostengruppe'", () => { assert.deepEqual( filterProjectDevices(devices, { ...emptyProjectDeviceFilters, costGroups: [withoutCostGroupFilterId], }).map((entry) => entry.id), ["d3", "d4"] ); assert.deepEqual(buildCostGroupFilterOptions(devices), [ { id: "444", label: "444" }, { id: "445", label: "445" }, { id: withoutCostGroupFilterId, label: "Ohne Kostengruppe" }, ]); }); it("returns an empty list when the filters hide every device", () => { assert.deepEqual( filterProjectDevices(devices, { ...emptyProjectDeviceFilters, query: "gibt es nicht" }), [] ); assert.equal(isProjectDeviceFilterActive(emptyProjectDeviceFilters), false); }); }); describe("overview filters: selection helpers", () => { it("toggles values without mutating the selection", () => { const selected = ["a"]; assert.deepEqual(toggleFilterValue(selected, "b"), ["a", "b"]); assert.deepEqual(toggleFilterValue(selected, "a"), []); assert.deepEqual(selected, ["a"]); }); it("drops selected values that are no longer offered", () => { assert.deepEqual( retainAvailableFilterValues(["floor-eg", "deleted-floor"], buildFloorFilterOptions(floors)), ["floor-eg"] ); }); });