import { Locator, Page, expect } from '@playwright/test'; import { Header } from './Header'; /** * The public catalogue. * * The item card locator is the one place in the suite that knows the storefront * renders items into `.item-card` — three specs reached for that class directly, * and two others used `.ant-col`, which is antd's grid rather than anything this * application owns. Both break on an antd upgrade, in files that have nothing to * do with the upgrade. */ export class StorefrontPage { readonly header: Header; readonly filtersButton: Locator; readonly retryButton: Locator; readonly loadFailureHeading: Locator; constructor(private readonly page: Page) { this.header = new Header(page); this.filtersButton = page.getByRole('button', { name: /Filters/ }); this.retryButton = page.getByRole('button', { name: 'Retry' }); this.loadFailureHeading = page.getByRole('heading', { name: "The item list didn't load" }); } async goto(): Promise { await this.page.goto('/'); } /** * Goes to the storefront and waits for the session to settle. * * Navigating remounts the app, so the session is briefly still resolving. The * favorite control deliberately ignores clicks in that window rather than * wrongly prompting a signed-in customer to sign in, so a test that clicks * immediately gets nothing and no error. Waiting for the header is what a real * customer sees settle too. */ async gotoSignedIn(): Promise { await this.goto(); await this.header.waitForSignedIn(); } /** One item's card, located by the name shown on it. */ card(name: string): Locator { return this.page.locator('.item-card').filter({ hasText: name }); } addToCartButton(name: string): Locator { return this.card(name).getByRole('button', { name: 'Add to Cart' }); } /** The heart. Named for what it does rather than what it looks like. */ favoriteToggle(name: string): Locator { return this.card(name).getByRole('button', { name: /favorite/i }); } async openFilters(): Promise { await this.filtersButton.click(); } /** * Waits until the catalogue has rendered something. * * A test that asserts an item is absent needs to know the list arrived and did * not contain it, rather than that it asserted before the fetch resolved — * which passes for the wrong reason and keeps passing when the filter breaks. */ async waitForAnyItem(): Promise { await expect(this.page.locator('.item-card').first()).toBeVisible(); } }