From 9500337421aff33f0cea892526e776827062912d Mon Sep 17 00:00:00 2001 From: Matt Sykes Date: Thu, 30 Nov 2023 15:34:50 -0500 Subject: [PATCH 01/36] Make separate analytics APPLICATION_NAME --- src/constants/application-config.js | 1 + src/store/index.js | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/constants/application-config.js b/src/constants/application-config.js index b5233d170..1e2324ba7 100644 --- a/src/constants/application-config.js +++ b/src/constants/application-config.js @@ -6,6 +6,7 @@ const applicationConfig = { COOKIE_PATH: "/", CURRENT_ENVIRONMENT: process.env.VUE_APP_CURRENT_ENVIRONMENT, // "Localhost", "Dev", "QA", and "Prod" APPLICATION_NAME: "FixMyGlass", + ANALYTICS_APPLICATION_NAME: "FixMyGlassNextGen", APPLICATION_ABBREVIATION: "fmg", PAGE_QUERYSTRING: "fmgPage", SITE_ENTRY_TRIGGER_VALUE: "FixMyGlass", diff --git a/src/store/index.js b/src/store/index.js index efaf5b428..684ca83e1 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1076,7 +1076,7 @@ export const actions = { sessionKey: sessionKey, sessionId: sessionId, pageName: pageName, - applicationName: applicationConfig.APPLICATION_NAME, + applicationName: applicationConfig.ANALYTICS_APPLICATION_NAME, action: action, event: event, shouldUseSessionId: shouldUseSessionId, @@ -1124,7 +1124,7 @@ export const actions = { sessionKey: sessionKey, sessionId: sessionId, pageName: pageName, - applicationName: applicationConfig.APPLICATION_NAME, + applicationName: applicationConfig.ANALYTICS_APPLICATION_NAME, category: category, action: action, label: label, @@ -1153,7 +1153,7 @@ export const actions = { }, initializeSession(context, { userId, sessionId, userAgent, referrer }) { var payload = { - applicationName: applicationConfig.APPLICATION_NAME, + applicationName: applicationConfig.ANALYTICS_APPLICATION_NAME, userId: userId, deviceId: userId, sessionId: sessionId, From a79a3204841152a2d1b859ac16e3e8f8b1cee25c Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Thu, 30 Nov 2023 16:28:20 -0500 Subject: [PATCH 02/36] Add new cookie ids --- src/constants/cookie-names.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/constants/cookie-names.js b/src/constants/cookie-names.js index 5fab52899..0d8d0b05f 100644 --- a/src/constants/cookie-names.js +++ b/src/constants/cookie-names.js @@ -3,6 +3,9 @@ import { applicationConfig } from "@/constants/application-config.js"; const cookieNames = { FUNNEL_SESSION_INFO: `FunnelSessionInfo-${applicationConfig.CURRENT_ENVIRONMENT}`, + FUNNEL_SESSION_KEY: `FunnelSessionKey-${applicationConfig.CURRENT_ENVIRONMENT}`, + FUNNEL_USER_ID: `FunnelUserId-${applicationConfig.CURRENT_ENVIRONMENT}`, + // Existing Safelite.com cookies DXDEV: "dxdev", SESSION_ID: "sid", From edd8158148035e00b08632f72acd27b38e654511 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Thu, 30 Nov 2023 17:35:21 -0500 Subject: [PATCH 03/36] Iteration 1 --- src/constants/cookie-names.js | 2 +- .../heritage-integration/cookie-helper.js | 24 +++- src/mixins/analytics-mixin.js | 125 +++++++++++++++--- 3 files changed, 128 insertions(+), 23 deletions(-) diff --git a/src/constants/cookie-names.js b/src/constants/cookie-names.js index 0d8d0b05f..bf6bc130a 100644 --- a/src/constants/cookie-names.js +++ b/src/constants/cookie-names.js @@ -5,7 +5,7 @@ const cookieNames = { FUNNEL_SESSION_KEY: `FunnelSessionKey-${applicationConfig.CURRENT_ENVIRONMENT}`, FUNNEL_USER_ID: `FunnelUserId-${applicationConfig.CURRENT_ENVIRONMENT}`, - + // Existing Safelite.com cookies DXDEV: "dxdev", SESSION_ID: "sid", diff --git a/src/helpers/heritage-integration/cookie-helper.js b/src/helpers/heritage-integration/cookie-helper.js index 1ef43bff0..71a1051f2 100644 --- a/src/helpers/heritage-integration/cookie-helper.js +++ b/src/helpers/heritage-integration/cookie-helper.js @@ -82,7 +82,7 @@ export function getDeviceIdValue() { Gets value of skey cookie, returns 0 if not found. */ export function getSessionKeyValue() { - const cookieValue = getCookieValueByName(cookieNames.SESSION_KEY); + const cookieValue = getCookieValueByName(cookieNames.FUNNEL_SESSION_KEY); if (cookieValue) { return cookieValue; @@ -111,6 +111,16 @@ export function updateSessionIdCookie() { createOrUpdateCookie(cookieNames.SESSION_ID, getSessionIdValue(), { maxAge: 60 * 30 }); } +export function getUserIdValue() { + const cookieValue = getCookieValueByName(cookieNames.FUNNEL_USER_ID); + + if(cookieValue) { + return cookieValue; + } + + return "00000000-0000-0000-0000-000000000000"; +} + export function setCookieProperties( properties, { useDefaultFunnelCookieAttributes = true, maxAge, isSecure } @@ -126,6 +136,18 @@ export function setCookieProperties( } } +export function isCookieSet(name) { + const val = getCookieValueByName(name); + + return !!val; +} + +export function refreshCookieExpiration(name, expirationTime) { + if (isCookieSet(name)) { + createOrUpdateCookie(name, getCookieValueByName(name), { maxAge: expirationTime }); + } +} + /* =========================== = PRIVATE FUNCTIONS = diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 37615f68b..65c77c44e 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -4,6 +4,9 @@ import { getDeviceIdValue, getSessionIdValue, getSessionKeyValue, + getUserIdValue, + isCookieSet, + refreshCookieExpiration, } from "@/helpers/heritage-integration/cookie-helper"; import { queryStrings } from "@/constants/query-strings"; import { experimentSettings } from "@/constants/experiments"; @@ -135,39 +138,102 @@ export default { }; }, - async initSession() { - const sid = getSessionIdValue(); - const skey = getSessionKeyValue(); - const referrer = - applicationConfig.CURRENT_ENVIRONMENT != "Localhost" ? document.referrer : null; + // async initSession() { + // const sid = getSessionIdValue(); + // const skey = getSessionKeyValue(); + // const referrer = + // applicationConfig.CURRENT_ENVIRONMENT != "Localhost" ? document.referrer : null; - var payload = { - userId: getDeviceIdValue(), - sessionId: sid, - userAgent: navigator.userAgent, - referrer: referrer, + // var payload = { + // userId: getDeviceIdValue(), + // sessionId: sid, + // userAgent: navigator.userAgent, + // referrer: referrer, + // }; + + // const response = await baseMixin.methods.dispatchStoreAction( + // storeActions.INITIALIZE_SESSION, + // payload, + // false + // ); + + // if (response?.data) { + // if (response?.data.sessionKey && skey === 0) { + // setCookieProperties( + // { [cookieNames.SESSION_KEY]: response?.data.sessionKey }, + // { + // useDefaultFunnelCookieAttributes: false, + // } + // ); + // } + // if (response?.data.sessionId && sid === "00000000-0000-0000-0000-000000000000") { + // setCookieProperties( + // { [cookieNames.SESSION_ID]: response?.data.sessionId }, + // { + // maxAge: 60 * 30, // 30 minutes + // } + // ); + // } + // } + // }, + + async initSession() { + if (!isCookieSet(cookieNames.FUNNEL_USER_ID)) { + setCookieProperties( + { + [cookieNames.FUNNEL_USER_ID]: crypto.randomUUID(), + }, + { maxAge: 7 * 24 * 60 * 60 } + ); + } + if (!isCookieSet(cookieNames.DXDEV)) { + setCookieProperties( + { + [cookieNames.DXDEV]: crypto.randomUUID(), + }, + { maxAge: 365 * 24 * 60 * 60 } + ); + } + + const userId = getUserIdValue(); // cookieNames.FUNNEL_USER_ID + const deviceId = getDeviceIdValue(); // cookieNames.DXDEV + const sessionId = getSessionIdValue(); // cookieNames.SESSION_ID + const userAgent = navigator.userAgent; // navigator.userAgent + const refferer = applicationConfig.CURRENT_ENVIRONMENT != "Localhost" ? document.referrer : null; // see above + + const payload = { + userId: userId, + deviceId: deviceId, + sessionId: sessionId, + userAgent: userAgent, + refferer: refferer, }; - const response = await baseMixin.methods.dispatchStoreAction( + const response = await baseMixin.dispatchStoreAction( storeActions.INITIALIZE_SESSION, payload, false ); - if (response?.data) { - if (response?.data.sessionKey && skey === 0) { + if(response?.data) { + if(response.data.sessionKey && !isCookieSet(cookieNames.FUNNEL_SESSION_KEY)) { setCookieProperties( - { [cookieNames.SESSION_KEY]: response?.data.sessionKey }, { - useDefaultFunnelCookieAttributes: false, + [cookieNames.FUNNEL_SESSION_KEY]: response.data.sessionKey, + }, + { + maxAge: 30 * 60 } ); } - if (response?.data.sessionId && sid === "00000000-0000-0000-0000-000000000000") { + + if(response.data.sessionId && !isCookieSet(cookieNames.SESSION_ID)) { setCookieProperties( - { [cookieNames.SESSION_ID]: response?.data.sessionId }, { - maxAge: 60 * 30, // 30 minutes + [cookieNames.SESSION_ID]: response.data.sessionId + }, + { + maxAge: 30 * 60 } ); } @@ -175,12 +241,29 @@ export default { }, noSession() { - return ( - getSessionKeyValue() === 0 || - getSessionIdValue() === "00000000-0000-0000-0000-000000000000" + return !( + isCookieSet(cookieNames.SESSION_ID) && + isCookieSet(cookieNames.DXDEV) && + isCookieSet(cookieNames.FUNNEL_SESSION_KEY) && + isCookieSet(cookieNames.FUNNEL_USER_ID) ); }, + async validateSession() { + if (this.noSession()) { + await this.initSession(); + } + + this.refreshSessionExpiration(); + }, + + refreshSessionExpiration() { + refreshCookieExpiration(cookieNames.SESSION_ID, 30 * 60); + refreshCookieExpiration(cookieNames.DXDEV, 365 * 24 * 60 * 60); + refreshCookieExpiration(cookieNames.FUNNEL_USER_ID, 7 * 24 * 60 * 60); + refreshCookieExpiration(cookieNames.FUNNEL_SESSION_KEY, 30 * 60); + }, + removeParamsFromEndpoint(endpoint) { const endpointWithoutParams = endpoint.split("?")[0]; const numSlashesBeforeParams = 6; From 4510241bddc75abffb2f91b5f2944c1b955169aa Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Fri, 1 Dec 2023 08:12:19 -0500 Subject: [PATCH 04/36] Cleanup --- .../heritage-integration/cookie-helper.js | 2 +- src/mixins/analytics-mixin.js | 56 +++---------------- src/router/index.js | 6 +- 3 files changed, 11 insertions(+), 53 deletions(-) diff --git a/src/helpers/heritage-integration/cookie-helper.js b/src/helpers/heritage-integration/cookie-helper.js index 71a1051f2..1e1db1f1e 100644 --- a/src/helpers/heritage-integration/cookie-helper.js +++ b/src/helpers/heritage-integration/cookie-helper.js @@ -114,7 +114,7 @@ export function updateSessionIdCookie() { export function getUserIdValue() { const cookieValue = getCookieValueByName(cookieNames.FUNNEL_USER_ID); - if(cookieValue) { + if (cookieValue) { return cookieValue; } diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 65c77c44e..f793f4825 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -138,45 +138,6 @@ export default { }; }, - // async initSession() { - // const sid = getSessionIdValue(); - // const skey = getSessionKeyValue(); - // const referrer = - // applicationConfig.CURRENT_ENVIRONMENT != "Localhost" ? document.referrer : null; - - // var payload = { - // userId: getDeviceIdValue(), - // sessionId: sid, - // userAgent: navigator.userAgent, - // referrer: referrer, - // }; - - // const response = await baseMixin.methods.dispatchStoreAction( - // storeActions.INITIALIZE_SESSION, - // payload, - // false - // ); - - // if (response?.data) { - // if (response?.data.sessionKey && skey === 0) { - // setCookieProperties( - // { [cookieNames.SESSION_KEY]: response?.data.sessionKey }, - // { - // useDefaultFunnelCookieAttributes: false, - // } - // ); - // } - // if (response?.data.sessionId && sid === "00000000-0000-0000-0000-000000000000") { - // setCookieProperties( - // { [cookieNames.SESSION_ID]: response?.data.sessionId }, - // { - // maxAge: 60 * 30, // 30 minutes - // } - // ); - // } - // } - // }, - async initSession() { if (!isCookieSet(cookieNames.FUNNEL_USER_ID)) { setCookieProperties( @@ -199,7 +160,8 @@ export default { const deviceId = getDeviceIdValue(); // cookieNames.DXDEV const sessionId = getSessionIdValue(); // cookieNames.SESSION_ID const userAgent = navigator.userAgent; // navigator.userAgent - const refferer = applicationConfig.CURRENT_ENVIRONMENT != "Localhost" ? document.referrer : null; // see above + const refferer = + applicationConfig.CURRENT_ENVIRONMENT != "Localhost" ? document.referrer : null; // see above const payload = { userId: userId, @@ -209,31 +171,31 @@ export default { refferer: refferer, }; - const response = await baseMixin.dispatchStoreAction( + const response = await baseMixin.methods.dispatchStoreAction( storeActions.INITIALIZE_SESSION, payload, false ); - if(response?.data) { - if(response.data.sessionKey && !isCookieSet(cookieNames.FUNNEL_SESSION_KEY)) { + if (response?.data) { + if (response.data.sessionKey && !isCookieSet(cookieNames.FUNNEL_SESSION_KEY)) { setCookieProperties( { [cookieNames.FUNNEL_SESSION_KEY]: response.data.sessionKey, }, { - maxAge: 30 * 60 + maxAge: 30 * 60, } ); } - if(response.data.sessionId && !isCookieSet(cookieNames.SESSION_ID)) { + if (response.data.sessionId && !isCookieSet(cookieNames.SESSION_ID)) { setCookieProperties( { - [cookieNames.SESSION_ID]: response.data.sessionId + [cookieNames.SESSION_ID]: response.data.sessionId, }, { - maxAge: 30 * 60 + maxAge: 30 * 60, } ); } diff --git a/src/router/index.js b/src/router/index.js index e2188997a..7769d8a2d 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -38,11 +38,7 @@ const routes = [ async beforeEnter(to, from, next) { // If we have no query string, or we don't have the FmgPage query string. try { - if (analyticsMixin.methods.noSession()) { - await analyticsMixin.methods.initSession(); - } else { - updateSessionIdCookie(); - } + await analyticsMixin.methods.validateSession(); if (getFunnelCookie()?.SuppressConceptFunnel) { await navigateToHeritageFunnel({ shouldSaveSession: false }); From 0ba926d20c9fa8114687a1eaa8726c9b2bcf8c28 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Fri, 1 Dec 2023 08:37:22 -0500 Subject: [PATCH 05/36] Move all specific cookie logic to cookie-helper --- .../heritage-integration/cookie-helper.js | 80 ++++++++++++++++--- src/mixins/analytics-mixin.js | 63 ++++----------- 2 files changed, 84 insertions(+), 59 deletions(-) diff --git a/src/helpers/heritage-integration/cookie-helper.js b/src/helpers/heritage-integration/cookie-helper.js index 1e1db1f1e..56736d340 100644 --- a/src/helpers/heritage-integration/cookie-helper.js +++ b/src/helpers/heritage-integration/cookie-helper.js @@ -78,6 +78,38 @@ export function getDeviceIdValue() { return "00000000-0000-0000-0000-000000000000"; } +export function regenerateDeviceId() { + if (!isCookieSet(cookieNames.DXDEV)) { + setCookieProperties( + { + [cookieNames.DXDEV]: `did=${crypto.randomUUID()}`, + }, + { maxAge: 365 * 24 * 60 * 60 } + ); + } +} + +export function getUserIdValue() { + const cookieValue = getCookieValueByName(cookieNames.FUNNEL_USER_ID); + + if (cookieValue) { + return cookieValue; + } + + return "00000000-0000-0000-0000-000000000000"; +} + +export function regenerateUserId() { + if (!isCookieSet(cookieNames.FUNNEL_USER_ID)) { + setCookieProperties( + { + [cookieNames.FUNNEL_USER_ID]: crypto.randomUUID(), + }, + { maxAge: 7 * 24 * 60 * 60 } + ); + } +} + /* Gets value of skey cookie, returns 0 if not found. */ @@ -91,6 +123,17 @@ export function getSessionKeyValue() { return 0; } +export function setSessionKeyIfUnset(value) { + if (!isCookieSet(cookieNames.FUNNEL_SESSION_KEY)) { + setCookieProperties( + { + [cookieNames.FUNNEL_SESSION_KEY]: value, + }, + { maxAge: 30 * 60 } + ); + } +} + /* Gets value of skey cookie, returns 0 if not found. */ @@ -104,6 +147,17 @@ export function getSessionIdValue() { return "00000000-0000-0000-0000-000000000000"; } +export function setSessionIdIfUnset(value) { + if (!isCookieSet(cookieNames.SESSION_ID)) { + setCookieProperties( + { + [cookieNames.SESSION_ID]: value, + }, + { maxAge: 30 * 60 } + ); + } +} + /* Updates session ID cookie with new expiration date */ @@ -111,16 +165,6 @@ export function updateSessionIdCookie() { createOrUpdateCookie(cookieNames.SESSION_ID, getSessionIdValue(), { maxAge: 60 * 30 }); } -export function getUserIdValue() { - const cookieValue = getCookieValueByName(cookieNames.FUNNEL_USER_ID); - - if (cookieValue) { - return cookieValue; - } - - return "00000000-0000-0000-0000-000000000000"; -} - export function setCookieProperties( properties, { useDefaultFunnelCookieAttributes = true, maxAge, isSecure } @@ -142,6 +186,22 @@ export function isCookieSet(name) { return !!val; } +export function areAllSessionCookiesSet() { + return ( + isCookieSet(cookieNames.SESSION_ID) && + isCookieSet(cookieNames.DXDEV) && + isCookieSet(cookieNames.FUNNEL_SESSION_KEY) && + isCookieSet(cookieNames.FUNNEL_USER_ID) + ); +} + +export function refreshSessionExpiration() { + refreshCookieExpiration(cookieNames.SESSION_ID, 30 * 60); + refreshCookieExpiration(cookieNames.DXDEV, 365 * 24 * 60 * 60); + refreshCookieExpiration(cookieNames.FUNNEL_USER_ID, 7 * 24 * 60 * 60); + refreshCookieExpiration(cookieNames.FUNNEL_SESSION_KEY, 30 * 60); +} + export function refreshCookieExpiration(name, expirationTime) { if (isCookieSet(name)) { createOrUpdateCookie(name, getCookieValueByName(name), { maxAge: expirationTime }); diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index f793f4825..de408218c 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -6,7 +6,12 @@ import { getSessionKeyValue, getUserIdValue, isCookieSet, - refreshCookieExpiration, + regenerateDeviceId, + regenerateUserId, + refreshSessionExpiration, + areAllSessionCookiesSet, + setSessionIdIfUnset, + setSessionKeyIfUnset, } from "@/helpers/heritage-integration/cookie-helper"; import { queryStrings } from "@/constants/query-strings"; import { experimentSettings } from "@/constants/experiments"; @@ -139,22 +144,8 @@ export default { }, async initSession() { - if (!isCookieSet(cookieNames.FUNNEL_USER_ID)) { - setCookieProperties( - { - [cookieNames.FUNNEL_USER_ID]: crypto.randomUUID(), - }, - { maxAge: 7 * 24 * 60 * 60 } - ); - } - if (!isCookieSet(cookieNames.DXDEV)) { - setCookieProperties( - { - [cookieNames.DXDEV]: crypto.randomUUID(), - }, - { maxAge: 365 * 24 * 60 * 60 } - ); - } + regenerateDeviceId(); + regenerateUserId(); const userId = getUserIdValue(); // cookieNames.FUNNEL_USER_ID const deviceId = getDeviceIdValue(); // cookieNames.DXDEV @@ -178,37 +169,18 @@ export default { ); if (response?.data) { - if (response.data.sessionKey && !isCookieSet(cookieNames.FUNNEL_SESSION_KEY)) { - setCookieProperties( - { - [cookieNames.FUNNEL_SESSION_KEY]: response.data.sessionKey, - }, - { - maxAge: 30 * 60, - } - ); + if (response.data.sessionKey) { + setSessionKeyIfUnset(response.data.sessionKey); } - if (response.data.sessionId && !isCookieSet(cookieNames.SESSION_ID)) { - setCookieProperties( - { - [cookieNames.SESSION_ID]: response.data.sessionId, - }, - { - maxAge: 30 * 60, - } - ); + if (response.data.sessionId) { + setSessionIdIfUnset(response.data.sessionId); } } }, noSession() { - return !( - isCookieSet(cookieNames.SESSION_ID) && - isCookieSet(cookieNames.DXDEV) && - isCookieSet(cookieNames.FUNNEL_SESSION_KEY) && - isCookieSet(cookieNames.FUNNEL_USER_ID) - ); + return !areAllSessionCookiesSet(); }, async validateSession() { @@ -216,14 +188,7 @@ export default { await this.initSession(); } - this.refreshSessionExpiration(); - }, - - refreshSessionExpiration() { - refreshCookieExpiration(cookieNames.SESSION_ID, 30 * 60); - refreshCookieExpiration(cookieNames.DXDEV, 365 * 24 * 60 * 60); - refreshCookieExpiration(cookieNames.FUNNEL_USER_ID, 7 * 24 * 60 * 60); - refreshCookieExpiration(cookieNames.FUNNEL_SESSION_KEY, 30 * 60); + refreshSessionExpiration(); }, removeParamsFromEndpoint(endpoint) { From c6a50eb67ddaf4f13120d72ba93c99d005bd6ede Mon Sep 17 00:00:00 2001 From: hiteshkumar87 Date: Fri, 1 Dec 2023 19:07:27 +0530 Subject: [PATCH 06/36] Update confirmation.vue Back to Safelite button redirecting to environment content site. --- src/layouts/confirmation/confirmation.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layouts/confirmation/confirmation.vue b/src/layouts/confirmation/confirmation.vue index 51546a97e..32c36c161 100644 --- a/src/layouts/confirmation/confirmation.vue +++ b/src/layouts/confirmation/confirmation.vue @@ -300,7 +300,7 @@ export default { return serviceLocationReqs && scheduleReqs && customerReqs; }, forwardButtonAction() { - window.location.assign("//www.safelite.com/"); + window.location.assign(location.protocol + "//" + location.host); }, }, components: { From b4792236d8de27f0ae82329e0909afa603c8b216 Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Fri, 1 Dec 2023 08:46:50 -0500 Subject: [PATCH 07/36] CSR-1857 | Exclude promos from lineItems sent to safelite-hop --- src/layouts/payment/payment.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layouts/payment/payment.vue b/src/layouts/payment/payment.vue index 6de58dc3c..559a44ee2 100644 --- a/src/layouts/payment/payment.vue +++ b/src/layouts/payment/payment.vue @@ -532,7 +532,7 @@ export default { } cartItems.forEach((item) => { - if (item.name !== null) { + if (item.name !== null && item.category != "promos") { lineItems.push(`${item.name}|${(item.salesTax + item.subTotal).toFixed(2)}|1`); } }); From 921a3efbfd210c299562722b1305d7374d1ae5a7 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Fri, 1 Dec 2023 09:08:32 -0500 Subject: [PATCH 08/36] Define cookie TTLs as constants --- src/constants/cookie-names.js | 21 +++++++++++++++- .../heritage-integration/cookie-helper.js | 24 +++++++++++-------- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/constants/cookie-names.js b/src/constants/cookie-names.js index bf6bc130a..d62934cf5 100644 --- a/src/constants/cookie-names.js +++ b/src/constants/cookie-names.js @@ -12,4 +12,23 @@ const cookieNames = { SESSION_KEY: "skey", }; -export { cookieNames }; +const cookieExpirations = { + SESSION_ID: convertToSeconds({ minutes: 30 }), + DXDEV: convertToSeconds({ years: 1 }), + FUNNEL_USER_ID: convertToSeconds({ weeks: 1 }), + FUNNEL_SESSION_KEY: convertToSeconds({ minutes: 30 }), +}; + +export { cookieNames, cookieExpirations }; + +function convertToSeconds({ years, months, weeks, days, hours, minutes, seconds }) { + let total = seconds ?? 0; + total += (minutes ?? 0) * 60; + total += (hours ?? 0) * 60 * 60; + total += (days ?? 0) * 24 * 60 * 60; + total += (weeks ?? 0) * 7 * 24 * 60 * 60; + total += (months ?? 0) * 30 * 24 * 60 * 60; + total += (years ?? 0) * 365 * 24 * 60 * 60; + + return total; +} diff --git a/src/helpers/heritage-integration/cookie-helper.js b/src/helpers/heritage-integration/cookie-helper.js index 56736d340..50896cab2 100644 --- a/src/helpers/heritage-integration/cookie-helper.js +++ b/src/helpers/heritage-integration/cookie-helper.js @@ -1,4 +1,4 @@ -import { cookieNames } from "@/constants/cookie-names"; +import { cookieNames, cookieExpirations } from "@/constants/cookie-names"; import store from "@/store"; import { applicationConfig } from "@/constants/application-config"; @@ -84,7 +84,7 @@ export function regenerateDeviceId() { { [cookieNames.DXDEV]: `did=${crypto.randomUUID()}`, }, - { maxAge: 365 * 24 * 60 * 60 } + { maxAge: cookieExpirations.DXDEV } ); } } @@ -105,7 +105,7 @@ export function regenerateUserId() { { [cookieNames.FUNNEL_USER_ID]: crypto.randomUUID(), }, - { maxAge: 7 * 24 * 60 * 60 } + { maxAge: cookieExpirations.FUNNEL_USER_ID } ); } } @@ -129,7 +129,7 @@ export function setSessionKeyIfUnset(value) { { [cookieNames.FUNNEL_SESSION_KEY]: value, }, - { maxAge: 30 * 60 } + { maxAge: cookieExpirations.FUNNEL_SESSION_KEY } ); } } @@ -153,7 +153,7 @@ export function setSessionIdIfUnset(value) { { [cookieNames.SESSION_ID]: value, }, - { maxAge: 30 * 60 } + { maxAge: cookieExpirations.SESSION_ID } ); } } @@ -162,7 +162,9 @@ export function setSessionIdIfUnset(value) { Updates session ID cookie with new expiration date */ export function updateSessionIdCookie() { - createOrUpdateCookie(cookieNames.SESSION_ID, getSessionIdValue(), { maxAge: 60 * 30 }); + createOrUpdateCookie(cookieNames.SESSION_ID, getSessionIdValue(), { + maxAge: cookieExpirations.SESSION_ID, + }); } export function setCookieProperties( @@ -196,10 +198,12 @@ export function areAllSessionCookiesSet() { } export function refreshSessionExpiration() { - refreshCookieExpiration(cookieNames.SESSION_ID, 30 * 60); - refreshCookieExpiration(cookieNames.DXDEV, 365 * 24 * 60 * 60); - refreshCookieExpiration(cookieNames.FUNNEL_USER_ID, 7 * 24 * 60 * 60); - refreshCookieExpiration(cookieNames.FUNNEL_SESSION_KEY, 30 * 60); + console.log(cookieExpirations); + + refreshCookieExpiration(cookieNames.SESSION_ID, cookieExpirations.SESSION_ID); + refreshCookieExpiration(cookieNames.DXDEV, cookieExpirations.DXDEV); + refreshCookieExpiration(cookieNames.FUNNEL_USER_ID, cookieExpirations.FUNNEL_USER_ID); + refreshCookieExpiration(cookieNames.FUNNEL_SESSION_KEY, cookieExpirations.FUNNEL_SESSION_KEY); } export function refreshCookieExpiration(name, expirationTime) { From 999d274d88a9e632fa7e0242796c9bcd6b410311 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Fri, 1 Dec 2023 09:11:10 -0500 Subject: [PATCH 09/36] Cleanup imports --- src/mixins/analytics-mixin.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index de408218c..7b7a2d762 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -1,11 +1,9 @@ import { storeActions } from "@/constants/store-actions"; import { - setCookieProperties, getDeviceIdValue, getSessionIdValue, getSessionKeyValue, getUserIdValue, - isCookieSet, regenerateDeviceId, regenerateUserId, refreshSessionExpiration, @@ -23,7 +21,6 @@ import { GaEvents, ValueToLogTypes, } from "@/constants/analytics"; -import { cookieNames } from "@/constants/cookie-names"; import store from "@/store"; import baseMixin from "@/mixins/base-mixin"; From 648a43ba81dd21458960064d5bd60f5379622749 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Fri, 1 Dec 2023 09:38:24 -0500 Subject: [PATCH 10/36] Remove log --- src/helpers/heritage-integration/cookie-helper.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/helpers/heritage-integration/cookie-helper.js b/src/helpers/heritage-integration/cookie-helper.js index 50896cab2..abb1c0a67 100644 --- a/src/helpers/heritage-integration/cookie-helper.js +++ b/src/helpers/heritage-integration/cookie-helper.js @@ -198,8 +198,6 @@ export function areAllSessionCookiesSet() { } export function refreshSessionExpiration() { - console.log(cookieExpirations); - refreshCookieExpiration(cookieNames.SESSION_ID, cookieExpirations.SESSION_ID); refreshCookieExpiration(cookieNames.DXDEV, cookieExpirations.DXDEV); refreshCookieExpiration(cookieNames.FUNNEL_USER_ID, cookieExpirations.FUNNEL_USER_ID); From 411976efe59d8dfdaf64b1674ade2657dff3003c Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Fri, 1 Dec 2023 09:38:40 -0500 Subject: [PATCH 11/36] Use correct deviceId in initialize --- src/store/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/store/index.js b/src/store/index.js index 684ca83e1..55a2ba120 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1151,11 +1151,11 @@ export const actions = { } ); }, - initializeSession(context, { userId, sessionId, userAgent, referrer }) { + initializeSession(context, { userId, deviceId, sessionId, userAgent, referrer }) { var payload = { applicationName: applicationConfig.ANALYTICS_APPLICATION_NAME, userId: userId, - deviceId: userId, + deviceId: deviceId, sessionId: sessionId, userAgent: userAgent, operatorId: "WEB", From 53aed87e1d814a2b15238a7ba7c983b0da688a33 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Fri, 1 Dec 2023 09:42:11 -0500 Subject: [PATCH 12/36] Use correct userId for analytics logging --- src/mixins/analytics-mixin.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 7b7a2d762..8dd0c7f96 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -35,7 +35,7 @@ export default { logPageView(pageEvent) { const currentPageName = getPageNameByQueryString(); var payload = { - userId: getDeviceIdValue(), + userId: getUserIdValue(), sessionKey: getSessionKeyValue(), pageName: currentPageName, sessionId: getSessionIdValue(), @@ -54,7 +54,7 @@ export default { const currentPageName = getPageNameByQueryString(); var payload = { - userId: getDeviceIdValue(), + userId: getUserIdValue(), sessionKey: getSessionKeyValue(), pageName: currentPageName, sessionId: getSessionIdValue(), From ad5014ac6f4fc1f2c6431bc4cf844288506bbb7e Mon Sep 17 00:00:00 2001 From: Bryan Mauger Date: Fri, 1 Dec 2023 10:29:51 -0500 Subject: [PATCH 13/36] CSR-1819 remove unneeded accessibility spans. --- src/fmg-components/cart/cart.vue | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index d5ef09542..4e6aeb1da 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -16,8 +16,7 @@
- - {{ screenReaderPackagePriceText }} + {{ getFormattedAmount("", packagePrice) }}
@@ -64,39 +63,29 @@ {{ recycleFeeCartItem.name }} - {{ screenReaderRecycleFeeText }}{{ getFormattedAmount(cartItem.category, cartItem.subTotal) }}{{ getFormattedAmount(cartItem.category, cartItem.subTotal) }}
{{ subtotalText }}{{ screenReaderSubTotalText }}{{ getFormattedAmount("", subTotal) }}{{ getFormattedAmount("", subTotal) }}
{{ salesTaxText }}{{ screenReaderSalesTaxText }}{{ getFormattedAmount("", salesTax) }}{{ getFormattedAmount("", salesTax) }}
{{ amountPaidText }}{{ screenReaderAmountPaidText }}{{ getFormattedAmount("", amountPaid) }}{{ getFormattedAmount("", amountPaid) }}
{{ amountDueText }}{{ screenReaderTotalAmountDueText }}{{ getFormattedAmount("", amountDue) }}{{ getFormattedAmount("", amountDue) }}
@@ -136,6 +125,7 @@ export default { allowItemRemoval: Boolean, recyclingModalCmsWidgetName: String, showAsPaid: Boolean, + cmsWidgetName: String, }, data() { return { From c2996187bbed7145e2e5defef49dc1a1b877f3de Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Fri, 1 Dec 2023 10:30:15 -0500 Subject: [PATCH 14/36] Ensure session state is valid before logging --- src/mixins/analytics-mixin.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 8dd0c7f96..dcadb3f07 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -32,8 +32,10 @@ export default { return getPageNameByQueryString(); }, - logPageView(pageEvent) { + async logPageView(pageEvent) { const currentPageName = getPageNameByQueryString(); + await this.validateSession(); + var payload = { userId: getUserIdValue(), sessionKey: getSessionKeyValue(), @@ -50,8 +52,9 @@ export default { baseMixin.methods.dispatchStoreAction(storeActions.LOG_PAGE_VIEW, payload, false); }, - logCustomEvent(category, action, label, value) { + async logCustomEvent(category, action, label, value) { const currentPageName = getPageNameByQueryString(); + await this.validateSession(); var payload = { userId: getUserIdValue(), From 088060a354d5217bb83f9fc6d622760066c3d131 Mon Sep 17 00:00:00 2001 From: Bryan Mauger Date: Fri, 1 Dec 2023 10:31:30 -0500 Subject: [PATCH 15/36] Remove unneeded prop. --- src/fmg-components/cart/cart.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index 4e6aeb1da..29ce7aca8 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -125,7 +125,6 @@ export default { allowItemRemoval: Boolean, recyclingModalCmsWidgetName: String, showAsPaid: Boolean, - cmsWidgetName: String, }, data() { return { From 42d60f4e22f29c8a0208bd6fe4da56098e153da9 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Fri, 1 Dec 2023 11:32:33 -0500 Subject: [PATCH 16/36] Update existing unit test suite --- src/helpers/unit-test-helper.js | 3 +- src/mixins/analytics-mixin.js | 16 ++++--- src/mixins/analytics-mixin.spec.js | 72 +++++++++++++++++++++++++----- 3 files changed, 74 insertions(+), 17 deletions(-) diff --git a/src/helpers/unit-test-helper.js b/src/helpers/unit-test-helper.js index 467b38c2b..123dc9d40 100644 --- a/src/helpers/unit-test-helper.js +++ b/src/helpers/unit-test-helper.js @@ -100,7 +100,8 @@ export const cookies = { someOtherCookie: "{}", dxdev: "did=21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe", sid: "cba0c3d1-3c1b-4305-bb56-31aa50f58e27", - skey: "12345", + [cookieNames.FUNNEL_SESSION_KEY]: "12345", + [cookieNames.FUNNEL_USER_ID]: "11aec5e8-92ba-4dc9-a8b6-179a916d8d7a", }; // Removes test cookies for testing cookie-helper and order-helper diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index dcadb3f07..6ea36a903 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -49,7 +49,7 @@ export default { parentAccountNumber: store.getters.order.payment.parentAccountNumber, }; - baseMixin.methods.dispatchStoreAction(storeActions.LOG_PAGE_VIEW, payload, false); + await baseMixin.methods.dispatchStoreAction(storeActions.LOG_PAGE_VIEW, payload, false); }, async logCustomEvent(category, action, label, value) { @@ -71,10 +71,14 @@ export default { parentAccountNumber: store.getters.order.payment.parentAccountNumber, }; - baseMixin.methods.dispatchStoreAction(storeActions.LOG_CUSTOM_EVENT, payload, false); + await baseMixin.methods.dispatchStoreAction( + storeActions.LOG_CUSTOM_EVENT, + payload, + false + ); }, - pushEventToGA(category, action, label, pushToLogApp = false, valueToLogType = null) { + async pushEventToGA(category, action, label, pushToLogApp = false, valueToLogType = null) { const currentPageName = getPageNameByQueryString(); const labelToLog = getValueToLog(label, valueToLogType); @@ -90,11 +94,11 @@ export default { pushToDataLayerIfDefined(eventToBePushed); if (pushToLogApp) { - this.logCustomEvent(category, action, labelToLog, undefined); + await this.logCustomEvent(category, action, labelToLog, undefined); } }, - pushPageViewToGA() { + async pushPageViewToGA() { const currentPageName = getPageNameByQueryString(); const pageViewEvent = { event: GaEvents.PAGE_VIEW_EVENT, @@ -104,7 +108,7 @@ export default { pushToDataLayerIfDefined(pageViewEvent); - this.logPageView(analyticsPageEvents.ENTRY); + await this.logPageView(analyticsPageEvents.ENTRY); }, pushExperimentsToDataLayer() { diff --git a/src/mixins/analytics-mixin.spec.js b/src/mixins/analytics-mixin.spec.js index 0426ca297..8f7cfceaa 100644 --- a/src/mixins/analytics-mixin.spec.js +++ b/src/mixins/analytics-mixin.spec.js @@ -11,8 +11,20 @@ import { } from "@/constants/analytics"; import store from "@/store"; +// Mock only the regenerate functions; window.crypto is not available in testing. +jest.mock("@/helpers/heritage-integration/cookie-helper", () => { + const originalModule = jest.requireActual("@/helpers/heritage-integration/cookie-helper"); + + return { + __esModule: true, + ...originalModule, + regenerateDeviceId: jest.fn(), + regenerateUserId: jest.fn(), + }; +}); + describe("analyticsMixin.js", () => { - test("logPageView: calls dispatch with type and payload", () => { + test("logPageView: calls dispatch with type and payload", async () => { const type = ""; const payload = {}; @@ -21,6 +33,9 @@ describe("analyticsMixin.js", () => { { actionName: storeActions.LOG_PAGE_VIEW, }, + { + actionName: storeActions.INITIALIZE_SESSION, + }, ], }; const mocks = setupMocksForJsFiles(mockData); @@ -31,27 +46,35 @@ describe("analyticsMixin.js", () => { setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) }); - analyticsMixin.methods.logPageView(type, payload); + await analyticsMixin.methods.logPageView(type, payload); expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled(); }); - test("logCustomEvent: calls dispatch with type and payload", () => { + test("logCustomEvent: calls dispatch with type and payload", async () => { const mockData = { actionList: [ { actionName: storeActions.LOG_CUSTOM_EVENT, }, + { + actionName: storeActions.INITIALIZE_SESSION, + }, ], }; const mocks = setupMocksForJsFiles(mockData); - analyticsMixin.methods.logCustomEvent("someCat", "someAction", "someLabel", "someVal"); + await analyticsMixin.methods.logCustomEvent( + "someCat", + "someAction", + "someLabel", + "someVal" + ); expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled(); }); - test("pushEventToGA, should call dataLayer push and logCustomEvent too", () => { + test("pushEventToGA, should call dataLayer push and logCustomEvent too", async () => { // Arrange window.dataLayer = []; const mockData = { @@ -59,6 +82,9 @@ describe("analyticsMixin.js", () => { { actionName: storeActions.LOG_CUSTOM_EVENT, }, + { + actionName: storeActions.INITIALIZE_SESSION, + }, ], }; const mocks = setupMocksForJsFiles(mockData); @@ -73,14 +99,14 @@ 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)); expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled(); }); - 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 = []; var expectedDataLayer = []; @@ -93,8 +119,21 @@ describe("analyticsMixin.js", () => { path: "/fmg/?fmgPage=", }); + const mockData = { + actionList: [ + { + actionName: storeActions.LOG_CUSTOM_EVENT, + }, + { + actionName: storeActions.INITIALIZE_SESSION, + }, + ], + }; + + const mocks = setupMocksForJsFiles(mockData); + // Act - analyticsMixin.methods.pushEventToGA( + await analyticsMixin.methods.pushEventToGA( "category", "action", "1111122222333333", @@ -106,7 +145,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 = []; var expectedDataLayer = []; @@ -119,8 +158,21 @@ describe("analyticsMixin.js", () => { path: "/fmg/?fmgPage=", }); + const mockData = { + actionList: [ + { + actionName: storeActions.LOG_CUSTOM_EVENT, + }, + { + actionName: storeActions.INITIALIZE_SESSION, + }, + ], + }; + + const mocks = setupMocksForJsFiles(mockData); + // Act - analyticsMixin.methods.pushEventToGA( + await analyticsMixin.methods.pushEventToGA( "category", "action", "111", From 2106738f5d9a7e3f3c9898bb7fbbb2bed0138f73 Mon Sep 17 00:00:00 2001 From: Bryan Mauger Date: Fri, 1 Dec 2023 13:30:32 -0500 Subject: [PATCH 17/36] Format code. --- src/fmg-components/cart/cart.vue | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index 29ce7aca8..cd50a8fda 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -63,30 +63,25 @@ {{ recycleFeeCartItem.name }} - {{ getFormattedAmount(cartItem.category, cartItem.subTotal) }} + {{ getFormattedAmount(cartItem.category, cartItem.subTotal) }}
{{ subtotalText }}{{ getFormattedAmount("", subTotal) }} + >{{ getFormattedAmount("", subTotal) }}
{{ salesTaxText }}{{ getFormattedAmount("", salesTax) }} + >{{ getFormattedAmount("", salesTax) }}
{{ amountPaidText }}{{ getFormattedAmount("", amountPaid) }} + >{{ getFormattedAmount("", amountPaid) }}
{{ amountDueText }}{{ getFormattedAmount("", amountDue) }} + >{{ getFormattedAmount("", amountDue) }}
From 3cd95431ff9caf77c186bab110cc6f339b465318 Mon Sep 17 00:00:00 2001 From: CarlNation <32103961+CarlNation@users.noreply.github.com> Date: Fri, 1 Dec 2023 14:39:21 -0500 Subject: [PATCH 18/36] CSR-1858 ctu corrections --- .../mobile-location-modal-questions.vue | 1 + .../service-location/service-location.vue | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue b/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue index 8bac8dbb4..762af0d72 100644 --- a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue +++ b/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue @@ -252,6 +252,7 @@ export default { this.$emit("updated-mobile-fee-part", mobileFeePart); this.$emit("updated-serviceability", serviceabilityDetails.data); this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase); + this.$emit("updated-mobile-ctu", zipCodeData.zipCodeCtu); // update the page level model this.$emit("update:modelValue", this.internalModel); diff --git a/src/layouts/service-location/service-location.vue b/src/layouts/service-location/service-location.vue index 0a74ab21e..1c25b8f40 100644 --- a/src/layouts/service-location/service-location.vue +++ b/src/layouts/service-location/service-location.vue @@ -83,6 +83,7 @@ @updated-mobile-fee-part="setMobileFeePart" @updated-serviceability="setServiceabilityDetails" @updated-contains-military-base="setContainsMilitaryBase" + @updated-mobile-ctu="setCtuForMobile" validationRules="mobile-location-required" ref="mobileLocationQuestions" linkWidgetName="MobileLocationLinkWidget" @@ -128,6 +129,7 @@ import contentGroupModal from "@/fmg-components/content-group-modal/content-grou // Supporting files import baseMixin from "@/mixins/base-mixin.js"; +import { AppointmentTypeStrings } from "@/constants/schedule-constants"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { settleAllPromises } from "@/helpers/layout-helper"; @@ -374,6 +376,9 @@ export default { this.zipContainsMilitaryBase = val; } }, + setCtuForMobile(val) { + this.zipCodeCtu = val; + }, setMobileFeePart(mobileFeePart) { this.mobileFeePart = mobileFeePart; }, @@ -472,6 +477,28 @@ export default { } }, async forwardButtonAction() { + // clear items not needed for appointmentType + if (this.selectedAppointmentType == AppointmentTypeStrings.IN_SHOP) { + // if we have a provider.zipCodeCtu that's different than the servicelocation.zipCodeCtu then they + // may have selected a shop in a different ctu. change the servicelocation zip/ctu if different + if (this.zipCodeCtu != this.selectedProvider.address.zipCodeCtu) { + this.zipCodeCtu = this.selectedProvider.address.zipCodeCtu; + this.zipCode = this.selectedProvider.address.zipCode; + } + + this.city = null; + this.streetAddress = null; + } + + if (this.selectedAppointmentType == AppointmentTypeStrings.MOBILE && + this.selectedProvider && this.selectedProvider.address) { + this.selectedProvider.address.streetAddress = null; + this.selectedProvider.address.city = null; + this.selectedProvider.address.state = null; + this.selectedProvider.address.zipCode = null; + this.selectedProvider.address.zipCodeCtu = null; + } + await this.dispatchStoreAction( this.storeActions.SAVE_SERVICE_LOCATION, { From 7783d233d2909f0f618b417d89c41a71779cec44 Mon Sep 17 00:00:00 2001 From: CarlNation Date: Fri, 1 Dec 2023 14:43:03 -0500 Subject: [PATCH 19/36] csr-1853 prettier --- src/layouts/service-location/service-location.vue | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/layouts/service-location/service-location.vue b/src/layouts/service-location/service-location.vue index 1c25b8f40..e6cb2138e 100644 --- a/src/layouts/service-location/service-location.vue +++ b/src/layouts/service-location/service-location.vue @@ -479,7 +479,7 @@ export default { async forwardButtonAction() { // clear items not needed for appointmentType if (this.selectedAppointmentType == AppointmentTypeStrings.IN_SHOP) { - // if we have a provider.zipCodeCtu that's different than the servicelocation.zipCodeCtu then they + // if we have a provider.zipCodeCtu that's different than the servicelocation.zipCodeCtu then they // may have selected a shop in a different ctu. change the servicelocation zip/ctu if different if (this.zipCodeCtu != this.selectedProvider.address.zipCodeCtu) { this.zipCodeCtu = this.selectedProvider.address.zipCodeCtu; @@ -490,8 +490,11 @@ export default { this.streetAddress = null; } - if (this.selectedAppointmentType == AppointmentTypeStrings.MOBILE && - this.selectedProvider && this.selectedProvider.address) { + if ( + this.selectedAppointmentType == AppointmentTypeStrings.MOBILE && + this.selectedProvider && + this.selectedProvider.address + ) { this.selectedProvider.address.streetAddress = null; this.selectedProvider.address.city = null; this.selectedProvider.address.state = null; From 82e2e041def20be3794b1140414504cd3ed94166 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Fri, 1 Dec 2023 15:27:05 -0500 Subject: [PATCH 20/36] Fix typo --- src/mixins/analytics-mixin.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 6ea36a903..d3fa4d0dd 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -155,7 +155,7 @@ export default { const deviceId = getDeviceIdValue(); // cookieNames.DXDEV const sessionId = getSessionIdValue(); // cookieNames.SESSION_ID const userAgent = navigator.userAgent; // navigator.userAgent - const refferer = + const referrer = applicationConfig.CURRENT_ENVIRONMENT != "Localhost" ? document.referrer : null; // see above const payload = { @@ -163,7 +163,7 @@ export default { deviceId: deviceId, sessionId: sessionId, userAgent: userAgent, - refferer: refferer, + referrer: referrer, }; const response = await baseMixin.methods.dispatchStoreAction( From 0630faa6a5d4d0b01931c1b34ddfc0e39a0e2b2b Mon Sep 17 00:00:00 2001 From: CarlNation Date: Tue, 5 Dec 2023 05:31:10 -0500 Subject: [PATCH 21/36] CSR-1867 introduced by defect CSR-1858 needing to reset zipcode and ctu when selecting a location in a different ctu. fix is to not execute zipcode watch logic while navigating forward. --- .../service-location/service-location.vue | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/layouts/service-location/service-location.vue b/src/layouts/service-location/service-location.vue index e6cb2138e..bfec46932 100644 --- a/src/layouts/service-location/service-location.vue +++ b/src/layouts/service-location/service-location.vue @@ -183,6 +183,7 @@ export default { zipContainsMilitaryBase: false, zipCodeCtu: null, shopProviderData: null, + navigatingForward: false, }; }, async beforeRouteEnter(to, from, next) { @@ -477,6 +478,8 @@ export default { } }, async forwardButtonAction() { + this.navigatingForward = true; + // clear items not needed for appointmentType if (this.selectedAppointmentType == AppointmentTypeStrings.IN_SHOP) { // if we have a provider.zipCodeCtu that's different than the servicelocation.zipCodeCtu then they @@ -537,16 +540,18 @@ export default { watch: { zipCode: { handler(newValue) { - getShopProviderData(this.zipCode).then(async (result) => { - this.shopProviderData = result.data; - if (this.selectedAppointmentType === "Mobile") { - this.selectedProvider = new Provider( - this.shopProviderData.mobileProviderNumber - ); - } else { - this.selectedProvider = new Provider(); - } - }); + if (!this.navigatingForward) { + getShopProviderData(this.zipCode).then(async (result) => { + this.shopProviderData = result.data; + if (this.selectedAppointmentType === "Mobile") { + this.selectedProvider = new Provider( + this.shopProviderData.mobileProviderNumber + ); + } else { + this.selectedProvider = new Provider(); + } + }); + } }, }, selectedAppointmentType: { From 25f56e51c4dfe9e2491940bace7a52577e0d8414 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Tue, 5 Dec 2023 09:31:08 -0500 Subject: [PATCH 22/36] Remove unneeded damage check --- src/store/index.js | 10 +--------- src/store/store.spec.js | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/store/index.js b/src/store/index.js index 574df8012..a094a8a15 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -2495,15 +2495,7 @@ export const actions = { isVinOptionalVehicle(context) { //Optional for carIds with only a single windshield - if ( - singleWindshieldCarIds.find((item) => item === context.state.order.vehicle.carId) && - context.state.order.damage.glassToReplace.length == 1 && - context.state.order.damage.glassToReplace.find( - (glassToReplace) => - glassToReplace.glassLocation.toLowerCase() === - damageLocationsSelected.WINDSHIELD.toLowerCase() - ) - ) { + if (singleWindshieldCarIds.find((item) => item === context.state.order.vehicle.carId)) { return true; } //Optional for specific YMMSs diff --git a/src/store/store.spec.js b/src/store/store.spec.js index e7c004090..8353ec0d8 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -3329,7 +3329,7 @@ describe("isVinOptionalVehicle", () => { "CR00062396", "make3", [{ glassLocation: "windshield" }, { glassLocation: "driver" }], - false, + true, ], ["CR00066428", "make4", [{ glassLocation: "rear" }], false], ]; From 57d2b4ee0ee3833ee13fd2ab6c7d029798bb6a81 Mon Sep 17 00:00:00 2001 From: Bryan Mauger Date: Tue, 5 Dec 2023 09:38:11 -0500 Subject: [PATCH 23/36] CSR-1860 fix styling for select input. --- public/css/hop-styling.css | 2 ++ public/scss/hop-styling.scss | 2 ++ 2 files changed, 4 insertions(+) diff --git a/public/css/hop-styling.css b/public/css/hop-styling.css index cd94e3024..68e556d53 100644 --- a/public/css/hop-styling.css +++ b/public/css/hop-styling.css @@ -73,6 +73,8 @@ body select { background-position: 95% 50%; background-size: 15px 9px; appearance: none; + font-size: 16px; + color: #000; } body .creditCardSpecific div { padding: 8px 0; diff --git a/public/scss/hop-styling.scss b/public/scss/hop-styling.scss index 5f21c46dd..ed8e8e14c 100644 --- a/public/scss/hop-styling.scss +++ b/public/scss/hop-styling.scss @@ -85,6 +85,8 @@ body { background-position: 95% 50%; background-size: 15px 9px; appearance: none; + font-size: 16px; + color: #000; } .creditCardSpecific { From f45d73e297dfaf979a75dc40ca8beb584263f7fd Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Tue, 5 Dec 2023 10:49:05 -0500 Subject: [PATCH 24/36] Tests for deviceID --- .../cookie-helper.spec.js | 91 +++++++++++++++---- src/helpers/unit-test-helper.js | 8 ++ 2 files changed, 83 insertions(+), 16 deletions(-) diff --git a/src/helpers/heritage-integration/cookie-helper.spec.js b/src/helpers/heritage-integration/cookie-helper.spec.js index 5df2ae925..8abd265e5 100644 --- a/src/helpers/heritage-integration/cookie-helper.spec.js +++ b/src/helpers/heritage-integration/cookie-helper.spec.js @@ -3,10 +3,19 @@ import { getDeviceIdValue, getSessionKeyValue, getSessionIdValue, + setCookieProperties, + regenerateDeviceId, } from "@/helpers/heritage-integration/cookie-helper.js"; -import { removeAllTestCookies, setupCookies } from "@/helpers/unit-test-helper"; +import { cookieNames } from "@/constants/cookie-names"; +import { removeAllTestCookies, setupCookies, setupCrypto } from "@/helpers/unit-test-helper"; + +const randomUUID = "68d89736-c277-46f1-8fee-c3dacdb23c08"; describe("cookies", () => { + beforeEach(() => { + setupCrypto(randomUUID); + }); + afterEach(() => { removeAllTestCookies(); }); @@ -106,30 +115,80 @@ describe("cookies", () => { }); }); - describe("getDeviceIdValue", () => { - test("getDeviceIdValue, should return GUID", () => { - // Arrange - setupCookies({}); + describe("Device Id", () => { + describe("getDeviceIdValue", () => { + test("getDeviceIdValue, should return GUID", () => { + // Arrange + setupCookies({}); - // Act - const result = getDeviceIdValue(); + // Act + const result = getDeviceIdValue(); - //Assert - expect(result).toBe("21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe"); + //Assert + expect(result).toBe("21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe"); + }); + + test("Should return 0s if unset", () => { + //Arrange + //Act + const result = getDeviceIdValue(); + + //Assert + expect(result).toBe("00000000-0000-0000-0000-000000000000"); + }); + + test("Should return id even if cookie contains other data", () => { + //Arrange + setCookieProperties({ + [cookieNames.DXDEV]: "did=f4a1a9e8-b3f3-4936-8c30-2f06a98644af&tz=-300&tzd=1", + }, {}); + + //Act + const result = getDeviceIdValue(); + + //Assert + expect(result).toBe("f4a1a9e8-b3f3-4936-8c30-2f06a98644af"); + }); }); - test("getSessionKeyValue, should return session key int", () => { - // Arrange - setupCookies({}); + describe("regenerateDeviceId", () => { + test("Generates a new id if unset", () => { + // Arrange + // Act + regenerateDeviceId(); + const result = getDeviceIdValue(); - // Act - const result = getSessionKeyValue(); + // Assert + expect(result).not.toBe("00000000-0000-0000-0000-000000000000"); + expect(global.crypto.randomUUID).toBeCalled(); + }); - //Assert - expect(result).toBe("12345"); + test("Does not create a new id if already set", () => { + // Arrange + setupCookies({}); + + // Act + regenerateDeviceId(); + const result = getDeviceIdValue(); + + // Assert + expect(result).toBe("21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe"); + expect(global.crypto.randomUUID).not.toBeCalled(); + }); }); }); + test("getSessionKeyValue, should return session key int", () => { + // Arrange + setupCookies({}); + + // Act + const result = getSessionKeyValue(); + + //Assert + expect(result).toBe("12345"); + }); + describe("getSessionIdValue", () => { test("getSessionIdValue, should return GUID", () => { // Arrange diff --git a/src/helpers/unit-test-helper.js b/src/helpers/unit-test-helper.js index 123dc9d40..457dd47fb 100644 --- a/src/helpers/unit-test-helper.js +++ b/src/helpers/unit-test-helper.js @@ -139,6 +139,14 @@ export function setupCookies({ funnelCookieValue = "", includeHeritageCookie = t }); } +export function setupCrypto(mockValue) { + global.crypto = { + randomUUID: jest.fn() + }; + + global.crypto.randomUUID.mockImplementation(() => mockValue); +} + // Private methods function setupBaseMixinDispatchStoreAction(mockData) { if (mockData.actionList !== undefined) { From aa59d955cb1a5faace71bf34286cfedfd6428e4b Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Tue, 5 Dec 2023 11:35:51 -0500 Subject: [PATCH 25/36] UserId + Formatting --- .../cookie-helper.spec.js | 62 ++++++++++++++++++- src/helpers/unit-test-helper.js | 2 +- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/helpers/heritage-integration/cookie-helper.spec.js b/src/helpers/heritage-integration/cookie-helper.spec.js index 8abd265e5..349b2206a 100644 --- a/src/helpers/heritage-integration/cookie-helper.spec.js +++ b/src/helpers/heritage-integration/cookie-helper.spec.js @@ -5,6 +5,8 @@ import { getSessionIdValue, setCookieProperties, regenerateDeviceId, + getUserIdValue, + regenerateUserId, } from "@/helpers/heritage-integration/cookie-helper.js"; import { cookieNames } from "@/constants/cookie-names"; import { removeAllTestCookies, setupCookies, setupCrypto } from "@/helpers/unit-test-helper"; @@ -139,9 +141,13 @@ describe("cookies", () => { test("Should return id even if cookie contains other data", () => { //Arrange - setCookieProperties({ - [cookieNames.DXDEV]: "did=f4a1a9e8-b3f3-4936-8c30-2f06a98644af&tz=-300&tzd=1", - }, {}); + setCookieProperties( + { + [cookieNames.DXDEV]: + "did=f4a1a9e8-b3f3-4936-8c30-2f06a98644af&tz=-300&tzd=1", + }, + {} + ); //Act const result = getDeviceIdValue(); @@ -178,6 +184,56 @@ describe("cookies", () => { }); }); + describe("User Id", () => { + describe("getUserIdValue", () => { + test("Should return GUID", () => { + // Arrange + setupCookies({}); + + // Act + const result = getUserIdValue(); + + //Assert + expect(result).toBe("11aec5e8-92ba-4dc9-a8b6-179a916d8d7a"); + }); + + test("Should return 0s if unset", () => { + //Arrange + //Act + const result = getUserIdValue(); + + //Assert + expect(result).toBe("00000000-0000-0000-0000-000000000000"); + }); + }); + + describe("regenerateUserId", () => { + test("Generates a new id if unset", () => { + // Arrange + // Act + regenerateUserId(); + const result = getUserIdValue(); + + // Assert + expect(result).not.toBe("00000000-0000-0000-0000-000000000000"); + expect(global.crypto.randomUUID).toBeCalled(); + }); + + test("Does not create a new id if already set", () => { + // Arrange + setupCookies({}); + + // Act + regenerateUserId(); + const result = getUserIdValue(); + + // Assert + expect(result).toBe("11aec5e8-92ba-4dc9-a8b6-179a916d8d7a"); + expect(global.crypto.randomUUID).not.toBeCalled(); + }); + }); + }); + test("getSessionKeyValue, should return session key int", () => { // Arrange setupCookies({}); diff --git a/src/helpers/unit-test-helper.js b/src/helpers/unit-test-helper.js index 457dd47fb..2cd580df0 100644 --- a/src/helpers/unit-test-helper.js +++ b/src/helpers/unit-test-helper.js @@ -141,7 +141,7 @@ export function setupCookies({ funnelCookieValue = "", includeHeritageCookie = t export function setupCrypto(mockValue) { global.crypto = { - randomUUID: jest.fn() + randomUUID: jest.fn(), }; global.crypto.randomUUID.mockImplementation(() => mockValue); From 736aee68e990e82d5f862890661fc7c84ab396d3 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Tue, 5 Dec 2023 11:46:31 -0500 Subject: [PATCH 26/36] Session Key --- .../cookie-helper.spec.js | 54 ++++++++++++++++--- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/src/helpers/heritage-integration/cookie-helper.spec.js b/src/helpers/heritage-integration/cookie-helper.spec.js index 349b2206a..c5cbc5bee 100644 --- a/src/helpers/heritage-integration/cookie-helper.spec.js +++ b/src/helpers/heritage-integration/cookie-helper.spec.js @@ -7,6 +7,7 @@ import { regenerateDeviceId, getUserIdValue, regenerateUserId, + setSessionKeyIfUnset, } from "@/helpers/heritage-integration/cookie-helper.js"; import { cookieNames } from "@/constants/cookie-names"; import { removeAllTestCookies, setupCookies, setupCrypto } from "@/helpers/unit-test-helper"; @@ -234,15 +235,54 @@ describe("cookies", () => { }); }); - test("getSessionKeyValue, should return session key int", () => { - // Arrange - setupCookies({}); + describe("Session Key", () => { + describe("getSessionKeyValue", () => { + test("Should return session key int", () => { + // Arrange + setupCookies({}); - // Act - const result = getSessionKeyValue(); + // Act + const result = getSessionKeyValue(); - //Assert - expect(result).toBe("12345"); + //Assert + expect(result).toBe("12345"); + }); + + test("Should return 0 if unset", () => { + // Arrange + + // Act + const result = getSessionKeyValue(); + + //Assert + expect(result).toBe(0); + }); + }); + + describe("setSessionKeyIfUnset", () => { + test("Should set the cookie if not previously set", () => { + // Arrange + + // Act + setSessionKeyIfUnset("54321"); + const result = getSessionKeyValue(); + + // Assert + expect(result).toBe("54321"); + }); + + test("Should not set the cookie if previously set", () => { + // Arrange + setupCookies({}); + + // Act + setSessionKeyIfUnset("54321"); + const result = getSessionKeyValue(); + + // Assert + expect(result).toBe("12345"); + }); + }); }); describe("getSessionIdValue", () => { From 1f0a0870c932fd2511129c4356c9695068c78220 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Tue, 5 Dec 2023 12:40:37 -0500 Subject: [PATCH 27/36] Session Id --- .../cookie-helper.spec.js | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/helpers/heritage-integration/cookie-helper.spec.js b/src/helpers/heritage-integration/cookie-helper.spec.js index c5cbc5bee..99a502559 100644 --- a/src/helpers/heritage-integration/cookie-helper.spec.js +++ b/src/helpers/heritage-integration/cookie-helper.spec.js @@ -8,6 +8,7 @@ import { getUserIdValue, regenerateUserId, setSessionKeyIfUnset, + setSessionIdIfUnset, } from "@/helpers/heritage-integration/cookie-helper.js"; import { cookieNames } from "@/constants/cookie-names"; import { removeAllTestCookies, setupCookies, setupCrypto } from "@/helpers/unit-test-helper"; @@ -285,6 +286,56 @@ describe("cookies", () => { }); }); + describe("Session Id", () => { + describe("getSessionIdValue", () => { + test("Should return session id", () => { + // Arrange + setupCookies({}); + + // Act + const result = getSessionIdValue(); + + //Assert + expect(result).toBe("cba0c3d1-3c1b-4305-bb56-31aa50f58e27"); + }); + + test("Should return 0s if unset", () => { + // Arrange + + // Act + const result = getSessionIdValue(); + + //Assert + expect(result).toBe("00000000-0000-0000-0000-000000000000"); + }); + }); + + describe("setSessionIdIfUnset", () => { + test("Should set the cookie if not previously set", () => { + // Arrange + + // Act + setSessionIdIfUnset("f01f463a-a02c-4d1a-8aaf-6ca920ae5f02"); + const result = getSessionIdValue(); + + // Assert + expect(result).toBe("f01f463a-a02c-4d1a-8aaf-6ca920ae5f02"); + }); + + test("Should not set the cookie if previously set", () => { + // Arrange + setupCookies({}); + + // Act + setSessionIdIfUnset("f01f463a-a02c-4d1a-8aaf-6ca920ae5f02"); + const result = getSessionIdValue(); + + // Assert + expect(result).toBe("cba0c3d1-3c1b-4305-bb56-31aa50f58e27"); + }); + }); + }); + describe("getSessionIdValue", () => { test("getSessionIdValue, should return GUID", () => { // Arrange From 48756a9683262c4c4af4ecaf78c923b4bdeafec1 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Tue, 5 Dec 2023 12:44:01 -0500 Subject: [PATCH 28/36] Cleanup --- .../heritage-integration/cookie-helper.spec.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/helpers/heritage-integration/cookie-helper.spec.js b/src/helpers/heritage-integration/cookie-helper.spec.js index 99a502559..73cc388d6 100644 --- a/src/helpers/heritage-integration/cookie-helper.spec.js +++ b/src/helpers/heritage-integration/cookie-helper.spec.js @@ -335,17 +335,4 @@ describe("cookies", () => { }); }); }); - - describe("getSessionIdValue", () => { - test("getSessionIdValue, should return GUID", () => { - // Arrange - setupCookies({}); - - // Act - const result = getSessionIdValue(); - - //Assert - expect(result).toBe("cba0c3d1-3c1b-4305-bb56-31aa50f58e27"); - }); - }); }); From 4be83e96493cdd5a5a6d16634995c6d1f2e7d8be Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Tue, 5 Dec 2023 13:08:41 -0500 Subject: [PATCH 29/36] Is Cookie Set --- .../cookie-helper.spec.js | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/helpers/heritage-integration/cookie-helper.spec.js b/src/helpers/heritage-integration/cookie-helper.spec.js index 73cc388d6..55b4a73ff 100644 --- a/src/helpers/heritage-integration/cookie-helper.spec.js +++ b/src/helpers/heritage-integration/cookie-helper.spec.js @@ -9,6 +9,7 @@ import { regenerateUserId, setSessionKeyIfUnset, setSessionIdIfUnset, + isCookieSet, } from "@/helpers/heritage-integration/cookie-helper.js"; import { cookieNames } from "@/constants/cookie-names"; import { removeAllTestCookies, setupCookies, setupCrypto } from "@/helpers/unit-test-helper"; @@ -335,4 +336,43 @@ describe("cookies", () => { }); }); }); + + describe("isCookieSet", () => { + test("Returns true if cookie is set and unexpired", () => { + // Arrange + setupCookies({}); + + // Act + const result = isCookieSet(cookieNames.SESSION_ID); + + // Assert + expect(result).toBe(true); + }); + + test("Returns false if never set", () => { + // Arrange + // Act + const result = isCookieSet(cookieNames.SESSION_ID); + + // Assert + expect(result).toBe(false); + }); + + test("Returns false if cookie is expired", () => { + // Arrange + setupCookies({}); + + // Act + setCookieProperties( + { + [cookieNames.SESSION_ID]: "test", + }, + { maxAge: 0 } + ); + const result = isCookieSet(cookieNames.SESSION_ID); + + // Assert + expect(result).toBe(false); + }); + }); }); From 67c9b1609bd1cd9422969c52beebefa4739149de Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Tue, 5 Dec 2023 14:00:28 -0500 Subject: [PATCH 30/36] Final cookie tests --- .../cookie-helper.spec.js | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/helpers/heritage-integration/cookie-helper.spec.js b/src/helpers/heritage-integration/cookie-helper.spec.js index 55b4a73ff..0929e0ed6 100644 --- a/src/helpers/heritage-integration/cookie-helper.spec.js +++ b/src/helpers/heritage-integration/cookie-helper.spec.js @@ -10,6 +10,7 @@ import { setSessionKeyIfUnset, setSessionIdIfUnset, isCookieSet, + refreshCookieExpiration, } from "@/helpers/heritage-integration/cookie-helper.js"; import { cookieNames } from "@/constants/cookie-names"; import { removeAllTestCookies, setupCookies, setupCrypto } from "@/helpers/unit-test-helper"; @@ -375,4 +376,29 @@ describe("cookies", () => { expect(result).toBe(false); }); }); + + describe("refreshCookieExpiration", () => { + test("Preserves value", () => { + // Arrange + setupCookies({}); + + // Act + const result1 = getSessionIdValue(); + refreshCookieExpiration(cookieNames.SESSION_ID, 1000000); + const result2 = getSessionIdValue(); + + // Assert + expect(result1).toBe(result2); + }); + + test("Does not set cookie if not already set", () => { + // Arrange + + // Act + refreshCookieExpiration(cookieNames.SESSION_ID, 1000000); + const result = isCookieSet(cookieNames.SESSION_ID); + + expect(result).toBe(false); + }); + }); }); From ef5a6a55e3771edf64706ca3f79feed281e92b34 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Tue, 5 Dec 2023 14:08:05 -0500 Subject: [PATCH 31/36] Properly mock crypto module --- src/mixins/analytics-mixin.spec.js | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/mixins/analytics-mixin.spec.js b/src/mixins/analytics-mixin.spec.js index 8f7cfceaa..7e04215af 100644 --- a/src/mixins/analytics-mixin.spec.js +++ b/src/mixins/analytics-mixin.spec.js @@ -1,5 +1,5 @@ import analyticsMixin from "@/mixins/analytics-mixin"; -import { setupMocksForJsFiles, setupCookies } from "@/helpers/unit-test-helper.js"; +import { setupMocksForJsFiles, setupCookies, setupCrypto } from "@/helpers/unit-test-helper.js"; import { storeActions } from "@/constants/store-actions"; import { analyticsPageEvents, @@ -11,19 +11,12 @@ import { } from "@/constants/analytics"; import store from "@/store"; -// Mock only the regenerate functions; window.crypto is not available in testing. -jest.mock("@/helpers/heritage-integration/cookie-helper", () => { - const originalModule = jest.requireActual("@/helpers/heritage-integration/cookie-helper"); - - return { - __esModule: true, - ...originalModule, - regenerateDeviceId: jest.fn(), - regenerateUserId: jest.fn(), - }; -}); describe("analyticsMixin.js", () => { + beforeEach(() => { + setupCrypto(); + }); + test("logPageView: calls dispatch with type and payload", async () => { const type = ""; const payload = {}; From 9b9001d67a36b9f00fc0302fe6b3f0394a448eed Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Tue, 5 Dec 2023 18:06:12 -0500 Subject: [PATCH 32/36] Tests for initsession --- src/mixins/analytics-mixin.spec.js | 133 ++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 3 deletions(-) diff --git a/src/mixins/analytics-mixin.spec.js b/src/mixins/analytics-mixin.spec.js index 7e04215af..567d9154e 100644 --- a/src/mixins/analytics-mixin.spec.js +++ b/src/mixins/analytics-mixin.spec.js @@ -1,5 +1,10 @@ import analyticsMixin from "@/mixins/analytics-mixin"; -import { setupMocksForJsFiles, setupCookies, setupCrypto } from "@/helpers/unit-test-helper.js"; +import { + setupMocksForJsFiles, + setupCookies, + setupCrypto, + removeAllTestCookies, +} from "@/helpers/unit-test-helper.js"; import { storeActions } from "@/constants/store-actions"; import { analyticsPageEvents, @@ -10,11 +15,17 @@ import { ValueToLogTypes, } from "@/constants/analytics"; import store from "@/store"; - +import { + getDeviceIdValue, + getSessionIdValue, + getSessionKeyValue, + getUserIdValue, +} from "@/helpers/heritage-integration/cookie-helper"; describe("analyticsMixin.js", () => { beforeEach(() => { - setupCrypto(); + removeAllTestCookies(); + setupCrypto("7e4727f3-9a3d-4c59-9cb3-6f4121b5ea94"); }); test("logPageView: calls dispatch with type and payload", async () => { @@ -341,4 +352,120 @@ describe("analyticsMixin.js", () => { //Assert expect(gaLabels).toEqual(GaLabels); }); + describe("initSession", () => { + test("Generates random values for userId and deviceId if not present", async () => { + // Arrange + const mockData = { + actionList: [ + { + actionName: storeActions.LOG_CUSTOM_EVENT, + }, + { + actionName: storeActions.INITIALIZE_SESSION, + data: { + sessionKey: "12345", + sessionId: "4f1eba4a-4dd5-4144-9a6b-1363c1e5e54f", + }, + }, + ], + }; + const mocks = setupMocksForJsFiles(mockData); + + // Act + await analyticsMixin.methods.initSession(); + const userId = getUserIdValue(); + const deviceId = getDeviceIdValue(); + + // Assert + expect(userId).not.toBe("00000000-0000-0000-0000-000000000000"); + expect(deviceId).not.toBe("00000000-0000-0000-0000-000000000000"); + }); + + test("Pulls sessionId and sessionKey from api if not set", async () => { + // Arrange + const mockData = { + actionList: [ + { + actionName: storeActions.LOG_CUSTOM_EVENT, + }, + { + actionName: storeActions.INITIALIZE_SESSION, + data: { + sessionKey: "54321", + sessionId: "4f1eba4a-4dd5-4144-9a6b-1363c1e5e54f", + }, + }, + ], + }; + const mocks = setupMocksForJsFiles(mockData); + + // Act + await analyticsMixin.methods.initSession(); + const sessionId = getSessionIdValue(); + const sessionKey = getSessionKeyValue(); + + // Assert + expect(sessionId).toBe("4f1eba4a-4dd5-4144-9a6b-1363c1e5e54f"); + expect(sessionKey).toBe("54321"); + }); + + test("Does not overwrite values that are already set", async () => { + // Arrange + const mockData = { + actionList: [ + { + actionName: storeActions.LOG_CUSTOM_EVENT, + }, + { + actionName: storeActions.INITIALIZE_SESSION, + data: { + sessionKey: "54321", + sessionId: "4f1eba4a-4dd5-4144-9a6b-1363c1e5e54f", + }, + }, + ], + }; + const mocks = setupMocksForJsFiles(mockData); + setupCookies({}); + + // Act + await analyticsMixin.methods.initSession(); + + const userId = getUserIdValue(); + const deviceId = getDeviceIdValue(); + const sessionId = getSessionIdValue(); + const sessionKey = getSessionKeyValue(); + + // Assert + expect(userId).toBe("11aec5e8-92ba-4dc9-a8b6-179a916d8d7a"); + expect(deviceId).toBe("21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe"); + expect(sessionId).toBe("cba0c3d1-3c1b-4305-bb56-31aa50f58e27"); + expect(sessionKey).toBe("12345"); + }); + + test("Calls dispatchStoreAction", async () => { + // Arrange + const mockData = { + actionList: [ + { + actionName: storeActions.LOG_CUSTOM_EVENT, + }, + { + actionName: storeActions.INITIALIZE_SESSION, + data: { + sessionKey: "12345", + sessionId: "4f1eba4a-4dd5-4144-9a6b-1363c1e5e54f", + }, + }, + ], + }; + const mocks = setupMocksForJsFiles(mockData); + + // Act + await analyticsMixin.methods.initSession(); + + // Assert + expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled(); + }); + }); }); From be4524e5adf464647da408c3a5955a42ff9de21c Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Wed, 6 Dec 2023 10:19:11 -0500 Subject: [PATCH 33/36] Loader tests --- src/ux-components/loader/loader.spec.js | 54 +++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/ux-components/loader/loader.spec.js b/src/ux-components/loader/loader.spec.js index fb1eea21e..d56c4e0ed 100644 --- a/src/ux-components/loader/loader.spec.js +++ b/src/ux-components/loader/loader.spec.js @@ -28,4 +28,58 @@ describe("loader.vue", () => { loaderPosition: "left", }); }); + + describe("Blocking interaction on page", () => { + test("Does capture clicks if enabled (default)", async () => { + // Arrange + const div = document.createElement("div"); + div.id = "parent"; + document.body.appendChild(div); + + const parentClickFn = jest.fn(); + + div.addEventListener("click", parentClickFn); + + const wrapper = shallowMount(loader, { + props: {}, + attachTo: "#parent", + }); + + // Act + await wrapper.trigger("click"); + + // Assert + expect(parentClickFn).not.toBeCalled(); + + // Cleanup + document.body.removeChild(div); + }); + + test("Does not capture clicks if disabled", async () => { + // Arrange + const div = document.createElement("div"); + div.id = "parent"; + document.body.appendChild(div); + + const parentClickFn = jest.fn(); + + div.addEventListener("click", parentClickFn); + + const wrapper = shallowMount(loader, { + props: { + allowPageInteraction: true, + }, + attachTo: "#parent", + }); + + // Act + await wrapper.trigger("click"); + + // Assert + expect(parentClickFn).toBeCalled(); + + // Cleanup + document.body.removeChild(div); + }); + }); }); From 8b7229608c37aef4894db7fabb864a559a508ab2 Mon Sep 17 00:00:00 2001 From: Bryan Mauger Date: Wed, 6 Dec 2023 10:37:18 -0500 Subject: [PATCH 34/36] CSR-1729 add label copy for textarea. --- .../textarea-question/textarea-question.vue | 33 +++++++++++-------- .../customer-details/customer-details.vue | 1 + 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/digital-components/textarea-question/textarea-question.vue b/src/digital-components/textarea-question/textarea-question.vue index eda3a6f9c..27eeda216 100644 --- a/src/digital-components/textarea-question/textarea-question.vue +++ b/src/digital-components/textarea-question/textarea-question.vue @@ -1,12 +1,13 @@ -