Merge remote-tracking branch 'origin/develop' into feature/CASH-1529

This commit is contained in:
Scott Kiener 2025-10-02 12:06:48 -04:00
commit 7853750139
24 changed files with 254 additions and 231 deletions

74
package-lock.json generated
View file

@ -9,7 +9,7 @@
"version": "0.1.0",
"dependencies": {
"@iframe-resizer/child": "^5.3.3",
"axios": "^0.23.0",
"axios": "^0.30.2",
"bootstrap": "^5.3.3",
"core-js": "^3.38.1",
"http-status-codes": "^2.3.0",
@ -5024,7 +5024,6 @@
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true,
"license": "MIT"
},
"node_modules/at-least-node": {
@ -5082,12 +5081,30 @@
}
},
"node_modules/axios": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.23.0.tgz",
"integrity": "sha512-NmvAE4i0YAv5cKq8zlDoPd1VLKAqX5oLuZKs8xkJa4qi6RGn0uhCYFjWtHHC9EM/MwOwYWOs53W+V0aqEXq1sg==",
"version": "0.30.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.30.2.tgz",
"integrity": "sha512-0pE4RQ4UQi1jKY6p7u6i1Tkzqmu+d+/tHS7Q7rKunWLB9WyilBTpHHpXzPNMDj5hTbK0B0PTLSz07yqMBiF6xg==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.14.4"
"follow-redirects": "^1.15.4",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axios/node_modules/form-data": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/babel-jest": {
@ -5598,7 +5615,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@ -5969,7 +5985,6 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
@ -6009,9 +6024,9 @@
}
},
"node_modules/compression": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz",
"integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==",
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
"integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -6019,7 +6034,7 @@
"compressible": "~2.0.18",
"debug": "2.6.9",
"negotiator": "~0.6.4",
"on-headers": "~1.0.2",
"on-headers": "~1.1.0",
"safe-buffer": "5.2.1",
"vary": "~1.1.2"
},
@ -6841,7 +6856,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.4.0"
@ -7083,7 +7097,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
@ -7300,7 +7313,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@ -7310,7 +7322,6 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@ -7327,7 +7338,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
@ -7340,7 +7350,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@ -8544,15 +8553,16 @@
}
},
"node_modules/form-data": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.3.tgz",
"integrity": "sha512-q5YBMeWy6E2Un0nMGWMgI65MAKtaylxfNJGJxpGh45YDciZB4epbWpaAfImil6CPAPTYB4sh0URQNDRIZG5F2w==",
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz",
"integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.35"
},
"engines": {
@ -8642,7 +8652,6 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
@ -8672,7 +8681,6 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
@ -8707,7 +8715,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"dev": true,
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
@ -8807,7 +8814,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@ -8880,7 +8886,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@ -8893,7 +8898,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
@ -8916,7 +8920,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
@ -11976,7 +11979,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@ -12090,7 +12092,6 @@
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@ -12100,7 +12101,6 @@
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
@ -12643,9 +12643,9 @@
}
},
"node_modules/on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
"dev": true,
"license": "MIT",
"engines": {
@ -13922,6 +13922,12 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",

View file

@ -13,7 +13,7 @@
},
"dependencies": {
"@iframe-resizer/child": "^5.3.3",
"axios": "^0.23.0",
"axios": "^0.30.2",
"bootstrap": "^5.3.3",
"core-js": "^3.38.1",
"http-status-codes": "^2.3.0",

View file

@ -4,4 +4,5 @@ import { PaymentMethod } from './localTypes/Enums'
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
}

View file

@ -5,23 +5,23 @@ export enum PaymentMethod {
}
export enum ProgressBarPercentages {
VehicleSelectionPage = '8%',
VehicleDamagePage = '11%',
EstimatePage = '18%',
ServiceZipPage = '22%',
VehicleLookupAddressPage = '22%',
VehicleLookupLicensePage = '22%',
VinLookupPage = '22%',
PartQuestionsPage = '30%',
VehicleSelectionPage = '5%',
VehicleDamagePage = '9%',
EstimatePage = '12%',
ServiceZipPage = '20%',
VehicleLookupAddressPage = '20%',
VehicleLookupLicensePage = '20%',
VinLookupPage = '15%',
PartQuestionsPage = '15%',
MoldingQuestionsPage = '30%',
CapabilityQuestionsPage = '30%',
VehiclePartsPage = '30%',
ServicePackagePage = '62%',
InsuranceCompanyPage = '52%',
VehiclePartsPage = '20%',
ServicePackagePage = '60%',
InsuranceCompanyPage = '60%',
ServiceLocationPage = '76%',
SchedulePage = '76%',
MobileDetailsPage = '76%',
ContactDetailsPage = '84%',
PaymentMethodPage = '97%',
ContactDetailsPage = '85%',
PaymentMethodPage = '98.5%',
OrderConfirmationPage = '100%'
}

View file

