Files
redefined-designs/frontend/tests/e2e/pagination.spec.ts
T
bermudalambandClaude Opus 5 1bd0024e6f
Linting / lint (pull_request) Successful in 3m17s
SonarQube Analysis / sonarqube (pull_request) Failing after 31m17s
test(storefront): cover paging, and say what the filter tests meant (#269)
Two assertions named a fixture and expected it visible in the unfiltered grid. No paginated catalogue can promise that — the item is on some page, not necessarily the first — so both would have started failing the moment paging landed. They were only ever proxies for "the result set got bigger", and the visible total lets them say that directly, which is what the issue predicted when it asked for a count.

The new cases assert the control and the URL rather than which item is on which page, because the development database never truncates and which item lands where is not something a test may rely on. That is the same trap the two rewritten assertions had fallen into, and repeating it in new tests would have been worse than leaving them alone.

Writing them found a real defect rather than just covering the feature. The control was rendering while the catalogue was still loading, showing "0 items" for a moment before the real count arrived — the empty-state early return only fires once loading has finished, so a mid-load render fell through to the grid branch with a total of zero. It is now suppressed until there is something to count, which is both true and what makes the count usable as a signal in a test. StorefrontPage.totalItems waits for the control for the same reason: reading during the load returned zero and quietly made "the result set shrank" compare against nothing.

The conditional skips carry a file-level eslint exception with its reasoning rather than being left to add four warnings. They are honest about a real limit: against a catalogue of ten items or fewer these cases prove nothing, and if the e2e database is ever seeded that thinly they need fixtures of their own instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 16:11:07 -05:00

106 lines
4.2 KiB
TypeScript

import { test, expect } from './fixtures';
/* eslint-disable sonarjs/no-skipped-tests --
* The `test.skip(total <= 10, ...)` calls below are conditional guards, not
* disabled tests: they run whenever the catalogue is large enough to have a
* second page, which it is on any real database. The rule cannot tell a
* runtime condition from a permanently ignored test, and its own message asks
* for an explanation rather than removal — this is it.
*
* The guards are honest about a real limit, though: against a catalogue of ten
* items or fewer these cases prove nothing. If the e2e database is ever seeded
* that thinly, they should be given fixtures of their own rather than left to
* skip quietly.
*/
/**
* Paging the catalogue (#269).
*
* Every assertion here is about the control and the URL, never about a
* particular item being on a particular page. The development database never
* truncates, so which item lands where is not something a test may rely on —
* that is exactly the trap the two rewritten assertions in filters.spec.ts and
* favorites-filter.spec.ts had fallen into.
*/
test.describe('Paging the catalogue', () => {
test('shows how many items there are', async ({ storefront }) => {
await storefront.goto();
await expect(storefront.resultCount).toBeVisible();
expect(await storefront.totalItems()).toBeGreaterThan(0);
});
// Ten by default, which is the decision on the issue. A denser grid is
// something a customer asks for, not something they get by accident.
test('shows ten items to a page by default', async ({ page, storefront }) => {
await storefront.goto();
const total = await storefront.totalItems();
test.skip(total <= 10, 'needs more than one page of catalogue to be meaningful');
await expect(page.locator('.item-card')).toHaveCount(10);
});
// The reason numbered pages were chosen over infinite scroll: a page is a
// place you can send someone.
test('puts the page in the URL so it can be linked', async ({ page, storefront }) => {
await storefront.goto();
const total = await storefront.totalItems();
test.skip(total <= 10, 'needs more than one page of catalogue to be meaningful');
await storefront.pagination.getByRole('listitem', { name: '2', exact: true }).click();
await expect(page).toHaveURL(/[?&]page=2\b/);
});
// Page one is the absence of the parameter, so the plain URL stays clean.
test('leaves page one out of the URL', async ({ page, storefront }) => {
await storefront.goto();
const total = await storefront.totalItems();
test.skip(total <= 10, 'needs more than one page of catalogue to be meaningful');
await storefront.pagination.getByRole('listitem', { name: '2', exact: true }).click();
await expect(page).toHaveURL(/[?&]page=2\b/);
await storefront.pagination.getByRole('listitem', { name: '1', exact: true }).click();
await expect(page).not.toHaveURL(/[?&]page=/);
});
// A link to a page that no longer exists lands on the last one. An empty
// grid would read as "this shop has nothing".
test('clamps a page past the end rather than showing nothing', async ({ page, storefront }) => {
await page.goto('/?page=99999');
await expect(storefront.resultCount).toBeVisible();
await expect(page.locator('.item-card').first()).toBeVisible();
});
// The jump box was deliberately left off.
test('offers no "go to page" box', async ({ storefront }) => {
await storefront.goto();
await expect(
storefront.pagination.locator('.ant-pagination-options-quick-jumper')
).toHaveCount(0);
});
test('remembers a chosen page size', async ({ page, storefront }) => {
await storefront.goto();
const total = await storefront.totalItems();
test.skip(total <= 20, 'needs more than twenty items for a size change to show');
await storefront.pagination.locator('.ant-select').click();
await page.getByTitle('20 / page').click();
await expect(page.locator('.item-card')).toHaveCount(20);
// The preference is the point: it survives a reload without being in the
// URL, because it belongs to this person and not to a shared link.
await page.reload();
await expect(page.locator('.item-card')).toHaveCount(20);
await expect(page).not.toHaveURL(/pageSize/);
});
});