Befunde der Schlussdurchsicht (Attrappe des Backends, 320 x 568 bis 1440 px):
- Rueckfragen: Bei 320 x 568 lagen "Installieren"/"Abbrechen" unter dem Bildschirmrand
und liessen sich nicht erreichen (Dialog 791 px hoch, kein Scrollen). Jetzt hoechstens
Bildschirmhoehe, der Text scrollt, die Knoepfe bleiben unten stehen. Gilt fuer alle.
- Installieren: Die Rueckfrage sagt jetzt, dass llama-swap beim Eintragen sofort neu laedt
(--watch-config): laufende Antworten von Lucy/OpenChamber brechen ab, mit Rolle nach dem
Download noch einmal. Der Ersatz-Hinweis nennt, dass das bisherige Modell "heavy" behaelt.
- Abbrechen nur noch bei Downloads (eigener Prozess). Updates und die Suche laufen als
Shell-Kette, ein Abbruch stoppt dort nicht sicher alles.
- Auftragstitel in den Worten der Oberflaeche ("Update Motor (llama.cpp)" statt
"Engine-Update (llama.cpp Vulkan)"); "Laeuft seit ... min" zaehlt weiter, auch wenn der
Auftrag nichts schreibt; Ueberschriftenebene passend zum Panel.
- Updates: "Alles jetzt aktualisieren" richtet sich wie die Zeilen nach den frischen
Details (vorher nur nach dem bis zu 10 min alten Start-Stand); "Nach Neuem suchen"
sperrt, solange eine Suche laeuft (zwei apt-get update stolpern ueber die Sperre);
Rueckfragetext sagt genau, was gesichert wird.
- Nach Auftragsende nur nachladen, was betroffen ist (Download: Modelle/Platte; sonst
Versionen/Verlauf/Sicherungen) - die Update-Pruefung fragt GitHub, apt und git.
- Radar "Uebernehmen"/"Verwerfen" und "Endgueltig loeschen": Ja-Knopf gesperrt, solange
die Aktion laeuft, und ein zweiter Klick waehrend des Schliessens loest nichts mehr aus.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
100 lines
4.6 KiB
TypeScript
100 lines
4.6 KiB
TypeScript
import { fireEvent, render, screen } from "@testing-library/react"
|
|
import { jobTitel, sichtbareJobs } from "@/lib/wartung"
|
|
import type { Job } from "@/lib/typen"
|
|
import { JobKarte } from "./JobKarte"
|
|
|
|
const JETZT = 1_790_000_000 // Unix-Sekunden, fester Bezugspunkt
|
|
|
|
function job(teil: Partial<Job>): Job {
|
|
return {
|
|
id: "j1", label: "Nach Updates suchen", state: "running", group: null,
|
|
log: ["$ bash -c 'sudo apt-get update'"], returncode: null,
|
|
started_at: JETZT - 120, finished_at: null, ...teil,
|
|
}
|
|
}
|
|
|
|
describe("JobKarte", () => {
|
|
it("zeigt bei einem Download Fortschritt und fragt vor dem Abbrechen nach", () => {
|
|
const onAbbrechen = vi.fn()
|
|
render(
|
|
<JobKarte
|
|
job={job({
|
|
label: "download unsloth/Qwen3.8-27B-GGUF", progress: 42,
|
|
done_bytes: 12 * 1024 ** 3, total_bytes: 29 * 1024 ** 3, eta_s: 360,
|
|
})}
|
|
onAbbrechen={onAbbrechen}
|
|
/>,
|
|
)
|
|
expect(screen.getByText("Download unsloth/Qwen3.8-27B-GGUF")).toBeInTheDocument()
|
|
expect(screen.getByRole("progressbar")).toHaveAttribute("aria-valuenow", "42")
|
|
expect(screen.getByText("42 % · 12,0 GB von 29,0 GB · noch etwa 6 min")).toBeInTheDocument()
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "Abbrechen" }))
|
|
expect(screen.getByRole("dialog", { name: "Auftrag abbrechen?" })).toBeInTheDocument()
|
|
expect(onAbbrechen).not.toHaveBeenCalled()
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "Ja, abbrechen" }))
|
|
expect(onAbbrechen).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
it("nennt einen abgebrochenen Auftrag „Abgebrochen“ und nicht „Läuft“", () => {
|
|
render(<JobKarte job={job({ state: "canceled", finished_at: JETZT })} onAbbrechen={() => {}} />)
|
|
expect(screen.getByText("Abgebrochen")).toBeInTheDocument()
|
|
expect(screen.queryByText("Läuft")).not.toBeInTheDocument()
|
|
expect(screen.queryByRole("button", { name: "Abbrechen" })).not.toBeInTheDocument()
|
|
})
|
|
|
|
it("bietet bei Updates und der Suche kein Abbrechen an und zeigt bei Fehlern die letzte Meldung", () => {
|
|
const { rerender } = render(
|
|
<JobKarte job={job({ label: "Hermes-Agent-Update", group: "maintenance" })} onAbbrechen={() => {}} />,
|
|
)
|
|
expect(screen.getByText("Update Hermes")).toBeInTheDocument()
|
|
expect(screen.queryByRole("button", { name: "Abbrechen" })).not.toBeInTheDocument()
|
|
|
|
// Die Suche läuft als Shell-Kette wie die Updates: kein Abbrechen, das womöglich nichts stoppt.
|
|
rerender(<JobKarte job={job({ label: "Nach Updates suchen" })} onAbbrechen={() => {}} />)
|
|
expect(screen.queryByRole("button", { name: "Abbrechen" })).not.toBeInTheDocument()
|
|
|
|
rerender(
|
|
<JobKarte
|
|
job={job({
|
|
label: "Hermes-Agent-Update", group: "maintenance", state: "failed", finished_at: JETZT,
|
|
log: ["$ bash -c …", "✗ HERMES-UPDATE FEHLGESCHLAGEN (Dienste laufen wieder, aber alter Stand)", ""],
|
|
})}
|
|
/>,
|
|
)
|
|
expect(screen.getByText("Fehler")).toBeInTheDocument()
|
|
expect(screen.getByText(/Letzte Meldung: ✗ HERMES-UPDATE FEHLGESCHLAGEN/)).toBeInTheDocument()
|
|
})
|
|
})
|
|
|
|
describe("jobTitel", () => {
|
|
it("nennt die Bausteine wie die Oberfläche und lässt Unbekanntes stehen", () => {
|
|
expect(jobTitel({ label: "Engine-Update (llama.cpp Vulkan)" })).toBe("Update Motor (llama.cpp)")
|
|
expect(jobTitel({ label: "Router-Update (llama-swap)" })).toBe("Update llama-swap")
|
|
expect(jobTitel({ label: "OS-Update (apt)" })).toBe("Update Betriebssystem")
|
|
expect(jobTitel({ label: "Alle aktualisieren (Engine (llama.cpp) → Router (llama-swap) → Hermes-Agent → OS (apt))" }))
|
|
.toBe("Alle aktualisieren (Motor → llama-swap → Hermes → Betriebssystem)")
|
|
expect(jobTitel({ label: "download unsloth/Qwen3.8-27B-GGUF" })).toBe("Download unsloth/Qwen3.8-27B-GGUF")
|
|
expect(jobTitel({ label: "Nach Updates suchen" })).toBe("Nach Updates suchen")
|
|
})
|
|
})
|
|
|
|
describe("sichtbareJobs", () => {
|
|
it("zeigt alle laufenden Aufträge jeder Gruppe und die drei zuletzt beendeten", () => {
|
|
const liste = sichtbareJobs(
|
|
[
|
|
job({ id: "alt", state: "done", finished_at: JETZT - 2 * 86_400 }),
|
|
job({ id: "f1", state: "done", finished_at: JETZT - 50 }),
|
|
job({ id: "f2", state: "failed", finished_at: JETZT - 40 }),
|
|
job({ id: "f3", state: "canceled", finished_at: JETZT - 30 }),
|
|
job({ id: "f4", state: "done", finished_at: JETZT - 20 }),
|
|
job({ id: "download", label: "download a/b", state: "running", group: null }),
|
|
job({ id: "update", group: "maintenance", state: "queued", started_at: JETZT - 10 }),
|
|
],
|
|
JETZT,
|
|
)
|
|
expect(liste.map((j) => j.id)).toEqual(["download", "update", "f4", "f3", "f2"])
|
|
})
|
|
})
|