- Add HVAC_Test_User_Factory class with: * User creation with specific roles * Multiple role support * Persona management system * Account cleanup integration - Create comprehensive test suite in HVAC_Test_User_Factory_Test.php - Update testing improvement plan documentation - Add implementation decisions to project memory bank - Restructure .gitignore with: * Whitelist approach for better file management * Explicit backup exclusions * Specific bin directory inclusions Part of the Account Management component from the testing framework improvement plan.
217 lines
No EOL
7.9 KiB
TypeScript
217 lines
No EOL
7.9 KiB
TypeScript
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<number> {
|
|
return this.page.locator('.events-table tbody tr').count();
|
|
}
|
|
} |