Merge branch 'develop' into feature/jnou/more-fixes-playwright

This commit is contained in:
JennyNou 2026-05-05 12:43:08 -04:00
commit f4b4e6e863
7 changed files with 164 additions and 43 deletions

View file

@ -165,8 +165,9 @@ export function getCartTotal(order) {
export function getRecalibrationTotal(order) { export function getRecalibrationTotal(order) {
const itemsWithRecalibration = getServiceLineItems(order) const itemsWithRecalibration = getServiceLineItems(order)
.filter((item) => item.requiresRecalibration); .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); return getPriceOfLineItems(recalibrationLineItems);

View file

@ -4,6 +4,7 @@ import {
getDeductible, getDeductible,
getLineItems, getLineItems,
getMobileFeeLineItem, getNonServiceLineItems, getMobileFeeLineItem, getNonServiceLineItems,
getRecalibrationTotal,
getRecycleFeeLineItem, getSalesTax, getRecycleFeeLineItem, getSalesTax,
getServiceLineItems, getSubtotal, isOrderITAC, isOrderNoComp, getServiceLineItems, getSubtotal, isOrderITAC, isOrderNoComp,
isOrderUnverified isOrderUnverified
@ -25,7 +26,7 @@ describe('cart-helper', () => {
}; };
} }
function createDummyFeeItem(partNumber, partType, price, salesTax) { function createDummyItemWithPartType(partNumber, partType, price, salesTax) {
return { return {
partNumber, partNumber,
partType, partType,
@ -56,8 +57,8 @@ describe('cart-helper', () => {
const glassLineItem = createDummyItem('glass', 40, 2); const glassLineItem = createDummyItem('glass', 40, 2);
const otherLineItem = createDummyItem('other', 50, 3); const otherLineItem = createDummyItem('other', 50, 3);
const vapsLineItem = createDummyItem('vaps', 60, 4); const vapsLineItem = createDummyItem('vaps', 60, 4);
const mobileFeeLineItem = createDummyFeeItem(null, partTypeStrings.MOBILE_FEE, 70, 5); const mobileFeeLineItem = createDummyItemWithPartType(null, partTypeStrings.MOBILE_FEE, 70, 5);
const recycleFeeLineItem = createDummyFeeItem(partNumberStrings.RECYCLE_FEE, null, 80, 6); const recycleFeeLineItem = createDummyItemWithPartType(partNumberStrings.RECYCLE_FEE, null, 80, 6);
const defaultLineItems = { const defaultLineItems = {
supportingItems: [supportingLineItem], 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);
});
});
}); });

View file

