Commit f0db5495 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'jejacks0n/disable-global-form-tracking' into 'master'

Require global form tracking to use an allow list

See merge request gitlab-org/gitlab!62229
parents 948ecefe b582907f
...@@ -25,6 +25,10 @@ const DEFAULT_SNOWPLOW_OPTIONS = { ...@@ -25,6 +25,10 @@ const DEFAULT_SNOWPLOW_OPTIONS = {
formTracking: false, formTracking: false,
linkClickTracking: false, linkClickTracking: false,
pageUnloadTimer: 10, pageUnloadTimer: 10,
formTrackingConfig: {
forms: { allow: [] },
fields: { allow: [] },
},
}; };
const addExperimentContext = (opts) => { const addExperimentContext = (opts) => {
...@@ -156,13 +160,18 @@ export default class Tracking { ...@@ -156,13 +160,18 @@ export default class Tracking {
static enableFormTracking(config, contexts = []) { static enableFormTracking(config, contexts = []) {
if (!this.enabled()) return; if (!this.enabled()) return;
if (!config?.forms?.whitelist?.length && !config?.fields?.whitelist?.length) { if (!Array.isArray(config?.forms?.allow) && !Array.isArray(config?.fields?.allow)) {
// eslint-disable-next-line @gitlab/require-i18n-strings // eslint-disable-next-line @gitlab/require-i18n-strings
throw new Error('Unable to enable form event tracking without whitelist rules.'); throw new Error('Unable to enable form event tracking without allow rules.');
} }
contexts.unshift(STANDARD_CONTEXT); contexts.unshift(STANDARD_CONTEXT);
const enabler = () => window.snowplow('enableFormTracking', config, contexts); const mappedConfig = {
forms: { whitelist: config.forms?.allow || [] },
fields: { whitelist: config.fields?.allow || [] },
};
const enabler = () => window.snowplow('enableFormTracking', mappedConfig, contexts);
if (document.readyState !== 'loading') enabler(); if (document.readyState !== 'loading') enabler();
else document.addEventListener('DOMContentLoaded', enabler); else document.addEventListener('DOMContentLoaded', enabler);
...@@ -207,11 +216,13 @@ export function initUserTracking() { ...@@ -207,11 +216,13 @@ export function initUserTracking() {
export function initDefaultTrackers() { export function initDefaultTrackers() {
if (!Tracking.enabled()) return; if (!Tracking.enabled()) return;
const opts = { ...DEFAULT_SNOWPLOW_OPTIONS, ...window.snowplowOptions };
window.snowplow('enableActivityTracking', 30, 30); window.snowplow('enableActivityTracking', 30, 30);
// must be after enableActivityTracking // must be after enableActivityTracking
window.snowplow('trackPageView', null, [STANDARD_CONTEXT]); window.snowplow('trackPageView', null, [STANDARD_CONTEXT]);
if (window.snowplowOptions.formTracking) window.snowplow('enableFormTracking'); if (window.snowplowOptions.formTracking) Tracking.enableFormTracking(opts.formTrackingConfig);
if (window.snowplowOptions.linkClickTracking) window.snowplow('enableLinkClickTracking'); if (window.snowplowOptions.linkClickTracking) window.snowplow('enableLinkClickTracking');
Tracking.bindDocument(); Tracking.bindDocument();
......
...@@ -4,7 +4,7 @@ import Tracking from '~/tracking'; ...@@ -4,7 +4,7 @@ import Tracking from '~/tracking';
const select = document.querySelector('.js-jobs-to-be-done-dropdown'); const select = document.querySelector('.js-jobs-to-be-done-dropdown');
if (select) { if (select) {
Tracking.enableFormTracking( Tracking.enableFormTracking(
{ fields: { whitelist: ['jobs_to_be_done', 'jobs_to_be_done_other'] } }, { fields: { allow: ['jobs_to_be_done', 'jobs_to_be_done_other'] } },
getExperimentContexts('jobs_to_be_done'), getExperimentContexts('jobs_to_be_done'),
); );
......
...@@ -9,6 +9,7 @@ describe('Tracking', () => { ...@@ -9,6 +9,7 @@ describe('Tracking', () => {
let snowplowSpy; let snowplowSpy;
let bindDocumentSpy; let bindDocumentSpy;
let trackLoadEventsSpy; let trackLoadEventsSpy;
let enableFormTracking;
beforeEach(() => { beforeEach(() => {
getExperimentData.mockReturnValue(undefined); getExperimentData.mockReturnValue(undefined);
...@@ -38,6 +39,10 @@ describe('Tracking', () => { ...@@ -38,6 +39,10 @@ describe('Tracking', () => {
formTracking: false, formTracking: false,
linkClickTracking: false, linkClickTracking: false,
pageUnloadTimer: 10, pageUnloadTimer: 10,
formTrackingConfig: {
fields: { allow: [] },
forms: { allow: [] },
},
}); });
}); });
}); });
...@@ -46,6 +51,9 @@ describe('Tracking', () => { ...@@ -46,6 +51,9 @@ describe('Tracking', () => {
beforeEach(() => { beforeEach(() => {
bindDocumentSpy = jest.spyOn(Tracking, 'bindDocument').mockImplementation(() => null); bindDocumentSpy = jest.spyOn(Tracking, 'bindDocument').mockImplementation(() => null);
trackLoadEventsSpy = jest.spyOn(Tracking, 'trackLoadEvents').mockImplementation(() => null); trackLoadEventsSpy = jest.spyOn(Tracking, 'trackLoadEvents').mockImplementation(() => null);
enableFormTracking = jest
.spyOn(Tracking, 'enableFormTracking')
.mockImplementation(() => null);
}); });
it('should activate features based on what has been enabled', () => { it('should activate features based on what has been enabled', () => {
...@@ -59,10 +67,11 @@ describe('Tracking', () => { ...@@ -59,10 +67,11 @@ describe('Tracking', () => {
...window.snowplowOptions, ...window.snowplowOptions,
formTracking: true, formTracking: true,
linkClickTracking: true, linkClickTracking: true,
formTrackingConfig: { forms: { whitelist: ['foo'] }, fields: { whitelist: ['bar'] } },
}; };
initDefaultTrackers(); initDefaultTrackers();
expect(snowplowSpy).toHaveBeenCalledWith('enableFormTracking'); expect(enableFormTracking).toHaveBeenCalledWith(window.snowplowOptions.formTrackingConfig);
expect(snowplowSpy).toHaveBeenCalledWith('enableLinkClickTracking'); expect(snowplowSpy).toHaveBeenCalledWith('enableLinkClickTracking');
}); });
...@@ -157,25 +166,22 @@ describe('Tracking', () => { ...@@ -157,25 +166,22 @@ describe('Tracking', () => {
describe('.enableFormTracking', () => { describe('.enableFormTracking', () => {
it('tells snowplow to enable form tracking', () => { it('tells snowplow to enable form tracking', () => {
const config = { forms: { whitelist: [''] }, fields: { whitelist: [''] } }; const config = { forms: { allow: ['form-class1'] }, fields: { allow: ['input-class1'] } };
Tracking.enableFormTracking(config, ['_passed_context_']); Tracking.enableFormTracking(config, ['_passed_context_']);
expect(snowplowSpy).toHaveBeenCalledWith('enableFormTracking', config, [ expect(snowplowSpy).toHaveBeenCalledWith(
{ data: { source: 'gitlab-javascript' }, schema: undefined }, 'enableFormTracking',
'_passed_context_', { forms: { whitelist: ['form-class1'] }, fields: { whitelist: ['input-class1'] } },
]); [{ data: { source: 'gitlab-javascript' }, schema: undefined }, '_passed_context_'],
);
}); });
it('throws an error if no whitelist rules are provided', () => { it('throws an error if no allow rules are provided', () => {
const expectedError = new Error( const expectedError = new Error('Unable to enable form event tracking without allow rules.');
'Unable to enable form event tracking without whitelist rules.',
);
expect(() => Tracking.enableFormTracking()).toThrow(expectedError); expect(() => Tracking.enableFormTracking()).toThrow(expectedError);
expect(() => Tracking.enableFormTracking({ fields: { whitelist: [] } })).toThrow( expect(() => Tracking.enableFormTracking({ fields: { allow: true } })).toThrow(expectedError);
expectedError, expect(() => Tracking.enableFormTracking({ fields: { allow: [] } })).not.toThrow(
);
expect(() => Tracking.enableFormTracking({ fields: { whitelist: [1] } })).not.toThrow(
expectedError, expectedError,
); );
}); });
......
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