DigitalConsumer.FixMyGlass/playwright-tests/pages/PaymentMethodPage.ts
2026-03-16 10:25:30 -04:00

579 lines
No EOL
29 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. 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 { IPaymentDetails, Soft, waitUntil } from 'safelite-playwright-core';
import { AppointmentTimeslot, ServiceLocation, PaymentType, ServicePackage, VehicleDamage, } from 'safelite-playwright-core';
import { PaymentMethod, ProgressBarPercentages } from 'framework/localTypes/Enums';
import { PaymentPage } from './PaymentPage';
import { AfterpayPage } from './AfterpayPage';
import { PaypalPage } from './PaypalPage';
import { ITestData } from 'framework/TestData';
import { step } from 'framework/localTypes/Step';
import { IExperiments } from 'framework/localTypes/IExperiments';
import { PaymentAdyenPage } from './PaymentAdyenPage';
export class PaymentMethodPage extends BasePage {
readonly page: Page;
readonly payAtServiceButton: Locator;
// readonly paypalButton: Locator;
// readonly creditCardButton: Locator;
readonly payNowButton: Locator;
readonly payWithInsuranceButton: Locator;
readonly payInFourButton: Locator;
readonly amountDueTextField: Locator;
readonly amountDueDropDown: Locator;
readonly appointmentDetailsSection: Locator;
readonly appointmentDetailsDropdown: Locator;
readonly subtotalAmountTextField: Locator;
readonly submitButton: Locator;
readonly recalibrationCheckbox: Locator;
readonly paymentPage: PaymentPage;
readonly paymentAdyenPage: PaymentAdyenPage;
readonly paypalPage: PaypalPage;
readonly afterpayPage: AfterpayPage
readonly afterPayBreakoutSection: Locator;
readonly afterPayToggle: Locator;
// Payment detail page validation locators
readonly reviewTable: Locator;
readonly serviceDetailsSection: Locator;
readonly serviceLocationDateandTimeSection: Locator;
readonly vehicleDamageLocationsSection: Locator;
readonly contactDetailsSection: Locator;
readonly cartPanelDetails: Locator;
readonly subtotalText: Locator;
readonly finalAmountDueText: Locator;
readonly glassServiceText: Locator;
readonly wiperBladesText: Locator;
readonly rainDefenseText: Locator;
readonly deductibleText: Locator;
constructor(page: Page) {
super(page);
this.page = page;
this.payAtServiceButton = this.page.locator('[buttonlabel="Pay at my appointment"]'); //this.page.getByText('Pay at time of service');
this.amountDueTextField = this.page.getByLabel('expand cart').locator('.amount-due');
this.amountDueDropDown = this.page.getByLabel('expand cart');
this.appointmentDetailsSection = this.page.locator('div .appt-details-snapshot');
this.appointmentDetailsDropdown = this.page.getByLabel('expand appointment details');
this.payWithInsuranceButton = this.page.locator('div').filter({ hasText: /^Pay with insurance$/ }).nth(1);
this.subtotalAmountTextField = this.page.locator('.sub-total span').nth(1);
this.payNowButton = this.page.locator('[buttonlabel="Pay now"]');
this.payInFourButton = this.page.locator('[buttonlabel="Pay in 4 installments"]');
this.submitButton = this.page.locator('[data-test-id="nav-bar-main-button"]');
this.recalibrationCheckbox = this.page.locator('label:has(>input[name=\'recalAckOptIn\'])');
// this.creditCardButton = page.locator('div').filter({ hasText: /^Credit or Debit$/ }).nth(1);
this.paymentPage = new PaymentPage(page);
this.paymentAdyenPage = new PaymentAdyenPage(page);
this.paypalPage = new PaypalPage(page);
this.afterpayPage = new AfterpayPage(page);
// Payment details validation locators
this.reviewTable = this.page.locator('div.review-table');
// Section locators - find by heading text
this.serviceLocationDateandTimeSection = this.page.locator('.review-table').locator('div .service-location');
this.vehicleDamageLocationsSection = this.page.locator('.review-table').locator('div.py-3[damagelocationswidgetname="DamageLocationsWidget"]');
this.contactDetailsSection = this.page.locator('.review-table').locator('div', { hasText: 'Contact details' }).first();
// Cart panel elements
this.cartPanelDetails = this.page.locator('.cart-panel');
this.subtotalText = this.page.locator('.sub-total');
this.finalAmountDueText = this.cartPanelDetails.locator('div.amount-due');
this.glassServiceText = this.page.locator('div', { hasText: /^Glass service only$/ });
this.wiperBladesText = this.page.locator('div', { hasText: /^New wiper blades$/ });
this.rainDefenseText = this.page.locator('div', { hasText: /^Rain Defense™$/ });
this.deductibleText = this.page.locator('#deductible-value');
this.afterPayBreakoutSection = this.page.locator('div.alert-info:has(.after-pay)');
this.afterPayToggle = this.page.locator('.after-pay-toggle');
}
async validatePaymentDetailsPage(testData: Partial<ITestData>) {
// Destructure data we use
const { vehicleDetails, customerDetails, servicePackage,
flow, claimDetails, paymentDetails, appointmentDetails,
isUseVehicleOnPolicy, paymentMethod, vehicleDamage } = testData;
// Wait for review table to be visible to ensure page is loaded
await this.appointmentDetailsDropdown.waitFor({ state: "visible" });
await this.appointmentDetailsDropdown.click().then(
async () => {
await waitUntil(async () => {
return (await this.page.locator('.review-toggle.expanded').evaluate(el => window.getComputedStyle(el, ':after').transform)) === 'matrix(-1, 0, 0, -1, 0, 0)';
});
}
);
let expectedAppointmentDetails = new Map<string, string[]>();
expectedAppointmentDetails = await this.getExpectedServiceLocationDateAndTimeSection(testData, expectedAppointmentDetails);
expectedAppointmentDetails = await this.getExpectedVehicleDamageAndVehicle(testData, expectedAppointmentDetails);
expectedAppointmentDetails = await this.getExpectedContactDetails(testData, expectedAppointmentDetails);
expectedAppointmentDetails = await this.getExpectedTextServiceUpdatesOptInOrOut(testData, expectedAppointmentDetails);
let actualAppointmentDetails = await this.getActualAppointmentDetails();
for (const key in expectedAppointmentDetails) {
Soft.expect(actualAppointmentDetails[key]?.map(item => item.toLowerCase()))
.toEqual(expectedAppointmentDetails[key].map(item => item.toLowerCase()));
}
// Expand cart to see all details
await this.amountDueDropDown.click();
await this.reviewTable.waitFor({ state: "visible" });
// Cart Validation
// Get pricing information from cart panel
if (await this.subtotalText.isVisible()) {
const subtotalValue = await this.subtotalText.innerText();
const finalAmountDueValue = await this.finalAmountDueText.innerText();
// Pricing validations differ by payment method
if (paymentMethod === PaymentMethod.SelfPay && paymentDetails?.paymentType !== PaymentType.PayWithInsurance) {
// Extract amounts for self-pay customers
const subtotalAmount = this.extractAmount(subtotalValue);
const finalAmountDueAmount = this.extractAmount(finalAmountDueValue);
// Subtotal should be greater than 0
Soft.expect(subtotalAmount).toBeGreaterThan(0);
// Final amount differs based on payment type
if (paymentDetails?.paymentType === PaymentType.PayAtService) {
Soft.expect(finalAmountDueAmount).toBeGreaterThan(0);
} else if (paymentDetails?.paymentType === PaymentType.Credit ||
paymentDetails?.paymentType === PaymentType.Paypal ||
paymentDetails?.paymentType === PaymentType.AfterPay) {
// For payment types that charge immediately, amount due could be 0
// This logic might need adjusting based on actual business rules
}
} else {
// For insurance payments, check for proper indicators
// TODO: Apply logic for deductible and insurance payment logic
}
// Service package validations
const servicePackageValue = await this.cartPanelDetails.textContent();
await Soft.expect(servicePackageValue).toContain(`${servicePackage}`)
if (servicePackage === ServicePackage.Premium || servicePackage === ServicePackage.Standard) {
Soft.expect(servicePackageValue).toContain('New wiper blades');
}
if (servicePackage === ServicePackage.Premium) {
Soft.expect(servicePackageValue).toContain('Rain Repel Treatment');
}
// Promo Code Validation
if (paymentDetails!.promoCode) {
expect.soft(servicePackageValue).toContain(`Promo code ${paymentDetails!.promoCode} applied`);
};
// Early Bird line item validation
if (appointmentDetails?.appointmentTimeSlot == AppointmentTimeslot.EarlyBird) {
Soft.expect(servicePackageValue).toContain('Early bird');
}
}
}
/**
* Helper method to extract numeric amount from price strings like "$123.45"
*/
private extractAmount(valueString: string): number {
const matches = valueString.match(/\$([0-9,]+(\.[0-9]{2})?)/);
if (matches && matches[1]) {
return parseFloat(matches[1].replace(/,/g, ''));
}
return 0;
}
async getAmountDue(): Promise<string> {
await expect(this.amountDueTextField).toBeVisible();
const text = await this.amountDueTextField.textContent();
if (!text) throw new Error("Amount due text is empty");
return text;
}
async getSubtotal(): Promise<string> {
await this.amountDueDropDown.click();
await expect(this.subtotalAmountTextField).toBeVisible();
const text = await this.subtotalAmountTextField.textContent();
if (!text) throw new Error("Subtotal text is empty");
return text;
}
async executePayment(testData: Partial<ITestData>) {
const browserContext = this.page.context();
const { paymentDetails, isRecalVehicle, experiments } = testData;
switch (paymentDetails!.paymentType) {
case PaymentType.Credit:
await this.selectCreditCard();
await this.nextPage();
experiments!.isAdyenPayments
? await this.paymentAdyenPage.populateAdyenCreditCardDetails(testData)
: await this.paymentPage.populateCreditCardDetails(paymentDetails!);
break;
case PaymentType.AfterPay:
await this.payInFourButton.click();
await this.continueButton.click();
if (experiments?.isAdyenPayments) {
await this.paymentAdyenPage.afterPayButton.click();
await this.paymentAdyenPage.navigateToAfterPayButton.click();
await this.afterpayPage.executeAfterpayPayment(paymentDetails!);
} else {
// Capture popup
const afterpayPopup = await browserContext.waitForEvent('page');
const afterpayPage = new AfterpayPage(afterpayPopup);
// Execute payment
await afterpayPage.executeAfterpayPayment(paymentDetails!);
}
break;
case PaymentType.Paypal:
await this.selectPaypal();
// TODO: Click paypal button
await this.nextPage();
const paypalPage = experiments?.isAdyenPayments
? await this.paymentAdyenPage.navigateToAdyenPaypalCheckout()
: await this.paymentPage.navigateToPaypalCheckout();
/*if (experiments?.isAdyenPayments) {
// Capture popup
const payPalPopup = await this.page.waitForEvent('popup');
const paypalPage = new PaypalPage(payPalPopup);
paypalPage.completePaypalPurchase(testData);
} else {
await this.paypalPage.completePaypalPurchase(testData);
}*/
await paypalPage.completePaypalPurchase(testData);
break;
case PaymentType.PayAtService:
await this.selectPayAtService(isRecalVehicle!);
await this.nextPage();
break;
case PaymentType.PayWithInsurance:
await this.payWithInsuranceButton.click();
await this.nextPage();
break;
default:
console.error('PaymentMethodPage >> Logic for this payment method unimplemented');
break;
}
}
async selectPaypal() {
await this.payNowButton.click();
}
l
async selectCreditCard() {
await this.payNowButton.click();
}
async selectPayAtService(isRecalVehicle: boolean) {
if (await this.payAtServiceButton.isVisible()) {
await this.payAtServiceButton.click();
} else if (isRecalVehicle) {
await this.recalibrationCheckbox.click();
}
}
async verifyVAPS(): Promise<void> {
// Get Vuex state from localStorage
const vuexState = JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')'));
// Validate the count of FRONT WIPER parts
if (vuexState.order?.lineItems?.vaps?.length > 0) {
const frontWiperPartsCount = vuexState.order.lineItems.vaps.filter(vap =>
vap.partType === "FRONT WIPER"
).length;
await expect(frontWiperPartsCount).toBe(2);
} else {
throw new Error("No VAPS found in the order");
}
}
async getActualAppointmentDetails(): Promise<any> {
let actualAppointmentDetails = new Map<string, string[]>();
let appointmentDetailsSubSections = await this.appointmentDetailsSection.locator('.review-table .py-3').all();
for (let element of appointmentDetailsSubSections) {
let label = await element.locator('div .text-block').innerText();
//added to trim the text to remove any leading or trailing spaces (example: "expert installation " to "expert installation")
let value = (await element.locator('.review-block-content').allInnerTexts() as string[]).map(item => item.trim());
actualAppointmentDetails[label] = value;
}
return actualAppointmentDetails;
}
async getExpectedVehicleDetails(testdata: Partial<ITestData>, expectedServicePackageDetails: Map<string, string[]>): Promise<any> {
const { vehicleDetails } = testdata;
expectedServicePackageDetails["Vehicle"] = [vehicleDetails?.year + " " + vehicleDetails?.make + " " + vehicleDetails?.model];
return expectedServicePackageDetails;
}
async getExpectedVehicleDamageAndVehicle(testdata: Partial<ITestData>, expectedServicePackageDetails: Map<string, string[]>): Promise<any> {
const { vehicleDetails, vehicleDamage } = testdata;
let vehicleDamageText: string = '';
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.WindshieldOneChip || item == VehicleDamage.WindshieldTwoChips || item == VehicleDamage.WindshieldThreeChips)) {
vehicleDamageText = "Repair the windshield of your";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.WindshieldCrack)) {
vehicleDamageText = vehicleDamage?.length > 1 ? "Replace the Windshield" : "Replace the windshield";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.DriverVentGlass) && !vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.WindshieldCrack)) {
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Driver Vent Glass" : "Replace the Driver Vent Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.DriverFrontDoor)) {
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Driver Front Glass" : "Replace the Driver Front Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.DriverRearDoor)) {
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Driver Back Glass" : "Replace the Driver Back Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.DriverVentGlass) && vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.WindshieldCrack)) {
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Driver Vent Glass" : "Replace the Driver Vent Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.DriverQuarterPanel)) {
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Driver Quarter Glass" : "Replace the Driver Quarter Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.PassengerVentGlass)) {
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Passenger Vent Glass" : "Replace the Passenger Vent Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.PassengerFrontDoor)) {
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Passenger Front Glass" : "Replace the Passenger Front Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.PassengerRearDoor)) {
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Passenger Back Glass" : "Replace the Passenger Back Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.PassengerQuarterPanel)) {
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Passenger Quarter Glass" : "Replace the Passenger Quarter Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.RearWindow || item == VehicleDamage.RearSliding)) {
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Back Glass" : "Replace the Back Glass";
}
// Replace last comma with " and" if there are multiple damages
if (vehicleDamageText.includes(",") && vehicleDamageText !== "Replace the windshield") {
vehicleDamageText = vehicleDamageText.replace(/,([^,]*)$/, " and$1") + " of your";
}
expectedServicePackageDetails[vehicleDamageText] = [vehicleDetails?.year + " " + vehicleDetails?.make + " " + vehicleDetails?.model];
return expectedServicePackageDetails;
}
async expectedServicePackageDetails(testData: Partial<ITestData>, expectedServicePackageDetails: Map<string, string[]>): Promise<any> {
const { servicePackage } = testData;
let localStorage = JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')'));
let isRepair = localStorage.order.damage.isRepair as boolean;
let isInsurance = localStorage.order.payment.isInsurance as boolean;
let hasNonWindshieldGlass: boolean = false;
if (!isRepair) {
hasNonWindshieldGlass = localStorage.order.lineItems.glassParts.find((item: any) => item.partType !== "WINDSHIELD") ? true : false;
}
let isCaliforniaState = localStorage.order.serviceLocation.state as string == 'CA' ? true : false;
let hasRecalPart: boolean = false;
let canSafeliteRecalibrate: boolean = false;
if (!isRepair) {
hasRecalPart = localStorage.order.lineItems.glassParts.find((item: any) => item.requiresRecalibration === true) ? true : false;
canSafeliteRecalibrate = localStorage.order.lineItems.glassParts.find((item: any) => item.canSafeliteRecalibrate === true) ? true : false;
}
let recalRequired = !isRepair && (isInsurance || isCaliforniaState) && hasRecalPart && canSafeliteRecalibrate
let stringForRepair: string[] = ["Expert windshield repair", "Exclusive resin sealant", "Nationwide lifetime guarantee"];
let stringIfRecal = isRepair
? ""
: recalRequired
? " and recalibration"
: "";
let stringForReplace: string[] = [hasNonWindshieldGlass ? "New replacement glass" : "New replacement windshield", "Expert installation" + `${stringIfRecal}`, "Nationwide lifetime warranty"];
switch (servicePackage) {
case ServicePackage.GlassOnly:
expectedServicePackageDetails["Glass service only"] = isRepair
? stringForRepair
: stringForReplace;
break;
case ServicePackage.Standard:
stringForRepair.push("New wiper blades");
stringForReplace.push("New wiper blades");
expectedServicePackageDetails["Standard service"] = isRepair
? stringForRepair
: stringForReplace;
break;
case ServicePackage.Premium:
stringForRepair.push("New wiper blades", "Rain repel treatment");
stringForReplace.push("New wiper blades", "Rain repel treatment");
expectedServicePackageDetails["Premium service"] = isRepair
? stringForRepair
: stringForReplace;
break;
}
return expectedServicePackageDetails;
}
async getExpectedServiceLocationDateAndTimeSection(testData: Partial<ITestData>, expectedServicePackageDetails: Map<string, string[]>): Promise<any> {
const { customerDetails, appointmentDetails } = testData;
let serviceLocationTitle = appointmentDetails?.serviceLocation == ServiceLocation.Mobile
? "We're coming to you"
: "You're coming to us";
let serviceLocationText = appointmentDetails?.serviceLocation == ServiceLocation.Mobile
? appointmentDetails?.serviceAddress
? appointmentDetails.serviceAddress.street + ", " + appointmentDetails.serviceAddress.city + ", " + appointmentDetails.serviceAddress.state + " " + appointmentDetails.serviceAddress.postalCode
: ""
: appointmentDetails?.shopAddress
? appointmentDetails.shopAddress
: "";
serviceLocationText += " on "
serviceLocationText += customerDetails?.apptDate ? await this.getFormattedAppointmentDate(customerDetails.apptDate) + " " + customerDetails?.apptTime?.replace("-", "—") : "";
expectedServicePackageDetails[serviceLocationTitle] = [serviceLocationText];
return expectedServicePackageDetails;
}
async getExpectedVehicleDamageLocations(testData: Partial<ITestData>, expectedServicePackageDetails: Map<string, string[]>): Promise<any> {
const { customerDetails } = testData;
let VehicleDamageLocationsText: string[] = [];
if (VehicleDamage.WindshieldCrack) {
let VehicleDamageLocationsText: string[] = [];
VehicleDamageLocationsText.push("Replace the windshield");
};
expectedServicePackageDetails["VehicleDamageLocationsText"] = VehicleDamageLocationsText;
return expectedServicePackageDetails;
}
async getExpectedContactDetails(testData: Partial<ITestData>, expectedServicePackageDetails: Map<string, string[]>): Promise<any> {
const { customerDetails } = testData;
let customerDetailsText: string[] = [];
customerDetailsText.push(
// customerDetails?.firstName.toUpperCase() + " " + customerDetails?.lastName.toUpperCase(),
customerDetails?.email ? customerDetails?.email.toUpperCase() : "",
customerDetails?.phoneNumber ? customerDetails?.phoneNumber : "",
);
expectedServicePackageDetails["Contact details"] = customerDetailsText
return expectedServicePackageDetails;
}
async getExpectedTextServiceUpdatesOptInOrOut(testData: Partial<ITestData>, expectedServicePackageDetails: Map<string, string[]>): Promise<any> {
const { customerDetails } = testData;
let isOptedInForTextMessages = testData.isOptedInForTextMessages ?? false;
let expectedText = isOptedInForTextMessages ? customerDetails?.phoneNumber : "Not opted in";
expectedServicePackageDetails["Well text service updates to"] = [expectedText];
return expectedServicePackageDetails;
}
async ValidateAfterPayBreakOutSection() {
/*const vuexState = JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')'));
const isInsurance = vuexState.order.payment.isInsurance;
const currentDeductible = vuexState.order.policy.currentDeductible;
const isVerified = vuexState.order.payment.insuranceCoverage.isVerified;*/
const amountDueText = await this.amountDueTextField.first().textContent();
const verifyAfterpayBreakout = amountDueText?.includes('$')
? Number.parseFloat(amountDueText.replace(/[^0-9.]/g, '')) > 0
: false
if (verifyAfterpayBreakout) {
const afterPayAmountString = await this.afterPayBreakoutSection.locator('.afterpay-amount').textContent();
const afterPayAmount = Number.parseFloat(afterPayAmountString!.replace(/[^0-9.]/g, ''));
Soft.expect(afterPayAmount, `AfterPayAmount (${afterPayAmount}) is > 0`).toBeGreaterThan(0);
await this.afterPayToggle.click().then(
async () => {
await waitUntil(async () => {
return (await this.page.locator('.after-pay-toggle.expanded').evaluate(el => window.getComputedStyle(el, ':after').transform)) === 'matrix(-1, 0, 0, -1, 0, 0)';
});
}
);
const afterPayCards = await this.page.locator('.after-pay-details .payment-card').all();
Soft.expect(afterPayCards.length, `There are 4 afterpay cards`).toBe(4);
for (const afterPayCard of afterPayCards) {
const afterPayAmountWithinCardString = await afterPayCard.locator('.amount-due').textContent();
const afterPayAmountWithinCard = Number.parseFloat(afterPayAmountWithinCardString!.replace(/[^0-9.]/g, ''));
Soft.expect(afterPayAmountWithinCard, `AfterPayAmount (${afterPayAmountWithinCard}) > 0`).toBeGreaterThan(0);
}
}
}
/*async getFormattedAppointmentDate(appointmentDate: string) {
// Parse the original date
let parsedAppointmentDate = new Date(`${appointmentDate}` + 'T00:00:00');
// Format the new date as a string
let updatedAppointmentDate = parsedAppointmentDate.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
return updatedAppointmentDate;
}*/
async getFormattedAppointmentDate(appointmentDate: string) {
let formattedAppointmentDate: Date;
const now = new Date();
switch (true) {
case /^\d{4}-\d{2}-\d{2}$/.test(appointmentDate):
formattedAppointmentDate = new Date(appointmentDate + 'T00:00:00');
break;
case /[A-Za-z]+,\s+[A-Za-z]+\s+\d{1,2}/.test(appointmentDate):
const [, month, day] = appointmentDate.match(/[A-Za-z]+,\s+([A-Za-z]+)\s+(\d{1,2})/) || [];
formattedAppointmentDate = new Date(`${month} ${day}, ${now.getFullYear()}`);
if (formattedAppointmentDate < now) formattedAppointmentDate.setFullYear(now.getFullYear() + 1); // rollover
break;
default:
throw new Error('Unsupported date format: ' + appointmentDate);
}
return formattedAppointmentDate.toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
});
}
@step("PaymentMethodPage >> Select Payment Method: ")
async handlePaymentMethodPage(testData: Partial<ITestData>) {
const { servicePackage, isRecalVehicle, paymentDetails, isForcedOEM, experiments } = testData;
await this.validateProgressBar(ProgressBarPercentages.PaymentMethodPage);
await this.validatePaymentDetailsPage(testData);
await this.ValidateAfterPayBreakOutSection();
if (isForcedOEM) {
await this.validateOEMPart(isForcedOEM);
}
// Verify VAPS wipers on backend for standard and premium packages
if (servicePackage === ServicePackage.Standard || servicePackage === ServicePackage.Premium) {
await this.verifyVAPS();
}
if (paymentDetails?.paymentType) {
// await this.executePayment(paymentDetails!, isRecalVehicle!, experiments!);
await this.executePayment(testData);
} else {
await this.nextPage();
}
}
}