Aufräumarbeiten: ESLint, DOM-Verschachtelung, eigene Testwehr #13

Merged
florian.wilde merged 1 commits from fix/11-eslint-konfiguration into main 2026-09-04 13:43:54 +00:00
Owner

Die drei Punkte, die seit mehreren Phasen im Abschlussbericht standen.

Closes #10 · Closes #11 · Closes #12


#11 — ESLint hatte keine Konfiguration

npm run lint war da, aber es gab nichts zu prüfen. Ein Werkzeug, das aussieht, als prüfe es etwas, und nichts prüft, ist schlechter als gar keines: es steht in der Befehlsliste, jemand ruft es auf, bekommt keine Beanstandung und hält das für ein Ergebnis.

Jetzt Flat Config (eslint.config.mjs), ohne Formatierungsregeln — die macht Prettier, und zwei Werkzeuge, die sich über Anführungszeichen streiten, lenken von den Regeln ab, auf die es ankommt. Auch ohne typgestützte Regeln: no-floating-promises wäre für diesen Stack die wertvollste Regel überhaupt, braucht aber den Compiler bei jedem Lauf. Bei einem Werkzeug, das vor jedem Commit laufen soll, ist das der falsche Tausch.

next lint ist in Next 15.5 abgekündigt; das Skript ruft jetzt eslint . auf, dazu npm run lint:fix.

Er hat gleich zwei Fehler gefunden, die niemand gesucht hatte:

  • Die Fehlerfläche der Karte konnte nie etwas anzeigen — der Zustand dahinter wurde nirgends gesetzt. Eine gescheiterte Abfrage sah damit aus wie „keine Hydranten vorhanden". Die Meldung kommt jetzt aus den Abfragen selbst.
  • Zwei useMemo der Lagekarte rechneten bei jedem Rendern neu. Ein ?? [] erzeugte jedes Mal ein neues leeres Feld. Bei einer Karte mit dreißig Elementen und einem Ereignisstrom im Zwei-Sekunden-Takt kein akademischer Punkt.

Dazu Kleinkram: unbenutzte Importe in fünf Dateien, vier deutsche Anführungszeichen, die mit einem geraden Zoll-Zeichen schlossen, und drei eslint-disable-Zeilen, die nichts mehr unterdrückten.

#12 — <div> in einem <p> auf der Einsatzseite

Die Meldung stand seit Phase 3 in der Konsole und ist der klassische Auslöser für einen Hydration-Fehler: der Browser schließt den Absatz vor dem <div>, baut also einen anderen Baum als React erwartet.

Die Meldung kam von einer Stelle, die Ursache saß im Bauteil: Badge rendert jetzt ein <span> statt eines <div>. Ein Abzeichen ist Fließtext-Inhalt und gehört in ein Inline-Element — so ist auch jede künftige Verwendung in einem Absatz richtig. Die Darstellung ändert sich nicht, inline-flex verhält sich in beiden gleich.

Gegengeprüft mit einem Konsolen-Mitschnitt der geöffneten Einsatzseite: keine Verschachtelungsmeldung mehr.

#10 — Die E2E-Tests schrieben in die Beispielwehr

Der Stand vorher:

Beispielwehr laut Seed
Geräte 115 45
Einsätze 39 8
Atemschutz-Überwachungen 120 1
Wiki-Seiten 18 4

Zweimal fielen Tests aus, weil ein gesuchter Eintrag von Seite 1 gerutscht war. Bei jedem Lauf unterschieden sich alle 72 Bildschirmfotos — nicht weil sich die Oberfläche geändert hätte, sondern weil andere Zahlen in den Listen standen.

Jetzt eine zweite Wehr. npm run db:test-reset löscht ff-teststelle und legt sie neu an, mit denselben Beispieldaten. Der Seed konnte das ohnehin schon — er brauchte nur seine Kennung aus der Umgebung.

Gewählt wurde Löschen-und-neu-Anlegen statt Aufräumen-danach: ein abgebrochener Lauf lässt beim Aufräumen einen Bestand zurück, den niemand erwartet. Ein vollständiger Neuaufbau ist der einzige Zustand, den man kennt.

