import { test, expect, createItem, uniqueSuffix } from './fixtures'; /** * The countdown is the one part of this app whose output depends on the wall * clock, so these use Playwright's clock control rather than waiting in real * time. Installing it also makes the assertions deterministic: without it, "the * text changed" would depend on where in the minute the test happened to start. */ test.describe('The cart reservation countdown', () => { test('counts down on its own, with no interaction', async ({ page, customer, cart }) => { const name = `Held c${uniqueSuffix()}`; const item = await createItem(page.request, { name, price: '80' }); expect((await page.request.post(`/api/cart/items/${item.id}`)).status()).toBe(201); // Installed before navigating: the interval is created on mount, and a clock // swapped in afterwards would not be the one it is scheduled against. await page.clock.install(); await cart.goto(); const before = await cart.remaining(name).textContent(); expect(before).toMatch(/left/); // Two minutes, which is four ticks of the 30s interval and enough to move a // display with one-minute resolution. await page.clock.fastForward('02:00'); // The assertion the issue exists for: the text changes with nothing // clicked, reloaded or navigated. Before this, it was a still photograph of // the moment the page rendered. await expect(cart.remaining(name)).not.toHaveText(before ?? ''); }); test('warns near the end of the hold rather than for the whole of it', async ({ page, customer, cart }) => { const name = `Waning c${uniqueSuffix()}`; const item = await createItem(page.request, { name, price: '80' }); expect((await page.request.post(`/api/cart/items/${item.id}`)).status()).toBe(201); await page.clock.install(); await cart.goto(); // Freshly reserved, so it is not urgent. antd renders `type="danger"` as a // class rather than an attribute, which is why this asserts on the class. await expect(cart.remaining(name)).not.toHaveClass(/ant-typography-danger/); // Past 90% of the default 24-hour hold, so inside the final tenth. The old // hardcoded "final hour" threshold would have been silent here. await page.clock.fastForward('22:00:00'); await expect(cart.remaining(name)).toHaveClass(/ant-typography-danger/); }); test('says the hold is expiring once it is past, rather than freezing', async ({ page, customer, cart }) => { const name = `Lapsing c${uniqueSuffix()}`; const item = await createItem(page.request, { name, price: '80' }); expect((await page.request.post(`/api/cart/items/${item.id}`)).status()).toBe(201); await page.clock.install(); await cart.goto(); // Past the deadline. The server releases held items on a sweep every few // minutes, so between the deadline and that sweep the item is genuinely // still held — "expiring…" claims imminence rather than completion. await page.clock.fastForward('25:00:00'); await expect(cart.remaining(name)).toHaveText('expiring…'); }); });