debian-mirror-gitlab/spec/frontend/tracking/tracking_spec.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

700 lines
23 KiB
JavaScript
Raw Normal View History

2021-03-08 18:12:59 +05:30
import { setHTMLFixture } from 'helpers/fixtures';
2021-11-11 11:23:49 +05:30
import { TEST_HOST } from 'helpers/test_constants';
2021-04-17 20:07:23 +05:30
import { TRACKING_CONTEXT_SCHEMA } from '~/experimentation/constants';
2021-11-11 11:23:49 +05:30
import { getExperimentData, getAllExperimentContexts } from '~/experimentation/utils';
2021-09-04 01:27:46 +05:30
import Tracking, { initUserTracking, initDefaultTrackers } from '~/tracking';
2021-11-11 11:23:49 +05:30
import { REFERRER_TTL, URLS_CACHE_STORAGE_KEY } from '~/tracking/constants';
2021-09-04 01:27:46 +05:30
import getStandardContext from '~/tracking/get_standard_context';
2021-04-17 20:07:23 +05:30
2021-11-11 11:23:49 +05:30
jest.mock('~/experimentation/utils', () => ({
getExperimentData: jest.fn(),
2021-11-18 22:05:49 +05:30
getAllExperimentContexts: jest.fn().mockReturnValue([]),
2021-11-11 11:23:49 +05:30
}));
2019-10-12 21:52:04 +05:30
2021-11-18 22:05:49 +05:30
const TEST_CATEGORY = 'root:index';
const TEST_ACTION = 'generic';
const TEST_LABEL = 'button';
2019-10-12 21:52:04 +05:30
describe('Tracking', () => {
2021-09-04 01:27:46 +05:30
let standardContext;
2019-12-04 20:38:33 +05:30
let snowplowSpy;
2021-09-04 01:27:46 +05:30
beforeAll(() => {
window.gl = window.gl || {};
2021-11-11 11:23:49 +05:30
window.gl.snowplowUrls = {};
2021-09-04 01:27:46 +05:30
window.gl.snowplowStandardContext = {
schema: 'iglu:com.gitlab/gitlab_standard',
data: {
environment: 'testing',
source: 'unknown',
extra: {},
},
};
2021-11-18 22:05:49 +05:30
window.snowplowOptions = {
namespace: 'gl_test',
hostname: 'app.test.com',
cookieDomain: '.test.com',
formTracking: true,
linkClickTracking: true,
formTrackingConfig: { forms: { allow: ['foo'] }, fields: { allow: ['bar'] } },
};
2021-09-04 01:27:46 +05:30
standardContext = getStandardContext();
2021-11-18 22:05:49 +05:30
window.snowplow = window.snowplow || (() => {});
document.body.dataset.page = TEST_CATEGORY;
initUserTracking();
initDefaultTrackers();
2021-09-04 01:27:46 +05:30
});
2019-12-04 20:38:33 +05:30
2019-10-12 21:52:04 +05:30
beforeEach(() => {
2021-04-17 20:07:23 +05:30
getExperimentData.mockReturnValue(undefined);
2021-11-11 11:23:49 +05:30
getAllExperimentContexts.mockReturnValue([]);
2021-04-17 20:07:23 +05:30
2019-12-04 20:38:33 +05:30
snowplowSpy = jest.spyOn(window, 'snowplow');
2019-10-12 21:52:04 +05:30
});
2019-12-04 20:38:33 +05:30
describe('.event', () => {
afterEach(() => {
window.doNotTrack = undefined;
navigator.doNotTrack = undefined;
navigator.msDoNotTrack = undefined;
2021-11-18 22:05:49 +05:30
jest.clearAllMocks();
2019-10-12 21:52:04 +05:30
});
it('tracks to snowplow (our current tracking system)', () => {
2021-11-18 22:05:49 +05:30
Tracking.event(TEST_CATEGORY, TEST_ACTION, { label: TEST_LABEL });
2019-10-12 21:52:04 +05:30
2023-06-20 00:43:36 +05:30
expect(snowplowSpy).toHaveBeenCalledWith('trackStructEvent', {
category: TEST_CATEGORY,
action: TEST_ACTION,
label: TEST_LABEL,
property: undefined,
value: undefined,
context: [standardContext],
});
2021-09-04 01:27:46 +05:30
});
2022-07-16 23:28:13 +05:30
it('returns `true` if the Snowplow library was called without issues', () => {
expect(Tracking.event(TEST_CATEGORY, TEST_ACTION)).toBe(true);
});
it('returns `false` if the Snowplow library throws an error', () => {
snowplowSpy.mockImplementation(() => {
throw new Error();
});
expect(Tracking.event(TEST_CATEGORY, TEST_ACTION)).toBe(false);
});
2021-09-04 01:27:46 +05:30
it('allows adding extra data to the default context', () => {
const extra = { foo: 'bar' };
2021-11-18 22:05:49 +05:30
Tracking.event(TEST_CATEGORY, TEST_ACTION, { extra });
2021-09-04 01:27:46 +05:30
2023-06-20 00:43:36 +05:30
expect(snowplowSpy).toHaveBeenCalledWith('trackStructEvent', {
category: TEST_CATEGORY,
action: TEST_ACTION,
label: undefined,
property: undefined,
value: undefined,
context: [
2021-09-04 01:27:46 +05:30
{
...standardContext,
data: {
...standardContext.data,
extra,
},
},
],
2023-06-20 00:43:36 +05:30
});
2019-10-12 21:52:04 +05:30
});
it('skips tracking if snowplow is unavailable', () => {
window.snowplow = false;
2021-11-18 22:05:49 +05:30
Tracking.event(TEST_CATEGORY, TEST_ACTION);
2019-10-12 21:52:04 +05:30
expect(snowplowSpy).not.toHaveBeenCalled();
});
2019-12-04 20:38:33 +05:30
it('skips tracking if the user does not want to be tracked (general spec)', () => {
window.doNotTrack = '1';
2021-11-18 22:05:49 +05:30
Tracking.event(TEST_CATEGORY, TEST_ACTION);
2019-12-04 20:38:33 +05:30
expect(snowplowSpy).not.toHaveBeenCalled();
});
it('skips tracking if the user does not want to be tracked (firefox legacy)', () => {
navigator.doNotTrack = 'yes';
2021-11-18 22:05:49 +05:30
Tracking.event(TEST_CATEGORY, TEST_ACTION);
2019-12-04 20:38:33 +05:30
expect(snowplowSpy).not.toHaveBeenCalled();
});
it('skips tracking if the user does not want to be tracked (IE legacy)', () => {
navigator.msDoNotTrack = '1';
2021-11-18 22:05:49 +05:30
Tracking.event(TEST_CATEGORY, TEST_ACTION);
2019-12-04 20:38:33 +05:30
expect(snowplowSpy).not.toHaveBeenCalled();
});
2019-10-12 21:52:04 +05:30
});
2022-06-21 17:19:12 +05:30
describe('.definition', () => {
const TEST_VALID_BASENAME = '202108302307_default_click_button';
const TEST_EVENT_DATA = { category: undefined, action: 'click_button' };
let eventSpy;
let dispatcherSpy;
beforeAll(() => {
Tracking.definitionsManifest = {
'202108302307_default_click_button': 'config/events/202108302307_default_click_button.yml',
};
});
beforeEach(() => {
eventSpy = jest.spyOn(Tracking, 'event');
dispatcherSpy = jest.spyOn(Tracking, 'dispatchFromDefinition');
});
it('throws an error if the definition does not exists', () => {
const basename = '20220230_default_missing_definition';
const expectedError = new Error(`Missing Snowplow event definition "${basename}"`);
expect(() => Tracking.definition(basename)).toThrow(expectedError);
});
it('dispatches an event from a definition present in the manifest', () => {
Tracking.definition(TEST_VALID_BASENAME);
expect(dispatcherSpy).toHaveBeenCalledWith(TEST_VALID_BASENAME, {});
});
it('push events to the queue if not loaded', () => {
Tracking.definitionsLoaded = false;
Tracking.definitionsEventsQueue = [];
const dispatched = Tracking.definition(TEST_VALID_BASENAME);
expect(dispatched).toBe(false);
expect(Tracking.definitionsEventsQueue[0]).toStrictEqual([TEST_VALID_BASENAME, {}]);
expect(eventSpy).not.toHaveBeenCalled();
});
it('dispatch events when the definition is loaded', () => {
const definition = { key: TEST_VALID_BASENAME, ...TEST_EVENT_DATA };
Tracking.definitions = [{ ...definition }];
Tracking.definitionsEventsQueue = [];
Tracking.definitionsLoaded = true;
const dispatched = Tracking.definition(TEST_VALID_BASENAME);
expect(dispatched).not.toBe(false);
expect(Tracking.definitionsEventsQueue).toEqual([]);
expect(eventSpy).toHaveBeenCalledWith(definition.category, definition.action, {});
});
it('lets defined event data takes precedence', () => {
const definition = { key: TEST_VALID_BASENAME, category: undefined, action: 'click_button' };
const eventData = { category: TEST_CATEGORY };
Tracking.definitions = [{ ...definition }];
Tracking.definitionsLoaded = true;
Tracking.definition(TEST_VALID_BASENAME, eventData);
expect(eventSpy).toHaveBeenCalledWith(TEST_CATEGORY, definition.action, eventData);
});
});
2021-06-08 01:23:25 +05:30
describe('.enableFormTracking', () => {
2021-09-04 01:27:46 +05:30
it('tells snowplow to enable form tracking, with only explicit contexts', () => {
2023-06-20 00:43:36 +05:30
const config = {
forms: { allow: ['form-class1'] },
fields: { allow: ['input-class1'] },
};
2021-09-04 01:27:46 +05:30
Tracking.enableFormTracking(config, ['_passed_context_', standardContext]);
2021-06-08 01:23:25 +05:30
2023-06-20 00:43:36 +05:30
expect(snowplowSpy).toHaveBeenCalledWith('enableFormTracking', {
options: { forms: { allowlist: ['form-class1'] }, fields: { allowlist: ['input-class1'] } },
context: ['_passed_context_'],
});
2021-06-08 01:23:25 +05:30
});
2021-09-04 01:27:46 +05:30
it('throws an error if no allow rules are provided', () => {
const expectedError = new Error('Unable to enable form event tracking without allow rules.');
2021-06-08 01:23:25 +05:30
expect(() => Tracking.enableFormTracking()).toThrow(expectedError);
2021-09-04 01:27:46 +05:30
expect(() => Tracking.enableFormTracking({ fields: { allow: true } })).toThrow(expectedError);
expect(() => Tracking.enableFormTracking({ fields: { allow: [] } })).not.toThrow(
2021-06-08 01:23:25 +05:30
expectedError,
);
});
2021-09-30 23:02:18 +05:30
2021-11-18 22:05:49 +05:30
it('does not add empty form allow rules', () => {
2021-09-30 23:02:18 +05:30
Tracking.enableFormTracking({ fields: { allow: ['input-class1'] } });
2023-06-20 00:43:36 +05:30
expect(snowplowSpy).toHaveBeenCalledWith('enableFormTracking', {
options: { fields: { allowlist: ['input-class1'] } },
context: [],
});
2021-09-30 23:02:18 +05:30
});
describe('when `document.readyState` does not equal `complete`', () => {
const originalReadyState = document.readyState;
const setReadyState = (value) => {
Object.defineProperty(document, 'readyState', {
value,
configurable: true,
});
};
const fireReadyStateChangeEvent = () => {
document.dispatchEvent(new Event('readystatechange'));
};
beforeEach(() => {
setReadyState('interactive');
});
afterEach(() => {
setReadyState(originalReadyState);
});
it('does not call `window.snowplow` until `readystatechange` is fired and `document.readyState` equals `complete`', () => {
Tracking.enableFormTracking({ fields: { allow: ['input-class1'] } });
expect(snowplowSpy).not.toHaveBeenCalled();
fireReadyStateChangeEvent();
expect(snowplowSpy).not.toHaveBeenCalled();
setReadyState('complete');
fireReadyStateChangeEvent();
expect(snowplowSpy).toHaveBeenCalled();
});
});
2021-06-08 01:23:25 +05:30
});
2021-04-17 20:07:23 +05:30
describe('.flushPendingEvents', () => {
it('flushes any pending events', () => {
Tracking.initialized = false;
2021-11-18 22:05:49 +05:30
Tracking.event(TEST_CATEGORY, TEST_ACTION, { label: TEST_LABEL });
2021-04-17 20:07:23 +05:30
expect(snowplowSpy).not.toHaveBeenCalled();
Tracking.flushPendingEvents();
2023-06-20 00:43:36 +05:30
expect(snowplowSpy).toHaveBeenCalledWith('trackStructEvent', {
category: TEST_CATEGORY,
action: TEST_ACTION,
label: TEST_LABEL,
property: undefined,
value: undefined,
context: [standardContext],
});
2021-04-17 20:07:23 +05:30
});
});
2021-11-11 11:23:49 +05:30
describe('.setAnonymousUrls', () => {
afterEach(() => {
window.gl.snowplowPseudonymizedPageUrl = '';
localStorage.removeItem(URLS_CACHE_STORAGE_KEY);
});
it('does nothing if URLs are not provided', () => {
Tracking.setAnonymousUrls();
expect(snowplowSpy).not.toHaveBeenCalled();
expect(localStorage.getItem(URLS_CACHE_STORAGE_KEY)).toBe(null);
});
it('sets the page URL when provided and populates the cache', () => {
window.gl.snowplowPseudonymizedPageUrl = TEST_HOST;
Tracking.setAnonymousUrls();
expect(snowplowSpy).toHaveBeenCalledWith('setCustomUrl', TEST_HOST);
expect(JSON.parse(localStorage.getItem(URLS_CACHE_STORAGE_KEY))[0]).toStrictEqual({
url: TEST_HOST,
referrer: '',
originalUrl: window.location.href,
timestamp: Date.now(),
});
});
2021-11-18 22:05:49 +05:30
it('does not appends the hash/fragment to the pseudonymized URL', () => {
2021-11-11 11:23:49 +05:30
window.gl.snowplowPseudonymizedPageUrl = TEST_HOST;
2021-11-18 22:05:49 +05:30
window.location.hash = 'first-heading';
2021-11-11 11:23:49 +05:30
Tracking.setAnonymousUrls();
2021-11-18 22:05:49 +05:30
expect(snowplowSpy).toHaveBeenCalledWith('setCustomUrl', TEST_HOST);
2021-11-11 11:23:49 +05:30
});
2022-05-07 20:08:51 +05:30
describe('allowed hashes/fragments', () => {
it.each`
hash | appends | description
${'note_abc_123'} | ${true} | ${'appends'}
${'diff-content-819'} | ${true} | ${'appends'}
${'first_heading'} | ${false} | ${'does not append'}
`('$description `$hash` hash', ({ hash, appends }) => {
window.gl.snowplowPseudonymizedPageUrl = TEST_HOST;
window.location.hash = hash;
Tracking.setAnonymousUrls();
const url = appends ? `${TEST_HOST}#${hash}` : TEST_HOST;
expect(snowplowSpy).toHaveBeenCalledWith('setCustomUrl', url);
});
});
2021-11-11 11:23:49 +05:30
it('does not set the referrer URL by default', () => {
window.gl.snowplowPseudonymizedPageUrl = TEST_HOST;
Tracking.setAnonymousUrls();
expect(snowplowSpy).not.toHaveBeenCalledWith('setReferrerUrl', expect.any(String));
});
describe('with referrers cache', () => {
const testUrl = '/namespace:1/project:2/-/merge_requests/5';
const testOriginalUrl = '/my-namespace/my-project/-/merge_requests/';
const setUrlsCache = (data) =>
localStorage.setItem(URLS_CACHE_STORAGE_KEY, JSON.stringify(data));
beforeEach(() => {
window.gl.snowplowPseudonymizedPageUrl = TEST_HOST;
Object.defineProperty(document, 'referrer', { value: '', configurable: true });
});
it('does nothing if a referrer can not be found', () => {
setUrlsCache([
{
url: testUrl,
originalUrl: TEST_HOST,
timestamp: Date.now(),
},
]);
Tracking.setAnonymousUrls();
expect(snowplowSpy).not.toHaveBeenCalledWith('setReferrerUrl', expect.any(String));
});
it('sets referrer URL from the page URL found in cache', () => {
Object.defineProperty(document, 'referrer', { value: testOriginalUrl });
setUrlsCache([
{
url: testUrl,
originalUrl: testOriginalUrl,
timestamp: Date.now(),
},
]);
Tracking.setAnonymousUrls();
expect(snowplowSpy).toHaveBeenCalledWith('setReferrerUrl', testUrl);
});
it('ignores and removes old entries from the cache', () => {
const oldTimestamp = Date.now() - (REFERRER_TTL + 1);
Object.defineProperty(document, 'referrer', { value: testOriginalUrl });
setUrlsCache([
{
url: testUrl,
originalUrl: testOriginalUrl,
timestamp: oldTimestamp,
},
]);
Tracking.setAnonymousUrls();
expect(snowplowSpy).not.toHaveBeenCalledWith('setReferrerUrl', testUrl);
2022-08-13 15:12:31 +05:30
expect(localStorage.getItem(URLS_CACHE_STORAGE_KEY)).not.toContain(oldTimestamp.toString());
2021-11-11 11:23:49 +05:30
});
});
});
2021-11-18 22:05:49 +05:30
describe('tracking interface events with data-track-action', () => {
2019-12-21 20:55:43 +05:30
let eventSpy;
2019-10-12 21:52:04 +05:30
beforeEach(() => {
eventSpy = jest.spyOn(Tracking, 'event');
setHTMLFixture(`
2021-11-18 22:05:49 +05:30
<input data-track-action="click_input1" data-track-label="button" value="0" />
<input data-track-action="click_input2" data-track-value="0" value="0" />
<input type="checkbox" data-track-action="toggle_checkbox" value=1 checked />
<input class="dropdown" data-track-action="toggle_dropdown"/>
<div data-track-action="nested_event"><span class="nested"></span></div>
<input data-track-bogus="click_bogusinput" data-track-label="button" value="1" />
<input data-track-action="click_input3" data-track-experiment="example" value="1" />
<input data-track-action="event_with_extra" data-track-extra='{ "foo": "bar" }' />
<input data-track-action="event_with_invalid_extra" data-track-extra="invalid_json" />
2019-10-12 21:52:04 +05:30
`);
});
2021-11-18 22:05:49 +05:30
it(`binds to clicks on elements matching [data-track-action]`, () => {
document.querySelector(`[data-track-action="click_input1"]`).click();
2019-10-12 21:52:04 +05:30
2021-11-18 22:05:49 +05:30
expect(eventSpy).toHaveBeenCalledWith(TEST_CATEGORY, 'click_input1', {
label: TEST_LABEL,
2021-09-04 01:27:46 +05:30
value: '0',
2019-10-12 21:52:04 +05:30
});
});
2021-11-18 22:05:49 +05:30
it(`does not bind to clicks on elements without [data-track-action]`, () => {
2021-04-29 21:17:54 +05:30
document.querySelector('[data-track-bogus="click_bogusinput"]').click();
2020-05-24 23:13:21 +05:30
expect(eventSpy).not.toHaveBeenCalled();
});
2019-10-12 21:52:04 +05:30
it('allows value override with the data-track-value attribute', () => {
2021-11-18 22:05:49 +05:30
document.querySelector(`[data-track-action="click_input2"]`).click();
2019-10-12 21:52:04 +05:30
2021-11-18 22:05:49 +05:30
expect(eventSpy).toHaveBeenCalledWith(TEST_CATEGORY, 'click_input2', {
2021-09-04 01:27:46 +05:30
value: '0',
2019-10-12 21:52:04 +05:30
});
2022-05-07 20:08:51 +05:30
2023-06-20 00:43:36 +05:30
expect(snowplowSpy).toHaveBeenCalledWith('trackStructEvent', {
category: TEST_CATEGORY,
action: 'click_input2',
label: undefined,
property: undefined,
value: 0,
context: [standardContext],
});
2019-10-12 21:52:04 +05:30
});
it('handles checkbox values correctly', () => {
2021-11-18 22:05:49 +05:30
const checkbox = document.querySelector(`[data-track-action="toggle_checkbox"]`);
2020-06-23 00:09:42 +05:30
checkbox.click(); // unchecking
2019-10-12 21:52:04 +05:30
2021-11-18 22:05:49 +05:30
expect(eventSpy).toHaveBeenCalledWith(TEST_CATEGORY, 'toggle_checkbox', {
2021-09-04 01:27:46 +05:30
value: 0,
2019-10-12 21:52:04 +05:30
});
2020-06-23 00:09:42 +05:30
checkbox.click(); // checking
2019-10-12 21:52:04 +05:30
2021-11-18 22:05:49 +05:30
expect(eventSpy).toHaveBeenCalledWith(TEST_CATEGORY, 'toggle_checkbox', {
2021-09-04 01:27:46 +05:30
value: '1',
2019-10-12 21:52:04 +05:30
});
});
it('handles bootstrap dropdowns', () => {
2021-11-18 22:05:49 +05:30
const dropdown = document.querySelector(`[data-track-action="toggle_dropdown"]`);
2020-06-23 00:09:42 +05:30
dropdown.dispatchEvent(new Event('show.bs.dropdown', { bubbles: true }));
2019-10-12 21:52:04 +05:30
2021-11-18 22:05:49 +05:30
expect(eventSpy).toHaveBeenCalledWith(TEST_CATEGORY, 'toggle_dropdown_show', {});
2019-10-12 21:52:04 +05:30
2020-06-23 00:09:42 +05:30
dropdown.dispatchEvent(new Event('hide.bs.dropdown', { bubbles: true }));
2019-12-21 20:55:43 +05:30
2021-11-18 22:05:49 +05:30
expect(eventSpy).toHaveBeenCalledWith(TEST_CATEGORY, 'toggle_dropdown_hide', {});
2019-12-21 20:55:43 +05:30
});
2019-10-12 21:52:04 +05:30
2019-12-21 20:55:43 +05:30
it('handles nested elements inside an element with tracking', () => {
2020-06-23 00:09:42 +05:30
document.querySelector('span.nested').click();
2019-10-12 21:52:04 +05:30
2021-11-18 22:05:49 +05:30
expect(eventSpy).toHaveBeenCalledWith(TEST_CATEGORY, 'nested_event', {});
2019-10-12 21:52:04 +05:30
});
2021-04-17 20:07:23 +05:30
2021-04-29 21:17:54 +05:30
it('includes experiment data if linked to an experiment', () => {
2021-04-17 20:07:23 +05:30
const mockExperimentData = {
variant: 'candidate',
2021-11-11 11:23:49 +05:30
experiment: 'example',
2021-04-17 20:07:23 +05:30
key: '2bff73f6bb8cc11156c50a8ba66b9b8b',
};
getExperimentData.mockReturnValue(mockExperimentData);
2021-11-18 22:05:49 +05:30
document.querySelector(`[data-track-action="click_input3"]`).click();
2021-04-17 20:07:23 +05:30
2021-11-18 22:05:49 +05:30
expect(eventSpy).toHaveBeenCalledWith(TEST_CATEGORY, 'click_input3', {
value: '1',
2021-04-17 20:07:23 +05:30
context: { schema: TRACKING_CONTEXT_SCHEMA, data: mockExperimentData },
});
});
2021-09-04 01:27:46 +05:30
it('supports extra data as JSON', () => {
2021-11-18 22:05:49 +05:30
document.querySelector(`[data-track-action="event_with_extra"]`).click();
2021-09-04 01:27:46 +05:30
2021-11-18 22:05:49 +05:30
expect(eventSpy).toHaveBeenCalledWith(TEST_CATEGORY, 'event_with_extra', {
2021-09-04 01:27:46 +05:30
extra: { foo: 'bar' },
});
});
it('ignores extra if provided JSON is invalid', () => {
2021-11-18 22:05:49 +05:30
document.querySelector(`[data-track-action="event_with_invalid_extra"]`).click();
2021-09-04 01:27:46 +05:30
2021-11-18 22:05:49 +05:30
expect(eventSpy).toHaveBeenCalledWith(TEST_CATEGORY, 'event_with_invalid_extra', {});
2021-09-04 01:27:46 +05:30
});
2019-10-12 21:52:04 +05:30
});
2020-01-01 13:55:28 +05:30
2021-11-18 22:05:49 +05:30
describe('tracking page loaded events with -action', () => {
2020-05-24 23:13:21 +05:30
let eventSpy;
beforeEach(() => {
eventSpy = jest.spyOn(Tracking, 'event');
setHTMLFixture(`
2021-11-18 22:05:49 +05:30
<div data-track-action="click_link" data-track-label="all_nested_links">
<input data-track-action="render" data-track-label="label1" value=1 data-track-property="_property_" />
<span data-track-action="render" data-track-label="label2" data-track-value="1">
2021-10-27 15:23:28 +05:30
<a href="#" id="link">Something</a>
</span>
2021-11-18 22:05:49 +05:30
<input data-track-action="_render_bogus_" data-track-label="label3" value="_value_" data-track-property="_property_" />
2021-10-27 15:23:28 +05:30
</div>
2020-05-24 23:13:21 +05:30
`);
2021-11-18 22:05:49 +05:30
Tracking.trackLoadEvents(TEST_CATEGORY);
2020-05-24 23:13:21 +05:30
});
2021-11-18 22:05:49 +05:30
it(`sends tracking events when [data-track-action="render"] is on an element`, () => {
2020-05-24 23:13:21 +05:30
expect(eventSpy.mock.calls).toEqual([
[
2021-11-18 22:05:49 +05:30
TEST_CATEGORY,
2020-05-24 23:13:21 +05:30
'render',
{
label: 'label1',
2021-09-04 01:27:46 +05:30
value: '1',
2020-05-24 23:13:21 +05:30
property: '_property_',
},
],
[
2021-11-18 22:05:49 +05:30
TEST_CATEGORY,
2020-05-24 23:13:21 +05:30
'render',
{
label: 'label2',
2021-09-04 01:27:46 +05:30
value: '1',
2020-05-24 23:13:21 +05:30
},
],
]);
});
2021-10-27 15:23:28 +05:30
describe.each`
event | actionSuffix
${'click'} | ${''}
${'show.bs.dropdown'} | ${'_show'}
${'hide.bs.dropdown'} | ${'_hide'}
`(`auto-tracking $event events on nested elements`, ({ event, actionSuffix }) => {
let link;
beforeEach(() => {
link = document.querySelector('#link');
eventSpy.mockClear();
});
2021-11-18 22:05:49 +05:30
it(`avoids using ancestor [data-track-action="render"] tracking configurations`, () => {
2021-10-27 15:23:28 +05:30
link.dispatchEvent(new Event(event, { bubbles: true }));
expect(eventSpy).not.toHaveBeenCalledWith(
2021-11-18 22:05:49 +05:30
TEST_CATEGORY,
2021-10-27 15:23:28 +05:30
`render${actionSuffix}`,
expect.any(Object),
);
expect(eventSpy).toHaveBeenCalledWith(
2021-11-18 22:05:49 +05:30
TEST_CATEGORY,
2021-10-27 15:23:28 +05:30
`click_link${actionSuffix}`,
expect.objectContaining({ label: 'all_nested_links' }),
);
});
});
2020-05-24 23:13:21 +05:30
});
2020-01-01 13:55:28 +05:30
describe('tracking mixin', () => {
describe('trackingOptions', () => {
2021-04-17 20:07:23 +05:30
it('returns the options defined on initialisation', () => {
2020-01-01 13:55:28 +05:30
const mixin = Tracking.mixin({ foo: 'bar' });
expect(mixin.computed.trackingOptions()).toEqual({ foo: 'bar' });
});
2021-04-17 20:07:23 +05:30
it('lets local tracking value override and extend options', () => {
2020-01-01 13:55:28 +05:30
const mixin = Tracking.mixin({ foo: 'bar' });
2021-04-17 20:07:23 +05:30
// The value of this in the Vue lifecyle is different, but this serves the test's purposes
2020-01-01 13:55:28 +05:30
mixin.computed.tracking = { foo: 'baz', baz: 'bar' };
expect(mixin.computed.trackingOptions()).toEqual({ foo: 'baz', baz: 'bar' });
});
2021-04-29 21:17:54 +05:30
it('includes experiment data if linked to an experiment', () => {
const mockExperimentData = {
variant: 'candidate',
experiment: 'darkMode',
};
getExperimentData.mockReturnValue(mockExperimentData);
const mixin = Tracking.mixin({ foo: 'bar', experiment: 'darkMode' });
expect(mixin.computed.trackingOptions()).toEqual({
foo: 'bar',
context: {
schema: TRACKING_CONTEXT_SCHEMA,
data: mockExperimentData,
},
});
});
it('does not include experiment data if experiment data does not exist', () => {
const mixin = Tracking.mixin({ foo: 'bar', experiment: 'lightMode' });
expect(mixin.computed.trackingOptions()).toEqual({
foo: 'bar',
});
});
2020-01-01 13:55:28 +05:30
});
describe('trackingCategory', () => {
2021-04-17 20:07:23 +05:30
it('returns the category set in the component properties first', () => {
2020-01-01 13:55:28 +05:30
const mixin = Tracking.mixin({ category: 'foo' });
mixin.computed.tracking = {
category: 'bar',
};
expect(mixin.computed.trackingCategory()).toBe('bar');
});
2021-04-17 20:07:23 +05:30
it('returns the category set in the options', () => {
2020-01-01 13:55:28 +05:30
const mixin = Tracking.mixin({ category: 'foo' });
expect(mixin.computed.trackingCategory()).toBe('foo');
});
2021-04-17 20:07:23 +05:30
it('returns undefined if no category is selected', () => {
2020-01-01 13:55:28 +05:30
const mixin = Tracking.mixin();
expect(mixin.computed.trackingCategory()).toBe(undefined);
});
});
describe('track', () => {
let eventSpy;
let mixin;
beforeEach(() => {
eventSpy = jest.spyOn(Tracking, 'event').mockReturnValue();
mixin = Tracking.mixin();
mixin = {
...mixin.computed,
...mixin.methods,
};
});
2020-04-08 14:13:33 +05:30
it('calls the event method with no category or action defined', () => {
mixin.trackingCategory = mixin.trackingCategory();
mixin.trackingOptions = mixin.trackingOptions();
mixin.track();
expect(eventSpy).toHaveBeenCalledWith(undefined, undefined, {});
});
2020-01-01 13:55:28 +05:30
it('calls the event method', () => {
mixin.trackingCategory = mixin.trackingCategory();
mixin.trackingOptions = mixin.trackingOptions();
mixin.track('foo');
expect(eventSpy).toHaveBeenCalledWith(undefined, 'foo', {});
});
2021-04-17 20:07:23 +05:30
it('gives precedence to data for category and options', () => {
2020-01-01 13:55:28 +05:30
mixin.trackingCategory = mixin.trackingCategory();
mixin.trackingOptions = mixin.trackingOptions();
const data = { category: 'foo', label: 'baz' };
mixin.track('foo', data);
expect(eventSpy).toHaveBeenCalledWith('foo', 'foo', data);
});
});
});
2019-10-12 21:52:04 +05:30
});