Merge branch 'develop' into dependabot/npm_and_yarn/multi-75e6bc5210
This commit is contained in:
commit
8544592a5b
141 changed files with 4290 additions and 3920 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
41
package-lock.json
generated
41
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ITestPages> = (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),
|
||||
|
|
|
|||
4
playwright-tests/framework/localTypes/IExperiments.ts
Normal file
4
playwright-tests/framework/localTypes/IExperiments.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export interface IExperiments {
|
||||
isMobileFirst: boolean,
|
||||
isAdyenPayments: boolean
|
||||
}
|
||||
|
|
@ -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<ITestData>) {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<ITestData>): Promise<string> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
42
playwright-tests/pages/HeritageProblemGlassQuestionsPage.ts
Normal file
42
playwright-tests/pages/HeritageProblemGlassQuestionsPage.ts
Normal file
|
|
@ -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<ITestData>){
|
||||
|
||||
await this.dontShareVinButton.click();
|
||||
await this.yestoGlassQuestionButton.click();
|
||||
await this.continueButton.click();
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ITestData>) {
|
||||
const experimentUrlExtension = await this.buildExperimentUrl(testData);
|
||||
await this.page.goto(process.env['BASE_URL']! + experimentUrlExtension);
|
||||
}
|
||||
|
||||
async isCurrentVariant(): Promise<boolean> {
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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<ITestData>, zip: string, lookupType: VehicleLookupType, alertFlags?: IAlertFlags): Promise<boolean> {
|
||||
async handleZipValidation(testData: Partial<ITestData>): Promise<boolean> {
|
||||
|
||||
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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export class OrderConfirmationPage extends BasePage {
|
|||
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()));
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ITestData>) {
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ITestData>) {
|
||||
|
|
@ -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<ITestData>) {
|
||||
|
||||
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<ITestData>) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -12,9 +12,9 @@ export class ServiceZipPage extends LookupPage {
|
|||
|
||||
@step("ZipLookupPage >> Lookup by service ZIP: ")
|
||||
async handleServiceZipPage(testData: Partial<ITestData>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -29,10 +29,10 @@ export class VehicleLookupAddressPage extends LookupPage {
|
|||
|
||||
@step("VehicleLookupAddressPage >> Lookup by address: ")
|
||||
async handleVehicleLookupAddressPage(testData: Partial<ITestData>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -34,11 +34,11 @@ export class VehicleLookupLicensePage extends LookupPage {
|
|||
|
||||
@step("VehicleLookupLicensePage >> Lookup by license plate: ")
|
||||
async handleVehicleLookupLicensePage(testData: Partial<ITestData>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,11 +24,11 @@ export class VinLookupPage extends LookupPage {
|
|||
|
||||
@step("VinLookupPage >> Lookup by VIN: ")
|
||||
async handleVehicleLookupVinPage(testData: Partial<ITestData>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'],
|
||||
|
|
|
|||
|
|
@ -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<void> {
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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<ITestData> = {
|
|||
},
|
||||
|
||||
// Use predefined payment data
|
||||
paymentDetails: ClientData.getDefaultAfterpayDetails()
|
||||
paymentDetails: ClientData.getDefaultAfterpayDetails(),
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
const cashRepairInShopAfterPayTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
},
|
||||
|
||||
// Use predefined payment data
|
||||
paymentDetails: ClientData.getDefaultPaypalDetails()
|
||||
paymentDetails: ClientData.getDefaultPaypalDetails(),
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
const cashRepairInShopPayPalTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
},
|
||||
|
||||
// Use predefined payment data
|
||||
paymentDetails: ClientData.getDefaultCreditCardDetails()
|
||||
paymentDetails: ClientData.getDefaultCreditCardDetails(),
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
const cashRepairMobileCCTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
postalCode: '21237'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
servicePackage: ServicePackage.Premium,
|
||||
// Override vehicle details
|
||||
vehicleDetails: {
|
||||
...getDefaultTestData().vehicleDetails!,
|
||||
|
|
@ -59,7 +60,14 @@ const cashReplaceDynamicRecalMobileData: Partial<ITestData> = {
|
|||
|
||||
// Override payment details
|
||||
paymentDetails: {
|
||||
paymentType: PaymentType.PayAtService
|
||||
paymentType: PaymentType.PayAtService,
|
||||
// promoCode: 'digitalrain'
|
||||
promoCode: 'rain50'
|
||||
},
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
},
|
||||
|
||||
// Override payment details
|
||||
paymentDetails: ClientData.getDefaultAfterpayDetails()
|
||||
paymentDetails: ClientData.getDefaultAfterpayDetails(),
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
const cashReplaceGlassAddressLookupInshopAfterPayTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
// 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[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
paymentType: PaymentType.PayAtService,
|
||||
// Add promo code - key feature of this test
|
||||
promoCode: '20CALL',
|
||||
},
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
85
playwright-tests/tests/CashReplaceMobileFirstModal.ts
Normal file
85
playwright-tests/tests/CashReplaceMobileFirstModal.ts
Normal file
|
|
@ -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<ITestData> = {
|
||||
...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;
|
||||
|
|
@ -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<ITestData> = {
|
|||
optionToSelect: 'Gray Tint Privacy',
|
||||
secondaryQuestionOptionToSelect: 'solar, passenger side, encap, chrome molding'
|
||||
}
|
||||
]
|
||||
],
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
const cashReplaceMultiGlassMobileTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
optionToSelect: 'Green Tint',
|
||||
secondaryQuestionOptionToSelect: 'heated glass, solar, antenna, manual liftgate, 1 hole'
|
||||
}
|
||||
]
|
||||
],
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
const cashReplaceMultiGlassPromoInshopTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
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<ITestData> = {
|
|||
optionToSelect: 'Gray Tint Privacy',
|
||||
secondaryQuestionOptionToSelect: 'heated glass, solar, slider, power, kit'
|
||||
}
|
||||
]
|
||||
],
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
const cashReplaceMultiSlidingGlassDropoffTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
// Rain Defense promo code
|
||||
promoCode: 'rd50'
|
||||
},
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
const cashReplaceRainDefensePromoInshopTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
isOnPage: true,
|
||||
optionToSelect: 'Yes'
|
||||
},
|
||||
]
|
||||
],
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
const cashReplaceSafeliteCanNotRecalMobileTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
// Override payment details
|
||||
paymentDetails: {
|
||||
paymentType: PaymentType.PayAtService
|
||||
},
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
83
playwright-tests/tests/CashReplaceStaticInshop.ts
Normal file
83
playwright-tests/tests/CashReplaceStaticInshop.ts
Normal file
|
|
@ -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<ITestData> = {
|
||||
...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;
|
||||
|
|
@ -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<ITestData> = {
|
|||
appointmentDate: getDefaultTestData().appointmentDetails?.appointmentDate,
|
||||
},
|
||||
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
};
|
||||
|
||||
// Create and register the test case
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
paymentDetails: {
|
||||
paymentType: PaymentType.PayAtService
|
||||
},
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
const cashReplaceVinMobileTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
},
|
||||
{
|
||||
partQuestionType: PartQuestionType.GeneralQuestion2,
|
||||
isOnPage: false,
|
||||
isOnPage: true,
|
||||
optionToSelect: 'Yes'
|
||||
},
|
||||
]
|
||||
],
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
const cashReplaceWiperDropoffTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
paymentType: PaymentType.PayAtService,
|
||||
// Specific wiper promo code
|
||||
promoCode: '1WIPER0',
|
||||
},
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
],
|
||||
|
||||
// Override payment details (empty because we skip payment method page in insurance flow)
|
||||
paymentDetails: {}
|
||||
paymentDetails: {},
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
const insuranceAcuityPaypalTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
},
|
||||
|
||||
// Override payment details (empty because we skip payment method page in insurance flow)
|
||||
paymentDetails: {}
|
||||
paymentDetails: {},
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
const insuranceBigTruckVerifiedTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
},
|
||||
|
||||
// Override payment details (empty because we skip payment method page in insurance flow)
|
||||
paymentDetails: {}
|
||||
paymentDetails: {},
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
const insuranceGeicoTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
isOnPage: true,
|
||||
optionToSelect: 'Yes'
|
||||
},
|
||||
]
|
||||
],
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
const insuranceITAC21stCenturyTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
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[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
],
|
||||
|
||||
// Override payment details (empty because we skip payment method page in insurance flow)
|
||||
paymentDetails: {}
|
||||
paymentDetails: {},
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
};
|
||||
|
||||
const insuranceMeemicNearSchoolVerifiedTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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<ITestData> = {
|
|||
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<ITestData> = {
|
|||
// 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[] = [];
|
||||
|
|
|
|||
93
playwright-tests/tests/InsuranceOEMAllState.ts
Normal file
93
playwright-tests/tests/InsuranceOEMAllState.ts
Normal file
|
|
@ -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<ITestData> = {
|
||||
...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;
|
||||
|
|
@ -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<ITestData> = {
|
|||
},
|
||||
|
||||
// Override payment details (empty because we skip payment method page in insurance flow)
|
||||
paymentDetails: {}
|
||||
paymentDetails: {},
|
||||
|
||||
// Experiments
|
||||
experiments: {
|
||||
...getDefaultExperimentsData()
|
||||
}
|
||||
}
|
||||
|
||||
const insuranceUnverifiedTests: ITestCase[] = [];
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta name="robots" content="noindex" />
|
||||
<script>
|
||||
window.dataLayer = [{}];
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -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' },
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ const cartItemCategories = {
|
|||
GLASS_PARTS: "glassParts",
|
||||
SUPPORTING_ITEMS: "supportingItems",
|
||||
PROMOS: "promos",
|
||||
SERVICE_PACKAGE_DISCOUNT: "servicePackageDiscount",
|
||||
QUOTE_PAGE_DISCOUNT: "quotePageDiscount",
|
||||
};
|
||||
|
||||
export { cartItemCategories };
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
105
src/constants/quote-page-discounts.js
Normal file
105
src/constants/quote-page-discounts.js
Normal file
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,17 +1,18 @@
|
|||
<template>
|
||||
<div
|
||||
class="date-picker text-center"
|
||||
:class="`${calendarViewDirection} calendar-2025 ${showPricingByDayClass}`">
|
||||
:class="`${calendarViewDirection} calendar-2025 ${showPricingByDayClass} ${loadingStatus === 'reinitial' ? 'loading-reinitial' : ''}`">
|
||||
<fieldset id="date-picker-fieldset" ref="datePickerFieldset">
|
||||
<legend class="sr-only">Select a day and time</legend>
|
||||
<div
|
||||
v-for="month in months"
|
||||
v-for="(month, i) in months"
|
||||
:key="`${month.monthLabel}-${month.yearNum?.toString()}`"
|
||||
:id="`${month.monthLabel}-${month.yearNum?.toString()}`"
|
||||
class="calendar-grid-container"
|
||||
class="calendar-grid-container month"
|
||||
:class="[
|
||||
hideSomeDaysForInitialView ? 'partial-month-initial-view' : '',
|
||||
month.monthClass,
|
||||
i === 0 ? 'first-month-shown' : '',
|
||||
]">
|
||||
<div class="month-year body-small d-flex align-items-center">
|
||||
{{ month.monthLabel }} {{ month.yearNum?.toString() }}
|
||||
|
|
@ -114,7 +115,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<loader
|
||||
:class="[!isLoading ? 'date-picker-hidden' : '']"
|
||||
class="date-picker-loader"
|
||||
:class="[loadingStatus !== 'none' ? 'show-loader' : '']"
|
||||
loaderColor="blue"
|
||||
loaderPosition="center" />
|
||||
<div class="row form-test-error" id="date-of-month-error">
|
||||
|
|
@ -125,7 +127,7 @@
|
|||
type="button"
|
||||
class="btn btn-link view-more-dates"
|
||||
id="viewMoreDates"
|
||||
:disabled="isLoading"
|
||||
:disabled="loadingStatus !== 'none'"
|
||||
@click="showAnotherMonth">
|
||||
View more dates
|
||||
</button>
|
||||
|
|
@ -160,7 +162,6 @@ export default {
|
|||
name: "datePicker",
|
||||
data() {
|
||||
return {
|
||||
isLoading: true,
|
||||
months: null,
|
||||
disableViewMoreDatesButton: false,
|
||||
hideSomeDaysForInitialView: null,
|
||||
|
|
@ -169,7 +170,10 @@ export default {
|
|||
};
|
||||
},
|
||||
props: {
|
||||
customComponentId: String,
|
||||
customComponentId: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
selectableDatesSetting: {
|
||||
type: String,
|
||||
validator(value) {
|
||||
|
|
@ -200,6 +204,10 @@ export default {
|
|||
pricingByDayUpcharge: Number,
|
||||
isPricingByDayExperiment: Boolean,
|
||||
isMobileSelected: Boolean,
|
||||
loadingStatus: {
|
||||
type: String,
|
||||
default: "none",
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const uuid = uuidv4();
|
||||
|
|
@ -281,7 +289,7 @@ export default {
|
|||
if (event.screenX === 0 && event.screenY === 0) {
|
||||
return;
|
||||
}
|
||||
this.$emit("date-clicked", date);
|
||||
this.$emit("date-selected", date);
|
||||
},
|
||||
getWeekStartDate(dateString) {
|
||||
const date = convertDateStringToDate(dateString);
|
||||
|
|
@ -324,9 +332,9 @@ export default {
|
|||
let preSelectedDateMonthEnd = this.getMonthEnd(
|
||||
preSelectedDate.replace("-mobile", "")
|
||||
);
|
||||
let weekIncludesPreSelectedMonthEnd = false;
|
||||
let monthEndsByThisWeek = false;
|
||||
let i = 0;
|
||||
while (!weekIncludesPreSelectedMonthEnd) {
|
||||
while (!monthEndsByThisWeek && i < 52) {
|
||||
if (i > 0) {
|
||||
weekStartDate = this.getNextWeekSunday(weekEndDate);
|
||||
weekEndDate = this.getWeekEndDate(weekStartDate);
|
||||
|
|
@ -338,7 +346,12 @@ export default {
|
|||
preSelectedDateMonthEnd === weekEndDate
|
||||
) {
|
||||
weekEndDate = preSelectedDateMonthEnd;
|
||||
weekIncludesPreSelectedMonthEnd = true;
|
||||
monthEndsByThisWeek = true;
|
||||
} else if (preSelectedDateMonthEnd < weekEndDate) {
|
||||
// Additional case: if month ends before this week (but not necessarily during it),
|
||||
// still cut the loop here, but don't truncate the week.
|
||||
|
||||
monthEndsByThisWeek = true;
|
||||
}
|
||||
}
|
||||
weeks.push({
|
||||
|
|
@ -466,7 +479,7 @@ export default {
|
|||
const response = config.getSelectableDatesCallback({
|
||||
startDateString: initialViewStartDate,
|
||||
endDateString: initialViewEndDate,
|
||||
providerNumber: config.providerNumber,
|
||||
inshopProviderNumber: config.inshopProviderNumber,
|
||||
zipCode: config.zipCode,
|
||||
includeMobileTimeSlots: config.includeMobileTimeSlots,
|
||||
includeInshopTimeSlots: config.includeInshopTimeSlots,
|
||||
|
|
@ -489,7 +502,6 @@ export default {
|
|||
});
|
||||
},
|
||||
async setCalendarData(config = {}) {
|
||||
this.isLoading = true;
|
||||
this.selectableDatesInshop = [];
|
||||
this.selectableDatesMobile = [];
|
||||
this.months = [];
|
||||
|
|
@ -541,7 +553,6 @@ export default {
|
|||
// }
|
||||
}
|
||||
this.months = months;
|
||||
this.isLoading = false;
|
||||
|
||||
if (config.preSelectedDate) {
|
||||
this.$nextTick(() => {
|
||||
|
|
@ -729,7 +740,6 @@ export default {
|
|||
},
|
||||
async showAnotherMonth() {
|
||||
if (!this.months) return;
|
||||
this.isLoading = true;
|
||||
let monthToShow;
|
||||
let monthStartDate;
|
||||
|
||||
|
|
@ -774,7 +784,6 @@ export default {
|
|||
monthToShowDates[monthToShowDates.length - 1].inputValue
|
||||
);
|
||||
|
||||
this.isLoading = false;
|
||||
this.hideSomeDaysForInitialView = false; // if hid days on initial partial view, this will reveal those days
|
||||
monthToShow.monthClass = monthToShow.monthClass.replace(" month-hidden", "");
|
||||
this.scrollToElement(monthToShow.monthString);
|
||||
|
|
@ -787,7 +796,6 @@ export default {
|
|||
},
|
||||
async updateSelectableDates(monthStart, monthEnd) {
|
||||
const moreSelectableDates = await this.getMoreDatesCallback(monthStart, monthEnd);
|
||||
|
||||
moreSelectableDates.inshopTimeSlotsData.days.forEach((selectableDate) => {
|
||||
const index = this.selectableDatesInshop.findIndex(
|
||||
(dateObj) => dateObj.date === selectableDate.date
|
||||
|
|
@ -846,12 +854,6 @@ export default {
|
|||
},
|
||||
},
|
||||
watch: {
|
||||
isLoading(newValue) {
|
||||
// if done loading dates, then set to first available date
|
||||
if (newValue === false && this.firstAvailableSelectableDate) {
|
||||
this.selectedDate = this.firstAvailableSelectableDate;
|
||||
}
|
||||
},
|
||||
modelValue(newValue) {
|
||||
this.resetField({
|
||||
value: newValue,
|
||||
|
|
@ -872,9 +874,14 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.date-picker-hidden {
|
||||
.date-picker-loader {
|
||||
opacity: 0;
|
||||
max-height: 0;
|
||||
|
||||
&.show-loader {
|
||||
opacity: 1;
|
||||
max-height: none;
|
||||
}
|
||||
}
|
||||
.date-picker {
|
||||
position: relative;
|
||||
|
|
@ -903,12 +910,12 @@ export default {
|
|||
height: 1.5rem;
|
||||
}
|
||||
}
|
||||
.calendar-grid-container {
|
||||
.month {
|
||||
margin: 0 auto 1rem auto;
|
||||
position: relative;
|
||||
transition:
|
||||
height ease 2s,
|
||||
opacity ease 2s;
|
||||
height ease 1s,
|
||||
max-height ease 1s;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
justify-content: center;
|
||||
|
|
@ -1157,7 +1164,7 @@ export default {
|
|||
}
|
||||
|
||||
.past {
|
||||
.calendar-grid-container {
|
||||
.month {
|
||||
.month-year {
|
||||
grid-area: 1 / 1 / 2 / 6;
|
||||
}
|
||||
|
|
@ -1214,7 +1221,7 @@ export default {
|
|||
}
|
||||
// new calendar styling overrides
|
||||
&.calendar-2025 {
|
||||
.calendar-grid-container .grid-item {
|
||||
.month .grid-item {
|
||||
margin: 0 0 0.15rem 0;
|
||||
}
|
||||
.month-year {
|
||||
|
|
@ -1316,6 +1323,33 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.loading-reinitial {
|
||||
.month {
|
||||
opacity: 0;
|
||||
max-height: 0;
|
||||
}
|
||||
.month.first-month-shown {
|
||||
opacity: 1;
|
||||
max-height: none;
|
||||
|
||||
.grid-item.radio-wrapper.selectable-day {
|
||||
input[type="radio"]:checked + label,
|
||||
label {
|
||||
background-color: $white;
|
||||
border: none;
|
||||
color: initial;
|
||||
cursor: default;
|
||||
&:hover {
|
||||
border: none;
|
||||
}
|
||||
span {
|
||||
font-family: UrbanistRegular;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.timeslots .button-inner-wrapper) {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -160,6 +160,10 @@ export default {
|
|||
return this.staticBackdrop ? "static" : "true";
|
||||
},
|
||||
},
|
||||
unmounted() {
|
||||
// remove any modal effects before leaving page (e.g. user hits browser back button)
|
||||
this.closeModal();
|
||||
},
|
||||
components: {
|
||||
modalButtonMain,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
/* eslint-disable */
|
||||
// ***************************************************************
|
||||
// This file wraps the Salesforce generated code.
|
||||
// See the comment below for where you need to paste in the Salesforce generated code.
|
||||
// You may also need to modify the script tag src located in salesforce-webchat.vue to point to the correct Salesforce instance.
|
||||
// ***************************************************************
|
||||
|
||||
function setupCustomSettings(
|
||||
modifyEmbeddedSvcCallback,
|
||||
|
|
@ -7,90 +12,38 @@ function setupCustomSettings(
|
|||
onWebchatCloseCallback
|
||||
) {
|
||||
modifyEmbeddedSvcCallback();
|
||||
embedded_svc.addEventHandler("onSettingsCallCompleted", finishedLoadingCallback);
|
||||
embedded_svc.addEventHandler("afterMaximize", onWebchatOpenCallback);
|
||||
embedded_svc.addEventHandler("afterDestroy", onWebchatCloseCallback);
|
||||
window.addEventListener("onEmbeddedMessagingButtonCreated", finishedLoadingCallback);
|
||||
window.addEventListener("onEmbeddedMessagingWindowMaximized", onWebchatOpenCallback);
|
||||
window.addEventListener("onEmbeddedMessagingWindowClosed", onWebchatCloseCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
The code below is generated from salesforce but modified in the following ways:
|
||||
* Only the javascript inside the second <script> tag in the provided salesforce code is copy pasted
|
||||
* CSS is not copied in
|
||||
* First <script> tag loading in the file from the CDN is not copied in
|
||||
* Copy the entire contents of the second <script> tag inside the initializeSalesforceWebchatForDev
|
||||
method and add a call to setupCustomSettings before the 'embedded_svc.init()' method is called
|
||||
|
||||
Actual call to paste in:
|
||||
setupCustomSettings(
|
||||
modifyEmbeddedSvcCallback,
|
||||
finishedLoadingCallback,
|
||||
onWebchatOpenCallback,
|
||||
onWebchatCloseCallback
|
||||
);
|
||||
*/
|
||||
|
||||
export function initializeSalesforceWebchatForDev(
|
||||
modifyEmbeddedSvcCallback,
|
||||
finishedLoadingCallback,
|
||||
onWebchatOpenCallback,
|
||||
onWebchatCloseCallback
|
||||
) {
|
||||
var initESW = function (gslbBaseURL) {
|
||||
embedded_svc.settings.displayHelpButton = true; //Or false
|
||||
embedded_svc.settings.language = ""; //For example, enter 'en' or 'en-US'
|
||||
//embedded_svc.settings.defaultMinimizedText = '...'; //(Defaults to Chat with an Expert)
|
||||
//embedded_svc.settings.disabledMinimizedText = '...'; //(Defaults to Agent Offline)
|
||||
//embedded_svc.settings.loadingText = ''; //(Defaults to Loading)
|
||||
//embedded_svc.settings.storageDomain = 'yourdomain.com'; //(Sets the domain for your deployment so that visitors can navigate subdomains during a chat session)
|
||||
// Settings for Chat
|
||||
//embedded_svc.settings.directToButtonRouting = function(prechatFormData) {
|
||||
// Dynamically changes the button ID based on what the visitor enters in the pre-chat form.
|
||||
// Returns a valid button ID.
|
||||
//};
|
||||
//embedded_svc.settings.prepopulatedPrechatFields = {}; //Sets the auto-population of pre-chat form fields
|
||||
//embedded_svc.settings.fallbackRouting = []; //An array of button IDs, user IDs, or userId_buttonId
|
||||
//embedded_svc.settings.offlineSupportMinimizedText = '...'; //(Defaults to Contact Us)
|
||||
setupCustomSettings(
|
||||
modifyEmbeddedSvcCallback,
|
||||
finishedLoadingCallback,
|
||||
onWebchatOpenCallback,
|
||||
onWebchatCloseCallback
|
||||
);
|
||||
// START OF SALESFORCE GENERATED CODE
|
||||
// Copy in the contents of the first <script> tag inside the initEmbeddedMessaging() method
|
||||
try {
|
||||
embeddedservice_bootstrap.settings.language = "en_US"; // For example, enter 'en' or 'en-US'
|
||||
|
||||
embedded_svc.settings.enabledFeatures = ["LiveAgent"];
|
||||
embedded_svc.settings.entryFeature = "LiveAgent";
|
||||
|
||||
setupCustomSettings(
|
||||
modifyEmbeddedSvcCallback,
|
||||
finishedLoadingCallback,
|
||||
onWebchatOpenCallback,
|
||||
onWebchatCloseCallback
|
||||
);
|
||||
|
||||
embedded_svc.init(
|
||||
"https://safelite2--dev.sandbox.my.salesforce.com",
|
||||
"https://safelite2--dev.sandbox.my.salesforce-sites.com/chat",
|
||||
gslbBaseURL,
|
||||
embeddedservice_bootstrap.init(
|
||||
"00D6s0000008r6A",
|
||||
"Sales_Central_Snap_in",
|
||||
"Sales_Central_TFN",
|
||||
"https://safelite2--dev.sandbox.my.site.com/ESWSalesCentralTFN1755660094477",
|
||||
{
|
||||
baseLiveAgentContentURL:
|
||||
"https://c.la12s-core1.sfdc-8tgtt5.salesforceliveagent.com/content",
|
||||
deploymentId: "572Wr0000017xKX",
|
||||
buttonId: "573Wr000000CpgH",
|
||||
baseLiveAgentURL: "https://d.la12s-core1.sfdc-8tgtt5.salesforceliveagent.com/chat",
|
||||
eswLiveAgentDevName:
|
||||
"EmbeddedServiceLiveAgent_Parent04IWr000003yKq9MAE_1961681659a",
|
||||
isOfflineSupportEnabled: false,
|
||||
scrt2URL: "https://safelite2--dev.sandbox.my.salesforce-scrt.com",
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
if (!window.embedded_svc) {
|
||||
var s = document.createElement("script");
|
||||
s.setAttribute(
|
||||
"src",
|
||||
"https://safelite2--dev.sandbox.my.salesforce.com/embeddedservice/5.0/esw.min.js"
|
||||
);
|
||||
s.onload = function () {
|
||||
initESW(null);
|
||||
};
|
||||
document.body.appendChild(s);
|
||||
} else {
|
||||
initESW("https://service.force.com");
|
||||
} catch (err) {
|
||||
console.error("Error loading Embedded Messaging: ", err);
|
||||
}
|
||||
// END OF SALESFORCE GENERATED CODE
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
/* eslint-disable */
|
||||
// ***************************************************************
|
||||
// This file wraps the Salesforce generated code.
|
||||
// See the comment below for where you need to paste in the Salesforce generated code.
|
||||
// You may also need to modify the script tag src located in salesforce-webchat.vue to point to the correct Salesforce instance.
|
||||
// ***************************************************************
|
||||
|
||||
function setupCustomSettings(
|
||||
modifyEmbeddedSvcCallback,
|
||||
|
|
@ -7,91 +12,39 @@ function setupCustomSettings(
|
|||
onWebchatCloseCallback
|
||||
) {
|
||||
modifyEmbeddedSvcCallback();
|
||||
embedded_svc.addEventHandler("onSettingsCallCompleted", finishedLoadingCallback);
|
||||
embedded_svc.addEventHandler("afterMaximize", onWebchatOpenCallback);
|
||||
embedded_svc.addEventHandler("afterDestroy", onWebchatCloseCallback);
|
||||
window.addEventListener("onEmbeddedMessagingButtonCreated", finishedLoadingCallback);
|
||||
window.addEventListener("onEmbeddedMessagingWindowMaximized", onWebchatOpenCallback);
|
||||
window.addEventListener("onEmbeddedMessagingWindowClosed", onWebchatCloseCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
The code below is generated from salesforce but modified in the following ways:
|
||||
* Only the javascript inside the second <script> tag in the provided salesforce code is copy pasted
|
||||
* CSS is not copied in
|
||||
* First <script> tag loading in the file from the CDN is not copied in
|
||||
* Copy the entire contents of the second <script> tag inside the initializeSalesforceWebchatForDev
|
||||
method and add a call to setupCustomSettings before the 'embedded_svc.init()' method is called
|
||||
|
||||
Actual call to paste in:
|
||||
setupCustomSettings(
|
||||
modifyEmbeddedSvcCallback,
|
||||
finishedLoadingCallback,
|
||||
onWebchatOpenCallback,
|
||||
onWebchatCloseCallback
|
||||
);
|
||||
*/
|
||||
|
||||
export function initializeSalesforceWebchatForProd(
|
||||
modifyEmbeddedSvcCallback,
|
||||
finishedLoadingCallback,
|
||||
onWebchatOpenCallback,
|
||||
onWebchatCloseCallback
|
||||
) {
|
||||
var initESW = function (gslbBaseURL) {
|
||||
let embedded_svc = window.embedded_svc;
|
||||
embedded_svc.settings.displayHelpButton = true; //Or false
|
||||
embedded_svc.settings.language = ""; //For example, enter 'en' or 'en-US'
|
||||
setupCustomSettings(
|
||||
modifyEmbeddedSvcCallback,
|
||||
finishedLoadingCallback,
|
||||
onWebchatOpenCallback,
|
||||
onWebchatCloseCallback
|
||||
);
|
||||
|
||||
//embedded_svc.settings.defaultMinimizedText = '...'; //(Defaults to Chat with an Expert)
|
||||
//embedded_svc.settings.disabledMinimizedText = '...'; //(Defaults to Agent Offline)
|
||||
// START OF SALESFORCE GENERATED CODE
|
||||
// Copy in the contents of the first <script> tag inside the initEmbeddedMessaging() method
|
||||
try {
|
||||
embeddedservice_bootstrap.settings.language = "en_US"; // For example, enter 'en' or 'en-US'
|
||||
|
||||
//embedded_svc.settings.loadingText = ''; //(Defaults to Loading)
|
||||
//embedded_svc.settings.storageDomain = 'yourdomain.com'; //(Sets the domain for your deployment so that visitors can navigate subdomains during a chat session)
|
||||
|
||||
// Settings for Chat
|
||||
//embedded_svc.settings.directToButtonRouting = function(prechatFormData) {
|
||||
// Dynamically changes the button ID based on what the visitor enters in the pre-chat form.
|
||||
// Returns a valid button ID.
|
||||
//};
|
||||
//embedded_svc.settings.prepopulatedPrechatFields = {}; //Sets the auto-population of pre-chat form fields
|
||||
//embedded_svc.settings.fallbackRouting = []; //An array of button IDs, user IDs, or userId_buttonId
|
||||
//embedded_svc.settings.offlineSupportMinimizedText = '...'; //(Defaults to Contact Us)
|
||||
|
||||
embedded_svc.settings.enabledFeatures = ["LiveAgent"];
|
||||
embedded_svc.settings.entryFeature = "LiveAgent";
|
||||
|
||||
setupCustomSettings(
|
||||
modifyEmbeddedSvcCallback,
|
||||
finishedLoadingCallback,
|
||||
onWebchatOpenCallback,
|
||||
onWebchatCloseCallback
|
||||
);
|
||||
|
||||
embedded_svc.init(
|
||||
"https://safelite2.my.salesforce.com",
|
||||
"https://safelite2.my.salesforce-sites.com/chat",
|
||||
gslbBaseURL,
|
||||
embeddedservice_bootstrap.init(
|
||||
"00D3t000004nvsT",
|
||||
"Sales_Central_Snap_in",
|
||||
"Sales_Central_TFN",
|
||||
"https://safelite2.my.site.com/ESWSalesCentralTFN1769108630294",
|
||||
{
|
||||
baseLiveAgentContentURL:
|
||||
"https://c.la13-core2.sfdc-lywfpd.salesforceliveagent.com/content",
|
||||
deploymentId: "5723t0000008wWB",
|
||||
buttonId: "5733t000000D90z",
|
||||
baseLiveAgentURL: "https://d.la13-core2.sfdc-lywfpd.salesforceliveagent.com/chat",
|
||||
eswLiveAgentDevName:
|
||||
"EmbeddedServiceLiveAgent_Parent04I3t000000Cq0SEAS_188de1871ea",
|
||||
isOfflineSupportEnabled: false,
|
||||
scrt2URL: "https://safelite2.my.salesforce-scrt.com",
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
if (!window.embedded_svc) {
|
||||
var s = document.createElement("script");
|
||||
s.setAttribute("src", "https://safelite2.my.salesforce.com/embeddedservice/5.0/esw.min.js");
|
||||
s.onload = function () {
|
||||
initESW(null);
|
||||
};
|
||||
document.body.appendChild(s);
|
||||
} else {
|
||||
initESW("https://service.force.com");
|
||||
} catch (err) {
|
||||
console.error("Error loading Embedded Messaging: ", err);
|
||||
}
|
||||
// END OF SALESFORCE GENERATED CODE
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
/* eslint-disable */
|
||||
// ***************************************************************
|
||||
// This file wraps the Salesforce generated code.
|
||||
// See the comment below for where you need to paste in the Salesforce generated code.
|
||||
// You may also need to modify the script tag src located in salesforce-webchat.vue to point to the correct Salesforce instance.
|
||||
// ***************************************************************
|
||||
|
||||
function setupCustomSettings(
|
||||
modifyEmbeddedSvcCallback,
|
||||
|
|
@ -7,93 +12,38 @@ function setupCustomSettings(
|
|||
onWebchatCloseCallback
|
||||
) {
|
||||
modifyEmbeddedSvcCallback();
|
||||
embedded_svc.addEventHandler("onSettingsCallCompleted", finishedLoadingCallback);
|
||||
embedded_svc.addEventHandler("afterMaximize", onWebchatOpenCallback);
|
||||
embedded_svc.addEventHandler("afterDestroy", onWebchatCloseCallback);
|
||||
window.addEventListener("onEmbeddedMessagingButtonCreated", finishedLoadingCallback);
|
||||
window.addEventListener("onEmbeddedMessagingWindowMaximized", onWebchatOpenCallback);
|
||||
window.addEventListener("onEmbeddedMessagingWindowClosed", onWebchatCloseCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
The code below is generated from salesforce but modified in the following ways:
|
||||
* Only the javascript inside the second <script> tag in the provided salesforce code is copy pasted
|
||||
* CSS is not copied in
|
||||
* First <script> tag loading in the file from the CDN is not copied in
|
||||
* Copy the entire contents of the second <script> tag inside the initializeSalesforceWebchatForQa
|
||||
method and add a call to setupCustomSettings before the 'embedded_svc.init()' method is called
|
||||
|
||||
Actual call to paste in:
|
||||
setupCustomSettings(
|
||||
modifyEmbeddedSvcCallback,
|
||||
finishedLoadingCallback,
|
||||
onWebchatOpenCallback,
|
||||
onWebchatCloseCallback
|
||||
);
|
||||
*/
|
||||
|
||||
export function initializeSalesforceWebchatForQa(
|
||||
modifyEmbeddedSvcCallback,
|
||||
finishedLoadingCallback,
|
||||
onWebchatOpenCallback,
|
||||
onWebchatCloseCallback
|
||||
) {
|
||||
var initESW = function (gslbBaseURL) {
|
||||
embedded_svc.settings.displayHelpButton = true; //Or false
|
||||
embedded_svc.settings.language = ""; //For example, enter 'en' or 'en-US'
|
||||
setupCustomSettings(
|
||||
modifyEmbeddedSvcCallback,
|
||||
finishedLoadingCallback,
|
||||
onWebchatOpenCallback,
|
||||
onWebchatCloseCallback
|
||||
);
|
||||
// START OF SALESFORCE GENERATED CODE
|
||||
// Copy in the contents of the first <script> tag inside the initEmbeddedMessaging() method
|
||||
try {
|
||||
embeddedservice_bootstrap.settings.language = "en_US"; // For example, enter 'en' or 'en-US'
|
||||
|
||||
//embedded_svc.settings.defaultMinimizedText = '...'; //(Defaults to Chat with an Expert)
|
||||
//embedded_svc.settings.disabledMinimizedText = '...'; //(Defaults to Agent Offline)
|
||||
|
||||
//embedded_svc.settings.loadingText = ''; //(Defaults to Loading)
|
||||
//embedded_svc.settings.storageDomain = 'yourdomain.com'; //(Sets the domain for your deployment so that visitors can navigate subdomains during a chat session)
|
||||
|
||||
// Settings for Chat
|
||||
//embedded_svc.settings.directToButtonRouting = function(prechatFormData) {
|
||||
// Dynamically changes the button ID based on what the visitor enters in the pre-chat form.
|
||||
// Returns a valid button ID.
|
||||
//};
|
||||
//embedded_svc.settings.prepopulatedPrechatFields = {}; //Sets the auto-population of pre-chat form fields
|
||||
//embedded_svc.settings.fallbackRouting = []; //An array of button IDs, user IDs, or userId_buttonId
|
||||
//embedded_svc.settings.offlineSupportMinimizedText = '...'; //(Defaults to Contact Us)
|
||||
|
||||
embedded_svc.settings.enabledFeatures = ["LiveAgent"];
|
||||
embedded_svc.settings.entryFeature = "LiveAgent";
|
||||
|
||||
setupCustomSettings(
|
||||
modifyEmbeddedSvcCallback,
|
||||
finishedLoadingCallback,
|
||||
onWebchatOpenCallback,
|
||||
onWebchatCloseCallback
|
||||
);
|
||||
|
||||
embedded_svc.init(
|
||||
"https://safelite2--safeliteua.sandbox.my.salesforce.com",
|
||||
"https://safelite2--safeliteua.sandbox.my.salesforce-sites.com/chat",
|
||||
gslbBaseURL,
|
||||
"00DDJ000000HRqO",
|
||||
"Sales_Central_Snap_in",
|
||||
embeddedservice_bootstrap.init(
|
||||
"00DTI000005x7c7",
|
||||
"Sales_Central_TFN",
|
||||
"https://safelite2--qa.sandbox.my.site.com/ESWSalesCentralTFN1768832680884",
|
||||
{
|
||||
baseLiveAgentContentURL:
|
||||
"https://c.la12s-core1.sfdc-8tgtt5.salesforceliveagent.com/content",
|
||||
deploymentId: "572DJ00000001j3",
|
||||
buttonId: "573DJ00000001ur",
|
||||
baseLiveAgentURL: "https://d.la12s-core1.sfdc-8tgtt5.salesforceliveagent.com/chat",
|
||||
eswLiveAgentDevName:
|
||||
"EmbeddedServiceLiveAgent_Parent04IDJ0000008OZk2AM_1883057c9a6",
|
||||
isOfflineSupportEnabled: false,
|
||||
scrt2URL: "https://safelite2--qa.sandbox.my.salesforce-scrt.com",
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
if (!window.embedded_svc) {
|
||||
var s = document.createElement("script");
|
||||
s.setAttribute(
|
||||
"src",
|
||||
"https://safelite2--safeliteua.sandbox.my.salesforce.com/embeddedservice/5.0/esw.min.js"
|
||||
);
|
||||
s.onload = function () {
|
||||
initESW(null);
|
||||
};
|
||||
document.body.appendChild(s);
|
||||
} else {
|
||||
initESW("https://service.force.com");
|
||||
} catch (err) {
|
||||
console.error("Error loading Embedded Messaging: ", err);
|
||||
}
|
||||
// END OF SALESFORCE GENERATED CODE
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
/* eslint-disable */
|
||||
// ***************************************************************
|
||||
// This file wraps the Salesforce generated code.
|
||||
// See the comment below for where you need to paste in the Salesforce generated code.
|
||||
// You may also need to modify the script tag src located in salesforce-webchat.vue to point to the correct Salesforce instance.
|
||||
// ***************************************************************
|
||||
|
||||
function setupCustomSettings(
|
||||
modifyEmbeddedSvcCallback,
|
||||
finishedLoadingCallback,
|
||||
onWebchatOpenCallback,
|
||||
onWebchatCloseCallback
|
||||
) {
|
||||
modifyEmbeddedSvcCallback();
|
||||
window.addEventListener("onEmbeddedMessagingButtonCreated", finishedLoadingCallback);
|
||||
window.addEventListener("onEmbeddedMessagingWindowMaximized", onWebchatOpenCallback);
|
||||
window.addEventListener("onEmbeddedMessagingWindowClosed", onWebchatCloseCallback);
|
||||
}
|
||||
|
||||
export function initializeSalesforceWebchatForSys(
|
||||
modifyEmbeddedSvcCallback,
|
||||
finishedLoadingCallback,
|
||||
onWebchatOpenCallback,
|
||||
onWebchatCloseCallback
|
||||
) {
|
||||
setupCustomSettings(
|
||||
modifyEmbeddedSvcCallback,
|
||||
finishedLoadingCallback,
|
||||
onWebchatOpenCallback,
|
||||
onWebchatCloseCallback
|
||||
);
|
||||
|
||||
// START OF SALESFORCE GENERATED CODE
|
||||
// Copy in the contents of the first <script> tag inside the initEmbeddedMessaging() method
|
||||
try {
|
||||
embeddedservice_bootstrap.settings.language = "en_US"; // For example, enter 'en' or 'en-US'
|
||||
|
||||
embeddedservice_bootstrap.init(
|
||||
"00DcY0000067qKz",
|
||||
"Sales_Central_TFN",
|
||||
"https://safelite2--sys.sandbox.my.site.com/ESWSalesCentralTFN1767797265631",
|
||||
{
|
||||
scrt2URL: "https://safelite2--sys.sandbox.my.salesforce-scrt.com",
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.error("Error loading Embedded Messaging: ", err);
|
||||
}
|
||||
// END OF SALESFORCE GENERATED CODE
|
||||
}
|
||||
|
|
@ -2,10 +2,20 @@ import { shallowMount } from "@vue/test-utils";
|
|||
import salesforceWebchat from "./salesforce-webchat.vue";
|
||||
import * as devHelper from "./salesforce-helper-dev";
|
||||
import * as qaHelper from "./salesforce-helper-qa";
|
||||
import * as sysHelper from "./salesforce-helper-sys";
|
||||
import * as prodHelper from "./salesforce-helper-prod";
|
||||
import { applicationConfig } from "../../constants/application-config";
|
||||
|
||||
describe("salesforceWebchat.vue", () => {
|
||||
beforeEach(() => {
|
||||
window.embeddedservice_bootstrap = {
|
||||
settings: {},
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
delete window.embeddedservice_bootstrap;
|
||||
});
|
||||
it("renders correctly", () => {
|
||||
const wrapper = shallowMount(salesforceWebchat);
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
|
|
@ -14,7 +24,7 @@ describe("salesforceWebchat.vue", () => {
|
|||
it("has the correct default data", () => {
|
||||
const wrapper = shallowMount(salesforceWebchat);
|
||||
expect(wrapper.vm.$data).toEqual({
|
||||
isAgentAvailable: false,
|
||||
isWebchatLoaded: false,
|
||||
isWebchatOpen: false,
|
||||
});
|
||||
});
|
||||
|
|
@ -37,6 +47,12 @@ describe("salesforceWebchat.vue", () => {
|
|||
wrapper.vm.initializeSalesforceWebchat();
|
||||
expect(mockQa).toHaveBeenCalled();
|
||||
|
||||
applicationConfig.CURRENT_ENVIRONMENT = "SysTest";
|
||||
wrapper = shallowMount(salesforceWebchat);
|
||||
const mockSys = jest.spyOn(sysHelper, "initializeSalesforceWebchatForSys");
|
||||
wrapper.vm.initializeSalesforceWebchat();
|
||||
expect(mockSys).toHaveBeenCalled();
|
||||
|
||||
applicationConfig.CURRENT_ENVIRONMENT = "Dev";
|
||||
wrapper = shallowMount(salesforceWebchat);
|
||||
const mockDev = jest.spyOn(devHelper, "initializeSalesforceWebchatForDev");
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
// Supporting files
|
||||
import { initializeSalesforceWebchatForDev } from "./salesforce-helper-dev";
|
||||
import { initializeSalesforceWebchatForQa } from "./salesforce-helper-qa";
|
||||
import { initializeSalesforceWebchatForSys } from "./salesforce-helper-sys";
|
||||
import { initializeSalesforceWebchatForProd } from "./salesforce-helper-prod";
|
||||
import { applicationConfig } from "@/constants/application-config";
|
||||
import { webchatHelper } from "@/helpers/webchat-helper";
|
||||
|
|
@ -16,7 +17,7 @@ export default {
|
|||
mixins: [analyticsMixin],
|
||||
data() {
|
||||
return {
|
||||
isAgentAvailable: false,
|
||||
isWebchatLoaded: false,
|
||||
isWebchatOpen: false,
|
||||
};
|
||||
},
|
||||
|
|
@ -33,32 +34,13 @@ export default {
|
|||
mounted() {
|
||||
// Load in script from salesforce CDN
|
||||
const script = document.createElement("script");
|
||||
script.src = "https://service.force.com/embeddedservice/5.0/esw.min.js";
|
||||
script.src = this.webChatScriptSrc;
|
||||
script.onload = this.initializeSalesforceWebchat;
|
||||
document.body.appendChild(script);
|
||||
|
||||
// Notify funnel-header about Salesforce chat open/close state
|
||||
this._salesforceChatOpenObserver = new MutationObserver(() => {
|
||||
const isOpen = !!document.querySelector(".embeddedServiceSidebar");
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("salesforce-chat-visibility", { detail: { open: isOpen } })
|
||||
);
|
||||
});
|
||||
this._salesforceChatOpenObserver.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
},
|
||||
beforeUnmount() {
|
||||
// Clean up event listeners and observers
|
||||
window.removeEventListener(
|
||||
"salesforce-chat-visibility",
|
||||
this._handleSalesforceChatVisibility
|
||||
);
|
||||
window.removeEventListener("sierra-chat-transfer", this._handleSierraToSalesforceTransfer);
|
||||
if (this._salesforceChatOpenObserver) {
|
||||
this._salesforceChatOpenObserver.disconnect();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initializeSalesforceWebchat() {
|
||||
|
|
@ -72,7 +54,6 @@ export default {
|
|||
);
|
||||
break;
|
||||
case "QA":
|
||||
case "SysTest":
|
||||
initializeSalesforceWebchatForQa(
|
||||
this.modifyEmbeddedSvcCallback,
|
||||
this.finishedLoadingCallback,
|
||||
|
|
@ -80,8 +61,17 @@ export default {
|
|||
this.onWebchatCloseCallback
|
||||
);
|
||||
break;
|
||||
case "SysTest":
|
||||
initializeSalesforceWebchatForSys(
|
||||
this.modifyEmbeddedSvcCallback,
|
||||
this.finishedLoadingCallback,
|
||||
this.onWebchatOpenCallback,
|
||||
this.onWebchatCloseCallback
|
||||
);
|
||||
break;
|
||||
case "Dev":
|
||||
case "Localhost":
|
||||
default:
|
||||
initializeSalesforceWebchatForDev(
|
||||
this.modifyEmbeddedSvcCallback,
|
||||
this.finishedLoadingCallback,
|
||||
|
|
@ -92,16 +82,13 @@ export default {
|
|||
}
|
||||
},
|
||||
modifyEmbeddedSvcCallback() {
|
||||
window.embedded_svc.settings.avatarImgURL = require("@/assets/img/salesforce-webchat/avatar_webchat.png");
|
||||
window.embedded_svc.settings.prechatBackgroundImgURL = require("@/assets/img/salesforce-webchat/safelite_logo_webchat.png");
|
||||
window.embedded_svc.settings.smallCompanyLogoImgURL = require("@/assets/img/salesforce-webchat/minimized_chat_bubble_webchat.png");
|
||||
window.embedded_svc.settings.displayHelpButton = false;
|
||||
window.embedded_svc.addEventHandler("onChatEstablished", () => {
|
||||
window.embeddedservice_bootstrap.settings.hideChatButtonOnLoad = true;
|
||||
window.addEventListener("onEmbeddedMessagingConversationStarted", () => {
|
||||
this.pushEventForChatsToGA("support_chat", "chat_opened", "Live Agent", true);
|
||||
});
|
||||
},
|
||||
finishedLoadingCallback(data) {
|
||||
this.isAgentAvailable = data.isAgentAvailable;
|
||||
this.isWebchatLoaded = true;
|
||||
this.updateShouldShowWebchatButton();
|
||||
},
|
||||
onWebchatOpenCallback() {
|
||||
|
|
@ -117,16 +104,35 @@ export default {
|
|||
updateShouldShowWebchatButton() {
|
||||
this.webchatGlobalNonpersistedState.showWebchatButton =
|
||||
!this.hideSalesforceWebchatLaunchButton &&
|
||||
this.isAgentAvailable &&
|
||||
this.isWebchatLoaded &&
|
||||
!this.isWebchatOpen;
|
||||
},
|
||||
},
|
||||
computed: {},
|
||||
computed: {
|
||||
webChatScriptSrc() {
|
||||
// These URLs are from the src tag on the script tag in the Salesforce generated code.
|
||||
switch (applicationConfig.CURRENT_ENVIRONMENT) {
|
||||
case "Prod":
|
||||
return "https://safelite2.my.site.com/ESWSalesCentralTFN1769108630294/assets/js/bootstrap.min.js";
|
||||
case "QA":
|
||||
return "https://safelite2--qa.sandbox.my.site.com/ESWSalesCentralTFN1768832680884/assets/js/bootstrap.min.js";
|
||||
case "SysTest":
|
||||
return "https://safelite2--sys.sandbox.my.site.com/ESWSalesCentralTFN1767797265631/assets/js/bootstrap.min.js";
|
||||
case "Dev":
|
||||
case "Localhost":
|
||||
default:
|
||||
return "https://safelite2--dev.sandbox.my.site.com/ESWSalesCentralTFN1755660094477/assets/js/bootstrap.min.js";
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.embedded-messaging > .embeddedMessagingFrame[class~="isMaximized"] {
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
.dockableContainer {
|
||||
&.showDockableContainer {
|
||||
color: $gray-600;
|
||||
|
|
|
|||
|
|
@ -59,14 +59,6 @@ describe("sierraWebchat.vue", () => {
|
|||
expect(window.sierraChat.closeChatModal).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("dispatches sierra-chat-closed on handleSierraOnClose", () => {
|
||||
const eventSpy = jest.spyOn(window, "dispatchEvent");
|
||||
wrapper.vm.createSierraConfig().onClose();
|
||||
expect(eventSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ type: "sierra-chat-closed" })
|
||||
);
|
||||
});
|
||||
|
||||
it("dispatches sierra-chat-transfer on handleSierraOnTransfer", () => {
|
||||
const eventSpy = jest.spyOn(window, "dispatchEvent");
|
||||
const transfer = {
|
||||
|
|
@ -77,25 +69,4 @@ describe("sierraWebchat.vue", () => {
|
|||
expect.objectContaining({ type: "sierra-chat-transfer" })
|
||||
);
|
||||
});
|
||||
|
||||
it("handles sierra-chat-transfer event and starts Salesforce chat with prepopulated fields", () => {
|
||||
const transfer = {
|
||||
data: {
|
||||
first_name: "A",
|
||||
last_name: "B",
|
||||
email: "a@b.com",
|
||||
chat_summary: "summary",
|
||||
},
|
||||
};
|
||||
const event = new CustomEvent("sierra-chat-transfer", { detail: transfer });
|
||||
window.dispatchEvent(event);
|
||||
expect(window.sierraChat.closeChatModal).toHaveBeenCalled();
|
||||
expect(window.embedded_svc.settings.prepopulatedPrechatFields).toEqual({
|
||||
FirstName: "A",
|
||||
LastName: "B",
|
||||
Email: "a@b.com",
|
||||
Subject: "summary",
|
||||
});
|
||||
expect(window.embedded_svc.liveAgentAPI.startChat).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,10 +7,20 @@ import analyticsMixin from "@/mixins/analytics-mixin";
|
|||
import store from "@/store";
|
||||
import { routeData } from "@/router/constants/routes";
|
||||
import { deepClone } from "@/helpers/object-helper";
|
||||
import { webchatHelper } from "@/helpers/webchat-helper";
|
||||
|
||||
export default {
|
||||
name: "sierraWebchat",
|
||||
mixins: [analyticsMixin],
|
||||
data() {
|
||||
return {
|
||||
transferInProgress: false,
|
||||
};
|
||||
},
|
||||
setup() {
|
||||
const { webchatGlobalNonpersistedState, launchWebchat } = webchatHelper();
|
||||
return { webchatGlobalNonpersistedState, launchWebchat };
|
||||
},
|
||||
methods: {
|
||||
openSierraChatModal() {
|
||||
const sierra = window.sierraChat || window.sierra || window.SierraChat;
|
||||
|
|
@ -35,11 +45,18 @@ export default {
|
|||
display: "corner",
|
||||
onLoad: () => this.openSierraChatModal(),
|
||||
onOpen: () => {},
|
||||
onClose: () => window.dispatchEvent(new CustomEvent("sierra-chat-closed")),
|
||||
onTransfer: (transfer) =>
|
||||
onClose: () => {
|
||||
if (!this.transferInProgress) {
|
||||
this.webchatGlobalNonpersistedState.showWebchatButton = true;
|
||||
this.transferInProgress = false;
|
||||
}
|
||||
},
|
||||
onTransfer: (transfer) => {
|
||||
this.transferInProgress = true;
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("sierra-chat-transfer", { detail: transfer })
|
||||
),
|
||||
);
|
||||
},
|
||||
};
|
||||
},
|
||||
getSierraContext() {
|
||||
|
|
@ -138,10 +155,7 @@ export default {
|
|||
}
|
||||
// Pass data to Salesforce chat if available
|
||||
const transfer = event.detail;
|
||||
if (
|
||||
window.embedded_svc &&
|
||||
typeof window.embedded_svc.bootstrapEmbeddedService === "function"
|
||||
) {
|
||||
if (window.embeddedservice_bootstrap && window.embeddedservice_bootstrap.prechatAPI) {
|
||||
if (
|
||||
transfer &&
|
||||
transfer.data &&
|
||||
|
|
@ -149,51 +163,22 @@ export default {
|
|||
transfer.data.last_name &&
|
||||
transfer.data.email
|
||||
) {
|
||||
window.embedded_svc.settings.prepopulatedPrechatFields = {
|
||||
window.embeddedservice_bootstrap.prechatAPI.setHiddenPrechatFields({
|
||||
ScarlettAISummary: transfer.data.chat_summary,
|
||||
FirstName: transfer.data.first_name,
|
||||
LastName: transfer.data.last_name,
|
||||
Email: transfer.data.email,
|
||||
Subject: transfer.data.chat_summary,
|
||||
};
|
||||
window.embedded_svc.settings.extraPrechatFormDetails = [
|
||||
{
|
||||
label: "Scarlett AI Summary",
|
||||
value: transfer.data.chat_summary,
|
||||
transcriptFields: ["Scarlett_AI_Summary__c"],
|
||||
displayToAgent: true,
|
||||
},
|
||||
];
|
||||
if (
|
||||
window.embedded_svc.liveAgentAPI &&
|
||||
typeof window.embedded_svc.liveAgentAPI.startChat === "function"
|
||||
) {
|
||||
window.embedded_svc.liveAgentAPI.startChat();
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
window.embedded_svc.bootstrapEmbeddedService();
|
||||
this.webchatGlobalNonpersistedState.shouldLaunchSierra = false;
|
||||
this.launchWebchat();
|
||||
}
|
||||
};
|
||||
window.addEventListener("sierra-chat-transfer", this._handleSierraTransfer);
|
||||
|
||||
// Notify funnel-header about Sierra chat open/close state
|
||||
this._sierraChatContainerObserver = new MutationObserver(() => {
|
||||
const isOpen = !!document.querySelector("[data-sierra-chat-container]");
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("sierra-chat-visibility", { detail: { open: isOpen } })
|
||||
);
|
||||
});
|
||||
this._sierraChatContainerObserver.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener("launch-sierra-webchat", this.triggerSierraChat);
|
||||
window.removeEventListener("sierra-chat-transfer", this._handleSierraTransfer);
|
||||
if (this._sierraChatContainerObserver) {
|
||||
this._sierraChatContainerObserver.disconnect();
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -145,9 +145,7 @@ export default {
|
|||
return "$" + decimalPrice.toFixed(2);
|
||||
},
|
||||
hasPackageDiscount() {
|
||||
return (
|
||||
this.additionalButtonData.servicePackageDiscount && this.additionalButtonData.Text
|
||||
);
|
||||
return this.additionalButtonData.hasDiscount && this.additionalButtonData.Text;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
class="package-label pricing-by-day-pkg-lbl"
|
||||
:class="[
|
||||
this.buttonLabelSubCopy ? 'has-subheader' : '',
|
||||
!this.additionalButtonData.servicePackageDiscount ? 'adjust-top' : '',
|
||||
!this.additionalButtonData.hasDiscount ? 'adjust-top' : '',
|
||||
]"
|
||||
for="testradio">
|
||||
<div class="package-specs">
|
||||
<div v-if="this.additionalButtonData.servicePackageDiscount" class="row">
|
||||
<div v-if="this.additionalButtonData.hasDiscount" class="row">
|
||||
<div class="col md-6">
|
||||
<p class="m-0">
|
||||
<span v-html="this.buttonLabel"></span>
|
||||
|
|
@ -84,10 +84,7 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
v-if="
|
||||
this.additionalButtonData.servicePackageDiscount &&
|
||||
this.additionalButtonData.Text
|
||||
"
|
||||
v-if="this.additionalButtonData.hasDiscount && this.additionalButtonData.Text"
|
||||
class="special-save-box">
|
||||
<span v-html="this.additionalButtonData.Text"></span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -190,6 +190,7 @@ import { cartItemCategories } from "@/constants/cart-item-categories";
|
|||
import { cartItemTypes } from "@/constants/cart-item-types";
|
||||
import { coverageStatus, cartItemTypesCoveredByInsurance } from "@/constants/insurance";
|
||||
import { experimentSettings } from "@/constants/experiments";
|
||||
import { quotePageDiscountTable } from "@/constants/quote-page-discounts";
|
||||
|
||||
export default {
|
||||
name: "cart",
|
||||
|
|
@ -256,11 +257,11 @@ export default {
|
|||
|
||||
return subTotal;
|
||||
},
|
||||
getServicePackageDiscount() {
|
||||
const servicePackageDiscountLineItems = this.servicePackageDiscountCartItem;
|
||||
getQuotePageDiscount() {
|
||||
const quotePageDiscountLineItems = this.quotePageDiscountCartItem;
|
||||
let subTotal = 0;
|
||||
|
||||
subTotal += servicePackageDiscountLineItems?.subTotal ?? 0;
|
||||
subTotal += quotePageDiscountLineItems?.subTotal ?? 0;
|
||||
return subTotal;
|
||||
},
|
||||
getVapsCartItemsForSelectedPackage(packageName) {
|
||||
|
|
@ -303,7 +304,8 @@ export default {
|
|||
|
||||
getLineItemAmount(amount, useVerifyingText, category) {
|
||||
if (useVerifyingText) return this.verifyingCoverageText;
|
||||
return category == "promos" || category == "servicePackageDiscount"
|
||||
return category == cartItemCategories.PROMOS ||
|
||||
category == cartItemCategories.QUOTE_PAGE_DISCOUNT
|
||||
? "(" + this.currencyFormatter.format(amount * -1) + ")"
|
||||
: this.currencyFormatter.format(amount);
|
||||
},
|
||||
|
|
@ -316,16 +318,21 @@ export default {
|
|||
if (category == cartItemCategories.VAPS || category == cartItemCategories.PROMOS) {
|
||||
this.saveVaps(this.lineItems);
|
||||
// Check if service package discount should be removed after vaps change
|
||||
if (
|
||||
this.servicePackageDiscountCartItem &&
|
||||
this.discountPackageNames != this.packageLevel
|
||||
) {
|
||||
this.lineItems.supportingItems = this.lineItems.supportingItems.filter(
|
||||
(lineItemsToKeep) =>
|
||||
lineItemsToKeep.cartItemType !=
|
||||
this.servicePackageDiscountCartItem.cartItemType
|
||||
if (this.quotePageDiscountCartItem) {
|
||||
const existingLineItem = this.quotePageDiscountCartItem.lineItems.at(0);
|
||||
const discountInfo = quotePageDiscountTable.find(
|
||||
(info) => info.partNumber === existingLineItem?.partNumber
|
||||
);
|
||||
shouldSaveSupportingItems = true;
|
||||
const packageLevelForDiscount = discountInfo?.packageLevel;
|
||||
|
||||
if (this.packageLevel !== packageLevelForDiscount) {
|
||||
this.lineItems.supportingItems = this.lineItems.supportingItems.filter(
|
||||
(lineItemsToKeep) =>
|
||||
lineItemsToKeep.cartItemType !=
|
||||
this.quotePageDiscountCartItem.cartItemType
|
||||
);
|
||||
shouldSaveSupportingItems = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shouldSaveSupportingItems) {
|
||||
|
|
@ -483,8 +490,8 @@ export default {
|
|||
cartItems.push(this.mobileFeeCartItem);
|
||||
}
|
||||
|
||||
if (this.servicePackageDiscountCartItem) {
|
||||
cartItems.push(this.servicePackageDiscountCartItem);
|
||||
if (this.quotePageDiscountCartItem) {
|
||||
cartItems.push(this.quotePageDiscountCartItem);
|
||||
}
|
||||
|
||||
if (this.premiumAppointmentDiscountCartItem) {
|
||||
|
|
@ -500,12 +507,6 @@ export default {
|
|||
return cartItems;
|
||||
},
|
||||
},
|
||||
discountPackageNames() {
|
||||
const discountServicePackage = experimentMixin.methods.getSettingValue(
|
||||
experimentSettings.PROMO_ON_PACKAGE
|
||||
);
|
||||
return getDiscountedPackageName(discountServicePackage);
|
||||
},
|
||||
servicePackageTitleWidget() {
|
||||
const servicePackageNames = this.getCmsContent(
|
||||
this.servicePackageOptionsCmsName,
|
||||
|
|
@ -550,8 +551,8 @@ export default {
|
|||
}
|
||||
|
||||
packagePrice += this.getVapsPrice(this.packageLevel);
|
||||
if (this.servicePackageDiscountCartItem) {
|
||||
packagePrice -= this.getServicePackageDiscount();
|
||||
if (this.quotePageDiscountCartItem) {
|
||||
packagePrice -= this.getQuotePageDiscount();
|
||||
}
|
||||
|
||||
return packagePrice;
|
||||
|
|
@ -928,25 +929,25 @@ export default {
|
|||
}
|
||||
return cartItem;
|
||||
},
|
||||
servicePackageDiscountCartItemName() {
|
||||
quotePageDiscountCartItemName() {
|
||||
return this.getCmsContent("ServicePackageDiscountTextWidget", "Text");
|
||||
},
|
||||
servicePackageDiscountCartItem() {
|
||||
quotePageDiscountCartItem() {
|
||||
let cartItem = null;
|
||||
|
||||
const servicePackageDiscountLineItem = this.supportingItems.find(
|
||||
(lineItem) => lineItem.partType == partTypeStrings.SERVICE_PACKAGE_DISCOUNT
|
||||
const quotePageDiscountLineItem = this.supportingItems.find(
|
||||
(lineItem) => lineItem.partType == partTypeStrings.QUOTE_PAGE_DISCOUNT
|
||||
);
|
||||
|
||||
if (servicePackageDiscountLineItem) {
|
||||
if (quotePageDiscountLineItem) {
|
||||
cartItem = {
|
||||
name:
|
||||
this.servicePackageDiscountCartItemName +
|
||||
this.quotePageDiscountCartItemName +
|
||||
Math.abs(
|
||||
baseMixin.methods.getTotalLineItemPrice(servicePackageDiscountLineItem)
|
||||
baseMixin.methods.getTotalLineItemPrice(quotePageDiscountLineItem)
|
||||
),
|
||||
category: cartItemCategories.SERVICE_PACKAGE_DISCOUNT,
|
||||
cartItemType: cartItemTypes.SERVICE_PACKAGE_DISCOUNT,
|
||||
category: cartItemCategories.QUOTE_PAGE_DISCOUNT,
|
||||
cartItemType: cartItemTypes.QUOTE_PAGE_DISCOUNT,
|
||||
isDisplayed: true,
|
||||
isRemovable: false,
|
||||
subTotal: 0,
|
||||
|
|
@ -955,15 +956,15 @@ export default {
|
|||
isCoveredByInsurance: false,
|
||||
};
|
||||
|
||||
servicePackageDiscountLineItem.cartItemType = cartItem.cartItemType;
|
||||
cartItem.lineItems.push(servicePackageDiscountLineItem);
|
||||
quotePageDiscountLineItem.cartItemType = cartItem.cartItemType;
|
||||
cartItem.lineItems.push(quotePageDiscountLineItem);
|
||||
|
||||
cartItem.subTotal +=
|
||||
(servicePackageDiscountLineItem.kitPrice ?? 0) +
|
||||
(servicePackageDiscountLineItem.laborAmount ?? 0) +
|
||||
(servicePackageDiscountLineItem.sellingPrice ?? 0);
|
||||
(quotePageDiscountLineItem.kitPrice ?? 0) +
|
||||
(quotePageDiscountLineItem.laborAmount ?? 0) +
|
||||
(quotePageDiscountLineItem.sellingPrice ?? 0);
|
||||
|
||||
cartItem.salesTax += servicePackageDiscountLineItem.salesTax ?? 0;
|
||||
cartItem.salesTax += quotePageDiscountLineItem.salesTax ?? 0;
|
||||
}
|
||||
|
||||
return cartItem;
|
||||
|
|
@ -972,7 +973,7 @@ export default {
|
|||
let cartItem = null;
|
||||
|
||||
const otherSupportingItems = this.supportingItems.filter((item) => {
|
||||
return item.partType != partTypeStrings.SERVICE_PACKAGE_DISCOUNT;
|
||||
return item.partType != partTypeStrings.QUOTE_PAGE_DISCOUNT;
|
||||
});
|
||||
// Don't include fees they are part of the package total
|
||||
let otherSupportingItemsLineItems =
|
||||
|
|
|
|||
|
|
@ -75,17 +75,12 @@ export default {
|
|||
|
||||
:deep(a) {
|
||||
text-decoration: none;
|
||||
&.new-window-link {
|
||||
font-family: Urbanist, Arial, Helvetica, sans-serif;
|
||||
margin: 0 0.75rem;
|
||||
color: $gray-600;
|
||||
font-weight: 400;
|
||||
}
|
||||
&.new-window-link,
|
||||
&.navigation-link {
|
||||
font-family: Urbanist, Arial, Helvetica, sans-serif;
|
||||
font-family: UrbanistRegular, Arial, Helvetica, sans-serif;
|
||||
margin: 0 0.75rem;
|
||||
color: $gray-600;
|
||||
font-weight: 400;
|
||||
font-weight: initial;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ export default {
|
|||
return {
|
||||
globalAlertMessages: [],
|
||||
sierraChatOpen: false,
|
||||
salesforceChatOpen: false,
|
||||
isSalesforceTransferInProgress: false,
|
||||
isScrolled: false,
|
||||
};
|
||||
|
|
@ -80,20 +79,7 @@ export default {
|
|||
return this.getCmsContent(this.cmsWidgetName, "LogoImage");
|
||||
},
|
||||
shouldShowWebchatButton() {
|
||||
// Hide the chat icon if Sierra chat is open
|
||||
if (this.sierraChatOpen) {
|
||||
return false;
|
||||
}
|
||||
// Hide the chat icon if Salesforce chat is open or transfer is in progress
|
||||
if (this.salesforceChatOpen || this.isSalesforceTransferInProgress) {
|
||||
return false;
|
||||
}
|
||||
// Show if either Sierra is enabled or Salesforce button is available and not hidden by page
|
||||
return (
|
||||
(this.webchatGlobalNonpersistedState.shouldLaunchSierra ||
|
||||
this.webchatGlobalNonpersistedState.showWebchatButton) &&
|
||||
!this.shouldHideWebchatButtonOnPage
|
||||
);
|
||||
return this.webchatGlobalNonpersistedState.showWebchatButton;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -121,10 +107,6 @@ export default {
|
|||
webchatClicked(event) {
|
||||
event.preventDefault();
|
||||
this.launchWebchat();
|
||||
// Only set sierraChatOpen if Sierra experiment is active
|
||||
if (this.webchatGlobalNonpersistedState.shouldLaunchSierra) {
|
||||
this.sierraChatOpen = true;
|
||||
}
|
||||
},
|
||||
syncSierraExperimentFlag() {
|
||||
const experiments = store.getters.applicationUser.experiments;
|
||||
|
|
@ -164,37 +146,6 @@ export default {
|
|||
unknownAlertEvent.displayAlert = true;
|
||||
this.globalAlertMessages.push(unknownAlertEvent);
|
||||
}
|
||||
// Listen for Sierra chat open/close events from sierra-webchat
|
||||
this._handleSierraChatVisibility = (event) => {
|
||||
this.sierraChatOpen = !!(event.detail && event.detail.open);
|
||||
if (this.sierraChatOpen) {
|
||||
this.isSalesforceTransferInProgress = false;
|
||||
}
|
||||
};
|
||||
window.addEventListener("sierra-chat-visibility", this._handleSierraChatVisibility);
|
||||
|
||||
// Listen for Salesforce chat open/close events from salesforce-webchat
|
||||
this._handleSalesforceChatVisibility = (event) => {
|
||||
this.salesforceChatOpen = !!(event.detail && event.detail.open === true);
|
||||
};
|
||||
window.addEventListener("salesforce-chat-visibility", this._handleSalesforceChatVisibility);
|
||||
|
||||
// Listen for transfer event to set transfer-in-progress flag
|
||||
this._handleSierraToSalesforceTransfer = () => {
|
||||
this.isSalesforceTransferInProgress = true;
|
||||
};
|
||||
window.addEventListener("sierra-chat-transfer", this._handleSierraToSalesforceTransfer);
|
||||
|
||||
// Reset transfer flag when Salesforce chat opens
|
||||
this._handleSalesforceChatOpened = (event) => {
|
||||
if (event.detail && event.detail.open === true) {
|
||||
this.isSalesforceTransferInProgress = false;
|
||||
this.salesforceChatOpen = true;
|
||||
} else {
|
||||
this.salesforceChatOpen = false;
|
||||
}
|
||||
};
|
||||
window.addEventListener("salesforce-chat-visibility", this._handleSalesforceChatOpened);
|
||||
|
||||
// Add scroll listener for header shadow effect
|
||||
this._handleScroll = () => {
|
||||
|
|
@ -203,14 +154,7 @@ export default {
|
|||
window.addEventListener("scroll", this._handleScroll);
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener("sierra-chat-closed", this._handleSierraChatClosed);
|
||||
window.removeEventListener("sierra-chat-visibility", this._handleSierraChatVisibility);
|
||||
window.removeEventListener(
|
||||
"salesforce-chat-visibility",
|
||||
this._handleSalesforceChatVisibility
|
||||
);
|
||||
window.removeEventListener("sierra-chat-transfer", this._handleSierraToSalesforceTransfer);
|
||||
window.removeEventListener("salesforce-chat-visibility", this._handleSalesforceChatOpened);
|
||||
window.removeEventListener("scroll", this._handleScroll);
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -57,10 +57,6 @@ export default {
|
|||
modalWidgetName: String,
|
||||
pageName: String,
|
||||
},
|
||||
unmounted() {
|
||||
// remove any modal effects before leaving page (e.g. user hits browser back button)
|
||||
this.modal?.closeModal();
|
||||
},
|
||||
computed: {
|
||||
buttonText() {
|
||||
return this.getCmsContent(this.modalWidgetName, "SubheaderText");
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ defineRule("email-address-required", required(errorMessages.EMAIL_ADDRESS_REQUIR
|
|||
defineRule(
|
||||
"email-address-format",
|
||||
regex(
|
||||
/^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9-]+)\.([a-zA-Z]{2,})$/,
|
||||
/^([a-zA-Z0-9_.+]+)@([a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*\.)+([a-zA-Z]{2,})$/,
|
||||
errorMessages.EMAIL_ADDRESS_FORMAT
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -54,10 +54,6 @@ export default {
|
|||
mounted() {
|
||||
if (this.showSaveProgressPopup) this.modal.openModal();
|
||||
},
|
||||
unmounted() {
|
||||
// remove any modal effects before leaving page (e.g. user hits browser back button)
|
||||
this.modal.closeModal();
|
||||
},
|
||||
emits: ["close-save-progress-popup", "save-progress"], // <--- should remove oodles of warnings in dev tools
|
||||
props: {
|
||||
modelValue: Object,
|
||||
|
|
|
|||
|
|
@ -101,7 +101,13 @@ export default {
|
|||
return resolve(response);
|
||||
},
|
||||
(error) => {
|
||||
if (endpoint.toLowerCase().includes(endpoints.LogDigitalConsumer.url)) {
|
||||
if (
|
||||
endpoint.toLowerCase().includes(endpoints.LogDigitalConsumer.url) ||
|
||||
endpoint.toLowerCase().includes(endpoints.LogFmgSessionData.url) ||
|
||||
endpoint.toLowerCase().includes(endpoints.LogPageView.url) ||
|
||||
endpoint.toLowerCase().includes(endpoints.LogCustomEvent.url) ||
|
||||
endpoint.toLowerCase().includes(endpoints.LogPartQuestions.url)
|
||||
) {
|
||||
return resolve({ data: null, error: "Ignore errors when logging" });
|
||||
} else {
|
||||
if (logApiCall) {
|
||||
|
|
|
|||
168
src/helpers/adyen-helper.js
Normal file
168
src/helpers/adyen-helper.js
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
import { AdyenCheckout } from "@adyen/adyen-web/auto";
|
||||
import store from "@/store";
|
||||
import globalMethods from "@/global-methods";
|
||||
import { endpoints } from "@/constants/endpoints";
|
||||
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||
import { paymentMethods } from "@/constants/payment-method-constants";
|
||||
import { applicationConfig } from "../constants/application-config";
|
||||
|
||||
export async function getNewSession(sessionConfig) {}
|
||||
|
||||
export async function getSessionInfo(sessionId, encryptedSessionResult, pageNameToLog = "") {
|
||||
const order = store.getters.order;
|
||||
const location = getLocationInfo(order);
|
||||
const workOrder = getWorkOrderLastSixDigits(order);
|
||||
const system = getSourceSystem();
|
||||
|
||||
const request = {
|
||||
sessionId: sessionId,
|
||||
sessionResult: encryptedSessionResult,
|
||||
zipCodeCtu: location.zipCodeCtu,
|
||||
workOrderNumber: workOrder,
|
||||
sourceSystem: system,
|
||||
};
|
||||
|
||||
const response = await globalMethods.callHttpClient({
|
||||
method: endpoints.GetAdyenSessionResult.method,
|
||||
endpoint: endpoints.GetAdyenSessionResult.url,
|
||||
payload: request,
|
||||
logApiCall: true,
|
||||
pageNameToLog: pageNameToLog,
|
||||
});
|
||||
|
||||
return response?.data;
|
||||
}
|
||||
|
||||
export async function createAdyenCheckout({
|
||||
session: { id, sessionData },
|
||||
handlers: { onPaymentCompleted, onPaymentFailed, onError },
|
||||
options: { showPayButton = true, amount },
|
||||
}) {
|
||||
const config = {
|
||||
session: {
|
||||
id: id,
|
||||
sessionData: sessionData,
|
||||
},
|
||||
clientKey: applicationConfig.ADYEN_CLIENT_KEY,
|
||||
environment: applicationConfig.ADYEN_ENVIRONMENT,
|
||||
locale: "en_US",
|
||||
countryCode: "US",
|
||||
showPayButton: showPayButton,
|
||||
translations: {
|
||||
en_US: {
|
||||
"creditCard.securityCode.label": "CVV/CVC",
|
||||
},
|
||||
},
|
||||
onPaymentCompleted: onPaymentCompleted,
|
||||
onPaymentFailed: onPaymentFailed,
|
||||
onError: onError,
|
||||
};
|
||||
|
||||
if (amount !== undefined && amount !== null) {
|
||||
config.amount = {
|
||||
value: amount,
|
||||
currency: "USD",
|
||||
};
|
||||
}
|
||||
|
||||
console.log(`Creating checkout with configuration:`);
|
||||
console.log(config);
|
||||
|
||||
return await AdyenCheckout(config);
|
||||
}
|
||||
|
||||
export function mapAdyenToFmgPaymentMethod(adyenMethod) {
|
||||
const paymentMethodMap = {
|
||||
["scheme"]: paymentMethods.CREDIT_CARD,
|
||||
["afterpaytouch"]: paymentMethods.AFTERPAY,
|
||||
["paypal"]: paymentMethods.PAYPAL,
|
||||
["applepay"]: paymentMethods.APPLE_PAY,
|
||||
};
|
||||
|
||||
const match = paymentMethodMap[adyenMethod];
|
||||
|
||||
return match ?? paymentMethods.CREDIT_CARD;
|
||||
}
|
||||
|
||||
export function mapFmgToAdyenPaymentMethod(fmgMethod) {
|
||||
const paymentMethodMap = {
|
||||
[paymentMethods.CREDIT_CARD]: "scheme",
|
||||
[paymentMethods.AFTERPAY]: "afterpaytouch",
|
||||
[paymentMethods.PAYPAL]: "paypal",
|
||||
[paymentMethods.APPLE_PAY]: "applepay",
|
||||
[paymentMethods.PAY_NOW]: null,
|
||||
[paymentMethods.LATER]: null,
|
||||
};
|
||||
|
||||
const match = paymentMethodMap[fmgMethod];
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
export function generateCcToken(adyenSessionInfo) {
|
||||
const order = store.getters.order;
|
||||
const locationInfo = getLocationInfo(order);
|
||||
|
||||
const ccToken = {
|
||||
subscriptionId: adyenSessionInfo?.storedToken,
|
||||
expMonth: adyenSessionInfo?.cardExpiryMonth,
|
||||
expYear: adyenSessionInfo?.cardExpiryYear,
|
||||
cardType: adyenSessionInfo?.cardType,
|
||||
billToPostalCode: locationInfo.zipCode, // TODO - real data?
|
||||
billToFirstName: order.customer.firstName, // TODO - real data?
|
||||
billToLastName: order.customer.lastName, // TODO - real data?
|
||||
referenceNumber: adyenSessionInfo?.workOrderNumber,
|
||||
authCode: adyenSessionInfo?.authCode,
|
||||
transactionId: adyenSessionInfo?.transactionReference,
|
||||
transReferenceNumber: adyenSessionInfo?.transactionReference,
|
||||
lastFour: adyenSessionInfo?.last4DigitsOfCard,
|
||||
};
|
||||
|
||||
return ccToken;
|
||||
}
|
||||
|
||||
function getLocationInfo(order) {
|
||||
const serviceLocation = order.serviceLocation;
|
||||
const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
|
||||
if (isMobile) {
|
||||
return {
|
||||
address: serviceLocation.address,
|
||||
address2: serviceLocation.address2,
|
||||
city: serviceLocation.city,
|
||||
state: serviceLocation.state,
|
||||
zipCode: serviceLocation.zipCode,
|
||||
zipCodeCtu: serviceLocation.zipCodeCtu,
|
||||
};
|
||||
} else {
|
||||
const providerInfo = serviceLocation.provider.address;
|
||||
return {
|
||||
address: providerInfo.streetAddress,
|
||||
address2: "",
|
||||
city: providerInfo.city,
|
||||
state: providerInfo.state,
|
||||
zipCode: providerInfo.zipCode,
|
||||
zipCodeCtu: providerInfo.zipCodeCtu,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getWorkOrderLastSixDigits(order) {
|
||||
const workOrderNumber = order.workOrderNumber ?? "";
|
||||
const tokens = workOrderNumber.split("-");
|
||||
const lastSegment = tokens[tokens.length - 1] ?? "";
|
||||
const numDigits = lastSegment.length;
|
||||
const numZeroes = 6 - numDigits;
|
||||
|
||||
let result = "";
|
||||
for (let i = 0; i < numZeroes; i++) {
|
||||
result += "0";
|
||||
}
|
||||
|
||||
result += lastSegment;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getSourceSystem() {
|
||||
return "FMG-2.0";
|
||||
}
|
||||
|
|
@ -3,6 +3,14 @@ import { partTypeStrings } from "@/constants/part-type-strings";
|
|||
|
||||
const recalPartTypes = [partTypeStrings.RECALIBRATION, partTypeStrings.ADAS_RECALIBRATION];
|
||||
|
||||
export function anyPartWithRequiresRecalFlag(lineItems) {
|
||||
if (!lineItems) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return lineItems.glassParts?.some((li) => li.requiresRecalibration === true);
|
||||
}
|
||||
|
||||
export function isRecalPartOrHasChildRecalPart(lineItem) {
|
||||
if (lineItem.childParts && lineItem.childParts.length > 0) {
|
||||
return isRecalPart(lineItem) || containsRecalParts(lineItem.childParts);
|
||||
|
|
|
|||
|
|
@ -254,6 +254,19 @@ export function getDiscountedPackageName(discountPackage) {
|
|||
return package_names[discountPackage] || null;
|
||||
}
|
||||
|
||||
export function getPackageLabel(packageName) {
|
||||
switch (packageName) {
|
||||
case packageNames.TIER_ONE:
|
||||
return externalParamPackageLabels.GLASS_ONLY;
|
||||
case packageNames.TIER_TWO:
|
||||
return externalParamPackageLabels.STANDARD;
|
||||
case packageNames.TIER_THREE:
|
||||
return externalParamPackageLabels.PREMIUM;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function maxTier(tierA, tierB) {
|
||||
if (tierA === packageNames.TIER_THREE || tierB === packageNames.TIER_THREE) {
|
||||
return packageNames.TIER_THREE;
|
||||
|
|
|
|||
|
|
@ -8,10 +8,11 @@ const webchatGlobalNonpersistedState = reactive({
|
|||
|
||||
export const webchatHelper = () => {
|
||||
function launchWebchat() {
|
||||
webchatGlobalNonpersistedState.showWebchatButton = false;
|
||||
if (webchatGlobalNonpersistedState.shouldLaunchSierra) {
|
||||
window.dispatchEvent(new CustomEvent("launch-sierra-webchat")); // launches the sierra chat
|
||||
} else {
|
||||
window.embedded_svc.bootstrapEmbeddedService(); // launches the salesForce prechat form
|
||||
window.embeddedservice_bootstrap.utilAPI.launchChat(); // launches the salesForce prechat form
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ defineRule("email-address-required", required(errorMessages.EMAIL_ADDRESS_REQUIR
|
|||
defineRule(
|
||||
"email-address-format",
|
||||
regex(
|
||||
/^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9-]+)\.([a-zA-Z]{2,})$/,
|
||||
/^([a-zA-Z0-9_.+]+)@([a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*\.)+([a-zA-Z]{2,})$/,
|
||||
errorMessages.EMAIL_ADDRESS_FORMAT
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ defineRule("email-address-required", required(errorMessages.EMAIL_ADDRESS_REQUIR
|
|||
defineRule(
|
||||
"email-address-format",
|
||||
regex(
|
||||
/^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9-]+)\.([a-zA-Z]{2,})$/,
|
||||
/^([a-zA-Z0-9_.+]+)@([a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*\.)+([a-zA-Z]{2,})$/,
|
||||
errorMessages.EMAIL_ADDRESS_FORMAT
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -164,6 +164,8 @@ export default {
|
|||
// await any pending save-sessions. if you don't, we will save the new parent account into vuex and then a pending async save-session response overwrites it
|
||||
await flushSaveSessionQueue();
|
||||
|
||||
await this.removeQPDLineItemIfExists();
|
||||
|
||||
await this.dispatchStoreAction(
|
||||
this.storeActions.SAVE_PARENT_ACCOUNT_NUMBER,
|
||||
this.parentAccountNumber.toString(),
|
||||
|
|
@ -193,6 +195,17 @@ export default {
|
|||
handlePageShow() {
|
||||
window.location.reload();
|
||||
},
|
||||
async removeQPDLineItemIfExists() {
|
||||
const lineItems = deepClone(store.getters.order.lineItems);
|
||||
lineItems.supportingItems = baseMixin.methods.filterOutQuotePageDiscountPart(
|
||||
lineItems.supportingItems
|
||||
);
|
||||
await baseMixin.methods.dispatchStoreAction(
|
||||
storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING,
|
||||
lineItems.supportingItems,
|
||||
false
|
||||
);
|
||||
},
|
||||
},
|
||||
unmounted() {
|
||||
window.removeEventListener("pageshow", this.handlePageShow);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,11 @@
|
|||
cmsWidgetName="PleaseProvideAddressWidget"
|
||||
class="provide-address-header" />
|
||||
<textBlock
|
||||
v-if="isMSRFeeApplicable()"
|
||||
cmsWidgetName="VehicleKeysMSRMessageWidget"
|
||||
class="keys-message" />
|
||||
<textBlock
|
||||
v-else
|
||||
cmsWidgetName="VehicleKeysMessageWidget"
|
||||
class="keys-message" />
|
||||
<mobileAddressQuestions
|
||||
|
|
@ -143,6 +148,9 @@ export default {
|
|||
getIsVehicleProtectedFromStore() {
|
||||
return store.getters.order.serviceLocation.isVehicleProtected;
|
||||
},
|
||||
isMSRFeeApplicable() {
|
||||
return store.getters.order.isMSRFeeApplicable;
|
||||
},
|
||||
async forwardButtonAction() {
|
||||
await this.dispatchStoreAction(
|
||||
this.storeActions.SAVE_MOBILE_DETAILS,
|
||||
|
|
|
|||
|
|
@ -306,6 +306,12 @@ describe("partQuestions.vue...", () => {
|
|||
//Assert
|
||||
expect(spy).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
storeActions.SAVE_IS_OEM_GLASS_SELECTED,
|
||||
false,
|
||||
false
|
||||
);
|
||||
expect(spy).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
"savePartQuestionAnswers",
|
||||
[
|
||||
{
|
||||
|
|
|
|||
|
|
@ -188,6 +188,8 @@ export default {
|
|||
}
|
||||
});
|
||||
|
||||
this.dispatchStoreAction(this.storeActions.SAVE_IS_OEM_GLASS_SELECTED, false, false);
|
||||
|
||||
// save to vuex store as order.damage.partQuestionAnswers (array)
|
||||
// used in GET_PARTS call following this one
|
||||
await this.dispatchStoreAction(
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue