fix(e2e): the Organisations index filtered on values #520 deleted, and the SBOM provenance line read a name that no longer existed - #536
Merged
Conversation
…deleted, and the SBOM provenance line read a name that no longer existed Five of the six E2E failures on `development` came from two renames that moved one half of a pair and left the other behind. Neither raised an error, which is why both survived: one produced an empty list, the other produced an element that never rendered. 1. THE ORGANISATIONS INDEX WAS EMPTY FOR EVERY USER. `src/manifest.json`'s Organisaties page filtered on `status: ["Concept", "Actief", "Deactief"]`. #520 translated that enum to Draft/Active/Inactive/merged and migrated the stored rows, but not this filter — so the page filtered on three values no row can hold. OpenRegister answers such a filter `200 {"total": 0}`, so the index rendered "No items found" and read as an empty catalogue: no console error, no failed request, nothing in the log. Measured on a running instance, positive and negative control: ?status[]=Draft&status[]=Active -> total 1 (the seeded row) ?status[]=Concept&status[]=Actief&... -> total 0 and reproduced in the browser: one organisation exists, the index shows "No items found". The same commit missed `organization.status`'s `default` ("Concept", not a member of its own enum, so every newly created organisation lands outside this filter) and the whole `x-openregister-lifecycle` block, whose `initial`, `final` and every `from`/`to` still named the Dutch values — a lifecycle whose transitions match no row simply offers nothing. Both are fixed here, with the schema version bumped: a deployed version >= the declared one makes the import SKIP, and OpenRegister's schemaContentDiffers() escape hatch compares only properties/required/ authorization — never `configuration` — so a lifecycle-only edit would never have deployed. 2. THE SBOM PROVENANCE LINE COULD NEVER RENDER. `SbomComponentsPanel`'s producer computed is `moduleVersie()`; when the schema slug was translated the CONSUMER was renamed to `this.moduleVersion` and the producer was not. Vue resolves the unknown property to `undefined`, `moduleVersionData` returned `{}`, and every derived value went empty: `lastImportedLabel` returned '' so the `v-if`-gated `data-testid="sbom-provenance"` never mounted, and `parentModuleId` returned '' so the vulnerability-match heuristic ran with an empty scope. "No import yet" is a legitimate state, so the broken build was indistinguishable from an unimported module version. 3. Three e2e tests asserted a surface the product stopped rendering. Organisations was decomposed from a bespoke `type: custom` OrganisatieIndexView to a standard `type: index` page; the tests still looked for that view's "Add organisation" button and its "No organisations" empty state. The empty- state assertion was the worse half: `toHaveCount(0)` against a string nothing renders passes unconditionally, so the guard meant to catch an empty list said nothing while the list really was empty. They now assert the CnIndexPage surface — heading, Cards/Table toggle, create action, list body — which is strictly more than before. 4. `gemma-faceted-search` still named the pre-#518 Dutch slug `dienst` in three places: the message assertion, the 200 control, and a `supportedSchemas.sort()` compared against an UNSORTED literal, which could not have held for any naming. 5. `index-pages`' "index standards" test.fixme claimed "blocked: missing `standaard` schema". The page is bound to `"schema": "element"`, which the CI seed enumerates among the 36 schemas present, and a running instance renders the index with an "Add Element" action and no app-origin error. A skip whose reason has stopped being true reads exactly like a passing test, so it is put back to work rather than re-worded. BEFORE / AFTER (local, same command both sides) tests/vitest/sbomProvenanceLabel.spec.js (new, 4 tests) on HEAD: 2 failed / 2 passed (both failures are the defect; both passes are the negative controls, so the assertions discriminate) after: 4 passed / 0 failed tests/vitest/manifestFilterEnumParity.spec.js (new, 3 tests) on HEAD: 1 failed / 2 passed — reporting exactly the three stale filter values, with its positive control passing on both sides after: 3 passed / 0 failed full vitest suite, run from `git archive HEAD` with the same node_modules: HEAD: 22 files, 21 passed / 1 failed, 226 tests passed branch: 23 files, 22 passed / 1 failed, 230 tests passed The one failing file is `adminApi.spec.js` (`ReferenceError: window is not defined`); it fails identically on pristine HEAD and this change does not touch it or anything it imports. eslint on the changed component: clean. `node tests/validate-manifest.js`: PASS (0 errors), 29 pages, schema 2.22.0. FILES MEASURED: 11 changed (2 config/JSON, 1 component, 5 e2e specs + 1 e2e helper, 2 new vitest specs + 1 stub). NOT DONE, DELIBERATELY — recorded on the fleet board: - Five more schemas carry the same #520 miss: `usage.status` default 'In productie', `connection.status` and `moduleVersion.status` default 'in gebruik', `module.type` default 'Applicatie', `connection.integrationType` default template emitting 'extern'/'intern' — every one outside its own enum — plus four more Dutch `x-openregister-lifecycle` blocks (usage 'Verwerving', contract 'In onderhandeling', connection and moduleVersion 'in ontwikkeling'). They are the same class of bug on surfaces this change does not measure, so they belong to whoever owns #520 rather than to an E2E repair. - The schema title is authored "Organization" while every other string in the app is British, and a deployed instance can still serve the older "Organisation" because a title change never redeploys. Rather than rename a schema title from an E2E fix, the affected assertions accept either spelling of that one word. - `organisatie-crud`'s UI-create test.fixme is NOT re-enabled. Its stated reason (an ObjectModal Catalogus cascade) describes a removed surface, so the reason is corrected to "unverified" rather than restated — the body has never been re-authored against the dialog that replaced it, and guessing which fields that dialog exposes is exactly the kind of assertion that passes without testing anything.
Contributor
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ❌ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 704/704 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-16 23:21 UTC
Download the full PDF report from the workflow artifacts.
Un-skipping `index standards` exposed a real defect, and this records what it is so the next reader does not re-derive it — and so nobody "fixes" it the wrong way. The surface assertions pass (chrome, "Add Element", list body). The failure is `expectNoAppErrors`: `Error fetching 14-element collection`. The page config is `register: "@resolve:voorzieningen_register"` + `schema: "element"`, but `element` is bound to the OTHER register declared in the same register file — `components.registers.vng-gemma.schemas`, not `.voorzieningen.schemas`. Same family as openconnector#1275's `synchronization_run`: declaring a schema does not attach it, and only an attached schema is fetchable.⚠️ Adding `element` to the voorzieningen register would make the request succeed and return NOTHING, because objects live per register and the GEMMA elements were imported under vng-gemma — a visible error turned into an empty list, which is an invisible pass and worse than the red. The honest fix needs a second `@resolve:` sentinel for the gemma register. `voorzieningen_register` is currently the only one (34 uses), provisioned in Application.php::boot() from the `voorzieningen_config` blob; no app-config key holds a vng-gemma register id, and tests/e2e/ci-seed.sh does not provision that register at all. Where that id lives is a config-ownership decision, so it is escalated on the board rather than guessed. No behaviour change: comment only.
`quality / Frontend Check (format)` was the one check this branch INTRODUCED
against `development` — prettier disagreed with two of my line breaks. Fixed by
running the repo's own `prettier --write` on exactly those two files, plus the
two eslint errors on files this branch added:
- perfectionist/sort-imports — the register import must precede the manifest
import in the new manifest/enum parity spec;
- prefer-object-has-own — `Object.hasOwn()` in the l10n stub.
Re-verified after the change: `prettier --check "**/*.{js,ts,vue,css,scss}"`
reports "All matched files use Prettier code style!", eslint on the four
touched/added files is silent, and both new vitest specs still pass 7/7 —
including the manifest guard's positive control, so the reformat did not turn
the instrument off.
Contributor
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ❌ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 704/704 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ❌ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-16 23:25 UTC
Download the full PDF report from the workflow artifacts.
Contributor
Quality Report — ConductionNL/softwarecatalog @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-vue-demi | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| composer | ✅ | ✅ 130/130 | |||
| npm | ✅ | ✅ 704/704 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-16 23:42 UTC
Download the full PDF report from the workflow artifacts.
rubenvdlinde
added a commit
that referenced
this pull request
Aug 16, 2026
…ve defaults it did not reach #536 landed the same two diagnoses independently: the Organisations index filtering on values #520 translated away, and the SBOM provenance computed whose declaration and reader disagreed. Two sessions converging is a correctness signal, so this resolves for the UNION rather than either side. TOOK THEIRS, because each is strictly stronger: - `SbomComponentsPanel.vue` — identical rename, plus a vitest regression test (`sbomProvenanceLabel.spec.js`) that fails if the producer/consumer pair drifts again. Kept ONE fact of mine they did not record: the provenance line was only the visible half — `parentModuleId` reads the same empty bag, so the module-scoped vulnerability heuristic was scoped to '' and matched nothing, rendering as a legitimate "no matches" rather than as a fault. - `src/manifest.json` `_note` — theirs carries the live measurement (`?status[]=Draft&status[]=Active` returns the seeded row; `?status[]=Concept&...` returns total=0). Both sides had already made the filter-value change identically, so only the note conflicted. - `dashboard.spec.ts` / `index-pages.spec.ts` / `organisatie-crud.spec.ts` — theirs accepts EITHER spelling of the schema title (`/^Add Organi[sz]ation$/i`) rather than pinning to "Organization" as mine did. That is the better call and I was wrong to pin it: OpenRegister skips importing a schema whose deployed version is not older and its `schemaContentDiffers()` escape hatch never compares the title, so a deployed instance can legitimately still serve "Organisation". Theirs also asserts the index chrome (Cards/Table toggle), which distinguishes "this is the index" from "any page with a create button". - `gemma-faceted-search.spec.ts` — theirs copies before sorting (`[...(body?.supportedSchemas ?? [])].sort()`), so the assertion does not mutate the response body. Mine sorted in place. Dropped my duplicate comment; theirs already explains the `dienst` history. KEPT MINE, because #536 does not contain it: - **Five of the six schema `default`s that sit outside their own enum.** #536 fixed `organization.status` only. `usage.status` ('In productie'), `connection.status` ('in gebruik'), `connection.integrationType` (a template emitting extern/intern), `module.type` ('Applicatie') and `moduleVersion.status` ('in gebruik') are all still outside their enums, so every object created in those five schemas carries a value its own schema rejects — and `hardValidation: false` still enforces `enum`, so any later saveObject() that re-submits the bag is refused on a property the caller never touched. Their four version bumps came with it; #536's covers organization. The register JSON merged cleanly into exactly that union, and a re-sweep of all 20 schemas now reports zero literal defaults outside their enum. - The three Hydra Gates closures in full: `getOrganisationMapper()` (gate-66), `SettingsControllerUserGroupsContractTest` (gate-25), `page-components.ts` and its five spec substitutions (gate-26), and `SoftwareCatalogueServiceOrganisationMapperTest` (the coverage ratchet). #536 touches none of these. Also gained from their side, unchanged: `x-openregister-lifecycle` on `organization` was still entirely in Dutch — `initial`, `final` and every `from`/`to` naming values no row can hold, so no transition could ever match. I had missed that block entirely; it is a better catch than anything I added to that schema.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Five of the six E2E failures on
developmentcame from two renames that movedone half of a pair and left the other behind. Neither raised an error, which is
why both survived: one produced an empty list, the other produced an element
that never rendered.
THE ORGANISATIONS INDEX WAS EMPTY FOR EVERY USER.
src/manifest.json's Organisaties page filtered onstatus: ["Concept", "Actief", "Deactief"]. refactor(softwarecatalog): translate the stored enum values, and migrate them #520 translated that enum toDraft/Active/Inactive/merged and migrated the stored rows, but not this
filter — so the page filtered on three values no row can hold. OpenRegister
answers such a filter
200 {"total": 0}, so the index rendered"No items found" and read as an empty catalogue: no console error, no failed
request, nothing in the log.
Measured on a running instance, positive and negative control:
?status[]=Draft&status[]=Active -> total 1 (the seeded row)
?status[]=Concept&status[]=Actief&... -> total 0
and reproduced in the browser: one organisation exists, the index shows
"No items found".
The same commit missed
organization.status'sdefault("Concept", not amember of its own enum, so every newly created organisation lands outside
this filter) and the whole
x-openregister-lifecycleblock, whoseinitial,finaland everyfrom/tostill named the Dutch values — alifecycle whose transitions match no row simply offers nothing. Both are
fixed here, with the schema version bumped: a deployed version >= the
declared one makes the import SKIP, and OpenRegister's
schemaContentDiffers() escape hatch compares only properties/required/
authorization — never
configuration— so a lifecycle-only edit would neverhave deployed.
THE SBOM PROVENANCE LINE COULD NEVER RENDER.
SbomComponentsPanel's producer computed ismoduleVersie(); when theschema slug was translated the CONSUMER was renamed to
this.moduleVersionand the producer was not. Vue resolves the unknown property to
undefined,moduleVersionDatareturned{}, and every derived value went empty:lastImportedLabelreturned '' so thev-if-gateddata-testid="sbom-provenance"never mounted, andparentModuleIdreturned'' so the vulnerability-match heuristic ran with an empty scope. "No import
yet" is a legitimate state, so the broken build was indistinguishable from an
unimported module version.
Three e2e tests asserted a surface the product stopped rendering. Organisations
was decomposed from a bespoke
type: customOrganisatieIndexView to astandard
type: indexpage; the tests still looked for that view's"Add organisation" button and its "No organisations" empty state. The empty-
state assertion was the worse half:
toHaveCount(0)against a string nothingrenders passes unconditionally, so the guard meant to catch an empty list said
nothing while the list really was empty. They now assert the CnIndexPage
surface — heading, Cards/Table toggle, create action, list body — which is
strictly more than before.
gemma-faceted-searchstill named the pre-refactor(softwarecatalog): translate eight schema slugs, and migrate them #518 Dutch slugdienstin threeplaces: the message assertion, the 200 control, and a
supportedSchemas.sort()compared against an UNSORTED literal, which couldnot have held for any naming.
index-pages' "index standards" test.fixme claimed "blocked: missingstandaardschema". The page is bound to"schema": "element", which the CIseed enumerates among the 36 schemas present, and a running instance renders
the index with an "Add Element" action and no app-origin error. A skip whose
reason has stopped being true reads exactly like a passing test, so it is put
back to work rather than re-worded.
BEFORE / AFTER (local, same command both sides)
tests/vitest/sbomProvenanceLabel.spec.js (new, 4 tests)
on HEAD: 2 failed / 2 passed (both failures are the defect; both passes
are the negative controls, so the
assertions discriminate)
after: 4 passed / 0 failed
tests/vitest/manifestFilterEnumParity.spec.js (new, 3 tests)
on HEAD: 1 failed / 2 passed — reporting exactly the three stale filter
values, with its positive control passing on both sides
after: 3 passed / 0 failed
full vitest suite, run from
git archive HEADwith the same node_modules:HEAD: 22 files, 21 passed / 1 failed, 226 tests passed
branch: 23 files, 22 passed / 1 failed, 230 tests passed
The one failing file is
adminApi.spec.js(ReferenceError: window is not defined); it fails identically on pristine HEAD and this change does nottouch it or anything it imports.
eslint on the changed component: clean.
node tests/validate-manifest.js: PASS (0 errors), 29 pages, schema 2.22.0.FILES MEASURED: 11 changed (2 config/JSON, 1 component, 5 e2e specs + 1 e2e
helper, 2 new vitest specs + 1 stub).
NOT DONE, DELIBERATELY — recorded on the fleet board:
usage.statusdefault'In productie',
connection.statusandmoduleVersion.statusdefault'in gebruik',
module.typedefault 'Applicatie',connection.integrationTypedefault template emitting 'extern'/'intern' —every one outside its own enum — plus four more Dutch
x-openregister-lifecycleblocks (usage 'Verwerving', contract'In onderhandeling', connection and moduleVersion 'in ontwikkeling'). They
are the same class of bug on surfaces this change does not measure, so they
belong to whoever owns refactor(softwarecatalog): translate the stored enum values, and migrate them #520 rather than to an E2E repair.
app is British, and a deployed instance can still serve the older
"Organisation" because a title change never redeploys. Rather than rename a
schema title from an E2E fix, the affected assertions accept either spelling
of that one word.
organisatie-crud's UI-create test.fixme is NOT re-enabled. Its statedreason (an ObjectModal Catalogus cascade) describes a removed surface, so
the reason is corrected to "unverified" rather than restated — the body has
never been re-authored against the dialog that replaced it, and guessing
which fields that dialog exposes is exactly the kind of assertion that
passes without testing anything.