# WP3a — BE live streaming + failure-screenshot/trace transfer

## Goal
Make the runner update the DB progressively as each test finishes (so the FE can show scenarios flipping one-by-one), and copy each failed test's screenshot + trace to a web-reachable folder with recorded paths.

## Repo & branch
BE repo `/home/moonui/moon-erp-be` (RunE2eCommand) + FE repo specs/reporters `/home/moonui/public_html/moon-erp` (the reporter file lives with the specs). Branch `hazemdev`. Commits: `feat(e2e):` (BE) and `test(e2e):` (reporter, FE repo).

## Current state (what to change)
- `Modules/E2eRunner/app/Console/RunE2eCommand.php` runs `new Process([npx,playwright,test,--config,cfg,(--grep,filter)])` and calls `$process->run()` (blocking), then parses `e2e/results/last-run.json` and writes `e2e_runs.results_json` ONCE at the end (`:103`). It already sets status=running at start (`:37`).
- Playwright config: `outputDir: './test-results'`, `screenshot:'only-on-failure'`, `trace:'retain-on-failure'`, JSON reporter → `e2e/results/last-run.json`. (in FE repo `e2e/playwright.config.ts`.)
- BE `public/` is web-served (FE reaches it via the `moon-erp-be` symlink → `.../moon-erp-be/public`). So files under `public/e2e-artifacts/...` are reachable at `<apiHost>/moon-erp-be/e2e-artifacts/...` — but simpler: serve from the app public. USE the BE public: copy artifacts to `/home/moonui/moon-erp-be/public/e2e-artifacts/{runId}/` and expose the URL as `{APP_URL}/e2e-artifacts/{runId}/{file}` (APP_URL is the BE app url `https://moonui.elbaset.com/moon-erp-be`). Confirm the exact public path/URL and record it.

## Changes
1. **Streaming reporter** `e2e/reporters/streaming-reporter.ts` (FE repo): a Playwright Reporter that on `onTestBegin`/`onTestEnd` writes/updates a small JSON file `e2e/results/live.json` = an array of `{title, status, duration_ms}` for tests seen so far (+ a `current` field = the running test title). Keep it cheap (rewrite the file each event). Register it in `playwright.config.ts` reporter array ALONGSIDE the existing json reporter (don't remove json — it stays the source of truth at the end).
2. **RunE2eCommand — read live + write progressively:** instead of blocking `$process->run()`, use `$process->start()` and loop: while running, every ~1.5s read `e2e/results/live.json` (if present), map to the scenarios shape (reuse the group/description/steps parse where possible — for live, title+status+duration is enough; enrich at the end from last-run.json), and `$run->update(['results_json'=>..., 'total'=>seen, 'passed'=>..,'failed'=>..])`. On process end, do the final authoritative parse from `last-run.json` (existing logic) so the end state is exact. Keep the hard timeout + orphan-chromium kill.
3. **Artifact transfer (on failure):** after the run finishes, scan `test-results/` for each failed test's `test-failed-*.png` (screenshot) and `trace.zip`; copy them to `/home/moonui/moon-erp-be/public/e2e-artifacts/{runId}/{safeTestSlug}.png|.zip`; record per-scenario `screenshot` + `trace` URL in the scenario object in `results_json`. Create the dir (chmod so the web user reads it). If none failed, nothing to copy. Best-effort: a copy failure must not fail the run.
4. Keep it safe: only the int runId + fixed paths reach any shell; no user input in file ops. Clean old artifact dirs opportunistically (e.g. keep last ~10 runs) — optional.

## Interfaces exposed to WP3b (FE)
- `GET /api/e2e/runs/latest` `scenarios[]` now updates PROGRESSIVELY (not all-at-once) and each failed scenario carries `screenshot` (URL) + `trace` (URL). The run row may add a `current` (running test title) — surface it in `latest()` if easy (optional; FE can derive "running" from a scenario status).

## Acceptance criteria
- [ ] During a run, `GET /e2e/runs/latest` shows scenarios appearing/flipping status ONE BY ONE (verify with a live trigger: poll during a run and see counts climb before it finishes).
- [ ] A failed scenario's `screenshot` URL resolves to a real PNG under the BE public and is reachable over HTTP; `trace` URL likewise.
- [ ] Final state still exact (parsed from last-run.json). Existing gates/single-flight/timeout intact.
- [ ] Pest still green (E2eRunner 7 pass); no regression. Reporter registered without breaking the normal run (`playwright test` still green).

## Out of scope
FE display (WP3b). Do NOT change the WP2 filter or WP4 parser fields (extend, don't break).

## Flags
No [FIN]. No migration (reuse results_json; add fields inside the JSON). Touches the runner's process handling — native review for the streaming loop + artifact copy safety.
