Files
alpenwerk-hr/tests/unit/onboarding.test.ts
Andrei Laas 8d0c9b4b65 Workshop-Anforderungen, erster Teil: was ohne Migration geht
Anforderung 1 — Freiwilliger vs unfreiwilliger Austritt. Die Liste der
Beendigungsarten zieht aus TerminatePanel.tsx nach lib/beendigung.ts um: der
Berichtemanager braucht sie ebenso, und zwei Listen liefen auseinander. Zwei
neue Arten (Beendigung in der Probezeit, je Seite). Auf wessen Betreiben
beendet wurde, wird aus der Art **abgeleitet** und nicht daneben gespeichert
— als zweites freies Feld liesse sich "Entlassung, freiwillig" erfassen. Das
Dropdown im Formular schraenkt die Auswahl darunter ein.

Drei Gruppen statt zwei: Befristungsablauf geschieht auf niemandes
Betreiben, ein Nichtantritt ist kein Austritt. Beide einer Seite
zuzuschlagen wuerde jede Fluktuationsquote verfaelschen.

Anforderung 2 — Namensfilter in "Anstehend", ab neun Eintraegen.

Anforderung 3 — die zwei Unterschriftenfelder im gedruckten Blatt sind weg;
"Firmenfahrzeug" steht in beiden Checklisten. has_dienstwagen sagt, ob eines
zusteht, nicht ob es uebergeben wurde.

Anforderung 4 — "+794 weitere" ist ein Knopf geworden; die Namen waren
vorher nur ueber den Export erreichbar. Stammdatenaenderung und
Gehaltsanpassung stehen nicht mehr zur Auswahl: die eine entsteht bei jeder
geaenderten Telefonnummer, die andere ist ein totes Ereignis, seit das
Gehalt in Loga liegt. Neu ist der Untertyp — Beendigungsart beim Austritt,
Art der Abwesenheit bei der Langzeitabwesenheit, im Bericht und im Export.

Anforderung 10 — zwei Kacheln. "Aktives Dienstverhaeltnis" ist nicht
dasselbe wie "Aktive Mitarbeiter:innen": dort steht, wer heute arbeitet,
hier, mit wem ein Vertrag laeuft. Sichtbar waren 806 und 10, addieren musste
man selbst.
2026-09-15 22:35:47 +02:00

133 lines
5.2 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
ONBOARDING_GRUPPEN,
ONBOARDING_PUNKTE,
fortschritt,
istErledigt,
punktZu,
wertIstGueltig,
type AufgabenStand,
} from "@/lib/onboarding";
// Die Checkliste kommt von einem Blatt Papier. Was dort ein Kästchen war, ist
// hier nicht überall eines: „Prämienanspruch" hatte zwei Kästchen (ja | nein),
// und die Grössen waren Felder zum Hineinschreiben. Die Feldart trägt also
// eine fachliche Aussage — deshalb wird sie hier festgehalten.
function stand(teil: Partial<AufgabenStand> & { item_key: string }): AufgabenStand {
return { erledigt: false, wert: null, kommentar: null, updated_at: null, updated_by_name: null, ...teil };
}
describe("Das Verzeichnis", () => {
it("hat eindeutige Schlüssel — sie stehen in den Daten", () => {
const keys = ONBOARDING_PUNKTE.map((p) => p.key);
expect(new Set(keys).size).toBe(keys.length);
});
it("bildet jeden Punkt aus dem Blatt ab", () => {
// 25 vom Blatt, dazu „Firmenfahrzeug" aus dem Workshop.
expect(ONBOARDING_PUNKTE).toHaveLength(26);
for (const key of ["dienstzettel", "meldezettel", "salmonellenbefund", "willkommensmappe", "webreise", "firmenfahrzeug"]) {
expect(punktZu(key), key).toBeDefined();
}
});
it("führt Prämienanspruch als Ja/Nein, nicht als Haken", () => {
// Zwei Kästchen im Blatt, und das ist kein Zufall: „nein" ist ein
// erhobener Befund, „noch nicht gefragt" nicht.
expect(punktZu("praemienanspruch")?.art).toBe("janein");
});
it("führt die Grössen als Text", () => {
// Abgehakt wäre eine Schuhgrösse wertlos.
for (const key of ["groesse_schuhe", "groesse_tshirt", "groesse_hose"]) {
expect(punktZu(key)?.art, key).toBe("text");
}
});
it("gruppiert vollständig — kein Punkt fällt zwischen die Gruppen", () => {
const ausGruppen = ONBOARDING_GRUPPEN.flatMap((g) => g.punkte.map((p) => p.key));
expect(new Set(ausGruppen)).toEqual(new Set(ONBOARDING_PUNKTE.map((p) => p.key)));
});
});
describe("istErledigt", () => {
const haken = punktZu("meldezettel")!;
const janein = punktZu("praemienanspruch")!;
const text = punktZu("groesse_schuhe")!;
it("zählt einen gesetzten Haken", () => {
expect(istErledigt(haken, stand({ item_key: haken.key, erledigt: true }))).toBe(true);
expect(istErledigt(haken, stand({ item_key: haken.key }))).toBe(false);
});
it("zählt bei Ja/Nein auch ein Nein als erledigt", () => {
// Die Aufgabe war, die Frage zu beantworten — nicht, sie zu bejahen.
expect(istErledigt(janein, stand({ item_key: janein.key, wert: "nein" }))).toBe(true);
expect(istErledigt(janein, stand({ item_key: janein.key, wert: "ja" }))).toBe(true);
expect(istErledigt(janein, stand({ item_key: janein.key }))).toBe(false);
});
it("zählt bei Text die Angabe selbst, nicht den Haken", () => {
expect(istErledigt(text, stand({ item_key: text.key, wert: "43" }))).toBe(true);
expect(istErledigt(text, stand({ item_key: text.key, wert: " " }))).toBe(false);
// Ein Haken ohne Angabe wäre eine leere Behauptung.
expect(istErledigt(text, stand({ item_key: text.key, erledigt: true }))).toBe(false);
});
it("hält eine fehlende Zeile für offen", () => {
expect(istErledigt(haken, undefined)).toBe(false);
});
});
describe("fortschritt", () => {
it("ist auf einer leeren Liste vollständig offen", () => {
const f = fortschritt([]);
expect(f.erledigt).toBe(0);
expect(f.gesamt).toBe(ONBOARDING_PUNKTE.length);
expect(f.offen).toHaveLength(ONBOARDING_PUNKTE.length);
});
it("nennt die offenen Punkte beim Namen, nicht beim Schlüssel", () => {
// Die Zahl allein sagt nicht, worauf noch gewartet wird.
expect(fortschritt([]).offen).toContain("Meldezettel");
expect(fortschritt([]).offen).not.toContain("meldezettel");
});
it("zählt gemischte Arten zusammen", () => {
const f = fortschritt([
stand({ item_key: "meldezettel", erledigt: true }),
stand({ item_key: "praemienanspruch", wert: "nein" }),
stand({ item_key: "groesse_schuhe", wert: "43" }),
]);
expect(f.erledigt).toBe(3);
expect(f.offen).not.toContain("Meldezettel");
});
it("stört sich nicht an Zeilen zu Punkten, die es nicht mehr gibt", () => {
// Ein gestrichener Punkt lässt seine Antworten stehen — sie sollen die
// Zählung weder erhöhen noch zum Absturz führen.
const f = fortschritt([stand({ item_key: "gibt_es_nicht_mehr", erledigt: true })]);
expect(f.erledigt).toBe(0);
expect(f.gesamt).toBe(ONBOARDING_PUNKTE.length);
});
});
describe("wertIstGueltig", () => {
it("lässt bei Ja/Nein nur ja, nein oder nichts zu", () => {
const janein = punktZu("praemienanspruch")!;
expect(wertIstGueltig(janein, "ja")).toBe(true);
expect(wertIstGueltig(janein, "nein")).toBe(true);
expect(wertIstGueltig(janein, null)).toBe(true);
expect(wertIstGueltig(janein, "vielleicht")).toBe(false);
});
it("lässt bei einem Haken keinen Wert zu", () => {
expect(wertIstGueltig(punktZu("meldezettel")!, "irgendwas")).toBe(false);
});
it("lässt bei Text alles zu — eine Grösse ist keine Auswahlliste", () => {
expect(wertIstGueltig(punktZu("groesse_hose")!, "W32/L34")).toBe(true);
});
});