import { Locator, Page } from '@playwright/test'; /** * The cart, where the reservation countdown lives. * * The countdown is the reason this page object exists: it is the one place in * the app whose output depends on the clock, so a test has to be able to name * the text without re-deriving the locator each time. */ export class CartPage { readonly continueShoppingButton: Locator; readonly emptyNotice: Locator; readonly checkoutButton: Locator; readonly demoNotice: Locator; constructor(private readonly page: Page) { this.continueShoppingButton = page.getByRole('button', { name: 'Continue Shopping' }); this.emptyNotice = page.getByText('Your cart is empty'); // Matched on a prefix rather than the whole label, so a test can assert what // the label says instead of having to know it in order to find the button. // That is the point of #195: the label was wrong, and a locator naming it in // full would have gone looking for the right button and found nothing. this.checkoutButton = page.getByRole('button', { name: /^Checkout/ }); this.demoNotice = page.getByText('Demonstration only'); } async goto(): Promise { await this.page.goto('/cart'); } row(itemName: string): Locator { return this.page.getByRole('listitem').filter({ hasText: itemName }); } /** The "2h 15m left" / "expiring…" line for one held item. */ remaining(itemName: string): Locator { return this.row(itemName).getByText(/left|expiring/); } removeButton(itemName: string): Locator { return this.row(itemName).getByRole('button', { name: 'Remove' }); } }