@ -60,6 +60,10 @@
<div <div
class="body-text" class="body-text"
v-html="nextStepsBody"></div> v-html="nextStepsBody"></div>
<textBlock
v-if="MAPolicy"
cmsWidgetName="MASteeringText">
</textBlock>
<div <div
v-if="isNoCompQuoteVisible" v-if="isNoCompQuoteVisible"
class="no-comp-price-text"> class="no-comp-price-text">
@ -357,6 +361,9 @@ export default {
return this.isITACQuoteVisible return this.isITACQuoteVisible
|| this.isNoCompQuoteVisible || this.isNoCompQuoteVisible
|| (this.showDeductibleOnly && this.deductibleValue > 0); || (this.showDeductibleOnly && this.deductibleValue > 0);
},
MAPolicy() {
return useMainStore().customerData.addressQuestions.state === "MA";
} }
}, },
watch: { watch: {

View file

@ -7,7 +7,8 @@ import {
regenerateUserId, regenerateUserId,
regenerateDeviceId, regenerateDeviceId,
setSessionIdIfUnset, setSessionIdIfUnset,
setSessionKeyIfUnset setSessionKeyIfUnset,
updateSessionIdCookie
} from '@/helpers/cookie-helper'; } from '@/helpers/cookie-helper';
import applicationConfig from '@/constants/application-config'; import applicationConfig from '@/constants/application-config';
import queryStrings from '@/constants/query-strings'; import queryStrings from '@/constants/query-strings';
@ -52,12 +53,25 @@ export default {
} }
return ''; return '';
}, },
logPageView(pageEvent) { async validateSession() {
const emptySessionId = '00000000-0000-0000-0000-000000000000';
if (this.noSession()) {
await this.initSession();
}
const sessionId = getSessionIdValue();
if (sessionId && sessionId !== emptySessionId) {
updateSessionIdCookie();
}
},
async logPageView(pageEvent) {
const store = useMainStore(); const store = useMainStore();
const currentPageName = this.getPageNameByQueryString(); const currentPageName = this.getPageNameByQueryString();
//await this.validateSession();
const payload = { const payload = {
userId: getDeviceIdValue(), userId: getUserIdValue(),
sessionKey: getSessionKeyValue(), sessionKey: getSessionKeyValue(),
pageName: currentPageName, pageName: currentPageName,
referralSequenceNumber: store.order.referralSequenceNumber, referralSequenceNumber: store.order.referralSequenceNumber,
@ -69,15 +83,16 @@ export default {
experimentsForUser: store.applicationUser.experiments 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 store = useMainStore();
const currentPageName = this.getPageNameByQueryString(); const currentPageName = this.getPageNameByQueryString();
//await this.validateSession();
const payload = { const payload = {
userId: getDeviceIdValue(), userId: getUserIdValue(),
sessionKey: getSessionKeyValue(), sessionKey: getSessionKeyValue(),
pageName: currentPageName, pageName: currentPageName,
referralSequenceNumber: store.order.referralSequenceNumber, referralSequenceNumber: store.order.referralSequenceNumber,
@ -91,9 +106,9 @@ export default {
experimentsForUser: store.applicationUser.experiments 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 currentPageName = this.getPageNameByQueryString();
const labelToLog = getValueToLog(label, valueToLogType); const labelToLog = getValueToLog(label, valueToLogType);
@ -109,14 +124,14 @@ export default {
pushToDataLayerIfDefined(eventToBePushed); pushToDataLayerIfDefined(eventToBePushed);
if (pushToLogApp) { if (pushToLogApp) {
this.logCustomEvent(category, action, labelToLog, value); await this.logCustomEvent(category, action, labelToLog, value);
} }
}, },
pushGenericObjectToGA(object) { pushGenericObjectToGA(object) {
pushToDataLayerIfDefined(object); pushToDataLayerIfDefined(object);
}, },
pushPageViewToGA() { async pushPageViewToGA() {
const currentPageName = this.getPageNameByQueryString(); const currentPageName = this.getPageNameByQueryString();
const pageViewEvent = { const pageViewEvent = {
event: GaEvents.PAGE_VIEW_EVENT, event: GaEvents.PAGE_VIEW_EVENT,
@ -126,7 +141,7 @@ export default {
pushToDataLayerIfDefined(pageViewEvent); pushToDataLayerIfDefined(pageViewEvent);
this.logPageView(analyticsPageEvents.ENTRY); await this.logPageView(analyticsPageEvents.ENTRY);
}, },
pushOrderToDataLayer() { pushOrderToDataLayer() {
@ -533,6 +548,7 @@ export default {
}, },
async initSession() { async initSession() {
regenerateDeviceId(); regenerateDeviceId();
regenerateUserId(); regenerateUserId();

View file

@ -8,29 +8,32 @@ import {
ValueToLogTypes ValueToLogTypes
} from '@/constants/analytics'; } from '@/constants/analytics';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
import crypto from 'crypto';
global.crypto = crypto;
describe('analyticsMixin.js', () => { describe('analyticsMixin.js', () => {
test('logPageView: calls dispatch with type and payload', () => { test('logPageView: calls dispatch with type and payload', async () => {
const payload = {}; const payload = {};
const testCookieValue = { const testCookieValue = {
sid: '10000000-0000-0000-0000-000000000001' 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(); expect(useMainStore().logPageView).toBeCalled();
}); });
test('logCustomEvent: calls dispatch with type and payload', () => { test('logCustomEvent: calls dispatch with type and payload', async () => {
useMainStore().logCustomEvent('someCat', 'someAction', 'someLabel', 'someVal'); await analyticsMixin.methods.logCustomEvent('someCat', 'someAction', 'someLabel', 'someVal');
expect(useMainStore().logCustomEvent).toBeCalled(); expect(useMainStore().logCustomEvent).toBeCalled();
}); });
test('pushEventToGA, should call dataLayer push and logCustomEvent too', () => { test('pushEventToGA, should call dataLayer push and logCustomEvent too', async () => {
// Arrange // Arrange
window.dataLayer = []; window.dataLayer = [];
const mockDataLayer = []; const mockDataLayer = [];
@ -44,13 +47,13 @@ describe('analyticsMixin.js', () => {
}); });
// Act // Act
analyticsMixin.methods.pushEventToGA('category', 'action', 'label', true); await analyticsMixin.methods.pushEventToGA('category', 'action', 'label', true);
// Assert // Assert
expect(mockDataLayer).toEqual(expect.arrayContaining(window.dataLayer)); 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 // Arrange
window.dataLayer = []; window.dataLayer = [];
const expectedDataLayer = []; const expectedDataLayer = [];
@ -64,7 +67,7 @@ describe('analyticsMixin.js', () => {
}); });
// Act // Act
analyticsMixin.methods.pushEventToGA( await analyticsMixin.methods.pushEventToGA(
'category', 'category',
'action', 'action',
'1111122222333333', '1111122222333333',
@ -76,7 +79,7 @@ describe('analyticsMixin.js', () => {
expect(expectedDataLayer).toEqual(expect.arrayContaining(window.dataLayer)); 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 // Arrange
window.dataLayer = []; window.dataLayer = [];
const expectedDataLayer = []; const expectedDataLayer = [];
@ -90,7 +93,7 @@ describe('analyticsMixin.js', () => {
}); });
// Act // Act
analyticsMixin.methods.pushEventToGA( await analyticsMixin.methods.pushEventToGA(
'category', 'category',
'action', 'action',
'111', '111',

View file

@ -35,14 +35,10 @@ const routes = [
return await GoToAccessIsDenied(next); return await GoToAccessIsDenied(next);
} }
await analyticsMixin.methods.validateSession();
// Do not run these for the main entry page - as it is not part of the user flow. // Do not run these for the main entry page - as it is not part of the user flow.
if (issPageToUse !== issPageValues.ENTRY_PAGE) { if (issPageToUse !== issPageValues.ENTRY_PAGE) {
if (analyticsMixin.methods.noSession()) {
await analyticsMixin.methods.initSession();
} else {
updateSessionIdCookie();
}
try { try {
await runExperiments(issPageToUse); await runExperiments(issPageToUse);
} catch (error) { } catch (error) {

View file

@ -1120,7 +1120,7 @@ export const useMainStore = defineStore({
.then((response) => { .then((response) => {
const recalResponse = response.data; const recalResponse = response.data;
if (recalResponse && recalResponse.recalibrationParts && recalResponse.recalibrationParts.length > 0) { if (recalResponse && recalResponse.recalibrationParts && recalResponse.recalibrationParts.length > 0) {
this.addGlassPartRecalibrationInfo(partNumberChecked, recalResponse.recalibrationParts[0]); this.addGlassPartRecalibrationInfo(partNumberChecked, recalResponse.recalibrationParts);
} }
})); }));
} }
@ -1128,15 +1128,17 @@ export const useMainStore = defineStore({
await Promise.allSettled(recalPromises); await Promise.allSettled(recalPromises);
} }
}, },
addGlassPartRecalibrationInfo(partNumber, recalPartInformation) { addGlassPartRecalibrationInfo(partNumber, recalPartArray) {
const glassPart = this.lineItems.glassParts.find((gp) => gp.partNumber === partNumber); const glassPart = this.lineItems.glassParts.find((gp) => gp.partNumber === partNumber);
if (glassPart) { if (glassPart) {
const recalChildPart = glassPart.childParts?.find((cp) => cp.partNumber === recalPartInformation.partNumber); for (const recalPartInformation of recalPartArray) {
if (!recalChildPart) { const recalChildPart = glassPart.childParts?.find((cp) => cp.partNumber === recalPartInformation.partNumber);
if (!Array.isArray(glassPart.childParts) || !glassPart.childParts.length) { if (!recalChildPart) {
glassPart.childParts = []; if (!Array.isArray(glassPart.childParts) || !glassPart.childParts.length) {
glassPart.childParts = [];
}
glassPart.childParts.push(recalPartInformation);
} }
glassPart.childParts.push(recalPartInformation);
} }
} }
}, },
@ -2560,7 +2562,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 = { const payload = {
userId, userId,
sessionKey, sessionKey,
@ -2588,7 +2590,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'; } if (pageName == null || pageName.length === 0) { pageName = 'none'; }
const payload = { const payload = {
@ -2625,7 +2627,7 @@ export const useMainStore = defineStore({
this.logExperimentIfExists(issPage, experimentUniverses.ISS_FEATURETOGGLE_AREFEES_HIDDEN); this.logExperimentIfExists(issPage, experimentUniverses.ISS_FEATURETOGGLE_AREFEES_HIDDEN);
this.logExperimentIfExists(issPage, experimentUniverses.ISS_FEATURETOGGLE_AREFEES_OVERRIDDEN); this.logExperimentIfExists(issPage, experimentUniverses.ISS_FEATURETOGGLE_AREFEES_OVERRIDDEN);
}, },
initializeSession({ userId, sessionId, userAgent, referrer }) { async initializeSession({ userId, sessionId, userAgent, referrer }) {
const payload = { const payload = {
applicationName: applicationConfig.APPLICATION_NAME, applicationName: applicationConfig.APPLICATION_NAME,
userId, userId,