DigitalConsumer.FixMyGlass/playwright-tests/pages/ServiceLocationPage.ts
maguire-arman d26885d0f0 Improves handling of repeated clicks modal
Updates the selector for the repeated clicks modal close button to be more specific, and adds a short wait after closing the modal before proceeding.  This improves reliability when the modal appears and interferes with subsequent actions.
Removes redundant check for modal visibility in the service location page as it is handled on the service packages page.
2025-04-10 13:37:41 -04:00

143 lines
No EOL
6.2 KiB
TypeScript

import { expect, type Locator, type Page } from '@playwright/test';
import { BasePage } from './BasePage';
import { IAppointmentDetails } from '@business-logic/types/CustomerDetails';
import { AppointmentType } from '@business-logic/types/Enums';
import { AddressForm } from './forms/AddressForm';
import { faker } from '@faker-js/faker';
export class ServiceLocationPage extends BasePage {
readonly page: Page;
readonly addressForm: AddressForm;
// Initial selection
readonly inShopButton: Locator;
readonly mobileButton: Locator;
readonly dropOffButton: Locator;
readonly RecalWarningMessage1: Locator;
readonly RecalWarningMessage2: Locator;
readonly militaryWarningMessage: Locator;
// For in-shop and drop off
readonly selectAShopOptions: Locator;
readonly firstAppointmentButton: Locator;
readonly changeZipButton: Locator;
readonly updateZipTextBox: Locator;
readonly saveZipButton: Locator;
// For mobile
readonly enterServiceAddressButton: Locator;
readonly serviceAddressTextBox: Locator;
readonly aptNumberTextBox: Locator;
readonly cityTextBox: Locator;
readonly stateDropDown: Locator;
readonly zipCodeTextBox: Locator;
readonly vehicleProtectedYesButton: Locator;
readonly vehicleProtectedNoButton: Locator;
readonly saveAddressButton: Locator;
readonly repeatedClicksModalCloseButton: Locator;
url = process.env['BASE_URL']! + '/fmg/?fmgPage=service-location';
constructor(page: Page) {
super(page);
this.page = page;
this.addressForm = new AddressForm(page);
// Initial selection
this.inShopButton = this.page.getByText(/In-shop/);
this.mobileButton = this.page.getByText(/Mobile/);
this.dropOffButton = this.page.getByText(/Drop-off/);
this.RecalWarningMessage1 = this.page.getByText(/We're not able to provide mobile service/);
this.RecalWarningMessage2 = this.page.getByText(/advanced safety system recalibration needs to be done in our shop./);
this.militaryWarningMessage = this.page.locator('[class*="widget-name-AlertMilitaryBaseZipWidget"]');
// For in-shop and drop off
this.selectAShopOptions = this.page.locator('[class="shop-question"]');
this.firstAppointmentButton = this.page.locator('div').filter({ hasText: /Appts/}).first();
this.changeZipButton = this.page.locator('a:has(span.sr-only:has-text("edit zip code"))');
this.updateZipTextBox = this.page.locator('#serviceZipCode');
this.saveZipButton = this.page.getByText('Save ZIP code', { exact: true });
// For mobile
this.enterServiceAddressButton = this.page.getByRole('link', { name: 'Enter your service address' });
this.serviceAddressTextBox = this.page.getByRole('textbox', { name: 'Street Address' });
this.aptNumberTextBox = this.page.getByRole('textbox', { name: 'Apt. number'});
this.cityTextBox = this.page.getByRole('textbox', { name: 'City' });
this.stateDropDown = this.page.getByRole('combobox', { name: 'State' });
this.zipCodeTextBox = this.page.getByRole('textbox', { name: 'Zip code' });
this.vehicleProtectedYesButton = this.page.locator('label').filter({ hasText: 'Yes' }).locator('div');
this.vehicleProtectedNoButton = this.page.locator('label').filter({ hasText: 'No' }).locator('div');
this.saveAddressButton = this.page.getByRole('button', { name: 'Continue' })
this.repeatedClicksModalCloseButton = this.page.getByRole('img').nth(1);
}
async selectLocation(appointmentDetails: IAppointmentDetails){
switch(appointmentDetails.serviceLocation) {
case AppointmentType.Mobile:
await this.scheduleMobile(appointmentDetails);
break;
case AppointmentType.InShop:
await this.scheduleInShop(appointmentDetails);
break;
case AppointmentType.DropOff:
await this.scheduleDropOff(appointmentDetails);
break;
}
}
async scheduleInShop(appointmentDetails?: IAppointmentDetails) {
await this.inShopButton.click();
if (appointmentDetails && appointmentDetails.shopAddress) {
const zipCodeMatch = appointmentDetails.shopAddress.match(/\b\d{5}$/);
if (zipCodeMatch) {
const zipCode = zipCodeMatch[0];
// Enter the ZIP code into the updateZipTextBox
await this.changeZipButton.click();
await this.page.waitForTimeout(500);
await this.updateZipTextBox.fill(zipCode);
await this.saveZipButton.click();
}
await this.inShopButton.click();
await this.selectAShopOptions.locator(`[buttonbodycopy="${appointmentDetails.shopAddress}"]`).check();
} else {
await this.firstAppointmentButton.click();
}
}
async scheduleMobile(appointmentDetails: IAppointmentDetails){
if (appointmentDetails.serviceAddress) {
await this.mobileButton.click();
await this.enterServiceAddressButton.click();
await this.addressForm.populateAddress({ address: appointmentDetails.serviceAddress! });
if (faker.datatype.boolean()) {
await this.vehicleProtectedYesButton.check();
} else {
await this.vehicleProtectedNoButton.check();
}
await this.saveAddressButton.click();
} else {
console.error('ServiceLocationPage >> Please supply an address')
}
}
async scheduleDropOff(appointmentDetails?: IAppointmentDetails){
await this.dropOffButton.click();
if (appointmentDetails && appointmentDetails.shopAddress) {
await this.selectAShopOptions.locator(`[buttonbodycopy="${appointmentDetails.shopAddress}"]`).check();
} else {
// await this.firstAppointmentButton.click();
await this.clickWithRetry(this.firstAppointmentButton, this.page);
}
}
async validateRecalWarning(){
await expect(this.RecalWarningMessage1).toBeVisible();
await expect(this.RecalWarningMessage2).toBeVisible();
}
}