Commit d656f19c authored by Paul Slaughter's avatar Paul Slaughter

Merge branch 'snowplow-ee-to-ce' into 'master'

Updates current tracking code to use Tracking.js

See merge request gitlab-org/gitlab-ee!14960
parents e1f8093d b4fecd74
import stats from 'ee/stats';
import Tracking from '~/tracking';
export default {
methods: {
trackUninstallButtonClick: application => {
stats.trackEvent('k8s_cluster', 'uninstall', {
Tracking.event('k8s_cluster', 'uninstall', {
label: application,
});
},
......
import Stats from 'ee/stats';
import Tracking from '~/tracking';
import { mergeUrlParams } from '~/lib/utils/url_utility';
const TRACKING_CATEGORY = 'navbar_top';
const NAVSOURCE_KEY = 'nav_source';
const NAVSOURCE_VALUE = 'navbar';
/**
* intercepts clicks on navbar links
* and adds the 'nav_source=navbar' query parameter
*/
const appendLinkParam = e => {
const target = e.currentTarget;
// get closest link in case the target is a wrapping DOM node
const link = target.tagName === 'A' ? target : target.closest('a');
if (link && link.href && link.href.indexOf(`${NAVSOURCE_KEY}=${NAVSOURCE_VALUE}`) === -1) {
const url = mergeUrlParams({ [NAVSOURCE_KEY]: NAVSOURCE_VALUE }, link.href);
link.setAttribute('href', url);
}
};
export default function trackNavbarEvents() {
const container = '.navbar-gitlab';
const category = 'navbar_top';
if (!Tracking.enabled()) return;
const navbar = document.querySelector('.navbar-gitlab');
if (!navbar) return;
new Tracking(TRACKING_CATEGORY).bind(navbar);
// track search inputs within frequent-items component
navbar.querySelectorAll(`.frequent-items-dropdown-container input`).forEach(el => {
el.addEventListener('click', e => {
const parentDropdown = e.currentTarget.closest('li.dropdown');
/**
* intercepts clicks on navbar links
* and adds the 'nav_source=navbar' query parameter
*/
const appendLinkParam = e => {
const NAVSOURCE_KEY = 'nav_source';
const NAVSOURCE_VALUE = 'navbar';
const target = e.target || e.srcElement;
// get closest link in case the target is a wrapping DOM node
const link = target.tagName === 'A' ? target : target.closest('a');
if (link && link.href && link.href.indexOf(`${NAVSOURCE_KEY}=${NAVSOURCE_VALUE}`) === -1) {
const url = mergeUrlParams({ [NAVSOURCE_KEY]: NAVSOURCE_VALUE }, link.href);
link.setAttribute('href', url);
}
};
Stats.bindTrackableContainer(container, category);
if (Stats.snowplowEnabled) {
// track search inputs within frequent-items component
document
.querySelectorAll(`${container} .frequent-items-dropdown-container input`)
.forEach(element => {
element.addEventListener('click', e => {
const target = e.currentTarget;
const parentDropdown = target.closest('li.dropdown');
const label = `${parentDropdown.getAttribute('data-track-label')}_search`;
Stats.trackEvent(category, 'activate_form_input', { label, property: '', value: '' });
});
Tracking.event(TRACKING_CATEGORY, 'activate_form_input', {
label: `${parentDropdown.getAttribute('data-track-label')}_search`,
property: '',
value: '',
});
});
});
if (navbar) {
navbar.addEventListener('click', appendLinkParam);
navbar.addEventListener('contextmenu', appendLinkParam);
}
if (navbar) {
navbar.addEventListener('click', appendLinkParam);
navbar.addEventListener('contextmenu', appendLinkParam);
}
}
import Stats from 'ee/stats';
import Tracking from '~/tracking';
export default () => {
document.querySelector('.main-notes-list').addEventListener('click', event => {
......@@ -7,7 +7,7 @@ export default () => {
);
if (isReplyButtonClick) {
Stats.trackEvent(document.body.dataset.page, 'click_button', {
Tracking.event(document.body.dataset.page, 'click_button', {
label: 'reply_comment_button',
property: '',
value: '',
......@@ -15,5 +15,5 @@ export default () => {
}
});
Stats.bindTrackableContainer('.js-main-target-form');
new Tracking().bind();
};
......@@ -2,7 +2,7 @@
import _ from 'underscore';
import { mapState, mapActions, mapGetters } from 'vuex';
import { redirectTo } from '~/lib/utils/url_utility';
import Stats from 'ee/stats';
import Tracking from '~/tracking';
import OnboardingHelper from './onboarding_helper.vue';
import actionPopoverUtils from './../action_popover_utils';
import eventHub from '../event_hub';
......@@ -111,7 +111,7 @@ export default {
},
handleRestartStep() {
this.showExitTourContent(false);
Stats.trackEvent(TRACKING_CATEGORY, 'click_link', {
Tracking.event(TRACKING_CATEGORY, 'click_link', {
label: this.getTrackingLabel(),
property: 'restart_this_step',
});
......@@ -122,7 +122,7 @@ export default {
const { selector } = this.actionPopover;
const popoverEl = selector ? document.querySelector(selector) : null;
if (popoverEl) {
Stats.trackEvent(TRACKING_CATEGORY, 'click_link', {
Tracking.event(TRACKING_CATEGORY, 'click_link', {
label: this.getTrackingLabel(),
property: 'skip_this_step',
});
......@@ -177,7 +177,7 @@ export default {
return;
}
Stats.trackEvent(TRACKING_CATEGORY, 'click_button', {
Tracking.event(TRACKING_CATEGORY, 'click_button', {
label: this.getTrackingLabel(),
property: 'got_it',
});
......@@ -185,7 +185,7 @@ export default {
this.showActionPopover();
},
handleShowExitTourContent(showExitTour) {
Stats.trackEvent(TRACKING_CATEGORY, 'click_link', {
Tracking.event(TRACKING_CATEGORY, 'click_link', {
label: this.getTrackingLabel(),
property: 'exit_learn_gitlab',
});
......
import '~/pages/projects/issues/index/index';
import Stats from 'ee/stats';
import Tracking from '~/tracking';
document.addEventListener('DOMContentLoaded', () => {
Stats.bindTrackableContainer('.issues-export-modal');
Stats.bindTrackableContainer('.issues-import-modal');
new Tracking().bind();
});
import '~/pages/projects/new/index';
import initCustomProjectTemplates from 'ee/projects/custom_project_templates';
import bindTrackEvents from 'ee/projects/track_project_new';
import Tracking from '~/tracking';
import { bindOnboardingEvents } from 'ee/onboarding/new_project';
document.addEventListener('DOMContentLoaded', () => {
initCustomProjectTemplates();
bindTrackEvents('.js-toggle-container');
new Tracking().bind();
bindOnboardingEvents(document.getElementById('new_project'));
});
import Stats from 'ee/stats';
import Tracking from '~/tracking';
export default () => {
Stats.bindTrackableContainer('#signin-container');
const container = document.getElementById('#signin-container');
new Tracking().bind(container);
};
import Stats from 'ee/stats';
const bindTrackEvents = container => {
Stats.bindTrackableContainer(container);
};
export default bindTrackEvents;
import Stats from 'ee/stats';
import Tracking from '~/tracking';
import * as types from './mutation_types';
export const setFilter = ({ commit }, payload) => {
commit(types.SET_FILTER, payload);
Stats.trackEvent(document.body.dataset.page, 'set_filter', {
Tracking.event(document.body.dataset.page, 'set_filter', {
label: payload.filterId,
value: payload.optionId,
});
......
import $ from 'jquery';
const snowplowEnabled = () => typeof window.snowplow === 'function';
const trackEvent = (
category,
eventName,
additionalData = { label: '', property: '', value: '' },
) => {
if (!snowplowEnabled()) {
return;
}
if (!category || !eventName) {
return;
}
const { label, property, value } = additionalData;
try {
window.snowplow('trackStructEvent', category, eventName, label, property, value);
} catch (e) {
// do nothing
}
};
const isSelect2 = element => element.classList.contains('select2');
const isBsDropdown = element => {
const hasDropdownClass = element.classList.contains('dropdown');
const dropdownToggle = element.querySelector('[data-toggle="dropdown"]');
return hasDropdownClass && dropdownToggle !== null;
};
const bindTrackableContainer = (container = '', category = document.body.dataset.page) => {
if (!snowplowEnabled()) {
return;
}
const clickHandler = e => {
const target = e.currentTarget;
const label = target.getAttribute('data-track-label');
const property = target.getAttribute('data-track-property') || '';
const eventName = target.getAttribute('data-track-event');
let value = target.value || '';
// overrides value for checkboxes
if (target.type === 'checkbox') {
value = target.checked;
}
// overrides value if data-track_value is set
if (
typeof target.getAttribute('data-track-value') !== 'undefined' &&
target.getAttribute('data-track-value') !== null
) {
value = target.getAttribute('data-track-value');
}
trackEvent(category, eventName, { label, property, value });
};
const trackableElements = document.querySelectorAll(`${container} [data-track-label]`);
trackableElements.forEach(element => {
if (!isSelect2(element) && !isBsDropdown(element)) {
element.addEventListener('click', e => clickHandler(e));
}
});
// jquery required for select2 events
// see: https://github.com/select2/select2/issues/4686#issuecomment-264747428
$(`${container} .select2[data-track-label]`).on('click', e => clickHandler(e));
const dropdownHandler = (e, open = true) => {
const target = e.currentTarget;
const property = target.getAttribute('data-track-property') || '';
const eventName = target.getAttribute('data-track-event');
const value = target.value || '';
const label = target.getAttribute('data-track-label') + (open ? '_open' : '_close');
trackEvent(category, eventName, { label, property, value });
};
// bootstrap dropdowns
$(`${container} [data-track-label][data-track-event="click_dropdown"]`).on(
'show.bs.dropdown',
e => dropdownHandler(e),
);
$(`${container} [data-track-label][data-track-event="click_dropdown"]`).on(
'hide.bs.dropdown',
e => dropdownHandler(e, false),
);
};
export default {
snowplowEnabled,
trackEvent,
bindTrackableContainer,
};
<script>
import { GlButton } from '@gitlab/ui';
import Stats from 'ee/stats';
import Tracking from '~/tracking';
import { s__ } from '~/locale';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
......@@ -31,11 +31,11 @@ export default {
},
methods: {
addCommentAndDismiss() {
Stats.trackEvent(document.body.dataset.page, 'click_add_comment_and_dismiss');
Tracking.event(document.body.dataset.page, 'click_add_comment_and_dismiss');
this.$emit('addCommentAndDismiss');
},
addDismissalComment() {
Stats.trackEvent(document.body.dataset.page, 'click_add_comment');
Tracking.event(document.body.dataset.page, 'click_add_comment');
this.$emit('addDismissalComment');
},
handleSubmit() {
......
import trackUninstallButtonClick from 'ee/clusters/mixins/track_uninstall_button_click';
import stats from 'ee/stats';
import Tracking from '~/tracking';
jest.mock('ee/stats');
jest.mock('~/tracking');
describe('trackUninstallButtonClickMixin', () => {
describe('trackUninstallButtonClick', () => {
it('sends snowplow event indicating which application will be uninstalled', () => {
it('tracks an event indicating which application will be uninstalled', () => {
const application = 'ingress';
trackUninstallButtonClick.methods.trackUninstallButtonClick(application);
expect(stats.trackEvent).toHaveBeenCalledWith('k8s_cluster', 'uninstall', {
expect(Tracking.event).toHaveBeenCalledWith('k8s_cluster', 'uninstall', {
label: application,
});
});
......
import Vue from 'vue';
import Stats from 'ee_else_ce/stats';
import Tracking from '~/tracking';
import { shallowMount } from '@vue/test-utils';
import initNoteStats from 'ee_else_ce/event_tracking/notes';
jest.mock('~/tracking');
describe('initNoteStats', () => {
let wrapper;
const createComponent = template => {
......@@ -14,13 +16,8 @@ describe('initNoteStats', () => {
return shallowMount(component, { attachToDocument: true });
};
jest.mock('ee_else_ce/stats');
Stats.trackEvent = jest.fn();
Stats.bindTrackableContainer = jest.fn();
afterEach(() => {
Stats.trackEvent.mockClear();
Stats.bindTrackableContainer.mockClear();
jest.clearAllMocks();
wrapper.destroy();
});
......@@ -33,12 +30,12 @@ describe('initNoteStats', () => {
});
it('calls bindTrackableContainer', () => {
expect(Stats.bindTrackableContainer).toHaveBeenCalledTimes(1);
expect(Tracking.prototype.bind).toHaveBeenCalledTimes(1);
});
it('calls trackEvent', () => {
wrapper.find('.main-notes-list').trigger('click');
expect(Stats.trackEvent).toHaveBeenCalledTimes(1);
expect(Tracking.event).toHaveBeenCalledTimes(1);
});
});
......@@ -47,7 +44,7 @@ describe('initNoteStats', () => {
wrapper = createComponent("<div><button class='main-notes-list'></button></div>");
initNoteStats();
wrapper.find('.main-notes-list').trigger('click');
expect(Stats.trackEvent).not.toHaveBeenCalled();
expect(Tracking.event).not.toHaveBeenCalled();
});
});
});
import { mount } from '@vue/test-utils';
import Stats from 'ee/stats';
import Tracking from '~/tracking';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import component from 'ee/vue_shared/security_reports/components/dismissal_comment_modal_footer.vue';
jest.mock('ee/stats');
jest.mock('~/tracking');
describe('DismissalCommentModalFooter', () => {
let origPage;
let wrapper;
afterEach(() => {
document.body.dataset.page = origPage;
jest.clearAllMocks();
wrapper.destroy();
});
beforeEach(() => {
origPage = document.body.dataset.page;
document.body.dataset.page = '_track_category_';
});
describe('with an non-dismissed vulnerability', () => {
beforeEach(() => {
wrapper = mount(component, { sync: false });
......@@ -21,8 +33,8 @@ describe('DismissalCommentModalFooter', () => {
wrapper.find(LoadingButton).trigger('click');
expect(wrapper.emitted().addCommentAndDismiss).toBeTruthy();
expect(Stats.trackEvent).toHaveBeenCalledWith(
document.body.dataset.page,
expect(Tracking.event).toHaveBeenCalledWith(
'_track_category_',
'click_add_comment_and_dismiss',
);
});
......@@ -49,10 +61,7 @@ describe('DismissalCommentModalFooter', () => {
wrapper.find(LoadingButton).trigger('click');
expect(wrapper.emitted().addDismissalComment).toBeTruthy();
expect(Stats.trackEvent).toHaveBeenCalledWith(
document.body.dataset.page,
'click_add_comment',
);
expect(Tracking.event).toHaveBeenCalledWith('_track_category_', 'click_add_comment');
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment