import { Page, expect } from '@playwright/test'; import { LogParser } from '../utils/logParser'; export class DashboardPage { readonly page: Page; private readonly selectors = { // Navigation buttons createEventButton: '#create-event-btn', viewTrainerProfileButton: '#view-trainer-profile-btn', logoutButton: '#logout-btn', // Statistics summary totalEvents: '#total-events', upcomingEvents: '#upcoming-events', pastEvents: '#past-events', totalTickets: '#total-tickets', totalRevenue: '#total-revenue', revenueTargetComparison: '#revenue-comparison', // Events table eventsTable: '#events-table', eventStatusIcon: '.event-status-icon', eventNameLink: '.event-name-link', eventDateCell: '.event-date', eventOrganizer: '.event-organizer', eventCapacity: '.event-capacity', eventTicketsSold: '.event-tickets-sold', eventRevenue: '.event-revenue', sortButton: '.sort-button', filterDropdown: '#event-filter' }; constructor(page: Page) { this.page = page; } async navigate() { await this.page.goto('/hvac-dashboard/'); await this.page.waitForLoadState('networkidle'); } // Navigation button methods async clickCreateEvent() { await this.page.click(this.selectors.createEventButton); } async clickViewTrainerProfile() { await this.page.click(this.selectors.viewTrainerProfileButton); } async clickLogout() { await this.page.click(this.selectors.logoutButton); } // Statistics verification methods async verifyTotalEvents(expectedCount: number) { const count = await this.page.textContent(this.selectors.totalEvents); expect(Number(count)).toBe(expectedCount); } async verifyUpcomingEvents(expectedCount: number) { const count = await this.page.textContent(this.selectors.upcomingEvents); expect(Number(count)).toBe(expectedCount); } async verifyPastEvents(expectedCount: number) { const count = await this.page.textContent(this.selectors.pastEvents); expect(Number(count)).toBe(expectedCount); } async verifyTotalTickets(expectedCount: number) { const count = await this.page.textContent(this.selectors.totalTickets); expect(Number(count)).toBe(expectedCount); } async verifyTotalRevenue(expectedAmount: number) { const amount = await this.page.textContent(this.selectors.totalRevenue); expect(amount).not.toBeNull(); const parsedAmount = amount ? parseFloat(amount.replace(/[^0-9.]/g, '')) : 0; expect(parsedAmount).toBe(expectedAmount); // Verify proper currency formatting expect(amount).toMatch(/^\$\d{1,3}(,\d{3})*(\.\d{2})?$/); } async verifyRevenueComparison(expectedText: string) { const comparison = await this.page.textContent(this.selectors.revenueTargetComparison); expect(comparison).toContain(expectedText); } // Navigation button selectors get createEventButton() { return this.page.locator('button:has-text("Create Event")'); } get viewTrainerProfileButton() { return this.page.locator('button:has-text("View Trainer Profile")'); } // Statistics selectors get totalEventsCount() { return this.page.locator('[data-testid="total-events-count"]'); } get upcomingEventsCount() { return this.page.locator('[data-testid="upcoming-events-count"]'); } get pastEventsCount() { return this.page.locator('[data-testid="past-events-count"]'); } get totalRevenue() { return this.page.locator('[data-testid="total-revenue"]'); } get revenueProgress() { return this.page.locator('[data-testid="revenue-progress"]'); } // Events table methods getStatusIcon(status: string) { return this.page.locator(`[data-testid="status-icon-${status}"]`); } getEventLink(eventName: string) { return this.page.locator(`[data-testid="event-link"]:has-text("${eventName}")`); } getEventRow(eventName: string) { return this.page.locator(`[data-testid="event-row"]:has-text("${eventName}")`); } async verifyEventStatusIcon(eventRow: number, expectedStatus: string) { const statusIcon = await this.page.locator(this.selectors.eventStatusIcon).nth(eventRow); const classList = await statusIcon.getAttribute('class'); expect(classList).toContain(`status-${expectedStatus.toLowerCase()}`); // Verify tooltip content const tooltip = await statusIcon.getAttribute('title'); expect(tooltip).toContain(expectedStatus); } async verifyEventNameLink(eventRow: number, expectedUrl: string) { const link = await this.page.locator(this.selectors.eventNameLink).nth(eventRow); const href = await link.getAttribute('href'); expect(href).toBe(expectedUrl); } async verifyEventDateHighlight(eventRow: number, shouldBeHighlighted: boolean) { const dateCell = await this.page.locator(this.selectors.eventDateCell).nth(eventRow); const classList = await dateCell.getAttribute('class'); if (shouldBeHighlighted) { expect(classList).toContain('highlighted'); } else { expect(classList).not.toContain('highlighted'); } } async sortBy(columnName: string) { await this.page.locator(this.selectors.sortButton, { hasText: columnName }).click(); } async filterBy(filterOption: string) { await this.page.selectOption(this.selectors.filterDropdown, filterOption); } async verifyEventOrganizer(eventRow: number, expectedOrganizer: string) { const organizer = await this.page.locator(this.selectors.eventOrganizer).nth(eventRow).textContent(); expect(organizer).toBe(expectedOrganizer); } async verifyEventCapacity(eventRow: number, expectedCapacity: number) { const capacity = await this.page.locator(this.selectors.eventCapacity).nth(eventRow).textContent(); expect(Number(capacity)).toBe(expectedCapacity); } async verifyEventTicketsSold(eventRow: number, expectedSold: number) { const sold = await this.page.locator(this.selectors.eventTicketsSold).nth(eventRow).textContent(); expect(Number(sold)).toBe(expectedSold); } async verifyEventRevenue(eventRow: number, expectedRevenue: number) { const revenue = await this.page.locator(this.selectors.eventRevenue).nth(eventRow).textContent(); expect(revenue).not.toBeNull(); const parsedRevenue = revenue ? parseFloat(revenue.replace(/[^0-9.]/g, '')) : 0; expect(parsedRevenue).toBe(expectedRevenue); } async verifyTableSortOrder(columnName: string, expectedOrder: string[]) { const column = columnName.toLowerCase(); const selector = column === 'date' ? this.selectors.eventDateCell : column === 'revenue' ? this.selectors.eventRevenue : this.selectors.eventNameLink; const elements = await this.page.locator(selector).all(); const actualValues = await Promise.all(elements.map(async el => await el.textContent())); expect(actualValues).toEqual(expectedOrder); } async verifyFilterResults(filterType: string, expectedCount?: number) { const statuses = await this.page.locator(this.selectors.eventStatusIcon).all(); for (const status of statuses) { const classList = await status.getAttribute('class'); expect(classList).toContain(`status-${filterType}`); } if (expectedCount !== undefined) { const actualCount = await this.getTableRowCount(); expect(actualCount).toBe(expectedCount); } } async getTableRowCount(): Promise { return this.page.locator('.events-table tbody tr').count(); } }