DigitalConsumer.FixMyGlass/playwright-tests/pages/ServicePackagesPage.ts
maguire-arman 458e552d6b Removes URL declaration from page classes
Removes the explicit URL declaration from the page classes.

The URL validation and navigation are now handled directly within the tests, providing greater flexibility and control over test execution and removing the need to manage URLs within each page object.
2025-07-14 16:13:56 -04:00

241 lines
No EOL
10 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { expect, type Locator, type Page } from '@playwright/test';
import { BasePage } from './BasePage';
import { ServicePackage, VehicleDamage } from 'safelite-playwright-core';
import { ProgressBarPercentages } from 'framework/localTypes/Enums';
import { PaymentMethod } from "framework/localTypes/Enums";
import { step } from 'framework/localTypes/Step';
import { ITestData } from 'framework/TestData';
export class ServicePackagesPage extends BasePage {
readonly page: Page;
readonly standardPackageButton: Locator;
readonly premiumPackageButton: Locator;
readonly glassOnlyButton: Locator;
readonly payOnMyOwnButton: Locator;
readonly paywithInsuranceButton: Locator;
readonly iHavePromoCodeButton: Locator;
//Your quote is almost ready modal
readonly skipQuoteEmailButton: Locator;
readonly emailInput: Locator;
readonly getMyQuoteButton: Locator;
readonly closeButton: Locator;
//Enter a promo code modal
readonly promoCodeTextbox: Locator;
readonly applyPromoButton: Locator;
readonly repeatedClicksModalCloseButton: Locator;
constructor(page: Page) {
super(page);
this.page = page;
this.standardPackageButton = this.page.getByText('Standard');
this.premiumPackageButton = this.page.getByText('Premium');
this.glassOnlyButton = this.page.getByText('Glass service only', { exact: true });
this.payOnMyOwnButton = this.page.locator('label').filter({ hasText: 'Pay on my own' }).locator('div');
this.paywithInsuranceButton = this.page.locator('label').filter({ hasText: 'Pay with insurance' }).locator('div');
this.iHavePromoCodeButton = this.page.getByRole('link', { name: 'I have a promo code' });
this.skipQuoteEmailButton = this.page.getByRole('button', { name: 'Skip' });
this.emailInput = this.page.getByRole('textbox', { name: 'Enter your email address' });
this.getMyQuoteButton = this.page.getByRole('button', { name: 'Get my quote' });
this.closeButton = this.page. getByRole('dialog').locator('button').filter({ hasText: 'Close' });
this.promoCodeTextbox = this.page.getByLabel('Enter a promo code');
this.applyPromoButton = this.page.getByRole('button', { name: 'Apply promo code' });
this.repeatedClicksModalCloseButton = this.page.locator('.QSISlider').locator('img[src*=\'close\']');
}
async selectPaymentMethod(method: PaymentMethod): Promise<void> {
const locators = {
[PaymentMethod.Insurance]: this.paywithInsuranceButton,
[PaymentMethod.SelfPay]: this.payOnMyOwnButton
}
await locators[method].click();
}
async selectServicePackage(servicePackage: ServicePackage): Promise<void> {
const locators = {
[ServicePackage.GlassOnly]: this.glassOnlyButton,
[ServicePackage.Premium]: this.premiumPackageButton,
[ServicePackage.Standard]: this.standardPackageButton,
}
if (servicePackage != null) {
await locators[servicePackage].click();
}
}
async handleQuotePopup(email?: string): Promise<void> {
await this.emailInput.waitFor({ state: 'visible' });
if (await this.emailInput.isVisible()) {
if (email) {
await this.emailInput.fill(email);
await this.getMyQuoteButton.click();
if (await this.repeatedClicksModalCloseButton.isVisible()) {
await this.repeatedClicksModalCloseButton.click();
}
await this.page.waitForTimeout(3000);
await this.closeButton.click();
} else {
await this.skipQuoteEmailButton.click();
}
}
}
async enterPromo(promoCode: string): Promise<void> {
await this.iHavePromoCodeButton.click();
await this.promoCodeTextbox.fill(promoCode);
await this.applyPromoButton.click();
}
async verifyCanNotRecal(): Promise<boolean> {
// Get Vuex state from localStorage
const vuexState = JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')'));
// Validate in the backend to make sure the can safelite recalibrate data is correct
if (vuexState.order?.lineItems?.glassParts?.length > 0) {
for (const glassPart of vuexState.order.lineItems.glassParts) {
await expect(glassPart.canSafeliteRecalibrate).toBe(false);
await expect(glassPart.requiresRecalibration).toBe(true);
}
return true;
}
return false;
}
async verifyDynamicRecal(): Promise<void> {
// Get Vuex state from localStorage
const vuexState = JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')'));
// Validate in the backend to make sure the dynamic recalibration part is present
if (vuexState.order?.lineItems?.glassParts?.length > 0) {
const hasDynamicRecalPart = vuexState.order.lineItems.glassParts.some(glassPart =>
glassPart.childParts.some(childPart =>
childPart.partNumber.includes("RECAL DYNAMIC")
)
);
await expect(hasDynamicRecalPart).toBe(true);
console.log("Recal part line item is verified");
} else {
throw new Error("No glass parts found in the order");
}
}
async verifyIsRepair(isRepair: boolean, vehicleDamage: VehicleDamage[]): Promise<void> {
// Get Vuex state from localStorage
const vuexState = JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')'));
// Validate in the backend to make sure isRepair is aligned with the data
if (isRepair) {
await expect(vuexState.order.damage.isRepair).toBe(true);
// Check the number of chips based on vehicleDamage array
if (vehicleDamage.includes(VehicleDamage.WindshieldOneChip)) {
await expect(vuexState.order.damage.numberOfChips).toBe(1);
}
if (vehicleDamage.includes(VehicleDamage.WindshieldTwoChips)) {
await expect(vuexState.order.damage.numberOfChips).toBe(2);
}
if (vehicleDamage.includes(VehicleDamage.WindshieldThreeChips)) {
await expect(vuexState.order.damage.numberOfChips).toBe(3);
}
} else {
await expect(vuexState.order.damage.isRepair).toBe(false);
}
}
async verifyVehicleParts(vehicleDamage: VehicleDamage[]): Promise<void> {
// Get Vuex state from localStorage
const vuexState = JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')'));
// Validate the presence of specific parts in the glassParts array
const glassParts = vuexState.order?.lineItems?.glassParts;
const WINDSHIELD_TYPES = [
  "SINGLE WINDSHIELD",
  "DRIVER SPLIT WINDSHIELD",
  "PASSENGER SPLIT WINDSHIELD"
];
if (glassParts?.length > 0) {
for (const partType of vehicleDamage) {
// Normalize part type to "WINDSHIELD" if it matches any of the defined types (Split Windshield types)
  const normalizedPartType = WINDSHIELD_TYPES.includes(partType) ? "WINDSHIELD" : partType;
const hasPartType = glassParts.some(glassPart => glassPart.partType === normalizedPartType);
await expect(hasPartType, `Expected part type: ${partType}`).toBe(true);
}
} else {
throw new Error("No glass parts found in the order");
}
}
async verifyOEMPart(): Promise<void> {
// Get Vuex state from localStorage
const vuexState = JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')'));
// Validate the presence of an OEM part
if (vuexState.order?.lineItems?.glassParts?.length > 0) {
const firstGlassPartNumber = vuexState.order.lineItems.glassParts[0].partNumber;
await expect(firstGlassPartNumber.includes("OEM")).toBe(true);
} else {
throw new Error("No glass parts found in the order");
}
}
@step("ServicePackagePage >> Select Payment Method and Service Type: ")
async handleServicePackagePage(testData: Partial<ITestData>) {
const { customerDetails, paymentMethod, paymentDetails, servicePackage, isCanNotRecal, isDynamicRecal, hasOemEndorsement, vehicleDamage } = testData;
await this.validateProgressBar(ProgressBarPercentages.ServicePackagePage);
// Define repair damage types (vs. replacement types)
const repairTypes: VehicleDamage[] = [
VehicleDamage.WindshieldOneChip,
VehicleDamage.WindshieldTwoChips,
VehicleDamage.WindshieldThreeChips
];
// Determine if we're replacing or repairing
const isReplace = !repairTypes.some(damageType => {
return vehicleDamage!.includes(damageType);
});
await this.handleQuotePopup(customerDetails!.email!);
await this.selectPaymentMethod(paymentMethod!);
await this.selectServicePackage(servicePackage!);
// Enter promo code
if (paymentDetails?.promoCode) {
await this.enterPromo(paymentDetails.promoCode);
}
// Backend Validations
// Validate backend for can not recal if applicable
if (isCanNotRecal) {
await this.verifyCanNotRecal();
}
// Validate backend for dynamic recal if applicable
if (isDynamicRecal) {
await this.verifyDynamicRecal();
}
// Validate backend for repair info (including chip verification)
await this.verifyIsRepair(!isReplace, vehicleDamage!);
if (isReplace) {
// Validate backend for parts info
await this.verifyVehicleParts(vehicleDamage!);
}
// Validate backend for OEM endorsement
if (hasOemEndorsement) {
await this.verifyOEMPart();
}
await this.nextPage();
}
}