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
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions