diff --git a/environment-variables.js b/environment-variables.js index 4d894b006..17c797cfd 100644 --- a/environment-variables.js +++ b/environment-variables.js @@ -13,6 +13,8 @@ const environmentVariables = { ['__VUE_APP_SAFELITE_HOP__']: "https://sv2-safelitehop-dev.safelite.com/fmgCheckoutShared.aspx", ['__VUE_APP_SESSION_TIMEOUT_MINUTES__']: 30, ['__VUE_APP_SOLARWINDS_MONITORING_SCRIPT__']: "", + ['__VUE_APP_ADYEN_ENVIRONMENT__']: "test", + ['__VUE_APP_ADYEN_CLIENT_KEY__']: "test_WYGT4F5ZT5BRRL5MPWHK6QLYOIXZXROD", }; const keysOnly = Object.keys(environmentVariables); diff --git a/jest.config.js b/jest.config.js index 10af0ccf0..6c0addf5f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -10,7 +10,6 @@ module.exports = { }, transformIgnorePatterns: ["'/node_modules/(?!vee-validate)"], moduleFileExtensions: ["js", "vue"], - modulePathIgnorePatterns: ["vin-lookup"], collectCoverageFrom: [ "src/**/*.{js,vue}", "!src/main.js", @@ -33,11 +32,7 @@ module.exports = { "!src/layouts/payment-pia-return/*.vue", // Temp test exclusion while in development "!src/layouts/insurance/*.vue", // Temp test exclusion while in development "!src/layouts/insurance-company/*.vue", // Temp test exclusion while in development - "!src/layouts/schedule/**/*.vue", // Temp test exclusion while in development "!src/digital-components/date-picker/**/*.vue", // Temp test exclusion while in development - - "!src/**/*-june-2025.vue", // Exclude these temporary files for CASH-845 project - "!src/layouts/insurance-company/insurance-company-question/*.vue", // Temp test exclusion while in development "!src/experiment-components/*.vue", // END @@ -45,7 +40,7 @@ module.exports = { testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"], coverageThreshold: { global: { - statements: 64, + statements: 68, }, }, // Uncomment this to avoid the massive amount of warnings we are getting for onSubmit and onInvalidSubmit diff --git a/package-lock.json b/package-lock.json index ece184f89..91f3245d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "Safelite", "version": "0.1.0", "dependencies": { + "@adyen/adyen-web": "^6.27.0", "@iframe-resizer/child": "^5.3.3", "axios": "^0.30.2", "bootstrap": "^5.3.3", @@ -59,6 +60,18 @@ "node": "8 || 9 || 10 || 11 || 12 || 13 || 14 || 15 || 16 || 17 || 18 || 19 || 20 || 21 || 22" } }, + "node_modules/@adyen/adyen-web": { + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/@adyen/adyen-web/-/adyen-web-6.27.0.tgz", + "integrity": "sha512-6E6hqEkMkoY/FevjO53zhgeCWMt+i/PDc11MX6eVM7Fvd+Sdgc9cIwTBz+NKIXs4ZerG6n2gd+ZIZEywqF/aBQ==", + "license": "MIT", + "dependencies": { + "@types/applepayjs": "14.0.9", + "@types/googlepay": "0.7.8", + "classnames": "2.5.1", + "preact": "10.22.1" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -3173,6 +3186,12 @@ "node": ">=10.13.0" } }, + "node_modules/@types/applepayjs": { + "version": "14.0.9", + "resolved": "https://registry.npmjs.org/@types/applepayjs/-/applepayjs-14.0.9.tgz", + "integrity": "sha512-xEprYbb0TEP/XIiDPbVnTYpDai8fTFpsQfVSfTd81Is2GOMUy7ie019eyX6Mz2ECxfjoUVKaiGSL577roIeHCg==", + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3328,6 +3347,12 @@ "@types/send": "*" } }, + "node_modules/@types/googlepay": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/@types/googlepay/-/googlepay-0.7.8.tgz", + "integrity": "sha512-EHl7G7jIeit/Px/fi/Du/19SZDvqrRhy0DjJX40Vzs/97m3sro9mzVyAYNC0pfzpWGDY+zPjtTLEfhV+OYWUcQ==", + "license": "MIT" + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -5789,6 +5814,12 @@ "dev": true, "license": "MIT" }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, "node_modules/clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", @@ -13681,6 +13712,16 @@ "dev": true, "license": "MIT" }, + "node_modules/preact": { + "version": "10.22.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.22.1.tgz", + "integrity": "sha512-jRYbDDgMpIb5LHq3hkI0bbl+l/TQ9UnkdQ0ww+lp+4MMOdqaUYdFc5qeyP+IV8FAd/2Em7drVPeKdQxsiWCf/A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", diff --git a/package.json b/package.json index 7d06ad112..c55259920 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "lint": "vue-cli-service lint" }, "dependencies": { + "@adyen/adyen-web": "^6.27.0", "@iframe-resizer/child": "^5.3.3", "axios": "^0.30.2", "bootstrap": "^5.3.3", 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 9ed648426..f863a74fd 100644 --- a/playwright-tests/framework/TestData.ts +++ b/playwright-tests/framework/TestData.ts @@ -1,9 +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 -} \ No newline at end of file + paymentMethod: PaymentMethod, + isOptedInForTextMessages: boolean, + totalAmount?: number, + 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/TestPages.ts b/playwright-tests/framework/TestPages.ts index e873a1c7b..c2cc04b35 100644 --- a/playwright-tests/framework/TestPages.ts +++ b/playwright-tests/framework/TestPages.ts @@ -24,6 +24,7 @@ import { CCPolicyInfoPage } from "../pages/CCPolicyInfoPage" import { DuplicateCheckPage } from "../pages/DuplicateCheckPage" import { PolicyVehiclesPage } from "../pages/PolicyVehiclesPage" import { PolicyInfoSubmittedPage } from "../pages/PolicyInfoSubmittedPage" +import { HeritageProblemGlassQuestionsPage } from "../pages/HeritageProblemGlassQuestionsPage" import RecalibrationInfoPage from "../pages/RecalibrationInfoPage" import { CoverageStatementPage } from "../pages/CoverageStatementPage" import { VerifyDetailsPage } from "../pages/VerifyDetailsPage" @@ -48,6 +49,7 @@ export interface ITestPages { paymentMethodPage: PaymentMethodPage, policyInfoSubmittedPage: PolicyInfoSubmittedPage, policyVehiclesPage: PolicyVehiclesPage, + heritageProblemGlassQuestionsPage: HeritageProblemGlassQuestionsPage, recalibrationInfoPage: RecalibrationInfoPage, schedulePage: SchedulePage, mobileDetailsPage: MobileDetailsPage, @@ -83,6 +85,7 @@ export const createTestPages: TestPagesFactory = (page: Page) => { paymentMethodPage: new PaymentMethodPage(page), policyInfoSubmittedPage: new PolicyInfoSubmittedPage(page), policyVehiclesPage: new PolicyVehiclesPage(page), + heritageProblemGlassQuestionsPage: new HeritageProblemGlassQuestionsPage(page), recalibrationInfoPage: new RecalibrationInfoPage(page), schedulePage: new SchedulePage(page), mobileDetailsPage: new MobileDetailsPage(page), 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 6596986df..07d254391 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) => { @@ -169,4 +212,58 @@ export class BasePage { Soft.expect(actualProgressPercentage).toBe(progressPercentage); 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 + const vuexState = JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')')); + + // Validate the presence of an OEM part + if (vuexState.order?.lineItems?.glassParts?.length > 0) { + const firstGlassPartNumber = vuexState.order.lineItems.glassParts[0].partNumber; + + await expect(firstGlassPartNumber.includes("OEM")).toBe(true); + } else { + throw new Error("No glass parts found in the order"); + } + } + } + + async buildExperimentUrl(testData: Partial): Promise { + + 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"; + + experimentsURLExtension += experiments?.isAdyenPayments + ? ",Adyen%20Payments=Adyen%20Payment%20Test=Adyen%20Payment%20(Test)" + : ",Adyen%20Payments=Adyen%20Payment%20Test=CyberSource%20(Control)"; + } else { + 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/HeritageProblemGlassQuestionsPage.ts b/playwright-tests/pages/HeritageProblemGlassQuestionsPage.ts new file mode 100644 index 000000000..097fd5200 --- /dev/null +++ b/playwright-tests/pages/HeritageProblemGlassQuestionsPage.ts @@ -0,0 +1,42 @@ +import { expect, type Locator, type Page } from '@playwright/test'; +import { InsuranceBasePage } from './InsuranceBasePage'; +import { ITestData } from 'framework/TestData'; +import { step } from 'framework/localTypes/Step'; + +export class HeritageProblemGlassQuestionsPage extends InsuranceBasePage { + readonly provideVinManuallyButton: Locator; + readonly provideLicensePlateButton: Locator; + readonly provideHomeAddressButton: Locator; + readonly continueOnVinPopUpButton: Locator; + readonly dontShareVinButton: Locator; + + readonly yestoGlassQuestionButton: Locator; + readonly notoGlassQuestionButton: Locator; + readonly continueButton: Locator; + + + constructor(page: Page) { + super(page); + + this.provideVinManuallyButton = page.locator('label[for="VINManually"]'); + this.provideLicensePlateButton = page.locator('label[for="licensePlate"]'); + this.provideHomeAddressButton = page.locator('label[for="HomeAddress"]'); + this.continueOnVinPopUpButton = page.locator('button:has-text("Continue")'); + this.dontShareVinButton = page.getByRole('button', { name: "I would rather not share my VIN" }); + + this.yestoGlassQuestionButton = page.locator('label:has-text("Yes")'); + this.notoGlassQuestionButton = page.locator('label:has-text("No")'); + this.continueButton = page.locator('button[type="submit"].btn-success.nav-continue'); + + } + + + @step("HeritageProblemGlassQuestionsPage >> Handle VIN popup and answer glass question on heritage") + // Handles OEM scenario with vehicle that has only one glass question + async handleHeritageProblemGlassQuestionsPage(testData: Partial){ + + await this.dontShareVinButton.click(); + await this.yestoGlassQuestionButton.click(); + await this.continueButton.click(); + } +} \ No newline at end of file diff --git a/playwright-tests/pages/HomePage.ts b/playwright-tests/pages/HomePage.ts index b28dd75a4..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 { @@ -30,7 +31,7 @@ export class HomePage extends BasePage { super(page); this.page = page; - this.letsGetStartedButton = this.page.locator('.hero-content #zipCodeTextboxButton'); + this.letsGetStartedButton = this.page.locator('#zipCodeTextboxButton'); this.cusmodalPopup = this.page.locator('#Cusmodalpopup'); this.closePopupButton = this.page.getByRole('button', { name: '×' }); @@ -47,12 +48,13 @@ export class HomePage extends BasePage { //Zip Entry this.enterServiceZipTextBox = this.page.locator('#zipCodeTextbox'); - this.zipEntryLetsGetStartedButton = this.page.locator('#zipCodeTextboxButton'); + this.zipEntryLetsGetStartedButton = this.page.locator('#zipCodeTextbox'); 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 { @@ -61,10 +63,11 @@ export class HomePage extends BasePage { async letsGetStarted(zip: string, enterFunnelWithZip: boolean) { if (enterFunnelWithZip) { - await this.letsGetStartedButton.evaluate((element, zip) => { + await this.zipEntryLetsGetStartedButton.fill(zip); + /* await this.letsGetStartedButton.evaluate((element, zip) => { const currentHref = element.getAttribute('href') || ''; - element.setAttribute('href', `${currentHref}&zipCode=${zip}`); - }, zip); + element.setAttribute('href', `${currentHref}?zipCode=${zip}`); + }, zip);*/ } // Wait until modal pop up opens and close pop up @@ -74,6 +77,4 @@ export class HomePage extends BasePage { } await this.letsGetStartedButton.click(); } - - } \ No newline at end of file diff --git a/playwright-tests/pages/LookupPage.ts b/playwright-tests/pages/LookupPage.ts index e5f2fca04..7d6c1df03 100644 --- a/playwright-tests/pages/LookupPage.ts +++ b/playwright-tests/pages/LookupPage.ts @@ -81,9 +81,9 @@ export class LookupPage extends BasePage { // Map of expected messages by lookup type const expectedMessages = { - 'address': "Your address didn't return a VIN match.Please re-enter the information below or provide your VIN in a different way.", - 'license plate': "Your license plate didn’t return a VIN match.Please re-enter the information above or provide your VIN in a different way.", - 'VIN': "Your VIN didn’t return a vehicle matchPlease re-enter your VIN or we can look up your VIN for you.", + 'address': "Your address didn't return a VIN match.Please re-enter the information below or provide your VIN in a different way.", + 'license plate': "Your license plate didn’t return a VIN match.Please re-enter the information above or provide your VIN in a different way.", + 'VIN': "Your VIN didn’t return a vehicle matchPlease re-enter your VIN or we can look up your VIN for you.", 'ZIP code': "Your ZIP code didn't return a match.Please re-enter the information above or provide your VIN in a different way." }; @@ -106,19 +106,19 @@ export class LookupPage extends BasePage { } } - async handleZipValidation(testData: Partial, zip: string, lookupType: VehicleLookupType, alertFlags?: IAlertFlags): Promise { + async handleZipValidation(testData: Partial): Promise { - let { vehicleDamage } = testData; + let { customerDetails,vehicleDamage, vehicleDetails, alertFlags } = testData; // Only proceed with validation if alertFlags is provided if (alertFlags) { if (alertFlags.isUnserviceableZip) { - await this.checkForUnserviceableZipAlertMessage(zip); + await this.checkForUnserviceableZipAlertMessage(customerDetails!.address.postalCode!); throw new TestSuccessAlert('Unserviceable ZIP validation successful.'); } else if (alertFlags.isInvalidZip) { await this.checkForInvalidZipAlertMessage(); throw new TestSuccessAlert('Invalid ZIP validation successful.'); } else if (alertFlags.isVinNotFound) { - await this.checkForVinNotFoundAlertMessage(lookupType); + await this.checkForVinNotFoundAlertMessage(vehicleDetails!.vehicleLookupType!); throw new TestSuccessAlert('Vin not found validation successful.'); } else { diff --git a/playwright-tests/pages/OrderConfirmationPage.ts b/playwright-tests/pages/OrderConfirmationPage.ts index 02951bdd2..43ba53850 100644 --- a/playwright-tests/pages/OrderConfirmationPage.ts +++ b/playwright-tests/pages/OrderConfirmationPage.ts @@ -47,7 +47,7 @@ export class OrderConfirmationPage extends BasePage { async validateOrderConfirmationPage(testData: Partial) { // Destructure data we use const { vehicleDetails, customerDetails, servicePackage, isCashInsuranceFlow, - isPolicyFound, claimDetails, paymentDetails, isUseVehicleOnPolicy, paymentMethod, isPolicyUnverified } = testData; + isPolicyFound, claimDetails, paymentDetails, isUseVehicleOnPolicy, paymentMethod, isPolicyUnverified} = testData; await this.serviceText.waitFor({ state: "visible" }); Soft.expect((await this.getActualAppointmentSummary()).map(item => item.toLowerCase())).toEqual((await this.getExpectedAppointmentSummary(testData)).map(item => item.toLowerCase())); @@ -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/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/PaymentMethodPage.ts b/playwright-tests/pages/PaymentMethodPage.ts index 4ea1a8b82..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,16 +495,48 @@ 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 } = testData; + const { servicePackage, isRecalVehicle, paymentDetails, isForcedOEM } = testData; await this.validateProgressBar(ProgressBarPercentages.PaymentMethodPage); await this.validatePaymentDetailsPage(testData); await this.ValidateAfterPayBreakOutSection(); + if (isForcedOEM) { + await this.validateOEMPart(isForcedOEM); + } + // Verify VAPS wipers on backend for standard and premium packages if (servicePackage === ServicePackage.Standard || servicePackage === ServicePackage.Premium) { await this.verifyVAPS(); diff --git a/playwright-tests/pages/PaypalPage.ts b/playwright-tests/pages/PaypalPage.ts index ce5547f3c..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' }); } @@ -41,6 +41,8 @@ export class PaypalPage extends BasePage { await this.paypalLoginButton.click(); await this.completePurchaseButton.click(); } else { + await this.usernameTextBox.waitFor({ state: 'visible' }); + await this.page.screenshot({ path: `test-results\\ortoni-data\\paypal-username-${Date.now()}.png`, fullPage: true }); await this.usernameTextBox.fill(paymentDetails.username!); await this.nextButton.click(); await this.tryAnotherWayButton.click(); @@ -48,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 77b330aff..30fc830d0 100644 --- a/playwright-tests/pages/SchedulePage.ts +++ b/playwright-tests/pages/SchedulePage.ts @@ -35,6 +35,11 @@ export class SchedulePage extends BasePage { readonly timeSlots: Locator; readonly allDayDropOffButton: Locator; readonly pickATimeButton: Locator; + readonly mobileFirstModalCloseButton: Locator; + readonly yesButtonRecalAckModal: Locator; + readonly noButtonRecalAckModal: Locator; + readonly continueButtonRecalAckModal: Locator; + readonly mobileFirstModal: Locator; constructor(page: Page) { super(page); @@ -65,6 +70,11 @@ 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.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"); + this.mobileFirstModal = this.page.locator("#mobile-first-modal-container"); } async selectLocation(testData: Partial) { @@ -96,6 +106,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(); @@ -225,12 +236,42 @@ 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 } = testData; + const { appointmentDetails, isCanNotRecal, experiments, handleMobileFirstModal } = testData; + await this.waitForPageOrComponentload(); await this.validateProgressBar(ProgressBarPercentages.SchedulePage); + + if (handleMobileFirstModal) { + return await this.handleMobileFirstPopUp(testData); + } + + if (experiments?.isMobileFirst && await this.mobileFirstModalCloseButton.isVisible()) { + await this.mobileFirstModalCloseButton.click(); + } await this.selectLocation(testData); + + if (experiments?.isMobileFirst && 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 63a60a6e6..2f16cb7b4 100644 --- a/playwright-tests/pages/ServicePackagesPage.ts +++ b/playwright-tests/pages/ServicePackagesPage.ts @@ -44,7 +44,7 @@ export class ServicePackagesPage extends BasePage { this.promoCodeTextbox = this.page.getByLabel('Enter a promo code'); this.applyPromoButton = this.page.getByRole('button', { name: 'Apply promo code' }); this.repeatedClicksModalCloseButton = this.page.locator('.QSISlider').locator('img[src*=\'close\']'); - this.afterPayBanner = this.page.locator('#afterpay-banner'); + this.afterPayBanner = this.page.locator('div.afterpay-modal-banner'); this.glassOnlypackagePrice = this.page.locator('fieldset:has(legend#ServicePackageQuestion) label').filter({ hasText: 'Glass service only' }).locator('.pricing-info'); } @@ -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/pages/ServiceZipPage.ts b/playwright-tests/pages/ServiceZipPage.ts index 91393f892..6c1a178f5 100644 --- a/playwright-tests/pages/ServiceZipPage.ts +++ b/playwright-tests/pages/ServiceZipPage.ts @@ -12,9 +12,9 @@ export class ServiceZipPage extends LookupPage { @step("ZipLookupPage >> Lookup by service ZIP: ") async handleServiceZipPage(testData: Partial) { - const { customerDetails, vehicleDetails, alertFlags } = testData; + const { customerDetails } = testData; await this.validateProgressBar(ProgressBarPercentages.ServiceZipPage); await this.enterZip(customerDetails!.address.postalCode!); - await this.handleZipValidation(testData, customerDetails!.address.postalCode!, vehicleDetails!.vehicleLookupType!, alertFlags!); + await this.handleZipValidation(testData); } } \ No newline at end of file diff --git a/playwright-tests/pages/VehicleLookupAddressPage.ts b/playwright-tests/pages/VehicleLookupAddressPage.ts index c5f37ee3d..50a7f1b24 100644 --- a/playwright-tests/pages/VehicleLookupAddressPage.ts +++ b/playwright-tests/pages/VehicleLookupAddressPage.ts @@ -29,10 +29,10 @@ export class VehicleLookupAddressPage extends LookupPage { @step("VehicleLookupAddressPage >> Lookup by address: ") async handleVehicleLookupAddressPage(testData: Partial) { - const { customerDetails, vehicleDetails, alertFlags } = testData; + const { customerDetails, vehicleDetails } = testData; await this.validateProgressBar(ProgressBarPercentages.VehicleLookupAddressPage); await this.lookupVehicleByAddress(customerDetails!, vehicleDetails!); - await this.handleZipValidation(customerDetails!.address.postalCode!, vehicleDetails!.vehicleLookupType!, alertFlags!); + await this.handleZipValidation(testData); } } \ No newline at end of file diff --git a/playwright-tests/pages/VehicleLookupLicensePage.ts b/playwright-tests/pages/VehicleLookupLicensePage.ts index e20828b6e..a19d2156b 100644 --- a/playwright-tests/pages/VehicleLookupLicensePage.ts +++ b/playwright-tests/pages/VehicleLookupLicensePage.ts @@ -34,11 +34,11 @@ export class VehicleLookupLicensePage extends LookupPage { @step("VehicleLookupLicensePage >> Lookup by license plate: ") async handleVehicleLookupLicensePage(testData: Partial) { - const { customerDetails, vehicleDetails, alertFlags } = testData; + const { customerDetails, vehicleDetails } = testData; await this.validateProgressBar(ProgressBarPercentages.VehicleLookupLicensePage); await this.enterPlateDetails(vehicleDetails!); await this.enterZip(customerDetails!.address.postalCode!); - await this.handleZipValidation(customerDetails!.address.postalCode!, vehicleDetails!.vehicleLookupType!, alertFlags!); + await this.handleZipValidation(testData); } } \ No newline at end of file diff --git a/playwright-tests/pages/VinLookupPage.ts b/playwright-tests/pages/VinLookupPage.ts index 3b69fc2d6..2f508acda 100644 --- a/playwright-tests/pages/VinLookupPage.ts +++ b/playwright-tests/pages/VinLookupPage.ts @@ -24,11 +24,11 @@ export class VinLookupPage extends LookupPage { @step("VinLookupPage >> Lookup by VIN: ") async handleVehicleLookupVinPage(testData: Partial) { - const { customerDetails, vehicleDetails, alertFlags } = testData; + const { customerDetails, vehicleDetails } = testData; let vin: string; if (Array.isArray(vehicleDetails?.vin)) { - vin = vehicleDetails.vin[0]; + vin = vehicleDetails!.vin[0]; } else { vin = vehicleDetails?.vin || ''; } @@ -36,6 +36,6 @@ export class VinLookupPage extends LookupPage { await this.validateProgressBar(ProgressBarPercentages.VinLookupPage); await this.enterVin(vin); await this.enterZip(customerDetails!.address.postalCode!); - await this.handleZipValidation(customerDetails!.address.postalCode!, vehicleDetails!.vehicleLookupType!, alertFlags!); + await this.handleZipValidation(testData); } } \ No newline at end of file diff --git a/playwright-tests/pages/forms/AddressForm.ts b/playwright-tests/pages/forms/AddressForm.ts index 4ddb6a512..92f270320 100644 --- a/playwright-tests/pages/forms/AddressForm.ts +++ b/playwright-tests/pages/forms/AddressForm.ts @@ -57,7 +57,7 @@ export class AddressForm extends BasePage { ); }); - await this.page.hover(".pac-container .pac-item"); + await this.addressSuggestionList.hover(); await new Promise(resolve => setTimeout(resolve, 1000)); await this.addressSuggestionList.click(); // Select the first suggestion //await this.streetAddressTextBox.press('Tab'); // Move focus to ZIP code field 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 c8788b04a..0dcb5155e 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"; @@ -28,8 +29,10 @@ 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'; @@ -60,32 +63,35 @@ 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 }, + { name: "CashReplaceMobileFirstModal", tests: cashReplaceMobileFirstModalTests }, // 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: "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 }, ]; @@ -138,13 +144,13 @@ 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 if (error instanceof TestSuccessAlert) { console.log(error.message); - // Catch and throw other errors + // Catch and throw other errors } else { throw error; } @@ -173,11 +179,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 => @@ -191,7 +197,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}`); @@ -209,6 +215,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); @@ -221,24 +237,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); @@ -255,7 +271,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; @@ -279,14 +295,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); @@ -305,7 +321,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); @@ -316,84 +332,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) { - const { isPolicyFound, isPolicyDriver, endorsements, isRecalVehicle, isCashInsuranceFlow, flow } = testCase.testData; - - // Check if the insurance policy has endorsements - const hasEndorsements = endorsements && endorsements.length > 0; +async function handleInsuranceFlow(testCase: TestCase) { + const { isPolicyFound, isPolicyDriver, endorsements, isRecalVehicle, isCashInsuranceFlow, isForcedOEM, flow } = testCase.testData; - // 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 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/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 7b16f582b..8ad5f2d8e 100644 --- a/playwright-tests/tests/CashReplaceDynamicRecalMobile.ts +++ b/playwright-tests/tests/CashReplaceDynamicRecalMobile.ts @@ -1,6 +1,6 @@ //Imports here -import { ITestData } from 'framework/TestData' -import { ServiceLocation, PaymentType } from 'safelite-playwright-core'; +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'; import { getDefaultTestData, setFakerSeedFromTestName } from 'safelite-playwright-core'; @@ -29,7 +29,8 @@ const cashReplaceDynamicRecalMobileData: Partial = { postalCode: '21237' } }, - + + servicePackage: ServicePackage.Premium, // Override vehicle details vehicleDetails: { ...getDefaultTestData().vehicleDetails!, @@ -59,7 +60,14 @@ const cashReplaceDynamicRecalMobileData: Partial = { // Override payment details paymentDetails: { - paymentType: PaymentType.PayAtService + 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..16ec6d0a2 --- /dev/null +++ b/playwright-tests/tests/CashReplaceMobileFirstModal.ts @@ -0,0 +1,85 @@ +//Imports here +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'; +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 + }, + + // Experiments + experiments: { + ...getDefaultExperimentsData(), + isMobileFirst: true + } +} + +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 f488a3555..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'; @@ -35,7 +35,7 @@ const cashReplaceMultiSlidingGlassDropoffData: Partial = { appointmentDetails: { serviceLocation: ServiceLocation.DropOff, appointmentDate: getDefaultTestData().appointmentDetails?.appointmentDate, - shopAddress: "6826 sawmill rd, Columbus, OH 43235" + shopAddress: "1343 cameron ave, lewis center, oh 43035" }, // Override vehicle details @@ -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 new file mode 100644 index 000000000..107b625a1 --- /dev/null +++ b/playwright-tests/tests/CashReplaceStaticInshop.ts @@ -0,0 +1,83 @@ +//Imports here +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'; +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 + }, + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } +} + +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/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 09083fcd2..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'; @@ -66,10 +66,15 @@ const cashReplaceWiperDropoffData: Partial = { }, { partQuestionType: PartQuestionType.GeneralQuestion2, - isOnPage: false, + 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 e609d4120..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"; @@ -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,12 +72,17 @@ 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 }, // 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 new file mode 100644 index 000000000..e3f5f56a8 --- /dev/null +++ b/playwright-tests/tests/InsuranceOEMAllState.ts @@ -0,0 +1,93 @@ +//Imports here +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"; +import { VehicleLookupType } from 'safelite-playwright-core'; +import { getDefaultTestData, setFakerSeedFromTestName } from 'safelite-playwright-core'; + +// Set the seed based on test name for consistent but unique data +setFakerSeedFromTestName("InsuranceOEMAllstate"); + +// Now get the test data with the seeded faker +const insuranceOEMAllstateData: Partial = { + ...getDefaultTestData(), // Get default data with current seed + + // Key feature: Insurance flow with GEICO + paymentMethod: PaymentMethod.Insurance, + + // Insurance claim flags + isPolicyFound: false, + isPolicyUnverified: true, + isRecalVehicle: true, + isForcedOEM: true, + + // Override customer details with specific name and California location + customerDetails: { + ...getDefaultTestData().customerDetails!, + firstName: 'Jane', + lastName: 'OEMTest', + address: { + ...getDefaultTestData().customerDetails!.address, + city: 'Richmond', + state: 'Virginia', + postalCode: '23219' + } + }, + + + // Insurance claim details + claimDetails: { + client: 'Allstate', + policyNumber: 'MockOEMTest', + policyDeductible: "Unverified", + policyZip: '43085', + damageDate: new Date(new Date().setDate(new Date().getDate() - 1)).toLocaleDateString('en-US', {month: '2-digit', day: '2-digit', year: 'numeric'}), + damageCause: DamageType.Rock + }, + + // Hyundai vehicle details with VIN lookup + vehicleDetails: { + ...getDefaultTestData().vehicleDetails!, + year: '2024', + make: 'Jeep', + model: 'Wrangler', + style: '4 door utility', + vehicleLookupType: VehicleLookupType.Zip, + }, + + // Part questions related to recalibration + partQuestions: [ + { + partQuestionType: PartQuestionType.GeneralQuestion1, + isOnPage: true, + optionToSelect: 'Yes' + }, + ], + + // Override for in-shop appointment + appointmentDetails: { + serviceLocation: ServiceLocation.InShop, + shopAddress: '5719 Brandt Pike, Dayton, OH 45424', + appointmentDate: getDefaultTestData().appointmentDetails?.appointmentDate + }, + + // Override payment details (empty because we skip payment method page in insurance flow) + paymentDetails: {}, + + // Experiments + experiments: { + ...getDefaultExperimentsData() + } +} + +const insuranceOEMAllstateTests: ITestCase[] = []; + +const tc = { + name: `InsuranceOEMAllstate`, + tags: ['@E2E','@InsuranceOEMAllstate', '@test_report', '@Insurance'], + testData: insuranceOEMAllstateData +}; +insuranceOEMAllstateTests.push(tc); + +export default insuranceOEMAllstateTests; \ No newline at end of file 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[] = []; diff --git a/playwright-tests/tests/alert-validation/alert0007_VinNotFound.ts b/playwright-tests/tests/alert-validation/alert0007_VinNotFound.ts index d88ceeab6..7c01188cb 100644 --- a/playwright-tests/tests/alert-validation/alert0007_VinNotFound.ts +++ b/playwright-tests/tests/alert-validation/alert0007_VinNotFound.ts @@ -75,10 +75,10 @@ const lookupTypesToTest: LookupTestCase[] = [ }, customerDetails: { address: { - street: '4076 Spectacle Drive', + street: '4219 Turpin Ln', city: 'Columbus', state: 'OH', - postalCode: '59261', + postalCode: '43230', country: 'United States' } }, diff --git a/public/index.html b/public/index.html index 45f83a20f..ab8f27144 100644 --- a/public/index.html +++ b/public/index.html @@ -1,6 +1,7 @@ + diff --git a/public/static/healthcheck/index.html b/public/static/healthcheck/index.html index 52e21bb5b..3f87a7587 100644 --- a/public/static/healthcheck/index.html +++ b/public/static/healthcheck/index.html @@ -545,7 +545,6 @@ { path: '/parts/parts-or-questions', method: 'POST', body: {"carId":"CR00046193","glassPieces":[{"location":"Windshield","name":"Single"}],"zip":"43015","vin":null,"serviceType":null,"referralSeqNumber":environmentContext.referralNumber,"AppName":"FixMyGlass"} }, { path: '/parts/wipers/CR00046193/43015', method: 'GET' }, { path: '/parts/mobile-premium-fee/Cash/Replace', method: 'GET' }, - { path: '/parts/service-package-discount', method: 'POST', body: {"glassPieces":[{"location":"Windshield","name":"Single"}],"damageType":"Replace","AppName":"FixMyGlass"} }, { path: '/parts/mobile-fee/?serviceType=Install&facilityType=Mobile&parentAccountNumber=167132&billToAccountNumber=87291&providerNumber=03357&isItacOptimized=false&zipCode=43015', method: 'GET' }, ] }, diff --git a/src/constants/analytics.js b/src/constants/analytics.js index fe43be06d..e156de11b 100644 --- a/src/constants/analytics.js +++ b/src/constants/analytics.js @@ -14,6 +14,8 @@ const GaCategories = { EVOX: "Evox", APPOINTMENT: "Appointment", SERVICE_LOCATION: "service-location", + CONFIRMATION_CLICKED: "confirmation clicked", + RECAL_DISCLAIMER: "Recalibration disclaimer", }; const GaActions = { @@ -26,6 +28,9 @@ const GaActions = { MOBILE_AVAILABLE: "mobile_available", SHOPS_FIRST_DISPLAYED: "shops_first_displayed", MORE_LOCATIONS_CLICKED: "more_locations_clicked", + YES: "yes", + NO: "no", + ACKNOWLEDGED: "Acknowledged", }; const GaLabels = { diff --git a/src/constants/application-config.js b/src/constants/application-config.js index 93a6f70d8..91873d455 100644 --- a/src/constants/application-config.js +++ b/src/constants/application-config.js @@ -22,6 +22,7 @@ const applicationConfig = { PIA_ERROR_URL: location.protocol + "//" + location.host + "/fmg/payment-pia-return?src=concept-funnel", CONFIRMATION_URL: location.protocol + "//" + location.host + "/fmg/confirmation", + PIA_ADYEN_RETURN_URL: location.protocol + "//" + location.host + "/fmg/virtual/payment/return", GOOGLE_CALENDAR: "https://www.google.com/calendar/render?action=TEMPLATE", YAHOO_CALENDAR: "https://calendar.yahoo.com/?v=60", OUTLOOK_CALENDAR: @@ -31,6 +32,8 @@ const applicationConfig = { COOKIE_NAME_LENGTH_MAX_LIMIT: 44, SESSION_TIMEOUT_MINUTES: process.env.__VUE_APP_SESSION_TIMEOUT_MINUTES__, RETURN_USER_PAGE: "/fmg/return-user", + ADYEN_CLIENT_KEY: process.env.__VUE_APP_ADYEN_CLIENT_KEY__, + ADYEN_ENVIRONMENT: process.env.__VUE_APP_ADYEN_ENVIRONMENT__, }; export { applicationConfig }; diff --git a/src/constants/cart-item-categories.js b/src/constants/cart-item-categories.js index 6bbb960b9..5c8faafcf 100644 --- a/src/constants/cart-item-categories.js +++ b/src/constants/cart-item-categories.js @@ -3,7 +3,7 @@ const cartItemCategories = { GLASS_PARTS: "glassParts", SUPPORTING_ITEMS: "supportingItems", PROMOS: "promos", - SERVICE_PACKAGE_DISCOUNT: "servicePackageDiscount", + QUOTE_PAGE_DISCOUNT: "quotePageDiscount", }; export { cartItemCategories }; diff --git a/src/constants/cart-item-types.js b/src/constants/cart-item-types.js index 0bb418d43..8f70aeaed 100644 --- a/src/constants/cart-item-types.js +++ b/src/constants/cart-item-types.js @@ -10,7 +10,7 @@ const cartItemTypes = { PROMOS: "PROMOS", EARLY_BIRD: "EARLY BIRD", SUPPLIES_REPAIR: "SUPPLIES-REPAIR", - SERVICE_PACKAGE_DISCOUNT: "SERVICE PACKAGE DISCOUNT", + QUOTE_PAGE_DISCOUNT: "QUOTE PAGE DISCOUNT", DONATION: "DONATION", }; diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index 17a8f9a08..d25fce6a3 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -76,10 +76,6 @@ const endpoints = { url: "/parts/api/v1/parts/pricing-by-day-part-number", method: "GET", }, - GetServicePackageDiscountPart: { - url: "/parts/api/v1/parts/service-package-discount", - method: "POST", - }, GetServiceabilityDetails: { url: "/location/api/v1/location/serviceability-details", method: "GET", @@ -124,6 +120,14 @@ const endpoints = { url: "/order/api/v1/order/save-session/fmg", method: "POST", }, + SubmitOrder: { + url: "/order/api/v1/order/submit-order", + method: "POST", + }, + SubmitOrderForPia: { + url: "/order/api/v1/order/submit-order-for-pia", + method: "POST", + }, LoadSession: { url: "/order/api/v1/order/load-session", method: "POST", @@ -154,7 +158,7 @@ const endpoints = { }, TaxOrderItems: { url: "/price/api/v1/price/taxed-order-items", - method: "GET", + method: "POST", }, LogExperimentExposureIfAssigned: { url: "/experiments/api/v1/experiments/log-exposure", @@ -208,6 +212,18 @@ const endpoints = { url: "/order/api/v1/order/add-donation-part", method: "POST", }, + GetOemAfterMarketParts: { + url: "/parts/api/v1/parts/oem-after-market-parts", + method: "POST", + }, + InitializeAdyenPayment: { + url: "/payment/api/v1/payment/payment/session/initialize-session", + method: "POST", + }, + GetAdyenSessionResult: { + url: "/payment/api/v1/payment/payment/session/session-result", + method: "POST", + }, }; export { endpoints }; diff --git a/src/constants/experiments.js b/src/constants/experiments.js index f358fc89f..f81484058 100644 --- a/src/constants/experiments.js +++ b/src/constants/experiments.js @@ -12,6 +12,9 @@ const experimentSettings = { SUPPRESS_VIN_CAPTURE: "SuppressVinCapture", DISPLAY_AVAILABILITY_INDICATORS: "DisplayAvailabilityIndicators", PIA_EXPERIENCE: "PIA Experience", + CONDITIONAL_PROVIDER_NUMBERS: "ConditionalProviderNumbers", + CONDITIONAL_PROVIDER_NUMBERS_2: "ConditionalProviderNumbers2", + CONDITIONAL_PROVIDER_SHUTOFF_DATE: "ConditionalProviderShutoffDate", PIA_INSURANCE: "DisplayPIAInsurance", SUBMIT_ORDER_ENABLE_PIA: "SubmitOrder_Enable_PIA", IS_EMAIL_OPTIONAL: "isEmailOptional", @@ -33,6 +36,11 @@ const experimentSettings = { SHOW_PM_MOBILE_DAYS: "Show_PMmobileDays", SHOW_NO_PM_MOBILE_DAYS: "Show_NoPMmobileDays", SHOW_NO_MOBILE_AVAILABLE_DAYS: "Show_NoMobileAvailableDays", + SHOW_MOBILE_FIRST_POPUP_REPAIR: "Show_Mobile_First_Popup_Repair", + SHOW_MOBILE_FIRST_POPUP_REPLACE_WITHOUT_MSR: "Show_Mobile_First_Popup_Replace_Without_MSR", + SHOW_MOBILE_FIRST_POPUP_REPLACE_WITH_MSR: "Show_Mobile_First_Popup_Replace_With_MSR", + OFFER_OEM: "OfferOem", + USE_ADYEN_PAYMENT: "UseAdyenPayment", }; const experimentTriggers = { diff --git a/src/constants/part-type-strings.js b/src/constants/part-type-strings.js index 741132e28..0118c0b2c 100644 --- a/src/constants/part-type-strings.js +++ b/src/constants/part-type-strings.js @@ -10,7 +10,9 @@ const partTypeStrings = { REPAIR_FEE: "REPAIR FEE", EARLY_BIRD: "EARLY BIRD", SERVICE_PACKAGE_DISCOUNT: "SERVICE PACKAGE DISCOUNT", + QUOTE_PAGE_DISCOUNT: "QUOTE PAGE DISCOUNT", DONATION: "DONATION", + WINDSHIELD: "WINDSHIELD", }; export { partTypeStrings }; diff --git a/src/constants/progress-bar-mapper.js b/src/constants/progress-bar-mapper.js index a6e41d31b..b21c6c9c7 100644 --- a/src/constants/progress-bar-mapper.js +++ b/src/constants/progress-bar-mapper.js @@ -48,6 +48,10 @@ export const pageProgressMapper = { percent: 30, step: 1, }, + "policy-info": { + percent: 55, + step: 2, + }, quote: { percent: 60, step: 2, @@ -80,6 +84,10 @@ export const pageProgressMapper = { percent: 98.5, step: 4, }, + "payment-adyen": { + percent: 98.5, + step: 4, + }, confirmation: { percent: 100, step: 4, diff --git a/src/constants/quote-page-discounts.js b/src/constants/quote-page-discounts.js new file mode 100644 index 000000000..3b61c136d --- /dev/null +++ b/src/constants/quote-page-discounts.js @@ -0,0 +1,105 @@ +import { packageNames } from "./package-names"; +import { partTypeStrings } from "./part-type-strings"; + +export const quotePageDiscountTable = [ + // Repair + { + name: "Repair Glass Only", + experimentCode: "Show_GlassOnly_Repair_Discount", + partNumber: "GL RPR CSH PKG", + partType: partTypeStrings.QUOTE_PAGE_DISCOUNT, + description: "GLASS RPR GEOTARGET DISC", + packageLevel: packageNames.TIER_ONE, + }, + { + name: "Repair Standard", + experimentCode: "Show_Standard_Repair_Discount", + partNumber: "STD RPR CSH PKG", + partType: partTypeStrings.QUOTE_PAGE_DISCOUNT, + description: "STD RPR GEOTARGET DISC", + packageLevel: packageNames.TIER_TWO, + }, + { + name: "Repair Premium", + experimentCode: "Show_Premium_Repair_Discount", + partNumber: "PRM RPR CSH PKG", + partType: partTypeStrings.QUOTE_PAGE_DISCOUNT, + description: "PREM RPR GEOTARGET DISC", + packageLevel: packageNames.TIER_THREE, + }, + // Replace - No recal + { + name: "Replace Glass Only", + experimentCode: "Show_GlassOnly_Replace_Discount", + partNumber: "GL RPL CSH PKG", + partType: partTypeStrings.QUOTE_PAGE_DISCOUNT, + description: "GLASS RPL GEOTARGET DISC", + packageLevel: packageNames.TIER_ONE, + }, + { + name: "Replace Standard", + experimentCode: "Show_Standard_Replace_Discount", + partNumber: "STD RPL CSH PKG", + partType: partTypeStrings.QUOTE_PAGE_DISCOUNT, + description: "STD RPL GEOTARGET DISC", + packageLevel: packageNames.TIER_TWO, + }, + { + name: "Replace Premium", + experimentCode: "Show_Premium_Replace_Discount", + partNumber: "PRM RPL CSH PKG", + partType: partTypeStrings.QUOTE_PAGE_DISCOUNT, + description: "PREM RPL GEOTARGET DISC", + packageLevel: packageNames.TIER_THREE, + }, + // Replace - with recal + { + name: "Recal Glass Only", + experimentCode: "Show_GlassOnly_Recal_Discount", + partNumber: "GL RCL CSH PKG", + partType: partTypeStrings.QUOTE_PAGE_DISCOUNT, + description: "GLASS RCL GEOTARGET DISC", + packageLevel: packageNames.TIER_ONE, + }, + { + name: "Recal Standard", + experimentCode: "Show_Standard_Recal_Discount", + partNumber: "STD RCL CSH PKG", + partType: partTypeStrings.QUOTE_PAGE_DISCOUNT, + description: "STD RCL GEOTARGET DISC", + packageLevel: packageNames.TIER_TWO, + }, + { + name: "Recal Premium", + experimentCode: "Show_Premium_Recal_Discount", + partNumber: "PRM RCL CSH PKG", + partType: partTypeStrings.QUOTE_PAGE_DISCOUNT, + description: "PREM RCL GEOTARGET DISC", + packageLevel: packageNames.TIER_THREE, + }, + // Replace - Curve Tempered + { + name: "Curve Tempered Glass Only", + experimentCode: "Show_GlassOnly_Replace_CT_Discount", + partNumber: "GL CT CSH PKG", + partType: partTypeStrings.QUOTE_PAGE_DISCOUNT, + description: "GLASS CT GEOTARGET DISC", + packageLevel: packageNames.TIER_ONE, + }, + { + name: "Curve Tempered Standard", + experimentCode: "Show_Standard_Replace_CT_Discount", + partNumber: "STD CT CSH PKG", + partType: partTypeStrings.QUOTE_PAGE_DISCOUNT, + description: "STD CT GEOTARGET DISC", + packageLevel: packageNames.TIER_TWO, + }, + { + name: "Curve Tempered Premium", + experimentCode: "Show_Premium_Replace_CT_Discount", + partNumber: "PRM CT CSH PKG", + partType: partTypeStrings.QUOTE_PAGE_DISCOUNT, + description: "PREM CT GEOTARGET DISC", + packageLevel: packageNames.TIER_THREE, + }, +]; diff --git a/src/constants/schedule-constants.js b/src/constants/schedule-constants.js index 2b626df18..2b6171075 100644 --- a/src/constants/schedule-constants.js +++ b/src/constants/schedule-constants.js @@ -11,6 +11,8 @@ const PREMIUM_FEE_PART_TYPE = "EARLY BIRD"; const PRICING_BY_DAY_PART_TYPE = "PREMAPPT GEN"; +const RECAL_ACK_YES = "Yes"; + const RouteCodeFlags = { ALL_DAY_DROP_OFF: "ALL DAY DROP OFF", OVERNIGHT_DROP_OFF: "OVERNIGHT DROP OFF", @@ -31,4 +33,5 @@ export { PRICING_BY_DAY_PART_TYPE, RouteCodeFlags, cmsWidgetFieldMappings, + RECAL_ACK_YES, }; diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 513891ea1..3afd4df24 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -23,6 +23,7 @@ const storeActions = { GET_ALERT_REASONS_BY_CTU: "getAlertReasonsByCtu", GET_PARTS_OR_QUESTIONS: "getPartsOrQuestions", GET_PARTS: "getParts", + GET_OEM_AFTERMARKET_PARTS: "getOemAfterMarketParts", GET_WIPERS: "getWipers", GET_RAIN_REPEL: "getRainRepel", GET_SUPPORTING_ITEMS: "getSupportingItems", @@ -32,7 +33,6 @@ const storeActions = { GET_MOLDING_QUESTIONS: "getMoldingQuestions", GET_MOBILE_FEE_PART: "getMobileFeePart", GET_PRICING_BY_DAY_PART: "getPricingByDayPart", - GET_SERVICE_PACKAGE_DISCOUNT_PART: "getServicePackageDiscountPart", GET_SERVICEABILITY_DETAILS: "getServiceabilityDetails", GET_SHOP_TIME_SLOTS: "getShopTimeSlots", GET_MOBILE_TIME_SLOTS: "getMobileTimeSlots", @@ -111,6 +111,8 @@ const storeActions = { SAVE_PAYPAL_TOKEN: "savePaypalToken", SAVE_NEXTGEN_SETTLED_AMOUNT: "saveNextGenSettledAmount", SAVE_IS_RECAL_ACK_OPT_IN: "saveIsRecalAckOptIn", + SAVE_IS_RECAL_ACKNOWLEDGED_FOR_SCHEDULING: "saveIsRecalAcknowledgedForScheduling", + SAVE_IS_OEM_GLASS_SELECTED: "saveIsOemGlassSelected", SAVE_IS_MSR_FEE_APPLICABLE: "saveIsMSRFeeApplicable", CREATE_SUBMITTED_STATE: "createSubmittedState", diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js index 2112c789c..9b4b3719c 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -69,6 +69,8 @@ const storeMutations = { LOCK_TOKEN: "updateLockToken", UPDATE_SETTLED_TENDER_AMOUNT: "updateSettledTenderAmount", UPDATE_IS_RECAL_ACK_OPT_IN: "updateIsRecalAckOptIn", + UPDATE_IS_RECAL_ACKNOWLEDGED_FOR_SCHEDULING: "updateIsRecalAcknowledgedForScheduling", + UPDATE_IS_OEM_GLASS_SELECTED: "updateIsOemGlassSelected", UPDATE_IS_MSR_FEE_APPLICABLE: "updateIsMSRFeeApplicable", UPDATE_CASH_PRICE_SUBTOTAL: "updateCashPriceSubTotal", @@ -117,6 +119,7 @@ const storeMutations = { UPDATE_EXTERNAL_PARAMETER_SERVICE_PACKAGE: "updateExternalParameterServicePackage", UPDATE_EXTERNAL_PARAMETER_SOURCE: "updateExternalParameterSource", UPDATE_EXTERNAL_PARAMETER_PHONE_NUMBER: "updateExternalParameterPhoneNumber", + UPDATE_EXTERNAL_PARAMETER_QS_STASH: "updateExternalParameterQsStash", //RESET EXTERNAL_PARAMETER MUTATIONS RESET_EXTERNAL_PARAMETER_VEHICLE_STATE: "resetExternalParameterVehicleState", diff --git a/src/digital-components/date-picker/date-picker-june-2025.vue b/src/digital-components/date-picker/date-picker-june-2025.vue deleted file mode 100644 index 9d001a113..000000000 --- a/src/digital-components/date-picker/date-picker-june-2025.vue +++ /dev/null @@ -1,1335 +0,0 @@ - - - - - - - - - - - diff --git a/src/digital-components/date-picker/date-picker.specDISABLED.js b/src/digital-components/date-picker/date-picker.specDISABLED.js deleted file mode 100644 index 6ed05d663..000000000 --- a/src/digital-components/date-picker/date-picker.specDISABLED.js +++ /dev/null @@ -1,1435 +0,0 @@ -import datePicker from "./date-picker"; - -// Supporting Files -import { shallowMount } from "@vue/test-utils"; -import { getMountOptions } from "@/helpers/unit-test-helper.js"; -import store from "@/store"; - -// Constants -const mockValuesForSetDate01 = { - overrideDate: "2023-12-01", - monthEnd: "2023-12-31", - initialViewWeeks: [ - { - weekNum: 1, - weekStartDate: "2023-12-01", - weekEndDate: "2023-12-02", - }, - { - weekNum: 2, - weekStartDate: "2023-12-03", - weekEndDate: "2023-12-09", - }, - { - weekNum: 3, - weekStartDate: "2023-12-10", - weekEndDate: "2023-12-16", - }, - { - weekNum: 4, - weekStartDate: "2023-12-17", - weekEndDate: "2023-12-23", - }, - { - weekNum: 5, - weekStartDate: "2023-12-24", - weekEndDate: "2023-12-30", - }, - ], - config: { - todayDate: "2023-12-01", - initialViewStartDate: "2023-12-01", - initialViewEndDate: "2023-12-30", - calendarViewDirection: "future", - initialShopTimeSlotsResponse: { - inshopTimeSlotsData: { - days: [ - { - date: "2023-12-30", - timeSlots: [ - { - id: "06747-01820-S-B*20453*7 AM", - startTime: "07:00", - endTime: "08:00", - offerPremium: false, - }, - ], - }, - ], - }, - mobileTimeSlotsData: { - days: [ - { - date: "2023-12-30", - timeSlots: [ - { - id: "06747-01820-S-B*20453*7 AM", - startTime: "07:00", - endTime: "08:00", - offerPremium: false, - }, - ], - }, - ], - }, - estimatedServiceMinutesMinimum: 90, - estimatedServiceMinutesMaximum: 120, - }, - hideSomeDaysForInitialView: true, - hideSecondMonth: true, - preSelectedDate: null, - }, - monthsData: [ - { - monthLabel: "December", - monthIndex: 12, - monthString: "December-2023", - yearNum: 2023, - datesInshop: [ - { - dateNum: 1, - dayClasses: " current-day", - inputValue: "2023-12-01", - isAvailable: false, - }, - { - dateNum: 2, - dayClasses: "", - inputValue: "2023-12-02", - isAvailable: false, - }, - { - dateNum: 3, - dayClasses: " sunday", - inputValue: "2023-12-03", - isAvailable: false, - }, - { - dateNum: 4, - dayClasses: "", - inputValue: "2023-12-04", - isAvailable: false, - }, - { - dateNum: 5, - dayClasses: "", - inputValue: "2023-12-05", - isAvailable: false, - }, - { - dateNum: 6, - dayClasses: "", - inputValue: "2023-12-06", - isAvailable: false, - }, - { - dateNum: 7, - dayClasses: "", - inputValue: "2023-12-07", - isAvailable: false, - }, - { - dateNum: 8, - dayClasses: "", - inputValue: "2023-12-08", - isAvailable: false, - }, - { - dateNum: 9, - dayClasses: "", - inputValue: "2023-12-09", - isAvailable: false, - }, - { - dateNum: 10, - dayClasses: " sunday", - inputValue: "2023-12-10", - isAvailable: false, - }, - { - dateNum: 11, - dayClasses: "", - inputValue: "2023-12-11", - isAvailable: false, - }, - { - dateNum: 12, - dayClasses: "", - inputValue: "2023-12-12", - isAvailable: false, - }, - { - dateNum: 13, - dayClasses: "", - inputValue: "2023-12-13", - isAvailable: false, - }, - { - dateNum: 14, - dayClasses: "", - inputValue: "2023-12-14", - isAvailable: false, - }, - { - dateNum: 15, - dayClasses: "", - inputValue: "2023-12-15", - isAvailable: false, - }, - { - dateNum: 16, - dayClasses: "", - inputValue: "2023-12-16", - isAvailable: false, - }, - { - dateNum: 17, - dayClasses: " sunday", - inputValue: "2023-12-17", - isAvailable: false, - }, - { - dateNum: 18, - dayClasses: "", - inputValue: "2023-12-18", - isAvailable: false, - }, - { - dateNum: 19, - dayClasses: "", - inputValue: "2023-12-19", - isAvailable: false, - }, - { - dateNum: 20, - dayClasses: "", - inputValue: "2023-12-20", - isAvailable: false, - }, - { - dateNum: 21, - dayClasses: "", - inputValue: "2023-12-21", - isAvailable: false, - }, - { - dateNum: 22, - dayClasses: "", - inputValue: "2023-12-22", - isAvailable: false, - }, - { - dateNum: 23, - dayClasses: "", - inputValue: "2023-12-23", - isAvailable: false, - }, - { - dateNum: 24, - dayClasses: " sunday", - inputValue: "2023-12-24", - isAvailable: false, - }, - { - dateNum: 25, - dayClasses: "", - inputValue: "2023-12-25", - isAvailable: false, - }, - { - dateNum: 26, - dayClasses: "", - inputValue: "2023-12-26", - isAvailable: false, - }, - { - dateNum: 27, - dayClasses: "", - inputValue: "2023-12-27", - isAvailable: false, - }, - { - dateNum: 28, - dayClasses: "", - inputValue: "2023-12-28", - isAvailable: false, - }, - { - dateNum: 29, - dayClasses: "", - inputValue: "2023-12-29", - isAvailable: false, - }, - { - dateNum: 30, - dayClasses: "", - inputValue: "2023-12-30", - isAvailable: true, - }, - { - dateNum: 31, - dayClasses: " sunday day-hidden", - inputValue: "2023-12-31", - isAvailable: false, - }, - ], - datesMobile: [], - startDateDayIndex: 5, - monthClass: "", - isMonthThatHidesSomeDaysForInitialView: true, - }, - { - monthLabel: "January", - monthIndex: 1, - monthString: "January-2024", - yearNum: 2024, - datesInshop: [ - { - dateNum: 1, - dayClasses: "", - inputValue: "2024-01-01", - isAvailable: false, - }, - { - dateNum: 2, - dayClasses: "", - inputValue: "2024-01-02", - isAvailable: false, - }, - { - dateNum: 30, - dayClasses: "", - inputValue: "2024-01-30", - isAvailable: false, - }, - { - dateNum: 31, - dayClasses: "", - inputValue: "2024-01-31", - isAvailable: false, - }, - ], - datesMobile: [], - startDateDayIndex: 1, - monthClass: " month-hidden", - }, - ], -}; - -const mockInitialData = { - todayDate: "2023-07-04", - initialViewStartDate: "2023-07-05", - initialViewEndDate: "2023-08-19", - calendarViewDirection: "future", - initialShopTimeSlotsResponse: { - inshopTimeSlotsData: { - days: [ - { - date: "2023-07-25", - timeSlots: [ - { - id: "06747-01820-S-B*20295*7 AM", - startTime: "07:00", - endTime: "08:00", - offerPremium: false, - }, - { - id: "06747-01820-S-B*20295*730 AM", - startTime: "07:30", - endTime: "08:30", - offerPremium: false, - }, - ], - }, - { - date: "2023-07-26", - timeSlots: [ - { - id: "06747-01820-S-B*20296*7 AM", - startTime: "07:00", - endTime: "08:00", - offerPremium: false, - }, - ], - }, - ], - }, - mobileTimeSlotsData: { - days: [ - { - date: "2023-07-25", - timeSlots: [ - { - id: "06747-01820-S-B*20295*7 AM", - startTime: "07:00", - endTime: "08:00", - offerPremium: false, - }, - { - id: "06747-01820-S-B*20295*730 AM", - startTime: "07:30", - endTime: "08:30", - offerPremium: false, - }, - ], - }, - { - date: "2023-07-26", - timeSlots: [ - { - id: "06747-01820-S-B*20296*7 AM", - startTime: "07:00", - endTime: "08:00", - offerPremium: false, - }, - ], - }, - ], - }, - estimatedServiceMinutesMinimum: 90, - estimatedServiceMinutesMaximum: 120, - }, - hideSomeDaysForInitialView: true, - hideSecondMonth: false, - preSelectedDate: null, -}; - -const mockCallbackResponse = { - inshopTimeSlotsData: { - days: [ - { - date: "2024-12-30", - timeSlots: [ - { - id: "06747-01820-S-B*20453*7 AM", - startTime: "07:00", - endTime: "08:00", - offerPremium: false, - }, - ], - }, - { - date: "2024-01-02", - timeSlots: [ - { - id: "06747-01820-S-B*20453*7 AM", - startTime: "07:00", - endTime: "08:00", - offerPremium: false, - }, - ], - }, - ], - }, - mobileTimeSlotsData: { - days: [ - { - date: "2024-12-30", - timeSlots: [ - { - id: "06747-01820-S-B*20453*7 AM", - startTime: "07:00", - endTime: "08:00", - offerPremium: false, - }, - ], - }, - { - date: "2024-01-02", - timeSlots: [ - { - id: "06747-01820-S-B*20453*7 AM", - startTime: "07:00", - endTime: "08:00", - offerPremium: false, - }, - ], - }, - ], - }, - - estimatedServiceMinutesMinimum: 90, - estimatedServiceMinutesMaximum: 120, -}; - -const mockPromiseResolve = (returnValue) => - jest.fn(async () => Promise.resolve({ valid: returnValue })); - -describe("date-picker.vue", () => { - describe("initial setup", () => { - test("Expect initializeComponent to be run", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - }, - }); - wrapper.vm.initializeComponent = jest.fn().mockImplementation(); - wrapper.vm.initializeComponent(mockInitialData); - - // Assert - expect(wrapper.vm.initializeComponent).toHaveBeenCalled(); - - wrapper.unmount(); - }); - - test("When initializeComponent runs, expect will run setCalendarData method", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - }, - }); - const spy = jest.spyOn(wrapper.vm, "setCalendarData"); - wrapper.vm.$refs.timeSlotModalQuestion.initializeComponent = jest.fn(); - wrapper.vm.$nextTick(); - - // Act - wrapper.vm.initializeComponent(mockInitialData); - - // Assert - expect(spy).toHaveBeenCalled(); - - wrapper.unmount(); - }); - - test("If prop customComponentId is supplied, expect it will be used for componentId in setup method", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - customComponentId: "dateQuestion", - }, - }); - - // Act - const componentIdValue = wrapper.vm.componentId; - - // Assert - expect(componentIdValue).toEqual("dateQuestion"); - - wrapper.unmount(); - }); - - test("If prop getMoreDatesCallback is not supplied, expect the default function will return empty array", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - }, - }); - - // Act - const callbackResponse = wrapper.vm.getMoreDatesCallback( - "2023-07-24", - "2023-08-19", - "Inshop", - "006747" - ); - - // Assert - expect(callbackResponse).toEqual([]); - - wrapper.unmount(); - }); - - test("expect a date string created from today", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - }, - }); - const todayDate = new Date(); - const todayDateString = - todayDate.getFullYear() + - "-" + - ("0" + (todayDate.getMonth() + 1)).slice(-2) + - "-" + - ("0" + todayDate.getDate()).slice(-2); - - // Assert - expect(wrapper.vm.todayString).toEqual(todayDateString); - - wrapper.unmount(); - }); - - test("expect a date string created from todayOverrideDateString", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: "2023-07-02", - }, - }); - - // Assert - expect(wrapper.vm.todayString).toEqual("2023-07-02"); - - wrapper.unmount(); - }); - - test("expect computed todayDayIndex returns the correct day index", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: "2023-07-02", - }, - }); - - // Assert - expect(wrapper.vm.todayDayIndex).toEqual(0); - - wrapper.unmount(); - }); - - test("expect computed todayDateNum returns the correct date number", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: "2023-07-02", - }, - }); - - // Assert - expect(wrapper.vm.todayDateNum).toEqual(2); - - wrapper.unmount(); - }); - - test("expect computed currentWeekStartDateNum returns the correct date number when todayDayIndex is < todayDateNum", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: "2023-07-05", - }, - }); - - // Assert - expect(wrapper.vm.currentWeekStartDateNum).toEqual(2); - - wrapper.unmount(); - }); - - test("expect computed currentWeekStartDateNum returns 1 when todayDayIndex is >= todayDateNum", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: "2023-07-01", - }, - }); - - // Assert - expect(wrapper.vm.currentWeekStartDateNum).toEqual(1); - - wrapper.unmount(); - }); - - test("expect computed currentWeekEndDateNum returns correct value", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: "2023-07-05", - }, - }); - - // Assert - expect(wrapper.vm.currentWeekEndDateNum).toEqual(8); - - wrapper.unmount(); - }); - - test("expect computed calendarViewDirection returns 'past' when prop selectableDatesSetting is 'past'", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "past", - todayOverrideDateString: "2023-07-05", - }, - }); - - // Assert - expect(wrapper.vm.calendarViewDirection).toEqual("past"); - - wrapper.unmount(); - }); - - test("expect computed calendarViewDirection returns 'future' when prop selectableDatesSetting is 'custom'", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: "2023-07-05", - }, - }); - - // Assert - expect(wrapper.vm.calendarViewDirection).toEqual("future"); - - wrapper.unmount(); - }); - - // describe("when loadInitialData is run...", () => { - // test("and this.todayString exists, expect it should have a todayDate matching today", async () => { - // // Arrange - // const { wrapper } = setupMocks({}); - // const todayDate = new Date(); - // const todayDateString = - // todayDate.getFullYear() + - // "-" + - // ("0" + (todayDate.getMonth() + 1)).slice(-2) + - // "-" + - // ("0" + todayDate.getDate()).slice(-2); - - // // Act - // const testResult = await wrapper.vm.loadInitialData({ - // selectableDatesSetting: "custom", - // initialViewRowsToShow: 5, - // getSelectableDatesCallback: mockPromiseResolve, - // preSelectedDate: null, - // }); - - // // Assert - // expect(testResult).toMatchObject({ - // todayDate: todayDateString, - // }); - - // wrapper.unmount(); - // }); - - // test("and overrride date exists, expect it should have a todayDate matching the override", async () => { - // // Arrange - // const overrideDate = mockValuesForSetDate01.overrideDate; - // const { wrapper } = setupMocks({ - // propsData: { - // selectableDatesSetting: "custom", - // todayOverrideDateString: overrideDate, - // }, - // }); - - // // Act - // const localThis = { - // todayString: null, - // todayOverrideDateString: overrideDate, - // getMonthEnd: () => mockValuesForSetDate01.monthEnd, - // getInitialViewWeeks: () => mockValuesForSetDate01.initialViewWeeks, - // }; - // const testResult = await datePicker.methods.loadInitialData.call(localThis, { - // selectableDatesSetting: "custom", - // initialViewRowsToShow: 5, - // getSelectableDatesCallback: mockPromiseResolve, - // preSelectedDate: null, - - // todayOverrideDateString: overrideDate, - // }); - - // // Assert - // expect(testResult).toMatchObject({ - // todayDate: overrideDate, - // }); - - // wrapper.unmount(); - // }); - - // test("with no this.todayString or overrride date, expect it should have a todayDate matching today", async () => { - // // Arrange - // const { wrapper } = setupMocks({}); - // const todayDate = new Date(); - // const todayDateString = - // todayDate.getFullYear() + - // "-" + - // ("0" + (todayDate.getMonth() + 1)).slice(-2) + - // "-" + - // ("0" + todayDate.getDate()).slice(-2); - - // // Act - // const localThis = { - // todayString: null, - // getMonthEnd: () => mockValuesForSetDate01.monthEnd, - // getInitialViewWeeks: () => mockValuesForSetDate01.initialViewWeeks, - // }; - - // //Act - // const testResult = await datePicker.methods.loadInitialData.call(localThis, { - // selectableDatesSetting: "custom", - // initialViewRowsToShow: 5, - // getSelectableDatesCallback: mockPromiseResolve, - // preSelectedDate: null, - // }); - - // // Assert - // expect(testResult).toMatchObject({ - // todayDate: todayDateString, - // }); - - // wrapper.unmount(); - // }); - - // test("and config.selectableDatesSetting is PAST, expect it should have a calendarViewDirection of PAST", async () => { - // // Arrange - // const { wrapper } = setupMocks({}); - - // // Act - // const testResult = await wrapper.vm.loadInitialData({ - // selectableDatesSetting: "past", - // initialViewRowsToShow: 5, - // getSelectableDatesCallback: mockPromiseResolve, - // }); - - // // Assert - // expect(testResult).toMatchObject({ - // calendarViewDirection: "past", - // }); - - // wrapper.unmount(); - // }); - - // test("and config.selectableDatesSetting is CUSTOM, expect it should have a calendarViewDirection of FUTURE", async () => { - // // Arrange - // const { wrapper } = setupMocks({}); - - // // Act - // const testResult = await wrapper.vm.loadInitialData({ - // selectableDatesSetting: "custom", - // initialViewRowsToShow: 5, - // getSelectableDatesCallback: mockPromiseResolve, - // }); - - // // Assert - // expect(testResult).toMatchObject({ - // calendarViewDirection: "future", - // }); - - // wrapper.unmount(); - // }); - // }); - - describe("when setCalendarData is run...", () => { - test("expect that data elements are updated properly", async () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: mockValuesForSetDate01.overrideDate, - }, - }); - - // Act - await wrapper.vm.setCalendarData(mockValuesForSetDate01.config); - - // Assert - expect(wrapper.vm.hideSomeDaysForInitialView).toBe(true); - expect(wrapper.vm.selectableDatesInshop[0]).toMatchObject({ - date: "2023-12-30", - }); - expect(wrapper.vm.selectableDatesInshop[0].timeSlots.length).toBeGreaterThan(0); - expect(wrapper.vm.months.length).toBe(7); - expect(wrapper.vm.months[0]).toMatchObject({ - monthLabel: "December", - monthIndex: 12, - monthString: "December-2023", - yearNum: 2023, - startDateDayIndex: 5, - monthClass: "", - isMonthThatHidesSomeDaysForInitialView: true, - }); - - wrapper.unmount(); - }); - - test("expect that this.months are reversed if direction is past", async () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "past", - todayOverrideDateString: mockValuesForSetDate01.overrideDate, - }, - }); - const modifiedConfig = { ...mockValuesForSetDate01.config }; - modifiedConfig.calendarViewDirection = "past"; - - // Act - await wrapper.vm.setCalendarData(modifiedConfig); - - // Assert - expect(wrapper.vm.months.length).toBe(37); - expect(wrapper.vm.months[0]).toMatchObject({ - monthString: "December-2020", - startDateDayIndex: 2, - monthClass: " month-hidden last-available-month", - }); - - wrapper.unmount(); - }); - - test("expect that if there's a pre-selected date that it will scroll to it", async () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: mockValuesForSetDate01.overrideDate, - }, - }); - const modifiedConfig = { ...mockValuesForSetDate01.config }; - modifiedConfig.preSelectedDate = "2023-12-25"; - wrapper.vm.scrollToElement = jest.fn().mockImplementation(); - - // Act - await wrapper.vm.setCalendarData(modifiedConfig); - await wrapper.vm.$nextTick(); - - // Assert - expect(wrapper.vm.scrollToElement).toHaveBeenCalledWith("December-2023"); - - wrapper.unmount(); - }); - - test("expect that if there's a pre-selected date within the last month available then the View More Dates button will be hidden", async () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: mockValuesForSetDate01.overrideDate, - }, - }); - const modifiedConfig = { ...mockValuesForSetDate01.config }; - modifiedConfig.preSelectedDate = "2024-06-11"; - wrapper.vm.scrollToElement = jest.fn().mockImplementation(); - - // Act - await wrapper.vm.setCalendarData(modifiedConfig); - await wrapper.vm.$nextTick(); - - // Assert - expect(wrapper.vm.disableViewMoreDatesButton).toBe(true); - - wrapper.unmount(); - }); - - test("expect that this.months are reversed if direction is past", async () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "past", - todayOverrideDateString: mockValuesForSetDate01.overrideDate, - }, - }); - const modifiedConfig = { ...mockValuesForSetDate01.config }; - modifiedConfig.calendarViewDirection = "past"; - - // Act - await wrapper.vm.setCalendarData(modifiedConfig); - - // Assert - expect(wrapper.vm.months.length).toBe(37); - expect(wrapper.vm.months[0]).toMatchObject({ - monthString: "December-2020", - startDateDayIndex: 2, - monthClass: " month-hidden last-available-month", - }); - - wrapper.unmount(); - }); - - test("expect that if there's a pre-selected date that it will scroll to it", async () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: mockValuesForSetDate01.overrideDate, - }, - }); - const modifiedConfig = { ...mockValuesForSetDate01.config }; - modifiedConfig.preSelectedDate = "2023-12-25"; - wrapper.vm.scrollToElement = jest.fn().mockImplementation(); - - // Act - await wrapper.vm.setCalendarData(modifiedConfig); - await wrapper.vm.$nextTick(); - - // Assert - expect(wrapper.vm.scrollToElement).toHaveBeenCalledWith("December-2023"); - - wrapper.unmount(); - }); - }); - - describe("when getInitialViewWeeks is run...", () => { - test("expect that 5 weeks will be returned when no preselected date", () => { - // Arrange - const { wrapper } = setupMocks({}); - const todayString = "2023-02-01"; - - // Act - const testResult = wrapper.vm.getInitialViewWeeks(todayString, 5); - - // Assert - expect(testResult.length).toBe(5); - - wrapper.unmount(); - }); - - test("expect that today will be in the start/end range of dates of the first week when no preselected date", () => { - // Arrange - const { wrapper } = setupMocks({}); - const todayString = "2023-02-01"; - - // Act - const testResult = wrapper.vm.getInitialViewWeeks(todayString, 5); - - // Assert - expect( - testResult[0].weekStartDate <= todayString && - todayString <= testResult[0].weekEndDate - ).toBe(true); - - wrapper.unmount(); - }); - - test("expect that single weeks that span 2 months will be split into separate shorter 'week' spans", () => { - // Arrange - const { wrapper } = setupMocks({}); - const todayString = "2023-02-15"; - - // Act - const testResult = wrapper.vm.getInitialViewWeeks(todayString, 5); - - // Assert - expect(testResult.length).toBe(5); - expect(testResult[0]).toMatchObject({ - weekStartDate: "2023-02-12", - }); - expect(testResult[testResult.length - 1]).toMatchObject({ - weekEndDate: "2023-03-11", - }); - - wrapper.unmount(); - }); - - test("expect that if today is 7/31/23 and there's a preselected date of 10/1/23 that there will be 14 initial weeks returned", () => { - // Arrange - const { wrapper } = setupMocks({}); - const mockPreSelectedDate = "2023-10-01"; - - // Act - const testResult = wrapper.vm.getInitialViewWeeks( - "2023-07-31", - 5, - mockPreSelectedDate - ); - - // Assert - expect(testResult.length).toBe(14); - - wrapper.unmount(); - }); - - test("expect that if there's a preselected date that the last week end date will be the end of the month of the preselected date", () => { - // Arrange - const { wrapper } = setupMocks({}); - const mockPreSelectedDate = "2023-10-01"; - - // Act - const testResult = wrapper.vm.getInitialViewWeeks( - "2023-07-31", - 5, - mockPreSelectedDate - ); - - // Assert - expect(testResult[testResult.length - 1]).toMatchObject({ - weekEndDate: "2023-10-31", - }); - - wrapper.unmount(); - }); - }); - - describe("when there's a pre-selected date...", () => { - test("and loadInitialData runs, expect hideSomeDaysForInitialView and hideSecondMonth to be false", async () => { - // Arrange - // uses "2023-12-01" which would set both hideSomeDaysForInitialView and hideSecondMonth to true, unless there was a pre-selected date - const overrideDate = mockValuesForSetDate01.overrideDate; - const mockMonthEnd = mockValuesForSetDate01.monthEnd; - const mockInitialViewWeeks = mockValuesForSetDate01.initialViewWeeks; - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: overrideDate, - }, - }); - - // Act - const localThis = { - todayString: null, - todayOverrideDateString: overrideDate, - getMonthEnd: () => mockMonthEnd, - getInitialViewWeeks: () => mockInitialViewWeeks, - }; - const testResult = await datePicker.methods.loadInitialData.call(localThis, { - selectableDatesSetting: "custom", - initialViewRowsToShow: 5, - getSelectableDatesCallback: mockPromiseResolve, - preSelectedDate: "2024-01-01", - todayOverrideDateString: overrideDate, - }); - - // Assert - expect(testResult).toMatchObject({ - hideSomeDaysForInitialView: false, - hideSecondMonth: false, - }); - - wrapper.unmount(); - }); - }); - }); - - describe("showAnotherMonth method...", () => { - test("expect that when hideSomeDaysForInitialView is true that it scrolls to correct month", async () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: mockValuesForSetDate01.overrideDate, - }, - }); - wrapper.vm.months = mockValuesForSetDate01.monthsData; - wrapper.vm.updateSelectableDates = jest.fn().mockImplementation(); - wrapper.vm.scrollToElement = jest.fn().mockImplementation(); - wrapper.vm.hideSomeDaysForInitialView = true; - - // Act - await wrapper.vm.showAnotherMonth(); - - // // Assert - // expect(wrapper.vm.updateSelectableDates).toHaveBeenCalledWith( - // "2023-12-31", - // "2023-12-31" - // ); - // expect(wrapper.vm.scrollToElement).toHaveBeenCalledWith("December-2023"); - - // wrapper.unmount(); - }); - - test("expect that when hideSomeDaysForInitialView is false and direction is FUTURE that it scrolls to correct month", async () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: mockValuesForSetDate01.overrideDate, - }, - }); - wrapper.vm.months = [...mockValuesForSetDate01.monthsData]; // copy array w/o reference to original - wrapper.vm.updateSelectableDates = jest.fn().mockImplementation(); - wrapper.vm.scrollToElement = jest.fn().mockImplementation(); - wrapper.vm.hideSomeDaysForInitialView = false; - - // Act - await wrapper.vm.showAnotherMonth(); - - // Assert - expect(wrapper.vm.updateSelectableDates).toHaveBeenCalledWith( - "2024-01-01", - "2024-01-31" - ); - expect(wrapper.vm.scrollToElement).toHaveBeenCalledWith("January-2024"); - - wrapper.unmount(); - }); - - test("expect that when hideSomeDaysForInitialView is false and direction is PAST that it scrolls to correct month", async () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "past", - todayOverrideDateString: mockValuesForSetDate01.overrideDate, - }, - }); - // Force correct state for this test. // TODO - figure out why original array is getting mutated here, making next line necessary - mockValuesForSetDate01.monthsData[1].monthClass = " month-hidden"; - - wrapper.vm.months = [...mockValuesForSetDate01.monthsData]; // copy array w/o reference to original - wrapper.vm.updateSelectableDates = jest.fn().mockImplementation(); - wrapper.vm.scrollToElement = jest.fn().mockImplementation(); - wrapper.vm.hideSomeDaysForInitialView = false; - - // Act - await wrapper.vm.showAnotherMonth(); - - // Assert - expect(wrapper.vm.updateSelectableDates).toHaveBeenCalledWith( - "2024-01-01", - "2024-01-31" - ); - expect(wrapper.vm.scrollToElement).toHaveBeenCalledWith("January-2024"); - - wrapper.unmount(); - }); - - test("expect that when the month to scroll to is the last available month that it will disable the View More Dates button", async () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: mockValuesForSetDate01.overrideDate, - }, - }); - wrapper.vm.months = [...mockValuesForSetDate01.monthsData]; // copy array w/o reference to original - wrapper.vm.updateSelectableDates = jest.fn().mockImplementation(); - wrapper.vm.scrollToElement = jest.fn().mockImplementation(); - wrapper.vm.disableViewMoreDatesButton = false; - mockValuesForSetDate01.monthsData[1].monthClass = " month-hidden last-available-month"; // Force correct state for this test. - - // Act - await wrapper.vm.showAnotherMonth(); - - // Assert - expect(wrapper.vm.disableViewMoreDatesButton).toBe(true); - - wrapper.unmount(); - }); - }); - - describe("updateSelectableDates method...", () => { - test("expect that new dates from callback are added to selectableDatesInshop", async () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: mockValuesForSetDate01.overrideDate, - getMoreDatesCallback: jest.fn(() => mockCallbackResponse), - }, - }); - wrapper.vm.selectableDatesInshop = [ - { - date: "2023-12-30", - timeSlots: [ - { - id: "06747-01820-S-B*20453*7 AM", - startTime: "07:00", - endTime: "08:00", - offerPremium: false, - }, - ], - }, - ]; - wrapper.vm.months = mockValuesForSetDate01.monthsData; - - // Act - await wrapper.vm.updateSelectableDates("2024-01-01", "2024-01-05"); - - const isFoundInArray = wrapper.vm.selectableDatesInshop.find((obj) => { - return obj.date === "2024-01-02"; - }); - - // Assert - expect(isFoundInArray).not.toBeUndefined(); - - wrapper.unmount(); - }); - - test("expect that isAvailable flag on selectable dates is set to true", async () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: mockValuesForSetDate01.overrideDate, - getMoreDatesCallback: jest.fn(() => mockCallbackResponse), - }, - }); - wrapper.vm.selectableDatesInshop = [ - { - date: "2023-12-30", - timeSlots: [ - { - id: "06747-01820-S-B*20453*7 AM", - startTime: "07:00", - endTime: "08:00", - offerPremium: false, - }, - ], - }, - ]; - wrapper.vm.months = mockValuesForSetDate01.monthsData; - - // Act - await wrapper.vm.updateSelectableDates("2024-01-01", "2024-01-05"); - const targetMonth = wrapper.vm.months.find((month) => { - return month.monthString === "January-2024"; - }); - - console.log("TEST targetMonth: ", targetMonth); - - const dateInArray = targetMonth.datesInshop.find((date) => { - return date.inputValue === "2024-01-02"; - }); - - // Assert - expect(dateInArray).toMatchObject({ - isAvailable: true, - }); - - wrapper.unmount(); - }); - }); - - describe("scrollToElement method...", () => { - test("expect that new dates from callback are added to selectableDatesInshop", () => { - // Arrange - const container = document.createElement("div"); - container.classList.add("page-container-grouped-styles"); - const targetElement = document.createElement("div"); - targetElement.setAttribute("id", "January-2024"); - container.appendChild(targetElement); - document.body.appendChild(container); - window.requestAnimationFrame = jest.fn(); - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: mockValuesForSetDate01.overrideDate, - }, - }); - - // Act - wrapper.vm.scrollToElement("January-2024"); - - // Assert - expect(window.requestAnimationFrame).toBeCalled(); - - wrapper.unmount(); - }); - }); - - describe("If user selects a date...", () => { - test("expect it will emit update:modelValue", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: "2023-07-05", - }, - }); - const dateToSelect = "2023-07-07"; - - // Act - wrapper.setValue({ selectedDate: dateToSelect }); - - // Assert - expect(wrapper.emitted()["update:modelValue"][0]).toEqual([ - { selectedDate: "2023-07-07" }, - ]); - - wrapper.unmount(); - }); - - test("expect watched modelValue will be triggered", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: "2023-07-05", - }, - }); - const dateToSelect = "2023-07-07"; - wrapper.vm.resetField = jest.fn(); - - // Act - wrapper.vm.$options.watch.modelValue.call(wrapper.vm, dateToSelect); - - // Assert - expect(wrapper.vm.resetField).toBeCalled(); - expect(wrapper.vm.resetField).toBeCalledWith({ value: dateToSelect }); - - wrapper.unmount(); - }); - }); - - describe("If fireDateSelectedEvent runs... ", () => { - test("expect it will emit date-clicked on mouse clicks ", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - }, - }); - - // Act - wrapper.vm.fireDateSelectedEvent({}); - const flattenedEmitted = JSON.stringify(wrapper.emitted()); - - // Assert - expect(wrapper.emitted()).toHaveProperty("date-clicked"); - expect(flattenedEmitted.includes("date-clicked")).toBeTruthy; - - wrapper.unmount(); - }); - - test("expect it will NOT emit date-clicked on keyboard arrow navigation ", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - }, - }); - - // Act - wrapper.vm.fireDateSelectedEvent({ screenX: 0, screenY: 0 }); - const flattenedEmitted = JSON.stringify(wrapper.emitted()); - - // Assert - expect(wrapper.emitted()).not.toHaveProperty("date-clicked"); - expect(flattenedEmitted.includes("date-clicked")).toBe(false); - - wrapper.unmount(); - }); - }); - - describe("If user triggers a validation error...", () => { - test("expect that watched errorMessage will be triggered", () => { - // Arrange - const { wrapper } = setupMocks({ - propsData: { - selectableDatesSetting: "custom", - todayOverrideDateString: "2023-07-05", - }, - }); - wrapper.vm.scrollToElement = jest.fn(); - - // Act - wrapper.vm.$options.watch.errorMessage.call(wrapper.vm, "anything"); - - // Assert - expect(wrapper.vm.scrollToElement).toBeCalled(); - expect(wrapper.vm.scrollToElement).toBeCalledWith("date-of-month-error"); - - wrapper.unmount(); - }); - }); -}); - -function setupMocks(mountOptionsMockData = {}) { - store.getters = { - order: { - serviceLocation: { appointmentType: "Inshop", provider: { providerNumber: "12345" } }, - }, - lineItems: { supportingItems: [] }, - }; - const initialMountOptionsMockData = { - store: { - getters: store.getters, - }, - }; - const mountOptions = getMountOptions( - Object.assign(initialMountOptionsMockData, mountOptionsMockData) - ); - - const wrapper = shallowMount(datePicker, mountOptions); - - return { wrapper }; -} diff --git a/src/digital-components/date-picker/date-picker.vue b/src/digital-components/date-picker/date-picker.vue index d586ff839..59297ae05 100644 --- a/src/digital-components/date-picker/date-picker.vue +++ b/src/digital-components/date-picker/date-picker.vue @@ -1,17 +1,18 @@