import type { ReactNode } from "react" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import { render, screen } from "@testing-library/react" import { api } from "@/lib/api" import type { Hinweis, Rueckfrage } from "@/lib/typen" import { HandySeite } from "./Handy" vi.mock("@/lib/api", async (original) => ({ ...(await original()), api: vi.fn(), post: vi.fn() })) vi.mock("@tanstack/react-router", async (original) => ({ ...(await original()), Link: ({ to, hash, children, className }: { to: string; hash?: string; children: ReactNode; className?: string }) => ( {children} ), })) // Handy-Startansicht (Ausbauplan „Später“, Punkt 19, 26.09.2026): Ampel und „Braucht Aufmerksamkeit“. const jetzt = Math.floor(Date.now() / 1000) const hinweis = (id: string, stufe: "rot" | "gelb", titel: string, zustand: Hinweis["zustand"] = "neu"): Hinweis => ({ id, stufe, titel, text: "", quelle: "x", seit: jetzt - 60, zuletzt: jetzt, aktionen: [], zustand, }) const frage: Rueckfrage = { nr: 7, zeit: jetzt - 60, frage: "Soll ich Gitea 1.27.4 einspielen?", text: "", pfad: "/x", von: "wochenbericht", stufe: "normal", ablauf: jetzt + 3600, status: "offen", befund: null, } function zeigen(d: { box?: Hinweis[]; lab?: Hinweis[]; fragen?: Rueckfrage[]; wartung?: boolean; stumm?: boolean }) { const daten: Record = { "/api/instanz": { rolle: "box", instanz: "Box-Wart", partner: { eingerichtet: true, name: "Homelab" } }, "/api/partner": { eingerichtet: true, name: "Homelab", erreichbar: true }, "/api/ziele": { ziele: [] }, "/api/homelab/ziele": { ziele: [{ id: "ct-104", name: "Gitea", art: "container", instanz: "homelab", erreichbar: !d.stumm, bausteine: [], geprueft: null, rueckweg: null, zaehlung: {}, offen: 0 }], bericht: null, ausfuehrer: { verbunden: true, zuletzt: jetzt }, wartung: { laeuft: !!d.wartung, fenster: "So 03:00–04:15" }, }, "/api/start": { hinweise: d.box ?? [], updates: { gelesen: true, bausteine: [], unklar: [], festgehalten: [] } }, "/api/homelab/hinweise": { hinweise: d.lab ?? [] }, "/api/rueckfragen": { offen: d.fragen ?? [], zuletzt: [] }, } vi.mocked(api).mockImplementation((async (pfad: string) => { if (pfad in daten) return daten[pfad] throw new Error(`unerwartete Abfrage ${pfad}`) }) as typeof api) render() } describe("Handy-Startansicht", () => { beforeEach(() => vi.clearAllMocks()) it("alles ruhig: grüne Ampel, nichts braucht dich", async () => { zeigen({ lab: [hinweis("a", "gelb", "Stummer Hinweis", "stumm")] }) expect(await screen.findByText("Nichts — alles läuft, und nichts wartet auf dich.")).toBeInTheDocument() expect(screen.getByRole("heading", { level: 2, name: "Alles läuft" })).toBeInTheDocument() }) it("rote Hinweise zuerst, dazu stumme Geräte", async () => { zeigen({ box: [hinweis("g", "gelb", "Job „Daily News Report“ mit Fehlern")], lab: [hinweis("r", "rot", "Gitea antwortet nicht")], stumm: true }) expect(await screen.findByText("Gitea antwortet nicht")).toBeInTheDocument() const eintraege = (await screen.findAllByRole("link")).map((l) => l.textContent) expect(eintraege.indexOf("Gitea antwortet nichtHomelab")).toBeLessThan(eintraege.indexOf("Job „Daily News Report“ mit FehlernKI-Box")) expect(screen.getByRole("heading", { level: 2, name: "Störung" })).toBeInTheDocument() }) it("Rückfragen stehen oben mit Ja und Nein", async () => { zeigen({ fragen: [frage] }) expect(await screen.findByRole("heading", { level: 2, name: "Lucy wartet auf dich" })).toBeInTheDocument() expect(screen.getByText("Soll ich Gitea 1.27.4 einspielen?")).toBeInTheDocument() expect(screen.getByRole("button", { name: "Ja, ausführen" })).toBeInTheDocument() }) it("im Wartungsfenster: Wartung statt Störung-Farbe", async () => { zeigen({ wartung: true }) expect(await screen.findByRole("heading", { level: 2, name: "Wartung läuft" })).toBeInTheDocument() expect(screen.getByText("Fenster So 03:00–04:15 — Meldung am Ende.")).toBeInTheDocument() }) })