140 lines
4.9 KiB
JavaScript
140 lines
4.9 KiB
JavaScript
/* 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, /<select[^>]*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/);
|
|
});
|