import { describe, expect, it } from "vitest"; import { ABSENCE_TYPES, absenceLabel, isAbsenceType, RUECKKEHR_GRUENDE, statusLabel, STUNDEN_GRUENDE } from "@/lib/absence"; // The stored employment status is still 'Karenz' — renaming the enum value // would mean rewriting every stored function that spells it. The rename // therefore lives entirely in this mapping, which makes it the one thing // standing between the database value and what an HR user reads. describe("statusLabel", () => { it("renders the stored 'Karenz' value as Langzeitabwesenheit", () => { expect(statusLabel("Karenz")).toBe("Langzeitabwesenheit"); }); it("leaves the other statuses alone", () => { expect(statusLabel("Aktiv")).toBe("Aktiv"); expect(statusLabel("Geplant")).toBe("Geplant"); expect(statusLabel("Ausgetreten")).toBe("Ausgetreten"); }); }); describe("absenceLabel", () => { it("shows the specific kind when one is recorded", () => { expect(absenceLabel("Karenz", "Bildungskarenz")).toBe("Bildungskarenz"); expect(absenceLabel("Karenz", "Präsenzdienst")).toBe("Präsenzdienst"); }); it("falls back to the generic name for absences recorded before the field existed", () => { expect(absenceLabel("Karenz", null)).toBe("Langzeitabwesenheit"); expect(absenceLabel("Karenz", "")).toBe("Langzeitabwesenheit"); }); it("ignores a type that is not on the list rather than displaying it", () => { // The column has a check constraint, but the value arrives here as a // plain string — echoing an unknown one would put unvalidated text in // the status chip. expect(absenceLabel("Karenz", "Ausgedachte Karenz")).toBe("Langzeitabwesenheit"); }); it("ignores the absence type entirely for any other status", () => { expect(absenceLabel("Aktiv", "Sabbatical")).toBe("Aktiv"); expect(absenceLabel("Ausgetreten", "Papamonat")).toBe("Ausgetreten"); }); }); describe("ABSENCE_TYPES", () => { it("holds the ten kinds, in the order they are offered", () => { // Waren dreizehn. Die vier Teilzeiten sind dorthin gewandert, wo sie // hingehören — siehe unten. expect(ABSENCE_TYPES).toHaveLength(10); expect(ABSENCE_TYPES[0]).toBe("Wochenhilfe (Mutterschutz)"); expect(ABSENCE_TYPES.at(-1)).toBe("Sabbatical"); }); it("guards every entry with isAbsenceType", () => { for (const t of ABSENCE_TYPES) expect(isAbsenceType(t)).toBe(true); expect(isAbsenceType("Karenz")).toBe(false); expect(isAbsenceType(null)).toBe(false); }); }); // Eine Teilzeit ist keine Abwesenheit. Wer in Bildungs-, Pflege-, Eltern- // oder Wiedereingliederungsteilzeit als „langzeitabwesend" geführt wurde, // verschwand aus dem Bestand, verlor die Berichtslinie an eine Vertretung und // zählte in Auswertungen nicht mehr mit — obwohl er jede Woche im Haus war, // nur kürzer. const TEILZEITEN = ["Bildungsteilzeit", "Elternteilzeit", "Wiedereingliederungsteilzeit", "Pflegeteilzeit"]; describe("Teilzeiten sind keine Abwesenheiten", () => { it("führt keine Teilzeit mehr als Abwesenheit", () => { for (const t of TEILZEITEN) { expect(ABSENCE_TYPES).not.toContain(t); expect(isAbsenceType(t)).toBe(false); } }); it("behält die Karenzen — sie setzen aus, statt zu reduzieren", () => { // Die Wörter ähneln sich, die Sachverhalte nicht: Bildungskarenz ist // eine Abwesenheit, Bildungsteilzeit sind weniger Stunden. for (const t of ["Elternkarenz (inkl. Väterkarenz)", "Bildungskarenz", "Pflegekarenz"]) { expect(ABSENCE_TYPES).toContain(t); } }); }); describe("Gründe für weniger Stunden", () => { it("bietet bei der Rückkehr die zwei an, die dort hingehören", () => { expect([...RUECKKEHR_GRUENDE]).toEqual(["Wiedereingliederungsteilzeit", "Elternteilzeit"]); }); it("bietet bei der Stundenänderung die übrigen an, mit dem Normalfall zuerst", () => { expect(STUNDEN_GRUENDE[0]).toBe("Vertragliche Stundenänderung"); expect([...STUNDEN_GRUENDE]).toContain("Bildungsteilzeit"); expect([...STUNDEN_GRUENDE]).toContain("Pflegeteilzeit"); }); it("bietet alle vier Teilzeiten bei der Stundenänderung an", () => { // Bis zum 29.09. standen Eltern- und Wiedereingliederungsteilzeit nur bei // der Rückkehr — mit der Begründung, dass sie typischerweise dann // beginnen. Typischerweise ist nicht immer, und wer sie ohne // vorangegangene Abwesenheit vereinbart, fand sie nirgends. Gemeldet von // Lara: „Teilzeitvariante Elternteilzeit fehlt". for (const teilzeit of TEILZEITEN) { expect([...STUNDEN_GRUENDE], `„${teilzeit}" fehlt bei der Stundenänderung`).toContain(teilzeit); } }); it("überschneidet sich dabei bewusst mit der Rückkehr", () => { // Die Überschneidung ist kein Versehen: es ist derselbe Wert in derselben // Spalte, nur aus zwei Anlässen erfasst. Was es nicht geben darf, ist eine // Teilzeit, die an *keinem* der beiden Wege hängt. const beide = TEILZEITEN.filter( (t) => ([...RUECKKEHR_GRUENDE] as string[]).includes(t) && ([...STUNDEN_GRUENDE] as string[]).includes(t) ); expect(new Set(beide)).toEqual(new Set(RUECKKEHR_GRUENDE)); }); it("führt den Normalfall nicht als Teilzeit", () => { // „Vertragliche Stundenänderung" ist keine Variante, sondern ihr Fehlen: // sie landet als null in teilzeit_art. expect([...TEILZEITEN]).not.toContain("Vertragliche Stundenänderung"); }); });