import type { Page, Locator } from '@playwright/test'; /** * Page object for `/export` (see [frontend/src/routes/export/+page.svelte]). * * The two archive buttons are BOTH labelled exactly "Download" — the format is carried by * the surrounding card ("ZIP-Archiv" / "HTML-Viewer"), not by the accessible name. Earlier * versions of this file looked for `/zip.*herunter/i` and `/^herunterladen$/i`, which match * nothing on this page; any test asserting `not.toBeVisible()` on those passed no matter * what the page rendered. Locators here must match the real UI, so scope by card. * * "Herunterladen" IS a real label — but only inside the HTML-guide confirm modal, which is * a different element entirely. */ export class ExportPage { readonly page: Page; /** Empty state shown while the host has not released the export yet. */ readonly notAvailableBanner: Locator; /** Any "Download" button on the page (both cards). Useful for absence assertions. */ readonly downloadButtons: Locator; readonly zipDownloadButton: Locator; readonly htmlDownloadButton: Locator; /** Confirm button inside the "Hinweis zum HTML-Viewer" modal. */ readonly htmlGuideModalContinue: Locator; constructor(page: Page) { this.page = page; this.notAvailableBanner = page.getByText('Export noch nicht verfügbar'); this.downloadButtons = page.getByRole('button', { name: 'Download', exact: true }); this.zipDownloadButton = this.cardButton('ZIP-Archiv'); this.htmlDownloadButton = this.cardButton('HTML-Viewer'); this.htmlGuideModalContinue = page .getByRole('dialog') .getByRole('button', { name: /^herunterladen$/i }); } /** * The "Download" button inside the card whose heading is `heading`. * * Scoped to the card element (`div.rounded-xl`) rather than "any div containing the * heading" — the latter also matches the page wrapper, which contains BOTH cards' buttons. */ private cardButton(heading: string): Locator { return this.page .locator('div.rounded-xl') .filter({ has: this.page.getByRole('heading', { name: heading, exact: true }) }) .getByRole('button', { name: 'Download', exact: true }); } async goto() { await this.page.goto('/export'); } }