import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { NotesBell, type Kollege } from "@/components/shell/NotesBell"; import { ToastProvider } from "@/components/ui/Toast"; import type { OpenNote } from "@/lib/notes"; const setNotizSichtbarkeit = vi.fn(async () => ({ success: true }) as { success: boolean; error?: string }); const refresh = vi.fn(); vi.mock("@/actions/notes", () => ({ setNotizSichtbarkeit: (...a: unknown[]) => setNotizSichtbarkeit(...(a as [])) })); vi.mock("@/actions/employees", () => ({ completeEmployeeNote: vi.fn(async () => ({ success: true })) })); vi.mock("next/navigation", () => ({ useRouter: () => ({ refresh, push: vi.fn() }) })); // Die Einstellung steckt in der Glocke, weil sie dort gebraucht wird: wer // eine fremde Wiedervorlage sieht, die ihn nichts angeht, will sie an Ort // und Stelle abwählen können — nicht auf einer Einstellungsseite. const KOLLEGEN: Kollege[] = [ { id: "a", name: "Anna Berger", sichtbar: true }, { id: "b", name: "Bernd Huber", sichtbar: false }, ]; // Die Vorgabe ist die eigene Person: ohne Haken sieht man nur sich selbst. function notiz(teil: Partial = {}): OpenNote { return { id: "n1", employee_id: "e1", author_user_id: "a", author_name: "Anna Berger", category: "Wiedervorlage", note_text: "Dienstzettel nachfassen", due_date: "2026-09-20", done: false, done_by: null, done_at: null, created_at: "2026-09-01T08:00:00.000Z", employeeName: "Manuel Aigner", vonMir: false, ...teil, } as OpenNote; } function zeige(kollegen = KOLLEGEN, notes = [notiz()]) { return render( ); } async function oeffneEinstellung(user: ReturnType) { await user.click(screen.getByRole("button", { name: /Meine Notizen/i })); await user.click(screen.getByRole("button", { name: /Kolleg:innen/i })); } beforeEach(() => { setNotizSichtbarkeit.mockClear(); setNotizSichtbarkeit.mockResolvedValue({ success: true }); refresh.mockClear(); }); describe("Sichtbarkeit der Kolleg:innen", () => { it("zeigt die Einstellung erst auf Klick", async () => { const user = userEvent.setup(); zeige(); await user.click(screen.getByRole("button", { name: /Meine Notizen/i })); expect(screen.queryByRole("checkbox", { name: "Anna Berger" })).not.toBeInTheDocument(); await user.click(screen.getByRole("button", { name: /Kolleg:innen/i })); expect(screen.getByRole("checkbox", { name: "Anna Berger" })).toBeInTheDocument(); }); it("spiegelt den gespeicherten Stand in den Haken", async () => { const user = userEvent.setup(); zeige(); await oeffneEinstellung(user); expect(screen.getByRole("checkbox", { name: "Anna Berger" })).toBeChecked(); expect(screen.getByRole("checkbox", { name: "Bernd Huber" })).not.toBeChecked(); }); it("meldet beim Abwählen die Kennung und den neuen Stand", async () => { // sichtbar: false heisst jetzt „Abo entfernen", nicht „ausblenden". const user = userEvent.setup(); zeige(); await oeffneEinstellung(user); await user.click(screen.getByRole("checkbox", { name: "Anna Berger" })); expect(setNotizSichtbarkeit).toHaveBeenCalledWith({ kollegeId: "a", sichtbar: false }); expect(screen.getByRole("checkbox", { name: "Anna Berger" })).not.toBeChecked(); }); it("wählt beim zweiten Klick wieder an", async () => { const user = userEvent.setup(); zeige(); await oeffneEinstellung(user); await user.click(screen.getByRole("checkbox", { name: "Bernd Huber" })); expect(setNotizSichtbarkeit).toHaveBeenCalledWith({ kollegeId: "b", sichtbar: true }); expect(screen.getByRole("checkbox", { name: "Bernd Huber" })).toBeChecked(); }); it("dreht den Haken zurück, wenn das Speichern scheitert", async () => { // Der Haken springt sofort um, damit sich das Durchgehen der Liste nicht // zäh anfühlt. Bleibt er stehen, obwohl nichts gespeichert wurde, zeigt // die Einstellung etwas anderes als die Datenbank. setNotizSichtbarkeit.mockResolvedValue({ success: false, error: "Nicht gespeichert." }); const user = userEvent.setup(); zeige(); await oeffneEinstellung(user); await user.click(screen.getByRole("checkbox", { name: "Anna Berger" })); expect(screen.getByRole("checkbox", { name: "Anna Berger" })).toBeChecked(); expect(await screen.findByText("Nicht gespeichert.")).toBeInTheDocument(); }); it("nennt die Zahl der Hinzugewählten nur, wenn welche da sind", async () => { const user = userEvent.setup(); const { unmount } = zeige(); await user.click(screen.getByRole("button", { name: /Meine Notizen/i })); expect(screen.getByRole("button", { name: /Kolleg:innen/i })).toHaveTextContent("+1"); unmount(); // Niemand hinzugewählt: die Vorgabe. Eine „+0" wäre bei jedem // Aufschlagen zu lesen und sagte nichts. zeige(KOLLEGEN.map((k) => ({ ...k, sichtbar: false }))); await user.click(screen.getByRole("button", { name: /Meine Notizen/i })); expect(screen.getByRole("button", { name: /Kolleg:innen/i })).not.toHaveTextContent("+"); }); it("nennt an jeder Notiz den Verfasser", async () => { // Sichtbar sind die eigenen und die hinzugewählten — ohne diese Zeile // wäre nicht zu unterscheiden, welche davon man gerade liest. const user = userEvent.setup(); zeige(KOLLEGEN, [notiz({ author_name: "Anna Berger" })]); await user.click(screen.getByRole("button", { name: /Meine Notizen/i })); expect(screen.getByText("von Anna Berger")).toBeInTheDocument(); }); it("sagt bei eigenen Notizen: von mir", async () => { // Der eigene Name stünde an jeder zweiten Zeile und sagte nichts — man // kennt ihn. const user = userEvent.setup(); zeige(KOLLEGEN, [notiz({ vonMir: true, author_name: "Max Stubhan" })]); await user.click(screen.getByRole("button", { name: /Meine Notizen/i })); expect(screen.getByText("von mir")).toBeInTheDocument(); expect(screen.queryByText(/Max Stubhan/)).not.toBeInTheDocument(); }); it("kommt ohne Kolleg:innen zurecht", async () => { // Eine Personalabteilung aus einer Person: die Liste ist leer, und ein // Kasten ohne Inhalt wäre eine Sackgasse. const user = userEvent.setup(); zeige([]); await oeffneEinstellung(user); expect(screen.getByText(/Keine weiteren HR-Kolleg:innen/i)).toBeInTheDocument(); }); it("lässt die Notizen selbst stehen, während die Einstellung offen ist", async () => { // Die Einstellung schiebt sich über die Liste, sie ersetzt sie nicht: // wer abwählt, will sehen, was daraufhin verschwindet. const user = userEvent.setup(); zeige(); await oeffneEinstellung(user); expect(screen.getByText("Dienstzettel nachfassen")).toBeInTheDocument(); expect(screen.getByRole("checkbox", { name: "Anna Berger" })).toBeInTheDocument(); }); });