/* eslint-disable @typescript-eslint/no-require-imports -- Render the actual client component through the TypeScript loader. */ const { test } = require("node:test"); const assert = require("node:assert/strict"); const fs = require("node:fs"); const path = require("node:path"); const vm = require("node:vm"); const ts = require("typescript"); const React = require("react"); const { renderToStaticMarkup } = require("react-dom/server"); function loader(overrides = {}) { const cache = new Map(); function load(file) { file = path.resolve(file); if (cache.has(file)) return cache.get(file).exports; const mod = { exports: {} }; cache.set(file, mod); const code = ts.transpileModule(fs.readFileSync(file, "utf8"), { compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS, jsx: ts.JsxEmit.ReactJSX, esModuleInterop: true, }, fileName: file, }).outputText; const req = (spec) => { if (Object.hasOwn(overrides, spec)) return overrides[spec]; if (spec === "server-only") return {}; if (spec === "@/lib/prisma") throw new Error("No database in component render tests"); if (spec === "next/headers") return { headers: async () => new Headers() }; if (spec === "next/cache") return { revalidatePath() {} }; if (spec === "next/navigation") return { redirect(url) { throw new Error(`REDIRECT:${url}`); }, notFound() { throw new Error("NOT_FOUND"); }, }; if (spec.startsWith("@/") || spec.startsWith(".")) { const target = spec.startsWith("@/") ? path.resolve("src", spec.slice(2)) : path.resolve(path.dirname(file), spec); for (const suffix of [".ts", ".tsx", "/index.ts"]) if (fs.existsSync(target + suffix)) return load(target + suffix); } return require(spec); }; vm.runInThisContext(`(function(require,module,exports){${code}\n})`, { filename: file, })(req, mod, mod.exports); return mod.exports; } return load; } const load = loader({ "./actions": {} }); const { ManualAssignmentTable } = load( "src/app/dashboard/notdienst/plan/plan-view.tsx", ); const children = [ { id: "a", name: "Alex", parentName: "Haushalt A", familyId: "family-a" }, { id: "b", name: "Ben", parentName: "Haushalt B", familyId: "family-b" }, { id: "a2", name: "Anna", parentName: "Haushalt A", familyId: "family-a" }, ]; const day = { date: "2030-11-04T00:00:00.000Z", assignmentId: null, childId: null, }; const familyStatuses = [ { id: "family-a", selectedDays: [{ iso: day.date }] }, { id: "family-b", selectedDays: [{ iso: "2030-11-05T00:00:00.000Z" }] }, ]; const render = (overrides = {}) => renderToStaticMarkup( React.createElement(ManualAssignmentTable, { days: [day], allChildren: children, familyStatuses, isDraft: true, isPending: false, onAssignmentChange() {}, ...overrides, }), ); test("Verfügbare Haushalte stehen zuerst; Geschwister zählen als eine verfügbare Familie", () => { const html = render(); assert.ok( html.indexOf("Verfügbar an diesem Tag") < html.indexOf("Keine Verfügbarkeit für diesen Tag"), ); assert.match(html, /1 Familie ist verfügbar/); assert.match(html, /value="a2"/); assert.match(html, /value="b"/); assert.match(html, /aria-label="Familie für 04.11.2030"/); }); test("Bestehende Einteilung ohne Rückmeldung bleibt auswählbar und wird kenntlich gemacht", () => { const html = render({ days: [{ ...day, childId: "b" }] }); assert.match(html, /value="b" selected/); assert.match( html, /Eingeteilte Familie hat für diesen Tag keine Verfügbarkeit angegeben/, ); }); test("Eingeteilte verfügbare Familie und Haushaltseinteilungszahl werden angezeigt", () => { const html = render({ days: [ { ...day, childId: "a" }, { ...day, date: "2030-11-05T00:00:00.000Z", childId: "a2" }, ], }); assert.match(html, /Eingeteilte Familie ist verfügbar/); assert.match(html, /Haushalt A \(Alex\) — 2x/); }); test("Fehlende Verfügbarkeiten erzeugen eine klare Angabe ohne Familien auszublenden", () => { const html = render({ familyStatuses: [] }); assert.match(html, /0 Familien sind verfügbar/); assert.match(html, /value="a"/); assert.match(html, /value="b"/); }); test("Aktiver Alarm sperrt die Auswahl weiterhin", () => { const html = render({ days: [{ ...day, childId: "a", activeAlarmId: "alarm" }], }); assert.match(html, /]*disabled/); }); test("Ungültige alte Einteilung bleibt entfernbar; neue Kinder sind dafür gesperrt", () => { const html = render({ days: [{ ...day, childId: "inactive", isInvalid: true }], }); assert.match(html, /value="empty"/); assert.match(html, /value="inactive" disabled/); assert.match(html, /value="a" disabled/); assert.match(html, /Einteilung nicht mehr gültig/); });