import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { describe, expect, it, vi } from "vitest"; import { TerminatePanel } from "@/components/employees/panels/TerminatePanel"; import { ToastProvider } from "@/components/ui/Toast"; import type { Database } from "@/lib/supabase/types"; type EmployeeRow = Database["public"]["Tables"]["employees"]["Row"]; vi.mock("@/actions/employees", () => ({ terminateEmployee: vi.fn(async () => ({ success: true })), })); vi.mock("next/navigation", () => ({ useRouter: () => ({ refresh: vi.fn(), push: vi.fn() }) })); // Wer nie angetreten ist, scheidet am Tag seines Eintritts aus. Nur so gibt es // keinen Stichtag, an dem die Person als beschäftigt zählt — „nie aktiv" // folgt aus dem Datum, nicht aus einer Sonderregel im Auswerten. const EINTRITT = "2026-09-10"; function mitarbeiter(teil: Partial = {}): EmployeeRow { return { id: "e1", personnel_number: 3038, first_name: "Hannah", last_name: "Winkler", gender: "w", birth_date: "1990-04-01", sv_nummer: null, nationality: "Österreich", hat_aufenthaltstitel: false, aufenthaltstitel_bis: null, address: null, address_country: null, postal_code: null, city: null, email: null, phone: null, job_title: "IT-Security-Spezialist:in", location_id: "l1", employment_type: "Vollzeit", weekly_hours: 38.5, monthly_salary_gross: null, contract_type: "unbefristet", contract_end_date: null, paygrade: "C", source: "Extern", status: "Geplant", entry_date: EINTRITT, exit_date: null, exit_reason: null, karenz_start_date: null, karenz_return_date: null, absence_type: null, avatar_color: null, worker_type: "Angestellte:r", collective_agreement: "Handel", work_days: ["Mo", "Di", "Mi", "Do", "Fr"], is_betriebsrat: false, has_dienstwagen: false, dienstwagen_art: null, has_kuendigungsschutz: false, kuendigungsschutz_bis: null, teilzeit_art: null, teilzeit_bis: null, is_laterale_fuehrung: false, is_c_level: false, title_prefix: [], title_suffix: [], emergency_contact_name: null, emergency_contact_phone: null, emergency_contact_relation: null, created_at: "2026-08-01T00:00:00.000Z", updated_at: "2026-08-01T00:00:00.000Z", ...teil, } as EmployeeRow; } function zeige(teil: Partial = {}) { return render( {}} employee={mitarbeiter(teil)} directReportCount={0} /> ); } describe("TerminatePanel — Nichtantritt", () => { it("bietet No Show als Beendigungsart an", () => { zeige(); expect(screen.getByRole("option", { name: "No Show (nicht angetreten)" })).toBeInTheDocument(); }); it("lässt das Datum bei einem gewöhnlichen Austritt frei", () => { zeige({ status: "Aktiv" }); const feld = screen.getByLabelText(/Austrittsdatum/); expect(feld).toBeEnabled(); expect(feld).toHaveValue(""); }); it("setzt das Datum bei No Show auf den Eintrittstag und sperrt es", async () => { const user = userEvent.setup(); zeige(); await user.selectOptions(screen.getByLabelText("Beendigungsart"), "No Show"); const feld = screen.getByLabelText(/Wirksam am/); expect(feld).toHaveValue(EINTRITT); expect(feld).toBeDisabled(); }); it("schickt den Eintrittstag, auch wenn vorher ein anderes Datum dastand", async () => { const user = userEvent.setup(); const { terminateEmployee } = await import("@/actions/employees"); zeige({ status: "Aktiv" }); // Erst ein Datum eintippen, dann auf No Show wechseln. await user.type(screen.getByLabelText(/Austrittsdatum/), "2026-12-31"); await user.selectOptions(screen.getByLabelText("Beendigungsart"), "No Show"); await user.click(screen.getByRole("button", { name: "Austritt bestätigen" })); expect(terminateEmployee).toHaveBeenCalledWith( expect.objectContaining({ exit_date: EINTRITT, exit_reason: "No Show" }) ); }); it("blendet die Offboarding-Checkliste aus — ausgegeben wurde nie etwas", async () => { const user = userEvent.setup(); zeige({ status: "Aktiv" }); expect(screen.getByText("Offboarding-Checkliste")).toBeVisible(); await user.selectOptions(screen.getByLabelText("Beendigungsart"), "No Show"); expect(screen.getByText("Offboarding-Checkliste")).not.toBeVisible(); }); it("sagt, was mit der Planstelle passiert", async () => { const user = userEvent.setup(); zeige(); await user.selectOptions(screen.getByLabelText("Beendigungsart"), "No Show"); expect(screen.getByText(/Planstelle wird wieder frei/)).toBeInTheDocument(); }); }); // Besonderer Kündigungsschutz: Betriebsrat, Mutterschutz, Karenz, begünstigte // Behinderung, Lehre. Die Anwendung entscheidet nicht, ob eine Beendigung // zulässig ist — sie darf es aber auch nicht verschweigen. Im Vertragsblatt // nachzusehen ist genau der Schritt, den man unter Zeitdruck auslässt. describe("TerminatePanel — Kündigungsschutz", () => { it("warnt mit dem Enddatum, wenn eines erfasst ist", () => { zeige({ has_kuendigungsschutz: true, kuendigungsschutz_bis: "2027-06-30" }); expect(screen.getByText(/Achtung: besonderer Kündigungsschutz bis 30\.06\.2027/)).toBeInTheDocument(); }); it("warnt auch ohne Enddatum, und sagt dass keines erfasst ist", () => { zeige({ has_kuendigungsschutz: true, kuendigungsschutz_bis: null }); expect(screen.getByText(/Ende nicht erfasst/)).toBeInTheDocument(); }); it("schweigt, wo kein Schutz besteht", () => { zeige(); expect(screen.queryByText(/besonderer Kündigungsschutz/i)).not.toBeInTheDocument(); }); it("warnt auch bei einem Nichtantritt — der Schutz gilt ab Vertragsbeginn", async () => { const user = userEvent.setup(); zeige({ has_kuendigungsschutz: true, kuendigungsschutz_bis: "2027-06-30" }); await user.selectOptions(screen.getByLabelText("Beendigungsart"), "No Show"); expect(screen.getByText(/Achtung: besonderer Kündigungsschutz/)).toBeInTheDocument(); }); }); describe("TerminatePanel — geplanter Eintritt", () => { it("schlägt bei einem geplanten Eintritt No Show vor", () => { zeige({ status: "Geplant" }); expect(screen.getByLabelText("Beendigungsart")).toHaveValue("No Show"); expect(screen.getByRole("heading", { name: "Nicht angetreten" })).toBeInTheDocument(); }); it("bleibt bei einer aktiven Person beim gewöhnlichen Austritt", () => { zeige({ status: "Aktiv" }); expect(screen.getByLabelText("Beendigungsart")).toHaveValue("Einvernehmliche Auflösung"); expect(screen.getByRole("heading", { name: "Austritt" })).toBeInTheDocument(); }); });