Skip to content

fix: flaky anvil-cmg pagination e2e test — rewrite to test-id-based locators #4830

@frano-m

Description

@frano-m

Problem

The anvil-cmg pagination e2e spec (e2e/anvil/anvil-pagination.spec.ts) is the last anvil suite still using the legacy shared helpers in e2e/testFunctions.ts (testFirstPagePagination, filterAndTestLastPagePagination, testPaginationContent). These helpers are the same family that produced flakes in #4800, #4751, #4656, and #4828 — class-/id-based locators, generic waitForLoadState between interactions, and consolidated multi-assertion tests that obscure which step actually failed.

The current spec also bundles unrelated behaviour into three sweeping tests, so a single timing flake masks itself as a single test failure with little signal about the actual broken step.

Likely root cause (of the legacy spec's brittleness)

  1. Three monolithic tests cover ~8 distinct behaviours. A failure at any internal step is reported as "the whole test failed", and the next CI re-run may pass for a different reason than the original failure — making flakes hard to diagnose.
  2. Shared helpers in e2e/testFunctions.ts (testFirstPagePagination:525, filterAndTestLastPagePagination:558, testPaginationContent:684) use class-based locators and rely on implicit waits between clicks.
  3. test.fail() inside a conditional (current line 28) — flips the expected outcome based on runtime data; if the helper returns falsy for an unrelated reason, the test passes by failing, which masks real regressions.
  4. No content-change polling between pagination clicks — the legacy helpers either click and immediately assert, or use waitForLoadState("load") which doesn't track the post-fetch row swap.

Proposed fix

Rewrite e2e/anvil/anvil-pagination.spec.ts using two references:

  1. Prior rewrite attempt: fran/tests-pagination branch — keep its overall structure and the triggerActionAndWaitForUpdate content-poll pattern, which is the right primitive for pagination assertions.
  2. Code-style standard: e2e/anvil/anvil-filters.spec.ts — apply its layout, helper conventions, and locator discipline.

Keep from the prior rewrite

  • Per-behaviour tests instead of three monolithic tests — e.g. shows page counter on first load, back disabled / next enabled on first page, page increments on each navigation, table content differs on every page, total-pages stays constant while navigating, forward button disabled on last page, updates total pages after applying a filter.
  • Test-id locators: TEST_IDS.TABLE_PAGINATION, TEST_IDS.TABLE_PAGINATION_RESULTS, TEST_IDS.TABLE_FIRST_CELL, TEST_IDS.FILTER_ITEM, TEST_IDS.FILTER_COUNT, TEST_IDS.SEARCH_ALL_FILTERS (all already defined in e2e/features/common/constants.ts).
  • triggerActionAndWaitForUpdate — click then expect.poll(() => locator.textContent()).not.toEqual(prev) to wait for the row swap rather than relying on waitForLoadState.
  • getFilterWithCountInRange helper to find a filter dynamically rather than hardcoding option names that may not exist in every dataset slice.
  • getPaginationRegex / getTotalPages parse helpers for the "Page X of Y" string.

Adjust to match the anvil-filters standard

  • Use current constant names: MUI_CLASSES (not MUI_CLASS), KEYBOARD_KEYS (not KEYBOARD_KEY) — the prior branch predates the rename.
  • Replace hardcoded test-id strings like getByTestId("search-all-filters") with getByTestId(TEST_IDS.SEARCH_ALL_FILTERS).
  • Drop module-level let mutated in beforeEach. anvil-filters re-derives locators per-test via helper functions (getFilters(page), filterPopover(page), etc.). Mirror that: getPagination(page), paginationButtons(page). Avoids cross-test state and lifecycle ordering surprises.
  • Remove the commented-out urlOrPredicate block at the bottom of the prior file — dead code.
  • Move helpers to a top-level alphabetised block below a /* ——— helpers ——— */ divider, matching anvil-filters.spec.ts:201. None of the helpers should be exported (the prior branch exports getFilterWithCountInRange and openSearchAllFilters unnecessarily).
  • Use dispatchEvent('click') for filter-item clicks within the popover/search-all-filters dropdown — matches the webkit/overlay workaround already adopted across the filter helpers in e2e/features/common/filters.ts.
  • Re-use shared helpers from e2e/features/common/filters.ts where applicable — e.g. closeAutocompletePopper(page) instead of a raw page.keyboard.press(KEYBOARD_KEYS.ESCAPE); namedFilterItem if the test wants to re-locate by name after a click.
  • Lift magic numbers to constants at the top of the file — page size (25, currently inline in Math.ceil(count / 25)), the iteration counts (for (let i = 0; i < 3; ...)), and the filter-count range bounds (min = 25, max = 120).
  • Top-of-file constants block in the style of the ENTITIES / FACET_NAMES block in anvil-filters.spec.ts:17-25.
  • Drop conditional test.fail() — every test should assert deterministic outcomes.

Acceptance criteria

  • e2e/anvil/anvil-pagination.spec.ts runs reliably across chromium, firefox, and webkit (no flakes across multiple CI runs).
  • Every locator uses getByTestId, getByRole, or a container-scoped role/text query — no top-level .Mui* class selectors.
  • No dependency on the legacy testFirstPagePagination / filterAndTestLastPagePagination / testPaginationContent helpers in e2e/testFunctions.ts. (If no other spec uses them after this rewrite, remove them.)
  • Tests are split per-behaviour; each test name describes the single property under assertion.
  • Helpers and constants follow the layout conventions used in anvil-filters.spec.ts (alphabetised, non-exported, below a /* ——— helpers ——— */ divider, with a top-of-file constants block).
  • Page transitions are guarded by content-change polling (triggerActionAndWaitForUpdate or equivalent), not waitForLoadState.

Reference

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions