import { IAppointmentDetails, ICustomerDetails } from '@business-logic/types/CustomerDetails'; import Soft from '@business-logic/validations/Soft'; import test, { expect, type Locator, type Page } from '@playwright/test'; import { error } from 'console'; /** * Base class for all page objects. * Provides common functionality for interacting with pages. */ export class BasePage { // Common locators readonly page: Page; readonly continueButton: Locator; readonly backButton: Locator; readonly pageSpinner: Locator; readonly buttonLoadSpin: Locator; readonly hamburgerMenu: Locator; readonly progressBar: Locator; constructor(page: Page){ this.page = page; this.continueButton = page.locator('[id="infoBox"]').getByRole('button'); this.backButton = page.locator('[id="infoBox"]').getByRole('link'); this.pageSpinner = page.getByRole('status'); this.buttonLoadSpin = page.getByRole('alert'); this.hamburgerMenu = this.page.getByRole('button', { name: 'Hamburger Menu (modal window)' }); this.progressBar = this.page.locator('#progress-bar-container progress'); } async nextPage() { const startingUrl = this.page.url(); await expect(async () => { const currentUrl = this.page.url(); if (currentUrl === startingUrl) { await this.continueButton.click({ timeout: 1000 }); } expect(currentUrl).not.toEqual(startingUrl); }).toPass({ timeout: 240_000 }); } async previousPage() { const startingUrl = this.page.url(); await expect(async () => { const currentUrl = this.page.url(); if (currentUrl === startingUrl) { await this.backButton.click({ timeout: 1000 }); } expect(currentUrl).not.toEqual(startingUrl); }).toPass({ timeout: 240_000 }); } async validateURL(url:string){ await expect(this.pageSpinner).toHaveCount(0, {timeout: 60000}); await this.page.waitForURL(url); } async fillAndValidate(element: Locator, value: string){ await expect(async () => { await element.clear(); await element.fill(value); await expect(element).toHaveValue(value); }).toPass(); } async clickWithRetry(element, page) { const timeout = 5000; // milli seconds const startTime = Date.now(); while (Date.now() - startTime < timeout) { try { if (await element.isEnabled()) { await element.click(); await element.keyboard.press('Tab'); return; // Exit loop if click succeeds } } catch (error) { // Ignore error and retry } await page.waitForTimeout(100); // Small delay before retrying } console.log(`Failed to click the the element within ${timeout/1000} seconds` + error); } async logReferralNumber() { let mainLocalStorage = JSON.parse(await this.page.evaluate('localStorage.getItem(\'main\')')); let referralNumber = mainLocalStorage.order.referralNumber as number; let referralSequenceNumber = mainLocalStorage.order.referralSequenceNumber as number; if (referralNumber == null) { for (let i = 1; i <= 20; i++) { if (!referralNumber == null) break; await this.page.waitForTimeout(500); mainLocalStorage = JSON.parse(await this.page.evaluate('localStorage.getItem(\'main\')')); referralNumber = mainLocalStorage.order.referralNumber as number; referralSequenceNumber = mainLocalStorage.order.referralSequenceNumber as number; } } await test.step(`Referral Number:${referralNumber} Referral Sequence Number:${referralSequenceNumber}`, async () => { console.log(`Referral Number:${referralNumber}`); console.log(`Referral Sequence Number:${referralSequenceNumber}`); }); } async mockScheduleResponseForEarlyBird(customerDetails: ICustomerDetails) { { const apiUrl = `https://digitalapi.${process.env['NODE_ENV']!.replace('sys', 'test').toLowerCase()}.safelite.io/schedule/api/v1/schedule/mobile-time-slots`; await this.page.route(apiUrl, async (route) => { const response = await route.fetch(); const responseBody = await response.json(); responseBody.days.forEach((day: any) => { day.timeSlots.forEach((slot: any) => { if (slot.id.includes("AM")) { slot.offerPremium = true; } }); }); customerDetails.apptDate = responseBody.days.find((day: any) => day.timeSlots.some((slot: any) => slot.offerPremium === true) ).date || undefined; // Mock the response await route.fulfill({ response, body: JSON.stringify(responseBody), }); }); } } async validateProgressBar(progressPercentage: string) { await this.page.locator('button .loader').waitFor({ state: 'hidden', timeout: 60000 }); const actualProgressPercentage = await this.progressBar.getAttribute("value") || "Not Found"; Soft.expect(actualProgressPercentage).toBe(progressPercentage); console.log(`Progress Bar Percentage: Actual - ${actualProgressPercentage} vs Expected - ${progressPercentage}`); } }