From 706c29c9d3aa08dd75064718a41349e13cdb12ab Mon Sep 17 00:00:00 2001 From: kpatel8hs4io <31411746+kpatel8hs4io@users.noreply.github.com> Date: Tue, 17 Feb 2026 00:09:55 -0500 Subject: [PATCH 1/5] fix broken playwright tests. added a new tests --- playwright-tests/framework/TestData.ts | 1 + playwright-tests/pages/BasePage.ts | 58 +++++ playwright-tests/pages/PartQuestionPage.ts | 2 +- playwright-tests/pages/PaypalPage.ts | 6 +- playwright-tests/pages/SchedulePage.ts | 23 +- playwright-tests/pages/ServicePackagesPage.ts | 4 + playwright-tests/tests/0000__M.test.ts | 227 +++++++++--------- .../tests/CashReplaceDynamicRecalMobile.ts | 3 +- .../tests/CashReplaceStaticInshop.ts | 78 ++++++ .../tests/CashReplaceWiperDropoff.ts | 2 +- .../tests/InsuranceNoCompProgressive.ts | 11 +- 11 files changed, 294 insertions(+), 121 deletions(-) create mode 100644 playwright-tests/tests/CashReplaceStaticInshop.ts diff --git a/playwright-tests/framework/TestData.ts b/playwright-tests/framework/TestData.ts index 1af123c41..9148db34e 100644 --- a/playwright-tests/framework/TestData.ts +++ b/playwright-tests/framework/TestData.ts @@ -7,4 +7,5 @@ export interface ITestData extends base { isOptedInForTextMessages: boolean, totalAmount?: number, isForcedOEM?: boolean + mockFirstInshopCallNoSchedule?: boolean } \ No newline at end of file diff --git a/playwright-tests/pages/BasePage.ts b/playwright-tests/pages/BasePage.ts index 1562677a2..4f7f54557 100644 --- a/playwright-tests/pages/BasePage.ts +++ b/playwright-tests/pages/BasePage.ts @@ -18,6 +18,7 @@ export class BasePage { readonly buttonLoadSpin: Locator; readonly hamburgerMenu: Locator; readonly progressBar: Locator; + readonly loaders: Locator; constructor(page: Page) { this.page = page; @@ -27,6 +28,7 @@ export class BasePage { this.buttonLoadSpin = page.getByRole('alert'); this.hamburgerMenu = this.page.getByRole('button', { name: 'Hamburger Menu (modal window)' }); this.progressBar = this.page.locator('.progress-bar-outer .progress-bar-inner'); + this.loaders = this.page.locator('.spinner-border, .loader'); } async nextPage() { @@ -131,6 +133,47 @@ export class BasePage { }); } + + async mockScheduleResponseForFirstInshopCallNoSchedule(customerDetails: ICustomerDetails) { + let fistInshopScheduleCall = true; + + const apiUrl = `https://digitalapi.${process.env['PLAYWRIGHT_ENV']!.replace('sys', 'test').toLowerCase()}.safelite.io/schedule/api/v1/schedule/shop-time-slots`; + await this.page.route(apiUrl, async (route) => { + if (!fistInshopScheduleCall) { + return route.continue(); + } + + // const fmt = (d: Date) => `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,"0")}-${String(d.getDate()).padStart(2,"0")}`; + // const startDate = new Date(); + //const beginningOfWeek = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate() - startDate.getDay()); + // const endDate = new Date(beginningOfWeek.getFullYear(), beginningOfWeek.getMonth(), beginningOfWeek.getDate() + 13); + // e.g., "2025-07-23" + /*if (route.request().postDataJSON().startDate === fmt(startDate) && route.request().postDataJSON().endDate === fmt(endDate)) { + const response = await route.fetch(); + const responseBody = await response.json(); + + responseBody.days = []; + // Mock the response + await route.fulfill({ + response, + body: JSON.stringify(responseBody), + }); + }*/ + + const response = await route.fetch(); + const responseBody = await response.json(); + + responseBody.days = []; + + // Mock the response + await route.fulfill({ + response, + body: JSON.stringify(responseBody), + }); + fistInshopScheduleCall = false; + }); + } + async getRepairPartsTotal(testData: Partial) { const apiUrl = `https://digitalapi.${process.env['PLAYWRIGHT_ENV']!.replace('sys', 'test').toLowerCase()}.safelite.io/price/api/v1/price/order-items`; await this.page.on('response', async (response) => { @@ -170,6 +213,21 @@ export class BasePage { console.log(`Progress Bar Percentage: Actual - ${actualProgressPercentage} vs Expected - ${progressPercentage}`); } + async waitForPageOrComponentload(): Promise { + await Promise.all([ + (async () => { + const loaderCount = await this.loaders.count(); + if (loaderCount > 0) { + for (let i = 0; i < loaderCount; i++) { + await this.loaders.nth(i).waitFor({ state: 'hidden' }); + } + } + })(), + this.page.waitForLoadState('load'), + this.page.waitForLoadState('networkidle') + ]); + } + async validateOEMPart( isOEM: boolean): Promise { if (isOEM!) { // Get Vuex state from localStorage diff --git a/playwright-tests/pages/PartQuestionPage.ts b/playwright-tests/pages/PartQuestionPage.ts index a6ecbc0bd..463746d0b 100644 --- a/playwright-tests/pages/PartQuestionPage.ts +++ b/playwright-tests/pages/PartQuestionPage.ts @@ -44,6 +44,7 @@ export class PartQuestionsPage extends BasePage { } else { await expect(partQuestionOptions).not.toBeAttached(); } + await this.selectPartQuestionResponses(partQuestions!); } } @@ -65,7 +66,6 @@ export class PartQuestionsPage extends BasePage { await this.validateProgressBar(ProgressBarPercentages.PartQuestionsPage); await this.validatePartQuestions(partQuestions!); - await this.selectPartQuestionResponses(partQuestions!); await this.nextPage(); } } diff --git a/playwright-tests/pages/PaypalPage.ts b/playwright-tests/pages/PaypalPage.ts index e67e0a57b..d0601ffcb 100644 --- a/playwright-tests/pages/PaypalPage.ts +++ b/playwright-tests/pages/PaypalPage.ts @@ -26,7 +26,7 @@ export class PaypalPage extends BasePage { this.paypalLoginButton = page.getByRole('button', { name: 'Log In', exact: true }); this.completePurchaseButton = page.getByTestId('submit-button-initial') this.payWithRadioButton = page.getByRole('button').filter({ hasText: 'Pay with' }); - this.payButton = page.locator('#one-time-cta'); + this.payButton = page.locator('footer #one-time-cta'); this.tryAnotherWayButton = page.getByRole('button', { name: 'Try another way' }); } @@ -50,8 +50,8 @@ export class PaypalPage extends BasePage { await this.passwordTextBox.fill(paymentDetails.password!); await this.paypalLoginButton.click(); await this.payWithRadioButton.click(); - await this.page.waitForTimeout(2000); // wait for 2 seconds to ensure the Pay button is clickable + await this.page.waitForTimeout(2000); await this.payButton.dblclick(); } } -} +} \ No newline at end of file diff --git a/playwright-tests/pages/SchedulePage.ts b/playwright-tests/pages/SchedulePage.ts index 944b84136..fdd3f30ec 100644 --- a/playwright-tests/pages/SchedulePage.ts +++ b/playwright-tests/pages/SchedulePage.ts @@ -36,6 +36,9 @@ export class SchedulePage extends BasePage { readonly allDayDropOffButton: Locator; readonly pickATimeButton: Locator; readonly mobileFirstModalCloseButton: Locator; + readonly yesButtonRecalAckModal: Locator; + readonly noButtonRecalAckModal: Locator; + readonly continueButtonRecalAckModal: Locator; constructor(page: Page) { super(page); @@ -66,7 +69,10 @@ export class SchedulePage extends BasePage { this.viewMoreDatesLink = this.page.getByText(/View more dates/).first(); this.appointmentDuration = this.page.locator('.duration-text-block'); this.timeSlots = this.page.locator('fieldset:has(>legend#chooseTimeSlot) label').filter({ visible: true}); - this.mobileFirstModalCloseButton = this.page.getByRole('dialog').getByRole('button', { name: 'Close' }); + this.mobileFirstModalCloseButton = this.page.locator('#mobile-first-modal-container .btn-close'); + this.yesButtonRecalAckModal = this.page.locator("label[buttonlabel='Yes']"); + this.noButtonRecalAckModal = this.page.locator("label[buttonlabel='No']"); + this.continueButtonRecalAckModal = this.page.locator("#recal-ack-modal-container .modal-footer button"); } async selectLocation(testData: Partial) { @@ -98,6 +104,7 @@ export class SchedulePage extends BasePage { await this.safeliteShopsList.first().click(); // Click the save location button await this.saveLocationButton.click(); + await this.waitForPageOrComponentload(); } // await this.inShopButton.click(); @@ -229,13 +236,25 @@ export class SchedulePage extends BasePage { @step("SchedulePage >> Schedule appointment: ") async handleSchedulePage(testData: Partial) { - const { appointmentDetails } = testData; + const { appointmentDetails, isCanNotRecal } = testData; + await this.waitForPageOrComponentload(); await this.validateProgressBar(ProgressBarPercentages.SchedulePage); if (await this.mobileFirstModalCloseButton.isVisible()) { await this.mobileFirstModalCloseButton.click(); } await this.selectLocation(testData); + + if (await this.mobileFirstModalCloseButton.isVisible({timeout: 5000})) { + await this.page.waitForTimeout(1000); + await this.mobileFirstModalCloseButton.dblclick(); + } + await this.scheduleFirstAppointment(testData); + await this.nextPage(); + if (isCanNotRecal) { + await this.yesButtonRecalAckModal.click(); + await this.continueButtonRecalAckModal.click(); + } } } diff --git a/playwright-tests/pages/ServicePackagesPage.ts b/playwright-tests/pages/ServicePackagesPage.ts index ba95f67cd..2f16cb7b4 100644 --- a/playwright-tests/pages/ServicePackagesPage.ts +++ b/playwright-tests/pages/ServicePackagesPage.ts @@ -298,6 +298,10 @@ export class ServicePackagesPage extends BasePage { await this.mockScheduleResponseForEarlyBird(customerDetails!); } + if (testData.mockFirstInshopCallNoSchedule) { + await this.mockScheduleResponseForFirstInshopCallNoSchedule(testData.customerDetails!); + } + await this.nextPage(); } } \ No newline at end of file diff --git a/playwright-tests/tests/0000__M.test.ts b/playwright-tests/tests/0000__M.test.ts index cd8270a8a..aebb3b978 100644 --- a/playwright-tests/tests/0000__M.test.ts +++ b/playwright-tests/tests/0000__M.test.ts @@ -1,4 +1,4 @@ -import { Page } from "@playwright/test"; +import { Browser, Page } from "@playwright/test"; import { addSmokeTagToRandomTest, Flow, ServiceLocation } from 'safelite-playwright-core'; import { ValidationOptions } from 'safelite-playwright-core'; import heavyTruckTests from "./alert-validation/alert0001_HeavyTruck"; @@ -18,6 +18,7 @@ import cashReplaceGlassLicensePlateLookupInshopPaypalTests from "./CashReplaceGl import { ApiResponseInterceptUtil } from 'safelite-playwright-core'; import cashReplaceGlassPromoInshopTests from "./CashReplaceGlassPromoInshop"; import cashReplaceMultiGlassPromoInshopTests from "./CashReplaceMultiGlassPromoInshop"; +import cashReplaceStaticInshopTests from "./CashReplaceStaticInshop"; import cashReplaceRainDefensePromoInshopTests from "./CashReplaceRainDefensePromoInshop"; import cashReplaceSafeliteCanNotRecalMobileTests from "./CashReplaceSafeliteCanNotRecalMobile"; import cashReplaceVinMobileTests from "./CashReplaceVinMobile"; @@ -61,33 +62,34 @@ addSmokeTagToRandomTest(splitWindshieldTests); // Standard test scenarios const allStandardTests = [ - {name: "CashRepairMobileCreditCard", tests: cashRepairMobileCCTests}, - {name: "CashRepairInShopAfterPay", tests: cashRepairInShopAfterPayTests}, - {name: "CashRepairInShopPayPal", tests: cashRepairInShopPayPalTests}, - {name: "CashReplaceDynamicRecalMobile", tests: cashReplaceDynamicRecalMobileTests}, - {name: "CashReplaceGlassAddressLookupInshopAfterPay", tests: cashReplaceGlassAddressLookupInshopAfterPayTests}, - {name: "CashReplaceGlassLicensePlateLookupInshopPaypal", tests: cashReplaceGlassLicensePlateLookupInshopPaypalTests}, - {name: "CashReplaceGlassPromoInShop", tests: cashReplaceGlassPromoInshopTests}, - {name: "CashReplaceMultiGlassPromoInShop", tests: cashReplaceMultiGlassPromoInshopTests}, - {name: "CashReplaceMultiSlidingGlassDropoff",tests: cashReplaceMultiSlidingGlassDropoffTests}, - {name: "CashReplaceMultiGlassMobile",tests: cashReplaceMultiGlassMobileTests}, - {name: "CashReplaceRainDefensePromoInshop", tests: cashReplaceRainDefensePromoInshopTests}, - {name: "CashReplaceSafeliteCanNotRecalMobile", tests: cashReplaceSafeliteCanNotRecalMobileTests}, - {name: "CashReplaceVinMobile", tests: cashReplaceVinMobileTests}, - {name: "CashReplaceWiperDropoff", tests: cashReplaceWiperDropoffTests}, - {name: "CashReplaceWiperPromoInshop", tests: cashReplaceWiperPromoInShopTests}, - {name: "CashReplaceSwitchToInsuranceProgressiveNoComp", tests: cashReplaceSwitchToInsuranceProgressiveNoCompTests}, + { name: "CashRepairMobileCreditCard", tests: cashRepairMobileCCTests }, + { name: "CashRepairInShopAfterPay", tests: cashRepairInShopAfterPayTests }, + { name: "CashRepairInShopPayPal", tests: cashRepairInShopPayPalTests }, + { name: "CashReplaceDynamicRecalMobile", tests: cashReplaceDynamicRecalMobileTests }, + { name: "CashReplaceGlassAddressLookupInshopAfterPay", tests: cashReplaceGlassAddressLookupInshopAfterPayTests }, + { name: "CashReplaceGlassLicensePlateLookupInshopPaypal", tests: cashReplaceGlassLicensePlateLookupInshopPaypalTests }, + { name: "CashReplaceGlassPromoInShop", tests: cashReplaceGlassPromoInshopTests }, + { name: "CashReplaceMultiGlassPromoInShop", tests: cashReplaceMultiGlassPromoInshopTests }, + { name: "CashReplaceMultiSlidingGlassDropoff", tests: cashReplaceMultiSlidingGlassDropoffTests }, + { name: "CashReplaceMultiGlassMobile", tests: cashReplaceMultiGlassMobileTests }, + { name: "CashReplaceRainDefensePromoInshop", tests: cashReplaceRainDefensePromoInshopTests }, + { name: "CashReplaceSafeliteCanNotRecalMobile", tests: cashReplaceSafeliteCanNotRecalMobileTests }, + { name: "CashReplaceVinMobile", tests: cashReplaceVinMobileTests }, + { name: "CashReplaceWiperDropoff", tests: cashReplaceWiperDropoffTests }, + { name: "CashReplaceStaticInshop", tests: cashReplaceStaticInshopTests }, + { name: "CashReplaceWiperPromoInshop", tests: cashReplaceWiperPromoInShopTests }, + { name: "CashReplaceSwitchToInsuranceProgressiveNoComp", tests: cashReplaceSwitchToInsuranceProgressiveNoCompTests }, // TODO: Uncomment when QA is ready to run heavy truck tests // {name: "CashReplaceSplitWindshield", tests: CashReplaceSplitWindshieldTests}, - {name: "InsuranceAcuityPaypal", tests: insuranceAcuityPaypalTests}, - {name: "InsuranceITAC21stCentury", tests: insuranceITAC21stCenturyTests}, - {name: "InsuranceNoCompProgressive", tests: insuranceNoCompProgressiveTests}, + { name: "InsuranceAcuityPaypal", tests: insuranceAcuityPaypalTests }, + { name: "InsuranceITAC21stCentury", tests: insuranceITAC21stCenturyTests }, + { name: "InsuranceNoCompProgressive", tests: insuranceNoCompProgressiveTests }, // {name: "InsuranceBigTruckVerified", tests: insuranceBigTruckVerifiedTests}, - {name: "InsuranceOEMAllstate", tests: insuranceOEMAllstateTests}, - {name: "InsuranceUnverified", tests: insuranceUnverifiedTests}, + { name: "InsuranceOEMAllstate", tests: insuranceOEMAllstateTests }, + { name: "InsuranceUnverified", tests: insuranceUnverifiedTests }, // {name: "InsuranceGeico", tests: insuranceGeicoTests}, // {name: "InsuranceITACOptimizedPriceValidationAllState", tests: insuranceITACOptimizedPriceValidationAllStateTests} - {name: "InsuranceMeemicNearSchoolVerified", tests: insuranceMeemicNearSchoolVerifiedTests}, + { name: "InsuranceMeemicNearSchoolVerified", tests: insuranceMeemicNearSchoolVerifiedTests }, ]; @@ -146,7 +148,7 @@ async function run(page: Page, testInfo: TestInfo): Promise { // Catch successful alert tests and log message if (error instanceof TestSuccessAlert) { console.log(error.message); - // Catch and throw other errors + // Catch and throw other errors } else { throw error; } @@ -175,11 +177,11 @@ async function runWorkflow(page: Page, testCase: TestCase) { page.on('response', apiResponseInterceptUtil.handleInterceptResponse); // Destructure test data for easier access - const { - paymentMethod, customerDetails, vehicleDetails, vehicleDamage, - paymentDetails, partQuestions, isEnterFunnelWithZip, capabilityQuestions, + const { + paymentMethod, customerDetails, vehicleDetails, vehicleDamage, + paymentDetails, partQuestions, isEnterFunnelWithZip, capabilityQuestions, vehiclePartQuestions, moldingQuestions, isSkipEstimatePage - } = testCase.testData; + } = testCase.testData; // Check if the vehicle damage includes a windshield crack const hasWindshieldCrack = vehicleDamage!.some(damage => @@ -193,7 +195,7 @@ async function runWorkflow(page: Page, testCase: TestCase) { //============================= TEST WORKFLOW STEPS ============================= // Use Environment Variable to decide whether or not we want to skip content site aka home page - if (process.env.SKIP_CONTENT_SITE == "false") { + if (process.env.SKIP_CONTENT_SITE == "false") { await test.step('HomePage >> Lets Get Started', async () => { let homePage = testCase.pages.homePage; console.log(`Customer for this test: ${customerDetails?.firstName} ${customerDetails?.lastName}`); @@ -211,6 +213,16 @@ async function runWorkflow(page: Page, testCase: TestCase) { console.log(`Customer for this test: ${customerDetails?.firstName} ${customerDetails?.lastName}`); } + if (page.url().includes('ServiceDetails.aspx')) { + // Take a screenshot for diagnostics + await page.screenshot({ path: `test-results\\ortoni-data\\service-details-detected-${Date.now()}.png`, fullPage: true }); + console.log("ServiceDetails.aspx detected"); + await page.goBack(); + await page.reload(); + let homePage = testCase.pages.homePage; + await homePage.letsGetStarted(customerDetails?.address.postalCode!, !!isEnterFunnelWithZip!); + } + // Handle vehicle selection page let vehicleSelectionPage = testCase.pages.vehicleSelectionPage; await vehicleSelectionPage.handleVehicleSelectionPage(testCase.testData); @@ -223,24 +235,24 @@ async function runWorkflow(page: Page, testCase: TestCase) { if (hasWindshieldCrack && !isSkipEstimatePage) { let estimatePage = testCase.pages.estimatePage; await estimatePage.handleEstimatePage(testCase.testData); - + // Handle different vehicle lookup methods switch (vehicleDetails!.vehicleLookupType!) { case VehicleLookupType.Address: let vehicleLookupAddressPage = testCase.pages.vehicleLookupAddressPage; await vehicleLookupAddressPage.handleVehicleLookupAddressPage(testCase.testData); break; - + case VehicleLookupType.LicensePlateNumber: let vehicleLookupLicensePage = testCase.pages.vehicleLookupLicensePage; await vehicleLookupLicensePage.handleVehicleLookupLicensePage(testCase.testData); break; - + case VehicleLookupType.Vin: let vinLookupPage = testCase.pages.vinLookupPage; await vinLookupPage.handleVehicleLookupVinPage(testCase.testData); break; - + case VehicleLookupType.Zip: let serviceZipPage = testCase.pages.serviceZipPage; await serviceZipPage.handleServiceZipPage(testCase.testData); @@ -257,7 +269,7 @@ async function runWorkflow(page: Page, testCase: TestCase) { let partQuestionsPage = testCase.pages.partQuestionsPage; await partQuestionsPage.handlePartQuestionsPage(testCase.testData); } - + // Handle molding questions if applicable if (moldingQuestions && moldingQuestions.length > 0) { let moldingQuestionsPage = testCase.pages.moldingQuestionsPage; @@ -281,14 +293,14 @@ async function runWorkflow(page: Page, testCase: TestCase) { await servicePackagesPage.handleServicePackagePage(testCase.testData); //============================= INSURANCE FLOW =============================` - + // Insurance flow - if user selected Insurance as Payment Method if (paymentMethod == PaymentMethod.Insurance) { await handleInsuranceFlow(testCase); } //============================= SERVICE SCHEDULING ============================= - + // Select service location // let serviceLocationPage = testCase.pages.serviceLocationPage; // await serviceLocationPage.handleServiceLocationPage(testCase.testData); @@ -307,7 +319,7 @@ async function runWorkflow(page: Page, testCase: TestCase) { await contactDetailsPage.handleContactDetailsPage(testCase.testData); //============================= PAYMENT PROCESSING ============================= - + // Handle payment let paymentMethodPage = testCase.pages.paymentMethodPage; await paymentMethodPage.handlePaymentMethodPage(testCase.testData); @@ -318,91 +330,90 @@ async function runWorkflow(page: Page, testCase: TestCase) { } //============================= ORDER CONFIRMATION ============================= - + // Validate order confirmation let orderConfirmationPage = testCase.pages.orderConfirmationPage; await orderConfirmationPage.verifyOrderConfirmationPage(testCase.testData); } -export async function handleInsuranceFlow(testCase: TestCase) { +async function handleInsuranceFlow(testCase: TestCase) { const { isPolicyFound, isPolicyDriver, endorsements, isRecalVehicle, isCashInsuranceFlow, isForcedOEM, flow } = testCase.testData; - - // Check if the insurance policy has endorsements - const hasEndorsements = endorsements && endorsements.length > 0; - // Handle insurance company page - let insuranceCompanyPage = testCase.pages.insuranceCompanyPage; - await insuranceCompanyPage.handleInsuranceCompanyPage(testCase.testData); + // Check if the insurance policy has endorsements + const hasEndorsements = endorsements && endorsements.length > 0; - // Handle OEM scenario - if (isForcedOEM) { - // User should be on heritage problem glass questions page after selecting Allstate on insurance company page - let HeritageProblemGlassQuestionsPage = testCase.pages.heritageProblemGlassQuestionsPage; - await HeritageProblemGlassQuestionsPage.handleHeritageProblemGlassQuestionsPage(testCase.testData); - } - - // Handle ccPolicyInfoPage - let ccPolicyInfoPage = testCase.pages.ccPolicyInfoPage; - await ccPolicyInfoPage.handleCCPolicyInfoPage(testCase.testData); + // Handle insurance company page + let insuranceCompanyPage = testCase.pages.insuranceCompanyPage; + await insuranceCompanyPage.handleInsuranceCompanyPage(testCase.testData); - let duplicateCheckPage = testCase.pages.duplicateCheckPage; - if (duplicateCheckPage.page.url().includes('DuplicateCheck.aspx')) { - await duplicateCheckPage.handleDuplicateCheckPage(testCase.testData); + // Handle OEM scenario + if (isForcedOEM) { + // User should be on heritage problem glass questions page after selecting Allstate on insurance company page + let HeritageProblemGlassQuestionsPage = testCase.pages.heritageProblemGlassQuestionsPage; + await HeritageProblemGlassQuestionsPage.handleHeritageProblemGlassQuestionsPage(testCase.testData); + } + + // Handle ccPolicyInfoPage + let ccPolicyInfoPage = testCase.pages.ccPolicyInfoPage; + await ccPolicyInfoPage.handleCCPolicyInfoPage(testCase.testData); + + let duplicateCheckPage = testCase.pages.duplicateCheckPage; + if (duplicateCheckPage.page.url().includes('DuplicateCheck.aspx')) { + await duplicateCheckPage.handleDuplicateCheckPage(testCase.testData); + } + + if (flow === Flow.Managed) { + let policyVehiclesPage = testCase.pages.policyVehiclesPage; + await policyVehiclesPage.handlePolicyVehiclesPage(testCase.testData); + + // Handle policy driver selection if applicable + if (isPolicyDriver) { + let policyDriverPage = testCase.pages.policyDriverPage; + await policyDriverPage.handlePolicyDriverPage(testCase.testData); } - if ( flow === Flow.Managed ) { - let policyVehiclesPage = testCase.pages.policyVehiclesPage; - await policyVehiclesPage.handlePolicyVehiclesPage(testCase.testData); - - // Handle policy driver selection if applicable - if (isPolicyDriver) { - let policyDriverPage = testCase.pages.policyDriverPage; - await policyDriverPage.handlePolicyDriverPage(testCase.testData); - } - - // Handle endorsements if applicable - if (hasEndorsements) { - let endorsementsPage = testCase.pages.endorsementsPage; - await endorsementsPage.handleEndorsementsPage(testCase.testData); - } - } else { - - //Handle verify details page - let verifyDetailsPage = testCase.pages.verifyDetailsPage; - await verifyDetailsPage.handleVerifyDetailsPage(testCase.testData); + // Handle endorsements if applicable + if (hasEndorsements) { + let endorsementsPage = testCase.pages.endorsementsPage; + await endorsementsPage.handleEndorsementsPage(testCase.testData); } + } else { - // Handle Policy info submitted page - let policyInfoSubmittedPage = testCase.pages.policyInfoSubmittedPage; - await policyInfoSubmittedPage.handlePolicyInfoSubmittedPage(); + //Handle verify details page + let verifyDetailsPage = testCase.pages.verifyDetailsPage; + await verifyDetailsPage.handleVerifyDetailsPage(testCase.testData); + } - if(isRecalVehicle) - { - let recalibrationInfoPage = testCase.pages.recalibrationInfoPage; - await recalibrationInfoPage.handleRecalibrationInfoPage(); + // Handle Policy info submitted page + let policyInfoSubmittedPage = testCase.pages.policyInfoSubmittedPage; + await policyInfoSubmittedPage.handlePolicyInfoSubmittedPage(); + + if (isRecalVehicle) { + let recalibrationInfoPage = testCase.pages.recalibrationInfoPage; + await recalibrationInfoPage.handleRecalibrationInfoPage(); + } + + //handle coveraage statement page + let coverageStatementPage = testCase.pages.coverageStatementPage; + await coverageStatementPage.handleCoverageStatementPage(testCase.testData); + + // Handle scenario where user selected Cash to Insurance and needs to go back through the flow + // right now we are choosing pay at appointment as payment method for this scenario + if (isCashInsuranceFlow) { + // Schedule appointment + let schedulePage = testCase.pages.schedulePage; + await schedulePage.handleSchedulePage(testCase.testData); + + if (testCase.testData.appointmentDetails?.serviceLocation === ServiceLocation.Mobile) { + let mobileDetailsPage = testCase.pages.mobileDetailsPage; + await mobileDetailsPage.handleMobileDetailsPage(testCase.testData); } + //customer dertails + let contactDetailsPage = testCase.pages.contactDetailsPage; + await contactDetailsPage.nextPage(); - //handle coveraage statement page - let coverageStatementPage = testCase.pages.coverageStatementPage; - await coverageStatementPage.handleCoverageStatementPage(testCase.testData); - - // Handle scenario where user selected Cash to Insurance and needs to go back through the flow - // right now we are choosing pay at appointment as payment method for this scenario - if (isCashInsuranceFlow) { - // Schedule appointment - let schedulePage = testCase.pages.schedulePage; - await schedulePage.handleSchedulePage(testCase.testData); - - if (testCase.testData.appointmentDetails?.serviceLocation === ServiceLocation.Mobile) { - let mobileDetailsPage = testCase.pages.mobileDetailsPage; - await mobileDetailsPage.handleMobileDetailsPage(testCase.testData); - } - //customer dertails - let contactDetailsPage = testCase.pages.contactDetailsPage; - await contactDetailsPage.nextPage(); - - //paymentmethods - let paymentMethodPage = testCase.pages.paymentMethodPage; - await paymentMethodPage.nextPage(); - } + //paymentmethods + let paymentMethodPage = testCase.pages.paymentMethodPage; + await paymentMethodPage.nextPage(); + } } \ No newline at end of file diff --git a/playwright-tests/tests/CashReplaceDynamicRecalMobile.ts b/playwright-tests/tests/CashReplaceDynamicRecalMobile.ts index 5705a95a3..ffc2c9264 100644 --- a/playwright-tests/tests/CashReplaceDynamicRecalMobile.ts +++ b/playwright-tests/tests/CashReplaceDynamicRecalMobile.ts @@ -61,7 +61,8 @@ const cashReplaceDynamicRecalMobileData: Partial = { // Override payment details paymentDetails: { paymentType: PaymentType.PayAtService, - promoCode: 'digitalrain' + // promoCode: 'digitalrain' + promoCode: 'rain50' } } diff --git a/playwright-tests/tests/CashReplaceStaticInshop.ts b/playwright-tests/tests/CashReplaceStaticInshop.ts new file mode 100644 index 000000000..ca71b4cde --- /dev/null +++ b/playwright-tests/tests/CashReplaceStaticInshop.ts @@ -0,0 +1,78 @@ +//Imports here +import { ITestData } from 'framework/TestData' +import { ServicePackage, PaymentType, PartQuestionType } from 'safelite-playwright-core'; +import { ITestCase } from '../framework/Typedefs' +import { VehicleLookupType } from 'safelite-playwright-core'; +import { getDefaultTestData, setFakerSeedFromTestName } from 'safelite-playwright-core'; +import { PaymentMethod } from 'framework/localTypes/Enums'; + +// Set the seed before generating any data +setFakerSeedFromTestName("CashReplaceStaticInshop"); + +// Now get the test data with the seeded faker +const CashReplaceStaticInshopData: Partial = { + ...getDefaultTestData(), // Get default data with current seed + + // CASH Client + paymentMethod: PaymentMethod.SelfPay, + + // Key feature: Premium package with Rain Defense + servicePackage: ServicePackage.Standard, + + // Flag for recalibration vehicle + isRecalVehicle: true, + + // Override customer details + customerDetails: { + ...getDefaultTestData().customerDetails!, + address: { + ...getDefaultTestData().customerDetails!.address, + postalCode: '43085' + } + }, + + // override part questions + partQuestions: [ + { + partQuestionType: PartQuestionType.GeneralQuestion1, + isOnPage: true, + optionToSelect: 'Yes' + }, + { + partQuestionType: PartQuestionType.GeneralQuestion2, + isOnPage: true, + optionToSelect: 'Yes' + } + ], + + // Override vehicle details + vehicleDetails: { + ...getDefaultTestData().vehicleDetails!, + year: '2022', + make: 'Mazda', + model: 'CX-9', + style: '4 door utility', + vin: '3FA6P0HD8LR234510', + vehicleLookupType: VehicleLookupType.Zip + }, + + mockFirstInshopCallNoSchedule: true, + + // No need to override vehicleDamage as it already defaults to WindshieldCrack + + // Payment at service + paymentDetails: { + paymentType: PaymentType.PayAtService + }, +} + +const cashReplaceStaticInshopTests: ITestCase[] = []; + +const tc = { + name: `CashReplaceStaticInshop`, + tags: ['@E2E','@CashReplaceStaticInshop', '@test_report', '@CASH'], + testData: CashReplaceStaticInshopData +}; +cashReplaceStaticInshopTests.push(tc); + +export default cashReplaceStaticInshopTests; \ No newline at end of file diff --git a/playwright-tests/tests/CashReplaceWiperDropoff.ts b/playwright-tests/tests/CashReplaceWiperDropoff.ts index 09083fcd2..44a77a531 100644 --- a/playwright-tests/tests/CashReplaceWiperDropoff.ts +++ b/playwright-tests/tests/CashReplaceWiperDropoff.ts @@ -66,7 +66,7 @@ const cashReplaceWiperDropoffData: Partial = { }, { partQuestionType: PartQuestionType.GeneralQuestion2, - isOnPage: false, + isOnPage: true, optionToSelect: 'Yes' }, ] diff --git a/playwright-tests/tests/InsuranceNoCompProgressive.ts b/playwright-tests/tests/InsuranceNoCompProgressive.ts index e609d4120..a0f3a2594 100644 --- a/playwright-tests/tests/InsuranceNoCompProgressive.ts +++ b/playwright-tests/tests/InsuranceNoCompProgressive.ts @@ -32,10 +32,11 @@ const insuranceNoCompProgressiveData: Partial = { lastName: 'SCHATZ', address: { ...getDefaultTestData().customerDetails!.address, - city: 'Slidell', - state: 'Louisiana', - postalCode: '70458' - } + // city: 'Slidell', + // state: 'Louisiana', + // postalCode: '70458' + postalCode: '43085' + }, }, @@ -71,7 +72,7 @@ const insuranceNoCompProgressiveData: Partial = { // Override for in-shop appointment appointmentDetails: { serviceLocation: ServiceLocation.InShop, - shopAddress: '56705 Garrett Road, Slidell, LA 70458', + // shopAddress: '56705 Garrett Road, Slidell, LA 70458', appointmentDate: getDefaultTestData().appointmentDetails?.appointmentDate }, From e52edddbe7c9c69834d2d2c0e9e7e0d8266f16e3 Mon Sep 17 00:00:00 2001 From: kpatel8hs4io <31411746+kpatel8hs4io@users.noreply.github.com> Date: Thu, 19 Feb 2026 10:37:20 -0500 Subject: [PATCH 2/5] Added changes that support experiment management in playwright tests --- playwright-tests/.env | 9 ++- playwright-tests/.env.dev | 11 ++- playwright-tests/framework/TestData.ts | 19 ++++- .../framework/localTypes/IExperiments.ts | 4 + playwright-tests/pages/BasePage.ts | 46 ++++++++--- playwright-tests/pages/HomePage.ts | 8 +- .../pages/OrderConfirmationPage.ts | 20 ++++- playwright-tests/pages/PaymentMethodPage.ts | 32 +++++++- playwright-tests/pages/SchedulePage.ts | 23 +++++- playwright-tests/playwright.config.ts | 2 +- playwright-tests/tests/0000__M.test.ts | 4 +- .../tests/CashRepairInShopAfterPay.ts | 9 ++- .../tests/CashRepairInShopPayPal.ts | 9 ++- .../tests/CashRepairMobileCreditCard.ts | 9 ++- .../tests/CashReplaceDynamicRecalMobile.ts | 7 +- ...ReplaceGlassAddressLookupInshopAfterPay.ts | 9 ++- ...laceGlassLicensePlateLookupInshopPaypal.ts | 9 ++- .../tests/CashReplaceGlassPromoInshop.ts | 7 +- .../tests/CashReplaceMobileFirstModal.ts | 79 +++++++++++++++++++ .../tests/CashReplaceMultiGlassMobile.ts | 9 ++- .../tests/CashReplaceMultiGlassPromoInshop.ts | 9 ++- .../CashReplaceMultiSlidingGlassDropoff.ts | 9 ++- .../CashReplaceRainDefensePromoInshop.ts | 7 +- .../CashReplaceSafeliteCanNotRecalMobile.ts | 9 ++- .../tests/CashReplaceSplitWindshield.ts | 7 +- .../tests/CashReplaceStaticInshop.ts | 7 +- ...placeSwitchToInsuranceProgressiveNoComp.ts | 7 +- .../tests/CashReplaceVinMobile.ts | 7 +- .../tests/CashReplaceWiperDropoff.ts | 9 ++- .../tests/CashReplaceWiperPromoInshop.ts | 7 +- .../tests/InsuranceAcuityPaypal.ts | 9 ++- .../tests/InsuranceBigTruckVerified.ts | 9 ++- playwright-tests/tests/InsuranceGeico.ts | 9 ++- .../tests/InsuranceITAC21stCentury.ts | 9 ++- ...nceITACOptimizedPriceValidationAllState.ts | 9 ++- .../InsuranceMeemicNearSchoolVerified.ts | 9 ++- .../tests/InsuranceNoCompProgressive.ts | 9 ++- .../tests/InsuranceOEMAllState.ts | 9 ++- playwright-tests/tests/InsuranceUnverified.ts | 9 ++- 39 files changed, 404 insertions(+), 80 deletions(-) create mode 100644 playwright-tests/framework/localTypes/IExperiments.ts create mode 100644 playwright-tests/tests/CashReplaceMobileFirstModal.ts diff --git a/playwright-tests/.env b/playwright-tests/.env index 3429bc864..2b8d27d3e 100644 --- a/playwright-tests/.env +++ b/playwright-tests/.env @@ -5,7 +5,11 @@ PLAYWRIGHT_ENV="qa" # Skip content site -SKIP_CONTENT_SITE=false +SKIP_CONTENT_SITE="false" + +# Experiments Flag +IS_MOBILEFIRST="false" +IS_ADYENPAYMENTS="false" # Base URLs by environment # qa @@ -13,7 +17,8 @@ BASE_URL="https://www-qa2.safelite.com/" # local version of FMG (after running the local server) # BASE_URL="http://localhost:8080/fmg/" # qa with skipToInsurance Turned Off -# BASE_URL="https://fixmyglassqa.safelite.com/?cns=all&experiments=ConceptFunnel=ConceptFunnel_V1=ConceptFunnel_VinAndEmailOptional=true,NextGen_IGQSkipToInsurance=NextGen_IGQSkipToInsurance_V1=NextGen_IGQSkipToInsurance_CONTROL=true" +# BASE_URL="https://fixmyglassqa.safelite.com/?cns=all&experiments=ConceptFunnel=ConceptFunnel_V1=ConceptFunnel_VinAndEmailOptional=true" + # sys # BASE_URL="https://www-test2.safelite.com/fmg/?fmgPage=vehicle" # dev diff --git a/playwright-tests/.env.dev b/playwright-tests/.env.dev index f02f656d0..8d2786918 100644 --- a/playwright-tests/.env.dev +++ b/playwright-tests/.env.dev @@ -5,15 +5,20 @@ PLAYWRIGHT_ENV="qa" # Skip content site -SKIP_CONTENT_SITE=false +SKIP_CONTENT_SITE="false" + +# Experiments Flag +IS_MOBILEFIRST="false" +IS_ADYENPAYMENTS="false" # Base URLs by environment # qa -# BASE_URL="https://www-qa2.safelite.com/" +BASE_URL="https://www-qa2.safelite.com/" # local version of FMG (after running the local server) # BASE_URL="http://localhost:8080/fmg/" # qa with skipToInsurance Turned Off -BASE_URL="https://www-qa2.safelite.com/?&experiments=ConceptFunnel=ConceptFunnel_V1=ConceptFunnel_VinAndEmailOptional=true" +# BASE_URL="https://www-qa2.safelite.com/?&experiments=ConceptFunnel=ConceptFunnel_V1=ConceptFunnel_VinAndEmailOptional=true" + # sys # BASE_URL="https://www-test2.safelite.com/fmg/?fmgPage=vehicle" # dev diff --git a/playwright-tests/framework/TestData.ts b/playwright-tests/framework/TestData.ts index 9148db34e..f863a74fd 100644 --- a/playwright-tests/framework/TestData.ts +++ b/playwright-tests/framework/TestData.ts @@ -1,11 +1,22 @@ -import { ITestData as base } from 'safelite-playwright-core' +import { ITestData as base, getDefaultTestData } from 'safelite-playwright-core' import { PaymentMethod } from './localTypes/Enums' +import { IExperiments } from './localTypes/IExperiments'; export interface ITestData extends base { // Put any project-specific data here. Anything useful to other Safelite projects should be submitted as a pull request to safelite-playwright-core. paymentMethod: PaymentMethod, isOptedInForTextMessages: boolean, totalAmount?: number, - isForcedOEM?: boolean - mockFirstInshopCallNoSchedule?: boolean -} \ No newline at end of file + isForcedOEM?: boolean, + mockFirstInshopCallNoSchedule?: boolean, + handleMobileFirstModal?: boolean, + experiments?: IExperiments +} + + +export function getDefaultExperimentsData(): IExperiments { + return { + isAdyenPayments: !!process.env.IS_ADYENPAYMENTS && process.env.IS_ADYENPAYMENTS !== "" ? process.env.IS_ADYENPAYMENTS === "true" : false, + isMobileFirst: !!process.env.IS_MOBILEFIRST && process.env.IS_MOBILEFIRST !== "" ? process.env.IS_MOBILEFIRST === "true" : false, + } +} diff --git a/playwright-tests/framework/localTypes/IExperiments.ts b/playwright-tests/framework/localTypes/IExperiments.ts new file mode 100644 index 000000000..78935f480 --- /dev/null +++ b/playwright-tests/framework/localTypes/IExperiments.ts @@ -0,0 +1,4 @@ +export interface IExperiments { + isMobileFirst: boolean, + isAdyenPayments: boolean +} \ No newline at end of file diff --git a/playwright-tests/pages/BasePage.ts b/playwright-tests/pages/BasePage.ts index 4f7f54557..3ea0be374 100644 --- a/playwright-tests/pages/BasePage.ts +++ b/playwright-tests/pages/BasePage.ts @@ -142,7 +142,7 @@ export class BasePage { if (!fistInshopScheduleCall) { return route.continue(); } - + // const fmt = (d: Date) => `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,"0")}-${String(d.getDate()).padStart(2,"0")}`; // const startDate = new Date(); //const beginningOfWeek = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate() - startDate.getDay()); @@ -164,7 +164,7 @@ export class BasePage { const responseBody = await response.json(); responseBody.days = []; - + // Mock the response await route.fulfill({ response, @@ -228,19 +228,41 @@ export class BasePage { ]); } - async validateOEMPart( isOEM: boolean): Promise { + async validateOEMPart(isOEM: boolean): Promise { if (isOEM!) { - // Get Vuex state from localStorage - const vuexState = JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')')); + // 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; + // 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); + await expect(firstGlassPartNumber.includes("OEM")).toBe(true); + } else { + throw new Error("No glass parts found in the order"); + } + } + } + + async buildExperimentUrl(testData: Partial): Promise { + + let experimentsURLExtension = "?cns=all&experiments="; + + const { experiments } = testData; + + if (experiments !== undefined) { + experimentsURLExtension += experiments?.isMobileFirst + ? "MobileFirstAppointment=MobileFirstAppt_V1=MobileFirstAppt_TEST=true" + : "MobileFirstAppointment=MobileFirstAppt_V1=MobileFirstAppt_CONTROL=true"; + + experimentsURLExtension += experiments?.isAdyenPayments + ? ",Adyen%20Payments=Adyen%20Payment%20Test=Adyen%20Payment%20(Test)" + : ",Adyen%20Payments=Adyen%20Payment%20Test=CyberSource%20(Control)"; } else { - throw new Error("No glass parts found in the order"); + console.log("Url extension without query string"); } - } - } + + // Convert to HTML encoding before returning + return experimentsURLExtension; + } } \ No newline at end of file diff --git a/playwright-tests/pages/HomePage.ts b/playwright-tests/pages/HomePage.ts index 3d44b023a..e2db5ac26 100644 --- a/playwright-tests/pages/HomePage.ts +++ b/playwright-tests/pages/HomePage.ts @@ -1,5 +1,6 @@ import test, { expect, type Locator, type Page } from '@playwright/test'; import { BasePage } from './BasePage'; +import { ITestData } from 'framework/TestData'; export class HomePage extends BasePage { @@ -51,8 +52,9 @@ export class HomePage extends BasePage { this.getQuoteAndScheduleButton = this.page.getByLabel('main').getByRole('link', { name: 'Get quote + schedule' }); } - async goto() { - await this.page.goto(process.env['BASE_URL']!); + async goto(testData: Partial) { + const experimentUrlExtension = await this.buildExperimentUrl(testData); + await this.page.goto(process.env['BASE_URL']! + experimentUrlExtension); } async isCurrentVariant(): Promise { @@ -75,6 +77,4 @@ export class HomePage extends BasePage { } await this.letsGetStartedButton.click(); } - - } \ No newline at end of file diff --git a/playwright-tests/pages/OrderConfirmationPage.ts b/playwright-tests/pages/OrderConfirmationPage.ts index a65c80fd2..43ba53850 100644 --- a/playwright-tests/pages/OrderConfirmationPage.ts +++ b/playwright-tests/pages/OrderConfirmationPage.ts @@ -108,7 +108,7 @@ export class OrderConfirmationPage extends BasePage { } } - async getFormattedAppointmentDate(appointmentDate: string) { + /*async getFormattedAppointmentDate(appointmentDate: string) { // Parse the original date let parsedAppointmentDate = new Date(`${appointmentDate}` + 'T00:00:00'); @@ -116,6 +116,24 @@ export class OrderConfirmationPage extends BasePage { // Format the new date as a string let updatedAppointmentDate = parsedAppointmentDate.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' }); return updatedAppointmentDate; + }*/ + + async getFormattedAppointmentDate(appointmentDate: string) { + let formattedAppointmentDate: string; + + switch (true) { + case /^\d{4}-\d{2}-\d{2}$/.test(appointmentDate): + let parsedAppointmentDate = new Date(`${appointmentDate}` + 'T00:00:00'); + formattedAppointmentDate = parsedAppointmentDate.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' }); + break; + case /[A-Za-z]+,\s+[A-Za-z]+\s+\d{1,2}/.test(appointmentDate): + formattedAppointmentDate = appointmentDate; + break; + default: + throw new Error('Unsupported date format: ' + appointmentDate); + } + + return formattedAppointmentDate; } async toggleOrderDetailsSection() { diff --git a/playwright-tests/pages/PaymentMethodPage.ts b/playwright-tests/pages/PaymentMethodPage.ts index 015a088ae..248ce845f 100644 --- a/playwright-tests/pages/PaymentMethodPage.ts +++ b/playwright-tests/pages/PaymentMethodPage.ts @@ -487,7 +487,7 @@ export class PaymentMethodPage extends BasePage { } } - async getFormattedAppointmentDate(appointmentDate: string) { + /*async getFormattedAppointmentDate(appointmentDate: string) { // Parse the original date let parsedAppointmentDate = new Date(`${appointmentDate}` + 'T00:00:00'); @@ -495,8 +495,36 @@ export class PaymentMethodPage extends BasePage { // 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) { const { servicePackage, isRecalVehicle, paymentDetails, isForcedOEM } = testData; @@ -505,7 +533,7 @@ export class PaymentMethodPage extends BasePage { await this.validatePaymentDetailsPage(testData); await this.ValidateAfterPayBreakOutSection(); - if (isForcedOEM){ + if (isForcedOEM) { await this.validateOEMPart(isForcedOEM); } diff --git a/playwright-tests/pages/SchedulePage.ts b/playwright-tests/pages/SchedulePage.ts index fdd3f30ec..30fc830d0 100644 --- a/playwright-tests/pages/SchedulePage.ts +++ b/playwright-tests/pages/SchedulePage.ts @@ -39,6 +39,7 @@ export class SchedulePage extends BasePage { readonly yesButtonRecalAckModal: Locator; readonly noButtonRecalAckModal: Locator; readonly continueButtonRecalAckModal: Locator; + readonly mobileFirstModal: Locator; constructor(page: Page) { super(page); @@ -73,6 +74,7 @@ export class SchedulePage extends BasePage { this.yesButtonRecalAckModal = this.page.locator("label[buttonlabel='Yes']"); this.noButtonRecalAckModal = this.page.locator("label[buttonlabel='No']"); this.continueButtonRecalAckModal = this.page.locator("#recal-ack-modal-container .modal-footer button"); + this.mobileFirstModal = this.page.locator("#mobile-first-modal-container"); } async selectLocation(testData: Partial) { @@ -234,17 +236,32 @@ export class SchedulePage extends BasePage { return formattedTimeSlot; } + async handleMobileFirstPopUp(testData: Partial) { + + const { customerDetails } = testData; + customerDetails!.apptDate = (await this.mobileFirstModal.locator('li', { hasText: 'Date:' }).innerText()).replace('Date:', '').trim(); + customerDetails!.apptTime = (await this.mobileFirstModal.locator('li', { hasText: 'Appointment window:' }).innerText()).replace('Appointment window:', 'arriving between').trim(); + customerDetails!.apptDuration = (await this.mobileFirstModal.locator('li', { hasText: 'Estimate length:' }).innerText()).replace('Estimate length:', '').trim(); + + await this.mobileFirstModal.getByRole("button", { name: 'Confirm appointment'}).click(); + } + @step("SchedulePage >> Schedule appointment: ") async handleSchedulePage(testData: Partial) { - const { appointmentDetails, isCanNotRecal } = testData; + const { appointmentDetails, isCanNotRecal, experiments, handleMobileFirstModal } = testData; await this.waitForPageOrComponentload(); await this.validateProgressBar(ProgressBarPercentages.SchedulePage); - if (await this.mobileFirstModalCloseButton.isVisible()) { + + if (handleMobileFirstModal) { + return await this.handleMobileFirstPopUp(testData); + } + + if (experiments?.isMobileFirst && await this.mobileFirstModalCloseButton.isVisible()) { await this.mobileFirstModalCloseButton.click(); } await this.selectLocation(testData); - if (await this.mobileFirstModalCloseButton.isVisible({timeout: 5000})) { + if (experiments?.isMobileFirst && await this.mobileFirstModalCloseButton.isVisible({timeout: 5000})) { await this.page.waitForTimeout(1000); await this.mobileFirstModalCloseButton.dblclick(); } diff --git a/playwright-tests/playwright.config.ts b/playwright-tests/playwright.config.ts index 016977e3c..026f5b2d9 100644 --- a/playwright-tests/playwright.config.ts +++ b/playwright-tests/playwright.config.ts @@ -78,7 +78,7 @@ export default defineConfig({ /* Retry on CI only */ retries: process.env.CI ? 1 : 0, /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? 4 : 5, + workers: process.env.CI ? 4 : 3, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: process.env.CI? [ ['junit'], diff --git a/playwright-tests/tests/0000__M.test.ts b/playwright-tests/tests/0000__M.test.ts index aebb3b978..0dcb5155e 100644 --- a/playwright-tests/tests/0000__M.test.ts +++ b/playwright-tests/tests/0000__M.test.ts @@ -32,6 +32,7 @@ import insuranceNoCompProgressiveTests from "./InsuranceNoCompProgressive"; import insuranceOEMAllstateTests from "./InsuranceOEMAllState"; import cashRepairInShopAfterPayTests from "./CashRepairInShopAfterPay"; import cashRepairInShopPayPalTests from "./CashRepairInShopPayPal"; +import cashReplaceMobileFirstModalTests from "./CashReplaceMobileFirstModal"; import cashReplaceMultiSlidingGlassDropoffTests from "./CashReplaceMultiSlidingGlassDropoff"; import cashReplaceMultiGlassMobileTests from "./CashReplaceMultiGlassMobile"; import { getTestObject, TestCase, prepareTest, RuleEngine, TestInfo } from 'framework/Typedefs'; @@ -79,6 +80,7 @@ const allStandardTests = [ { name: "CashReplaceStaticInshop", tests: cashReplaceStaticInshopTests }, { name: "CashReplaceWiperPromoInshop", tests: cashReplaceWiperPromoInShopTests }, { name: "CashReplaceSwitchToInsuranceProgressiveNoComp", tests: cashReplaceSwitchToInsuranceProgressiveNoCompTests }, + { name: "CashReplaceMobileFirstModal", tests: cashReplaceMobileFirstModalTests }, // TODO: Uncomment when QA is ready to run heavy truck tests // {name: "CashReplaceSplitWindshield", tests: CashReplaceSplitWindshieldTests}, { name: "InsuranceAcuityPaypal", tests: insuranceAcuityPaypalTests }, @@ -142,7 +144,7 @@ async function run(page: Page, testInfo: TestInfo): Promise { try { await testInfo.testCase.setup(); testInfo.testCase.setupPages(page, createTestPages); - await testInfo.testCase.pages.homePage.goto(); + await testInfo.testCase.pages.homePage.goto(testInfo.testCase.testData); await runWorkflow(page, testInfo.testCase); } catch (error) { // Catch successful alert tests and log message diff --git a/playwright-tests/tests/CashRepairInShopAfterPay.ts b/playwright-tests/tests/CashRepairInShopAfterPay.ts index e328ba7e8..dc34404e5 100644 --- a/playwright-tests/tests/CashRepairInShopAfterPay.ts +++ b/playwright-tests/tests/CashRepairInShopAfterPay.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { VehicleDamage, ServiceLocation, ServicePackage } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { ClientData } from 'safelite-playwright-core'; @@ -34,7 +34,12 @@ const cashRepairInShopAfterPayData : Partial = { }, // Use predefined payment data - paymentDetails: ClientData.getDefaultAfterpayDetails() + paymentDetails: ClientData.getDefaultAfterpayDetails(), + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const cashRepairInShopAfterPayTests: ITestCase[] = []; diff --git a/playwright-tests/tests/CashRepairInShopPayPal.ts b/playwright-tests/tests/CashRepairInShopPayPal.ts index 0d559f03b..6fb3f19d4 100644 --- a/playwright-tests/tests/CashRepairInShopPayPal.ts +++ b/playwright-tests/tests/CashRepairInShopPayPal.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { VehicleDamage, ServicePackage } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { ClientData } from 'safelite-playwright-core'; @@ -35,7 +35,12 @@ const cashRepairInShopPayPalData : Partial = { }, // Use predefined payment data - paymentDetails: ClientData.getDefaultPaypalDetails() + paymentDetails: ClientData.getDefaultPaypalDetails(), + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const cashRepairInShopPayPalTests: ITestCase[] = []; diff --git a/playwright-tests/tests/CashRepairMobileCreditCard.ts b/playwright-tests/tests/CashRepairMobileCreditCard.ts index 8e38129e8..eece76520 100644 --- a/playwright-tests/tests/CashRepairMobileCreditCard.ts +++ b/playwright-tests/tests/CashRepairMobileCreditCard.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { VehicleDamage, ServiceLocation } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { ClientData } from 'safelite-playwright-core'; @@ -48,7 +48,12 @@ const cashRepairMobileCCData : Partial = { }, // Use predefined payment data - paymentDetails: ClientData.getDefaultCreditCardDetails() + paymentDetails: ClientData.getDefaultCreditCardDetails(), + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const cashRepairMobileCCTests: ITestCase[] = []; diff --git a/playwright-tests/tests/CashReplaceDynamicRecalMobile.ts b/playwright-tests/tests/CashReplaceDynamicRecalMobile.ts index ffc2c9264..8ad5f2d8e 100644 --- a/playwright-tests/tests/CashReplaceDynamicRecalMobile.ts +++ b/playwright-tests/tests/CashReplaceDynamicRecalMobile.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ServiceLocation, ServicePackage, PaymentType } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { VehicleLookupType } from 'safelite-playwright-core'; @@ -63,6 +63,11 @@ const cashReplaceDynamicRecalMobileData: Partial = { paymentType: PaymentType.PayAtService, // promoCode: 'digitalrain' promoCode: 'rain50' + }, + + // Experiments + experiments: { + ...getDefaultExperimentsData() } } diff --git a/playwright-tests/tests/CashReplaceGlassAddressLookupInshopAfterPay.ts b/playwright-tests/tests/CashReplaceGlassAddressLookupInshopAfterPay.ts index 43660fe21..50f885f96 100644 --- a/playwright-tests/tests/CashReplaceGlassAddressLookupInshopAfterPay.ts +++ b/playwright-tests/tests/CashReplaceGlassAddressLookupInshopAfterPay.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ITestCase } from '../framework/Typedefs' import { VehicleLookupType } from 'safelite-playwright-core'; import { ClientData } from 'safelite-playwright-core'; @@ -43,7 +43,12 @@ const cashReplaceGlassAddressLookupInshopAfterPayData: Partial = { }, // Override payment details - paymentDetails: ClientData.getDefaultAfterpayDetails() + paymentDetails: ClientData.getDefaultAfterpayDetails(), + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const cashReplaceGlassAddressLookupInshopAfterPayTests: ITestCase[] = []; diff --git a/playwright-tests/tests/CashReplaceGlassLicensePlateLookupInshopPaypal.ts b/playwright-tests/tests/CashReplaceGlassLicensePlateLookupInshopPaypal.ts index defa4b36c..63ebb3a64 100644 --- a/playwright-tests/tests/CashReplaceGlassLicensePlateLookupInshopPaypal.ts +++ b/playwright-tests/tests/CashReplaceGlassLicensePlateLookupInshopPaypal.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ITestCase } from '../framework/Typedefs' import { VehicleLookupType } from 'safelite-playwright-core'; import { ClientData } from 'safelite-playwright-core'; @@ -42,7 +42,12 @@ const cashReplaceGlassLicensePlateLookupInshopPaypalData: Partial = { // No need to override vehicleDamage as it already defaults to WindshieldCrack // Override payment details to use PayPal - paymentDetails: ClientData.getDefaultPaypalDetails() + paymentDetails: ClientData.getDefaultPaypalDetails(), + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const cashReplaceGlassLicensePlateLookupInshopPaypalTests: ITestCase[] = []; diff --git a/playwright-tests/tests/CashReplaceGlassPromoInshop.ts b/playwright-tests/tests/CashReplaceGlassPromoInshop.ts index 139efbb1b..a88e6941a 100644 --- a/playwright-tests/tests/CashReplaceGlassPromoInshop.ts +++ b/playwright-tests/tests/CashReplaceGlassPromoInshop.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { PaymentType } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { VehicleLookupType } from 'safelite-playwright-core'; @@ -55,6 +55,11 @@ const cashReplaceGlassPromoInshopData: Partial = { paymentType: PaymentType.PayAtService, // Add promo code - key feature of this test promoCode: '20CALL', + }, + + // Experiments + experiments: { + ...getDefaultExperimentsData() } } diff --git a/playwright-tests/tests/CashReplaceMobileFirstModal.ts b/playwright-tests/tests/CashReplaceMobileFirstModal.ts new file mode 100644 index 000000000..714d95d84 --- /dev/null +++ b/playwright-tests/tests/CashReplaceMobileFirstModal.ts @@ -0,0 +1,79 @@ +//Imports here +import { ITestData } from 'framework/TestData' +import { ServiceLocation, ServicePackage, PaymentType, PartQuestionType } from 'safelite-playwright-core'; +import { ITestCase } from '../framework/Typedefs' +import { VehicleLookupType } from 'safelite-playwright-core'; +import { getDefaultTestData, setFakerSeedFromTestName } from 'safelite-playwright-core'; +import { PaymentMethod } from 'framework/localTypes/Enums'; + +// Set the seed before generating any data +setFakerSeedFromTestName("CashReplaceMobileFirstModal"); + +// Now get the test data with the seeded faker +const cashReplaceMobileFirstModalData: Partial = { + ...getDefaultTestData(), // Get default data with current seed + + // CASH Client + paymentMethod: PaymentMethod.SelfPay, + + customerDetails: { + ...getDefaultTestData().customerDetails!, + address: { + ...getDefaultTestData().customerDetails!.address, + postalCode: '91761' + } + }, + + servicePackage: ServicePackage.GlassOnly, + // Override vehicle details + vehicleDetails: { + ...getDefaultTestData().vehicleDetails!, + year: '2018', + make: 'Honda', + model: 'Accord', + style: '4 door sedan', + }, + + handleMobileFirstModal: true, + + partQuestions: [ + { + partQuestionType: PartQuestionType.GeneralQuestion1, + isOnPage: true, + optionToSelect: 'Yes' + }, + ], + + mockFirstInshopCallNoSchedule: true, + // No need to override vehicleDamage as it already defaults to WindshieldCrack + + // Override appointment details + appointmentDetails: { + serviceLocation: ServiceLocation.Mobile, + appointmentDate: getDefaultTestData().appointmentDetails?.appointmentDate, + serviceAddress: { + // Use street address from current faker seed + street: "2064 S Sultana Ave", + city: 'Ontario', + state: 'CA', + postalCode: '91761', + country: 'United States' + }, + }, + + // Override payment details + paymentDetails: { + paymentType: PaymentType.PayAtService + } +} + +const cashReplaceMobileFirstModal: ITestCase[] = []; + +const tc = { + name: `CashReplaceMobileFirstModal`, + tags: ['@E2E','@CashReplaceMobileFirstModal', '@test_report', '@CASH'], + testData: cashReplaceMobileFirstModalData +}; +cashReplaceMobileFirstModal.push(tc); + +export default cashReplaceMobileFirstModal; \ No newline at end of file diff --git a/playwright-tests/tests/CashReplaceMultiGlassMobile.ts b/playwright-tests/tests/CashReplaceMultiGlassMobile.ts index 5db56fb85..bbf4db783 100644 --- a/playwright-tests/tests/CashReplaceMultiGlassMobile.ts +++ b/playwright-tests/tests/CashReplaceMultiGlassMobile.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { VehicleDamage, PartQuestionType, PaymentType, ServicePackage, ServiceLocation } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { VehicleLookupType } from 'safelite-playwright-core'; @@ -88,7 +88,12 @@ const cashReplaceMultiGlassMobileData: Partial = { optionToSelect: 'Gray Tint Privacy', secondaryQuestionOptionToSelect: 'solar, passenger side, encap, chrome molding' } - ] + ], + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const cashReplaceMultiGlassMobileTests: ITestCase[] = []; diff --git a/playwright-tests/tests/CashReplaceMultiGlassPromoInshop.ts b/playwright-tests/tests/CashReplaceMultiGlassPromoInshop.ts index 102c7b415..89406bc29 100644 --- a/playwright-tests/tests/CashReplaceMultiGlassPromoInshop.ts +++ b/playwright-tests/tests/CashReplaceMultiGlassPromoInshop.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { VehicleDamage, PartQuestionType, PaymentType } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { VehicleLookupType } from 'safelite-playwright-core'; @@ -94,7 +94,12 @@ const cashReplaceMultiGlassPromoInshopData: Partial = { optionToSelect: 'Green Tint', secondaryQuestionOptionToSelect: 'heated glass, solar, antenna, manual liftgate, 1 hole' } - ] + ], + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const cashReplaceMultiGlassPromoInshopTests: ITestCase[] = []; diff --git a/playwright-tests/tests/CashReplaceMultiSlidingGlassDropoff.ts b/playwright-tests/tests/CashReplaceMultiSlidingGlassDropoff.ts index 4042c67de..c687861a8 100644 --- a/playwright-tests/tests/CashReplaceMultiSlidingGlassDropoff.ts +++ b/playwright-tests/tests/CashReplaceMultiSlidingGlassDropoff.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { VehicleDamage, PartQuestionType, PaymentType, ServicePackage, ServiceLocation } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { VehicleLookupType } from 'safelite-playwright-core'; @@ -105,7 +105,12 @@ const cashReplaceMultiSlidingGlassDropoffData: Partial = { optionToSelect: 'Gray Tint Privacy', secondaryQuestionOptionToSelect: 'heated glass, solar, slider, power, kit' } - ] + ], + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const cashReplaceMultiSlidingGlassDropoffTests: ITestCase[] = []; diff --git a/playwright-tests/tests/CashReplaceRainDefensePromoInshop.ts b/playwright-tests/tests/CashReplaceRainDefensePromoInshop.ts index d9fbd1a91..8deee536a 100644 --- a/playwright-tests/tests/CashReplaceRainDefensePromoInshop.ts +++ b/playwright-tests/tests/CashReplaceRainDefensePromoInshop.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ServicePackage, PaymentType } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { VehicleLookupType } from 'safelite-playwright-core'; @@ -50,6 +50,11 @@ const cashReplaceRainDefensePromoInshopData: Partial = { // Rain Defense promo code promoCode: 'rd50' }, + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const cashReplaceRainDefensePromoInshopTests: ITestCase[] = []; diff --git a/playwright-tests/tests/CashReplaceSafeliteCanNotRecalMobile.ts b/playwright-tests/tests/CashReplaceSafeliteCanNotRecalMobile.ts index 7bbc671ee..f3d2c3010 100644 --- a/playwright-tests/tests/CashReplaceSafeliteCanNotRecalMobile.ts +++ b/playwright-tests/tests/CashReplaceSafeliteCanNotRecalMobile.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ServiceLocation, PartQuestionType, PaymentType } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { VehicleLookupType } from 'safelite-playwright-core'; @@ -70,7 +70,12 @@ const cashReplaceSafeliteCanNotRecalMobileData: Partial = { isOnPage: true, optionToSelect: 'Yes' }, - ] + ], + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const cashReplaceSafeliteCanNotRecalMobileTests: ITestCase[] = []; diff --git a/playwright-tests/tests/CashReplaceSplitWindshield.ts b/playwright-tests/tests/CashReplaceSplitWindshield.ts index 4a8d72509..dad7ff1df 100644 --- a/playwright-tests/tests/CashReplaceSplitWindshield.ts +++ b/playwright-tests/tests/CashReplaceSplitWindshield.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { PartQuestionType, PaymentType, VehicleDamage } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { VehicleLookupType } from 'safelite-playwright-core'; @@ -59,6 +59,11 @@ const CashReplaceSplitWindshieldData: Partial = { // Override payment details paymentDetails: { paymentType: PaymentType.PayAtService + }, + + // Experiments + experiments: { + ...getDefaultExperimentsData() } } diff --git a/playwright-tests/tests/CashReplaceStaticInshop.ts b/playwright-tests/tests/CashReplaceStaticInshop.ts index ca71b4cde..107b625a1 100644 --- a/playwright-tests/tests/CashReplaceStaticInshop.ts +++ b/playwright-tests/tests/CashReplaceStaticInshop.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ServicePackage, PaymentType, PartQuestionType } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { VehicleLookupType } from 'safelite-playwright-core'; @@ -64,6 +64,11 @@ const CashReplaceStaticInshopData: Partial = { paymentDetails: { paymentType: PaymentType.PayAtService }, + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const cashReplaceStaticInshopTests: ITestCase[] = []; diff --git a/playwright-tests/tests/CashReplaceSwitchToInsuranceProgressiveNoComp.ts b/playwright-tests/tests/CashReplaceSwitchToInsuranceProgressiveNoComp.ts index 91e997986..98c965b0c 100644 --- a/playwright-tests/tests/CashReplaceSwitchToInsuranceProgressiveNoComp.ts +++ b/playwright-tests/tests/CashReplaceSwitchToInsuranceProgressiveNoComp.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { Flow, ServiceLocation } from "safelite-playwright-core"; import { DamageType, PartQuestionType, PaymentType } from 'safelite-playwright-core' import { ITestCase } from '../framework/Typedefs' @@ -70,7 +70,10 @@ const cashReplaceSwitchToInsuranceProgressiveNoCompData: Partial = { appointmentDate: getDefaultTestData().appointmentDetails?.appointmentDate, }, - + // Experiments + experiments: { + ...getDefaultExperimentsData() + } }; // Create and register the test case diff --git a/playwright-tests/tests/CashReplaceVinMobile.ts b/playwright-tests/tests/CashReplaceVinMobile.ts index e81220cd0..3cdbc8cae 100644 --- a/playwright-tests/tests/CashReplaceVinMobile.ts +++ b/playwright-tests/tests/CashReplaceVinMobile.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { AppointmentTimeslot, ServiceLocation, PaymentType } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { VehicleLookupType } from 'safelite-playwright-core'; @@ -60,6 +60,11 @@ const cashReplaceVinMobileData: Partial = { paymentDetails: { paymentType: PaymentType.PayAtService }, + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const cashReplaceVinMobileTests: ITestCase[] = []; diff --git a/playwright-tests/tests/CashReplaceWiperDropoff.ts b/playwright-tests/tests/CashReplaceWiperDropoff.ts index 44a77a531..8948cceef 100644 --- a/playwright-tests/tests/CashReplaceWiperDropoff.ts +++ b/playwright-tests/tests/CashReplaceWiperDropoff.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ServicePackage, ServiceLocation, PartQuestionType, PaymentType } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { VehicleLookupType } from 'safelite-playwright-core'; @@ -69,7 +69,12 @@ const cashReplaceWiperDropoffData: Partial = { isOnPage: true, optionToSelect: 'Yes' }, - ] + ], + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const cashReplaceWiperDropoffTests: ITestCase[] = []; diff --git a/playwright-tests/tests/CashReplaceWiperPromoInshop.ts b/playwright-tests/tests/CashReplaceWiperPromoInshop.ts index 12cbbd6f1..835790051 100644 --- a/playwright-tests/tests/CashReplaceWiperPromoInshop.ts +++ b/playwright-tests/tests/CashReplaceWiperPromoInshop.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ServicePackage, ServiceLocation, PaymentType } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { VehicleLookupType } from 'safelite-playwright-core'; @@ -46,6 +46,11 @@ const cashReplaceWiperPromoInShopData: Partial = { paymentType: PaymentType.PayAtService, // Specific wiper promo code promoCode: '1WIPER0', + }, + + // Experiments + experiments: { + ...getDefaultExperimentsData() } } diff --git a/playwright-tests/tests/InsuranceAcuityPaypal.ts b/playwright-tests/tests/InsuranceAcuityPaypal.ts index 5cd3c0a40..342dc2c87 100644 --- a/playwright-tests/tests/InsuranceAcuityPaypal.ts +++ b/playwright-tests/tests/InsuranceAcuityPaypal.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ServiceLocation, DamageType, PartQuestionType, VehicleDamage, PaymentType, Flow } from 'safelite-playwright-core'; import { PaymentMethod } from "framework/localTypes/Enums"; import { ITestCase } from '../framework/Typedefs' @@ -83,7 +83,12 @@ const insuranceAcuityPaypalData: Partial = { ], // Override payment details (empty because we skip payment method page in insurance flow) - paymentDetails: {} + paymentDetails: {}, + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const insuranceAcuityPaypalTests: ITestCase[] = []; diff --git a/playwright-tests/tests/InsuranceBigTruckVerified.ts b/playwright-tests/tests/InsuranceBigTruckVerified.ts index f31918279..3e616230e 100644 --- a/playwright-tests/tests/InsuranceBigTruckVerified.ts +++ b/playwright-tests/tests/InsuranceBigTruckVerified.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ServiceLocation, DamageType, PaymentType, PartQuestionType } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { PaymentMethod } from "framework/localTypes/Enums"; @@ -67,7 +67,12 @@ const insuranceBigTruckVerifiedData: Partial = { }, // Override payment details (empty because we skip payment method page in insurance flow) - paymentDetails: {} + paymentDetails: {}, + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const insuranceBigTruckVerifiedTests: ITestCase[] = []; diff --git a/playwright-tests/tests/InsuranceGeico.ts b/playwright-tests/tests/InsuranceGeico.ts index f71917ff2..bfb8b7155 100644 --- a/playwright-tests/tests/InsuranceGeico.ts +++ b/playwright-tests/tests/InsuranceGeico.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ServiceLocation, DamageType, Flow } from 'safelite-playwright-core'; import { PaymentMethod } from "framework/localTypes/Enums"; import { ITestCase } from '../framework/Typedefs' @@ -64,7 +64,12 @@ const insuranceGeicoData: Partial = { }, // Override payment details (empty because we skip payment method page in insurance flow) - paymentDetails: {} + paymentDetails: {}, + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const insuranceGeicoTests: ITestCase[] = []; diff --git a/playwright-tests/tests/InsuranceITAC21stCentury.ts b/playwright-tests/tests/InsuranceITAC21stCentury.ts index cb11b4083..ba596ee54 100644 --- a/playwright-tests/tests/InsuranceITAC21stCentury.ts +++ b/playwright-tests/tests/InsuranceITAC21stCentury.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ServiceLocation, DamageType, PartQuestionType, Flow } from 'safelite-playwright-core'; import { PaymentMethod } from "framework/localTypes/Enums"; import { ITestCase } from '../framework/Typedefs' @@ -68,7 +68,12 @@ const insuranceITAC21stCenturyData: Partial = { isOnPage: true, optionToSelect: 'Yes' }, - ] + ], + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const insuranceITAC21stCenturyTests: ITestCase[] = []; diff --git a/playwright-tests/tests/InsuranceITACOptimizedPriceValidationAllState.ts b/playwright-tests/tests/InsuranceITACOptimizedPriceValidationAllState.ts index 5924830ba..da876b238 100644 --- a/playwright-tests/tests/InsuranceITACOptimizedPriceValidationAllState.ts +++ b/playwright-tests/tests/InsuranceITACOptimizedPriceValidationAllState.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { VehicleDamage, ServiceLocation, DamageType, Flow } from 'safelite-playwright-core'; import { PaymentMethod } from "framework/localTypes/Enums"; import { ITestCase } from '../framework/Typedefs' @@ -65,7 +65,12 @@ const insuranceITACOptimizedPriceValidationAllStateData: Partial = { appointmentDate: getDefaultTestData().appointmentDetails?.appointmentDate }, // Override payment details (empty because we skip payment method page in insurance flow) - paymentDetails: {} + paymentDetails: {}, + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const insuranceITACOptimizedPriceValidationAllStateTests: ITestCase[] = []; diff --git a/playwright-tests/tests/InsuranceMeemicNearSchoolVerified.ts b/playwright-tests/tests/InsuranceMeemicNearSchoolVerified.ts index c57526e1a..0d1554f3e 100644 --- a/playwright-tests/tests/InsuranceMeemicNearSchoolVerified.ts +++ b/playwright-tests/tests/InsuranceMeemicNearSchoolVerified.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ServiceLocation, DamageType, Flow, PartQuestionType, LossLocation } from 'safelite-playwright-core'; import { PaymentMethod } from "framework/localTypes/Enums"; import { ITestCase } from '../framework/Typedefs' @@ -62,7 +62,12 @@ const insuranceMeemicNearSchoolVerifiedData: Partial = { ], // Override payment details (empty because we skip payment method page in insurance flow) - paymentDetails: {} + paymentDetails: {}, + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } }; const insuranceMeemicNearSchoolVerifiedTests: ITestCase[] = []; diff --git a/playwright-tests/tests/InsuranceNoCompProgressive.ts b/playwright-tests/tests/InsuranceNoCompProgressive.ts index a0f3a2594..da24e2b55 100644 --- a/playwright-tests/tests/InsuranceNoCompProgressive.ts +++ b/playwright-tests/tests/InsuranceNoCompProgressive.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ServiceLocation, DamageType, PaymentType, PartQuestionType, Flow } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { PaymentMethod } from "framework/localTypes/Enums"; @@ -77,7 +77,12 @@ const insuranceNoCompProgressiveData: Partial = { }, // Override payment details (empty because we skip payment method page in insurance flow) - paymentDetails: {} + paymentDetails: {}, + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const insuranceNoCompProgressiveTests: ITestCase[] = []; diff --git a/playwright-tests/tests/InsuranceOEMAllState.ts b/playwright-tests/tests/InsuranceOEMAllState.ts index c55939a4c..e3f5f56a8 100644 --- a/playwright-tests/tests/InsuranceOEMAllState.ts +++ b/playwright-tests/tests/InsuranceOEMAllState.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ServiceLocation, DamageType, PaymentType, PartQuestionType } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { PaymentMethod } from "framework/localTypes/Enums"; @@ -73,7 +73,12 @@ const insuranceOEMAllstateData: Partial = { }, // Override payment details (empty because we skip payment method page in insurance flow) - paymentDetails: {} + paymentDetails: {}, + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const insuranceOEMAllstateTests: ITestCase[] = []; diff --git a/playwright-tests/tests/InsuranceUnverified.ts b/playwright-tests/tests/InsuranceUnverified.ts index 34d06697d..d6d71531b 100644 --- a/playwright-tests/tests/InsuranceUnverified.ts +++ b/playwright-tests/tests/InsuranceUnverified.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ServiceLocation, DamageType, PaymentType, PartQuestionType } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { PaymentMethod } from "framework/localTypes/Enums"; @@ -72,7 +72,12 @@ const insuranceUnverifiedData: Partial = { }, // Override payment details (empty because we skip payment method page in insurance flow) - paymentDetails: {} + paymentDetails: {}, + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } } const insuranceUnverifiedTests: ITestCase[] = []; From b81ea144b8b43d7a0b0d4adaa4add8bbe2acde9a Mon Sep 17 00:00:00 2001 From: kpatel8hs4io <31411746+kpatel8hs4io@users.noreply.github.com> Date: Thu, 19 Feb 2026 12:01:07 -0500 Subject: [PATCH 3/5] Minor change for mobile first test --- playwright-tests/tests/CashReplaceMobileFirstModal.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/playwright-tests/tests/CashReplaceMobileFirstModal.ts b/playwright-tests/tests/CashReplaceMobileFirstModal.ts index 714d95d84..16ec6d0a2 100644 --- a/playwright-tests/tests/CashReplaceMobileFirstModal.ts +++ b/playwright-tests/tests/CashReplaceMobileFirstModal.ts @@ -1,5 +1,5 @@ //Imports here -import { ITestData } from 'framework/TestData' +import { ITestData, getDefaultExperimentsData } from 'framework/TestData' import { ServiceLocation, ServicePackage, PaymentType, PartQuestionType } from 'safelite-playwright-core'; import { ITestCase } from '../framework/Typedefs' import { VehicleLookupType } from 'safelite-playwright-core'; @@ -64,6 +64,12 @@ const cashReplaceMobileFirstModalData: Partial = { // Override payment details paymentDetails: { paymentType: PaymentType.PayAtService + }, + + // Experiments + experiments: { + ...getDefaultExperimentsData(), + isMobileFirst: true } } From e1b44af0fc0df27c8638a0a28d8586454ddd65da Mon Sep 17 00:00:00 2001 From: kpatel8hs4io <31411746+kpatel8hs4io@users.noreply.github.com> Date: Thu, 19 Feb 2026 12:23:49 -0500 Subject: [PATCH 4/5] adding on more change. --- playwright-tests/pages/BasePage.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/playwright-tests/pages/BasePage.ts b/playwright-tests/pages/BasePage.ts index 3ea0be374..07d254391 100644 --- a/playwright-tests/pages/BasePage.ts +++ b/playwright-tests/pages/BasePage.ts @@ -246,11 +246,12 @@ export class BasePage { async buildExperimentUrl(testData: Partial): Promise { - let experimentsURLExtension = "?cns=all&experiments="; + let experimentsURLExtension = ""; const { experiments } = testData; if (experiments !== undefined) { + experimentsURLExtension += "?cns=all&experiments=" experimentsURLExtension += experiments?.isMobileFirst ? "MobileFirstAppointment=MobileFirstAppt_V1=MobileFirstAppt_TEST=true" : "MobileFirstAppointment=MobileFirstAppt_V1=MobileFirstAppt_CONTROL=true"; From 3823054faaaf7c10744628e0f87521b90b3932c3 Mon Sep 17 00:00:00 2001 From: CarlNation Date: Fri, 20 Feb 2026 15:57:56 -0500 Subject: [PATCH 5/5] CASH-2417 save lock token on load session to state CASH-2417 save lock token on load session to state --- src/global-methods.js | 8 +++++++- src/store/index.js | 9 +++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/global-methods.js b/src/global-methods.js index fc0b3cb41..4f1dc601b 100644 --- a/src/global-methods.js +++ b/src/global-methods.js @@ -101,7 +101,13 @@ export default { return resolve(response); }, (error) => { - if (endpoint.toLowerCase().includes(endpoints.LogDigitalConsumer.url)) { + if ( + endpoint.toLowerCase().includes(endpoints.LogDigitalConsumer.url) || + endpoint.toLowerCase().includes(endpoints.LogFmgSessionData.url) || + endpoint.toLowerCase().includes(endpoints.LogPageView.url) || + endpoint.toLowerCase().includes(endpoints.LogCustomEvent.url) || + endpoint.toLowerCase().includes(endpoints.LogPartQuestions.url) + ) { return resolve({ data: null, error: "Ignore errors when logging" }); } else { if (logApiCall) { diff --git a/src/store/index.js b/src/store/index.js index c5d9846f9..73e3c4cbe 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -686,6 +686,15 @@ export const mutations = { state.order.eon = sessionInformation.order.eon; state.order.customerPortalLoginToken = sessionInformation.order.CustomerPortalLoginToken; state.order.isRecalAckOptIn = sessionInformation.order.isRecalAckOptIn; + state.order.workOrderId = sessionInformation.order.workOrderId + ? sessionInformation.order.workOrderId + : null; + state.order.workOrderNumber = sessionInformation.order.workOrderNumber + ? sessionInformation.order.workOrderNumber + : null; + state.order.lockToken = sessionInformation.order.lockToken + ? sessionInformation.order.lockToken + : null; if (state.order.vehicle.vin !== sessionInformation.order.vehicle?.vin) { state.applicationUser.pageData[routeData.PART_QUESTIONS.name] = null;