From b6b03e876a6a111c754e1082ddc71f8898a3ed1a Mon Sep 17 00:00:00 2001 From: Jeremy-Z Date: Mon, 4 May 2026 14:47:00 -0400 Subject: [PATCH 1/7] Fix for invalid user seq number --- src/mixins/analytics-mixin.js | 38 ++++++++++++++++++++---------- src/mixins/analytics-mixin.spec.js | 25 +++++++++++--------- src/router/index.js | 6 ----- src/store/index.js | 4 ++-- 4 files changed, 41 insertions(+), 32 deletions(-) diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 6480a508..feaa112f 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -7,7 +7,8 @@ import { regenerateUserId, regenerateDeviceId, setSessionIdIfUnset, - setSessionKeyIfUnset + setSessionKeyIfUnset, + updateSessionIdCookie } from '@/helpers/cookie-helper'; import applicationConfig from '@/constants/application-config'; import queryStrings from '@/constants/query-strings'; @@ -52,12 +53,21 @@ export default { } return ''; }, - logPageView(pageEvent) { + validateSession() { + + if (this.noSession()) { + this.initSession(); + } + + updateSessionIdCookie(); + }, + async logPageView(pageEvent) { const store = useMainStore(); const currentPageName = this.getPageNameByQueryString(); + this.validateSession(); const payload = { - userId: getDeviceIdValue(), + userId: getUserIdValue(), sessionKey: getSessionKeyValue(), pageName: currentPageName, referralSequenceNumber: store.order.referralSequenceNumber, @@ -69,15 +79,16 @@ export default { experimentsForUser: store.applicationUser.experiments }; - store.logPageView(payload); + await store.logPageView(payload); }, - logCustomEvent(category, action, label, value) { + async logCustomEvent(category, action, label, value) { const store = useMainStore(); const currentPageName = this.getPageNameByQueryString(); + this.validateSession(); const payload = { - userId: getDeviceIdValue(), + userId: getUserIdValue(), sessionKey: getSessionKeyValue(), pageName: currentPageName, referralSequenceNumber: store.order.referralSequenceNumber, @@ -91,9 +102,9 @@ export default { experimentsForUser: store.applicationUser.experiments }; - store.logCustomEvent(payload); + await store.logCustomEvent(payload); }, - pushEventToGA(category, action, label, pushToLogApp = false, valueToLogType = null, value = undefined) { + async pushEventToGA(category, action, label, pushToLogApp = false, valueToLogType = null, value = undefined) { const currentPageName = this.getPageNameByQueryString(); const labelToLog = getValueToLog(label, valueToLogType); @@ -109,14 +120,14 @@ export default { pushToDataLayerIfDefined(eventToBePushed); if (pushToLogApp) { - this.logCustomEvent(category, action, labelToLog, value); + await this.logCustomEvent(category, action, labelToLog, value); } }, pushGenericObjectToGA(object) { pushToDataLayerIfDefined(object); }, - pushPageViewToGA() { + async pushPageViewToGA() { const currentPageName = this.getPageNameByQueryString(); const pageViewEvent = { event: GaEvents.PAGE_VIEW_EVENT, @@ -126,7 +137,7 @@ export default { pushToDataLayerIfDefined(pageViewEvent); - this.logPageView(analyticsPageEvents.ENTRY); + await this.logPageView(analyticsPageEvents.ENTRY); }, pushOrderToDataLayer() { @@ -532,7 +543,8 @@ export default { store.logIssSessionData(sessionData); }, - async initSession() { + initSession() { + regenerateDeviceId(); regenerateUserId(); @@ -549,7 +561,7 @@ export default { userAgent: navigator.userAgent }; - const response = await useMainStore().initializeSession(payload); + const response = useMainStore().initializeSession(payload); if (response?.data) { if (response?.data.sessionKey) { diff --git a/src/mixins/analytics-mixin.spec.js b/src/mixins/analytics-mixin.spec.js index 2c65e505..a13806c1 100644 --- a/src/mixins/analytics-mixin.spec.js +++ b/src/mixins/analytics-mixin.spec.js @@ -8,29 +8,32 @@ import { ValueToLogTypes } from '@/constants/analytics'; import { useMainStore } from '@/store'; +import crypto from 'crypto'; + +global.crypto = crypto; describe('analyticsMixin.js', () => { - test('logPageView: calls dispatch with type and payload', () => { + test('logPageView: calls dispatch with type and payload', async () => { const payload = {}; const testCookieValue = { sid: '10000000-0000-0000-0000-000000000001' }; - setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) }); + setupCookies({ ISSCookieValue: JSON.stringify(testCookieValue) }); - useMainStore().logPageView(payload); + await analyticsMixin.methods.logPageView(payload); expect(useMainStore().logPageView).toBeCalled(); }); - test('logCustomEvent: calls dispatch with type and payload', () => { - useMainStore().logCustomEvent('someCat', 'someAction', 'someLabel', 'someVal'); + test('logCustomEvent: calls dispatch with type and payload', async () => { + await analyticsMixin.methods.logCustomEvent('someCat', 'someAction', 'someLabel', 'someVal'); expect(useMainStore().logCustomEvent).toBeCalled(); }); - test('pushEventToGA, should call dataLayer push and logCustomEvent too', () => { + test('pushEventToGA, should call dataLayer push and logCustomEvent too', async () => { // Arrange window.dataLayer = []; const mockDataLayer = []; @@ -44,13 +47,13 @@ describe('analyticsMixin.js', () => { }); // Act - analyticsMixin.methods.pushEventToGA('category', 'action', 'label', true); + await analyticsMixin.methods.pushEventToGA('category', 'action', 'label', true); // Assert expect(mockDataLayer).toEqual(expect.arrayContaining(window.dataLayer)); }); - test('pushEventToGA, should call dataLayer push and ValueToLogTypes.LAST_5 only logs last 5 of label', () => { + test('pushEventToGA, should call dataLayer push and ValueToLogTypes.LAST_5 only logs last 5 of label', async () => { // Arrange window.dataLayer = []; const expectedDataLayer = []; @@ -64,7 +67,7 @@ describe('analyticsMixin.js', () => { }); // Act - analyticsMixin.methods.pushEventToGA( + await analyticsMixin.methods.pushEventToGA( 'category', 'action', '1111122222333333', @@ -76,7 +79,7 @@ describe('analyticsMixin.js', () => { expect(expectedDataLayer).toEqual(expect.arrayContaining(window.dataLayer)); }); - test('pushEventToGA, should call dataLayer push and ValueToLogTypes.LAST_5 logs only the last 3 characters for a 3 character string', () => { + test('pushEventToGA, should call dataLayer push and ValueToLogTypes.LAST_5 logs only the last 3 characters for a 3 character string', async () => { // Arrange window.dataLayer = []; const expectedDataLayer = []; @@ -90,7 +93,7 @@ describe('analyticsMixin.js', () => { }); // Act - analyticsMixin.methods.pushEventToGA( + await analyticsMixin.methods.pushEventToGA( 'category', 'action', '111', diff --git a/src/router/index.js b/src/router/index.js index 81dca7e3..60e05e6d 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -37,12 +37,6 @@ const routes = [ // Do not run these for the main entry page - as it is not part of the user flow. if (issPageToUse !== issPageValues.ENTRY_PAGE) { - if (analyticsMixin.methods.noSession()) { - await analyticsMixin.methods.initSession(); - } else { - updateSessionIdCookie(); - } - try { await runExperiments(issPageToUse); } catch (error) { diff --git a/src/store/index.js b/src/store/index.js index efd66b66..3bfe1a33 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -2560,7 +2560,7 @@ export const useMainStore = defineStore({ }); } }, - logPageView({ userId, sessionKey, pageName, referralSequenceNumber, parentAccountNumber, sessionId, action, event, shouldUseSessionId, experimentsForUser }) { + async logPageView({ userId, sessionKey, pageName, referralSequenceNumber, parentAccountNumber, sessionId, action, event, shouldUseSessionId, experimentsForUser }) { const payload = { userId, sessionKey, @@ -2588,7 +2588,7 @@ export const useMainStore = defineStore({ } ); }, - logCustomEvent({ userId, sessionKey, pageName, sessionId, category, action, label, value, shouldUseSessionId, experimentsForUser }) { + async logCustomEvent({ userId, sessionKey, pageName, sessionId, category, action, label, value, shouldUseSessionId, experimentsForUser }) { if (pageName == null || pageName.length === 0) { pageName = 'none'; } const payload = { From 4fbca5ccd8fd7250129b9781a3697150616a125e Mon Sep 17 00:00:00 2001 From: Alex Humphries Date: Mon, 4 May 2026 15:08:52 -0400 Subject: [PATCH 2/7] INSR-9513: Add all recal parts to glass part child parts array --- src/store/index.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/store/index.js b/src/store/index.js index e93e62b7..ca323c1c 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1120,7 +1120,7 @@ export const useMainStore = defineStore({ .then((response) => { const recalResponse = response.data; if (recalResponse && recalResponse.recalibrationParts && recalResponse.recalibrationParts.length > 0) { - this.addGlassPartRecalibrationInfo(partNumberChecked, recalResponse.recalibrationParts[0]); + this.addGlassPartRecalibrationInfo(partNumberChecked, recalResponse.recalibrationParts); } })); } @@ -1128,15 +1128,18 @@ export const useMainStore = defineStore({ await Promise.allSettled(recalPromises); } }, - addGlassPartRecalibrationInfo(partNumber, recalPartInformation) { + addGlassPartRecalibrationInfo(partNumber, recalPartArray) { const glassPart = this.lineItems.glassParts.find((gp) => gp.partNumber === partNumber); if (glassPart) { - const recalChildPart = glassPart.childParts?.find((cp) => cp.partNumber === recalPartInformation.partNumber); - if (!recalChildPart) { - if (!Array.isArray(glassPart.childParts) || !glassPart.childParts.length) { - glassPart.childParts = []; + for (const recalPartInformation of recalPartArray) { + const recalChildPart = glassPart.childParts?.find((cp) => cp.partNumber === recalPartInformation.partNumber); + console.log('Existing recalibration child part found', recalChildPart); + if (!recalChildPart) { + if (!Array.isArray(glassPart.childParts) || !glassPart.childParts.length) { + glassPart.childParts = []; + } + glassPart.childParts.push(recalPartInformation); } - glassPart.childParts.push(recalPartInformation); } } }, From 0e06dce78c265e1205b51aabcd648831f38121aa Mon Sep 17 00:00:00 2001 From: Jeremy-Z Date: Mon, 4 May 2026 15:22:58 -0400 Subject: [PATCH 3/7] Some additional fixes. --- src/mixins/analytics-mixin.js | 12 ++++++------ src/router/index.js | 6 ++++++ src/store/index.js | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index feaa112f..135f6e9a 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -53,10 +53,10 @@ export default { } return ''; }, - validateSession() { + async validateSession() { if (this.noSession()) { - this.initSession(); + await this.initSession(); } updateSessionIdCookie(); @@ -64,7 +64,7 @@ export default { async logPageView(pageEvent) { const store = useMainStore(); const currentPageName = this.getPageNameByQueryString(); - this.validateSession(); + //await this.validateSession(); const payload = { userId: getUserIdValue(), @@ -85,7 +85,7 @@ export default { async logCustomEvent(category, action, label, value) { const store = useMainStore(); const currentPageName = this.getPageNameByQueryString(); - this.validateSession(); + //await this.validateSession(); const payload = { userId: getUserIdValue(), @@ -543,7 +543,7 @@ export default { store.logIssSessionData(sessionData); }, - initSession() { + async initSession() { regenerateDeviceId(); regenerateUserId(); @@ -561,7 +561,7 @@ export default { userAgent: navigator.userAgent }; - const response = useMainStore().initializeSession(payload); + const response = await useMainStore().initializeSession(payload); if (response?.data) { if (response?.data.sessionKey) { diff --git a/src/router/index.js b/src/router/index.js index 60e05e6d..791b23d6 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -35,6 +35,12 @@ const routes = [ return await GoToAccessIsDenied(next); } + if (analyticsMixin.methods.noSession()) { + await analyticsMixin.methods.initSession(); + } else { + updateSessionIdCookie(); + } + // Do not run these for the main entry page - as it is not part of the user flow. if (issPageToUse !== issPageValues.ENTRY_PAGE) { try { diff --git a/src/store/index.js b/src/store/index.js index 3bfe1a33..e0a6a097 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -2625,7 +2625,7 @@ export const useMainStore = defineStore({ this.logExperimentIfExists(issPage, experimentUniverses.ISS_FEATURETOGGLE_AREFEES_HIDDEN); this.logExperimentIfExists(issPage, experimentUniverses.ISS_FEATURETOGGLE_AREFEES_OVERRIDDEN); }, - initializeSession({ userId, sessionId, userAgent, referrer }) { + async initializeSession({ userId, sessionId, userAgent, referrer }) { const payload = { applicationName: applicationConfig.APPLICATION_NAME, userId, From f8056d0099d59a2084ca4c7bbc9960b03082b78d Mon Sep 17 00:00:00 2001 From: Jeremy-Z Date: Mon, 4 May 2026 15:30:48 -0400 Subject: [PATCH 4/7] Some additional updates. --- src/mixins/analytics-mixin.js | 8 ++++++-- src/router/index.js | 6 +----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 135f6e9a..2c8c298d 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -54,12 +54,16 @@ export default { return ''; }, async validateSession() { + const emptySessionId = '00000000-0000-0000-0000-000000000000'; if (this.noSession()) { - await this.initSession(); + await this.initSession(); } - updateSessionIdCookie(); + const sessionId = getSessionIdValue(); + if (sessionId && sessionId !== emptySessionId) { + updateSessionIdCookie(); + } }, async logPageView(pageEvent) { const store = useMainStore(); diff --git a/src/router/index.js b/src/router/index.js index 791b23d6..2a332ab6 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -35,11 +35,7 @@ const routes = [ return await GoToAccessIsDenied(next); } - if (analyticsMixin.methods.noSession()) { - await analyticsMixin.methods.initSession(); - } else { - updateSessionIdCookie(); - } + await analyticsMixin.methods.validateSession(); // Do not run these for the main entry page - as it is not part of the user flow. if (issPageToUse !== issPageValues.ENTRY_PAGE) { From 2be1a91e16244fc94e12ea35b7637368c369331a Mon Sep 17 00:00:00 2001 From: Alex Humphries Date: Mon, 4 May 2026 15:39:34 -0400 Subject: [PATCH 5/7] INSR-9513: Remove console.log, handle multiple recal parts in price calc --- src/helpers/cart-helper.js | 5 +++-- src/store/index.js | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/helpers/cart-helper.js b/src/helpers/cart-helper.js index 1920df3c..22b0dcdb 100644 --- a/src/helpers/cart-helper.js +++ b/src/helpers/cart-helper.js @@ -165,8 +165,9 @@ export function getCartTotal(order) { export function getRecalibrationTotal(order) { const itemsWithRecalibration = getServiceLineItems(order) .filter((item) => item.requiresRecalibration); - const recalibrationLineItems = itemsWithRecalibration.map((item) => { - return item.childParts?.find((child) => child.partType === partTypeStrings.RECALIBRATION || child.partType === partTypeStrings.ADAS_RECALIBRATION) ?? {}; + + const recalibrationLineItems = itemsWithRecalibration.flatMap((item) => { + return item.childParts?.filter((child) => child.partType === partTypeStrings.RECALIBRATION || child.partType === partTypeStrings.ADAS_RECALIBRATION) ?? []; }); return getPriceOfLineItems(recalibrationLineItems); diff --git a/src/store/index.js b/src/store/index.js index ca323c1c..955c7283 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1133,7 +1133,6 @@ export const useMainStore = defineStore({ if (glassPart) { for (const recalPartInformation of recalPartArray) { const recalChildPart = glassPart.childParts?.find((cp) => cp.partNumber === recalPartInformation.partNumber); - console.log('Existing recalibration child part found', recalChildPart); if (!recalChildPart) { if (!Array.isArray(glassPart.childParts) || !glassPart.childParts.length) { glassPart.childParts = []; From 04bae6464a842549f1b560889880c01671205551 Mon Sep 17 00:00:00 2001 From: Katie Kroell Date: Mon, 4 May 2026 19:31:25 -0400 Subject: [PATCH 6/7] add MA steering language to coverage-statement --- src/layouts/coverage-statement/coverage-statement.vue | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/layouts/coverage-statement/coverage-statement.vue b/src/layouts/coverage-statement/coverage-statement.vue index 51595359..6576f745 100644 --- a/src/layouts/coverage-statement/coverage-statement.vue +++ b/src/layouts/coverage-statement/coverage-statement.vue @@ -60,6 +60,10 @@
+ +
@@ -357,6 +361,9 @@ export default { return this.isITACQuoteVisible || this.isNoCompQuoteVisible || (this.showDeductibleOnly && this.deductibleValue > 0); + }, + MAPolicy() { + return useMainStore().customerData.addressQuestions.state === "MA"; } }, watch: { From 5e5d31d97971c82c7715fc36dfe5eb9c12b776fe Mon Sep 17 00:00:00 2001 From: Alex Humphries Date: Tue, 5 May 2026 08:41:37 -0400 Subject: [PATCH 7/7] INSR-9513: Add getRecalibrationTotal tests --- src/helpers/cart-helper.spec.js | 102 +++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 3 deletions(-) diff --git a/src/helpers/cart-helper.spec.js b/src/helpers/cart-helper.spec.js index 5a70feab..c57eaa86 100644 --- a/src/helpers/cart-helper.spec.js +++ b/src/helpers/cart-helper.spec.js @@ -4,6 +4,7 @@ import { getDeductible, getLineItems, getMobileFeeLineItem, getNonServiceLineItems, + getRecalibrationTotal, getRecycleFeeLineItem, getSalesTax, getServiceLineItems, getSubtotal, isOrderITAC, isOrderNoComp, isOrderUnverified @@ -25,7 +26,7 @@ describe('cart-helper', () => { }; } - function createDummyFeeItem(partNumber, partType, price, salesTax) { + function createDummyItemWithPartType(partNumber, partType, price, salesTax) { return { partNumber, partType, @@ -56,8 +57,8 @@ describe('cart-helper', () => { const glassLineItem = createDummyItem('glass', 40, 2); const otherLineItem = createDummyItem('other', 50, 3); const vapsLineItem = createDummyItem('vaps', 60, 4); - const mobileFeeLineItem = createDummyFeeItem(null, partTypeStrings.MOBILE_FEE, 70, 5); - const recycleFeeLineItem = createDummyFeeItem(partNumberStrings.RECYCLE_FEE, null, 80, 6); + const mobileFeeLineItem = createDummyItemWithPartType(null, partTypeStrings.MOBILE_FEE, 70, 5); + const recycleFeeLineItem = createDummyItemWithPartType(partNumberStrings.RECYCLE_FEE, null, 80, 6); const defaultLineItems = { supportingItems: [supportingLineItem], @@ -592,4 +593,99 @@ describe('cart-helper', () => { }); }); }); + + describe('getRecalibrationTotal', () => { + test('Returns total price of recalibration items when there is only 1', () => { + // Arrange + const recalibrationItem = createDummyItemWithPartType('recal', partTypeStrings.RECALIBRATION, 25, 2); + const lineItemWithRecal = { + ...glassLineItem, + requiresRecalibration: true, + childParts: [recalibrationItem] + }; + const order = { + lineItems: { + ...defaultLineItems, + glassParts: [lineItemWithRecal] + } + }; + + // Act + const result = getRecalibrationTotal(order); + + // Assert + expect(result).toBe(25); + }); + test('Returns total price of recalibration items when there are multiple', () => { + // Arrange + const recalibrationItem = createDummyItemWithPartType('recal', partTypeStrings.RECALIBRATION, 25, 2); + const thirdRecalibrationItem = createDummyItemWithPartType('third-recal', partTypeStrings.ADAS_RECALIBRATION, 35, 3); + const lineItemWithRecal = { + ...glassLineItem, + requiresRecalibration: true, + childParts: [recalibrationItem, thirdRecalibrationItem] + }; + const order = { + lineItems: { + ...defaultLineItems, + glassParts: [lineItemWithRecal] + } + }; + + // Act + const result = getRecalibrationTotal(order); + + // Assert + expect(result).toBe(60); + }); + + test('Returns 0 when no items require recalibration', () => { + // Arrange + const order = { + lineItems: defaultLineItems + }; + + // Act + const result = getRecalibrationTotal(order); + + // Assert + expect(result).toBe(0); + }); + + test('Returns 0 when line items are null', () => { + // Arrange + const order = { + lineItems: nullLineItems + }; + + // Act + const result = getRecalibrationTotal(order); + + // Assert + expect(result).toBe(0); + }); + + test('Filters out non-recalibration child parts', () => { + // Arrange + const recalibrationItem = createDummyItemWithPartType('recal', partTypeStrings.RECALIBRATION, 25, 2); + const otherChildItem = createDummyItem('other-child', 15, 1); + const lineItemWithRecal = { + ...glassLineItem, + requiresRecalibration: true, + childParts: [recalibrationItem, otherChildItem] + }; + const order = { + lineItems: { + ...defaultLineItems, + glassParts: [lineItemWithRecal] + } + }; + + // Act + const result = getRecalibrationTotal(order); + + // Assert + expect(result).toBe(25); + }); + }); });