import { Page, Locator } from '@playwright/test' /** * Base Page Object for Analytics Pages * Provides common functionality shared across all 10 analytics pages */ export class AnalyticsPageBase { readonly page: Page readonly pageTitle: Locator readonly dateFilter: Locator readonly kpiSection: Locator readonly loadingSpinner: Locator readonly errorMessage: Locator constructor(page: Page) { this.page = page this.pageTitle = page.locator('[data-testid="page-title"]') this.dateFilter = page.locator('[data-testid="date-filter"]') this.kpiSection = page.locator('[data-testid="kpi-section"]') this.loadingSpinner = page.locator('[data-testid="loading-spinner"]') this.errorMessage = page.locator('[data-testid="error-message"]') } async waitForLoad() { // Wait for DOM content to be ready await this.page.waitForLoadState('domcontentloaded') // If loading spinner appears, wait for it to disappear const hasSpinner = await this.loadingSpinner.isVisible().catch(() => false) if (hasSpinner) { await this.loadingSpinner.waitFor({ state: 'hidden', timeout: 10000 }) } // Wait for page title to be visible - this is the key indicator page has loaded await this.pageTitle.waitFor({ state: 'visible', timeout: 10000 }) } async selectDateRange(range: '7d' | '30d' | 'this-month' | 'last-month' | 'custom') { const button = this.page.locator(`[data-testid="filter-${range}"]`) await button.click() } async getKpiCardValue(cardTestId: string): Promise { const card = this.page.locator(`[data-testid="${cardTestId}"]`) const valueElement = card.locator('.value') if (await valueElement.isVisible().catch(() => false)) { return valueElement.textContent() } return null } async verifyKpiCardsRendered(): Promise { await this.kpiSection.waitFor({ state: 'visible', timeout: 10000 }) const cards = this.kpiSection.locator('.kpi-card') return cards.count() } }