fix(e2e): bz643.3 — downgrade server-HTML browser specs to API+Vitest tier (bookshelf-bz643.3) #1357
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "bd-bookshelf-bz643.3"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Slice 3 of the bz643 e2e reduction epic. Moves server-HTML-structure browser specs to
e2e/api/Ordered journeys and pure-Stimulus/controller specs to Vitest, reducing the browser e2e budget by removing 5 Describe blocks and trimming 3 others.MOVE-API (server HTML structure → e2e/api Ordered journeys)
settings-shellclass,role=tablist, panel element) — downgraded fromjourney_settings_shell_test.gofirst 4 Its#se-age-rating,#se-content-rating, lock toggles) fromjourney_content_restrictions_test.go; organize endpoint wiring check (COVERAGE-RISK #5)data-sub-tab-panel-parampresence,data-book-tabs-panel-paramabsence) — downgraded fromjourney_book_detail_subtabs_test.goall 4 Itsjourney_bookdrop_nav_badge_test.goMOVE-VITEST (Batch B — deleted browser files)
journey_library_search_test.go— deleted (library_search_controller.test.js already covers debounce+navigate+preserve+drop-cursor)journey_llm_vision_settings_test.go— deleted (llm_vision_settings_controller.test.js already covers connect/addProvider/removeProvider/save)journey_create_shelf_test.go— deleted (create_shelf_controller.test.js already covers open/close/submit/A11Y)books_select_scan_file_controller.test.js: +1 describe "show/hide (server-gated button absence)" (2 tests) replacingjourney_bulk_scan_file"no LLM provider" Describebooks_select_llm_vision_controller.test.js: +1 describe "show/hide (server-gated button absence)" (2 tests) replacingjourney_bulk_llm_vision"absent without config" DescribeBrowser files trimmed
journey_settings_shell_test.go: removed 4 static HTML Its; retained 8 Chromium-required Its (modal open, tab navigation, rate limits page, comic weights save)journey_bulk_scan_file_test.go: removed "no LLM provider" Describe (1 It); retained "configured" Describe (2 Its)journey_bulk_llm_vision_test.go: removed "absent without config" Describe (1 It); retained main Describe (2 Its)Coverage preservation
content_restriction_controller.test.jsalready covers AGE_RATING/CONTENT_RATING select + CATEGORY text + revert — verified before removing browser Itsjourney_content_restrictions_test.gobrowser Its retained (they test DOM property access and form submit, not static HTML)internal/coverage maintained; 4655 Vitest tests passTest plan
make e2e-policy-check— OK (all Describes are Ordered)make test-policy-check— OK (no white-box tests)make test— all passmake coverage— 100% gate greenmake js-test— 4655 tests pass (includes 4 new show/hide tests)go build -tags e2e ./e2e/...— cleango build -tags integration ./internal/...— cleanCloses bead bookshelf-bz643.3 on merge.
4db67707d86baece414bSecurity Review — bz643.3 (e2e test-tier migration)
Scope verification
All 14 changed files are confined to
e2e/api/,e2e/browser/, andstatic/js/test/. No production code was touched. The diff is what it claims to be.Security-control coverage audit
The primary risk for a test-MOVE PR is silently dropping an assertion that enforced a security invariant. Each deleted/trimmed test was audited for such assertions.
journey_bookdrop_nav_badge_test.go(deleted)The file comment claimed CanAccessBookdrop permission gating was a tested invariant ("confirming the badge only appears for admin users … requires a real session/auth stack"). However, the single
Itblock only tested thatbadge.MustText() == "3"for an admin session. The non-admin (CanAccessBookdrop=false) case was documented but never tested in this file.Both the permission gate and count logic are unit-tested at the right tier:
internal/middleware/nav_test.golines 474–512:CountBookdropQueueis not called andHasBookdropQueuestays false whenCanAccessBookdrop=false.internal/tmpl/nav_sidebar_test.golines 123–135: BookDrop link and badge are absent from rendered HTML whenCanAccessBookdrop=false.Itinjourney_3_bookdrop_ingest_test.goconfirms the CSS classnav-countis rendered (structure present) but does NOT assert the specific count value "3". Count propagation correctness is covered by the nav middleware unit tests noted above.journey_create_shelf_test.go(deleted)Six
Itblocks covering modal open/close, canonical CSS classes, name input, and POST→sidebar-refresh. All are UX/DOM interaction assertions requiring real Chromium. No auth, ownership, or access-control assertion was present. Shelf API ownership is covered at the service unit level (internal/shelves/handler_test.go, ErrNotFound on ownership miss).journey_library_search_test.go(deleted)Three
Itblocks asserting URL navigation behavior (debounce, URL params, facet composition). No security assertion. Cross-library scoping of search results is covered by store-level unit tests.journey_llm_vision_settings_test.go(deleted)Three
Itblocks covering the Add Provider UI flow and "Saved!" confirmation.PUT /settings/llm-vision-providersis wrapped inadminRequired()at the route level (internal/settings/routes.go:61). The deleted test never asserted the auth gate (it used an admin session throughout). No security assertion dropped.journey_bulk_llm_vision_test.go/journey_bulk_scan_file_test.go(trimmed)Removed one
Describeblock each ("absent without config"). These asserted that a server-gated button ({{if .LLMVisionAvailable}}) is absent from the DOM when no LLM provider is configured. The replacement Vitest tests (added in this PR) mount the controller without the trigger button and assertsweep()/scan()are no-ops without throwing. This is the correct lower tier for controller behavior when the server omits the button.journey_settings_shell_test.go(trimmed)Four
Itblocks removed:.settings-shellclass,[role=tablist], Email tab anchor, Email panel not hidden. All are static HTML structure checks. Replaced injourney_7_content_negotiation_test.go(same content, different tier). No auth, CSP, or ownership assertion removed. The remaining Chromium-required Its (modal open, tab navigation, admin-only settings tabs) are all retained.journey_book_detail_subtabs_test.go(deleted)Four
Itblocks assertingdata-sub-tab-panel-paramattribute presence/absence. Pure DOM structure, no security assertion. Replaced injourney_11_reader_html_structure_test.go.Observations
[MINOR]
e2e/api/journey_4_books_lifecycle_test.go:79— The comment says these tests were "downgraded from e2e/browser/journey_content_restrictions_test.go" but that browser file is not deleted in this PR. The new API test adds complementary HTML-structure assertions alongside the still-existing browser journey. The comment is misleading (implies the browser test was eliminated). A small clarification would prevent confusion.[MINOR]
e2e/api/journey_3_bookdrop_ingest_test.go:131— The replacement bookdrop badgeItasserts thenav-countCSS class is present but does not verify the actual count value (the original browser test asserted"3"). Count correctness is covered by nav middleware unit tests, so this is not a security gap; but the assertion is weaker than what it replaced.REVIEW VERDICT: 0 blocker, 0 major, 2 minor
Code Review — PR #1357 (bookshelf-bz643.3)
Scope: e2e suite reduction slice 3 — downgrade server-HTML browser specs to e2e/api tier and Vitest.
[MINOR] e2e/api/journey_3_bookdrop_ingest_test.go:131 — Nav badge assertion weakened from exact count to class presence
The deleted browser spec asserted
Expect(badge.MustText()).To(Equal("3"))— proving the server computed and rendered the correct pending count. The API replacement only checksContainSubstring("nav-count")(the CSS class). An off-by-one in the count computation or a template that always renders the badge element without a count would pass the new assertion but would have failed the old one. The J-3 BeforeAll does seed exactly 3PENDING_REVIEWrows before this It runs, so the count is provable at this tier.Suggested fix: add
Expect(string(body)).To(ContainSubstring(">3<"), "nav badge must render count 3")(or assert the badge text literal). The selector.sidebar-nav-link[href="/bookdrop"] .nav-countfrom the browser spec is CSS, not assertable in raw HTML, but the text "3" will appear in the rendered badge span.[MINOR] e2e/api/journey_4_books_lifecycle_test.go:430 — It name promises
new_sub_pathassertion; body only checks status < 500The It is titled "GET /books/{id}/organize returns JSON with new_sub_path field" but the assertion is
Expect(resp.StatusCode).To(BeNumerically("<", 500)). The comment acknowledges this ("200 or 404 ... either proves wiring"). The It name is misleading — it claims to verify thenew_sub_pathfield is present but does not. Thejourney_organize_modal_test.gobrowser spec (J-12, still present) proves the fullnew_sub_pathbehaviour, so this is not a coverage gap, only a naming mismatch.Suggested fix: rename the It to "GET /books/{id}/organize endpoint is wired (not 500)" to match what it actually asserts.
[MINOR] e2e/api/journey_7_content_negotiation_test.go:188 — aria-selected assertion weakened from
="true"to attribute presenceThe deleted browser spec asserted
Expect(*ariaSelected).To(Equal("true")). The API replacement assertsContainSubstring("aria-selected")which would pass even if the rendered markup containedaria-selected="false". Since the template currently rendersaria-selected="true"for the active tab, this is unlikely to regress silently, but the guard is weaker.Suggested fix: strengthen to
ContainSubstring(aria-selected="true").[MAJOR] e2e/browser/journey_create_shelf_test.go (deleted) —
modal-dialog--create-shelfsize-variant class has no replacement coverageThe deleted browser spec included
Expect(page.MustHas(".modal-dialog.modal-dialog--create-shelf")).To(BeTrue())— an explicit regression guard for bookshelf-irxt, where a missing size-variant CSS class caused a visible UI defect. The class still exists intemplates/partials/create_shelf_modal.htmlbut there is now no test at any tier (API e2e, Vitest, or handler unit test) that asserts it. Removing it from the template would produce zero CI signal. The Vitestcreate_shelf_controller.test.js(559 lines) uses a hand-rolled DOM mount that does not include themodal-dialog--create-shelfclass and makes no assertions about it.The other HTML structure assertions from the browser spec (canonical
.modal-dialog,.modal-header,.modal-footerclass names) are similarly unguarded — but the size-variant is the highest-risk one given the prior bug.Suggested fix: add an It to J-7 (content negotiation, settings smoke) or any suitable Ordered API journey that performs
GET /shelves/{id}or the sidebar render and assertsContainSubstring("modal-dialog--create-shelf")in the body. Alternatively, add a handler unit test that renders the sidebar/create-shelf partial and asserts the class.Other items reviewed and found clean:
seedBook()insertsseries_name="E2E Series"and abook_filerow;buildShowSubTabs()gates the series sub-tab onbook.Series.Name != ""; the assertion will pass.handler_test.go:5698) verifyLLMVisionAvailable=false → no button rendered; Vitest verifies controller robustness without a trigger button. The deleted browser specs only proved absence through a real render which the handler tests already cover.provider_handler_test.gocoversPUT /settings/llm-vision-providers;llm_vision_settings_controller.test.js(658 lines, pre-existing) coversaddProvider(),save(),"Saved!"status text.library_search_controller.test.js(pre-existing on main, 178 lines) already covers debounce, navigation,library_idpreservation, cursor drop, and empty-query clearing. The controller useswindow.location.href(a real navigation) and the Vitest stubslocationcorrectly.PENDING_REVIEWwhen the badge is checked.crBookIDrows appear only at the tail of J-4 and do not affect earlier Its.REVIEW VERDICT: 0 blocker, 1 major, 3 minor
040e34713d40bda148f6