Merge pull request #2826 from Safelite/feature/CASH-1042_QA

Feature/cash 1042 qa
This commit is contained in:
kpatel8hs4io 2025-09-08 15:30:26 -04:00 committed by GitHub
commit 8b254b8fc4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 71 additions and 51 deletions

View file

@ -5,23 +5,23 @@ export enum PaymentMethod {
} }
export enum ProgressBarPercentages { export enum ProgressBarPercentages {
VehicleSelectionPage = '4%', VehicleSelectionPage = '8%',
VehicleDamagePage = '16%', VehicleDamagePage = '11%',
EstimatePage = '28%', EstimatePage = '18%',
ServiceZipPage = '32%', ServiceZipPage = '22%',
VehicleLookupAddressPage = '32%', VehicleLookupAddressPage = '22%',
VehicleLookupLicensePage = '32%', VehicleLookupLicensePage = '22%',
VinLookupPage = '32%', VinLookupPage = '22%',
PartQuestionsPage = '40%', PartQuestionsPage = '30%',
MoldingQuestionsPage = '40%', MoldingQuestionsPage = '30%',
CapabilityQuestionsPage = '40%', CapabilityQuestionsPage = '30%',
VehiclePartsPage = '40%', VehiclePartsPage = '30%',
ServicePackagePage = '48%', ServicePackagePage = '62%',
InsuranceCompanyPage = '52%', InsuranceCompanyPage = '52%',
ServiceLocationPage = '60%', ServiceLocationPage = '76%',
SchedulePage = '64%', SchedulePage = '76%',
MobileDetailsPage = '76%', MobileDetailsPage = '76%',
ContactDetailsPage = '84%', ContactDetailsPage = '84%',
PaymentMethodPage = '92%', PaymentMethodPage = '97%',
OrderConfirmationPage = '100%' OrderConfirmationPage = '100%'
} }

View file

@ -10,6 +10,8 @@ export class AfterpayPage extends BasePage {
readonly passwordTextBox: Locator; readonly passwordTextBox: Locator;
// Card details // Card details
readonly paymentOptionsButton: Locator;
readonly continueButtonAfterpay: Locator;
readonly cardholderNameTextBox: Locator; readonly cardholderNameTextBox: Locator;
readonly cardNumberTextBox: Locator; readonly cardNumberTextBox: Locator;
readonly expirationDateTextBox: Locator; readonly expirationDateTextBox: Locator;
@ -23,6 +25,8 @@ export class AfterpayPage extends BasePage {
this.passwordTextBox = page.getByTestId('login-password-input'); this.passwordTextBox = page.getByTestId('login-password-input');
this.submitButton = page.getByRole('button', { name: 'Continue' }); this.submitButton = page.getByRole('button', { name: 'Continue' });
this.paymentOptionsButton = page.locator('div:has(>input[id*=\'payment-types\']) label').nth(1);
this.continueButtonAfterpay = page.getByRole('button').filter({hasText: 'Continue'});
this.cardholderNameTextBox = page.getByTestId('payment-method-cardHolderName-input'); this.cardholderNameTextBox = page.getByTestId('payment-method-cardHolderName-input');
this.cardNumberTextBox = page.getByTestId('payment-method-cardNumber-input'); this.cardNumberTextBox = page.getByTestId('payment-method-cardNumber-input');
this.expirationDateTextBox = page.getByTestId('payment-method-cardExpiry-input'); this.expirationDateTextBox = page.getByTestId('payment-method-cardExpiry-input');
@ -46,7 +50,12 @@ export class AfterpayPage extends BasePage {
async executeAfterpayPayment(paymentDetails: IPaymentDetails) { async executeAfterpayPayment(paymentDetails: IPaymentDetails) {
await this.login(paymentDetails.password!); await this.login(paymentDetails.password!);
await this.page.locator('div[data-testid=\'loading-icon-svg\']').filter({ visible: true}).first().waitFor({ state: 'hidden' });
if (await this.paymentOptionsButton.isVisible())
{
await this.paymentOptionsButton.click();
await this.continueButtonAfterpay.click();
}
await this.confirmButton.click(); await this.confirmButton.click();
} }

View file

@ -25,7 +25,7 @@ export class BasePage {
this.pageSpinner = page.getByRole('status'); this.pageSpinner = page.getByRole('status');
this.buttonLoadSpin = page.getByRole('alert'); this.buttonLoadSpin = page.getByRole('alert');
this.hamburgerMenu = this.page.getByRole('button', { name: 'Hamburger Menu (modal window)' }); this.hamburgerMenu = this.page.getByRole('button', { name: 'Hamburger Menu (modal window)' });
this.progressBar = this.page.locator('.progress-bar-inner'); this.progressBar = this.page.locator('.progress-bar-outer.progress-bar-inner');
} }
async nextPage() { async nextPage() {

View file

@ -56,7 +56,7 @@ export class PaymentMethodPage extends BasePage {
this.payNowButton = this.page.locator('[buttonlabel="Pay now"]'); this.payNowButton = this.page.locator('[buttonlabel="Pay now"]');
this.payInFourButton = this.page.locator('[buttonlabel="Pay in 4 installments"]'); this.payInFourButton = this.page.locator('[buttonlabel="Pay in 4 installments"]');
this.submitButton = this.page.locator('[data-test-id="nav-bar-main-button"]'); this.submitButton = this.page.locator('[data-test-id="nav-bar-main-button"]');
this.recalibrationCheckbox = this.page.getByLabel('I understand after windshield'); this.recalibrationCheckbox = this.page.locator('label:has(>input[name=\'recalAckOptIn\'])');
// this.creditCardButton = page.locator('div').filter({ hasText: /^Credit or Debit$/ }).nth(1); // this.creditCardButton = page.locator('div').filter({ hasText: /^Credit or Debit$/ }).nth(1);
this.paymentPage = new PaymentPage(page); this.paymentPage = new PaymentPage(page);
this.paypalPage = new PaypalPage(page); this.paypalPage = new PaypalPage(page);
@ -101,7 +101,7 @@ export class PaymentMethodPage extends BasePage {
let expectedAppointmentDetails = new Map<string, string[]>(); let expectedAppointmentDetails = new Map<string, string[]>();
expectedAppointmentDetails = await this.getExpectedVehicleDetails(testData, expectedAppointmentDetails); expectedAppointmentDetails = await this.getExpectedVehicleDetails(testData, expectedAppointmentDetails);
expectedAppointmentDetails = await this.getExpectedVehicleDamage(testData, expectedAppointmentDetails); expectedAppointmentDetails = await this.getExpectedVehicleDamage(testData, expectedAppointmentDetails);
expectedAppointmentDetails = await this.expectedServicePackageDetails(testData, expectedAppointmentDetails); // expectedAppointmentDetails = await this.expectedServicePackageDetails(testData, expectedAppointmentDetails);
expectedAppointmentDetails = await this.getExpectedServiceLocation(testData, expectedAppointmentDetails); expectedAppointmentDetails = await this.getExpectedServiceLocation(testData, expectedAppointmentDetails);
expectedAppointmentDetails = await this.getExpectedAppointmentDate(testData, expectedAppointmentDetails); expectedAppointmentDetails = await this.getExpectedAppointmentDate(testData, expectedAppointmentDetails);
expectedAppointmentDetails = await this.getExpectedCustomerDetails(testData, expectedAppointmentDetails); expectedAppointmentDetails = await this.getExpectedCustomerDetails(testData, expectedAppointmentDetails);
@ -403,7 +403,7 @@ l
const { appointmentDetails } = testData; const { appointmentDetails } = testData;
let serviceLocationTitle = appointmentDetails?.serviceLocation == ServiceLocation.Mobile let serviceLocationTitle = appointmentDetails?.serviceLocation == ServiceLocation.Mobile
? "We're coming to you" ? "We're coming to you"
: "You're going to a Safelite shop"; : "You're coming to us";
let serviceLocation: string[] = []; let serviceLocation: string[] = [];
serviceLocation.push( serviceLocation.push(
@ -443,7 +443,7 @@ l
customerDetails?.apptDuration ? "Estimated appointment length: " + customerDetails.apptDuration : "" customerDetails?.apptDuration ? "Estimated appointment length: " + customerDetails.apptDuration : ""
); );
expectedServicePackageDetails["Appointment date + time"] = appointmentDateText; expectedServicePackageDetails["Appointment Date + Time"] = appointmentDateText;
return expectedServicePackageDetails; return expectedServicePackageDetails;
} }
@ -452,7 +452,7 @@ l
let customerDetailsText: string[] = []; let customerDetailsText: string[] = [];
customerDetailsText.push( customerDetailsText.push(
customerDetails?.firstName.toUpperCase() + " " + customerDetails?.lastName.toUpperCase(), // customerDetails?.firstName.toUpperCase() + " " + customerDetails?.lastName.toUpperCase(),
customerDetails?.email ? customerDetails?.email.toUpperCase() : "", customerDetails?.email ? customerDetails?.email.toUpperCase() : "",
customerDetails?.phoneNumber ? customerDetails?.phoneNumber : "", customerDetails?.phoneNumber ? customerDetails?.phoneNumber : "",
"Opted out of text message updates" "Opted out of text message updates"

View file

@ -24,8 +24,8 @@ export class PaypalPage extends BasePage {
this.passwordTextBox = page.getByPlaceholder('Password'); this.passwordTextBox = page.getByPlaceholder('Password');
this.paypalLoginButton = page.getByRole('button', { name: 'Log In', exact: true }); this.paypalLoginButton = page.getByRole('button', { name: 'Log In', exact: true });
this.completePurchaseButton = page.getByTestId('submit-button-initial') this.completePurchaseButton = page.getByTestId('submit-button-initial')
this.payWithRadioButton = page.locator('.py-4').first(); this.payWithRadioButton = page.getByRole('button').filter({ hasText: 'Pay with' });
this.payButton = page.getByRole('button', { name: 'Pay $' }); this.payButton = page.locator('#one-time-cta');
} }
async completePaypalPurchase(paymentDetails: IPaymentDetails){ async completePaypalPurchase(paymentDetails: IPaymentDetails){
@ -45,7 +45,8 @@ export class PaypalPage extends BasePage {
await this.passwordTextBox.fill(paymentDetails.password!); await this.passwordTextBox.fill(paymentDetails.password!);
await this.paypalLoginButton.click(); await this.paypalLoginButton.click();
await this.payWithRadioButton.click(); await this.payWithRadioButton.click();
await this.payButton.click(); await this.page.waitForTimeout(2000); // wait for 2 seconds to ensure the Pay button is clickable
await this.payButton.dblclick();
} }
} }
} }

View file

@ -17,8 +17,11 @@ export class SchedulePage extends BasePage {
readonly militaryWarningMessage: Locator; readonly militaryWarningMessage: Locator;
readonly changeZipButton: Locator; readonly changeZipButton: Locator;
readonly updateZipTextBox: Locator; readonly updateZipTextBox: Locator;
readonly safeliteShopsList: Locator;
readonly searchButton: Locator;
readonly storeAddressText: Locator;
readonly saveLocationButton: Locator; readonly saveLocationButton: Locator;
readonly changeShopLocationLink: Locator; readonly moreLocationsButton: Locator;
readonly selectAShopOptions: Locator; readonly selectAShopOptions: Locator;
readonly yourSafeliteShop: Locator; readonly yourSafeliteShop: Locator;
@ -44,12 +47,15 @@ export class SchedulePage extends BasePage {
this.militaryWarningMessage = this.page.locator('[class*="widget-name-AlertMilitaryBaseZipWidget"]'); this.militaryWarningMessage = this.page.locator('[class*="widget-name-AlertMilitaryBaseZipWidget"]');
this.changeZipButton = this.page.locator('a:has(span.sr-only:has-text("edit zip code"))'); this.changeZipButton = this.page.locator('a:has(span.sr-only:has-text("edit zip code"))');
this.updateZipTextBox = this.page.locator('#serviceZipCode').filter({ visible: true }); this.updateZipTextBox = this.page.locator('#serviceZipCode').filter({ visible: true });
this.safeliteShopsList = this.page.locator('fieldset:has(#chooseShop) label');
this.searchButton = this.page.locator('.search-icon-button');
this.storeAddressText = this.page.locator('#shop-location label:not(:first-child)');
this.saveLocationButton = this.page.getByText('Save location', { exact: true }); this.saveLocationButton = this.page.getByText('Save location', { exact: true });
this.changeShopLocationLink = this.page.locator(".shop-question a").filter({ hasText: "Change shop location " }); this.moreLocationsButton = this.page.locator(".shop-question a").filter({ hasText: "More locations " });
this.selectAShopOptions = this.page.locator('[class="shop-question"]'); this.selectAShopOptions = this.page.locator('[class="shop-question"]');
this.yourSafeliteShop = this.page.locator("fieldset:has(#chooseShop) label"); this.yourSafeliteShop = this.page.locator("fieldset:has(#chooseShop) label");
this.allDayDropOffButton = this.page.locator("label[buttonlabel='Drop off all day']"); this.allDayDropOffButton = this.page.locator("label[buttonlabel*='Drop']");
this.pickATimeButton = this.page.locator("label[buttonlabel='Pick a time']"); this.pickATimeButton = this.page.locator("label[buttonlabel='Pick a time']");
this.firstAvailableDate = this.page.locator('.selectable-day').filter({ visible: true}).locator('nth=0'); this.firstAvailableDate = this.page.locator('.selectable-day').filter({ visible: true}).locator('nth=0');
this.firstAvailableTime = this.page.locator('label').filter({ hasText: /AM|PM/ }).locator('div').locator('nth=0'); this.firstAvailableTime = this.page.locator('label').filter({ hasText: /AM|PM/ }).locator('div').locator('nth=0');
@ -58,7 +64,7 @@ export class SchedulePage extends BasePage {
this.dateText = this.page.locator('label.modal-title'); this.dateText = this.page.locator('label.modal-title');
this.viewMoreDatesLink = this.page.getByText(/View more dates/).first(); this.viewMoreDatesLink = this.page.getByText(/View more dates/).first();
this.appointmentDuration = this.page.locator('.duration-text-block'); this.appointmentDuration = this.page.locator('.duration-text-block');
this.timeSlots = this.page.locator('fieldset[aria-labelledby=\'chooseTimeSlot\'] label'); this.timeSlots = this.page.locator('fieldset:has(>legend#chooseTimeSlot) label').filter({ visible: true});
} }
async selectLocation(testData: Partial<ITestData>) { async selectLocation(testData: Partial<ITestData>) {
@ -76,25 +82,36 @@ export class SchedulePage extends BasePage {
} }
async scheduleInShop(appointmentDetails?: IAppointmentDetails) { async scheduleInShop(appointmentDetails?: IAppointmentDetails) {
await this.inShopButton.click();
await this.inShopButton.isVisible() ? await this.inShopButton.click() : null;
if (appointmentDetails && appointmentDetails.shopAddress) { if (appointmentDetails && appointmentDetails.shopAddress) {
const zipCodeMatch = appointmentDetails.shopAddress.match(/\b\d{5}$/); const zipCodeMatch = appointmentDetails.shopAddress.match(/\b\d{5}$/);
if (zipCodeMatch) { if (zipCodeMatch) {
const zipCode = zipCodeMatch[0]; const zipCode = zipCodeMatch[0];
// Enter the ZIP code into the updateZipTextBox // Enter the ZIP code into the updateZipTextBox
await this.changeShopLocationLink.click(); await this.moreLocationsButton.click();
await this.page.waitForTimeout(500); await this.page.waitForTimeout(500);
await this.updateZipTextBox.fill(zipCode); await this.updateZipTextBox.fill(zipCode);
await this.searchButton.click().then( async() => await this.page.locator('loader-wrapper').waitFor({state: 'hidden'}));
await this.safeliteShopsList.first().click();
// Click the save location button // Click the save location button
await this.saveLocationButton.click(); await this.saveLocationButton.click();
} }
await this.inShopButton.click(); // await this.inShopButton.click();
// await this.selectAShopOptions.locator(`[buttonbodycopy="${appointmentDetails.shopAddress}"]`).scrollIntoViewIfNeeded().then(() => this.selectAShopOptions.locator(`[buttonbodycopy="${appointmentDetails.shopAddress}"]`).click()); // await this.selectAShopOptions.locator(`[buttonbodycopy="${appointmentDetails.shopAddress}"]`).scrollIntoViewIfNeeded().then(() => this.selectAShopOptions.locator(`[buttonbodycopy="${appointmentDetails.shopAddress}"]`).click());
} }
if (appointmentDetails && (appointmentDetails.shopAddress === "" || appointmentDetails.shopAddress === undefined )) { if (appointmentDetails && (appointmentDetails.shopAddress === "" || appointmentDetails.shopAddress === undefined )) {
appointmentDetails.shopAddress = await this.yourSafeliteShop.getAttribute("buttonbodycopy") || ""; appointmentDetails.shopAddress = '';
await this.storeAddressText.first().waitFor({ state: 'visible' });
const elements = await this.storeAddressText.all();
for (let element of elements) {
let addressLine = await element.textContent() || "";
appointmentDetails.shopAddress += addressLine + ", "
};
appointmentDetails.shopAddress = appointmentDetails.shopAddress?.slice(0, -2);
} }
} }
@ -146,6 +163,7 @@ export class SchedulePage extends BasePage {
const inshopAvailableDates = this.page.locator('.selectable-day').filter({ visible: true}).all(); const inshopAvailableDates = this.page.locator('.selectable-day').filter({ visible: true}).all();
for (const inshopAvailableDate of await inshopAvailableDates) { for (const inshopAvailableDate of await inshopAvailableDates) {
await inshopAvailableDate.click(); await inshopAvailableDate.click();
await this.page.waitForTimeout(500);
if (await this.allDayDropOffButton.isVisible()) { if (await this.allDayDropOffButton.isVisible()) {
customerDetails!.apptDate = `${await inshopAvailableDate.getAttribute("id")}`; customerDetails!.apptDate = `${await inshopAvailableDate.getAttribute("id")}`;
break; break;
@ -186,13 +204,13 @@ export class SchedulePage extends BasePage {
} }
// appointmentmentDetails.serviceLocation === ServiceLocation.DropOff ? await this.dropOffButton.click() : await this.firstAvailableTime.click(); // appointmentmentDetails.serviceLocation === ServiceLocation.DropOff ? await this.dropOffButton.click() : await this.firstAvailableTime.click();
customerDetails!.apptDuration = (await this.appointmentDuration.innerText()).replace("Duration: ", ""); customerDetails!.apptDuration = (await this.appointmentDuration.innerText()).replace("Your service will take approximately ", "").replace("Duration: ", "");
} }
async getFormattedTimeSlot(timeSlot: Locator) { async getFormattedTimeSlot(timeSlot: Locator) {
const selectedTimeSlot = await timeSlot.innerText(); const selectedTimeSlot = await timeSlot.innerText();
let formattedTimeSlot: string = ""; let formattedTimeSlot: string = "";
if (selectedTimeSlot.toLowerCase().includes("drop off")) if (selectedTimeSlot.toLowerCase().includes("drop"))
{ {
formattedTimeSlot = selectedTimeSlot.includes("overnight") ? "drop off by 5:30 pm on the night of your scheduled appointment. Pick-up time dependent on shop schedule" : "drop off before 9:30 AM"; formattedTimeSlot = selectedTimeSlot.includes("overnight") ? "drop off by 5:30 pm on the night of your scheduled appointment. Pick-up time dependent on shop schedule" : "drop off before 9:30 AM";
} }

View file

@ -37,7 +37,7 @@ export class ServicePackagesPage extends BasePage {
this.iHavePromoCodeButton = this.page.getByRole('link', { name: 'I have a promo code' }); this.iHavePromoCodeButton = this.page.getByRole('link', { name: 'I have a promo code' });
this.skipQuoteEmailButton = this.page.getByRole('button', { name: 'Skip' }); this.skipQuoteEmailButton = this.page.getByRole('button', { name: 'Skip' });
this.emailInput = this.page.getByRole('textbox', { name: 'Enter your email address' }); this.emailInput = this.page.getByRole('textbox', { name: 'Enter your email address' });
this.getMyQuoteButton = this.page.getByRole('button', { name: 'Get my quote' }); this.getMyQuoteButton = this.page.getByRole('button', { name: 'Send' });
this.closeButton = this.page. getByRole('dialog').locator('button').filter({ hasText: 'Close' }); this.closeButton = this.page. getByRole('dialog').locator('button').filter({ hasText: 'Close' });
this.promoCodeTextbox = this.page.getByLabel('Enter a promo code'); this.promoCodeTextbox = this.page.getByLabel('Enter a promo code');
this.applyPromoButton = this.page.getByRole('button', { name: 'Apply promo code' }); this.applyPromoButton = this.page.getByRole('button', { name: 'Apply promo code' });

View file

@ -39,9 +39,9 @@ export class VehicleDamagePage extends BasePage {
super(page); super(page);
this.page = page; this.page = page;
this.windshieldChkBox = this.page.locator('[buttonlabel="Windshield"]'); this.windshieldChkBox = this.page.locator('[buttonlabel="Windshield"]');
this.crackButton = this.page.locator('[buttonlabel="Crack"]'); this.crackButton = this.page.locator('[buttonlabelsubcopy="Replace my windshield"]');
this.chipButton = this.page.locator('[buttonlabel="Chip(s)"]'); this.chipButton = this.page.locator('[buttonlabelsubcopy="Repair my windshield"]');
this.sideDoorButton = this.page.locator('[buttonlabel="Side door"]'); this.sideDoorButton = this.page.locator('[buttonlabel="Side window"]');
this.driverSideButton = this.page.locator('[buttonlabel="Driver side"]'); this.driverSideButton = this.page.locator('[buttonlabel="Driver side"]');
this.passengerSideButton = this.page.locator('[buttonlabel="Passenger side"]'); this.passengerSideButton = this.page.locator('[buttonlabel="Passenger side"]');
this.driverQuarterPanelChkBox = this.page.locator('[aria-labelledby="driverSideOptions"]').locator('[buttonlabel="Quarter panel"]'); this.driverQuarterPanelChkBox = this.page.locator('[aria-labelledby="driverSideOptions"]').locator('[buttonlabel="Quarter panel"]');

View file

@ -42,14 +42,6 @@ const cashReplaceGlassAddressLookupInshopAfterPayData: Partial<ITestData> = {
vehicleLookupType: VehicleLookupType.Address vehicleLookupType: VehicleLookupType.Address
}, },
// No need to override vehicleDamage as it already defaults to WindshieldCrack
// Override appointment details
appointmentDetails: {
...getDefaultTestData().appointmentDetails!,
shopAddress: "6826 Sawmill Rd, Columbus, OH 43235"
},
// Override payment details // Override payment details
paymentDetails: ClientData.getDefaultAfterpayDetails() paymentDetails: ClientData.getDefaultAfterpayDetails()
} }

View file

@ -35,7 +35,7 @@ const cashReplaceMultiSlidingGlassDropoffData: Partial<ITestData> = {
appointmentDetails: { appointmentDetails: {
serviceLocation: ServiceLocation.DropOff, serviceLocation: ServiceLocation.DropOff,
appointmentDate: getDefaultTestData().appointmentDetails?.appointmentDate, appointmentDate: getDefaultTestData().appointmentDetails?.appointmentDate,
shopAddress: "6826 Sawmill Rd, Columbus, OH 43235" shopAddress: "6826 sawmill rd, Columbus, OH 43235"
}, },
// Override vehicle details // Override vehicle details

View file

@ -20,7 +20,7 @@ const cashReplaceWiperDropoffData: Partial<ITestData> = {
servicePackage: ServicePackage.Standard, servicePackage: ServicePackage.Standard,
// Special flags // Special flags
isSkipEstimatePage: true, isSkipEstimatePage: false,
isRecalVehicle: true, isRecalVehicle: true,
// Override customer postal code // Override customer postal code
@ -28,7 +28,7 @@ const cashReplaceWiperDropoffData: Partial<ITestData> = {
...getDefaultTestData().customerDetails!, ...getDefaultTestData().customerDetails!,
address: { address: {
...getDefaultTestData().customerDetails!.address, ...getDefaultTestData().customerDetails!.address,
postalCode: '43085' postalCode: '43235'
} }
}, },