CLARIN-DSpace v9/Restore the FE-22 negative menu assertions - #1511
Conversation
The port of #1326 (card FE-22, merged as b0c48c5) dropped the source commit's negative assertion, and nothing covered the registration itself. The source asserted, in the Item describe block of dso-edit-menu.resolver.spec.ts: const searchEntry = menu.find(entry => entry.id === 'search-dso'); expect(searchEntry).toBeFalsy(); On v9 the resolver is gone, so the equivalent guard lives on app.menus.ts: ComColSearchMenuProvider is registered with onRoute(COMMUNITY_PAGE, COLLECTION_PAGE) and must never resolve a section on an item page. Adds src/app/app.menus.spec.ts with 5 tests: three on the registration data buildMenuStructure() produces, and two behavioural ones that drive the real MenuProviderService.resolveRouteMenus() with a community route and an item route. The item-page test carries a second, always-active provider so that it fails rather than passing vacuously if the resolution pipeline stops emitting. Tests only, no production code changed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Scope check — tests only
Why this is the right seamThe route gate the negative test exercises is the real one, // menu-provider.service.ts:158-160
if (!provider.shouldPersistOnRouteChange && isNotEmpty(provider.activePaths)) {
provider.activePaths.forEach((path: MenuRoute) => {
if (route.data.menuRoute === path) { shouldUpdate = true; }
});
}The spec drives that method rather than re-implementing the predicate, so the test cannot drift away from the production logic. The gap this closes, at git level
On v9 the resolver does not exist, so #1508 replaced the whole spec with Neither gap shows up in an Full negative-control outputA — delete the B — add The controls discriminate — exactly the two negative tests go red while the three registration tests stay green. C — remove the always-active control provider from the service's provider list (vacuity check):
All three controls were reverted; |
What
Adds
src/app/app.menus.spec.ts(5 tests, no production code) to close a coverage gap left by the FE-22 port of #1326, merged this morning asb0c48c578e(PR #1508).Why
The source commit
fecfa07e5dasserted the scoped-search entry in three places indso-edit-menu.resolver.spec.ts: it is present for a Community, present for a Collection, and — in the Itemdescribeblock, undershould not return Community/Collection-specific entries—On v9 the resolver does not exist, so that spec had no target and the port replaced it with
comcol-search.menu.spec.ts. That spec covers the provider well, but it only ever callsgetSectionsForContext(dso)directly. Two things it cannot see:onRoute(COMMUNITY_PAGE, COLLECTION_PAGE), so it must never resolve a section on an item page. Nothing asserted that;app.menus.tsat all. Deleting theComColSearchMenuProvider.onRoute(...)block left the whole existing suite green.Neither gap is visible to an
it()-count check: the port has 2 provider tests where the source had 2 resolver hunks, so it still reads 3 >= 3. The live probe in AC-FE-22-4 does cover the behaviour, but a refactor ofapp.menus.tscan silently re-break it between deploys.Changes
One new file,
src/app/app.menus.spec.ts.app.menus.tsis unchanged (git diff --statis a single added file, 166 insertions).buildMenuStructure()emits one resolvedMENU_PROVIDERentry per registered provider, and itsuseFactoryis what stampsmenuID,parentIDandactivePathsonto the instance. The spec finds the entry whosedeps[0]isComColSearchMenuProviderand runs that factory, so it asserts against the real registration rather than a restatement of it.menuID === MenuID.DSO_EDIT;parentIDis set, i.e. it is nested underDsoOptionMenuProviderand renders inside the options dropdown;activePathscontainsCOMMUNITY_PAGEandCOLLECTION_PAGEand notITEM_PAGE.MenuProviderServicewith a spyMenuService, andresolveRouteMenus()is driven with a community route and with an item route. On a community route exactly one section withmodel.link === '/search'is added, scoped to the community uuid. On an item route, none is.The item-page test carries a second, always-active provider whose only job is to emit a marker section.
resolveRouteMenusbuildscombineLatestover the providers that survive the route filter; with the search provider filtered out and nothing else in the list, thatcombineLatestnever emits,addSectionis never called, and a bareexpect(addSection).not.toHaveBeenCalled()would pass for the wrong reason — it would also pass if the pipeline threw. The marker assertion makes the test fail in that case instead. The two behavioural tests are wrapped infakeAsyncfor the same reason.Testing
Each test proven load-bearing by breaking what it guards:
Control A — delete the
ComColSearchMenuProvider.onRoute(...)block fromapp.menus.ts(the regression "nothing tests the registration" describes):Control B — add
MenuRoute.ITEM_PAGEto theonRoute(...)list, which is exactly the regression the source's dropped assertion guarded against:Precisely the two negative tests go red; the three registration tests stay green.
Control C — remove the always-active control provider from the service's provider list, to show the item-page test does not pass vacuously:
Regression over the FE-22 suite and the rest of the menu specs:
Follow-up to #1508 (card FE-22, source dtq-dev PR #1326).
🤖 Generated with Claude Code