mirror of
https://github.com/Since-3/Kuma.git
synced 2026-10-12 20:51:18 +00:00
122 lines
3.7 KiB
TypeScript
122 lines
3.7 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import {
|
|
bookableSpots,
|
|
findFreePosition,
|
|
itemFootprint,
|
|
nextLabel,
|
|
parseFloorPlan,
|
|
placeInRoom,
|
|
spotDisplayName,
|
|
type FloorPlan,
|
|
type FloorPlanItem,
|
|
} from "@/src/modules/rooms/lib/floor-plan";
|
|
|
|
let idCounter = 0;
|
|
const item = (overrides: Partial<FloorPlanItem> = {}): FloorPlanItem => ({
|
|
id: `item-${++idCounter}`,
|
|
type: "reformer",
|
|
x: 1,
|
|
y: 2,
|
|
rotation: 0,
|
|
label: "1",
|
|
...overrides,
|
|
});
|
|
|
|
const plan = (items: FloorPlanItem[]): FloorPlan => ({ width: 8, depth: 6, items });
|
|
|
|
describe("parseFloorPlan", () => {
|
|
it("liefert null für Räume ohne oder mit kaputtem Grundriss", () => {
|
|
expect(parseFloorPlan(null)).toBeNull();
|
|
expect(parseFloorPlan({})).toBeNull();
|
|
expect(parseFloorPlan({ width: 8, depth: 6, items: [{ id: "x", type: "sofa" }] })).toBeNull();
|
|
});
|
|
|
|
it("lehnt doppelte Platznummern innerhalb eines Gerätetyps ab", () => {
|
|
const duplicate = plan([item({ label: "1" }), item({ label: "1" })]);
|
|
expect(parseFloorPlan(duplicate)).toBeNull();
|
|
});
|
|
|
|
it("erlaubt dieselbe Nummer bei verschiedenen Gerätetypen und Deko ohne Nummer", () => {
|
|
const mixed = plan([
|
|
item({ type: "reformer", label: "1" }),
|
|
item({ type: "mat", label: "1" }),
|
|
item({ type: "mirror", label: "" }),
|
|
]);
|
|
expect(parseFloorPlan(mixed)?.items).toHaveLength(3);
|
|
});
|
|
|
|
it("verlangt bei buchbaren Geräten eine Nummer", () => {
|
|
expect(parseFloorPlan(plan([item({ label: "" })]))).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("bookableSpots", () => {
|
|
it("sortiert nach Gerätetyp und Nummer und lässt Deko weg", () => {
|
|
const spots = bookableSpots(
|
|
plan([
|
|
item({ type: "mat", label: "1" }),
|
|
item({ type: "reformer", label: "10" }),
|
|
item({ type: "trainer", label: "" }),
|
|
item({ type: "reformer", label: "2" }),
|
|
])
|
|
);
|
|
expect(spots.map(spotDisplayName)).toEqual(["Reformer 2", "Reformer 10", "Matte 1"]);
|
|
});
|
|
|
|
it("liefert für keinen Grundriss eine leere Liste", () => {
|
|
expect(bookableSpots(null)).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("nextLabel", () => {
|
|
it("füllt Lücken in der Nummerierung pro Gerätetyp", () => {
|
|
const items = [
|
|
item({ label: "1" }),
|
|
item({ label: "2" }),
|
|
item({ label: "4" }),
|
|
item({ type: "mat", label: "3" }),
|
|
];
|
|
expect(nextLabel(items, "reformer")).toBe("3");
|
|
expect(nextLabel(items, "mat")).toBe("1");
|
|
});
|
|
|
|
it("vergibt an Deko keine Nummer", () => {
|
|
expect(nextLabel([], "door")).toBe("");
|
|
});
|
|
});
|
|
|
|
describe("placeInRoom", () => {
|
|
it("hält ein Gerät vollständig innerhalb der Wände", () => {
|
|
// Reformer ungedreht: 0,7 m breit, 2,4 m lang
|
|
expect(placeInRoom(item(), { x: -3, y: 100 }, plan([]))).toEqual({ x: 0.35, y: 4.8 });
|
|
});
|
|
|
|
it("berücksichtigt die Drehung beim Einpassen", () => {
|
|
const turned = item({ rotation: 90 });
|
|
expect(itemFootprint(turned)).toEqual({ w: 2.4, h: 0.7 });
|
|
expect(placeInRoom(turned, { x: 0, y: 0 }, plan([]))).toEqual({ x: 1.2, y: 0.35 });
|
|
});
|
|
|
|
it("rastet auf 10 cm ein, ohne Gleitkomma-Reste", () => {
|
|
const pos = placeInRoom(item(), { x: 2.34, y: 3.26 }, plan([]));
|
|
expect(pos).toEqual({ x: 2.3, y: 3.3 });
|
|
});
|
|
});
|
|
|
|
describe("findFreePosition", () => {
|
|
it("reiht neue Geräte nebeneinander auf, statt sie zu stapeln", () => {
|
|
const room = plan([]);
|
|
for (let i = 0; i < 6; i++) {
|
|
const pos = findFreePosition(room, { type: "reformer", rotation: 0 });
|
|
room.items.push(item({ ...pos, label: String(i + 1) }));
|
|
}
|
|
for (const a of room.items) {
|
|
for (const b of room.items) {
|
|
if (a === b) continue;
|
|
const overlapX = Math.abs(a.x - b.x) < 0.7;
|
|
const overlapY = Math.abs(a.y - b.y) < 2.4;
|
|
expect(overlapX && overlapY).toBe(false);
|
|
}
|
|
}
|
|
});
|
|
});
|