- /handy: Ampel, „Braucht Aufmerksamkeit“ (Rückfragen mit Ja/Nein, rote vor gelben Hinweisen, Geräte ohne Antwort, bereite Updates), vier Wege weiter, Knopf „Als App auf dieses Gerät legen“, wo Chrome es anbietet - Manifest: startet in der Handy-Ansicht, Sprache, Beschreibung, Kurzwege - Seitenleiste: Handy-Ansicht nur in der Handy-Schublade; Strg+K kennt sie - BEDIENUNG.md: App aufs Handy legen Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
78 lines
4.3 KiB
TypeScript
78 lines
4.3 KiB
TypeScript
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<typeof import("@/lib/api")>()), api: vi.fn(), post: vi.fn() }))
|
||
vi.mock("@tanstack/react-router", async (original) => ({
|
||
...(await original<typeof import("@tanstack/react-router")>()),
|
||
Link: ({ to, hash, children, className }: { to: string; hash?: string; children: ReactNode; className?: string }) => (
|
||
<a href={hash ? `${to}#${hash}` : to} className={className}>{children}</a>
|
||
),
|
||
}))
|
||
|
||
// 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<string, unknown> = {
|
||
"/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(<QueryClientProvider client={new QueryClient()}><HandySeite /></QueryClientProvider>)
|
||
}
|
||
|
||
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()
|
||
})
|
||
})
|