Merge pull request #635 from Safelite/defect/CSR-705

CSR-705 value logging masking type
This commit is contained in:
matthew-sykes 2022-08-03 10:53:57 -04:00 committed by GitHub
commit b5c0bc8042
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 121 additions and 20 deletions

View file

@ -36,6 +36,7 @@
data-test="button" data-test="button"
:validationRules="validationRules" :validationRules="validationRules"
:class="[suppressError ? 'alertError' : '']" :class="[suppressError ? 'alertError' : '']"
:valueToLogType="valueToLogType"
/> />
<!-- For nested questions --> <!-- For nested questions -->
<transition name="fade" mode="out-in"> <transition name="fade" mode="out-in">
@ -91,6 +92,7 @@ export default {
validationRules: String, validationRules: String,
suppressError: Boolean, suppressError: Boolean,
useTextForValue: Boolean, useTextForValue: Boolean,
valueToLogType: String,
}, },
computed: { computed: {
formattedGroupName() { formattedGroupName() {

View file

@ -29,5 +29,8 @@ const GaLabels = {
ADDRESS_LOOKUP: 'Address_Look_up', ADDRESS_LOOKUP: 'Address_Look_up',
}; };
const ValueToLogTypes = {
LAST_5: "last_5",
};
export { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents}; export { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents, ValueToLogTypes };

View file

@ -7,7 +7,7 @@ import { cookieNames } from "@/constants/cookie-names";
import { Form } from "vee-validate"; import { Form } from "vee-validate";
import baseMixin from "@/mixins/base-mixin"; import baseMixin from "@/mixins/base-mixin";
import { getCookieDomainValue } from "@/helpers/heritage-integration/cookie-helper"; import { getCookieDomainValue } from "@/helpers/heritage-integration/cookie-helper";
import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents } from "@/constants/analytics"; import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents, ValueToLogTypes } from "@/constants/analytics";
import { queryStrings } from "@/constants/query-strings"; import { queryStrings } from "@/constants/query-strings";
import { routerParams } from "@/router/router-constants/router-params"; import { routerParams } from "@/router/router-constants/router-params";
@ -48,6 +48,7 @@ export function getMountOptions(mockData) {
mocks.GaActions = GaActions; mocks.GaActions = GaActions;
mocks.GaLabels = GaLabels; mocks.GaLabels = GaLabels;
mocks.GaEvents = GaEvents; mocks.GaEvents = GaEvents;
mocks.ValueToLogTypes = ValueToLogTypes;
mocks.queryStrings = queryStrings; mocks.queryStrings = queryStrings;
mocks.routerParams = routerParams; mocks.routerParams = routerParams;

View file

@ -1,5 +1,7 @@
import { shallowMount } from "@vue/test-utils"; import { shallowMount } from "@vue/test-utils";
import addressVehiclesQuestion from "@/layouts/address-vehicles/address-vehicles-question/address-vehicles-question"; import addressVehiclesQuestion from "@/layouts/address-vehicles/address-vehicles-question/address-vehicles-question";
import { ValueToLogTypes } from "@/constants/analytics";
describe("addressVehiclesQuestion.vue", () => { describe("addressVehiclesQuestion.vue", () => {
@ -54,9 +56,15 @@ const mockMixin = {
return 'FoundWindshieldTestReturn'; return 'FoundWindshieldTestReturn';
} }
return null; return null;
}), }),
vehicles: jest.fn(() => { vehicles: jest.fn(() => {
return [{ vehicle: "test" }]; return [{ vehicle: "test" }];
}) })
} },
computed: {
ValueToLogTypes() {
return ValueToLogTypes;
}
},
} }

View file

@ -8,6 +8,7 @@
v-model="selectedVehicleVinAsArray" v-model="selectedVehicleVinAsArray"
isRequired isRequired
:validation-rules="validationRules" :validation-rules="validationRules"
:valueToLogType="ValueToLogTypes.LAST_5"
/> />
<alert ref="differentVehicleAlert" <alert ref="differentVehicleAlert"
v-if="isCarIdDifferent" v-if="isCarIdDifferent"

View file

@ -2,7 +2,7 @@ import { storeActions } from "@/constants/store-actions";
import { setCookieProperties, getDeviceIdValue, getSessionIdValue, getSessionKeyValue } from "@/helpers/heritage-integration/cookie-helper"; import { setCookieProperties, getDeviceIdValue, getSessionIdValue, getSessionKeyValue } 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";
import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents } from "@/constants/analytics"; import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents, ValueToLogTypes } from "@/constants/analytics";
import { cookieNames } from "@/constants/cookie-names"; import { cookieNames } from "@/constants/cookie-names";
import baseMixin from "@/mixins/base-mixin"; import baseMixin from "@/mixins/base-mixin";
@ -42,13 +42,14 @@ export default {
baseMixin.methods.dispatchStoreAction(storeActions.LOG_CUSTOM_EVENT, payload, false); baseMixin.methods.dispatchStoreAction(storeActions.LOG_CUSTOM_EVENT, payload, false);
}, },
pushEventToGA(category, action, label, pushToLogApp = false) { pushEventToGA(category, action, label, pushToLogApp = false, valueToLogType = null) {
const currentPageName = getPageNameByQueryString(); const currentPageName = getPageNameByQueryString();
const labelToLog = getValueToLog(label, valueToLogType);
const eventToBePushed = { const eventToBePushed = {
'event': GaEvents.GENERIC_EVENT, 'event': GaEvents.GENERIC_EVENT,
'category': category, 'category': category,
'action': action, 'action': action,
'label': label, 'label': labelToLog,
'value': undefined, 'value': undefined,
'path': `/fmg/?${queryStrings.FMG_PAGE}=${currentPageName}` 'path': `/fmg/?${queryStrings.FMG_PAGE}=${currentPageName}`
} }
@ -56,7 +57,7 @@ export default {
pushToDataLayerIfDefined(eventToBePushed); pushToDataLayerIfDefined(eventToBePushed);
if (pushToLogApp) { if (pushToLogApp) {
this.logCustomEvent(category, action, label, undefined); this.logCustomEvent(category, action, labelToLog, undefined);
} }
}, },
@ -145,6 +146,9 @@ export default {
}, },
GaLabels() { GaLabels() {
return GaLabels; return GaLabels;
},
ValueToLogTypes() {
return ValueToLogTypes;
} }
}, },
}; };
@ -164,3 +168,10 @@ function getPageNameByQueryString() {
return ''; return '';
} }
} }
function getValueToLog(value, valueToLogType) {
if (valueToLogType != null && valueToLogType === ValueToLogTypes.LAST_5 ) {
return value.slice(-5);
}
return value;
}

View file

@ -1,7 +1,7 @@
import analyticsMixin from "@/mixins/analytics-mixin"; import analyticsMixin from "@/mixins/analytics-mixin";
import { setupMocksForJsFiles, setupCookies } from "@/helpers/unit-test-helper.js"; import { setupMocksForJsFiles, setupCookies } from "@/helpers/unit-test-helper.js";
import { storeActions } from "@/constants/store-actions"; import { storeActions } from "@/constants/store-actions";
import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents } from "@/constants/analytics"; import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents, ValueToLogTypes } from "@/constants/analytics";
describe("analyticsMixin.js", () => { describe("analyticsMixin.js", () => {
test("logPageView: calls dispatch with type and payload", () => { test("logPageView: calls dispatch with type and payload", () => {
@ -39,21 +39,71 @@ describe("analyticsMixin.js", () => {
expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled(); expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled();
}); });
test("pushEventToGA, should call logEvent too", () => { test("pushEventToGA, should call dataLayer push and logCustomEvent too", () => {
// Arrange // Arrange
window.dataLayer = [];
const mockData = { const mockData = {
actionList: [{ actionList: [{
actionName: storeActions.LOG_CUSTOM_EVENT actionName: storeActions.LOG_CUSTOM_EVENT
}], }],
} }
const mocks = setupMocksForJsFiles(mockData); const mocks = setupMocksForJsFiles(mockData);
var mockDataLayer = [];
mockDataLayer.push({
event: 'event',
category: 'category',
action: 'action',
label: 'label',
value: undefined,
path: '/fmg/?fmgPage='
});
// Act // Act
analyticsMixin.methods.pushEventToGA('category', 'action', 'label', true); analyticsMixin.methods.pushEventToGA('category', 'action', 'label', true);
// Assert // Assert
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", () => {
// Arrange
window.dataLayer = [];
var expectedDataLayer = [];
expectedDataLayer.push({
event: 'event',
category: 'category',
action: 'action',
label: '33333',
value: undefined,
path: '/fmg/?fmgPage='
});
// Act
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", () => {
// Arrange
window.dataLayer = [];
var expectedDataLayer = [];
expectedDataLayer.push({
event: 'event',
category: 'category',
action: 'action',
label: '111',
value: undefined,
path: '/fmg/?fmgPage='
});
// Act
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", () => { test("Experiments, should push to dataLayer with default Google Custom Dimension Index", () => {

View file

@ -78,6 +78,7 @@ export default {
validationRules: String, validationRules: String,
selectedValues: [Array, String], selectedValues: [Array, String],
hasError: Boolean, hasError: Boolean,
valueToLogType: String,
}, },
data() { data() {
return { return {
@ -116,7 +117,7 @@ export default {
this.handleCheckChange(); this.handleCheckChange();
} }
this.pushEventToGA(this.$route.query[queryStrings.FMG_PAGE], this.GaActions.CLICKED, this.value.toString(), true); this.pushEventToGA(this.$route.query[queryStrings.FMG_PAGE], this.GaActions.CLICKED, this.value.toString(), true, this.valueToLogType);
}, },
handleCheckChange() { handleCheckChange() {
const emitEvent = { const emitEvent = {

View file

@ -74,10 +74,12 @@ export default {
// Field initial value // Field initial value
type: [String, Number], type: [String, Number],
default: "", default: "",
}, },
validationRules: String, validationRules: String,
selectedValues: [Array, String], selectedValues: [Array, String],
hasError: Boolean, hasError: Boolean,
valueToLogType: String,
}, },
data() { data() {
return { return {
@ -111,7 +113,7 @@ export default {
this.displayLoader(); this.displayLoader();
this.handleCheckChange(); this.handleCheckChange();
} }
this.pushEventToGA(this.$route.query[queryStrings.FMG_PAGE], this.GaActions.CLICKED, this.value.toString(), true); this.pushEventToGA(this.$route.query[queryStrings.FMG_PAGE], this.GaActions.CLICKED, this.value.toString(), true, this.valueToLogType);
}, },
handleCheckChange() { handleCheckChange() {
const emitEvent = { const emitEvent = {

View file

@ -88,6 +88,7 @@ export default {
validationRules: String, validationRules: String,
selectedValues: [Array, String], selectedValues: [Array, String],
hasError: Boolean, hasError: Boolean,
valueToLogType: String,
}, },
data() { data() {
return { return {
@ -146,7 +147,7 @@ export default {
this.handleCheckChange(); this.handleCheckChange();
} }
this.pushEventToGA(this.$route.query[queryStrings.FMG_PAGE], this.GaActions.CLICKED, this.value.toString(), true); this.pushEventToGA(this.$route.query[queryStrings.FMG_PAGE], this.GaActions.CLICKED, this.value.toString(), true, this.valueToLogType);
}, },
handleCheckChange() { handleCheckChange() {
const emitEvent = { const emitEvent = {

View file

@ -1,6 +1,7 @@
import { shallowMount } from "@vue/test-utils"; import { shallowMount } from "@vue/test-utils";
import radio from "./radio"; import radio from "./radio";
import { nextTick } from "vue"; import { nextTick } from "vue";
import { GaActions } from "@/constants/analytics";
describe("radio.vue", () => { describe("radio.vue", () => {
it("Should return group name", async () => { it("Should return group name", async () => {
@ -64,6 +65,13 @@ describe("radio.vue", () => {
it("Should emit button value on click", async () => { it("Should emit button value on click", async () => {
// Act // Act
const wrapper = shallowMount(radio, { const wrapper = shallowMount(radio, {
global: {
mocks: {
'$route': { query: { fmgPage: 'page-name' } },
GaActions: GaActions,
pushEventToGA: jest.fn(),
}
},
propsData: { propsData: {
buttonLabel: "Windshield", buttonLabel: "Windshield",
value: "List Card Checkbox", value: "List Card Checkbox",
@ -77,12 +85,20 @@ describe("radio.vue", () => {
}); });
wrapper.vm.handleCheckChange(); wrapper.vm.handleCheckChange();
// Assert // Assert
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{"buttonID": "List Card Checkbox", value: "List Card Checkbox", checkValue: false}]); expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{"buttonID": "List Card Checkbox", value: "List Card Checkbox", checkValue: false}]);;
expect(wrapper.vm.pushEventToGA).toHaveBeenCalled();
}); });
it("Should set checkValue data if selectedButtonIDs has value(s)", async () => { it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
// Act // Act
const wrapper = shallowMount(radio, { const wrapper = shallowMount(radio, {
global: {
mocks: {
'$route': { query: { fmgPage: 'page-name' } },
GaActions: GaActions,
pushEventToGA: jest.fn(),
}
},
propsData: { propsData: {
buttonLabel: "Windshield", buttonLabel: "Windshield",
buttonID: "List Card Checkbox", buttonID: "List Card Checkbox",

View file

@ -25,6 +25,8 @@
<script> <script>
import { useField } from "vee-validate"; import { useField } from "vee-validate";
import { queryStrings } from "@/constants/query-strings";
export default { export default {
name: "radio", name: "radio",
props: { props: {
@ -39,7 +41,8 @@ export default {
screenReaderOnlyText: String, screenReaderOnlyText: String,
selectedValues: String, selectedValues: String,
hasError: Boolean, hasError: Boolean,
validationRules: String validationRules: String,
valueToLogType: String,
}, },
data() { data() {
return { return {
@ -65,6 +68,8 @@ export default {
this.$emit("isCheckedChanged", emitEvent); this.$emit("isCheckedChanged", emitEvent);
this.$emit("update:modelValue", emitEvent); this.$emit("update:modelValue", emitEvent);
this.pushEventToGA(this.$route.query[queryStrings.FMG_PAGE], this.GaActions.CLICKED, this.value.toString(), true, this.valueToLogType);
}, },
}, },
setup(props) { setup(props) {