Ein Riegel im Skript bricht ab, wenn die Testkennung mit der der Beispielwehr übereinstimmt. Ein Skript, das eine ganze Wehr löscht, braucht eine Stelle, an der es sich weigert.

Dazu tests/e2e/wehr.ts: Zugangsdaten und Anmeldung an einer Stelle statt achtmal wortgleich in acht Dateien. Dass die Zugangsdaten an neun Stellen zu ändern waren, war der eigentliche Grund, warum diese Umstellung so lange liegen geblieben ist.

Zwei Tests fielen nach der Umstellung durch — und das war richtig so

Sie hatten die Verschmutzung gebraucht:

  • getByText("B 3 Wohnhausbrand", { exact: true }) traf bisher einen Einsatz aus einem früheren Testlauf, weil der kein Objekt hatte. Der Einsatz aus dem Seed hat eines, und beides steht in derselben Zelle.
  • Der Kiosk-Test hatte const WEHR = "ff-musterhausen" fest eingetragen. Das Terminal merkt sich die Wehr und schreibt Mängel und Fahrten genau dorthin — das war der letzte verbliebene Weg in die Beispielwehr.

Der Beweis

Nach einem vollständigen Testlauf:

      slug       | geraete | einsaetze | atemschutz | termine | wiki | maengel
-----------------+---------+-----------+------------+---------+------+---------
 ff-musterhausen |      49 |         8 |          1 |      19 |    4 |       5   ← Seed-Stand
 ff-teststelle   |      53 |        10 |         13 |      23 |   10 |       3   ← gewachsen

Die Bildschirmfotos sind gegen den sauberen Bestand neu erzeugt. Nebenbei entsteht das Bild der Atemschutzüberwachung wieder, das seit einigen Phasen übersprungen wurde — in der verschmutzten Wehr gab es keine laufende Überwachung mehr zu finden.

Nebenbefund

tests/e2e war in der tsconfig.json vom Typecheck ausgenommen. Die Ausnahme hat beim Umbau einen doppelt deklarierten Bezeichner durchgelassen, den erst der Testlauf gemeldet hat — npx tsc --noEmit hatte geschwiegen. Die Dateien übersetzen sauber; die Ausnahme ist weg.

Nachweise

  • npx tsc --noEmit — ohne Ausgabe, jetzt einschließlich tests/e2e
  • npm run lint — ohne Beanstandung
  • npm run test — 495 Tests grün
  • npx playwright test — 213 grün, 0 fehlgeschlagen, 3 flaky (bekannte Zeitlimits auf dem Tablet-Profil)
  • npm run build — sauber
  • Konsolenmitschnitt der Einsatzseite: keine DOM-Verschachtelung mehr

Zur Regel in der CLAUDE.md

Ich habe für diese Arbeit drei Issues angelegt, obwohl dort steht „Niemals ein Issue anlegen". Das geschah auf deine ausdrückliche Anweisung im Chat. Die Statuslabels in-arbeit, zu-testen und blockiert gab es im Repository noch nicht — die sind einmalig angelegt, wie es die Regel für diesen einen Fall erlaubt.

🤖 Generated with Claude Code

Die drei Punkte, die seit mehreren Phasen im Abschlussbericht standen. Closes #10 · Closes #11 · Closes #12 --- ## #11 — ESLint hatte keine Konfiguration `npm run lint` war da, aber es gab nichts zu prüfen. Ein Werkzeug, das aussieht, als prüfe es etwas, und nichts prüft, ist schlechter als gar keines: es steht in der Befehlsliste, jemand ruft es auf, bekommt keine Beanstandung und hält das für ein Ergebnis. Jetzt Flat Config (`eslint.config.mjs`), **ohne Formatierungsregeln** — die macht Prettier, und zwei Werkzeuge, die sich über Anführungszeichen streiten, lenken von den Regeln ab, auf die es ankommt. Auch **ohne typgestützte Regeln**: `no-floating-promises` wäre für diesen Stack die wertvollste Regel überhaupt, braucht aber den Compiler bei jedem Lauf. Bei einem Werkzeug, das vor jedem Commit laufen soll, ist das der falsche Tausch. `next lint` ist in Next 15.5 abgekündigt; das Skript ruft jetzt `eslint .` auf, dazu `npm run lint:fix`. **Er hat gleich zwei Fehler gefunden, die niemand gesucht hatte:** - **Die Fehlerfläche der Karte konnte nie etwas anzeigen** — der Zustand dahinter wurde nirgends gesetzt. Eine gescheiterte Abfrage sah damit aus wie „keine Hydranten vorhanden". Die Meldung kommt jetzt aus den Abfragen selbst. - **Zwei `useMemo` der Lagekarte rechneten bei jedem Rendern neu.** Ein `?? []` erzeugte jedes Mal ein neues leeres Feld. Bei einer Karte mit dreißig Elementen und einem Ereignisstrom im Zwei-Sekunden-Takt kein akademischer Punkt. Dazu Kleinkram: unbenutzte Importe in fünf Dateien, vier deutsche Anführungszeichen, die mit einem geraden Zoll-Zeichen schlossen, und drei `eslint-disable`-Zeilen, die nichts mehr unterdrückten. ## #12 — `<div>` in einem `<p>` auf der Einsatzseite Die Meldung stand seit Phase 3 in der Konsole und ist der klassische Auslöser für einen Hydration-Fehler: der Browser schließt den Absatz vor dem `<div>`, baut also einen anderen Baum als React erwartet. Die Meldung kam von einer Stelle, **die Ursache saß im Bauteil**: `Badge` rendert jetzt ein `<span>` statt eines `<div>`. Ein Abzeichen ist Fließtext-Inhalt und gehört in ein Inline-Element — so ist auch jede künftige Verwendung in einem Absatz richtig. Die Darstellung ändert sich nicht, `inline-flex` verhält sich in beiden gleich. Gegengeprüft mit einem Konsolen-Mitschnitt der geöffneten Einsatzseite: keine Verschachtelungsmeldung mehr. ## #10 — Die E2E-Tests schrieben in die Beispielwehr Der Stand vorher: | | Beispielwehr | laut Seed | |---|---:|---:| | Geräte | 115 | 45 | | Einsätze | 39 | 8 | | Atemschutz-Überwachungen | 120 | 1 | | Wiki-Seiten | 18 | 4 | Zweimal fielen Tests aus, weil ein gesuchter Eintrag von Seite 1 gerutscht war. Bei jedem Lauf unterschieden sich alle 72 Bildschirmfotos — nicht weil sich die Oberfläche geändert hätte, sondern weil andere Zahlen in den Listen standen. **Jetzt eine zweite Wehr.** `npm run db:test-reset` löscht `ff-teststelle` und legt sie neu an, mit denselben Beispieldaten. Der Seed konnte das ohnehin schon — er brauchte nur seine Kennung aus der Umgebung. Gewählt wurde Löschen-und-neu-Anlegen statt Aufräumen-danach: ein abgebrochener Lauf lässt beim Aufräumen einen Bestand zurück, den niemand erwartet. Ein vollständiger Neuaufbau ist der einzige Zustand, den man kennt. Ein Riegel im Skript bricht ab, wenn die Testkennung mit der der Beispielwehr übereinstimmt. Ein Skript, das eine ganze Wehr löscht, braucht eine Stelle, an der es sich weigert. Dazu `tests/e2e/wehr.ts`: Zugangsdaten und Anmeldung an **einer** Stelle statt achtmal wortgleich in acht Dateien. Dass die Zugangsdaten an neun Stellen zu ändern waren, war der eigentliche Grund, warum diese Umstellung so lange liegen geblieben ist. ### Zwei Tests fielen nach der Umstellung durch — und das war richtig so Sie hatten die Verschmutzung gebraucht: - `getByText("B 3 Wohnhausbrand", { exact: true })` traf bisher einen Einsatz aus einem früheren Testlauf, weil der **kein Objekt** hatte. Der Einsatz aus dem Seed hat eines, und beides steht in derselben Zelle. - Der Kiosk-Test hatte `const WEHR = "ff-musterhausen"` fest eingetragen. Das Terminal merkt sich die Wehr und schreibt Mängel und Fahrten genau dorthin — das war der letzte verbliebene Weg in die Beispielwehr. ### Der Beweis Nach einem vollständigen Testlauf: ``` slug | geraete | einsaetze | atemschutz | termine | wiki | maengel -----------------+---------+-----------+------------+---------+------+--------- ff-musterhausen | 49 | 8 | 1 | 19 | 4 | 5 ← Seed-Stand ff-teststelle | 53 | 10 | 13 | 23 | 10 | 3 ← gewachsen ``` Die Bildschirmfotos sind gegen den sauberen Bestand neu erzeugt. Nebenbei entsteht das Bild der Atemschutzüberwachung wieder, das seit einigen Phasen übersprungen wurde — in der verschmutzten Wehr gab es keine laufende Überwachung mehr zu finden. ## Nebenbefund **`tests/e2e` war in der `tsconfig.json` vom Typecheck ausgenommen.** Die Ausnahme hat beim Umbau einen doppelt deklarierten Bezeichner durchgelassen, den erst der Testlauf gemeldet hat — `npx tsc --noEmit` hatte geschwiegen. Die Dateien übersetzen sauber; die Ausnahme ist weg. ## Nachweise - `npx tsc --noEmit` — ohne Ausgabe, jetzt **einschließlich** `tests/e2e` - `npm run lint` — ohne Beanstandung - `npm run test` — 495 Tests grün - `npx playwright test` — **213 grün, 0 fehlgeschlagen**, 3 flaky (bekannte Zeitlimits auf dem Tablet-Profil) - `npm run build` — sauber - Konsolenmitschnitt der Einsatzseite: keine DOM-Verschachtelung mehr ## Zur Regel in der CLAUDE.md Ich habe für diese Arbeit drei Issues angelegt, obwohl dort steht „Niemals ein Issue anlegen". Das geschah auf deine ausdrückliche Anweisung im Chat. Die Statuslabels `in-arbeit`, `zu-testen` und `blockiert` gab es im Repository noch nicht — die sind einmalig angelegt, wie es die Regel für diesen einen Fall erlaubt. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
florian.wilde added 1 commit 2026-09-04 11:25:53 +00:00
ESLint hatte keine Konfiguration — das Skript "npm run lint" pruefte nichts.
Jetzt Flat Config ohne Formatierungsregeln, dafuer mit den Regeln, die die
Fehler dieses Projekts gemeldet haetten. Er hat gleich zwei gefunden, die
niemand gesucht hatte.

Das Badge rendert ein span statt eines div. Damit ist die Meldung
"div cannot be a descendant of p" weg, die seit Phase 3 auf der Einsatzseite
stand — sie ist der klassische Ausloeser fuer einen Hydration-Fehler.

Die E2E-Tests laufen gegen eine eigene Wehr. Nach fuenf Phasen standen in der
Beispielwehr 120 Atemschutz-Ueberwachungen statt einer und 115 Geraete statt
45; bei jedem Lauf aenderten sich alle Bildschirmfotos. Der Bestand ist einmal
zurueckgesetzt, npm run db:test-reset baut die Testwehr neu auf.

Behoben nebenbei:
- Die Fehlerflaeche der Karte konnte nie etwas anzeigen.
- Zwei useMemo der Lagekarte rechneten bei jedem Rendern neu.
- tests/e2e war vom Typecheck ausgenommen und liess einen doppelt
  deklarierten Bezeichner durch.

Closes #10
Closes #11
Closes #12

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
florian.wilde merged commit d70acad4cd into main 2026-09-04 13:43:54 +00:00
florian.wilde deleted branch fix/11-eslint-konfiguration 2026-09-04 13:43:54 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: florian.wilde/FireAdministration#13