Phase 7c: restliche Screens - alle 21 Seiten stehen

Fahrzeugdaten, Batterieverlauf, Service mit Werkstatt und Servicebuch,
Reifen, die ganze Versicherungs- und Steuergruppe, Einstellungen in voller
Tiefe und die Live-Ansicht.

Portiert dazu: die eigene Oelwechsel-Prognose (rechnet ab dem letzten
Wechsel im Servicebuch neu, weil der Langzeitschnitt dafuer zu traege ist),
Kalenderdateien fuer Termine und die CSV-Ausgabe im deutschen Format.

Die Live-Ansicht ist gebaut, aber ueber einen Schalter abgeschaltet: die
Werte dafuer liefert erst der FMM003. Ein Menuepunkt mit lauter Nullen waere
schlechter als keiner.

Zwei Stellen, an denen die App bewusst zurueckhaltend ist: ein unbekannter
Pruefpunkt erscheint als hohler Ring statt als geratenes Gruen, und der
Reifen-Kilometerstand wird nie zurueckgeschrieben, weil ihn der Server
fortschreibt.

49 Tests, darunter ein Rendertest ueber alle 21 Seiten in beiden Layouts
mit Beispieldaten in der Form, die das Backend wirklich liefert.
This commit is contained in:
Paul Nothaft
2026-08-11 11:17:47 +02:00
parent 8b64a808ed
commit 8e6e5bc2ee
16 changed files with 2258 additions and 54 deletions
+109
View File
@@ -0,0 +1,109 @@
/**
* Abnahme für Phase 7 des Umsetzungsplans: Jede Seite muss mit echten
* Beispieldaten rendern — in beiden Layouts — und die Leerzustände müssen
* greifen, statt dass eine leere Liste die Seite zerlegt.
*/
import { render, screen, waitFor } from "@testing-library/react"
import { beforeEach, describe, expect, it } from "vitest"
import { DatenAnbieter } from "../daten/DatenKontext"
import { Shell } from "../Shell"
import type { SeitenName } from "../navigation"
import { TITEL } from "../navigation"
import { SeiteFuer } from "./register"
import { beispielApi } from "../tests/beispieldaten"
import { bildschirmbreiteSetzen } from "../tests/aufbau"
const ALLE_SEITEN = Object.keys(TITEL) as SeitenName[]
function zeige(name: SeitenName, id?: string) {
const api = beispielApi()
return render(
<div className="ads-root" data-theme="nacht">
<DatenAnbieter api={api}>
<Shell
route={id === undefined ? { name } : { name, id }}
geheZu={() => {}}
tabBeschriftung
>
<SeiteFuer
route={id === undefined ? { name } : { name, id }}
geheZu={() => {}}
tabBeschriftung
setzeTabBeschriftung={() => {}}
beiAbmeldung={() => {}}
/>
</Shell>
</DatenAnbieter>
</div>,
)
}
describe("Alle Seiten rendern mit Beispieldaten", () => {
beforeEach(() => {
bildschirmbreiteSetzen(390)
})
for (const name of ALLE_SEITEN) {
it(`${name} (${TITEL[name]})`, async () => {
const id = name === "trip" ? "t1" : name === "fill" ? "f1" : undefined
const { container } = zeige(name, id)
// Nach der Erstladung darf kein Ladehinweis mehr stehen.
await waitFor(() => {
expect(container.querySelector(".dm-laden")).toBeNull()
})
expect(container.textContent?.length ?? 0).toBeGreaterThan(20)
})
}
it("rendert jede Seite auch im Flächenlayout", async () => {
bildschirmbreiteSetzen(1280)
for (const name of ALLE_SEITEN) {
const id = name === "trip" ? "t1" : name === "fill" ? "f1" : undefined
const { container, unmount } = zeige(name, id)
await waitFor(() => {
expect(container.querySelector(".dm-laden")).toBeNull()
})
expect(container.querySelector(".dm-seitenleiste")).not.toBeNull()
unmount()
}
})
})
describe("Inhalte kommen wirklich aus den Daten", () => {
beforeEach(() => {
bildschirmbreiteSetzen(390)
})
it("zeigt Kilometerstand und Reichweite auf der Übersicht", async () => {
zeige("home")
await waitFor(() => expect(screen.getByText("48.250")).toBeTruthy())
expect(screen.getByText("385")).toBeTruthy()
})
it("zeigt einen unbekannten Prüfpunkt als unbekannt, nicht als sicher", async () => {
const { container } = zeige("sicherheit")
await waitFor(() => expect(container.querySelector(".dm-pruefliste")).not.toBeNull())
expect(container.querySelectorAll(".dm-punkt--unbekannt").length).toBe(1)
expect(screen.getByText("unbekannt")).toBeTruthy()
})
it("gruppiert Fahrten nach Jahr", async () => {
zeige("trips")
await waitFor(() => expect(screen.getByText("2026")).toBeTruthy())
expect(screen.getByText("42,5 km")).toBeTruthy()
})
it("zeigt bei einer Fahrt ohne Position keine erfundene Route", async () => {
const { container } = zeige("trip", "t1")
await waitFor(() => expect(screen.getByText("Keine Route aufgezeichnet")).toBeTruthy())
expect(container.querySelector(".dm-karte")).toBeNull()
})
it("rechnet den Tankvorgang mit mengengewichtetem Preis", async () => {
zeige("fill", "f1")
await waitFor(() => expect(screen.getByText("48,70")).toBeTruthy())
expect(screen.getByText("1,83 €")).toBeTruthy()
})
})