@ -25,7 +25,7 @@ export class BasePage {
this.pageSpinner = page.getByRole('status');
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.progressBar = this.page.locator('.progress-bar-outer .progress-bar-inner');
}
async nextPage() {
@ -133,22 +133,22 @@ export class BasePage {
async validateProgressBar(progressPercentage: string, timeout: number = 60000) {
// This section has been commented out until progress bar work is completed for parity.
// await waitUntil(async () => {
// let loaderElements = await this.page.locator('button .loader, .buy-loader, timeout, .modal-loader').all();
// return !(await Promise.any(loaderElements.map(el => el.isVisible())).catch(() => false));
// });
await waitUntil(async () => {
let loaderElements = await this.page.locator('button .loader, .buy-loader, timeout, .modal-loader').all();
return !(await Promise.any(loaderElements.map(el => el.isVisible())).catch(() => false));
});
// // Take a screenshot of the page before validating the progress bar
// await this.page.screenshot({ path: `test-results\\ortoni-data\\progress-bar-${Date.now()}.png`, fullPage: true });
// // Wait until the progress bar element is attached and visible
// Take a screenshot of the page before validating the progress bar
await this.page.screenshot({ path: `test-results\\ortoni-data\\progress-bar-${Date.now()}.png`, fullPage: true });
// Wait until the progress bar element is attached and visible
// const actualProgressPercentage = await this.progressBar.evaluate(
// async (element) => {
// await new Promise(resolve => setTimeout(resolve, 200));
// return element.style.width || "Not Found";
// }
// );
// Soft.expect(actualProgressPercentage).toBe(progressPercentage);
// console.log(`Progress Bar Percentage: Actual - ${actualProgressPercentage} vs Expected - ${progressPercentage}`);
const actualProgressPercentage = await this.progressBar.evaluate(
async (element) => {
await new Promise(resolve => setTimeout(resolve, 200));
return element.style.width || "Not Found";
}
);
Soft.expect(actualProgressPercentage).toBe(progressPercentage);
console.log(`Progress Bar Percentage: Actual - ${actualProgressPercentage} vs Expected - ${progressPercentage}`);
}
}

View file

@ -14,7 +14,7 @@ export class ContactDetailsPage extends BasePage {
readonly lastNameTextBox: Locator;
readonly emailAddressTextBox: Locator;
readonly phoneNumberTextBox: Locator;
readonly notesTextBox: Locator;
readonly textOptInBox: Locator;
constructor(page: Page) {
super(page);
@ -25,7 +25,7 @@ export class ContactDetailsPage extends BasePage {
this.lastNameTextBox = this.page.getByRole('textbox', { name: 'Last name' });
this.emailAddressTextBox = this.page.getByRole('textbox', { name: 'Email address' });
this.phoneNumberTextBox = this.page.getByRole('textbox', { name: 'Phone number' });
this.notesTextBox = this.page.getByRole('textbox', { name: 'Notes' });
this.textOptInBox = this.page.getByRole('checkbox', { name: 'Text me updates about my appointment*' });
}
@ -36,10 +36,8 @@ export class ContactDetailsPage extends BasePage {
await this.phoneNumberTextBox.fill(customerDetails.phoneNumber);
}
async fillNotes(notes?: string) {
if (notes) {
await this.notesTextBox.fill(notes);
}
async checkTextMeUpdates(customerDetails: ICustomerDetails) {
await this.textOptInBox.check();
}
@step("ContactDetailsPage >> Enter contact details: ")
@ -48,6 +46,11 @@ export class ContactDetailsPage extends BasePage {
await this.validateProgressBar(ProgressBarPercentages.ContactDetailsPage);
await this.enterContactDetails(customerDetails!);
if (testData.isOptedInForTextMessages) {
await this.checkTextMeUpdates(customerDetails!);
}
await this.nextPage();
}
}

View file

@ -1,7 +1,7 @@
import { expect, type Locator, type Page } from '@playwright/test';
import { BasePage } from './BasePage';
import { getTestObject } from 'framework/Typedefs';
import { ServicePackage, PaymentType, ServiceLocation, Soft } from 'safelite-playwright-core';
import { ServicePackage, PaymentType, ServiceLocation, Soft, waitUntil } from 'safelite-playwright-core';
import { PaymentMethod, ProgressBarPercentages } from "framework/localTypes/Enums";
import { ITestData } from 'framework/TestData';
import { step } from 'framework/localTypes/Step';
@ -22,6 +22,8 @@ export class OrderConfirmationPage extends BasePage {
readonly winshieldWiper: Locator;
readonly rainDefense: Locator;
readonly appointmentSummarySection: Locator;
readonly orderDetailsSection: Locator;
readonly amountPaid: Locator;
constructor(page: Page) {
super(page);
@ -38,14 +40,16 @@ export class OrderConfirmationPage extends BasePage {
this.finalAmountDue = this.page.locator('div.amount-due');
this.cartServicePackageText = this.cartServicePackageText = this.page.locator('.cart-panel');
this.appointmentSummarySection = this.page.locator('.main .scheduleText, .main .add-to-calendar-text, .main .appointment-text, .main .duration-text-block');
this.orderDetailsSection = this.page.locator('.cart .vin-toggle a');
this.amountPaid = this.page.locator('.amount-paid span').nth(1);
}
async validateOrderConfirmationPage(testData: Partial<ITestData>) {
// 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()));
// Grab text
@ -85,17 +89,15 @@ export class OrderConfirmationPage extends BasePage {
subtotalTextValue?.replaceAll(',', '')
const finalAmountDueAmt = Number.parseFloat(finalAmountDueValue!.split('$')[1].replaceAll(',', ''));
Soft.expect(subtotalAmt).toBeGreaterThan(0);
// Soft.expect(deductibleAmt).toEqual(0);
if ((paymentDetails!.paymentType === PaymentType.PayAtService || isCashInsuranceFlow) && (servicePackageAmt > 0)) {
if ((paymentDetails!.paymentType === PaymentType.PayAtService || isCashInsuranceFlow) && (servicePackageAmt > 0)) {
// Verify amount due > 0
Soft.expect(amountDueAmt).toBeGreaterThan(0);
Soft.expect(finalAmountDueAmt).toBeGreaterThan(0);
if (isPolicyUnverified && PaymentType.PayWithInsurance){
if (isPolicyUnverified && PaymentType.PayWithInsurance) {
Soft.expect(finalAmountDueAmt).toContain('Verifying coverage')
}
} else {
@ -103,8 +105,8 @@ export class OrderConfirmationPage extends BasePage {
Soft.expect(amountDueAmt).toEqual(0);
Soft.expect(finalAmountDueAmt).toEqual(0);
}
}
}
}
async getFormattedAppointmentDate(appointmentDate: string) {
@ -116,14 +118,39 @@ export class OrderConfirmationPage extends BasePage {
return updatedAppointmentDate;
}
async toggleOrderDetailsSection() {
await this.orderDetailsSection.click().then(
async () => {
await waitUntil(async () => {
return (await this.page.locator('.vin-toggle.expanded').evaluate(el => window.getComputedStyle(el, ':after').transform)) === 'matrix(-1, 0, 0, -1, 0, 0)';
});
}
);
}
async logOrderNumber() {
const sessionStorage = JSON.parse(await this.page.evaluate('sessionStorage.getItem(\'submittedState\')'));
return sessionStorage.order.workOrderNumber;
}
async validateSettledAmountForPia() {
const sessionStorage = JSON.parse(await this.page.evaluate('sessionStorage.getItem(\'submittedState\')'));
const isPia = sessionStorage.order.payment.isPia;
if (isPia) {
const nextGenSettledAmount = Number.parseFloat(sessionStorage.order.payment.nextGenSettledAmount);
Soft.expect(nextGenSettledAmount, `NextGen Settled Amount > 0. NextGenSettledAmount: ${nextGenSettledAmount}`).toBeGreaterThan(0);
await this.toggleOrderDetailsSection();
let amountPaid = await this.amountPaid.textContent();
const parsedAmount = amountPaid === null
? console.log("Amount paid is null. Check the locator.")
: Number.parseFloat(amountPaid.replace(/[^0-9.]/g, ''));
Soft.expect(parsedAmount).toEqual(nextGenSettledAmount);
}
}
async getActualAppointmentSummary(): Promise<string[]> {
let appointmentSummary: string[] = [];
for (let element of await this.appointmentSummarySection.all()){
for (let element of await this.appointmentSummarySection.all()) {
let text = (await element.textContent() || "").replaceAll(/\u00A0|&nbsp;/g, ' ');
appointmentSummary.push(text.trim());
}
@ -146,12 +173,12 @@ export class OrderConfirmationPage extends BasePage {
appointmentSummary.push("Add to calendar");
appointmentSummary.push(
appointmentDetails?.serviceLocation == ServiceLocation.Mobile
? appointmentDetails?.serviceAddress
? ("We're coming to you at" + appointmentDetails.serviceAddress.street + ", " + appointmentDetails.serviceAddress.city + ", " + appointmentDetails.serviceAddress.state + " " + appointmentDetails.serviceAddress.postalCode + "to service your " + `${vehicleDetails!.year} ${vehicleDetails!.make} ${vehicleDetails!.model}`)
: ""
: appointmentDetails?.shopAddress
? ("You're going to a Safelite shop at" + appointmentDetails.shopAddress + "to service your " + `${vehicleDetails!.year} ${vehicleDetails!.make} ${vehicleDetails!.model}`)
: "");
? appointmentDetails?.serviceAddress
? ("We're coming to you at" + appointmentDetails.serviceAddress.street + ", " + appointmentDetails.serviceAddress.city + ", " + appointmentDetails.serviceAddress.state + " " + appointmentDetails.serviceAddress.postalCode + "to service your " + `${vehicleDetails!.year} ${vehicleDetails!.make} ${vehicleDetails!.model}`)
: ""
: appointmentDetails?.shopAddress
? ("You're going to a Safelite shop at" + appointmentDetails.shopAddress + "to service your " + `${vehicleDetails!.year} ${vehicleDetails!.make} ${vehicleDetails!.model}`)
: "");
appointmentSummary.push("Duration: " + customerDetails!.apptDuration!);
return appointmentSummary;
@ -159,10 +186,11 @@ export class OrderConfirmationPage extends BasePage {
@step("OrderConfirmationPage >> Validate order")
async verifyOrderConfirmationPage(testData: Partial<ITestData>) {
await this.page.waitForURL(new RegExp('(.+)confirmation'), {timeout: 60000});
await this.page.waitForURL(new RegExp('(.+)confirmation'), { timeout: 60000 });
await this.validateProgressBar(ProgressBarPercentages.OrderConfirmationPage);
await this.validateOrderConfirmationPage(testData);
const workOrderNumber = await this.logOrderNumber();
await this.validateSettledAmountForPia();
await test.step(`Session Storage Work Order Number: ${workOrderNumber}`, async () => {
console.log(`Session Storage Work Order Number: ${workOrderNumber}`);
});

View file

@ -1,7 +1,7 @@
import { expect, type Locator, type Page } from '@playwright/test';
import { BasePage } from './BasePage';
import { IPaymentDetails, Soft, waitUntil } from 'safelite-playwright-core';
import { AppointmentTimeslot, ServiceLocation, PaymentType, ServicePackage, VehicleDamage } from 'safelite-playwright-core';
import { AppointmentTimeslot, ServiceLocation, PaymentType, ServicePackage, VehicleDamage, } from 'safelite-playwright-core';
import { PaymentMethod, ProgressBarPercentages } from 'framework/localTypes/Enums';
import { PaymentPage } from './PaymentPage';
import { AfterpayPage } from './AfterpayPage';
@ -29,11 +29,9 @@ export class PaymentMethodPage extends BasePage {
// Payment detail page validation locators
readonly reviewTable: Locator;
readonly vehicleSection: Locator;
readonly damageSection: Locator;
readonly serviceDetailsSection: Locator;
readonly serviceLocationSection: Locator;
readonly appointmentDateSection: Locator;
readonly serviceLocationDateandTimeSection: Locator;
readonly vehicleDamageLocationsSection: Locator;
readonly contactDetailsSection: Locator;
readonly cartPanelDetails: Locator;
readonly subtotalText: Locator;
@ -65,11 +63,8 @@ export class PaymentMethodPage extends BasePage {
this.reviewTable = this.page.locator('div.review-table');
// Section locators - find by heading text
this.vehicleSection = this.page.locator('.review-table').locator('div', { hasText: 'Vehicle' }).first();
this.damageSection = this.page.locator('.review-table').locator('div', { hasText: 'Damage' }).first();
this.serviceDetailsSection = this.page.locator('.review-table').locator('div', { hasText: 'Glass service only' }).first();
this.serviceLocationSection = this.page.locator('.review-table').locator('div', { hasText: "We're coming to you" }).first();
this.appointmentDateSection = this.page.locator('.review-table').locator('div', { hasText: 'Appointment date + time' }).first();
this.serviceLocationDateandTimeSection = this.page.locator('.review-table').locator('div .service-location');
this.vehicleDamageLocationsSection= this.page.locator('.review-table').locator('div.py-3[damagelocationswidgetname="DamageLocationsWidget"]');
this.contactDetailsSection = this.page.locator('.review-table').locator('div', { hasText: 'Contact details' }).first();
// Cart panel elements
@ -99,15 +94,12 @@ export class PaymentMethodPage extends BasePage {
);
let expectedAppointmentDetails = new Map<string, string[]>();
expectedAppointmentDetails = await this.getExpectedVehicleDetails(testData, expectedAppointmentDetails);
expectedAppointmentDetails = await this.getExpectedVehicleDamage(testData, expectedAppointmentDetails);
// expectedAppointmentDetails = await this.expectedServicePackageDetails(testData, expectedAppointmentDetails);
expectedAppointmentDetails = await this.getExpectedServiceLocation(testData, expectedAppointmentDetails);
expectedAppointmentDetails = await this.getExpectedAppointmentDate(testData, expectedAppointmentDetails);
expectedAppointmentDetails = await this.getExpectedCustomerDetails(testData, expectedAppointmentDetails);
expectedAppointmentDetails = await this.getExpectedServiceLocationDateAndTimeSection(testData, expectedAppointmentDetails);
expectedAppointmentDetails = await this.getExpectedVehicleDamageAndVehicle(testData, expectedAppointmentDetails);
expectedAppointmentDetails = await this.getExpectedContactDetails(testData, expectedAppointmentDetails);
expectedAppointmentDetails = await this.getExpectedTextServiceUpdatesOptInOrOut(testData, expectedAppointmentDetails);
let actualAppointmentDetails = await this.getActualAppointmentDetails();
for (const key in expectedAppointmentDetails) {
Soft.expect(actualAppointmentDetails[key]?.map(item => item.toLowerCase()))
.toEqual(expectedAppointmentDetails[key].map(item => item.toLowerCase()));
@ -288,64 +280,66 @@ l
return expectedServicePackageDetails;
}
async getExpectedVehicleDamage(testdata: Partial<ITestData>, expectedServicePackageDetails: Map<string, string[]>): Promise<any> {
async getExpectedVehicleDamageAndVehicle(testdata: Partial<ITestData>, expectedServicePackageDetails: Map<string, string[]>): Promise<any> {
const {vehicleDamage} = testdata;
const {vehicleDetails, vehicleDamage} = testdata;
let vehicleDamageText: string[] = [];
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.WindshieldOneChip)) {
vehicleDamageText.push("Windshield repair - 1 chip");
let vehicleDamageText: string= '';
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.WindshieldOneChip || item == VehicleDamage.WindshieldTwoChips || item == VehicleDamage.WindshieldThreeChips)) {
vehicleDamageText = "Repair the windshield of your";
}
else if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.WindshieldTwoChips)) {
vehicleDamageText.push("Windshield repair - 2 chips");
}
else if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.WindshieldThreeChips)) {
vehicleDamageText.push("Windshield repair - 3 chips");
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.WindshieldCrack)) {
vehicleDamageText.push("Windshield crack");
vehicleDamageText = vehicleDamage?.length > 1 ? "Replace the Windshield" : "Replace the windshield";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.DriverVentGlass)) {
vehicleDamageText.includes("Side door - Driver side") ? vehicleDamageText[vehicleDamageText.length - 1 ] += "\nVent glass" : vehicleDamageText.push("Side door - Driver side", "Vent glass");
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.DriverVentGlass) && !vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.WindshieldCrack)) {
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Driver Vent Glass" : "Replace the Driver Vent Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.DriverFrontDoor)) {
vehicleDamageText.includes("Side door - Driver side") ? vehicleDamageText[vehicleDamageText.length - 1 ] += "\nFront door" : vehicleDamageText.push("Side door - Driver side", "Front door");
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Driver Front Glass" : "Replace the Driver Front Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.DriverRearDoor)) {
vehicleDamageText.includes("Side door - Driver side") ? vehicleDamageText[vehicleDamageText.length - 1 ] += "\nBack door" : vehicleDamageText.push("Side door - Driver side", "Back door");
}
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Driver Back Glass" : "Replace the Driver Back Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.DriverVentGlass) && vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.WindshieldCrack)) {
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Driver Vent Glass" : "Replace the Driver Vent Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.DriverQuarterPanel)) {
vehicleDamageText.includes("Side door - Driver side") ? vehicleDamageText[vehicleDamageText.length - 1 ] += "\nQuarter panel" : vehicleDamageText.push("Side door - Driver side", "Quarter panel");
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Driver Quarter Glass" : "Replace the Driver Quarter Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.PassengerVentGlass)) {
vehicleDamageText.includes("Side door - Passenger side") ? vehicleDamageText[vehicleDamageText.length - 1 ] += "\nVent glass" : vehicleDamageText.push("Side door - Passenger side", "Vent glass");
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Passenger Vent Glass" : "Replace the Passenger Vent Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.PassengerFrontDoor)) {
vehicleDamageText.includes("Side door - Passenger side") ? vehicleDamageText[vehicleDamageText.length - 1 ] += "\nFront door" : vehicleDamageText.push("Side door - Passenger side", "Front door");
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Passenger Front Glass" : "Replace the Passenger Front Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.PassengerRearDoor)) {
vehicleDamageText.includes("Side door - Passenger side") ? vehicleDamageText[vehicleDamageText.length - 1 ] += "\nBack door" : vehicleDamageText.push("Side door - Passenger side", "Back door");
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Passenger Back Glass" : "Replace the Passenger Back Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.PassengerQuarterPanel)) {
vehicleDamageText.includes("Side door - Passenger side") ? vehicleDamageText[vehicleDamageText.length - 1 ] += "\nQuarter panel" : vehicleDamageText.push("Side door - Passenger side", "Quarter panel");
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Passenger Quarter Glass" : "Replace the Passenger Quarter Glass";
}
if (vehicleDamage?.find((item: VehicleDamage) => item == VehicleDamage.RearWindow || item == VehicleDamage.RearSliding)) {
vehicleDamageText.push("Rear window");
vehicleDamageText.includes("Replace") ? vehicleDamageText += ", Back Glass" : "Replace the Back Glass";
}
expectedServicePackageDetails["Damage"] = vehicleDamageText;
// Replace last comma with " and" if there are multiple damages
if (vehicleDamageText.includes(",") && vehicleDamageText !== "Replace the windshield")
{
vehicleDamageText = vehicleDamageText.replace(/,([^,]*)$/, " and$1") + " of your";
}
expectedServicePackageDetails[vehicleDamageText] = [vehicleDetails?.year + " " + vehicleDetails?.make + " " + vehicleDetails?.model];
return expectedServicePackageDetails;
}
@ -399,55 +393,39 @@ l
return expectedServicePackageDetails;
}
async getExpectedServiceLocation(testData: Partial<ITestData>, expectedServicePackageDetails: Map<string, string[]>): Promise<any> {
const { appointmentDetails } = testData;
async getExpectedServiceLocationDateAndTimeSection(testData: Partial<ITestData>, expectedServicePackageDetails: Map<string, string[]>): Promise<any> {
const { customerDetails, appointmentDetails } = testData;
let serviceLocationTitle = appointmentDetails?.serviceLocation == ServiceLocation.Mobile
? "We're coming to you"
: "You're coming to us";
let serviceLocation: string[] = [];
serviceLocation.push(
appointmentDetails?.serviceLocation == ServiceLocation.Mobile
let serviceLocationText = appointmentDetails?.serviceLocation == ServiceLocation.Mobile
? appointmentDetails?.serviceAddress
? appointmentDetails.serviceAddress.street + ", " + appointmentDetails.serviceAddress.city + ", " + appointmentDetails.serviceAddress.state + " " + appointmentDetails.serviceAddress.postalCode
: ""
: appointmentDetails?.shopAddress
? appointmentDetails.shopAddress
: ""
);
: "";
serviceLocationText += " on "
serviceLocationText += customerDetails?.apptDate ? await this.getFormattedAppointmentDate(customerDetails.apptDate) + " " + customerDetails?.apptTime?.replace("-", "—") : "";
expectedServicePackageDetails[serviceLocationTitle] = serviceLocation;
expectedServicePackageDetails[serviceLocationTitle] = [serviceLocationText];
return expectedServicePackageDetails;
}
async getExpectedAppointmentDate(testData: Partial<ITestData>, expectedServicePackageDetails: Map<string, string[]>): Promise<any> {
const { customerDetails, appointmentDetails } = testData;
let appointmentDateText: string[] = [];
const isMobileAppointment = appointmentDetails?.serviceLocation == ServiceLocation.Mobile
if (isMobileAppointment) {
let localStorage= JSON.parse(await this.page.evaluate('localStorage.getItem(\'vuex\')'));
let jobMinMinutes = localStorage.order.schedule.jobMinMinutes as number;
let jobMaxMinutes = localStorage.order.schedule.jobMaxMinutes as number;
async getExpectedVehicleDamageLocations(testData: Partial<ITestData>, expectedServicePackageDetails: Map<string, string[]>): Promise<any> {
const { customerDetails } = testData;
let VehicleDamageLocationsText: string[] = [];
if (VehicleDamage.WindshieldCrack) {
let VehicleDamageLocationsText: string[] = [];
VehicleDamageLocationsText.push("Replace the windshield");
};
if (jobMinMinutes < 60) {
customerDetails!.apptDuration = `${jobMinMinutes} - ${jobMaxMinutes} minutes`;
} else {
const minHours = Math.floor(jobMinMinutes / 60);
const maxHours = Math.floor(jobMaxMinutes / 60);
customerDetails!.apptDuration = `${minHours} - ${maxHours} hours`;
}
}
appointmentDateText.push(
customerDetails?.apptDate ? await this.getFormattedAppointmentDate(customerDetails.apptDate) + " " + customerDetails?.apptTime?.replace("-", "—") : "",
customerDetails?.apptDuration ? "Estimated appointment length: " + customerDetails.apptDuration : ""
);
expectedServicePackageDetails["Appointment Date + Time"] = appointmentDateText;
expectedServicePackageDetails["VehicleDamageLocationsText"] = VehicleDamageLocationsText;
return expectedServicePackageDetails;
}
async getExpectedCustomerDetails(testData: Partial<ITestData>, expectedServicePackageDetails: Map<string, string[]>): Promise<any> {
async getExpectedContactDetails(testData: Partial<ITestData>, expectedServicePackageDetails: Map<string, string[]>): Promise<any> {
const { customerDetails } = testData;
let customerDetailsText: string[] = [];
@ -455,10 +433,21 @@ l
// customerDetails?.firstName.toUpperCase() + " " + customerDetails?.lastName.toUpperCase(),
customerDetails?.email ? customerDetails?.email.toUpperCase() : "",
customerDetails?.phoneNumber ? customerDetails?.phoneNumber : "",
"Opted out of text message updates"
);
expectedServicePackageDetails["Contact details"] = customerDetailsText;
expectedServicePackageDetails["Contact details"] = customerDetailsText
return expectedServicePackageDetails;
}
async getExpectedTextServiceUpdatesOptInOrOut(testData: Partial<ITestData>, expectedServicePackageDetails: Map<string, string[]>): Promise<any> {
const { customerDetails } = testData;
let isOptedInForTextMessages = testData.isOptedInForTextMessages ?? false;
let expectedText = isOptedInForTextMessages ? customerDetails?.phoneNumber : "Not opted in";
expectedServicePackageDetails["Well text service updates to"] = [expectedText];
return expectedServicePackageDetails;
}

View file

@ -214,7 +214,7 @@ export class SchedulePage extends BasePage {
{
formattedTimeSlot = selectedTimeSlot.includes("overnight") ? "drop off by 5:30 pm on the night of your scheduled appointment. Pick-up time dependent on shop schedule" : "drop off before 9:30 AM";
}
else if (selectedTimeSlot.includes("-") || selectedTimeSlot.includes("Earlybird"))
else if (selectedTimeSlot.includes("-") || selectedTimeSlot.includes("Earlybird")) // - means Mobile time slot where service time is between 8:00 AM - 12:00 PM
{
formattedTimeSlot = selectedTimeSlot.includes("Earlybird") ? "arriving between 8:00 AM - 12:00 PM" : `arriving between ${selectedTimeSlot}`;
}

View file

@ -5,7 +5,6 @@ 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("CashReplaceGlassPromoInshop");
@ -47,6 +46,9 @@ const cashReplaceGlassPromoInshopData: Partial<ITestData> = {
...getDefaultTestData().appointmentDetails!,
shopAddress: "6826 Sawmill Rd, Columbus, OH 43235"
},
// Flag for opting in for text message appt updates
isOptedInForTextMessages: true,
// Override payment details
paymentDetails: {

View file

@ -43,7 +43,7 @@
]"
:validationRules="validationRules"
@change="handleChange"
@blur="handleChange"
@blur="handleBlur"
:maxlength="maxLength ? maxLength : '999'"
@focus="$emit('focus', $event.target.value)"
@keydown="keyDownHandler"

View file

@ -79,7 +79,7 @@ export default {
url: cfDistroUrl + endpoint,
data: payloadAndAnalyticsData,
crossDomain: true,
responseType: {},
responseType: "json",
headers: headers,
}).then(
(response) => {

View file

@ -275,6 +275,7 @@ export default {
}
.sub-text strong {
text-transform: uppercase;
font-weight: 400;
}
}

View file

@ -800,7 +800,7 @@ export default {
isAfterpayBreakoutDisplay() {
return (
this.showInsuranceCoverageAs !== coverageStatus.PENDING &&
(!this.isRecalPriceRemove || !this.isRecalibrationOnOrder) &&
!this.isRecalPriceRemove &&
this.hasSettingEqualTo(experimentSettings.DISPLAY_AFTERPAY_BREAKOUT_DISPLAY, "true")
);
},

View file

@ -2,7 +2,9 @@
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<loadingModal ref="loadingModal" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
<div class="container quote position-relative">
<div
class="container quote position-relative"
:class="[showSaveProgressPopup ? 'initial-popup-open' : '']">
<div class="row justify-content-center">
<div class="col-12">
<funnelSubHeader class="mb-5" cmsWidgetName="FunnelSubHeaderWidget" />
@ -287,6 +289,7 @@ export default {
const isEmailInStoreOnPageLoad = emailFromStore?.length > 0;
const isPopupSkipped =
store.getters.pageData(routeData.QUOTE.name)?.saveProgressPopupSkipped === true;
// "popup" is the automatic opening centered popup on load
const showSaveProgressPopup = !(isEmailInStoreOnPageLoad || isPopupSkipped);
const showSaveProgressModal = isEmailInStoreOnPageLoad ? false : true;
const shouldSkipToInsurance =
@ -556,8 +559,8 @@ export default {
servicePackage: null,
holdParentAccountNumber: null,
holdBillToAccountNumber: null,
showSaveProgressPopup: null,
showSaveProgressModal: null,
showSaveProgressPopup: null, // "popup" is the automatic opening centered popup on load
showSaveProgressModal: null, // "modal" is the user-triggered slide-in drawer
isSaveProgressComplete: null,
packageNumber: null,
skipToInsurance: null,
@ -875,6 +878,10 @@ export default {
<style lang="scss" scoped>
// Unique to quote page for 2.0/Heritage parity
// prettier-ignore
.prevent-modal-scroll.modal-open .container.quote.initial-popup-open .row:not(.save-progress-popup) {
display: none;
}
.quote {
padding-bottom: 2rem;

View file

@ -246,7 +246,7 @@ export default {
},
isAfterpayBreakoutDisplay() {
return (
(!this.shouldHideRecalibration || !this.isRecalibrationOnOrder) &&
!this.shouldHideRecalibration &&
experimentMixin.methods.hasSettingEqualTo(
experimentSettings.DISPLAY_AFTERPAY_BREAKOUT_DISPLAY,
"true"

View file

@ -483,7 +483,7 @@ export default {
serviceZipCodeCtu: zipCodeData.zipCodeCtu,
mobileProviderNumber: shopProviderData.data.mobileProviderNumber,
},
"schedule",
to.name,
false
);
}
@ -1322,7 +1322,7 @@ export default {
) {
navigateToHeritageFunnel({
shouldSaveSession: false,
pageNameToLog: "service-location",
pageNameToLog: this.pageName,
navType: "back",
});
} else {
@ -1676,7 +1676,7 @@ export default {
handleZipCodeChange(newZipCode) {
this.resetMobileLocation();
getShopProviderData(newZipCode.zipCode).then((result) => {
getShopProviderData(newZipCode.zipCode, this.pageName).then((result) => {
this.shopProviderData = result.data;
this.resetSelectedProvider();
this.state = newZipCode.state;

View file

@ -7,8 +7,8 @@ export async function getPricedMobileFeePart(serviceZipCode, pageNameToLog) {
return null;
}
const zipCodeData = await getZipCodeData(serviceZipCode);
const providerData = await getShopProviderData(serviceZipCode);
const zipCodeData = await getZipCodeData(serviceZipCode, pageNameToLog);
const providerData = await getShopProviderData(serviceZipCode, pageNameToLog);
// Get the Mobile Fee Part
const mobileFeePart = await baseMixin.methods.dispatchStoreActionWithLogging(
@ -50,7 +50,7 @@ export async function getPricedRecycleFeePart(serviceZipCode, pageNameToLog) {
return null;
}
const zipCodeData = await getZipCodeData(serviceZipCode);
const zipCodeData = await getZipCodeData(serviceZipCode, pageNameToLog);
// Get supporting items
const supportingItems = await baseMixin.methods.dispatchStoreActionWithLogging(
@ -102,23 +102,23 @@ export async function getServiceabilityDetails(serviceZipCode, lineItems, pageNa
);
}
export async function getBillToAccountNumber(providerNumber) {
export async function getBillToAccountNumber(providerNumber, pageNameToLog) {
// Get the BillTo Account Number
return await baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_BILL_TO_ACCOUNT_NUMBER,
{ providerNumber: providerNumber },
"service-location",
pageNameToLog,
false
);
}
export async function getShopProviderData(serviceZipCode) {
export async function getShopProviderData(serviceZipCode, pageNameToLog) {
return await baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.GET_PROVIDERS,
{
serviceZipCode: serviceZipCode,
},
"service-location"
pageNameToLog
);
}
@ -139,6 +139,6 @@ export async function getClosestApplicableShops(serviceZipCode, carId, pageNameT
return closestShops.data;
}
export async function getZipCodeData(serviceZipCode) {
return await baseMixin.methods.getZipCodeData(serviceZipCode, "service-location");
export async function getZipCodeData(serviceZipCode, pageNameToLog) {
return await baseMixin.methods.getZipCodeData(serviceZipCode, pageNameToLog);
}

View file

@ -193,8 +193,7 @@ export default {
return;
}
const zipCodeData = await this.getZipCodeData(
this.internalModel.addressQuestions.zipCode,
"service-location"
this.internalModel.addressQuestions.zipCode
);
if (!zipCodeData.isValid) {
@ -216,26 +215,21 @@ export default {
// retrieve mobile fee part
const serviceZipCode = this.internalModel.addressQuestions.zipCode;
const mobileFeePart = await getPricedMobileFeePart(
serviceZipCode,
"service-location"
);
const mobileFeePart = await getPricedMobileFeePart(serviceZipCode, this.pageName);
// retrieve recycle fee part
const recycleFeePart = await getPricedRecycleFeePart(
serviceZipCode,
"service-location"
);
const recycleFeePart = await getPricedRecycleFeePart(serviceZipCode, this.pageName);
// retrieve serviceability details
const serviceabilityDetails = await getServiceabilityDetails(
serviceZipCode,
null,
"service-location"
this.pageName
);
const billToAccountNumber = await getBillToAccountNumber(
this.internalModel.zipCodeCtu
this.internalModel.zipCodeCtu,
this.pageName
);
// update content related to service zip code

View file

@ -226,17 +226,17 @@ export default {
const cmsContentPromise = fetchCmsContentForPage(to.name);
const serviceZipCode = store.getters.order.serviceLocation.zipCode;
const zipCodeDataPromise = getZipCodeData(serviceZipCode);
const zipCodeDataPromise = getZipCodeData(serviceZipCode, to.name);
const serviceabilityDetailsPromise = getServiceabilityDetails(
serviceZipCode,
null,
"service-location"
to.name
);
const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode, "service-location");
const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode, to.name);
const shopProviderDataPromise = getShopProviderData(serviceZipCode); // shopQuestion.methods.loadInitialData(serviceZipCode);
const shopProviderDataPromise = getShopProviderData(serviceZipCode, to.name); // shopQuestion.methods.loadInitialData(serviceZipCode);
// Settle promises and get results
const promiseResultMap = [
@ -846,7 +846,7 @@ export default {
zipCode: {
handler(newValue) {
if (!this.navigatingForward) {
getShopProviderData(this.zipCode).then(async (result) => {
getShopProviderData(this.zipCode, this.pageName).then(async (result) => {
this.shopProviderData = result.data;
if (this.selectedAppointmentType === "Mobile") {
this.selectedProvider = new Provider(

View file

@ -162,7 +162,7 @@ export default {
const zipCodeData = await this.getZipCodeData(
this.internalModel.zipCode,
"service-location"
this.pageName
);
if (!zipCodeData.isValid) {
@ -175,7 +175,7 @@ export default {
const closestShops = await getClosestApplicableShops(
this.internalModel.zipCode,
this.carId,
"service-location"
this.pageName
);
if (!closestShops || closestShops.inShopProviders?.length === 0) {
@ -193,24 +193,25 @@ export default {
const serviceZipCode = this.internalModel.zipCode;
const mobileFeePart = await getPricedMobileFeePart(
serviceZipCode,
"service-location"
this.pageName
);
// retrieve recycle fee part
const recycleFeePart = await getPricedRecycleFeePart(
serviceZipCode,
"service-location"
this.pageName
);
// retrieve serviceability details
const serviceabilityDetails = await getServiceabilityDetails(
serviceZipCode,
null,
"service-location"
this.pageName
);
const billToAccountNumber = await getBillToAccountNumber(
this.internalModel.zipCodeCtu
this.internalModel.zipCodeCtu,
this.pageName
);
// update content related to service zip code

View file

@ -280,7 +280,7 @@ export default {
}
this.isLoading = true;
this.zipCodeData = await this.getZipCodeData(this.localZipCode, "service-location");
this.zipCodeData = await this.getZipCodeData(this.localZipCode, this.pageName);
// Add zipCode to zipCodeData as it does not come back from endpoint
this.zipCodeData.zipCode = this.localZipCode;
if (this.zipCodeData.isValid) {
@ -289,7 +289,7 @@ export default {
const closestShops = await getClosestApplicableShops(
this.localZipCode,
this.$store.getters.order.vehicle.carId,
"service-location"
this.pageName
);
if (!closestShops || closestShops.inShopProviders?.length === 0) {
this.isLoading = false;

View file

@ -318,10 +318,6 @@ export default {
"vin-lookup"
);
// Update button "Continue with..."
this.$refs.navbar.updateButtonText(
`Continue with ${resultMap.vehicleLookupResponse.year} ${resultMap.vehicleLookupResponse.make} ${resultMap.vehicleLookupResponse.model}`
);
return this.$refs.navbar.removeLoader();
}

View file

@ -1,26 +1,21 @@
<template>
<loadingModal v-if="useLoadingModal" notFullScreen ref="loadingModal" />
<a v-if="linkType === 'navigation'" @click="handleClick" class="navigation-link" :href="href">
{{ text }}
<slot name="after-text"></slot>
</a>
<!-- NOTE: each prettier-ignore is needed below...
each anchor that follows needs to be one single line in markup or adds unwanted spaces within link (fixes CASH-1502) -->
<!-- prettier-ignore -->
<a v-if="linkType === 'navigation'" @click="handleClick" class="navigation-link" :href="href">{{ text }}<slot name="after-text"></slot></a>
<!-- prettier-ignore -->
<a
v-else-if="linkType === 'newWindowLink'"
@click="handleClick"
class="new-window-link"
:href="href"
target="_blank">
{{ text }}
<slot name="after-text"></slot>
</a>
<a v-else-if="linkType === 'textSmall'" @click="handleClick" class="small" :href="href">
{{ text }}
<slot name="after-text"></slot>
</a>
<a v-else-if="linkType === 'text'" @click="handleClick" :href="href">
{{ text }}
<slot name="after-text"></slot>
</a>
target="_blank">{{ text }}<slot name="after-text"></slot></a>
<!-- prettier-ignore -->
<a v-else-if="linkType === 'textSmall'" @click="handleClick" class="small" :href="href">{{ text }}<slot name="after-text"></slot></a>
<!-- prettier-ignore -->
<a v-else-if="linkType === 'text'" @click="handleClick" :href="href">{{ text }}<slot name="after-text"></slot></a>
</template>
<script>