test(e2e): four integration surfaces nothing ever opened, and one screen already covered - #455
test(e2e): four integration surfaces nothing ever opened, and one screen already covered#455rubenvdlinde wants to merge 6 commits into
Conversation
…een already covered gate-26 (visual-coverage) FAIL 5. The five screens split cleanly into two very different problems, and only one of them was a real gap. FOUR SCREENS GENUINELY HAD NO TEST MeetingIntegrations, DecisionIntegrations, AgendaItemIntegrations and MotionIntegrations are routed, manifest-declared pages whose whole job is to mount CnDetailPage with `sidebar.useRegistry: true` for one object type (ADR-019). `integration-registry.spec.ts` navigates to the meeting route, but every assertion it makes is about the shared registry sidebar — the counts and tab ids CnObjectSidebar renders. Not one touches the page component, so all four views could render an empty shell and that spec would stay green. tests/e2e/integration-surfaces.spec.ts drives all four and asserts their OWN markup: the body block each view declares and the back-link it wires to its detail route. The eight `data-testid`s it targets were verified to exist in the four .vue files first — a spec that selects on invented ids fails for a reason that has nothing to do with the screen. Read-only by construction: GETs and navigations only, nothing created or deleted. It drives the objects ci-seed.sh already provisions, and when they are absent it fails LOUDLY rather than skipping, because a surface that was never opened must not report as a pass. THE FIFTH WAS ALREADY COVERED — THE GATE JUST COULD NOT SEE IT `src/views/settings/UserSettings.vue` was reported as having no e2e test. It has one, and has had all along: `user-settings.spec.ts:289` navigates to `/apps/decidesk/user-settings` and asserts the component's own `user-settings-page` body block mounts. gate-26 matches a page component to its coverage on the literal token `\bUserSettings\b` — file stem or manifest page id. Every mention in that file was the kebab-case route `/user-settings`, so the token never appeared and a screen with a running browser test was counted as uncovered. The fix is to write the component name where the test already is. That is RECORDING AN EXISTING TRUTH, not waiving a gap: no `@visual exclude` is used anywhere in this change, and the note is pinned to the test so the two cannot drift apart. MEASURED (gate package ConductionNL/.github @ b8c7eade, full scope, rebased onto 012f96b so the base is the tree that actually exists): before 19=984 25=25 26=5 54=2 63=2 53 PASS, skips 24/29/33/47/48/61 after 19=984 25=25 54=2 63=2 54 PASS, skips 24/29/33/47/48/61 Skip set is byte-identical, so no failure was converted into a skip, and no other gate moved. Both directions proven: with integration-surfaces.spec.ts moved aside, gate-26 reports `FAIL — 4` on the same runner — the four Integrations views come back and UserSettings stays closed, which is what confirms the two remedies are independent and that neither is doing the other's work.
Quality Report — ConductionNL/decidesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 548/548 | |||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-11 09:43 UTC
Download the full PDF report from the workflow artifacts.
…, not their own view The e2e spec added in the previous commit failed on three of the four surfaces it drives, and it was right to. MeetingIntegrations, DecisionIntegrations and AgendaItemIntegrations never rendered their own components at all. ✓ MotionIntegrations renders its body and back-link ✘ MeetingIntegrations [data-testid="meeting-integrations"] not found ✘ DecisionIntegrations [data-testid="decision-integrations"] not found ✘ AgendaItemIntegrations [data-testid="agenda-item-integrations"] not found The one that passed is the only page declared `type: "custom"`. The three that failed were `type: "detail"`, and for a detail page CnPageRenderer mounts CnDetailPage generically — it never resolves a `component`. Two of them already DECLARED `"component": "..."`; the key was simply ignored because of the type. So three .vue files, each documenting itself as "routed from the manifest at /…/integrations", were dead code, and the routes quietly served a generic detail page instead of the intended surface. MeetingIntegrations had a second, independent reason it could not render: it was never imported into src/registry.js. Fixing only the type would have left it resolving to nothing. This is the same defect this repo already hit once. The registry comment beside MotionIntegrations records it: "src/manifest.json page 15 names this component on the route /motions/:id/integrations, but nothing registered it — resolution fell through and the page rendered NOTHING." That one was found and fixed; these three were not, because nothing opened them. gate-26 was not reporting missing documentation — it was pointing at screens no test had ever rendered, which is exactly the state in which this class of bug survives. Fix: the three pages become `type: "custom"` with a page-level `component`, matching MotionIntegrations, and MeetingIntegrations is imported and registered. All four surfaces are now declared identically. npm run check:manifest -> Ajv validation: PASS (0 errors), schema 2.13.0 eslint src/registry.js -> clean Gates, full scope, package @ b8c7eade: 19=984 25=25 54=2 63=2, 54 PASS, skips 24/29/33/47/48/61 — unchanged from the previous commit. gate-26 stays PASS; no gate moved in either direction. FALSIFIABLE CLAIM FOR CI: integration-surfaces goes 1 passed / 3 failed to 4 passed / 0 failed, so the suite goes 14 failed / 103 passed to 11 failed / 106 passed, with skips held at 59. integration-registry must not regress — it drives the same meeting route and asserts on the registry sidebar, which these views mount themselves via `sidebar.useRegistry: true`. If that spec moves, this change is wrong and I will say so.
My prediction was wrong, and the failure was realI claimed 11 failed / 106 passed / 59 skipped. CI returned 14 failed / 103 passed / 59 skipped — three of the four new tests failed. They were right to fail. The one that passed is the only page declared So three This repo has hit this exact defect before. The registry comment beside
Falsifiable claim, second attempt: integration-surfaces goes 1 passed / 3 failed to 4 passed / 0 failed, suite 11 failed / 106 passed / 59 skipped. I am not merging until CI says so. |
Quality Report — ConductionNL/decidesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 548/548 | |||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-11 10:21 UTC
Download the full PDF report from the workflow artifacts.
…ail page, not their own view" This reverts commit b186a65.
…eir filenames suggest Second correction on this branch, and the first one was mine. I read gate-26's four Integrations findings as "these .vue views never render", flipped three manifest pages from `type: "detail"` to `type: "custom"`, registered the missing MeetingIntegrations, and predicted 4/4 green. CI returned exactly the same 3 failures. That change is reverted in 01979f3 — it did nothing, because the reason was wrong. The discriminator is `config.widgets`, not `type`: MotionIntegrations type: "custom" widgets: 0 -> renders the .vue file MeetingIntegrations type: "detail" widgets: 4 -> renders the widget grid DecisionIntegrations type: "detail" widgets: 3 -> renders the widget grid AgendaItemIntegrations type: "detail" widgets: 3 -> renders the widget grid A page that declares `config.widgets` is composed declaratively: CnPageRenderer lays out the grid and the `.vue` file is never mounted. That holds for `custom` too, which is why flipping the type changed nothing. Three of these screens are not broken at all — they work, they are simply not served by the file they are named after. So the spec now declares, per surface, HOW it renders, and asserts that: - `renders: 'component'` (Motion) — the view's own body block, its explanatory copy, and its back-link. Unchanged. - `renders: 'widgets'` (the other three) — the page heading plus a non-empty declarative widget grid. `config.widgets` is non-empty for all three, so zero rendered widgets means the grid did not build, which is the real regression to catch here. Both arms assert something that can fail. A component testid on a widget page is a test that can never pass; "the route loaded" is a test that can never fail.⚠️ SEPARATE FINDING, NOT FIXED HERE: src/views/MeetingIntegrations.vue, DecisionIntegrations.vue and AgendaItemIntegrations.vue are dead code. Nothing mounts them — MeetingIntegrations is not even imported into src/registry.js, and each file's own docblock claims it is "routed from the manifest", which is no longer true. Deleting them is the honest resolution and would make gate-26's finding disappear for the right reason rather than because a spec names the files. I am not deleting three components on my own read; it is filed for a decision. Gates, full scope, package @ b8c7eade: 19=984 25=25 54=2 63=2, 54 PASS, skips 24/29/33/47/48/61 — gate-26 PASS, nothing else moved, skip set byte-identical. CLAIM FOR CI: all four integration-surfaces tests pass, suite 11 failed / 106 passed / 59 skipped. If a widget arm fails, that page's grid genuinely does not build and I want to know.
Second correction: my fix did nothing, and the reason was wrongCI returned 14 failed / 103 passed / 59 skipped again — the identical three failures. The discriminator is
A page declaring
Separate finding, deliberately not fixed here
Hydra Gates
Claim for this run: all four integration-surfaces tests pass; suite 11 failed / 106 passed / 59 skipped. |
Quality Report — ConductionNL/decidesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 548/548 | |||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-11 11:02 UTC
Download the full PDF report from the workflow artifacts.
… what their filenames suggest" This reverts commit 1d6fd72.
…e comment closing the other three
Reduces this spec from four surfaces to one, and takes gate-26 from a false PASS
to an honest FAIL 3.
WHY ONE
I was wrong about the other three routes twice, at a CI run each:
1. "those views never render" -> flipped three manifest pages from
`type: "detail"` to `type: "custom"` and registered a missing import.
CI: byte-identical failures. Reverted (01979f3).
2. "`config.widgets` is the discriminator, so assert the widget grid" -> CI:
still failed, and a page-heading assertion added along the way ALSO broke
the motion test, the only one that had been passing: 14 failed became 15.
Reverted (4b18227).
What CI has proven, twice, is narrow: the motion route mounts its own component
and its body-block and back-link testids are there. The other three do not
produce their testids and I do not know what they render instead. The local dev
container cannot answer it — its bundle predates development by weeks — and
another guess costs another 28-minute run and teaches nothing.
THE COMMENT WAS CLOSING THE FINDING
Cutting the spec down to one surface left gate-26 reporting PASS, which is what
made me look. It was not the test. gate-26 matches a page component to its
coverage on the bare PascalCase token anywhere in the e2e corpus, and this file's
header listed all four paths as documentation. That was enough:
with this file present: [gate-26] visual-coverage: PASS
with it moved aside: [gate-26] FAIL — 4
Three screens no test touches, reported as covered, by a comment. The names are
now written nowhere in this file and the gate reports FAIL 3, which is true.
That is worth stating plainly because it is the same defect shape as .github#343
on gate-19 (a file-level tag counted as coverage without checking any test): when
a gate matches on a TOKEN rather than on a test, prose can satisfy it. Anything
that closes a finding has to be checked with the remedy REMOVED, or the check is
measuring the wrong thing.
WHAT LANDS
- MotionIntegrations: a real Playwright test, read-only, failing loudly rather
than skipping when its seed data is absent.
- UserSettings: unchanged from the earlier commit — an existing running test
at user-settings.spec.ts:289 that the gate could not see because every
mention was the kebab-case route. Recording an existing truth.
gate-26: 5 -> 3. No `@visual exclude` anywhere in this change.
WHAT DOES NOT, AND WHY IT STAYS RED
MeetingIntegrations.vue, DecisionIntegrations.vue and AgendaItemIntegrations.vue
are uncovered and gate-26 will go on saying so. Nothing appears to mount them;
MeetingIntegrations is not imported into src/registry.js at all; each docblock
still claims it is "routed from the manifest". They need a decision — delete as
dead code, or establish what actually serves those routes — not a fourth guess
dressed up as a test.
Quality Report — ConductionNL/decidesk @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 548/548 | |||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-11 11:42 UTC
Download the full PDF report from the workflow artifacts.
gate-26 (visual-coverage) FAIL 5. The five screens split cleanly into two very
different problems, and only one of them was a real gap.
FOUR SCREENS GENUINELY HAD NO TEST
MeetingIntegrations, DecisionIntegrations, AgendaItemIntegrations and
MotionIntegrations are routed, manifest-declared pages whose whole job is to
mount CnDetailPage with
sidebar.useRegistry: truefor one object type(ADR-019).
integration-registry.spec.tsnavigates to the meeting route, butevery assertion it makes is about the shared registry sidebar — the counts and
tab ids CnObjectSidebar renders. Not one touches the page component, so all four
views could render an empty shell and that spec would stay green.
tests/e2e/integration-surfaces.spec.ts drives all four and asserts their OWN
markup: the body block each view declares and the back-link it wires to its
detail route. The eight
data-testids it targets were verified to exist in thefour .vue files first — a spec that selects on invented ids fails for a reason
that has nothing to do with the screen.
Read-only by construction: GETs and navigations only, nothing created or
deleted. It drives the objects ci-seed.sh already provisions, and when they are
absent it fails LOUDLY rather than skipping, because a surface that was never
opened must not report as a pass.
THE FIFTH WAS ALREADY COVERED — THE GATE JUST COULD NOT SEE IT
src/views/settings/UserSettings.vuewas reported as having no e2e test. It hasone, and has had all along:
user-settings.spec.ts:289navigates to/apps/decidesk/user-settingsand asserts the component's ownuser-settings-pagebody block mounts.gate-26 matches a page component to its coverage on the literal token
\bUserSettings\b— file stem or manifest page id. Every mention in that filewas the kebab-case route
/user-settings, so the token never appeared and ascreen with a running browser test was counted as uncovered. The fix is to write
the component name where the test already is. That is RECORDING AN EXISTING
TRUTH, not waiving a gap: no
@visual excludeis used anywhere in this change,and the note is pinned to the test so the two cannot drift apart.
MEASURED (gate package ConductionNL/.github @ b8c7eade, full scope, rebased onto
012f96b so the base is the tree that actually exists):
before 19=984 25=25 26=5 54=2 63=2 53 PASS, skips 24/29/33/47/48/61
after 19=984 25=25 54=2 63=2 54 PASS, skips 24/29/33/47/48/61
Skip set is byte-identical, so no failure was converted into a skip, and no
other gate moved.
Both directions proven: with integration-surfaces.spec.ts moved aside, gate-26
reports
FAIL — 4on the same runner — the four Integrations views come back andUserSettings stays closed, which is what confirms the two remedies are
independent and that neither is doing the other's work.
The claim this PR makes, falsifiably
It adds four CI-executed e2e tests. The e2e baseline on development is 11 failed / 102 passed / 59 skipped. If these four pass, that becomes 11 failed / 106 passed / 59 skipped. If any of them fails, the screen it drives is broken and I would rather know — but I am not entitled to call this green until the run says so.
Skips must stay at 59. A rise there means a test declined to run and the coverage is not real.
🤖 Generated with Claude Code