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"; import repairAndReplaceTests from "./alert-validation/alert0002_RepairAndReplace"; import splitWindshieldTests from "./alert-validation/alert0003_SplitWindshield"; import repairOnlyTests from "./alert-validation/alert0004_RepairOnly"; import unserviceableZipTests from "./alert-validation/alert0005_UnserviceableZip"; import invalidZipTests from "./alert-validation/alert0006_InvalidZip"; import vinNotFoundTests from "./alert-validation/alert0007_VinNotFound"; import { TestSuccessAlert } from 'safelite-playwright-core'; import { VehicleLookupType, VehicleDamage, PaymentType } from 'safelite-playwright-core'; import { PaymentMethod } from "framework/localTypes/Enums"; import cashRepairMobileCCTests from "./CashRepairMobileCreditCard"; import cashReplaceDynamicRecalMobileTests from "./CashReplaceDynamicRecalMobile"; import cashReplaceGlassAddressLookupInshopAfterPayTests from "./CashReplaceGlassAddressLookupInshopAfterPay"; import cashReplaceGlassLicensePlateLookupInshopPaypalTests from "./CashReplaceGlassLicensePlateLookupInshopPaypal"; import { ApiResponseInterceptUtil } from 'safelite-playwright-core'; import cashReplaceGlassPromoInshopTests from "./CashReplaceGlassPromoInshop"; import cashReplaceMultiGlassPromoInshopTests from "./CashReplaceMultiGlassPromoInshop"; import cashReplaceStaticInshopTests from "./CashReplaceStaticInshop"; import cashReplaceStaticMobileMSRTests from "./CashReplaceStaticMobileMSR"; import cashReplaceRainDefensePromoInshopTests from "./CashReplaceRainDefensePromoInshop"; import cashReplaceSafeliteCanNotRecalMobileTests from "./CashReplaceSafeliteCanNotRecalMobile"; import cashReplaceVinMobileTests from "./CashReplaceVinMobile"; import cashReplaceWiperDropoffTests from "./CashReplaceWiperDropoff"; import cashReplaceWiperPromoInShopTests from "./CashReplaceWiperPromoInshop"; import insuranceAcuityPaypalTests from "./InsuranceAcuityPaypal"; // import insuranceUSAAMsrTests from "./InsuranceUSAAMsr"; import insuranceITAC21stCenturyTests from "./InsuranceITAC21stCentury"; import insuranceGeicoTests from "./InsuranceGeico"; import insuranceITACOptimizedPriceValidationAllStateTests from "./InsuranceITACOptimizedPriceValidationAllState"; 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'; import { createTestPages } from "framework/TestPages"; import cashReplaceSwitchToInsuranceProgressiveNoCompTests from "./CashReplaceSwitchToInsuranceProgressiveNoComp"; import insuranceUSAABigTruckVerifiedTests from "./InsuranceUSAABigTruckVerified"; import partsNotFoundBailoutTests from "./PartsNotFoundBailout"; import insuranceUnverifiedTests from "./InsuranceUnverified"; import CashReplaceSplitWindshieldTests from "./CashReplaceSplitWindshield"; import insuranceMeemicNearSchoolVerifiedTests from "./InsuranceMeemicNearSchoolVerified"; import insuranceLibertyMutualTests from "./InsuranceLibertyMutual"; import cashReplaceDualMobileMSRTests from "./CashReplaceDualMobileMSR"; import cashReplaceDualNonMSRInshopTests from "./CashReplaceDualNonMSRInshop"; const test = getTestObject(); /** * Master Test Runner * * This file orchestrates the execution of all test scenarios and defines the primary test workflow. * It imports all test cases and executes them through a common test runner function to ensure * consistent behavior across different test scenarios. */ // Initialize shared rule engine and validation options const ruleEngine = new RuleEngine(); const options = new ValidationOptions(); // Add smoke tag to selected tests for CI/CD pipelines 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: "CashReplaceStaticInshop", tests: cashReplaceStaticInshopTests }, { name: "CashReplaceDualMobileMSR", tests: cashReplaceDualMobileMSRTests }, { name: "CashReplaceDualNonMSRInshop", tests: cashReplaceDualNonMSRInshopTests }, { name: "CashReplaceStaticMobileMSR", tests: cashReplaceStaticMobileMSRTests }, { 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 }, // { name: "InsuranceUSAAMsr", tests: insuranceUSAAMsrTests }, { name: "InsuranceITAC21stCentury", tests: insuranceITAC21stCenturyTests }, { name: "InsuranceNoCompProgressive", tests: insuranceNoCompProgressiveTests }, {name: "InsuranceUSAABigTruckVerified", tests: insuranceUSAABigTruckVerifiedTests}, { name: "InsuranceOEMAllstate", tests: insuranceOEMAllstateTests }, { name: "InsuranceUnverified", tests: insuranceUnverifiedTests }, // {name: "InsuranceGeico", tests: insuranceGeicoTests}, // {name: "InsuranceITACOptimizedPriceValidationAllState", tests: insuranceITACOptimizedPriceValidationAllStateTests} { name: "InsuranceMeemicNearSchoolVerified", tests: insuranceMeemicNearSchoolVerifiedTests }, { name: "InsuranceLibertyMutual", tests: insuranceLibertyMutualTests }, // { name: "PartsNotFoundBailout", tests: partsNotFoundBailoutTests }, // code is not available in QA ]; // Alert validation scenarios const allAlertTests = [ //TODO: Uncomment when QA is ready to run heavy truck tests // { name: "Alert Scenario 1: Heavy Truck", tests: heavyTruckTests }, { name: "Alert Scenario 2: Repair and Replace", tests: repairAndReplaceTests }, // { name: "Alert Scenario 3: Split Windshield", tests: splitWindshieldTests }, { name: "Alert Scenario 4: Repair Only", tests: repairOnlyTests }, { name: "Alert Scenario 5: Unserviceable Zip", tests: unserviceableZipTests }, { name: "Alert Scenario 6: Invalid Zip", tests: invalidZipTests }, { name: "Alert Scenario 7: VIN Not Found", tests: vinNotFoundTests } ]; // Standard test cases test.describe.parallel('Standard E2E Test Flows', () => { allStandardTests.forEach(scenario => { scenario.tests.forEach(testCase => { test(...prepareTest(testCase, createTestPages, run, options, ruleEngine)); }); }); }); // Alert validation test cases test.describe.parallel('Alert Validation Tests', () => { allAlertTests.forEach(scenario => { test.describe(scenario.name, () => { scenario.tests.forEach(testCase => { test(...prepareTest(testCase, createTestPages, run, options, ruleEngine)); }); }); }); }); /** * After each test, capture screenshot and handle cleanup */ test.afterEach(async ({ page, testInfo }) => { await TestCase.afterEachMethod(page, testInfo); }); /** * Main test runner function that executes each test case * @param page - The Playwright page object * @param testInfo - Test information and context */ async function run(page: Page, testInfo: TestInfo): Promise { try { await testInfo.testCase.setup(); testInfo.testCase.setupPages(page, createTestPages); await testInfo.testCase.pages.homePage.goto(testInfo.testCase.testData); await runWorkflow(page, testInfo.testCase); } catch (error) { // Catch successful alert tests and log message if (error instanceof TestSuccessAlert) { console.log(error.message); // Catch and throw other errors } else { throw error; } } } /** * Main workflow that executes the test steps in sequence * * This function contains the common test flow for all test scenarios: * 1. Start at home page and enter vehicle/damage information * 2. Select lookup method and provide details * 3. Answer part questions if applicable * 4. Select service package and payment method * 5. Handle insurance flow if applicable * 6. Select service location and schedule appointment * 7. Complete order and validate confirmation * * @param page - The Playwright page object * @param testCase - The test case to execute */ async function runWorkflow(page: Page, testCase: TestCase) { // Intercept API Responses const apiResponseInterceptUtil = new ApiResponseInterceptUtil(testCase.testData); page.on('response', apiResponseInterceptUtil.handleInterceptResponse); // Destructure test data for easier access const { paymentMethod, customerDetails, vehicleDetails, vehicleDamage, paymentDetails, partQuestions, isEnterFunnelWithZip, capabilityQuestions, vehiclePartQuestions, moldingQuestions, isSkipEstimatePage } = testCase.testData; // Check if the vehicle damage includes a windshield crack const hasWindshieldCrack = vehicleDamage!.some(damage => damage === VehicleDamage.WindshieldCrack || damage === VehicleDamage.DriverSplitWindshield || damage === VehicleDamage.PassengerSplitWindshield || damage === VehicleDamage.SingleSplitWindshield ); //============================= 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") { await test.step('HomePage >> Lets Get Started', async () => { let homePage = testCase.pages.homePage; console.log(`Customer for this test: ${customerDetails?.firstName} ${customerDetails?.lastName}`); await homePage.letsGetStarted(customerDetails?.address.postalCode!, !!isEnterFunnelWithZip!); }); } else { // If enterFunnelWithZip is true, add zip code to url if (!!isEnterFunnelWithZip!) { const currentUrl = process.env['BASE_URL']!; const zipParam = `&zipCode=${customerDetails?.address.postalCode!}`; if (!currentUrl.includes('zipCode=')) { await page.goto(currentUrl + zipParam); } } 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); // Handle vehicle damage page let vehicleDamagePage = testCase.pages.vehicleDamagePage; await vehicleDamagePage.handleVehicleDamagePage(testCase.testData); // If the vehicle has a windshield crack as part of its damage, go to estimate page and select lookup type 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); break; } } else { // Otherwise just use zip lookup for service zip page let serviceZipPage = testCase.pages.serviceZipPage; await serviceZipPage.handleServiceZipPage(testCase.testData); } // Handle bailout page if parts bailout scenario if (testCase.testData.bailoutFlags?.isVehicleLookupBailout) { let bailoutPage = testCase.pages.bailoutPage; await bailoutPage.handleBailoutPage(testCase.testData); let bailoutSuccessPage = testCase.pages.bailoutSuccessPage; await bailoutSuccessPage.handleBailoutSuccessPage(); } // Handle part questions if applicable if (partQuestions && partQuestions.length > 0) { 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; await moldingQuestionsPage.handleMoldingQuestionsPage(testCase.testData); } // Handle vehicle part questions if applicable if (vehiclePartQuestions && vehiclePartQuestions.length > 0) { let vehiclePartsPage = testCase.pages.vehiclePartsPage; await vehiclePartsPage.handleVehiclePartsPage(testCase.testData); } // Handle capability questions if applicable if (capabilityQuestions && capabilityQuestions.length > 0) { let capabilityQuestionsPage = testCase.pages.capabilityQuestionsPage; await capabilityQuestionsPage.handleCapabilityQuestionsPage(testCase.testData); } // Select service package and payment method let servicePackagesPage = testCase.pages.servicePackagesPage; await servicePackagesPage.handleServicePackagePage(testCase.testData); //============================= INSURANCE FLOW =============================` // Insurance flow - if user selected Insurance as Payment Method if (paymentMethod == PaymentMethod.Insurance) { await handleInsuranceFlow(testCase); if (testCase.testData.isPolicyInfoPage) return; // If Liberty Mutual and on PolicyInfoPage, skip the rest of the flow until the page is built out } //============================= SERVICE SCHEDULING ============================= // Select service location // let serviceLocationPage = testCase.pages.serviceLocationPage; // await serviceLocationPage.handleServiceLocationPage(testCase.testData); // 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); } // Enter contact details let contactDetailsPage = testCase.pages.contactDetailsPage; await contactDetailsPage.handleContactDetailsPage(testCase.testData); //============================= PAYMENT PROCESSING ============================= // Handle payment let paymentMethodPage = testCase.pages.paymentMethodPage; await paymentMethodPage.handlePaymentMethodPage(testCase.testData); // If user selected Pay with Insurance as Payment Method, Enter Insurance Flow if (paymentDetails?.paymentType === PaymentType.PayWithInsurance) { await handleInsuranceFlow(testCase); } //============================= ORDER CONFIRMATION ============================= // Validate order confirmation let orderConfirmationPage = testCase.pages.orderConfirmationPage; await orderConfirmationPage.verifyOrderConfirmationPage(testCase.testData); } 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); // 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); } // Liberty Mutual route directly to PolicyInfoPage instead of CCPolicyInfoPage if (testCase.testData.isPolicyInfoPage) { let policyInfoPage = testCase.pages.policyInfoPage; await policyInfoPage.handlePolicyInfoPage(testCase.testData); return; } // 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); } // 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 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(); //paymentmethods let paymentMethodPage = testCase.pages.paymentMethodPage; await paymentMethodPage.nextPage(); } }