Commit b582907f authored by jejacks0n's avatar jejacks0n

Require global form tracking to use an allow list

- This reduces global form tracking to only those forms and fields which
are explicitly opted in.
parent 0767e822
......@@ -25,6 +25,10 @@ const DEFAULT_SNOWPLOW_OPTIONS = {
formTracking: false,
linkClickTracking: false,
pageUnloadTimer: 10,
formTrackingConfig: {
forms: { allow: [] },
fields: { allow: [] },
},
};
const addExperimentContext = (opts) => {
......@@ -156,13 +160,18 @@ export default class Tracking {
static enableFormTracking(config, contexts = []) {
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
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);
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();
else document.addEventListener('DOMContentLoaded', enabler);
......@@ -207,11 +216,13 @@ export function initUserTracking() {
export function initDefaultTrackers() {
if (!Tracking.enabled()) return;
const opts = { ...DEFAULT_SNOWPLOW_OPTIONS, ...window.snowplowOptions };
window.snowplow('enableActivityTracking', 30, 30);
// must be after enableActivityTracking
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');
Tracking.bindDocument();
......
......@@ -4,7 +4,7 @@ import Tracking from '~/tracking';
const select = document.querySelector('.js-jobs-to-be-done-dropdown');
if (select) {
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'),
);
......
......@@ -9,6 +9,7 @@ describe('Tracking', () => {
let snowplowSpy;
let bindDocumentSpy;
let trackLoadEventsSpy;
let enableFormTracking;
beforeEach(() => {
getExperimentData.mockReturnValue(undefined);
......@@ -38,6 +39,10 @@ describe('Tracking', () => {
formTracking: false,
linkClickTracking: false,
pageUnloadTimer: 10,
formTrackingConfig: {
fields: { allow: [] },
forms: { allow: [] },
},
});
});
});
......@@ -46,6 +51,9 @@ describe('Tracking', () => {
beforeEach(() => {
bindDocumentSpy = jest.spyOn(Tracking, 'bindDocument').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', () => {
......@@ -59,10 +67,11 @@ describe('Tracking', () => {
...window.snowplowOptions,
formTracking: true,
linkClickTracking: true,
formTrackingConfig: { forms: { whitelist: ['foo'] }, fields: { whitelist: ['bar'] } },
};
initDefaultTrackers();
expect(snowplowSpy).toHaveBeenCalledWith('enableFormTracking');
expect(enableFormTracking).toHaveBeenCalledWith(window.snowplowOptions.formTrackingConfig);
expect(snowplowSpy).toHaveBeenCalledWith('enableLinkClickTracking');
});
......@@ -157,25 +166,22 @@ describe('Tracking', () => {
describe('.enableFormTracking', () => {
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_']);
expect(snowplowSpy).toHaveBeenCalledWith('enableFormTracking', config, [
{ data: { source: 'gitlab-javascript' }, schema: undefined },
'_passed_context_',
]);
expect(snowplowSpy).toHaveBeenCalledWith(
'enableFormTracking',
{ 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', () => {
const expectedError = new Error(
'Unable to enable form event tracking without whitelist rules.',
);
it('throws an error if no allow rules are provided', () => {
const expectedError = new Error('Unable to enable form event tracking without allow rules.');
expect(() => Tracking.enableFormTracking()).toThrow(expectedError);
expect(() => Tracking.enableFormTracking({ fields: { whitelist: [] } })).toThrow(
expectedError,
);
expect(() => Tracking.enableFormTracking({ fields: { whitelist: [1] } })).not.toThrow(
expect(() => Tracking.enableFormTracking({ fields: { allow: true } })).toThrow(expectedError);
expect(() => Tracking.enableFormTracking({ fields: { allow: [] } })).not.toThrow(
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