diff --git a/src/common-components/button-question/button-question.spec.js b/src/common-components/button-question/button-question.spec.js index 591b34317..b70304035 100644 --- a/src/common-components/button-question/button-question.spec.js +++ b/src/common-components/button-question/button-question.spec.js @@ -1,9 +1,8 @@ import { shallowMount } from "@vue/test-utils"; import buttonQuestion from "@/common-components/button-question/button-question"; -import { nextTick } from "vue"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; -import store from "@/store"; -jest.mock("@/store",()=>{return{};},{virtual:true}); + +jest.mock("@/store", () => { return {}; }, { virtual: true }); describe("buttonQuestion.vue", () => { it("Should show overflow classes on fieldset if isOverflowScrollable is true", () => { @@ -76,7 +75,7 @@ describe("buttonQuestion.vue", () => { describe("buttonQuestion.vue", () => { it("getColLength should return '' if prop isWide is set to false", () => { // Act - const localThis = { + const localThis = { isWide: false, answers: ['a', 'b'] } @@ -110,12 +109,12 @@ describe("buttonQuestion.vue", () => { describe("buttonQuestion.vue", () => { it("Should trigger event modelValue change to new value on when radio button selected", async () => { // Act - const wrapper = shallowMount(buttonQuestion); + const wrapper = shallowMount(buttonQuestion, setupMocks({})); await wrapper.setProps({ answers: ["2022", "2021", "2020"], isMultiSelect: false }); - const val = {checkValue: true, value: "2021", } + const val = { checkValue: true, value: "2021", } wrapper.vm.handleCheckedChanged(val); // Assert expect(wrapper.emitted()["update:modelValue"][0]).toEqual([["2021"]]); @@ -125,13 +124,13 @@ describe("buttonQuestion.vue", () => { describe("buttonQuestion.vue", () => { it("Should add values to array on checkbox click", () => { // Act - const wrapper = shallowMount(buttonQuestion, { + const wrapper = shallowMount(buttonQuestion, setupMocks({ propsData: { modelValue: ["2022", "2021", "2020"], isMultiSelect: true, } - }); - const val = {checkValue: true, value: "2019", } + })); + const val = { checkValue: true, value: "2019", } wrapper.vm.handleCheckedChanged(val); // Assert expect(wrapper.emitted()["update:modelValue"][0]).toEqual([["2022", "2021", "2020", "2019"]]); @@ -141,12 +140,12 @@ describe("buttonQuestion.vue", () => { describe("buttonQuestion.vue", () => { it("Should add a value to this.selectedValues if prop isMultiSelect is true, checkValue is true and this.selectedValues already exists", () => { // Act - const wrapper = shallowMount(buttonQuestion, { + const wrapper = shallowMount(buttonQuestion, setupMocks({ propsData: { - isMultiSelect: true, - modelValue: [ 'a', 'b' ] + isMultiSelect: true, + modelValue: ['a', 'b'] } - }); + })); const val = { checkValue: true, value: "2021", } wrapper.vm.handleCheckedChanged(val); @@ -158,12 +157,13 @@ describe("buttonQuestion.vue", () => { describe("buttonQuestion.vue", () => { it("Should remove a value to this.selectedValues if prop isMultiSelect is true, checkValue is false and this.selectedValues already exists", () => { // Act - const wrapper = shallowMount(buttonQuestion, { + const wrapper = shallowMount(buttonQuestion, setupMocks({ propsData: { - isMultiSelect: true, - modelValue: [ 'a', 'b' ] + isMultiSelect: true, + modelValue: ['a', 'b'] } - }); + })); + const val = { checkValue: false, value: "a", } wrapper.vm.handleCheckedChanged(val); @@ -176,12 +176,12 @@ describe("buttonQuestion.vue", () => { describe("buttonQuestion.vue", () => { it("Should do nothing to this.selectedValues if this.selectedValues is not an array", () => { // Act - const wrapper = shallowMount(buttonQuestion, { + const wrapper = shallowMount(buttonQuestion, setupMocks({ propsData: { - isMultiSelect: true, + isMultiSelect: true, modelValue: 'a', } - }); + })); const val = { checkValue: true, value: "c", } wrapper.vm.handleCheckedChanged(val); @@ -189,3 +189,11 @@ describe("buttonQuestion.vue", () => { expect(wrapper.vm.selectedValues).toEqual("a"); }); }); + +function setupMocks(mountOptionsMockData = {}) { + const defaultMountOptions = { route: { query: { fmgPage: 'page-name' } } }; + const baseMountOptions = getMountOptions(Object.assign(defaultMountOptions, mountOptionsMockData)); + const allMountOptions = Object.assign(defaultMountOptions, baseMountOptions); + + return allMountOptions; +} diff --git a/src/common-components/button-question/button-question.vue b/src/common-components/button-question/button-question.vue index 48bbe9227..7fba59c51 100644 --- a/src/common-components/button-question/button-question.vue +++ b/src/common-components/button-question/button-question.vue @@ -52,6 +52,7 @@ import listButtonHorizontal from "@/ux-components/list-button-horizontal/list-bu import listCard from "@/ux-components/list-card/list-card"; import { ErrorMessage } from 'vee-validate'; import radio from "@/ux-components/radio/radio"; +import { queryStrings } from "@/constants/query-strings"; export default { name: "buttonQuestion", @@ -133,6 +134,9 @@ export default { return answer.Name ? answer.Name : answer; }, handleCheckedChanged(val) { + + this.pushEventToGA(this.$route.query[queryStrings.FMG_PAGE], this.GaActions.CLICKED, val.value, true); + if(this.isMultiSelect && this.selectedValues) { // Add or remove item to array of data to emit const newSelectedValues = this.selectedValues; diff --git a/src/constants/analytics-page-events.js b/src/constants/analytics-page-events.js deleted file mode 100644 index 74ff7cc2f..000000000 --- a/src/constants/analytics-page-events.js +++ /dev/null @@ -1,6 +0,0 @@ -const analyticsPageEvents = { - ENTRY: "ENTRY", - EVENT: "EVENT" -}; - -export { analyticsPageEvents }; diff --git a/src/constants/analytics.js b/src/constants/analytics.js new file mode 100644 index 000000000..f6f6ff82e --- /dev/null +++ b/src/constants/analytics.js @@ -0,0 +1,32 @@ +const analyticsPageEvents = { + ENTRY: "ENTRY", + EVENT: "EVENT" +}; + +// GA Constants +const GaEvents = { + GENERIC_EVENT: 'ga_Event', + PAGE_VIEW_EVENT : 'logPageview' +}; + +const GaCategories = { + API_RESPONSE: 'Api_Response', + EVOX: 'Evox' +}; + +const GaActions = { + RESULT: 'Result', + CLICKED: 'Clicked', + VIF: 'vif', + SUBMITTED: 'Submitted', +}; + +const GaLabels = { + SUCCESS: 'Success', + ERROR: 'Error', + LICENSE_PLATE_LOOKUP: 'License_Plate_Look_Up', + VIN_LOOKUP: 'Vin_Look_Up', +}; + + +export { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents}; diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index 82b286bc4..ca1555e7b 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -74,6 +74,10 @@ const endpoints = { LogActivity:{ url: "/analytics/api/v1/analytics/activity", method: "POST", + }, + GetExperimentsByUserForGa: { + url: "/analytics/api/v1/analytics/get-experiments-for-GA", + method: "GET", } }; diff --git a/src/constants/experiments.js b/src/constants/experiments.js index 13a51aa7f..cd2325a24 100644 --- a/src/constants/experiments.js +++ b/src/constants/experiments.js @@ -1,6 +1,10 @@ const experimentUniverses = { CONCEPT_FUNNEL: 'ConceptFunnel' }; + +const experimentSettings = { + GOOGLE_CUSTOM_DIMENSION_INDEX: 'Google Custom Dimension Index' +} -export { experimentUniverses }; +export { experimentUniverses, experimentSettings}; \ No newline at end of file diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 958d02d04..74dac5043 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -20,6 +20,7 @@ const storeActions = { VALIDATE_ZIP: "validateZip", LOG_EXPERIMENT_EXPOSURE: "logExperimentExposure", LOG_ACTIVITY: "logActivity", + GET_EXPERIMENTS_BY_USER_FOR_GA: "getExperimentsByUserForGa", // DEPENDENCY MUTATIONS RESET_VEHICLE_STATE_AND_DEPENDENCIES: "resetVehicleAndDependencies", diff --git a/src/global-methods.js b/src/global-methods.js index c0823f29a..e7996c4fa 100644 --- a/src/global-methods.js +++ b/src/global-methods.js @@ -1,39 +1,34 @@ import axios from "axios"; +import analyticsMixIn from "@/mixins/analytics-mixin.js"; + import { applicationConfig } from "@/constants/application-config.js"; -import httpStatusCodes from "http-status-codes"; +import { GaCategories, GaActions, GaLabels } from "@/constants/analytics"; export default { - callHttpClient({ method, endpoint, payload }) { + callHttpClient({ method, endpoint, payload, logApiCall = true }) { return new Promise((resolve, reject) => { const apiGatewayUrl = applicationConfig.CONSUMER_APIGATEWAY_URL; + const payloadAndAnalyticsData = Object.assign({}, payload, { AppName: "FixMyGlass" }); - const payloadAndAnalyticsData = Object.assign({}, payload, { - AppName: "FixMyGlass", - }); + axios({ method: method, url: apiGatewayUrl + endpoint, data: payloadAndAnalyticsData, crossDomain: true, responseType: {} }) + .then((response) => { - axios({ - method: method, - url: apiGatewayUrl + endpoint, - data: payloadAndAnalyticsData, - crossDomain: true, - responseType: {}, - }).then( - (response) => { - if (response.status == httpStatusCodes.OK) { - if(response.data == undefined) { - reject(response); - }else{ - resolve(response); - } - } else { - reject(response); + if (logApiCall) { + analyticsMixIn.methods.pushEventToGA(GaCategories.API_RESPONSE, GaActions.RESULT, `${GaLabels.SUCCESS}_${endpoint}`, true); } + + return resolve(response); }, - (error) => { - console.error(error); - return reject(error.response); - } - ); + error => { + console.error(error); + + if (logApiCall) { + analyticsMixIn.methods.pushEventToGA(GaCategories.API_RESPONSE, GaActions.RESULT, `${GaLabels.ERROR}_${endpoint}`, true); + } + + return reject(error.response); + } + ); }); }, @@ -48,11 +43,7 @@ export default { responseType: {}, }).then( (response) => { - if (response.status == httpStatusCodes.OK) { - resolve(response); - } else { - reject(response); - } + resolve(response); }, (error) => { return reject(error.response); diff --git a/src/global-methods.spec.js b/src/global-methods.spec.js index 59fa15b77..5dcba0ccd 100644 --- a/src/global-methods.spec.js +++ b/src/global-methods.spec.js @@ -1,13 +1,16 @@ import globalMethods from "@/global-methods"; import axios from "axios"; +import analyticsMixIn from "@/mixins/analytics-mixin"; //Mock external dependencies jest.mock("axios"); +jest.mock("@/mixins/analytics-mixin"); it("Global Methods - Call Http Client - Should Resolve Promise", () => { //Arrange const endpoint = "https://mock.safelite.com"; const httpArgs = setupMocksForHttpClient({ endpoint: endpoint }); + analyticsMixIn.methods.pushEventToGA = jest.fn(); //Act globalMethods.callHttpClient(httpArgs).then((response) => { @@ -25,6 +28,7 @@ it("Global Methods - Call Http Client - Should Reject Promise", () => { endpoint: endpoint, isError: true, }); + analyticsMixIn.methods.pushEventToGA = jest.fn(); //Act globalMethods.callHttpClient(httpArgs).catch((err) => { @@ -72,5 +76,6 @@ function setupMocksForHttpClient({ return { endpoint: endpoint, + logApiCall: true }; } diff --git a/src/helpers/damage-helper.js b/src/helpers/damage-helper.js index 453aadf69..cf363e4d9 100644 --- a/src/helpers/damage-helper.js +++ b/src/helpers/damage-helper.js @@ -7,7 +7,7 @@ export function getDamageString() { } export async function isGlassAvailableForCarId(carId){ - const newGlassOptions = await baseMixin.methods.dispatchNonBlockingStoreAction( + const newGlassOptions = await baseMixin.methods.dispatchStoreAction( storeActions.GET_DAMAGE_OPTIONS, { carId: carId } ); diff --git a/src/helpers/damage-helper.spec.js b/src/helpers/damage-helper.spec.js index abc8defe2..96cbdd0d4 100644 --- a/src/helpers/damage-helper.spec.js +++ b/src/helpers/damage-helper.spec.js @@ -22,7 +22,7 @@ jest.mock("@/store", () => ({ // windshieldOptions: {availableReplacementOptions: ["windshield"]} // } // } - // baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn().mockImplementation(()=> { + // baseMixin.methods.dispatchStoreAction = jest.fn().mockImplementation(()=> { // return updatedOptions; // }); // const misMatch = await isGlassAvailableForCarId(); diff --git a/src/helpers/heritage-integration/order-helper.js b/src/helpers/heritage-integration/order-helper.js index 35c3b8570..b18b64a85 100644 --- a/src/helpers/heritage-integration/order-helper.js +++ b/src/helpers/heritage-integration/order-helper.js @@ -18,7 +18,7 @@ export async function loadOrderIfPresent() { // Reset state if cookie says to. if (funnelCookie.ShouldResetState) { - baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.RESET_STATE); + baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE); deleteFunnelCookie(); return null; } @@ -34,10 +34,10 @@ export async function loadOrderIfPresent() { update the cookie. */ export async function saveOrder() { - const savedOrderInfo = await baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.SAVE_ORDER); + const savedOrderInfo = await baseMixin.methods.dispatchStoreAction(storeActions.SAVE_ORDER); // Save the referral information back from the store. - await baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.SET_REFERRAL_INFORMATION, { + await baseMixin.methods.dispatchStoreAction(storeActions.SET_REFERRAL_INFORMATION, { referralNumber: savedOrderInfo.data.referralNumber, referralCorrelationId: savedOrderInfo.data.referralCorrelationId, referralDate: savedOrderInfo.data.referralDate, @@ -56,7 +56,7 @@ export async function saveOrder() { and returns the response. */ async function loadOrder(referralNumber, referralDate, referralCorrelationId, accountNumber) { - const response = await baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.LOAD_ORDER, + const response = await baseMixin.methods.dispatchStoreAction(storeActions.LOAD_ORDER, { referralNumber: referralNumber.toString(), referralDate: referralDate, diff --git a/src/helpers/heritage-integration/order-helper.spec.js b/src/helpers/heritage-integration/order-helper.spec.js index cbe54ba25..803c94b6b 100644 --- a/src/helpers/heritage-integration/order-helper.spec.js +++ b/src/helpers/heritage-integration/order-helper.spec.js @@ -48,7 +48,7 @@ describe("loadOrderIfPresent", () => { // Assert expect(cookieHelper.getFunnelCookie).toHaveBeenCalled(); - expect(mocks.baseMixin.methods.dispatchNonBlockingStoreAction).toHaveBeenCalledWith(storeActions.RESET_STATE); + expect(mocks.baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith(storeActions.RESET_STATE); }); test("Funnel cookie is null => store is unchanged", () => { @@ -68,7 +68,7 @@ describe("loadOrderIfPresent", () => { // Assert expect(cookieHelper.getFunnelCookie).toHaveBeenCalled(); - expect(mocks.baseMixin.methods.dispatchNonBlockingStoreAction).not.toHaveBeenCalledWith(storeActions.RESET_STATE); + expect(mocks.baseMixin.methods.dispatchStoreAction).not.toHaveBeenCalledWith(storeActions.RESET_STATE); }); test("Funnel cookie valid, should call loadOrder", async () => { @@ -90,7 +90,7 @@ describe("loadOrderIfPresent", () => { // Assert expect(cookieHelper.getFunnelCookie).toHaveBeenCalled(); - expect(mocks.baseMixin.methods.dispatchNonBlockingStoreAction).not.toHaveBeenCalledWith(storeActions.LOAD_ORDER); + expect(mocks.baseMixin.methods.dispatchStoreAction).not.toHaveBeenCalledWith(storeActions.LOAD_ORDER); expect(result.ReferralNumber).toBe(123456); expect(result.vehicle.year).toBe(2010); }); @@ -127,8 +127,8 @@ describe("saveOrder", () => { await saveOrder(); // Assert - expect(mocks.baseMixin.methods.dispatchNonBlockingStoreAction).toHaveBeenCalledWith(storeActions.SAVE_ORDER); - expect(mocks.baseMixin.methods.dispatchNonBlockingStoreAction).toHaveBeenCalledWith(storeActions.SET_REFERRAL_INFORMATION, { + expect(mocks.baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith(storeActions.SAVE_ORDER); + expect(mocks.baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith(storeActions.SET_REFERRAL_INFORMATION, { referralNumber: mockReferralNumber, referralDate: mockReferralDate, referralCorrelationId: mockCorrelationId diff --git a/src/helpers/unit-test-helper.js b/src/helpers/unit-test-helper.js index 753465886..770dd893e 100644 --- a/src/helpers/unit-test-helper.js +++ b/src/helpers/unit-test-helper.js @@ -7,17 +7,25 @@ import { cookieNames } from "@/constants/cookie-names"; import { Form } from "vee-validate"; import baseMixin from "@/mixins/base-mixin"; import { getCookieDomainValue } from "@/helpers/heritage-integration/cookie-helper"; +import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents } from "@/constants/analytics"; +import { queryStrings } from "@/constants/query-strings"; +import { routerParams } from "@/router/router-constants/router-params"; // Common methods export function getMountOptions(mockData) { // Define our mocks to attached to the 'global' object for Vue/Jest. const mocks = {}; - //this is mocking if you use the mixin directly(baseMixin.methods.dispatchNonBlockingStoreAction) vs this.dispatchNonBlockingStoreAction - setupBaseMixinDispatchNonBlockingStoreAction(mockData); + //this is mocking if you use the mixin directly(baseMixin.methods.dispatchStoreAction) vs this.dispatchStoreAction + setupBaseMixinDispatchStoreAction(mockData); - mocks.dispatchNonBlockingStoreAction = jest.fn(); - mocks.dispatchNonBlockingStoreAction.mockImplementation((actionName) => { + mocks.pushEventToGA = jest.fn(); + mocks.pushPageViewToGA = jest.fn(); + mocks.logEvent = jest.fn(); + mocks.pushExperimentsToDataLayer = jest.fn(); + + mocks.dispatchStoreAction = jest.fn(); + mocks.dispatchStoreAction.mockImplementation((actionName) => { let actionFilterResult = mockData.actionList.filter( (x) => x.actionName == actionName ); @@ -35,6 +43,13 @@ export function getMountOptions(mockData) { mocks.navigationScenarios = navigationScenarios; mocks.vehicleCategories = vehicleCategories; mocks.fmgPageValues = fmgPageValues; + mocks.analyticsPageEvents = analyticsPageEvents; + mocks.GaCategories = GaCategories; + mocks.GaActions = GaActions; + mocks.GaLabels = GaLabels; + mocks.GaEvents = GaEvents; + mocks.queryStrings = queryStrings; + mocks.routerParams = routerParams; // Mock $store and $router when accessing this.$store/$router mocks.$store = mockData.store; @@ -50,7 +65,7 @@ export function getMountOptions(mockData) { } export function setupMocksForJsFiles(mockData = {}) { - setupBaseMixinDispatchNonBlockingStoreAction(mockData); + setupBaseMixinDispatchStoreAction(mockData); return { baseMixin }; } @@ -90,10 +105,10 @@ export function setupCookies({ funnelCookieValue = "", includeHeritageCookie = t } // Private methods -function setupBaseMixinDispatchNonBlockingStoreAction(mockData) { +function setupBaseMixinDispatchStoreAction(mockData) { if (mockData.actionList !== undefined) { - baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn(); - baseMixin.methods.dispatchNonBlockingStoreAction.mockImplementation((actionName) => { + baseMixin.methods.dispatchStoreAction = jest.fn(); + baseMixin.methods.dispatchStoreAction.mockImplementation((actionName) => { let actionFilterResult = mockData.actionList.filter( (x) => x.actionName == actionName ); diff --git a/src/layouts/form-test/form-test.vue b/src/layouts/form-test/form-test.vue index 046b0c234..8489f9558 100644 --- a/src/layouts/form-test/form-test.vue +++ b/src/layouts/form-test/form-test.vue @@ -95,7 +95,7 @@ export default { // Call APIs const cmsContentPromise = fetchCmsContentForPage('vehicle-damage'); const damageOptionsPromise = - baseMixin.methods.dispatchNonBlockingStoreAction( + baseMixin.methods.dispatchStoreAction( storeActions.GET_DAMAGE_OPTIONS, { carId: store.getters.vehicle.carId } ); diff --git a/src/layouts/license-plate-lookup/license-plate-lookup.spec.js b/src/layouts/license-plate-lookup/license-plate-lookup.spec.js index 63459a97f..8253a97b5 100644 --- a/src/layouts/license-plate-lookup/license-plate-lookup.spec.js +++ b/src/layouts/license-plate-lookup/license-plate-lookup.spec.js @@ -115,7 +115,7 @@ function setupMocks({ }, }) { //Mock api responses - baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn(); + baseMixin.methods.dispatchStoreAction = jest.fn(); const apiResponses = { cmsContent: { FunnelSubHeaderWidget: pageHeaderWidgetHeaderText, diff --git a/src/layouts/license-plate-lookup/license-plate-lookup.vue b/src/layouts/license-plate-lookup/license-plate-lookup.vue index f9f0cb41c..ecee25f79 100644 --- a/src/layouts/license-plate-lookup/license-plate-lookup.vue +++ b/src/layouts/license-plate-lookup/license-plate-lookup.vue @@ -174,6 +174,9 @@ export default { this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); }, async forwardButtonAction() { + + this.pushEventToGA(this.$route.query[this.queryStrings.FMG_PAGE], this.GaActions.SUBMITTED, this.GaLabels.LICENSE_PLATE_LOOKUP , true); + const zipValidation = this.serviceZip ? await this.validateZip(this.serviceZip) : await this.validateZip(this.registrationZip); if (!zipValidation.data.isServiceable) { this.$refs.funnelFooter.removeLoader(); @@ -204,7 +207,7 @@ export default { this.updateCustomerInfo(vinLookup.data.vin, vinLookup.data.vehicle, zipValidation.data.state); - const partsData = await baseMixin.methods.dispatchNonBlockingStoreAction( + const partsData = await baseMixin.methods.dispatchStoreAction( this.storeActions.GET_PARTS_OR_QUESTIONS, { carId: vinLookup.data.vehicle.carId, @@ -226,13 +229,13 @@ export default { } }, validateZip(zip) { - return baseMixin.methods.dispatchNonBlockingStoreAction( + return baseMixin.methods.dispatchStoreAction( storeActions.VALIDATE_ZIP, { zip } ); }, lookupVin(plate, state) { - return baseMixin.methods.dispatchNonBlockingStoreAction( + return baseMixin.methods.dispatchStoreAction( storeActions.LOOKUP_VIN_BY_PLATE, { licensePlate: plate, licenseState: state } ); diff --git a/src/layouts/vehicle-damage/vehicle-damage.spec.js b/src/layouts/vehicle-damage/vehicle-damage.spec.js index 7b28e4bb5..9a9f7e6d7 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.spec.js +++ b/src/layouts/vehicle-damage/vehicle-damage.spec.js @@ -17,6 +17,7 @@ import { storeMutations } from "@/constants/store-mutations"; import store from "@/store"; import { validate } from "vee-validate"; import { damageLocationsSelected } from "@/constants/damage-locations-selected.js"; +import { routerParams } from "@/router/router-constants/router-params"; // Mock our module for promises. jest.mock("@/helpers/layout-helper.js", () => ({ @@ -703,6 +704,57 @@ describe("vehicle-damage.vue", () => { }); }); +describe("vehicle-damage.vue", () => { + test("when displayVehicleChangeAlert router params is true, the alert: 'vehicleChangeAlert' should be visible", () => { + // Arrange & Act + const { wrapper } = setupMocks({ + mountOptionsMockData: { + route: { + params: { + [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true + } + } + } + }); + // Assert + expect(wrapper.findComponent({ref: 'vehicleChangeAlert'}).isVisible()).toBe(true); + }) +}); + +describe("vehicle-damage.vue", () => { + test("when displayVehicleChangeAlert router params is false, the alert: 'vehicleChangeAlert' should not be visible", () => { + // Arrange & Act + const { wrapper } = setupMocks({ + mountOptionsMockData: { + route: { + params: { + [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: false + } + } + } + }); + // Assert + expect(wrapper.findComponent({ref: 'vehicleChangeAlert'}).isVisible()).toBe(false); + }) +}); + +describe("vehicle-damage.vue", () => { + test("when displayVehicleChangeAlert router params is undefined, the alert: 'vehicleChangeAlert' should not be visible", () => { + // Arrange & Act + const { wrapper } = setupMocks({ + mountOptionsMockData: { + route: { + params: { + [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: undefined + } + } + } + }); + // Assert + expect(wrapper.findComponent({ref: 'vehicleChangeAlert'}).isVisible()).toBe(false); + }) +}); + // THE FOLLOWING TEST IS NOT NECESSARILY REQUIRED FOR COVERAGE // BUT KEEP FOR AN EXAMPLE OF A VALIDATION TEST // @@ -734,32 +786,41 @@ describe("vehicle-damage.vue", () => { }); -function setupMocks({ - pageHeaderWidgetHeaderText = {}, - mountOptionsMockData = { +function setupMocks({pageHeaderWidgetHeaderText, mountOptionsMockData}) { + var pageHeaderWidgetHeaderTextDefault = {}; + var mountOptionsMockDataDefault = { router: { navigate: jest.fn(), }, + route: { + params: { + [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: false + } + }, store: { getters: { vehicle: {}, - payment: { insuranceCoverage: { isVerified: false } }, + payment: { + insuranceCoverage: { + isVerified: false + } + }, }, }, - }, -}) { + }; + // Combine parameters with default values + pageHeaderWidgetHeaderText = Object.assign(pageHeaderWidgetHeaderTextDefault, pageHeaderWidgetHeaderText); + mountOptionsMockData = Object.assign(mountOptionsMockDataDefault, mountOptionsMockData); //Mock api responses - baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn(); + baseMixin.methods.dispatchStoreAction = jest.fn(); const apiResponses = { cmsContent: { FunnelSubHeaderWidget: pageHeaderWidgetHeaderText, VehicleBannerWidget: { - GenericVehicleImage: - "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3", + GenericVehicleImage: "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3", }, FunnelHeaderWidget: { - LogoImage: - "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3", + LogoImage: "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3", }, }, damageOptions: { diff --git a/src/layouts/vehicle-damage/vehicle-damage.vue b/src/layouts/vehicle-damage/vehicle-damage.vue index 4482ec2ff..862abaced 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.vue +++ b/src/layouts/vehicle-damage/vehicle-damage.vue @@ -9,50 +9,57 @@ - - - - - - + + + + + + + @@ -79,6 +86,7 @@ import { errorMessages } from "@/constants/error-messages"; import { damageLocationsCms } from "@/constants/damage-locations-cms.js"; import { damageLocationsSelected } from "@/constants/damage-locations-selected.js"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; +import { queryStrings } from "@/constants/query-strings"; import store from "@/store"; import baseMixin from "@/mixins/base-mixin"; @@ -92,7 +100,7 @@ export default { // Call APIs const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage); const damageOptionsPromise = - baseMixin.methods.dispatchNonBlockingStoreAction( + baseMixin.methods.dispatchStoreAction( storeActions.GET_DAMAGE_OPTIONS, { carId: store.getters.vehicle.carId } ); @@ -140,6 +148,12 @@ export default { selectedRearReplaceOptions: this.getRearReplaceOptionsFromStore(), } }, + mounted(){ + if(this.$store.getters.vehicle.imageVifNumber){ + this.pushEventToGA(this.GaCategories.EVOX, `${this.GaActions.VIF}_${this.$store.getters.vehicle.imageVifNumber}`, + this.$store.getters.vehicle.carId, true); + } + }, methods: { arePagePrerequisitesValid() { if(store.getters.vehicle.carId){ @@ -273,7 +287,7 @@ export default { store.commit(this.storeMutations.UPDATE_GLASS_TO_REPLACE, this.selectedGlassToReplace()); - const partsData = await baseMixin.methods.dispatchNonBlockingStoreAction(this.storeActions.GET_PARTS_OR_QUESTIONS, + const partsData = await baseMixin.methods.dispatchStoreAction(this.storeActions.GET_PARTS_OR_QUESTIONS, { carId: store.getters.vehicle.carId, glassArray: this.selectedGlassToReplace()}, false); this.navigateForward(partsData); @@ -416,6 +430,9 @@ export default { }) ); }, + shouldDisplayVehicleChangeAlert() { + return this.$route.params[this.routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]; + }, shouldHideBackButton(){ return this.$store.getters.payment.insuranceCoverage.isVerified; } diff --git a/src/layouts/vehicle-make/make-question/make-question.vue b/src/layouts/vehicle-make/make-question/make-question.vue index e1493c536..5bb04034d 100644 --- a/src/layouts/vehicle-make/make-question/make-question.vue +++ b/src/layouts/vehicle-make/make-question/make-question.vue @@ -50,7 +50,7 @@ export default { }, methods: { loadInitialData() { - return baseMixin.methods.dispatchNonBlockingStoreAction( + return baseMixin.methods.dispatchStoreAction( storeActions.GET_VEHICLE_MAKES, { year: store.getters.vehicle.year } ); diff --git a/src/layouts/vehicle-model/model-question/model-question.vue b/src/layouts/vehicle-model/model-question/model-question.vue index e3a56e8c7..d8cb21894 100644 --- a/src/layouts/vehicle-model/model-question/model-question.vue +++ b/src/layouts/vehicle-model/model-question/model-question.vue @@ -50,7 +50,7 @@ export default { }, methods: { loadInitialData() { - return baseMixin.methods.dispatchNonBlockingStoreAction( + return baseMixin.methods.dispatchStoreAction( storeActions.GET_VEHICLE_MODELS, { year: store.getters.vehicle.year, make: store.getters.vehicle.make } ); diff --git a/src/layouts/vehicle-style/style-question/style-question.vue b/src/layouts/vehicle-style/style-question/style-question.vue index e7a289bfd..45c0f7dea 100644 --- a/src/layouts/vehicle-style/style-question/style-question.vue +++ b/src/layouts/vehicle-style/style-question/style-question.vue @@ -50,7 +50,7 @@ export default { }, methods: { loadInitialData() { - return baseMixin.methods.dispatchNonBlockingStoreAction( + return baseMixin.methods.dispatchStoreAction( storeActions.GET_VEHICLE_STYLES, { year: store.getters.vehicle.year, diff --git a/src/layouts/vehicle-style/vehicle-style.spec.js b/src/layouts/vehicle-style/vehicle-style.spec.js index 852bf248d..3d62e8f15 100644 --- a/src/layouts/vehicle-style/vehicle-style.spec.js +++ b/src/layouts/vehicle-style/vehicle-style.spec.js @@ -87,7 +87,7 @@ describe("vehicle-style.vue", () => { }); describe("vehicle-style.vue", () => { - test("selectVehicle triggers a dispatchNonBlockingStoreAction commit", async (done) => { + test("selectVehicle triggers a dispatchStoreAction commit", async (done) => { //Arrange const { wrapper, apiPromise } = setupMocks({ pageHeaderWidgetHeaderText: "Select a style to get started", @@ -119,7 +119,7 @@ describe("vehicle-style.vue", () => { //Assert apiPromise.finally(() => { - expect(wrapper.vm.dispatchNonBlockingStoreAction).toHaveBeenCalled(); + expect(wrapper.vm.dispatchStoreAction).toHaveBeenCalled(); done(); }); }); diff --git a/src/layouts/vehicle-style/vehicle-style.vue b/src/layouts/vehicle-style/vehicle-style.vue index e98cc400d..8bb542154 100644 --- a/src/layouts/vehicle-style/vehicle-style.vue +++ b/src/layouts/vehicle-style/vehicle-style.vue @@ -76,7 +76,7 @@ export default { ); }, setVehicle() { - return this.dispatchNonBlockingStoreAction( + return this.dispatchStoreAction( this.storeActions.SET_VEHICLE, { year: this.$store.getters.vehicle.year, diff --git a/src/layouts/vehicle-year/vehicle-year.vue b/src/layouts/vehicle-year/vehicle-year.vue index 96f62409c..fec51eafd 100644 --- a/src/layouts/vehicle-year/vehicle-year.vue +++ b/src/layouts/vehicle-year/vehicle-year.vue @@ -44,7 +44,7 @@ export default { const yearQuestionInitialDataPromise = yearQuestion.methods.loadInitialData(); // Log experiment exposure - const logExperimentExposurePromise = baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.LOG_EXPERIMENT_EXPOSURE, + const logExperimentExposurePromise = baseMixin.methods.dispatchStoreAction(storeActions.LOG_EXPERIMENT_EXPOSURE, { userId: getDeviceIdValue(), sessionKey: getSessionKeyValue(), diff --git a/src/layouts/vehicle-year/year-question/year-question.vue b/src/layouts/vehicle-year/year-question/year-question.vue index 16bac5442..a5b263fe8 100644 --- a/src/layouts/vehicle-year/year-question/year-question.vue +++ b/src/layouts/vehicle-year/year-question/year-question.vue @@ -48,7 +48,7 @@ export default { }, methods: { loadInitialData() { - return baseMixin.methods.dispatchNonBlockingStoreAction( + return baseMixin.methods.dispatchStoreAction( storeActions.GET_VEHICLE_YEARS, {} ); diff --git a/src/layouts/vin-lookup/vin-lookup.vue b/src/layouts/vin-lookup/vin-lookup.vue index 1967635a8..4064da145 100644 --- a/src/layouts/vin-lookup/vin-lookup.vue +++ b/src/layouts/vin-lookup/vin-lookup.vue @@ -180,6 +180,8 @@ export default { this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); }, async forwardButtonAction() { + this.pushEventToGA(this.$route.query[this.queryStrings.FMG_PAGE], this.GaActions.SUBMITTED, this.GaLabels.VINLOOKUP , true); + const zipValidation = await this.validateZip(this.zip); if (!zipValidation.data.isServiceable) { this.customAlertData.zip = this.zip; @@ -200,7 +202,7 @@ export default { } const carInfo = this.vinDoesNotMatchCarId ? vinLookup.data : store.getters.vehicle; this.updateStore(carInfo) - const partsData = await baseMixin.methods.dispatchNonBlockingStoreAction( + const partsData = await baseMixin.methods.dispatchStoreAction( this.storeActions.GET_PARTS_OR_QUESTIONS, { carId: store.getters.vehicle.carId, @@ -224,13 +226,13 @@ export default { } }, validateZip(zip) { - return baseMixin.methods.dispatchNonBlockingStoreAction( + return baseMixin.methods.dispatchStoreAction( storeActions.VALIDATE_ZIP, { zip } ); }, lookupVin(vin) { - return baseMixin.methods.dispatchNonBlockingStoreAction( + return baseMixin.methods.dispatchStoreAction( storeActions.LOOKUP_VEHICLE_BY_VIN, { vin } ); diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 763a7c830..c2c86c2dd 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -1,69 +1,116 @@ import { storeActions } from "@/constants/store-actions"; -import baseMixin from "@/mixins/base-mixin"; -import { settleAllPromises } from "@/helpers/layout-helper"; import { getDeviceIdValue, getSessionIdValue, getSessionKeyValue } from "@/helpers/heritage-integration/cookie-helper"; import { queryStrings } from "@/constants/query-strings"; -import { analyticsPageEvents } from "@/constants/analytics-page-events"; +import { experimentSettings } from "@/constants/experiments"; +import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents } from "@/constants/analytics"; + +import baseMixin from "@/mixins/base-mixin"; + +// We will need current page name for multiple methods, define it once to re-use. +const currentPageName = getPageNameByQueryString(); export default { methods: { - logEvent(destinationFmgPageValue, pageEvent, category, action, label, value){ + logEvent(pageEvent, category, action, label, value) { var payload = { userId: getDeviceIdValue(), sessionKey: getSessionKeyValue(), - pageName: destinationFmgPageValue, + pageName: currentPageName, sessionId: getSessionIdValue(), shouldUseSessionId: true, }; if (pageEvent) { - payload.pageEvent = {action: '', event: pageEvent}; + payload.pageEvent = { action: '', event: pageEvent }; } if (category) { - payload.customEvent = {category: category, action: action, label: label, value: value}; + payload.customEvent = { category: category, action: action, label: label, value: value }; } - baseMixin.methods.dispatchNonBlockingStoreAction(storeActions.LOG_ACTIVITY, payload, false); + baseMixin.methods.dispatchStoreAction(storeActions.LOG_ACTIVITY, payload, false); }, - pushEventToGA(category, action, label, value, pageName, pushToLogApp) { + pushEventToGA(category, action, label, pushToLogApp = false) { const eventToBePushed = { - 'event': 'ga_event', + 'event': GaEvents.GENERIC_EVENT, 'category': category, 'action': action, 'label': label, - 'value': value, - 'path': `/fmg/?${queryStrings.FMG_PAGE}=${pageName}` + 'value': undefined, + 'path': `/fmg/?${queryStrings.FMG_PAGE}=${currentPageName}` } pushToDataLayerIfDefined(eventToBePushed); - + if (pushToLogApp) { - this.logEvent(pageName, null, category, action, label, value); + this.logEvent(undefined, category, action, label, undefined); } + }, - pushPageViewToGA(pageName) { + pushPageViewToGA() { const pageViewEvent = { - 'event': 'logPageview', - 'pagePath': `/fmg/?${queryStrings.FMG_PAGE}=${pageName}`, - 'pageTitle': pageName + 'event': GaEvents.PAGE_VIEW_EVENT, + 'pagePath': `/fmg/?${queryStrings.FMG_PAGE}=${currentPageName}`, + 'pageTitle': currentPageName }; pushToDataLayerIfDefined(pageViewEvent); - this.logEvent(pageName, analyticsPageEvents.ENTRY); + + this.logEvent(currentPageName, analyticsPageEvents.ENTRY); + }, + + pushExperimentsToDataLayer(experiments) { + experiments?.data?.forEach(exp => { + + // Set Google Dimension Index based on experiment settings. + let googleDimensionIndex = 99; + + if (exp.settings[experimentSettings.GOOGLE_CUSTOM_DIMENSION_INDEX] !== undefined) { + googleDimensionIndex = exp.settings[experimentSettings.GOOGLE_CUSTOM_DIMENSION_INDEX]; + } + + // Create object with dimension index and value. + const experimentWithDimension = {}; + + Object.keys(exp).forEach(key => { + experimentWithDimension[`${key}_${googleDimensionIndex}`] = exp[key]; + }); + + // Push to the data layer with the Google Custom Dimension Index. + pushToDataLayerIfDefined(experimentWithDimension); + }); } }, computed: { - storeActions() { - return storeActions; + analyticsPageEvents() { + return analyticsPageEvents; }, - }, + GaCategories() { + return GaCategories; + }, + GaActions() { + return GaActions; + }, + GaLabels() { + return GaLabels; + } + } }; function pushToDataLayerIfDefined(data) { if (window.dataLayer !== undefined) { window.dataLayer.push(data); } +} + +function getPageNameByQueryString() { + const params = new URLSearchParams(location.search); + + if (params.has(queryStrings.FMG_PAGE)) { + return params.get(queryStrings.FMG_PAGE); + } else { + return ''; + } } \ No newline at end of file diff --git a/src/mixins/analytics-mixin.spec.js b/src/mixins/analytics-mixin.spec.js index aa0cfb022..b4ec2a5b5 100644 --- a/src/mixins/analytics-mixin.spec.js +++ b/src/mixins/analytics-mixin.spec.js @@ -9,13 +9,75 @@ describe("analyticsMixin.js", () => { const mockData = { actionList: [{ - actionName: storeActions.LOG_ACTIVITY + actionName: storeActions.LOG_ACTIVITY }], } - var mocks = setupMocksForJsFiles(mockData); + const mocks = setupMocksForJsFiles(mockData); analyticsMixin.methods.logEvent(type, payload); - expect(mocks.baseMixin.methods.dispatchNonBlockingStoreAction).toBeCalled(); + expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled(); }); + + test("pushEventToGA, should call logEvent too", () => { + // Arrange + const mockData = { + actionList: [{ + actionName: storeActions.LOG_ACTIVITY + }], + } + const mocks = setupMocksForJsFiles(mockData); + + // Act + analyticsMixin.methods.pushEventToGA('category', 'action', 'label', true); + + // Assert + expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled(); + + }); + + test("Experiments, should push to dataLayer with default Google Custom Dimension Index", () => { + // Arrange + window.dataLayer = []; + + const mockExperimentData = { + data: [ + { + settings: {}, + variationName: 'test', + universeName: 'testUniverse' + } + ] + } + + // Act + analyticsMixin.methods.pushExperimentsToDataLayer(mockExperimentData); + + // Assert + expect(window.dataLayer).toEqual([ { settings_99: {}, variationName_99: 'test', universeName_99: 'testUniverse' } ]); + + }); + + test("Experiments, should push to dataLayer with custom Google Custom Dimension Index", () => { + // Arrange + window.dataLayer = []; + + const mockExperimentData = { + data: [ + { + settings: { "Google Custom Dimension Index": "5"}, + variationName: 'test', + universeName: 'testUniverse' + } + ] + } + + // Act + analyticsMixin.methods.pushExperimentsToDataLayer(mockExperimentData); + + // Assert + expect(window.dataLayer).toEqual([ { settings_5: {"Google Custom Dimension Index": "5"}, variationName_5: 'test', universeName_5: 'testUniverse' } ]); + + }); + }); \ No newline at end of file diff --git a/src/mixins/base-mixin.js b/src/mixins/base-mixin.js index 43ce3f6f0..d8df53281 100644 --- a/src/mixins/base-mixin.js +++ b/src/mixins/base-mixin.js @@ -3,6 +3,8 @@ import { storeActions } from "@/constants/store-actions.js"; import { storeMutations } from "@/constants/store-mutations.js"; import { navigationScenarios } from "@/router/router-constants/navigation-scenarios"; import { vehicleCategories } from "@/constants/vehicle-categories.js"; +import { routerParams } from "@/router/router-constants/router-params"; +import { queryStrings } from "@/constants/query-strings"; export default { data() { @@ -17,7 +19,7 @@ export default { getCmsContent(widgetName, fieldName) { return this.$root.cmsContentByWidget?.[widgetName]?.[fieldName] ? this.$root.cmsContentByWidget[widgetName][fieldName] : ''; }, - dispatchNonBlockingStoreAction(type, payload, encodePayload = true) { + dispatchStoreAction(type, payload, encodePayload = true) { // Encode the payload if required if (encodePayload) { encodeUriData(payload); @@ -54,6 +56,12 @@ export default { vehicleCategories() { return vehicleCategories; }, + routerParams() { + return routerParams; + }, + queryStrings(){ + return queryStrings; + } }, }; diff --git a/src/mixins/base-mixin.spec.js b/src/mixins/base-mixin.spec.js index 48af9a7c0..f343336c3 100644 --- a/src/mixins/base-mixin.spec.js +++ b/src/mixins/base-mixin.spec.js @@ -11,7 +11,7 @@ describe("baseMixin.js", () => { const type = ""; const payload = {}; - mixIn.methods.dispatchNonBlockingStoreAction(type, payload); + mixIn.methods.dispatchStoreAction(type, payload); expect(store.dispatch).toBeCalledWith(type, payload); }); @@ -21,7 +21,7 @@ describe("baseMixin.js", () => { const type = ""; const payload = { make: "Alfa Romeo/Chrysler" }; - mixIn.methods.dispatchNonBlockingStoreAction(type, payload, true); + mixIn.methods.dispatchStoreAction(type, payload, true); expect(store.dispatch).toBeCalledWith(type, payload); }); diff --git a/src/router/index.js b/src/router/index.js index 034294f81..9ffd28522 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -5,6 +5,7 @@ import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js" import { routingTable } from "@/router/router-constants/routing-table.js"; import { globalEvents, globalEventTypes } from "@/constants/events"; import { queryStrings } from "@/constants/query-strings"; +import { getDeviceIdValue } from "@/helpers/heritage-integration/cookie-helper"; // Heritage integration import { isSavedSessionStillActive } from "@/helpers/heritage-integration/session-helper"; @@ -21,6 +22,7 @@ import analyticsMixin from "@/mixins/analytics-mixin"; import ComponentTest from "@/layouts/component-test/component-test.vue"; import FormTest from "@/layouts/form-test/form-test.vue"; + const routes = [ { path: "/component-test", // This is a temporary route for testing. @@ -121,8 +123,11 @@ const router = createRouter({ //---------------------------------------------------------- Router Functions ---------------------------------------------------------- -router.afterEach((to, from) => { +router.afterEach(async (to, from) => { + // Push page view to GA analyticsMixin.methods.pushPageViewToGA(to.query[queryStrings.FMG_PAGE]); + const assignedExperiments = await baseMixin.methods.dispatchStoreAction(storeActions.GET_EXPERIMENTS_BY_USER_FOR_GA, { userId: getDeviceIdValue() }); + analyticsMixin.methods.pushExperimentsToDataLayer(assignedExperiments); }); router.navigate = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) => { @@ -167,7 +172,7 @@ async function navigate(scenario, currentRoute, invalidateOnSave, optionalQuery if (getFunnelCookie()?.ReferralNumber && getFunnelCookie()?.ReferralDate) { await saveOrder(); } - + router.push({ name: "root", query: Object.assign(optionalQuery, { diff --git a/src/router/router-constants/router-params.js b/src/router/router-constants/router-params.js new file mode 100644 index 000000000..80ddb779d --- /dev/null +++ b/src/router/router-constants/router-params.js @@ -0,0 +1,5 @@ +const routerParams = { + DISPLAY_VEHICLE_CHANGE_ALERT: "displayVehicleChangeAlert" + }; + + export { routerParams }; \ No newline at end of file diff --git a/src/store/index.js b/src/store/index.js index 03797976a..60c2f6435 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -454,7 +454,16 @@ export const actions = { return globalMethods.callHttpClient({ method: endpoints.LogActivity.method, endpoint: endpoints.LogActivity.url, - payload: payload + payload: payload, + logApiCall: false + }); + }, + + getExperimentsByUserForGa(context, { userId }){ + return globalMethods.callHttpClient({ + method: endpoints.GetExperimentsByUserForGa.method, + endpoint: `${endpoints.GetExperimentsByUserForGa.url}/${userId}`, + payload: {} }); }, diff --git a/src/ux-components/button-main/button-main.spec.js b/src/ux-components/button-main/button-main.spec.js index ee5589a1d..2f0b4c72e 100644 --- a/src/ux-components/button-main/button-main.spec.js +++ b/src/ux-components/button-main/button-main.spec.js @@ -1,15 +1,16 @@ import { shallowMount } from "@vue/test-utils"; import buttonMain from "./button-main"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { nextTick } from "vue"; describe("buttonMain.vue", () => { it("Should return btn-primary class", async () => { // Act - const wrapper = shallowMount(buttonMain, { + const wrapper = shallowMount(buttonMain, setupMocks({ propsData: { isPrimary: true, }, - }); + })); // Assert const button = wrapper.find("button"); @@ -20,11 +21,11 @@ describe("buttonMain.vue", () => { it("Should return aria-disabled state", async () => { // Act - const wrapper = shallowMount(buttonMain, { + const wrapper = shallowMount(buttonMain, setupMocks({ propsData: { isDisabled: true, }, - }); + })); // Assert const button = wrapper.find("button"); @@ -35,12 +36,12 @@ describe("buttonMain.vue", () => { it("Should return loader color", async () => { // Act - const wrapper = shallowMount(buttonMain, { + const wrapper = shallowMount(buttonMain, setupMocks({ propsData: { loaderColor: "blue", loaderEnabled: true, }, - }); + })); // Assert @@ -57,12 +58,12 @@ describe("buttonMain.vue", () => { it("Should return loader position", async () => { // Act - const wrapper = shallowMount(buttonMain, { + const wrapper = shallowMount(buttonMain, setupMocks({ propsData: { loaderPosition: "right", loaderEnabled: true, }, - }); + })); // Assert @@ -77,3 +78,11 @@ describe("buttonMain.vue", () => { expect(loader.attributes("class")).toContain("right"); }); }); + +function setupMocks(mountOptionsMockData = {}) { + const defaultMountOptions = { route: { query: { fmgPage: 'page-name' } } }; + const baseMountOptions = getMountOptions(Object.assign(defaultMountOptions, mountOptionsMockData)); + const allMountOptions = Object.assign(defaultMountOptions, baseMountOptions); + + return allMountOptions; +} \ No newline at end of file diff --git a/src/ux-components/button-main/button-main.vue b/src/ux-components/button-main/button-main.vue index df422234d..00a17a7b6 100644 --- a/src/ux-components/button-main/button-main.vue +++ b/src/ux-components/button-main/button-main.vue @@ -16,6 +16,7 @@