import { expect, type Locator, type Page } from '@playwright/test'; import { BasePage } from './BasePage'; import { IAppointmentDetails } from '@business-logic/types/CustomerDetails'; import { formatDate, formatTime } from '@impl/utils/DateUtils'; import { ServiceLocation } from '@business-logic/types/Enums'; import { getCustomerDetails, ITestData } from 'safelite-playwright-core'; export class SchedulePage extends BasePage { readonly page: Page; issPageValue = 'schedule-page'; readonly inShopButton: Locator; readonly mobileButton: Locator; readonly moreLocationsButton: Locator; readonly zipCodeTextBox: Locator; readonly findButton: Locator; readonly selectAShopOptions: Locator; readonly firstAppointmentButton : Locator; readonly firstAvailableAMDate: Locator; readonly firstAvailablePMDate: Locator; readonly firstAvailableTime: Locator; readonly firstAvailabeAMTime: Locator; readonly modalContinueButton: Locator; readonly dropOffButton: Locator; readonly dateText: Locator; readonly viewMoreDatesLink: Locator; readonly recalAcknowledgementModal: Locator; readonly learnMoreLinkRecalAcknowledgementModal: Locator; readonly recalAcknowledgementCheckBox: Locator; readonly continueButtonRecalAcknowledgementModal: Locator; readonly continueButton: Locator; constructor(page: Page) { super(page); this.page = page; this.inShopButton = this.page.getByText('At a Safelite shop'); this.mobileButton = this.page.getByText('Have Safelite come to me'); // For in shop this.moreLocationsButton = this.page.getByRole('button', { name: 'More Locations' }); // For mobile this.zipCodeTextBox = this.page.locator('#serviceZipCode'); this.findButton = this.page.getByRole('button', { name: 'Find' }); // For in-shop and drop off this.selectAShopOptions = this.page.locator('[class="shop-question"]'); this.firstAppointmentButton = this.page.locator('#availabilityIndicator').first(); this.firstAvailableAMDate = this.page.locator('morning-row-cell has-appointments selected-date').first(); this.firstAvailablePMDate = this.page.locator('afternoon-row-cell has-appointments selected-date').first(); this.firstAvailableTime = this.page.locator('.time-slot-button').first(); this.firstAvailabeAMTime = this.page.locator('.time-slot-button').nth(0); this.modalContinueButton = this.page.locator('#modalbtn'); this.dropOffButton = this.page.locator('[class="dropoff-label"]'); this.dateText = this.page.locator('[class="modal-header mb-2 mt-2"]'); this.viewMoreDatesLink = this.page.getByRole('button', { name: 'More Right arrow icon' }); // Recal acknowledgement modal when Safelite cannot recalibrate the vehicle this.recalAcknowledgementModal = this.page.getByLabel('ModalComponentLabel'); this.learnMoreLinkRecalAcknowledgementModal = this.page.getByRole('link', { name: 'Learn more' }); this.recalAcknowledgementCheckBox = this.page.getByRole('checkbox', { name: /I acknowledge/i }); this.continueButtonRecalAcknowledgementModal = this.page.locator('#RecalAckModalWidget').getByRole('button', { name: 'Continue' }); this.continueButton = this.page.locator('#stacked').locator('button:has-text("Continue")'); } // Select in shop and schedule async scheduleInShop(appointmentDetails?: IAppointmentDetails){ if (appointmentDetails?.serviceLocation === ServiceLocation.InShop) { try {await this.inShopButton.waitFor({ state: 'visible', timeout: 5000 }); await this.inShopButton.isVisible() await this.inShopButton.click(); await (await this.getFirstNonDropOffTimeSlot()).click(); } catch { await this.inShopButton.isHidden() await (await this.getFirstNonDropOffTimeSlot()).click(); } } else if (appointmentDetails?.serviceLocation === ServiceLocation.DropOff) { if (await this.inShopButton.isVisible()){ await this.inShopButton.waitFor({ state: 'visible', timeout: 5000 }); await this.inShopButton.click(); if (await this.dropOffButton.isVisible()) { await this.dropOffButton.waitFor({ state: 'visible', timeout: 5000 }); await this.dropOffButton.click() } else { await (await this.getFirstNonDropOffTimeSlot()).click(); } } else { await this.inShopButton.click(); await (await this.getFirstNonDropOffTimeSlot()).click(); } } else { await this.selectFirstAvailableTime(); } await this.continueButton.click(); } // Select mobile button, enter zip code, click find button, select first available time and continue async scheduleMobile(appointmentDetails: IAppointmentDetails){ await this.mobileButton.click(); await this.zipCodeTextBox.fill(appointmentDetails.serviceAddress!.postalCode!); await this.findButton.click(); await this.selectFirstAvailableTime(); await this.continueButton.click(); } // Selects the first available time slot but if its not visible, click "More" first to load more dates and times async selectFirstAvailableTime(): Promise { if (await this.firstAvailableTime.isVisible()) { await this.firstAvailableTime.click(); } else { await this.viewMoreDatesLink.click(); await this.firstAvailableTime.waitFor({ state: 'visible' }); await this.firstAvailableTime.click(); } } async getFirstNonDropOffTimeSlot(): Promise { const timeSlots = this.page.locator('.time-slot-button'); const nonDropOff = timeSlots.filter({ hasNotText: /Drop & Go/i }); return nonDropOff.first(); } async validateRecalAcknowledgementModal() { await this.learnMoreLinkRecalAcknowledgementModal.click(); await this.recalAcknowledgementCheckBox.click(); await this.continueButtonRecalAcknowledgementModal.click(); } }