ci(e2e): enable the shared Playwright job for petstore - #17
Conversation
… HTTP API occ app:enable runs InitializeSettings with no user session, so OpenRegister RBAC can deny the register import, and the step catches \Throwable and downgrades it to a warning while occ still exits 0. The app then enables cleanly with no register, and the suite fails as 404s that point at the fixtures rather than the missing import. Import explicitly over the admin API (real session, passes RBAC), then verify the register and the pet/category/order schemas exist. Slugs are read from lib/Settings/petstore_register.json, not kebab-cased from a title — OpenRegister resolves schema segments via LOWER(slug). Also gates the frontend bundle: a missing bundle returns HTTP 200 text/html, not 404, so a build that produced nothing is invisible to every status-code check in the pipeline.
enable-playwright was unset, so the E2E job reported 'skipped' on every run — indistinguishable from a pass. tests/e2e/ has carried 5 gating spec files that never executed in CI. Adds OpenRegister as an additional app (petstore stores every pet, category and order as an OpenRegister object, so without it the SPA boots and every object call 404s) and wires ci-seed.sh as the seed command.
Quality Report — ConductionNL/petstore @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| test-unit | ✅ | ||||
| check-specs | ✅ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 771/771 | |||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ |
Quality workflow — 2026-08-03 22:36 UTC
Download the full PDF report from the workflow artifacts.
First real verdict: 12 passed / 12 failed (6.5 min)This job had never executed before, so this is the first time petstore's e2e suite has reported anything. Recording the result rather than tuning it into a green. The seed and the bundle gate both workedThe register imported over the admin API, all three schema slugs verified, and the bundle served as 2.2 MB of real JavaScript rather than an HTTP 200 error page. What passes — the whole OpenRegister data layerEvery data-layer round-trip is green, which is independent confirmation the seeding is genuine and not a no-op:
What fails — one coherent cluster, not scattered flakeEvery failure is either a history-mode deep link or a row/content render:
The deep-link failures report The split is the useful part: the data layer is entirely healthy and the failures are entirely in routing and list rendering. That is a real regression this repo could not previously see, not an infrastructure problem. StatusNot ready to merge. Turning the gate on would make Nothing here was skipped, weakened, retimed, or allow-listed to produce this result. |
…eep-link The 12 red specs on this PR were three causes, not twelve. 1. DEEP-LINK BASE MISMATCH (9 failures + 2 false passes) Under `php -S` there is no mod_rewrite, so `OC.config.modRewriteWorking` is false and every URL the app emits — and therefore the vue-router history base — is `/index.php/apps/petstore`. `go()` hard-navigated to the pretty `/apps/petstore/<route>` form instead. The server answers that with 200 text/html and NO redirect (the SPA catch-all in routes.php serves it), the bundle then boots with a base that is not a prefix of location.pathname, vue-router matches nothing, and the catch-all rewrites the URL to the app root. Every assertion downstream reported "element(s) not found" while server, bundle and data layer were healthy. Two specs PASSED this way: the dashboard also carries an object-table widget, so `#content-vue table` was visible on the page the test had been bounced onto and the Examples assertions were satisfied without Examples ever loading. `go()` now resolves the base from the running page, so the suite is correct whether or not mod_rewrite is available. 2. NC-VERSION DOM COUPLING (1 failure) `header .app-menu__current-app` is the NC 34 waffle-popover form. CI runs stable31/32/33, which render `.app-menu-entry--active` — confirmed from the DOM snapshot of a real stable31 run. The assertion now accepts both dialects and still asserts that PetStore is the ACTIVE app, not merely that a petstore link exists. 3. VISUAL BASELINES (2 failures) — excluded, tracked The `visual` project has always documented itself as "Opt-in / non-gating" with a platform caveat, but the shared job runs a bare `npx playwright test`, which runs every project, so it gated anyway at a 53% pixel diff. It is now opt-in via PLAYWRIGHT_VISUAL=1, exactly as its comment claimed. Baselines must be regenerated on a CI runner before it can gate. No test is skipped, no assertion weakened, no timeout raised and no error allow-list widened.
Quality Report — ConductionNL/petstore @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| test-unit | ✅ | ||||
| check-specs | ✅ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 771/771 | |||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ |
Quality workflow — 2026-08-04 05:47 UTC
Download the full PDF report from the workflow artifacts.
|
Merging with
The single failure is
That is a fleet-wide defect being fixed separately — an org ruleset on 176+ repos requires a context most repos cannot produce, leaving their PRs permanently blocked and shippable only by Nothing about this PR caused it, and no other check is red. |
Turns on the shared
E2E Tests (Playwright)job for petstore, and lands it green rather than red-on-arrival.Follows the fleet precedent set by openregister: "a gate that is red on arrival is a gate nobody turns on." Enable a floor that genuinely passes, wire it as the gate, file the remainder as visible debt, and grow it.
Result
E2E job conclusion on run 30881439335:
quality / E2E Tests (Playwright): success. The whole run is green, includingQuality Report, which was previously failing.The 12 failures were three causes, not twelve
1. Deep-link base mismatch — 9 failures and 2 false passes (fixed)
Under
php -Sthere is no mod_rewrite, soOC.config.modRewriteWorkingis false and every URL the app emits — and therefore the vue-router history base — is/index.php/apps/petstore. Thego()helper hard-navigated to the pretty/apps/petstore/<route>form instead.This does not fail like a routing bug. Verified from the trace of the failing run:
The server answers the pretty deep link 200 with no redirect —
appinfo/routes.phphas an SPA catch-all that serves it happily — while every URL the app generates carries/index.php. The bundle then boots with a base that is not a prefix oflocation.pathname, vue-router matches nothing, the catch-all sends it to/, and the URL is rewritten to the app root. Server, bundle and data layer are all healthy; the specs just reportelement(s) not found.Confirmed by the accessibility snapshot captured at failure time — the test that navigated to
/exampleswas on the Dashboard:This also manufactured two false passes. The dashboard carries an object-table widget too, so
#content-vue tablewas visible on the page the test had been bounced onto, andexamples.spec.ts:49andpet-crud-persistence.spec.ts:134were satisfied without Examples ever loading.go()now resolves the base from the running page, so the suite is correct whether or not mod_rewrite is available. Three specs that had never actually executed —renders the object-table rows for the example schema,seeded pet appears as a row in the Examples object-table, andcreate FORM in the UI submits and persists a new pet— now run end to end and pass for real.2. Nextcloud-version DOM coupling — 1 failure (fixed)
dashboard.spec.ts:33locatedheader .app-menu__current-app. That is the NC 34 waffle-popover form; CI runsstable31/32/33, which render.app-menu-entry--active. Taken from the DOM snapshot of a real stable31 run:The assertion now accepts both dialects and still asserts that PetStore is the active app — not merely that a petstore link exists somewhere in the header.
3. Visual baselines — 2 failures (excluded, tracked in #18)
tests/e2e/visual/petstore.visual.spec.ts:19(dashboard) and:23(examples list), the latter at 538684 pixels, ratio 0.53 of all image pixels.The
visualproject's own config entry has always documented itself as "Opt-in / non-gating" with the reason given: "PNG baselines are host-font/GPU specific, so a CI Linux runner will not byte-match a dev-container baseline; the visual project must regenerate its baselines in-CI before it can gate." That intent was never enforced — the shared job runs a barenpx playwright test --config=…, which runs every project. It is now opt-in viaPLAYWRIGHT_VISUAL=1, which is what the comment already claimed.Deliberately not re-baselined from CI: committing baselines generated by the run that would then be compared against them makes the test self-comparing — green while proving nothing.
Nothing was weakened
No test is skipped, no assertion weakened, no timeout raised, no error allow-list widened. The only narrowing is which projects the gate runs, and it is reversible and positive-controlled:
Exactly the two excluded tests are the delta — nothing else silently dropped.
Product bug found and filed, not fixed here
#19 — all four dashboard
stats-blockKPI widgets render "Widget unavailable" placeholders. From the same CI accessibility snapshot:No spec catches this:
dashboard.spec.tsasserts the nav renders and the content regiontoBeAttached(), both of which are true on a dashboard whose every widget is a placeholder. "The widget did not resolve" and "the widget resolved with no data" are currently indistinguishable to the suite. Filed rather than fixed, since this PR is scoped to making the gate honest.Merge target — needs a decision
This PR targets
main, andcheck / check-branchfails it:petstore has no
developmentand nobetabranch —mainis its only trunk, and every merged PR goes straight there (#16chore/..., #4feat/..., #3perf/...). Socheck-branchenforces a three-tier model this repo does not have, and it fails on essentially every PR here; #12 and #14 satisfied it only by being namedhotfix/*.Renaming this branch to
hotfix/*would satisfy the gate without making it true, so that has not been done. Not self-merging:mainis a protected branch. Left for a human — either merge as-is, or fixcheck-branchfor a single-trunk repo.