DigitalConsumer.ISS/playwright-tests/pages/OrderConfirmationPage.ts
2026-04-21 15:00:36 -04:00

209 lines
No EOL
12 KiB
TypeScript

import test, { expect, type Locator, type Page } from '@playwright/test';
import { BasePage } from './BasePage';
import { ICustomerDetails, IVehicleDetails, IAddressVehicleDetails } from '@business-logic/types/CustomerDetails';
import { PaymentType, ServicePackage } from '@business-logic/types/Enums';
import { ITestData } from '@business-logic/types/ITestData';
export class OrderConfirmationPage extends BasePage {
readonly page: Page;
readonly successCheckmark: Locator;
readonly successHeader: Locator;
readonly serviceText: Locator;
readonly workOrderNumberText: Locator;
readonly confirmationText: Locator;
readonly appointmentTypeText: Locator;
readonly apptDetailsText: Locator;
readonly deductibleText: Locator;
readonly subtotalText: Locator;
readonly totalAmountDueText: Locator;
readonly amountPaidText: Locator;
readonly finalAmountDue: Locator;
readonly cartLineItemsText: Locator;
readonly unverifiedCoverageDeductibleText: Locator;
issPageValue = 'order-confirmation';
constructor(page: Page) {
super(page);
this.page = page;
this.successCheckmark = this.page.locator('img[src*="-checkmark.svg"]');
this.successHeader = this.page.locator('[class="header-text"]');
this.confirmationText = this.page.locator('[class="subheader-text"]', { hasText: 'Your appointment is on Safelite\'s schedule and your confirmation email is on the way.' });
this.serviceText = this.page.locator('[class="service-description confirmation-section"]');
this.workOrderNumberText = this.page.locator('[class="work-order-description"]');
this.appointmentTypeText = this.page.locator('[class="appointment-title"]');
this.apptDetailsText = this.page.locator('[class="appointment-details confirmation-section"]');
// Order details section
this.deductibleText = this.page.locator('span#deductible-value, span.deductible-value');
this.subtotalText = this.page.locator('#subtotal-value');
this.totalAmountDueText = this.page.locator('#total-value');
this.amountPaidText = this.page.locator('#amount-paid-value');
this.finalAmountDue = this.page.locator('#bottom-amount-due-value');
this.cartLineItemsText = this.page.locator('.cart-item-list');
this.unverifiedCoverageDeductibleText = this.page.locator('span#deductible-value', { hasText: 'Verifying coverage' });
}
async validateOrderConfirmationPage(testData: Partial<ITestData>) {
// Destructure data we use
const { vehicleDetails, servicePackage, isItac,
isNoComp, isPolicyFound, claimDetails, paymentDetails, isUseVehicleFromAddressLookup, addressVehicleDetails } = testData;
// Grab text
const serviceTextValue = await this.serviceText.textContent();
const apptDetailsValue = await this.apptDetailsText.textContent();
const appointmentTypeValue = await this.appointmentTypeText.textContent();
// Derived conditions
const isItacOrNoComp = !!(isItac || isNoComp);
const isUnverified = !isPolicyFound || claimDetails!.policyDeductible === -1;
const isUnverifiedPolicyAfterVehicleLookup = testData.isUnverifiedPolicyAfterVehicleLookup === true;
const hasPremiumOrStandard =
(claimDetails!.policyDeductible >= 0 || !isPolicyFound) &&
(servicePackage === ServicePackage.Premium || servicePackage === ServicePackage.Standard);
const payAtService =
(claimDetails!.policyDeductible >= 0 &&
paymentDetails?.paymentType === PaymentType.PayAtService) || !paymentDetails?.paymentType;
const paidWithPIA =
claimDetails!.policyDeductible >= 0 &&
paymentDetails?.paymentType !== PaymentType.PayAtService;
// Helper: grab textContent only when the element is expected on-screen
const textIf = async (condition: boolean, locator: Locator) =>
condition ? await locator.textContent() : null;
// Grab text content for elements that are expected on-screen
const lineItemsValue = await textIf(hasPremiumOrStandard, this.cartLineItemsText);
const deductibleTextValue = await textIf(!isItacOrNoComp, this.deductibleText);
const subtotalTextValue = await textIf((hasPremiumOrStandard || isItacOrNoComp) && !isUnverified && !isUnverifiedPolicyAfterVehicleLookup, this.subtotalText);
const totalAmountDueValue = await textIf((hasPremiumOrStandard && !payAtService) && (isItacOrNoComp && !payAtService) && !isUnverified && !isUnverifiedPolicyAfterVehicleLookup, this.totalAmountDueText);
const amountPaidTextValue = await textIf((hasPremiumOrStandard && !payAtService) && (isItacOrNoComp && !payAtService) && !isUnverified && !isUnverifiedPolicyAfterVehicleLookup, this.totalAmountDueText);
const finalAmountDueValue = await textIf((hasPremiumOrStandard && isItacOrNoComp) || (hasPremiumOrStandard && isUnverified), this.finalAmountDue);
const confirmationTextValue = await this.confirmationText.textContent();
// General Validations
expect.soft(this.serviceText).toBeVisible();;
expect.soft(this.successHeader).toBeVisible();
expect.soft(confirmationTextValue).toContain('Your appointment is on Safelite\'s schedule and your confirmation email is on the way.');
if (isUseVehicleFromAddressLookup) {
expect.soft(serviceTextValue).toContain(`${addressVehicleDetails!.year} ${addressVehicleDetails!.make} ${addressVehicleDetails!.model}`);
} else {
expect.soft(serviceTextValue).toContain(`${vehicleDetails!.year} ${vehicleDetails!.make} ${vehicleDetails!.model}`);
}
// Validate order number and appointment type header
await this.validateOrderNumber();
await this.validateAppointmentTypeHeader();
// Validate line items
if (servicePackage === ServicePackage.Premium) {
expect.soft(lineItemsValue).toContain('Front advanced beam blades');
expect.soft(lineItemsValue).toContain('Safelite Rain Repellent Treatment');
} else {
if (servicePackage === ServicePackage.Standard) {
expect.soft(lineItemsValue).toContain('Front advanced beam blades');
}
}
if (isPolicyFound && (claimDetails!.policyDeductible === 0 && (servicePackage === ServicePackage.GlassOnly))) {
const zeroDeductibleText = await this.deductibleText.textContent();
expect.soft(zeroDeductibleText).toEqual('$0.00');
} else if (isPolicyFound && claimDetails!.policyDeductible > 0 && !(isItac || isNoComp) && !isUnverifiedPolicyAfterVehicleLookup && servicePackage === ServicePackage.GlassOnly){
expect.soft(deductibleTextValue).not.toBeNull();
const deductibleAmt = deductibleTextValue ? Number.parseFloat(deductibleTextValue.split('$')[1].replaceAll(',', '')) : 0;
expect.soft(deductibleAmt).toEqual(claimDetails!.policyDeductible);
} else if (isPolicyFound && claimDetails!.policyDeductible > 0 && !(isItac || isNoComp) && !isUnverifiedPolicyAfterVehicleLookup && hasPremiumOrStandard && !payAtService) {
// Policy found, deductible > 0, Standard/Premium package, paid with PIA (not pay-at-service)
expect.soft(deductibleTextValue).not.toBeNull();
const deductibleAmt = deductibleTextValue ? Number.parseFloat(deductibleTextValue.split('$')[1].replaceAll(',', '')) : 0;
expect.soft(deductibleAmt).toEqual(claimDetails!.policyDeductible);
const totalAmountDueAmt = totalAmountDueValue ? Number.parseFloat(totalAmountDueValue!.split('$')[1].replaceAll(',', '')) : null;
const amountPaidValue = amountPaidTextValue ? Number.parseFloat(amountPaidTextValue!.split('$')[1].replaceAll(',', '')) : null;
expect.soft(amountPaidValue).toEqual(totalAmountDueAmt);
} else if ((isPolicyFound && isItac && (isNoComp && paymentDetails!.paymentType === PaymentType.PayAtService)) && !isUnverifiedPolicyAfterVehicleLookup && finalAmountDueValue !== null && subtotalTextValue !== null) {
// Extract numbers
const deductibleAmt = deductibleTextValue ? Number.parseFloat(deductibleTextValue.split('$')[1].replaceAll(',', '')) : null;
const subtotalAmt = subtotalTextValue ? Number.parseFloat(subtotalTextValue!.split('$')[1].replaceAll(',', '')) : null;
//const totalAmountDueAmt = Number.parseFloat(totalAmountDueValue!.split('$')[1].replaceAll(',', ''));
const finalAmountDueAmt = finalAmountDueValue ? Number.parseFloat(finalAmountDueValue!.split('$')[1].replaceAll(',', '')) : null;
if (!(isItac || isNoComp)) {
expect.soft(deductibleAmt).toEqual(claimDetails!.policyDeductible);
} else {
expect.soft(subtotalAmt).toBeGreaterThan(0);
//expect.soft(deductibleAmt).toEqual(0);
}
if ((paymentDetails!.paymentType === PaymentType.PayAtService) && (claimDetails!.policyDeductible >= 0 && (servicePackage === ServicePackage.Standard || servicePackage === ServicePackage.Premium))) {
expect.soft(finalAmountDueAmt).toBeGreaterThan(0);
} else {
const totalAmountDueAmt = Number.parseFloat(totalAmountDueValue!.split('$')[1].replaceAll(',', ''));
const amountPaidValue = Number.parseFloat(amountPaidTextValue!.split('$')[1].replaceAll(',', ''));
// Verify amount due 0 usually when PIA is selected
expect.soft(amountPaidValue).toEqual(totalAmountDueAmt);
expect.soft(finalAmountDueAmt).toEqual(0);
}
} else if (isUnverified && servicePackage === ServicePackage.GlassOnly) {
expect.soft(deductibleTextValue).toEqual('Verifying coverage');
} else {
if (isUnverified && servicePackage !== ServicePackage.GlassOnly) {
// essential flows will have deductible and amount due as "Verifying coverage";
expect.soft(deductibleTextValue).toEqual('Verifying coverage');
expect.soft(finalAmountDueValue).toEqual('Verifying coverage');
}
}
}
async validateOrderNumber() {
const sessionStorage = JSON.parse(await this.page.evaluate('sessionStorage.getItem(\'submittedOrder\')'));
const workOrderNumber = sessionStorage.workOrderNumber;
const workOrderNumberValue = await this.workOrderNumberText.textContent();
expect.soft(workOrderNumberValue).toEqual(workOrderNumber);
}
async validateAppointmentTypeHeader() {
const sessionStorage = JSON.parse(await this.page.evaluate('sessionStorage.getItem(\'submittedOrder\')'));
const appointmentType = sessionStorage.serviceLocation.appointmentType;
const appointmentTypeValue = await this.appointmentTypeText.textContent();
const apptDetailsValue = await this.apptDetailsText.textContent();
if (appointmentTypeValue?.includes('going to a Safelite shop') && apptDetailsValue?.includes('Drop off by 9:30 AM. Pick-up time dependent on shop schedule')) {
expect.soft(appointmentType).toEqual('Dropoff');
} else if (appointmentTypeValue?.includes('going to a Safelite shop')) {
expect.soft(appointmentType).toEqual('Inshop');
} else {
expect.soft(appointmentType).toEqual('Mobile');
}
}
async validateURL() {
let pass = false;
let currentUrl = this.page.url();
pass = currentUrl.includes(this.issPageValue);
while(!pass && currentUrl.includes('payment')) {
await this.waitForURLToChange(currentUrl);
currentUrl = this.page.url();
pass = currentUrl.includes(this.issPageValue);
}
const isBailout = currentUrl.includes('bailout-page');
if(!pass && !isBailout) {
await this.waitForURLToChange(currentUrl);
currentUrl = this.page.url();
}
expect(currentUrl).toContain(this.issPageValue);
}
}