1133 lines
38 KiB
JavaScript
1133 lines
38 KiB
JavaScript
import analyticsMixin from "@/mixins/analytics-mixin";
|
|
import {
|
|
setupMocksForJsFiles,
|
|
setupCookies,
|
|
setupCrypto,
|
|
removeAllTestCookies,
|
|
} from "@/helpers/unit-test-helper.js";
|
|
import { storeActions } from "@/constants/store-actions";
|
|
import {
|
|
analyticsPageEvents,
|
|
GaCategories,
|
|
GaActions,
|
|
GaLabels,
|
|
GaEvents,
|
|
ValueToLogTypes,
|
|
} from "@/constants/analytics";
|
|
import store from "@/store";
|
|
import {
|
|
getDeviceIdValue,
|
|
getSessionIdValue,
|
|
getSessionKeyValue,
|
|
getUserIdValue,
|
|
} from "@/helpers/heritage-integration/cookie-helper";
|
|
import baseMixin from "./base-mixin";
|
|
import { bailoutCodes } from "@/constants/bailout-codes";
|
|
|
|
const parts = {
|
|
windshield: {
|
|
name: "windshield",
|
|
canSafeliteRecalibrate: true,
|
|
childParts: [],
|
|
color: "Green Tint",
|
|
description:
|
|
"solar, soundproofing, lane keep assist, lane departure warning system, w/adaptive cruise control",
|
|
id: "db22fd44-10dd-456f-979b-ff88cf68cca6",
|
|
partNumber: "FW04896GTYN",
|
|
partType: "WINDSHIELD",
|
|
recalibrationType: "STATIC",
|
|
requiresCapabilityQuestions: false,
|
|
requiresRecalibration: true,
|
|
salesTax: 63.86,
|
|
sellingPrice: 791.46,
|
|
kitPrice: 0,
|
|
laborAmount: 0,
|
|
},
|
|
frontWipers: {
|
|
name: "front wipers",
|
|
partNumber: "SBB16",
|
|
description: "SAFELITE BEAM BLADE 16",
|
|
partType: "FRONT WIPER",
|
|
sellingPrice: 32.64,
|
|
kitPrice: 0,
|
|
laborAmount: 0,
|
|
salesTax: 1,
|
|
},
|
|
rearWipers: {
|
|
name: "rear wipers",
|
|
partNumber: "SBBR12A",
|
|
description: "SAFELITE REAR BLADE 12A",
|
|
partType: "REAR WIPER",
|
|
sellingPrice: 24.48,
|
|
kitPrice: 0,
|
|
laborAmount: 0,
|
|
salesTax: 2,
|
|
},
|
|
rainRepel: {
|
|
name: "rain repel",
|
|
partNumber: "RAIN REPEL",
|
|
description: null,
|
|
partType: "RAIN REPEL",
|
|
sellingPrice: 35.5,
|
|
kitPrice: 0,
|
|
laborAmount: 0,
|
|
salesTax: 0,
|
|
},
|
|
};
|
|
|
|
describe("analyticsMixin.js", () => {
|
|
beforeEach(() => {
|
|
removeAllTestCookies();
|
|
setupCrypto("7e4727f3-9a3d-4c59-9cb3-6f4121b5ea94");
|
|
});
|
|
|
|
test("logPageView: calls dispatch with type and payload", async () => {
|
|
const type = "";
|
|
const payload = {};
|
|
|
|
const mockData = {
|
|
actionList: [
|
|
{
|
|
actionName: storeActions.LOG_PAGE_VIEW,
|
|
},
|
|
{
|
|
actionName: storeActions.INITIALIZE_SESSION,
|
|
},
|
|
],
|
|
};
|
|
const mocks = setupMocksForJsFiles(mockData);
|
|
|
|
const testCookieValue = {
|
|
sid: "10000000-0000-0000-0000-000000000001",
|
|
};
|
|
|
|
setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) });
|
|
|
|
await analyticsMixin.methods.logPageView(type, payload);
|
|
|
|
expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled();
|
|
});
|
|
|
|
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);
|
|
|
|
await analyticsMixin.methods.logCustomEvent(
|
|
"someCat",
|
|
"someAction",
|
|
"someLabel",
|
|
"someVal"
|
|
);
|
|
|
|
expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled();
|
|
});
|
|
|
|
test("pushEventToGA, should call dataLayer push and logCustomEvent too", async () => {
|
|
// Arrange
|
|
window.dataLayer = [];
|
|
const mockData = {
|
|
actionList: [
|
|
{
|
|
actionName: storeActions.LOG_CUSTOM_EVENT,
|
|
},
|
|
{
|
|
actionName: storeActions.INITIALIZE_SESSION,
|
|
},
|
|
],
|
|
};
|
|
const mocks = setupMocksForJsFiles(mockData);
|
|
var mockDataLayer = [];
|
|
mockDataLayer.push({
|
|
event: "event",
|
|
category: "category",
|
|
action: "action",
|
|
label: "label",
|
|
value: undefined,
|
|
path: "/fmg/mockedPageName",
|
|
});
|
|
|
|
// Act
|
|
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", async () => {
|
|
// Arrange
|
|
window.dataLayer = [];
|
|
var expectedDataLayer = [];
|
|
expectedDataLayer.push({
|
|
event: "event",
|
|
category: "category",
|
|
action: "action",
|
|
label: "33333",
|
|
value: undefined,
|
|
path: "/fmg/mockedPageName",
|
|
});
|
|
|
|
const mockData = {
|
|
actionList: [
|
|
{
|
|
actionName: storeActions.LOG_CUSTOM_EVENT,
|
|
},
|
|
{
|
|
actionName: storeActions.INITIALIZE_SESSION,
|
|
},
|
|
],
|
|
};
|
|
|
|
const mocks = setupMocksForJsFiles(mockData);
|
|
|
|
// Act
|
|
await analyticsMixin.methods.pushEventToGA(
|
|
"category",
|
|
"action",
|
|
"1111122222333333",
|
|
false,
|
|
ValueToLogTypes.LAST_5
|
|
);
|
|
|
|
// Assert
|
|
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", async () => {
|
|
// Arrange
|
|
window.dataLayer = [];
|
|
var expectedDataLayer = [];
|
|
expectedDataLayer.push({
|
|
event: "event",
|
|
category: "category",
|
|
action: "action",
|
|
label: "111",
|
|
value: undefined,
|
|
path: "/fmg/mockedPageName",
|
|
});
|
|
|
|
const mockData = {
|
|
actionList: [
|
|
{
|
|
actionName: storeActions.LOG_CUSTOM_EVENT,
|
|
},
|
|
{
|
|
actionName: storeActions.INITIALIZE_SESSION,
|
|
},
|
|
],
|
|
};
|
|
|
|
const mocks = setupMocksForJsFiles(mockData);
|
|
|
|
// Act
|
|
await analyticsMixin.methods.pushEventToGA(
|
|
"category",
|
|
"action",
|
|
"111",
|
|
false,
|
|
ValueToLogTypes.LAST_5
|
|
);
|
|
|
|
// Assert
|
|
expect(expectedDataLayer).toEqual(expect.arrayContaining(window.dataLayer));
|
|
});
|
|
|
|
test("Experiments, should push to dataLayer with default Google Custom Dimension Index", () => {
|
|
// Arrange
|
|
window.dataLayer = [];
|
|
|
|
const mockExperimentData = [
|
|
{
|
|
settings: {},
|
|
variationName: "test",
|
|
universeName: "testUniverse",
|
|
},
|
|
];
|
|
|
|
// Mock store
|
|
jest.mock(
|
|
"@/store",
|
|
() => {
|
|
return {};
|
|
},
|
|
{ virtual: true }
|
|
);
|
|
|
|
store.getters = {
|
|
applicationUser: {
|
|
experiments: [
|
|
{
|
|
settings: {},
|
|
variationName: "test",
|
|
universeName: "testUniverse",
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
// Act
|
|
analyticsMixin.methods.pushExperimentsToDataLayer(mockExperimentData);
|
|
|
|
// Assert
|
|
expect(window.dataLayer).toEqual([
|
|
{
|
|
experimentId_99: undefined,
|
|
variationId_99: undefined,
|
|
experimentName_99: "testUniverse",
|
|
variationName_99: "test",
|
|
customDimension_99: "undefined_undefined_testUniverse_test",
|
|
},
|
|
]);
|
|
});
|
|
|
|
test("Experiments, should push to dataLayer with custom Google Custom Dimension Index", () => {
|
|
// Arrange
|
|
window.dataLayer = [];
|
|
|
|
const mockExperimentData = [
|
|
{
|
|
settings: { "Google Custom Dimension Index": "5" },
|
|
variationName: "test",
|
|
universeName: "testUniverse",
|
|
},
|
|
];
|
|
|
|
// Mock store
|
|
jest.mock(
|
|
"@/store",
|
|
() => {
|
|
return {};
|
|
},
|
|
{ virtual: true }
|
|
);
|
|
|
|
store.getters = {
|
|
applicationUser: {
|
|
experiments: [
|
|
{
|
|
settings: { "Google Custom Dimension Index": "5" },
|
|
variationName: "test",
|
|
universeName: "testUniverse",
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
// Act
|
|
analyticsMixin.methods.pushExperimentsToDataLayer(mockExperimentData);
|
|
|
|
// Assert
|
|
expect(window.dataLayer).toEqual([
|
|
{
|
|
experimentId_5: undefined,
|
|
variationId_5: undefined,
|
|
experimentName_5: "testUniverse",
|
|
variationName_5: "test",
|
|
customDimension_5: "undefined_undefined_testUniverse_test",
|
|
},
|
|
]);
|
|
});
|
|
|
|
describe("pushOrderToDataLayer", () => {
|
|
beforeEach(() => {
|
|
store.getters.hasSubmittedOrder = false;
|
|
store.getters.order = {
|
|
vehicle: {
|
|
year: "2020",
|
|
make: "acura",
|
|
model: "mdx",
|
|
style: "4-door sedan",
|
|
carId: "dummyCarId",
|
|
category: "dummyCategory",
|
|
vin: "dummyVin",
|
|
},
|
|
serviceLocation: {
|
|
address: "add1",
|
|
address2: "add2",
|
|
city: "city",
|
|
state: "state",
|
|
zipCode: "11111",
|
|
zipCodeCtu: "11110",
|
|
appointmentType: "IN_SHOP",
|
|
isVehicleProtected: true,
|
|
provider: {
|
|
providerNumber: 2,
|
|
address: {
|
|
streetAddress: "add3",
|
|
city: "city2",
|
|
state: "state2",
|
|
zipCode: "22222",
|
|
zipCodeCtu: "22220",
|
|
},
|
|
},
|
|
techNotes: "",
|
|
},
|
|
customer: {
|
|
firstName: "first",
|
|
lastName: "last",
|
|
emailAddress: "builddigitaltest@safelite.com",
|
|
phoneNumber: "555-555-5555",
|
|
isSmsOptIn: false,
|
|
},
|
|
damage: {
|
|
isRepair: false,
|
|
numberOfChips: null,
|
|
glassToReplace: [{ glassName: "single", glassLocation: "windshield" }],
|
|
},
|
|
lineItems: {
|
|
glassParts: [parts.windshield],
|
|
supportingItems: [],
|
|
vaps: [parts.frontWipers],
|
|
promos: [
|
|
{
|
|
promoCode: "promoTEST",
|
|
kitPrice: 0,
|
|
sellingPrice: 0,
|
|
laborAmount: 0,
|
|
salesTax: 0,
|
|
},
|
|
{
|
|
promoCode: "promoTEST2",
|
|
kitPrice: 0,
|
|
sellingPrice: 0,
|
|
laborAmount: 0,
|
|
salesTax: 0,
|
|
},
|
|
],
|
|
},
|
|
payment: {
|
|
isInsurance: false,
|
|
insuranceCoverage: {
|
|
isVerified: null,
|
|
coverageStatus: null,
|
|
coverageVerificationType: null,
|
|
},
|
|
isPia: true,
|
|
piaType: "Afterpay",
|
|
inactivePromos: [],
|
|
},
|
|
schedule: {
|
|
date: "date",
|
|
startTime: "start",
|
|
endTime: "end",
|
|
jobMinMinutes: "30",
|
|
jobMaxMinutes: "45",
|
|
},
|
|
workOrderNumber: "01820-111111",
|
|
workOrderId: "222222222222",
|
|
referralSequenceNumber: "1234567",
|
|
};
|
|
store.getters.isRecalibrationOnOrder = true;
|
|
store.getters.isRecalibrationOnSubmittedState = false;
|
|
});
|
|
|
|
test("Pushes to data layer if nominal", () => {
|
|
// Arrange
|
|
const mockDataLayerFn = jest.fn();
|
|
|
|
window.dataLayer = {
|
|
push: mockDataLayerFn,
|
|
};
|
|
|
|
// Act
|
|
analyticsMixin.methods.pushOrderToDataLayer();
|
|
|
|
// Assert
|
|
expect(mockDataLayerFn).toHaveBeenCalled();
|
|
});
|
|
|
|
test("Pushes populated data to data layer", () => {
|
|
// Arrange
|
|
window.dataLayer = [];
|
|
|
|
// Act
|
|
analyticsMixin.methods.pushOrderToDataLayer();
|
|
|
|
const result = window.dataLayer[0];
|
|
|
|
// Assert
|
|
console.log(result);
|
|
const allFieldsPopulated = Object.keys(result).every(
|
|
(key) =>
|
|
result[key] === false ||
|
|
!!result[key] ||
|
|
(result["accountType"] === "cash"
|
|
? result["isInsuranceVerified"] === "" &&
|
|
result["insuranceDeductible"] === "" &&
|
|
result["isInsuranceItac"] === "" &&
|
|
result["isInsuranceNoComp"] === ""
|
|
: false)
|
|
);
|
|
expect(allFieldsPopulated).toBe(true);
|
|
});
|
|
|
|
test("Pushes correct data when submitted order is present", () => {
|
|
// Arrange
|
|
window.dataLayer = [];
|
|
baseMixin.methods.hasSubmittedOrder = jest.fn().mockReturnValue(true);
|
|
baseMixin.methods.getSubmittedOrder = jest.fn().mockReturnValue(store.getters.order);
|
|
|
|
store.getters.order = {
|
|
vehicle: {
|
|
year: null,
|
|
make: null,
|
|
model: null,
|
|
style: null,
|
|
carId: null,
|
|
category: null,
|
|
vin: null,
|
|
},
|
|
serviceLocation: {
|
|
address: null,
|
|
address2: null,
|
|
city: null,
|
|
state: null,
|
|
zipCode: null,
|
|
zipCodeCtu: null,
|
|
appointmentType: null,
|
|
isVehicleProtected: null,
|
|
provider: {
|
|
providerNumber: null,
|
|
address: {
|
|
streetAddress: null,
|
|
city: null,
|
|
state: null,
|
|
zipCode: null,
|
|
zipCodeCtu: null,
|
|
},
|
|
},
|
|
techNotes: null,
|
|
},
|
|
customer: {
|
|
firstName: null,
|
|
lastName: null,
|
|
emailAddress: null,
|
|
phoneNumber: null,
|
|
isSmsOptIn: null,
|
|
},
|
|
damage: {
|
|
isRepair: null,
|
|
numberOfChips: null,
|
|
glassToReplace: null,
|
|
},
|
|
lineItems: {
|
|
glassParts: null,
|
|
supportingItems: null,
|
|
vaps: null,
|
|
promos: null,
|
|
},
|
|
payment: {
|
|
isInsurance: null,
|
|
insuranceCoverage: {
|
|
isVerified: null,
|
|
coverageStatus: null,
|
|
coverageVerificationType: null,
|
|
},
|
|
isPia: null,
|
|
piaType: null,
|
|
inactivePromos: null,
|
|
},
|
|
schedule: {
|
|
date: null,
|
|
startTime: null,
|
|
endTime: null,
|
|
jobMinMinutes: null,
|
|
jobMaxMinutes: null,
|
|
},
|
|
workOrderNumber: null,
|
|
workOrderId: null,
|
|
};
|
|
|
|
// Act
|
|
analyticsMixin.methods.pushOrderToDataLayer();
|
|
|
|
const result = window.dataLayer[0];
|
|
|
|
// Assert
|
|
console.log(result);
|
|
const allFieldsPopulated = Object.keys(result).every(
|
|
(key) =>
|
|
result[key] === false ||
|
|
!!result[key] ||
|
|
(result["accountType"] === "cash"
|
|
? result["isInsuranceVerified"] === "" &&
|
|
result["insuranceDeductible"] === "" &&
|
|
result["isInsuranceItac"] === "" &&
|
|
result["isInsuranceNoComp"] === ""
|
|
: false)
|
|
);
|
|
expect(allFieldsPopulated).toBe(true);
|
|
});
|
|
|
|
test("Pushes default values when data is missing", () => {
|
|
// Arrange
|
|
window.dataLayer = [];
|
|
store.getters.order = {
|
|
vehicle: {
|
|
year: null,
|
|
make: null,
|
|
model: null,
|
|
style: null,
|
|
carId: null,
|
|
category: null,
|
|
vin: null,
|
|
},
|
|
serviceLocation: {
|
|
address: null,
|
|
address2: null,
|
|
city: null,
|
|
state: null,
|
|
zipCode: null,
|
|
zipCodeCtu: null,
|
|
appointmentType: null,
|
|
isVehicleProtected: null,
|
|
provider: {
|
|
providerNumber: null,
|
|
address: {
|
|
streetAddress: null,
|
|
city: null,
|
|
state: null,
|
|
zipCode: null,
|
|
zipCodeCtu: null,
|
|
},
|
|
},
|
|
techNotes: null,
|
|
},
|
|
customer: {
|
|
firstName: null,
|
|
lastName: null,
|
|
emailAddress: null,
|
|
phoneNumber: null,
|
|
isSmsOptIn: null,
|
|
},
|
|
damage: {
|
|
isRepair: null,
|
|
numberOfChips: null,
|
|
glassToReplace: null,
|
|
},
|
|
lineItems: {
|
|
glassParts: null,
|
|
supportingItems: null,
|
|
vaps: null,
|
|
promos: null,
|
|
},
|
|
payment: {
|
|
isInsurance: null,
|
|
insuranceCoverage: {
|
|
isVerified: null,
|
|
coverageStatus: null,
|
|
coverageVerificationType: null,
|
|
},
|
|
isPia: null,
|
|
piaType: null,
|
|
inactivePromos: null,
|
|
},
|
|
schedule: {
|
|
date: null,
|
|
startTime: null,
|
|
endTime: null,
|
|
jobMinMinutes: null,
|
|
jobMaxMinutes: null,
|
|
},
|
|
workOrderNumber: null,
|
|
workOrderId: null,
|
|
};
|
|
|
|
// Act
|
|
analyticsMixin.methods.pushOrderToDataLayer();
|
|
|
|
const result = window.dataLayer[0];
|
|
|
|
// Assert
|
|
console.log(result);
|
|
const allFieldsPopulatedOrDefault = Object.keys(result).every(
|
|
(key) => result[key] === false || !!result[key] || result[key] === ""
|
|
);
|
|
expect(allFieldsPopulatedOrDefault).toBe(true);
|
|
});
|
|
|
|
test("Glass and Promo strings correctly formatted", () => {
|
|
// Arrange
|
|
window.dataLayer = [];
|
|
|
|
// Act
|
|
analyticsMixin.methods.pushOrderToDataLayer();
|
|
|
|
// Assert
|
|
const glassString = window.dataLayer[0].glassToReplace;
|
|
const promoString = window.dataLayer[0].promoCodes;
|
|
|
|
expect(glassString).toMatch(/(\w+\/\w+)?(,\w+\/\w+)*/);
|
|
expect(promoString).toMatch(/(\w+)?(,\w+)*/);
|
|
});
|
|
|
|
test("Pushes referralSequenceNumber when present on order", () => {
|
|
window.dataLayer = [];
|
|
|
|
analyticsMixin.methods.pushOrderToDataLayer();
|
|
|
|
expect(window.dataLayer[0].referralSequenceNumber).toBe("1234567");
|
|
});
|
|
});
|
|
|
|
test("Obj is not null after action prepended", () => {
|
|
//Arrange
|
|
const obj = { baseMethodName: "testMethodName", data: "testData" };
|
|
const method = { name: "testMethodName", data: "testData" };
|
|
const action = "testAction";
|
|
|
|
//Act
|
|
analyticsMixin.methods.prependActionToMethod(obj, method, action);
|
|
|
|
//Assert
|
|
expect(obj != null);
|
|
});
|
|
test("Obj method name does not include bound", () => {
|
|
//Arrange
|
|
const obj = { baseMethodName: "testMethodName", data: "testData" };
|
|
const method = { name: "testMethodName", data: "testData" };
|
|
const action = "testAction";
|
|
|
|
//Act
|
|
analyticsMixin.methods.prependActionToMethod(obj, method, action);
|
|
|
|
//Assert
|
|
expect(method.name.startsWith("bound ")).toBe(false);
|
|
});
|
|
test("Prepended action does not include bound", () => {
|
|
//Arrange
|
|
const obj = { baseMethodName: "testMethodName", data: "testData" };
|
|
const method = { name: "testMethodName", data: "testData" };
|
|
const action = "testAction";
|
|
|
|
//Act
|
|
analyticsMixin.methods.prependActionToMethod(obj, method, action);
|
|
|
|
//Assert
|
|
expect(action.startsWith("bound ")).toBe(false);
|
|
});
|
|
|
|
test("analyticsPageEvents returns constants analyticsPageEvents", () => {
|
|
//Act
|
|
const analyticsPE = analyticsMixin.computed.analyticsPageEvents();
|
|
|
|
//Assert
|
|
expect(analyticsPE).toEqual(analyticsPageEvents);
|
|
});
|
|
|
|
test("GaActions returns constants GaActions", () => {
|
|
//Act
|
|
const gaActions = analyticsMixin.computed.GaActions();
|
|
|
|
//Assert
|
|
expect(gaActions).toEqual(GaActions);
|
|
});
|
|
|
|
test("GaCategories returns constants GaCategories", () => {
|
|
//Act
|
|
const gaCategories = analyticsMixin.computed.GaCategories();
|
|
|
|
//Assert
|
|
expect(gaCategories).toEqual(GaCategories);
|
|
});
|
|
|
|
test("GaLabels returns constants GaLabels", () => {
|
|
//Act
|
|
const gaLabels = analyticsMixin.computed.GaLabels();
|
|
|
|
//Assert
|
|
expect(gaLabels).toEqual(GaLabels);
|
|
});
|
|
|
|
describe("getBailoutCodeEnum", () => {
|
|
test("returns null when bailout code is not provided", () => {
|
|
expect(analyticsMixin.methods.getBailoutCodeEnum(null)).toBeNull();
|
|
expect(analyticsMixin.methods.getBailoutCodeEnum(undefined)).toBeNull();
|
|
});
|
|
|
|
test("returns formatted value when bailout code matches a known enum", () => {
|
|
const result = analyticsMixin.methods.getBailoutCodeEnum(bailoutCodes.PART_NOT_FOUND);
|
|
|
|
expect(result).toBe(`${bailoutCodes.PART_NOT_FOUND}_PART_NOT_FOUND`);
|
|
});
|
|
|
|
test("returns original value when bailout code does not match a known enum", () => {
|
|
expect(analyticsMixin.methods.getBailoutCodeEnum(999)).toBe(999);
|
|
});
|
|
});
|
|
|
|
describe("pushFmgSessionData", () => {
|
|
beforeEach(() => {
|
|
baseMixin.methods.hasSubmittedOrder = jest.fn().mockReturnValue(false);
|
|
baseMixin.methods.getSubmittedOrder = jest.fn();
|
|
baseMixin.methods.hasSubmittedApplicationUser = jest.fn().mockReturnValue(false);
|
|
baseMixin.methods.getSubmittedApplicationUser = jest.fn();
|
|
store.getters.order = {
|
|
vehicle: { vinRequired: false },
|
|
payment: {
|
|
isInsurance: false,
|
|
isClaimAndCoverage: false,
|
|
isPia: false,
|
|
},
|
|
damage: { isRepair: false },
|
|
lineItems: {},
|
|
serviceLocation: {},
|
|
customer: {},
|
|
policy: {},
|
|
};
|
|
store.getters.applicationUser = {
|
|
experiments: [],
|
|
pageData: {
|
|
bailout: {
|
|
bailoutCode: bailoutCodes.PART_NOT_FOUND,
|
|
},
|
|
},
|
|
};
|
|
});
|
|
|
|
test("dispatches session data with formatted bailout code", async () => {
|
|
const mocks = setupMocksForJsFiles({
|
|
actionList: [{ actionName: storeActions.LOG_FMG_SESSION_DATA }],
|
|
});
|
|
|
|
await analyticsMixin.methods.pushFmgSessionData();
|
|
|
|
expect(mocks.baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith(
|
|
storeActions.LOG_FMG_SESSION_DATA,
|
|
expect.objectContaining({
|
|
currentPage: "mockedPageName",
|
|
bailoutCode: `${bailoutCodes.PART_NOT_FOUND}_PART_NOT_FOUND`,
|
|
}),
|
|
false
|
|
);
|
|
});
|
|
});
|
|
|
|
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();
|
|
});
|
|
});
|
|
describe("push product array and ecommerce cart to data layer", () => {
|
|
const mocks = setupMocksForJsFiles();
|
|
it("should push product array to data layer correctly", () => {
|
|
// Mock the methods
|
|
mocks.baseMixin.methods.hasSubmittedOrder = jest.fn();
|
|
mocks.baseMixin.methods.getSubmittedOrder = jest.fn();
|
|
mocks.baseMixin.methods.getTotalPriceOfAllLineItemsAndChildParts = jest.fn();
|
|
mocks.baseMixin.methods.hasSubmittedOrder.mockReturnValue(true);
|
|
mocks.baseMixin.methods.getSubmittedOrder.mockReturnValue({
|
|
lineItems: {
|
|
glassParts: [{ partNumber: "GP1", partType: "glass" }],
|
|
supportingItems: [{ partNumber: "SI1", partType: "support" }],
|
|
vaps: [{ partNumber: "VAP1", partType: "vap" }],
|
|
promos: [{ partNumber: "DISCOUNT", partType: "promo", promoCode: "PROMO4" }],
|
|
},
|
|
});
|
|
mocks.baseMixin.methods.getTotalPriceOfAllLineItemsAndChildParts.mockReturnValue(100);
|
|
|
|
// Call the function
|
|
analyticsMixin.methods.pushProductArrayToDataLayer();
|
|
|
|
// Assertions
|
|
expect(mocks.baseMixin.methods.hasSubmittedOrder).toHaveBeenCalled();
|
|
expect(mocks.baseMixin.methods.getSubmittedOrder).toHaveBeenCalled();
|
|
expect(
|
|
mocks.baseMixin.methods.getTotalPriceOfAllLineItemsAndChildParts
|
|
).toHaveBeenCalled();
|
|
});
|
|
it("should push eCommerce cart to data layer correctly", () => {
|
|
// Mock the methods
|
|
mocks.baseMixin.methods.hasSubmittedOrder = jest.fn();
|
|
mocks.baseMixin.methods.getSubmittedOrder = jest.fn();
|
|
mocks.baseMixin.methods.hasSubmittedOrder.mockReturnValue(true);
|
|
mocks.baseMixin.methods.getSubmittedOrder.mockReturnValue({
|
|
lineItems: {
|
|
supportingItems: [
|
|
{ partType: "RECALIBRATION", partNumber: "R1" },
|
|
{ partType: "DISPOSAL FEE", partNumber: "D1" },
|
|
{ partType: "MOBILE FEE", partNumber: "M1" },
|
|
{ partType: "REPAIR FEE", partNumber: "RF1" },
|
|
],
|
|
vaps: [{ partType: "FRONT WIPER", partNumber: "FW1" }],
|
|
},
|
|
payment: { isInsurance: true },
|
|
policy: { isItac: true, isNoComp: false },
|
|
});
|
|
|
|
// Call the function
|
|
analyticsMixin.methods.pushECommerceCartToDataLayer();
|
|
|
|
// Assertions
|
|
expect(mocks.baseMixin.methods.hasSubmittedOrder).toHaveBeenCalled();
|
|
expect(mocks.baseMixin.methods.getSubmittedOrder).toHaveBeenCalled();
|
|
expect(
|
|
mocks.baseMixin.methods.getTotalPriceOfAllLineItemsAndChildParts
|
|
).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("pushCommissionJunctionGtmDataToDataLayer", () => {
|
|
const mocks = setupMocksForJsFiles();
|
|
it("should handle empty affiliateCookies", () => {
|
|
store.getters = {
|
|
applicationUser: {
|
|
affiliateCookies: [],
|
|
},
|
|
};
|
|
|
|
mocks.baseMixin.methods.hasSubmittedOrder = jest.fn();
|
|
mocks.baseMixin.methods.hasSubmittedOrder.mockReturnValue(false);
|
|
const result = analyticsMixin.methods.pushCommissionJunctionGtmDataToDataLayer();
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
test("Pushes populated data to data layer", () => {
|
|
mocks.baseMixin.methods.hasSubmittedOrder = jest.fn();
|
|
mocks.baseMixin.methods.hasSubmittedOrder.mockReturnValue(true);
|
|
mocks.baseMixin.methods.getSubmittedOrder = jest.fn();
|
|
mocks.baseMixin.methods.getSubmittedOrder.mockReturnValue({
|
|
vehicle: {
|
|
year: "2020",
|
|
make: "acura",
|
|
model: "mdx",
|
|
style: "4-door sedan",
|
|
carId: "dummyCarId",
|
|
category: "dummyCategory",
|
|
vin: "dummyVin",
|
|
},
|
|
serviceLocation: {
|
|
address: "add1",
|
|
address2: "add2",
|
|
city: "city",
|
|
state: "state",
|
|
zipCode: "11111",
|
|
zipCodeCtu: "11110",
|
|
appointmentType: "IN_SHOP",
|
|
isVehicleProtected: true,
|
|
provider: {
|
|
providerNumber: 2,
|
|
address: {
|
|
streetAddress: "add3",
|
|
city: "city2",
|
|
state: "state2",
|
|
zipCode: "22222",
|
|
zipCodeCtu: "22220",
|
|
},
|
|
},
|
|
techNotes: "",
|
|
},
|
|
customer: {
|
|
firstName: "first",
|
|
lastName: "last",
|
|
emailAddress: "builddigitaltest@safelite.com",
|
|
phoneNumber: "555-555-5555",
|
|
isSmsOptIn: false,
|
|
},
|
|
damage: {
|
|
isRepair: false,
|
|
numberOfChips: null,
|
|
glassToReplace: [{ glassName: "single", glassLocation: "windshield" }],
|
|
},
|
|
lineItems: {
|
|
glassParts: [parts.windshield],
|
|
supportingItems: [],
|
|
vaps: [parts.frontWipers],
|
|
promos: [
|
|
{
|
|
promoCode: "promoTEST",
|
|
kitPrice: 0,
|
|
sellingPrice: 0,
|
|
laborAmount: 0,
|
|
salesTax: 0,
|
|
},
|
|
{
|
|
promoCode: "promoTEST2",
|
|
kitPrice: 0,
|
|
sellingPrice: 0,
|
|
laborAmount: 0,
|
|
salesTax: 0,
|
|
},
|
|
],
|
|
},
|
|
payment: {
|
|
isInsurance: false,
|
|
insuranceCoverage: {
|
|
isVerified: null,
|
|
coverageStatus: null,
|
|
coverageVerificationType: null,
|
|
},
|
|
isPia: true,
|
|
piaType: "Afterpay",
|
|
inactivePromos: [],
|
|
},
|
|
schedule: {
|
|
date: "date",
|
|
startTime: "start",
|
|
endTime: "end",
|
|
jobMinMinutes: "30",
|
|
jobMaxMinutes: "45",
|
|
},
|
|
workOrderNumber: "01820-111111",
|
|
workOrderId: "222222222222",
|
|
referralSequenceNumber: "1111111",
|
|
});
|
|
store.getters = {
|
|
applicationUser: {
|
|
affiliateCookies: [
|
|
{
|
|
CookieName: "Cookie1",
|
|
CookieValue: "timestamp=2023-09-18/12:00:00&tagEvent=event1",
|
|
},
|
|
{
|
|
CookieName: "Cookie2",
|
|
CookieValue: "timestamp=2023-09-19/12:00:00&tagEvent=event2",
|
|
},
|
|
],
|
|
},
|
|
};
|
|
// Arrange
|
|
window.dataLayer = [];
|
|
|
|
// Act
|
|
analyticsMixin.methods.pushCommissionJunctionGtmDataToDataLayer();
|
|
|
|
const result = window.dataLayer[0];
|
|
|
|
// Assert
|
|
console.log(result);
|
|
const allFieldsPopulated = Object.keys(result).every(
|
|
(key) => result[key] === false || !!result[key]
|
|
);
|
|
expect(allFieldsPopulated).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
jest.mock("@/router", () => ({
|
|
currentRoute: {
|
|
value: {
|
|
name: "mockedPageName",
|
|
},
|
|
},
|
|
}));
|