Steuerkram/tests/e2e/README.md

131 lines
5.9 KiB
Markdown

# Steuerkram — E2E-Tests (Playwright)
Browser-Tests für die Ebene, die die PHP-Testsuite nicht erreicht: Routing,
Controller, Views und das JavaScript in den Formularen.
Sie laufen **im Container** des generischen
[e2e-runner-Projekts](../../../e2e-runner/) (`~/projects/e2e-runner/`) — auf dem
Host wird nichts installiert (kein Node, kein Browser, kein npm).
## Warum diese Ebene
Die PHP-Suite (`tests/NN_*.php`) prüft Models und Services und ist dort gründlich.
Sie ruft aber nie einen Controller über HTTP auf. Genau dort saßen die zuletzt
aufgetretenen Fehler:
- **`/accounts/new` und `/assets/new` lieferten HTTP 500** — gefunden vom ersten
Smoke-Lauf. Ursache: undefinierte Variablen, die seit der Fehler-Härtung
(`Fix #142`) keine stille Warning mehr sind, sondern eine Exception.
- **Netto/Brutto-Umschalter** (`Fix #137`): Ein submit-Handler rechnet den
eingegebenen Bruttowert vor dem Absenden in Netto um. Serverseitig ist nur das
Ergebnis sichtbar — eine kaputte Umrechnung fiele nie auf.
- **DOM-XSS in der Bankimport-Vorschau** (`Fix #133`): Die Vorschau wird
vollständig im Browser aus einer `fetch`-Antwort gebaut.
## Voraussetzungen
1. **Steuerkram läuft** unter `http://localhost:8080` (`docker compose up -d`).
2. **Testnutzer existiert** (einmalig, idempotent wiederholbar):
```bash
docker compose exec web php scripts/e2e-user.php
```
Legt `e2e` / `e2e-test-Pass123!` an bzw. setzt Passwort und Sperrstatus
zurück. Abweichende Zugangsdaten als Argumente oder per `E2E_USER` /
`E2E_PASSWORD` (Defaults: `helpers/env.js`). Entfernen mit `--delete`.
3. **Testmodus aktiv** — sonst lassen sich Testbuchungen und -rechnungen nicht
wieder löschen (Einstellungen → Testmodus).
## Ausführen
```bash
tests/e2e/run.sh
```
Das Skript prüft vorab, ob Docker, das e2e-runner-Projekt (Pfad überschreibbar
via `E2E_RUNNER_DIR`) und das Playwright-Image vorhanden sind. Fehlt etwas, wird
**nur gewarnt und übersprungen** (Exit 0) — die E2E-Tests sind optional und
blockieren keine Umgebung ohne Runner.
Andere Zielinstanz: `BASE_URL=http://host.docker.internal:9000 tests/e2e/run.sh`
Einzelne Datei oder UI-Modus zum Debuggen:
```bash
cd ~/projects/e2e-runner
TEST_DIR=~/projects/Steuerkram/tests/e2e docker compose run --rm playwright \
sh -c "npm ci && npx playwright test smoke.spec.js"
# UI-Modus, danach http://localhost:8077 öffnen:
TEST_DIR=~/projects/Steuerkram/tests/e2e docker compose run --rm --service-ports playwright-ui
```
HTML-Report, Screenshots und Traces landen in `playwright-report/` und
`test-results/`.
## Umgang mit Testdaten
Die Tests laufen gegen eine Instanz mit **echten Arbeitsdaten**. Deshalb gilt:
- Sie legen ausschließlich eigene Datensätze an, markiert mit `E2E-TEST-<zeit>-<n>`
(`helpers/app.js`, `uniqueTag`), und räumen sie im `afterEach` wieder ab.
- Bestehende Belege, Buchungen und Rechnungen werden nie verändert oder gelöscht.
- Der Bankimport-Test bricht **vor** dem Bestätigen ab — die Vorschau allein legt
keine Transaktionen an.
Reste nach einem abgebrochenen Lauf entfernen:
```bash
docker compose exec web php scripts/e2e-cleanup.php # nur anzeigen
docker compose exec web php scripts/e2e-cleanup.php --force # löschen
```
### Nummernkreise
`rechnung-export.spec.js` finalisiert eine Rechnung und vergibt dabei eine
Rechnungs- und eine Belegnummer. Beide gehen beim Aufräumen wieder zurück:
`Invoice::delete()` und `Beleg::delete()` zählen den jeweiligen Nummernkreis
zurück, wenn der gelöschte Datensatz die **zuletzt vergebene** Nummer trug.
Nach einem vollständigen Testlauf stehen die Zähler also wieder auf ihrem
Ausgangswert; der Test `Löschen einer finalisierten Rechnung gibt die Nummer
wieder frei` weist das nach.
War die Nummer nicht die letzte (z.B. weil parallel eine echte Rechnung
finalisiert wurde), bleibt der Zähler stehen — eine Nummer wird nie zweimal
vergeben. In dem Fall entsteht wie bisher eine Lücke im Nummernkreis.
Bricht ein Lauf mittendrin ab, bleiben Testdatensätze liegen;
`scripts/e2e-cleanup.php --force` räumt sie nachträglich ab und arbeitet dabei
absteigend, damit möglichst viele Nummern zurückwandern.
## Aufbau
| Datei | Inhalt |
|---|---|
| `specs/smoke.spec.js` | Alle Hauptseiten rendern ohne Serverfehler; 404, Login-Zwang, CSV-Export |
| `specs/beleg-buchung.spec.js` | Upload → Buchungsmaske → gespeicherte Buchung, inkl. Netto/Brutto-Umschalter |
| `specs/rechnung-export.spec.js` | Entwurf → Finalisieren → PDF- und XML-Export (XRechnung 3.0) |
| `specs/bank-import.spec.js` | Bankimport-Vorschau inkl. XSS-Regression |
| `helpers/app.js` | Upload, Aufräumen, Betragsparsing, `gotoOk` |
| `helpers/env.js` | Zugangsdaten und Basis-URL |
| `global-setup.js` | Einmaliger Login, legt `.auth/user.json` an |
## Fallstricke
- **Submit-Buttons immer auf ihr Formular scopen.** In der Sidebar steht das
Logout-Formular (`layout/nav.php`); ein ungescopetes
`button[type="submit"]` trifft dessen Button zuerst und beendet die Session —
alle folgenden Tests laufen dann gegen die Login-Seite.
- **Reihenfolge in der Buchungsmaske:** Die Auswahl des Kategorie-Kontos ruft
`updateAutoUst()` und setzt den USt-Satz passend zum Steuercode. Den Satz
deshalb **nach** dem Konto setzen.
- **Ein Redirect auf `/login` endet mit Status 200.** `gotoOk()` prüft deshalb
zusätzlich den Pfad — sonst schlagen Tests mit irreführenden Meldungen fehl.
- **Belegverzeichnisse aus PHP-Testläufen:** Die PHP-Suite immer mit
`docker compose exec -u www-data web php tests/run.php` starten. Ohne `-u www-data`
läuft sie als *root* und kann in `data/belege/<Jahr>/` ein Verzeichnis anlegen, das
root gehört, während die Transaktion die Belegnummer zurückrollt. Trifft später
ein echter Upload auf dieselbe Nummer, scheitert `move_uploaded_file()` mit
HTTP 500. Solche Leichen aus früheren Läufen finden:
`find data/belege -maxdepth 2 -user root`