diff --git a/CLAUDE.md b/CLAUDE.md index aec188c..5ecba66 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -227,7 +227,9 @@ docker compose -f docker-compose.prod.yml exec app php artisan migrate --force - **Work on a feature branch, not `main`.** Commit in sensible steps. - **Secret hygiene:** `.env*` (incl. `.env.gitea`) + keys are gitignored; never commit/echo the Gitea token (read it only at push time). See `rules.md` → Appendix. -- **Verify before "done":** container builds, page renders, no console errors. Test the 3 breakpoints. +- **Verify before "done" (R12):** load **every touched page in a real browser** (headless ok) → + **HTTP 200** (no 500) + **zero console/network errors**; for lazy `wire:init` pages check the + **loaded** state, not the skeleton. A green `Livewire::test` is **not** enough. Test the 3 breakpoints. --- @@ -243,5 +245,7 @@ docker compose -f docker-compose.prod.yml exec app php artisan migrate --force 7. Run all tooling **inside the container** (R8). 8. UI copy **German, no emoji**; status via color/dots/pills (R9). 9. **Reuse** the token set + Blade component kit (R10). -10. **Verify** (build, render, no console errors, 3 breakpoints), then `git status` for stray +10. **Verify in a browser (R12)**: every touched route loads at **HTTP 200** with **zero console + errors** (loaded state of lazy pages, not the skeleton) — a green `Livewire::test` is not enough; + build + 3 breakpoints; then `git status` for stray secrets, commit on the feature branch. diff --git a/resources/views/livewire/services/index.blade.php b/resources/views/livewire/services/index.blade.php index 7cf3a62..80d4389 100644 --- a/resources/views/livewire/services/index.blade.php +++ b/resources/views/livewire/services/index.blade.php @@ -70,9 +70,9 @@ {{-- R5: wire to wire-elements/modal --}}
- Starten - Stopp - Neustart + Starten + Stopp + Neustart
diff --git a/rules.md b/rules.md index 273eddc..48d0c7b 100644 --- a/rules.md +++ b/rules.md @@ -21,6 +21,7 @@ - [ ] **R9** — UI copy is **German**, **no emoji**; status via color/dots/pills. - [ ] **R10** — Reused existing `@theme` tokens + Blade components (no ad-hoc colors/widgets). - [ ] **R11** — Records exposed in URLs are addressed by **UUID**, never the integer PK. +- [ ] **R12** — every touched page loads in a **browser** at **HTTP 200** with **zero console errors** (the **loaded** state of `wire:init` pages, not just the skeleton). - [ ] **Secrets** — `git status` shows **no** `.env*`, token, key, or home-dir dotfile staged. --- @@ -336,6 +337,40 @@ route('servers.show', $server->id); // exposes the auto-increment id --- +## R12 — Browser-verified: HTTP 200, zero console errors + +**Why.** A passing `Livewire::test(...)->assertOk()` only renders ONE state in PHP — it does not +catch client-side JS errors, and for a `wire:init`/lazy page it exercises the **skeleton**, not the +loaded view. Render-time Blade/PHP errors (e.g. `@disabled` on a component → "unexpected endif", a +500) only surface on the real request. A green unit test is **not** "done". + +**Rule.** Before declaring any UI change done, load **every affected page in a real browser** +(headless is fine) and confirm, per page: **HTTP 200** (no 500), **zero console errors**, **zero +failed requests**. For lazy pages, verify the **loaded** state (after `wire:init`), not the skeleton. +Never use `@disabled`/`@checked`/`@required` on a Blade **component** — use the bound `:disabled="…"` +attribute (the component attribute bag drops a `false` value correctly). + +✅ **Correct** — headless probe over the routes (captures console + HTTP status): +```bash +# mount the script into the image's home dir or require('puppeteer') fails +docker run --rm --network host -v /tmp/probe.js:/home/pptruser/probe.js -w /home/pptruser \ + ghcr.io/puppeteer/puppeteer:latest node probe.js +``` +```js +page.on('console', m => { if (m.type() === 'error') errors.push(m.text()); }); +const r = await page.goto(url, { waitUntil: 'networkidle2' }); // expect r.status() === 200 +await new Promise(s => setTimeout(s, 4000)); // let wire:init load, then assert errors == 0 +``` + +❌ **Forbidden** +```text +"Livewire::test rendered OK" → shipping without ever loading the page in a browser. +Testing only the skeleton state of a wire:init page and assuming the loaded state works. +@disabled($cond) on → injects if/endif into the component tag → 500. +``` + +--- + ## Appendix — Secret hygiene The project lives in `/home/nexxo/clusev` (its own git repo). The Gitea push token lives in