Merge branch 'develop' into feature/CSR-1851
This commit is contained in:
commit
2ea2176865
20 changed files with 786 additions and 153 deletions
|
|
@ -73,6 +73,8 @@ body select {
|
||||||
background-position: 95% 50%;
|
background-position: 95% 50%;
|
||||||
background-size: 15px 9px;
|
background-size: 15px 9px;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #000;
|
||||||
}
|
}
|
||||||
body .creditCardSpecific div {
|
body .creditCardSpecific div {
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,8 @@ body {
|
||||||
background-position: 95% 50%;
|
background-position: 95% 50%;
|
||||||
background-size: 15px 9px;
|
background-size: 15px 9px;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.creditCardSpecific {
|
.creditCardSpecific {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ const applicationConfig = {
|
||||||
COOKIE_PATH: "/",
|
COOKIE_PATH: "/",
|
||||||
CURRENT_ENVIRONMENT: process.env.VUE_APP_CURRENT_ENVIRONMENT, // "Localhost", "Dev", "QA", and "Prod"
|
CURRENT_ENVIRONMENT: process.env.VUE_APP_CURRENT_ENVIRONMENT, // "Localhost", "Dev", "QA", and "Prod"
|
||||||
APPLICATION_NAME: "FixMyGlass",
|
APPLICATION_NAME: "FixMyGlass",
|
||||||
|
ANALYTICS_APPLICATION_NAME: "FixMyGlassNextGen",
|
||||||
APPLICATION_ABBREVIATION: "fmg",
|
APPLICATION_ABBREVIATION: "fmg",
|
||||||
PAGE_QUERYSTRING: "fmgPage",
|
PAGE_QUERYSTRING: "fmgPage",
|
||||||
SITE_ENTRY_TRIGGER_VALUE: "FixMyGlass",
|
SITE_ENTRY_TRIGGER_VALUE: "FixMyGlass",
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,32 @@ import { applicationConfig } from "@/constants/application-config.js";
|
||||||
const cookieNames = {
|
const cookieNames = {
|
||||||
FUNNEL_SESSION_INFO: `FunnelSessionInfo-${applicationConfig.CURRENT_ENVIRONMENT}`,
|
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
|
// Existing Safelite.com cookies
|
||||||
DXDEV: "dxdev",
|
DXDEV: "dxdev",
|
||||||
SESSION_ID: "sid",
|
SESSION_ID: "sid",
|
||||||
SESSION_KEY: "skey",
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,31 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="textarea-question">
|
<div class="textarea-question">
|
||||||
<div class="label-wrapper mb-1" :aria-label="questionText">
|
<div class="label-wrapper d-flex flex-column mb-1" :aria-label="questionText">
|
||||||
<!-- Wrap label and span because v-html prevents v-if from displaying if v-if <span> is inside <label>-->
|
<!-- Wrap label and span because v-html prevents v-if from displaying if v-if <span> is inside <label>-->
|
||||||
<label for="textarea-question" class="fw-bold" v-html="questionText"></label>
|
<div class="d-flex mb-1">
|
||||||
<span v-if="!isRequired" class="fw-normal ms-1">(Optional)</span>
|
<label :for="textAreaLabelCopy" class="fw-bold" v-html="questionText"></label>
|
||||||
|
<span v-if="!isRequired" class="fw-normal ms-1">(Optional)</span>
|
||||||
|
</div>
|
||||||
|
<textarea
|
||||||
|
:id="textAreaLabelCopy"
|
||||||
|
ref="textarea"
|
||||||
|
v-model="value"
|
||||||
|
v-maska="mask"
|
||||||
|
@keyup="updateCount"
|
||||||
|
class="p-4"
|
||||||
|
:maxlength="maxLength"
|
||||||
|
role="textbox"
|
||||||
|
aria-multiline="true"
|
||||||
|
:aria-required="isRequired">
|
||||||
|
</textarea>
|
||||||
|
<p
|
||||||
|
tabindex="-1"
|
||||||
|
class="caption mt-2 mb-0 w-100"
|
||||||
|
id="charactersRemaining"
|
||||||
|
:class="[urgentCountdown ? 'urgent-countdown' : '']">
|
||||||
|
{{ remainingCount }}/{{ maxLength }} characters remaining
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<textarea
|
|
||||||
id="textareaQuestion"
|
|
||||||
ref="textarea"
|
|
||||||
v-model="value"
|
|
||||||
v-maska="mask"
|
|
||||||
@keyup="updateCount"
|
|
||||||
class="p-4"
|
|
||||||
:maxlength="maxLength"
|
|
||||||
role="textbox"
|
|
||||||
aria-multiline="true"
|
|
||||||
:aria-required="isRequired">
|
|
||||||
</textarea>
|
|
||||||
<p
|
|
||||||
tabindex="-1"
|
|
||||||
class="caption mt-2 mb-0"
|
|
||||||
id="charactersRemaining"
|
|
||||||
:class="[urgentCountdown ? 'urgent-countdown' : '']">
|
|
||||||
{{ remainingCount }}/{{ maxLength }} characters remaining
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -38,6 +40,10 @@ export default {
|
||||||
default: 150,
|
default: 150,
|
||||||
},
|
},
|
||||||
modelValue: String,
|
modelValue: String,
|
||||||
|
textAreaLabelCopy: {
|
||||||
|
type: String,
|
||||||
|
default: "text area",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
// TODO: At some point in the future we should probably add the tie in to validation here in case the field must be populated for some other use cases
|
// TODO: At some point in the future we should probably add the tie in to validation here in case the field must be populated for some other use cases
|
||||||
setup() {},
|
setup() {},
|
||||||
|
|
@ -74,7 +80,7 @@ export default {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss" scoped>
|
||||||
.textarea-question {
|
.textarea-question {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -83,7 +89,6 @@ export default {
|
||||||
}
|
}
|
||||||
.label-wrapper {
|
.label-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
label {
|
label {
|
||||||
span {
|
span {
|
||||||
color: $gray-500;
|
color: $gray-500;
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,7 @@
|
||||||
<div class="price-table">
|
<div class="price-table">
|
||||||
<div class="service-type">
|
<div class="service-type">
|
||||||
<textBlock :cmsWidgetName="servicePackageTitleWidget" />
|
<textBlock :cmsWidgetName="servicePackageTitleWidget" />
|
||||||
<span tabindex="0">
|
<span>
|
||||||
<span class="sr-only"> {{ screenReaderPackagePriceText }} </span>
|
|
||||||
{{ getFormattedAmount("", packagePrice) }}
|
{{ getFormattedAmount("", packagePrice) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -64,40 +63,25 @@
|
||||||
<span v-else-if="cartItem == recycleFeeCartItem">
|
<span v-else-if="cartItem == recycleFeeCartItem">
|
||||||
{{ recycleFeeCartItem.name }}
|
{{ recycleFeeCartItem.name }}
|
||||||
</span>
|
</span>
|
||||||
<span tabindex="0"
|
<span>{{ getFormattedAmount(cartItem.category, cartItem.subTotal) }}</span>
|
||||||
><span class="sr-only">{{ screenReaderRecycleFeeText }}</span
|
|
||||||
>{{ getFormattedAmount(cartItem.category, cartItem.subTotal) }}</span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Sub total, sales tax, total columns -->
|
<!-- Sub total, sales tax, total columns -->
|
||||||
<div class="sub-total">
|
<div class="sub-total">
|
||||||
<span>{{ subtotalText }}</span
|
<span>{{ subtotalText }}</span
|
||||||
><span tabindex="0"
|
><span>{{ getFormattedAmount("", subTotal) }}</span>
|
||||||
><span class="sr-only">{{ screenReaderSubTotalText }}</span
|
|
||||||
>{{ getFormattedAmount("", subTotal) }}</span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="sales-tax">
|
<div class="sales-tax">
|
||||||
<span>{{ salesTaxText }}</span
|
<span>{{ salesTaxText }}</span
|
||||||
><span tabindex="0"
|
><span>{{ getFormattedAmount("", salesTax) }}</span>
|
||||||
><span class="sr-only">{{ screenReaderSalesTaxText }}</span
|
|
||||||
>{{ getFormattedAmount("", salesTax) }}</span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showAsPaid" class="amount-paid">
|
<div v-if="showAsPaid" class="amount-paid">
|
||||||
<span>{{ amountPaidText }}</span
|
<span>{{ amountPaidText }}</span
|
||||||
><span tabindex="0"
|
><span>{{ getFormattedAmount("", amountPaid) }}</span>
|
||||||
><span class="sr-only">{{ screenReaderAmountPaidText }}</span
|
|
||||||
>{{ getFormattedAmount("", amountPaid) }}</span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="amount-due">
|
<div class="amount-due">
|
||||||
<span>{{ amountDueText }}</span
|
<span>{{ amountDueText }}</span
|
||||||
><span tabindex="0"
|
><span>{{ getFormattedAmount("", amountDue) }}</span>
|
||||||
><span class="sr-only">{{ screenReaderTotalAmountDueText }}</span
|
|
||||||
>{{ getFormattedAmount("", amountDue) }}</span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { cookieNames } from "@/constants/cookie-names";
|
import { cookieNames, cookieExpirations } from "@/constants/cookie-names";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import { applicationConfig } from "@/constants/application-config";
|
import { applicationConfig } from "@/constants/application-config";
|
||||||
|
|
||||||
|
|
@ -78,11 +78,43 @@ export function getDeviceIdValue() {
|
||||||
return "00000000-0000-0000-0000-000000000000";
|
return "00000000-0000-0000-0000-000000000000";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function regenerateDeviceId() {
|
||||||
|
if (!isCookieSet(cookieNames.DXDEV)) {
|
||||||
|
setCookieProperties(
|
||||||
|
{
|
||||||
|
[cookieNames.DXDEV]: `did=${crypto.randomUUID()}`,
|
||||||
|
},
|
||||||
|
{ maxAge: cookieExpirations.DXDEV }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: cookieExpirations.FUNNEL_USER_ID }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Gets value of skey cookie, returns 0 if not found.
|
Gets value of skey cookie, returns 0 if not found.
|
||||||
*/
|
*/
|
||||||
export function getSessionKeyValue() {
|
export function getSessionKeyValue() {
|
||||||
const cookieValue = getCookieValueByName(cookieNames.SESSION_KEY);
|
const cookieValue = getCookieValueByName(cookieNames.FUNNEL_SESSION_KEY);
|
||||||
|
|
||||||
if (cookieValue) {
|
if (cookieValue) {
|
||||||
return cookieValue;
|
return cookieValue;
|
||||||
|
|
@ -91,6 +123,17 @@ export function getSessionKeyValue() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setSessionKeyIfUnset(value) {
|
||||||
|
if (!isCookieSet(cookieNames.FUNNEL_SESSION_KEY)) {
|
||||||
|
setCookieProperties(
|
||||||
|
{
|
||||||
|
[cookieNames.FUNNEL_SESSION_KEY]: value,
|
||||||
|
},
|
||||||
|
{ maxAge: cookieExpirations.FUNNEL_SESSION_KEY }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Gets value of skey cookie, returns 0 if not found.
|
Gets value of skey cookie, returns 0 if not found.
|
||||||
*/
|
*/
|
||||||
|
|
@ -104,11 +147,24 @@ export function getSessionIdValue() {
|
||||||
return "00000000-0000-0000-0000-000000000000";
|
return "00000000-0000-0000-0000-000000000000";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setSessionIdIfUnset(value) {
|
||||||
|
if (!isCookieSet(cookieNames.SESSION_ID)) {
|
||||||
|
setCookieProperties(
|
||||||
|
{
|
||||||
|
[cookieNames.SESSION_ID]: value,
|
||||||
|
},
|
||||||
|
{ maxAge: cookieExpirations.SESSION_ID }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Updates session ID cookie with new expiration date
|
Updates session ID cookie with new expiration date
|
||||||
*/
|
*/
|
||||||
export function updateSessionIdCookie() {
|
export function updateSessionIdCookie() {
|
||||||
createOrUpdateCookie(cookieNames.SESSION_ID, getSessionIdValue(), { maxAge: 60 * 30 });
|
createOrUpdateCookie(cookieNames.SESSION_ID, getSessionIdValue(), {
|
||||||
|
maxAge: cookieExpirations.SESSION_ID,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setCookieProperties(
|
export function setCookieProperties(
|
||||||
|
|
@ -126,6 +182,34 @@ export function setCookieProperties(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isCookieSet(name) {
|
||||||
|
const val = getCookieValueByName(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, 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) {
|
||||||
|
if (isCookieSet(name)) {
|
||||||
|
createOrUpdateCookie(name, getCookieValueByName(name), { maxAge: expirationTime });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
===========================
|
===========================
|
||||||
= PRIVATE FUNCTIONS =
|
= PRIVATE FUNCTIONS =
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,25 @@ import {
|
||||||
getDeviceIdValue,
|
getDeviceIdValue,
|
||||||
getSessionKeyValue,
|
getSessionKeyValue,
|
||||||
getSessionIdValue,
|
getSessionIdValue,
|
||||||
|
setCookieProperties,
|
||||||
|
regenerateDeviceId,
|
||||||
|
getUserIdValue,
|
||||||
|
regenerateUserId,
|
||||||
|
setSessionKeyIfUnset,
|
||||||
|
setSessionIdIfUnset,
|
||||||
|
isCookieSet,
|
||||||
|
refreshCookieExpiration,
|
||||||
} from "@/helpers/heritage-integration/cookie-helper.js";
|
} 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", () => {
|
describe("cookies", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setupCrypto(randomUUID);
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
removeAllTestCookies();
|
removeAllTestCookies();
|
||||||
});
|
});
|
||||||
|
|
@ -106,40 +121,284 @@ describe("cookies", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("getDeviceIdValue", () => {
|
describe("Device Id", () => {
|
||||||
test("getDeviceIdValue, should return GUID", () => {
|
describe("getDeviceIdValue", () => {
|
||||||
// Arrange
|
test("getDeviceIdValue, should return GUID", () => {
|
||||||
setupCookies({});
|
// Arrange
|
||||||
|
setupCookies({});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = getDeviceIdValue();
|
const result = getDeviceIdValue();
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
expect(result).toBe("21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe");
|
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", () => {
|
describe("regenerateDeviceId", () => {
|
||||||
// Arrange
|
test("Generates a new id if unset", () => {
|
||||||
setupCookies({});
|
// Arrange
|
||||||
|
// Act
|
||||||
|
regenerateDeviceId();
|
||||||
|
const result = getDeviceIdValue();
|
||||||
|
|
||||||
// Act
|
// Assert
|
||||||
const result = getSessionKeyValue();
|
expect(result).not.toBe("00000000-0000-0000-0000-000000000000");
|
||||||
|
expect(global.crypto.randomUUID).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
//Assert
|
test("Does not create a new id if already set", () => {
|
||||||
expect(result).toBe("12345");
|
// Arrange
|
||||||
|
setupCookies({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
regenerateDeviceId();
|
||||||
|
const result = getDeviceIdValue();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toBe("21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe");
|
||||||
|
expect(global.crypto.randomUUID).not.toBeCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("getSessionIdValue", () => {
|
describe("User Id", () => {
|
||||||
test("getSessionIdValue, should return GUID", () => {
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Session Key", () => {
|
||||||
|
describe("getSessionKeyValue", () => {
|
||||||
|
test("Should return session key int", () => {
|
||||||
|
// Arrange
|
||||||
|
setupCookies({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = getSessionKeyValue();
|
||||||
|
|
||||||
|
//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("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("isCookieSet", () => {
|
||||||
|
test("Returns true if cookie is set and unexpired", () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
setupCookies({});
|
setupCookies({});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = getSessionIdValue();
|
const result = isCookieSet(cookieNames.SESSION_ID);
|
||||||
|
|
||||||
//Assert
|
// Assert
|
||||||
expect(result).toBe("cba0c3d1-3c1b-4305-bb56-31aa50f58e27");
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,8 @@ export const cookies = {
|
||||||
someOtherCookie: "{}",
|
someOtherCookie: "{}",
|
||||||
dxdev: "did=21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe",
|
dxdev: "did=21b9b94a-ec23-42c1-aaac-e2ae4e4dbffe",
|
||||||
sid: "cba0c3d1-3c1b-4305-bb56-31aa50f58e27",
|
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
|
// Removes test cookies for testing cookie-helper and order-helper
|
||||||
|
|
@ -138,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
|
// Private methods
|
||||||
function setupBaseMixinDispatchStoreAction(mockData) {
|
function setupBaseMixinDispatchStoreAction(mockData) {
|
||||||
if (mockData.actionList !== undefined) {
|
if (mockData.actionList !== undefined) {
|
||||||
|
|
|
||||||
|
|
@ -300,7 +300,7 @@ export default {
|
||||||
return serviceLocationReqs && scheduleReqs && customerReqs;
|
return serviceLocationReqs && scheduleReqs && customerReqs;
|
||||||
},
|
},
|
||||||
forwardButtonAction() {
|
forwardButtonAction() {
|
||||||
window.location.assign("//www.safelite.com/");
|
window.location.assign(location.protocol + "//" + location.host);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@
|
||||||
class="mb-4"
|
class="mb-4"
|
||||||
v-model="techNotes"
|
v-model="techNotes"
|
||||||
cmsWidgetName="TextAreaContentWidget"
|
cmsWidgetName="TextAreaContentWidget"
|
||||||
|
textAreaLabelCopy="Notes for your technician"
|
||||||
maxLength="150" />
|
maxLength="150" />
|
||||||
|
|
||||||
<textBlock cmsWidgetName="DisclaimerCopyWidget" typeStyle="caption" />
|
<textBlock cmsWidgetName="DisclaimerCopyWidget" typeStyle="caption" />
|
||||||
|
|
|
||||||
|
|
@ -532,7 +532,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
cartItems.forEach((item) => {
|
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`);
|
lineItems.push(`${item.name}|${(item.salesTax + item.subTotal).toFixed(2)}|1`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -252,6 +252,7 @@ export default {
|
||||||
this.$emit("updated-mobile-fee-part", mobileFeePart);
|
this.$emit("updated-mobile-fee-part", mobileFeePart);
|
||||||
this.$emit("updated-serviceability", serviceabilityDetails.data);
|
this.$emit("updated-serviceability", serviceabilityDetails.data);
|
||||||
this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase);
|
this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase);
|
||||||
|
this.$emit("updated-mobile-ctu", zipCodeData.zipCodeCtu);
|
||||||
|
|
||||||
// update the page level model
|
// update the page level model
|
||||||
this.$emit("update:modelValue", this.internalModel);
|
this.$emit("update:modelValue", this.internalModel);
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@
|
||||||
@updated-mobile-fee-part="setMobileFeePart"
|
@updated-mobile-fee-part="setMobileFeePart"
|
||||||
@updated-serviceability="setServiceabilityDetails"
|
@updated-serviceability="setServiceabilityDetails"
|
||||||
@updated-contains-military-base="setContainsMilitaryBase"
|
@updated-contains-military-base="setContainsMilitaryBase"
|
||||||
|
@updated-mobile-ctu="setCtuForMobile"
|
||||||
validationRules="mobile-location-required"
|
validationRules="mobile-location-required"
|
||||||
ref="mobileLocationQuestions"
|
ref="mobileLocationQuestions"
|
||||||
linkWidgetName="MobileLocationLinkWidget"
|
linkWidgetName="MobileLocationLinkWidget"
|
||||||
|
|
@ -128,6 +129,7 @@ import contentGroupModal from "@/fmg-components/content-group-modal/content-grou
|
||||||
|
|
||||||
// Supporting files
|
// Supporting files
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
|
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
|
|
@ -181,6 +183,7 @@ export default {
|
||||||
zipContainsMilitaryBase: false,
|
zipContainsMilitaryBase: false,
|
||||||
zipCodeCtu: null,
|
zipCodeCtu: null,
|
||||||
shopProviderData: null,
|
shopProviderData: null,
|
||||||
|
navigatingForward: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async beforeRouteEnter(to, from, next) {
|
async beforeRouteEnter(to, from, next) {
|
||||||
|
|
@ -374,6 +377,9 @@ export default {
|
||||||
this.zipContainsMilitaryBase = val;
|
this.zipContainsMilitaryBase = val;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
setCtuForMobile(val) {
|
||||||
|
this.zipCodeCtu = val;
|
||||||
|
},
|
||||||
setMobileFeePart(mobileFeePart) {
|
setMobileFeePart(mobileFeePart) {
|
||||||
this.mobileFeePart = mobileFeePart;
|
this.mobileFeePart = mobileFeePart;
|
||||||
},
|
},
|
||||||
|
|
@ -472,6 +478,33 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async forwardButtonAction() {
|
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
|
||||||
|
// 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(
|
await this.dispatchStoreAction(
|
||||||
this.storeActions.SAVE_SERVICE_LOCATION,
|
this.storeActions.SAVE_SERVICE_LOCATION,
|
||||||
{
|
{
|
||||||
|
|
@ -507,16 +540,18 @@ export default {
|
||||||
watch: {
|
watch: {
|
||||||
zipCode: {
|
zipCode: {
|
||||||
handler(newValue) {
|
handler(newValue) {
|
||||||
getShopProviderData(this.zipCode).then(async (result) => {
|
if (!this.navigatingForward) {
|
||||||
this.shopProviderData = result.data;
|
getShopProviderData(this.zipCode).then(async (result) => {
|
||||||
if (this.selectedAppointmentType === "Mobile") {
|
this.shopProviderData = result.data;
|
||||||
this.selectedProvider = new Provider(
|
if (this.selectedAppointmentType === "Mobile") {
|
||||||
this.shopProviderData.mobileProviderNumber
|
this.selectedProvider = new Provider(
|
||||||
);
|
this.shopProviderData.mobileProviderNumber
|
||||||
} else {
|
);
|
||||||
this.selectedProvider = new Provider();
|
} else {
|
||||||
}
|
this.selectedProvider = new Provider();
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
selectedAppointmentType: {
|
selectedAppointmentType: {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,15 @@
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import {
|
import {
|
||||||
setCookieProperties,
|
|
||||||
getDeviceIdValue,
|
getDeviceIdValue,
|
||||||
getSessionIdValue,
|
getSessionIdValue,
|
||||||
getSessionKeyValue,
|
getSessionKeyValue,
|
||||||
|
getUserIdValue,
|
||||||
|
regenerateDeviceId,
|
||||||
|
regenerateUserId,
|
||||||
|
refreshSessionExpiration,
|
||||||
|
areAllSessionCookiesSet,
|
||||||
|
setSessionIdIfUnset,
|
||||||
|
setSessionKeyIfUnset,
|
||||||
} from "@/helpers/heritage-integration/cookie-helper";
|
} from "@/helpers/heritage-integration/cookie-helper";
|
||||||
import { queryStrings } from "@/constants/query-strings";
|
import { queryStrings } from "@/constants/query-strings";
|
||||||
import { experimentSettings } from "@/constants/experiments";
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
|
|
@ -15,7 +21,6 @@ import {
|
||||||
GaEvents,
|
GaEvents,
|
||||||
ValueToLogTypes,
|
ValueToLogTypes,
|
||||||
} from "@/constants/analytics";
|
} from "@/constants/analytics";
|
||||||
import { cookieNames } from "@/constants/cookie-names";
|
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
import baseMixin from "@/mixins/base-mixin";
|
import baseMixin from "@/mixins/base-mixin";
|
||||||
|
|
@ -27,10 +32,12 @@ export default {
|
||||||
return getPageNameByQueryString();
|
return getPageNameByQueryString();
|
||||||
},
|
},
|
||||||
|
|
||||||
logPageView(pageEvent) {
|
async logPageView(pageEvent) {
|
||||||
const currentPageName = getPageNameByQueryString();
|
const currentPageName = getPageNameByQueryString();
|
||||||
|
await this.validateSession();
|
||||||
|
|
||||||
var payload = {
|
var payload = {
|
||||||
userId: getDeviceIdValue(),
|
userId: getUserIdValue(),
|
||||||
sessionKey: getSessionKeyValue(),
|
sessionKey: getSessionKeyValue(),
|
||||||
pageName: currentPageName,
|
pageName: currentPageName,
|
||||||
sessionId: getSessionIdValue(),
|
sessionId: getSessionIdValue(),
|
||||||
|
|
@ -42,14 +49,15 @@ export default {
|
||||||
parentAccountNumber: store.getters.order.payment.parentAccountNumber,
|
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);
|
||||||
},
|
},
|
||||||
|
|
||||||
logCustomEvent(category, action, label, value) {
|
async logCustomEvent(category, action, label, value) {
|
||||||
const currentPageName = getPageNameByQueryString();
|
const currentPageName = getPageNameByQueryString();
|
||||||
|
await this.validateSession();
|
||||||
|
|
||||||
var payload = {
|
var payload = {
|
||||||
userId: getDeviceIdValue(),
|
userId: getUserIdValue(),
|
||||||
sessionKey: getSessionKeyValue(),
|
sessionKey: getSessionKeyValue(),
|
||||||
pageName: currentPageName,
|
pageName: currentPageName,
|
||||||
sessionId: getSessionIdValue(),
|
sessionId: getSessionIdValue(),
|
||||||
|
|
@ -63,10 +71,14 @@ export default {
|
||||||
parentAccountNumber: store.getters.order.payment.parentAccountNumber,
|
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 currentPageName = getPageNameByQueryString();
|
||||||
const labelToLog = getValueToLog(label, valueToLogType);
|
const labelToLog = getValueToLog(label, valueToLogType);
|
||||||
|
|
||||||
|
|
@ -82,11 +94,11 @@ export default {
|
||||||
pushToDataLayerIfDefined(eventToBePushed);
|
pushToDataLayerIfDefined(eventToBePushed);
|
||||||
|
|
||||||
if (pushToLogApp) {
|
if (pushToLogApp) {
|
||||||
this.logCustomEvent(category, action, labelToLog, undefined);
|
await this.logCustomEvent(category, action, labelToLog, undefined);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
pushPageViewToGA() {
|
async pushPageViewToGA() {
|
||||||
const currentPageName = getPageNameByQueryString();
|
const currentPageName = getPageNameByQueryString();
|
||||||
const pageViewEvent = {
|
const pageViewEvent = {
|
||||||
event: GaEvents.PAGE_VIEW_EVENT,
|
event: GaEvents.PAGE_VIEW_EVENT,
|
||||||
|
|
@ -96,7 +108,7 @@ export default {
|
||||||
|
|
||||||
pushToDataLayerIfDefined(pageViewEvent);
|
pushToDataLayerIfDefined(pageViewEvent);
|
||||||
|
|
||||||
this.logPageView(analyticsPageEvents.ENTRY);
|
await this.logPageView(analyticsPageEvents.ENTRY);
|
||||||
},
|
},
|
||||||
|
|
||||||
pushExperimentsToDataLayer() {
|
pushExperimentsToDataLayer() {
|
||||||
|
|
@ -136,15 +148,21 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
async initSession() {
|
async initSession() {
|
||||||
const sid = getSessionIdValue();
|
regenerateDeviceId();
|
||||||
const skey = getSessionKeyValue();
|
regenerateUserId();
|
||||||
const referrer =
|
|
||||||
applicationConfig.CURRENT_ENVIRONMENT != "Localhost" ? document.referrer : null;
|
|
||||||
|
|
||||||
var payload = {
|
const userId = getUserIdValue(); // cookieNames.FUNNEL_USER_ID
|
||||||
userId: getDeviceIdValue(),
|
const deviceId = getDeviceIdValue(); // cookieNames.DXDEV
|
||||||
sessionId: sid,
|
const sessionId = getSessionIdValue(); // cookieNames.SESSION_ID
|
||||||
userAgent: navigator.userAgent,
|
const userAgent = navigator.userAgent; // navigator.userAgent
|
||||||
|
const referrer =
|
||||||
|
applicationConfig.CURRENT_ENVIRONMENT != "Localhost" ? document.referrer : null; // see above
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
userId: userId,
|
||||||
|
deviceId: deviceId,
|
||||||
|
sessionId: sessionId,
|
||||||
|
userAgent: userAgent,
|
||||||
referrer: referrer,
|
referrer: referrer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -155,30 +173,26 @@ export default {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response?.data) {
|
if (response?.data) {
|
||||||
if (response?.data.sessionKey && skey === 0) {
|
if (response.data.sessionKey) {
|
||||||
setCookieProperties(
|
setSessionKeyIfUnset(response.data.sessionKey);
|
||||||
{ [cookieNames.SESSION_KEY]: response?.data.sessionKey },
|
|
||||||
{
|
|
||||||
useDefaultFunnelCookieAttributes: false,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (response?.data.sessionId && sid === "00000000-0000-0000-0000-000000000000") {
|
|
||||||
setCookieProperties(
|
if (response.data.sessionId) {
|
||||||
{ [cookieNames.SESSION_ID]: response?.data.sessionId },
|
setSessionIdIfUnset(response.data.sessionId);
|
||||||
{
|
|
||||||
maxAge: 60 * 30, // 30 minutes
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
noSession() {
|
noSession() {
|
||||||
return (
|
return !areAllSessionCookiesSet();
|
||||||
getSessionKeyValue() === 0 ||
|
},
|
||||||
getSessionIdValue() === "00000000-0000-0000-0000-000000000000"
|
|
||||||
);
|
async validateSession() {
|
||||||
|
if (this.noSession()) {
|
||||||
|
await this.initSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshSessionExpiration();
|
||||||
},
|
},
|
||||||
|
|
||||||
removeParamsFromEndpoint(endpoint) {
|
removeParamsFromEndpoint(endpoint) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
import analyticsMixin from "@/mixins/analytics-mixin";
|
import analyticsMixin from "@/mixins/analytics-mixin";
|
||||||
import { setupMocksForJsFiles, setupCookies } from "@/helpers/unit-test-helper.js";
|
import {
|
||||||
|
setupMocksForJsFiles,
|
||||||
|
setupCookies,
|
||||||
|
setupCrypto,
|
||||||
|
removeAllTestCookies,
|
||||||
|
} from "@/helpers/unit-test-helper.js";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import {
|
import {
|
||||||
analyticsPageEvents,
|
analyticsPageEvents,
|
||||||
|
|
@ -10,9 +15,20 @@ import {
|
||||||
ValueToLogTypes,
|
ValueToLogTypes,
|
||||||
} from "@/constants/analytics";
|
} from "@/constants/analytics";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
import {
|
||||||
|
getDeviceIdValue,
|
||||||
|
getSessionIdValue,
|
||||||
|
getSessionKeyValue,
|
||||||
|
getUserIdValue,
|
||||||
|
} from "@/helpers/heritage-integration/cookie-helper";
|
||||||
|
|
||||||
describe("analyticsMixin.js", () => {
|
describe("analyticsMixin.js", () => {
|
||||||
test("logPageView: calls dispatch with type and payload", () => {
|
beforeEach(() => {
|
||||||
|
removeAllTestCookies();
|
||||||
|
setupCrypto("7e4727f3-9a3d-4c59-9cb3-6f4121b5ea94");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("logPageView: calls dispatch with type and payload", async () => {
|
||||||
const type = "";
|
const type = "";
|
||||||
const payload = {};
|
const payload = {};
|
||||||
|
|
||||||
|
|
@ -21,6 +37,9 @@ describe("analyticsMixin.js", () => {
|
||||||
{
|
{
|
||||||
actionName: storeActions.LOG_PAGE_VIEW,
|
actionName: storeActions.LOG_PAGE_VIEW,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
actionName: storeActions.INITIALIZE_SESSION,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const mocks = setupMocksForJsFiles(mockData);
|
const mocks = setupMocksForJsFiles(mockData);
|
||||||
|
|
@ -31,27 +50,35 @@ describe("analyticsMixin.js", () => {
|
||||||
|
|
||||||
setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) });
|
setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) });
|
||||||
|
|
||||||
analyticsMixin.methods.logPageView(type, payload);
|
await analyticsMixin.methods.logPageView(type, payload);
|
||||||
|
|
||||||
expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled();
|
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 = {
|
const mockData = {
|
||||||
actionList: [
|
actionList: [
|
||||||
{
|
{
|
||||||
actionName: storeActions.LOG_CUSTOM_EVENT,
|
actionName: storeActions.LOG_CUSTOM_EVENT,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
actionName: storeActions.INITIALIZE_SESSION,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const mocks = setupMocksForJsFiles(mockData);
|
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();
|
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
|
// Arrange
|
||||||
window.dataLayer = [];
|
window.dataLayer = [];
|
||||||
const mockData = {
|
const mockData = {
|
||||||
|
|
@ -59,6 +86,9 @@ describe("analyticsMixin.js", () => {
|
||||||
{
|
{
|
||||||
actionName: storeActions.LOG_CUSTOM_EVENT,
|
actionName: storeActions.LOG_CUSTOM_EVENT,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
actionName: storeActions.INITIALIZE_SESSION,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const mocks = setupMocksForJsFiles(mockData);
|
const mocks = setupMocksForJsFiles(mockData);
|
||||||
|
|
@ -73,14 +103,14 @@ 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));
|
||||||
expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled();
|
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
|
// Arrange
|
||||||
window.dataLayer = [];
|
window.dataLayer = [];
|
||||||
var expectedDataLayer = [];
|
var expectedDataLayer = [];
|
||||||
|
|
@ -93,8 +123,21 @@ describe("analyticsMixin.js", () => {
|
||||||
path: "/fmg/?fmgPage=",
|
path: "/fmg/?fmgPage=",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mockData = {
|
||||||
|
actionList: [
|
||||||
|
{
|
||||||
|
actionName: storeActions.LOG_CUSTOM_EVENT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: storeActions.INITIALIZE_SESSION,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const mocks = setupMocksForJsFiles(mockData);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
analyticsMixin.methods.pushEventToGA(
|
await analyticsMixin.methods.pushEventToGA(
|
||||||
"category",
|
"category",
|
||||||
"action",
|
"action",
|
||||||
"1111122222333333",
|
"1111122222333333",
|
||||||
|
|
@ -106,7 +149,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 = [];
|
||||||
var expectedDataLayer = [];
|
var expectedDataLayer = [];
|
||||||
|
|
@ -119,8 +162,21 @@ describe("analyticsMixin.js", () => {
|
||||||
path: "/fmg/?fmgPage=",
|
path: "/fmg/?fmgPage=",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mockData = {
|
||||||
|
actionList: [
|
||||||
|
{
|
||||||
|
actionName: storeActions.LOG_CUSTOM_EVENT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
actionName: storeActions.INITIALIZE_SESSION,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const mocks = setupMocksForJsFiles(mockData);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
analyticsMixin.methods.pushEventToGA(
|
await analyticsMixin.methods.pushEventToGA(
|
||||||
"category",
|
"category",
|
||||||
"action",
|
"action",
|
||||||
"111",
|
"111",
|
||||||
|
|
@ -296,4 +352,120 @@ describe("analyticsMixin.js", () => {
|
||||||
//Assert
|
//Assert
|
||||||
expect(gaLabels).toEqual(GaLabels);
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,7 @@ const routes = [
|
||||||
async beforeEnter(to, from, next) {
|
async beforeEnter(to, from, next) {
|
||||||
// If we have no query string, or we don't have the FmgPage query string.
|
// If we have no query string, or we don't have the FmgPage query string.
|
||||||
try {
|
try {
|
||||||
if (analyticsMixin.methods.noSession()) {
|
await analyticsMixin.methods.validateSession();
|
||||||
await analyticsMixin.methods.initSession();
|
|
||||||
} else {
|
|
||||||
updateSessionIdCookie();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getFunnelCookie()?.SuppressConceptFunnel) {
|
if (getFunnelCookie()?.SuppressConceptFunnel) {
|
||||||
await navigateToHeritageFunnel({ shouldSaveSession: false });
|
await navigateToHeritageFunnel({ shouldSaveSession: false });
|
||||||
|
|
|
||||||
|
|
@ -1076,7 +1076,7 @@ export const actions = {
|
||||||
sessionKey: sessionKey,
|
sessionKey: sessionKey,
|
||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
pageName: pageName,
|
pageName: pageName,
|
||||||
applicationName: applicationConfig.APPLICATION_NAME,
|
applicationName: applicationConfig.ANALYTICS_APPLICATION_NAME,
|
||||||
action: action,
|
action: action,
|
||||||
event: event,
|
event: event,
|
||||||
shouldUseSessionId: shouldUseSessionId,
|
shouldUseSessionId: shouldUseSessionId,
|
||||||
|
|
@ -1124,7 +1124,7 @@ export const actions = {
|
||||||
sessionKey: sessionKey,
|
sessionKey: sessionKey,
|
||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
pageName: pageName,
|
pageName: pageName,
|
||||||
applicationName: applicationConfig.APPLICATION_NAME,
|
applicationName: applicationConfig.ANALYTICS_APPLICATION_NAME,
|
||||||
category: category,
|
category: category,
|
||||||
action: action,
|
action: action,
|
||||||
label: label,
|
label: label,
|
||||||
|
|
@ -1151,11 +1151,11 @@ export const actions = {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
initializeSession(context, { userId, sessionId, userAgent, referrer }) {
|
initializeSession(context, { userId, deviceId, sessionId, userAgent, referrer }) {
|
||||||
var payload = {
|
var payload = {
|
||||||
applicationName: applicationConfig.APPLICATION_NAME,
|
applicationName: applicationConfig.ANALYTICS_APPLICATION_NAME,
|
||||||
userId: userId,
|
userId: userId,
|
||||||
deviceId: userId,
|
deviceId: deviceId,
|
||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
userAgent: userAgent,
|
userAgent: userAgent,
|
||||||
operatorId: "WEB",
|
operatorId: "WEB",
|
||||||
|
|
@ -2495,15 +2495,7 @@ export const actions = {
|
||||||
|
|
||||||
isVinOptionalVehicle(context) {
|
isVinOptionalVehicle(context) {
|
||||||
//Optional for carIds with only a single windshield
|
//Optional for carIds with only a single windshield
|
||||||
if (
|
if (singleWindshieldCarIds.find((item) => item === context.state.order.vehicle.carId)) {
|
||||||
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()
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//Optional for specific YMMSs
|
//Optional for specific YMMSs
|
||||||
|
|
|
||||||
|
|
@ -3329,7 +3329,7 @@ describe("isVinOptionalVehicle", () => {
|
||||||
"CR00062396",
|
"CR00062396",
|
||||||
"make3",
|
"make3",
|
||||||
[{ glassLocation: "windshield" }, { glassLocation: "driver" }],
|
[{ glassLocation: "windshield" }, { glassLocation: "driver" }],
|
||||||
false,
|
true,
|
||||||
],
|
],
|
||||||
["CR00066428", "make4", [{ glassLocation: "rear" }], false],
|
["CR00066428", "make4", [{ glassLocation: "rear" }], false],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -28,4 +28,58 @@ describe("loader.vue", () => {
|
||||||
loaderPosition: "left",
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue