Skip to content

fix(e2e): the Organisations index filtered on values #520 deleted, and the SBOM provenance line read a name that no longer existed - #536

Merged
rubenvdlinde merged 3 commits into
developmentfrom
S48/e2e-softwarecatalog
Aug 16, 2026
Merged

fix(e2e): the Organisations index filtered on values #520 deleted, and the SBOM provenance line read a name that no longer existed#536
rubenvdlinde merged 3 commits into
developmentfrom
S48/e2e-softwarecatalog

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

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"]. refactor(softwarecatalog): translate the stored enum values, and migrate them #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-refactor(softwarecatalog): translate eight schema slugs, and migrate them #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 refactor(softwarecatalog): translate the stored enum values, and migrate them #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 refactor(softwarecatalog): translate the stored enum values, and migrate them #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.

…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.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/softwarecatalog @ 7a22545

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.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/softwarecatalog @ b07e758

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.

@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/softwarecatalog @ 7993a3e

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
rubenvdlinde merged commit 24c2fa1 into development Aug 16, 2026
41 of 44 checks passed
@rubenvdlinde
rubenvdlinde deleted the S48/e2e-softwarecatalog branch August 16, 2026 23:44
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant