@guardian/ophan-tracker-js 2.1.0 → 2.1.1-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/dist/assets/adblock-detection.d.ts +5 -0
  2. package/dist/assets/adblock-detection.js +20 -0
  3. package/dist/assets/attention.d.ts +10 -0
  4. package/dist/assets/attention.js +126 -0
  5. package/dist/assets/campaign.d.ts +2 -0
  6. package/dist/assets/campaign.js +23 -0
  7. package/dist/assets/click-path-capture.d.ts +15 -0
  8. package/dist/assets/click-path-capture.js +68 -0
  9. package/dist/assets/components.d.ts +61 -0
  10. package/dist/assets/components.js +168 -0
  11. package/dist/assets/contribution.d.ts +6 -0
  12. package/{assets → dist/assets}/contribution.js +3 -12
  13. package/dist/assets/core.d.ts +65 -0
  14. package/dist/assets/core.js +230 -0
  15. package/dist/assets/embed.d.ts +6 -0
  16. package/{assets → dist/assets}/embed.js +2 -5
  17. package/dist/assets/fb-instant.d.ts +5 -0
  18. package/{assets → dist/assets}/fb-instant.js +1 -4
  19. package/dist/assets/heatmap.d.ts +6 -0
  20. package/dist/assets/heatmap.js +34 -0
  21. package/dist/assets/holidays.d.ts +6 -0
  22. package/{assets → dist/assets}/holidays.js +2 -5
  23. package/dist/assets/http-status.d.ts +6 -0
  24. package/{assets → dist/assets}/http-status.js +3 -5
  25. package/dist/assets/iframe-tracking.d.ts +2 -0
  26. package/dist/assets/iframe-tracking.js +80 -0
  27. package/dist/assets/interactive.d.ts +2 -0
  28. package/dist/assets/interactive.js +17 -0
  29. package/dist/assets/jobs-courses.d.ts +6 -0
  30. package/{assets → dist/assets}/jobs-courses.js +2 -5
  31. package/dist/assets/jobs.d.ts +6 -0
  32. package/{assets → dist/assets}/jobs.js +2 -5
  33. package/dist/assets/manage-my-account.d.ts +8 -0
  34. package/{assets → dist/assets}/manage-my-account.js +13 -22
  35. package/dist/assets/membership.d.ts +6 -0
  36. package/{assets → dist/assets}/membership.js +3 -12
  37. package/dist/assets/ng.d.ts +25 -0
  38. package/dist/assets/ng.js +53 -0
  39. package/dist/assets/perf.d.ts +3 -0
  40. package/dist/assets/perf.js +36 -0
  41. package/dist/assets/privatebrowsing.d.ts +7 -0
  42. package/dist/assets/privatebrowsing.js +66 -0
  43. package/dist/assets/r2.d.ts +5 -0
  44. package/{assets → dist/assets}/r2.js +2 -11
  45. package/dist/assets/smart-news.d.ts +6 -0
  46. package/{assets → dist/assets}/smart-news.js +3 -9
  47. package/dist/assets/support.d.ts +10 -0
  48. package/dist/assets/support.js +28 -0
  49. package/dist/assets/tests/click-path-capture.test.d.ts +2 -0
  50. package/dist/assets/tests/click-path-capture.test.js +44 -0
  51. package/dist/assets/tests/components.test.d.ts +2 -0
  52. package/dist/assets/tests/components.test.js +47 -0
  53. package/dist/assets/tests/core.test.d.ts +2 -0
  54. package/dist/assets/tests/core.test.js +161 -0
  55. package/dist/assets/tests/heatmap.test.d.ts +2 -0
  56. package/dist/assets/tests/heatmap.test.js +45 -0
  57. package/dist/assets/tests/transmit.test.d.ts +2 -0
  58. package/dist/assets/tests/transmit.test.js +118 -0
  59. package/dist/assets/transmit.d.ts +54 -0
  60. package/dist/assets/transmit.js +130 -0
  61. package/dist/assets/vendor/adBlockDetectionLib.d.ts +5 -0
  62. package/dist/assets/vendor/adBlockDetectionLib.js +312 -0
  63. package/dist/assets/visibility.d.ts +6 -0
  64. package/dist/assets/visibility.js +22 -0
  65. package/dist/assets/witness.d.ts +6 -0
  66. package/{assets → dist/assets}/witness.js +2 -5
  67. package/dist/types/abtest.d.ts +29 -0
  68. package/dist/types/abtest.js +0 -0
  69. package/dist/types/acquisition.d.ts +148 -0
  70. package/dist/types/acquisition.js +35 -0
  71. package/dist/types/component-event.d.ts +92 -0
  72. package/dist/types/component-event.js +67 -0
  73. package/dist/types/component-type.d.ts +58 -0
  74. package/dist/types/component-type.js +56 -0
  75. package/dist/types/consent.d.ts +47 -0
  76. package/dist/types/consent.js +15 -0
  77. package/dist/types/event.d.ts +115 -0
  78. package/dist/types/event.js +7 -0
  79. package/dist/types/inpageclick.d.ts +12 -0
  80. package/dist/types/inpageclick.js +0 -0
  81. package/dist/types/interaction.d.ts +18 -0
  82. package/dist/types/interaction.js +0 -0
  83. package/dist/types/link.d.ts +15 -0
  84. package/dist/types/link.js +0 -0
  85. package/dist/types/media.d.ts +103 -0
  86. package/dist/types/media.js +70 -0
  87. package/dist/types/platform.d.ts +33 -0
  88. package/dist/types/platform.js +33 -0
  89. package/dist/types/printproduct.d.ts +54 -0
  90. package/dist/types/printproduct.js +39 -0
  91. package/dist/types/product.d.ts +56 -0
  92. package/dist/types/product.js +54 -0
  93. package/dist/types/subscription.d.ts +65 -0
  94. package/dist/types/subscription.js +63 -0
  95. package/package.json +8 -5
  96. package/readme.md +98 -11
  97. package/assets/adblock-detection.js +0 -19
  98. package/assets/attention.js +0 -160
  99. package/assets/campaign.js +0 -25
  100. package/assets/click-path-capture.js +0 -80
  101. package/assets/components.js +0 -199
  102. package/assets/core.js +0 -282
  103. package/assets/heatmap.js +0 -53
  104. package/assets/iframe-tracking.js +0 -88
  105. package/assets/interactive.js +0 -17
  106. package/assets/ng.js +0 -41
  107. package/assets/perf.js +0 -40
  108. package/assets/privatebrowsing.js +0 -71
  109. package/assets/support.js +0 -34
  110. package/assets/transmit.js +0 -143
  111. package/assets/vendor/adBlockDetectionLib.js +0 -366
  112. package/assets/visibility.js +0 -23
@@ -0,0 +1,53 @@
1
+ // Generated by CoffeeScript 2.7.0
2
+ var ref;
3
+ import core from './core.js';
4
+ import transmit from './transmit.js';
5
+ import attention from './attention.js';
6
+ import visibility from './visibility.js';
7
+ import privatebrowsing from './privatebrowsing.js';
8
+ import heatmap from './heatmap.js';
9
+ import './click-path-capture.js';
10
+ import './perf.js';
11
+ import './iframe-tracking.js';
12
+ export * from '../types/abtest';
13
+ export * from '../types/acquisition';
14
+ export * from '../types/subscription';
15
+ export * from '../types/interaction';
16
+ export * from '../types/inpageclick';
17
+ export * from '../types/printproduct';
18
+ export * from '../types/product';
19
+ export * from '../types/platform';
20
+ export * from '../types/media';
21
+ export * from '../types/consent';
22
+ export * from '../types/component-type';
23
+ export * from '../types/component-event';
24
+ export * from '../types/event';
25
+ // TODO:
26
+ // pageViewId is used for Google Analytics on frontend, membership and some of the jobs sites.
27
+ // If we can migrate these to use viewId instead then pageViewId can safely be removed
28
+ /** @typedef {import("../types/event.ts").EventPayload} EventPayload */
29
+ /** @type {{record: (event: EventPayload, callback?: Function) => void, pageViewId: string, viewId: string, trackComponentAttention: (name: string, el: HTMLElement, visibilityThreshold: number) => void, setEventEmitter: (event: Object) => void}} */
30
+ let ng;
31
+ if (window.guardian && window.guardian.ophan) {
32
+ ng = window.guardian.ophan;
33
+ }
34
+ else {
35
+ core.init('next-gen');
36
+ privatebrowsing.init();
37
+ heatmap.init();
38
+ if (window.addEventListener) {
39
+ attention.init(visibility);
40
+ }
41
+ if (!window.guardian) {
42
+ window.guardian = {};
43
+ }
44
+ window.guardian.ophan = {
45
+ setEventEmitter: attention.setEventEmitter,
46
+ trackComponentAttention: attention.initComponent,
47
+ record: transmit.sendMore,
48
+ viewId: transmit.viewId,
49
+ pageViewId: transmit.viewId
50
+ };
51
+ ng = window.guardian.ophan;
52
+ }
53
+ export default ng;
@@ -0,0 +1,3 @@
1
+ export default perf;
2
+ declare const perf: Performance;
3
+ //# sourceMappingURL=perf.d.ts.map
@@ -0,0 +1,36 @@
1
+ // Generated by CoffeeScript 2.7.0
2
+ import core from './core.js';
3
+ const perf = window.performance ||
4
+ window.msPerformance ||
5
+ window.webkitPerformance ||
6
+ window.mozPerformance;
7
+ const sendTimingData = () => {
8
+ const t = perf?.timing;
9
+ if (t) {
10
+ return {
11
+ performance: {
12
+ // Time required for domain lookup.
13
+ dns: t.domainLookupEnd - t.domainLookupStart,
14
+ // Time to establish a connection to server.
15
+ connection: t.connectEnd - t.connectStart,
16
+ // From connection established to first byte of data.
17
+ firstByte: t.responseStart - t.connectEnd,
18
+ // First byte to last byte, or closed, including if from cache.
19
+ lastByte: t.responseEnd - t.responseStart,
20
+ // From last byte of doc to start of domContentLoaded
21
+ domContentLoadedEvent: t.domContentLoadedEventStart - t.responseEnd,
22
+ // domcontentLoaded to start of load event.
23
+ loadEvent: t.loadEventStart - t.domContentLoadedEventStart,
24
+ // click, back/forward, etc...
25
+ navType: perf.navigation.type,
26
+ // No. of redirects on current domain.
27
+ redirectCount: perf.navigation.redirectCount,
28
+ },
29
+ };
30
+ }
31
+ else {
32
+ return {};
33
+ }
34
+ };
35
+ core.onLoadCapture(sendTimingData);
36
+ export default perf;
@@ -0,0 +1,7 @@
1
+ declare namespace _default {
2
+ export function init(): void;
3
+ export { privateBrowsingMode };
4
+ }
5
+ export default _default;
6
+ declare function privateBrowsingMode(callback: any): void;
7
+ //# sourceMappingURL=privatebrowsing.d.ts.map
@@ -0,0 +1,66 @@
1
+ import transmit from './transmit.js';
2
+ /*
3
+ TO-DO:
4
+ This function could probably be simplified with an async function.
5
+ It basically returns a `boolean`, but uses a callback to do so... I can only guess it’s because of [`requestFileSystem`’s signature](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestFileSystem).
6
+ */
7
+ const privateBrowsingMode = (callback) => {
8
+ const reportNormalMode = () => callback(false);
9
+ const reportPrivateMode = () => callback(true);
10
+ const tryLocalStorage = () => {
11
+ try {
12
+ if (localStorage.length) {
13
+ reportNormalMode();
14
+ }
15
+ else {
16
+ localStorage.x = 1;
17
+ localStorage.removeItem('x');
18
+ reportNormalMode();
19
+ }
20
+ }
21
+ catch (error) {
22
+ reportPrivateMode();
23
+ }
24
+ };
25
+ const tryIndexedDB = () => {
26
+ try {
27
+ const db = indexedDB.open('test');
28
+ db.onsuccess = reportNormalMode;
29
+ db.onerror = reportPrivateMode;
30
+ }
31
+ catch (error) {
32
+ reportPrivateMode();
33
+ }
34
+ };
35
+ // Blink
36
+ if (window.webkitRequestFileSystem) {
37
+ window.webkitRequestFileSystem(window.TEMPORARY, 1, reportNormalMode, reportPrivateMode);
38
+ // Firefox
39
+ }
40
+ else if ('MozAppearance' in document.documentElement.style) {
41
+ tryIndexedDB();
42
+ // Safari
43
+ }
44
+ else if (/constructor/i.test(window.HTMLElement)) {
45
+ tryLocalStorage();
46
+ }
47
+ else if (!window.indexedDB &&
48
+ (window.PointerEvent || window.MSPointerEvent)) {
49
+ reportPrivateMode();
50
+ }
51
+ else {
52
+ // Rest
53
+ reportNormalMode();
54
+ }
55
+ };
56
+ export default {
57
+ init() {
58
+ privateBrowsingMode((value) => {
59
+ const report = {
60
+ inPrivateBrowsingMode: value,
61
+ };
62
+ transmit.sendMore(report);
63
+ });
64
+ },
65
+ privateBrowsingMode,
66
+ };
@@ -0,0 +1,5 @@
1
+ declare namespace _default {
2
+ let viewId: any;
3
+ }
4
+ export default _default;
5
+ //# sourceMappingURL=r2.d.ts.map
@@ -1,24 +1,15 @@
1
1
  // Generated by CoffeeScript 2.7.0
2
2
  import core from './core.js';
3
-
4
3
  import transmit from './transmit.js';
5
-
6
4
  import attention from './attention.js';
7
-
8
5
  import visibility from './visibility.js';
9
-
10
6
  import './click-path-capture.js';
11
-
12
7
  import './perf.js';
13
-
14
8
  import './campaign.js';
15
-
16
9
  core.init('r2');
17
-
18
10
  if (window.addEventListener != null) {
19
- attention.init(visibility);
11
+ attention.init(visibility);
20
12
  }
21
-
22
13
  export default {
23
- viewId: transmit.viewId,
14
+ viewId: transmit.viewId,
24
15
  };
@@ -0,0 +1,6 @@
1
+ declare namespace _default {
2
+ let record: (obj: import("../types/event.js").EventPayload, f?: Function | undefined) => void;
3
+ let viewId: any;
4
+ }
5
+ export default _default;
6
+ //# sourceMappingURL=smart-news.d.ts.map
@@ -1,19 +1,13 @@
1
1
  // Generated by CoffeeScript 2.7.0
2
2
  import core from './core.js';
3
-
4
3
  import transmit from './transmit.js';
5
-
6
4
  import attention from './attention.js';
7
-
8
5
  import visibility from './visibility.js';
9
-
10
6
  core.init('smart-news');
11
-
12
7
  if (window.addEventListener != null) {
13
- attention.init(visibility);
8
+ attention.init(visibility);
14
9
  }
15
-
16
10
  export default {
17
- record: transmit.sendMore,
18
- viewId: transmit.viewId,
11
+ record: transmit.sendMore,
12
+ viewId: transmit.viewId,
19
13
  };
@@ -0,0 +1,10 @@
1
+ declare namespace _default {
2
+ export { init };
3
+ export { sendInitialEvent };
4
+ export let record: (obj: import("../types/event.js").EventPayload, f?: Function | undefined) => void;
5
+ export let viewId: any;
6
+ }
7
+ export default _default;
8
+ declare function init(): void;
9
+ declare function sendInitialEvent(url?: string, referrer?: string): void;
10
+ //# sourceMappingURL=support.d.ts.map
@@ -0,0 +1,28 @@
1
+ import core from './core.js';
2
+ import transmit from './transmit.js';
3
+ import attention from './attention.js';
4
+ import visibility from './visibility.js';
5
+ import './click-path-capture.js';
6
+ import './perf.js';
7
+ import './campaign.js';
8
+ const init = () => {
9
+ core.init('support');
10
+ if (window.addEventListener) {
11
+ attention.init(visibility);
12
+ }
13
+ };
14
+ const sendInitialEvent = (url = location.href, referrer = document.referrer) => {
15
+ try {
16
+ transmit.bumpViewId();
17
+ core.sendInitialEvent(null, url, referrer);
18
+ }
19
+ catch (error) {
20
+ console.log(error);
21
+ }
22
+ };
23
+ export default {
24
+ init,
25
+ sendInitialEvent,
26
+ record: transmit.sendMore,
27
+ viewId: transmit.viewId,
28
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=click-path-capture.test.d.ts.map
@@ -0,0 +1,44 @@
1
+ import clickPathCapture, { validAncestorAnchorElement, getContainingComponent } from '../click-path-capture.js';
2
+ const { getDataLinkNames } = clickPathCapture;
3
+ describe('click-path-capture.js', () => {
4
+ beforeAll(() => {
5
+ document.body.innerHTML = `
6
+ <div data-component="component1">
7
+ <a href="#" data-link-name="link1">
8
+ <span id="testElement1"></span>
9
+ </a>
10
+ </div>
11
+ <div id="testElement2" data-component="component2"></div>
12
+ `;
13
+ });
14
+ describe('validAncestorAnchorElement', () => {
15
+ test('should return the ancestor anchor element', () => {
16
+ const element = document.getElementById('testElement1');
17
+ expect(validAncestorAnchorElement(element)).toBe(element.closest('a'));
18
+ });
19
+ test('should return null when no anchor ancestor is present', () => {
20
+ const element = document.getElementById('testElement2');
21
+ expect(validAncestorAnchorElement(element)).toBeNull();
22
+ });
23
+ });
24
+ describe('getContainingComponent', () => {
25
+ test('should find the parent element with data-component attribute', () => {
26
+ const element = document.getElementById('testElement1');
27
+ expect(getContainingComponent(element)).toBe('component1');
28
+ });
29
+ test('should return null if no data-component parent is found', () => {
30
+ const element = document.createElement('div');
31
+ expect(getContainingComponent(element)).toBeNull();
32
+ });
33
+ });
34
+ describe('getDataLinkNames', () => {
35
+ test('should collect data-link-name attributes from ancestors', () => {
36
+ const element = document.getElementById('testElement1');
37
+ expect(getDataLinkNames(element)).toEqual(['link1']);
38
+ });
39
+ test('should return an empty array when no data-link-name attributes are present', () => {
40
+ const element = document.getElementById('testElement2');
41
+ expect(getDataLinkNames(element)).toEqual([]);
42
+ });
43
+ });
44
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=components.test.d.ts.map
@@ -0,0 +1,47 @@
1
+ import { default as testExports } from '../components.js';
2
+ import { components } from "../components.js";
3
+ const { startMonitoring, stopMonitoring, getAttentionTimes } = testExports;
4
+ describe('components.js', () => {
5
+ beforeEach(() => {
6
+ components.length = 0;
7
+ components.push({
8
+ name: 'comp1',
9
+ isVisible: jest.fn().mockReturnValue(true),
10
+ makeActive: jest.fn(),
11
+ makeInactive: jest.fn(),
12
+ hadAttentionSinceLastGet: jest.fn().mockReturnValue(true),
13
+ getAttentionTime: jest.fn().mockReturnValue(123),
14
+ });
15
+ components.push({
16
+ name: 'comp2',
17
+ isVisible: jest.fn().mockReturnValue(true),
18
+ makeActive: jest.fn(),
19
+ makeInactive: jest.fn(),
20
+ hadAttentionSinceLastGet: jest.fn().mockReturnValue(true),
21
+ getAttentionTime: jest.fn().mockReturnValue(123),
22
+ });
23
+ });
24
+ test('startMonitoring activates visible components', () => {
25
+ components[0].isVisible.mockReturnValue(true);
26
+ components[1].isVisible.mockReturnValue(false);
27
+ const results = startMonitoring();
28
+ expect(components[0].makeActive).toHaveBeenCalled();
29
+ expect(components[1].makeActive).not.toHaveBeenCalled();
30
+ expect(results).toHaveLength(1);
31
+ });
32
+ test('stopMonitoring deactivates all components', () => {
33
+ const results = stopMonitoring();
34
+ components.forEach((component) => {
35
+ expect(component.makeInactive).toHaveBeenCalled();
36
+ });
37
+ expect(results).toHaveLength(components.length);
38
+ });
39
+ test('getAttentionTimes returns attention times for components with attention', () => {
40
+ components[0].hadAttentionSinceLastGet.mockReturnValue(true);
41
+ components[0].getAttentionTime.mockReturnValue(123);
42
+ components[1].hadAttentionSinceLastGet.mockReturnValue(false);
43
+ const attentionTimes = getAttentionTimes();
44
+ expect(attentionTimes).toEqual({ comp1: 123 });
45
+ expect(components[1].getAttentionTime).not.toHaveBeenCalled();
46
+ });
47
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=core.test.d.ts.map
@@ -0,0 +1,161 @@
1
+ import { _testExports } from '../core.js';
2
+ import transmit from '../transmit.js';
3
+ const mockSendMore = jest.fn();
4
+ transmit.sendMore = mockSendMore;
5
+ const { getQueryParameterValue, appendComponentDataToEvent, trimComponentNamesToUrlSafeLength, onLoadCapture, onLoadCaptures, processCaptures, appendContentType, } = _testExports;
6
+ describe('appendComponentDataToEvent', () => {
7
+ beforeEach(() => {
8
+ jest.clearAllMocks();
9
+ });
10
+ it('should append unique, trimmed component names to event', () => {
11
+ document.querySelectorAll = jest.fn().mockReturnValue([
12
+ {
13
+ getAttribute: () => 'longComponentNameThatExceedsLimit................................................',
14
+ },
15
+ { getAttribute: () => 'shortName' },
16
+ {
17
+ getAttribute: () => 'longComponentNameThatExceedsLimit................................................',
18
+ },
19
+ ]);
20
+ const event = {};
21
+ appendComponentDataToEvent(event);
22
+ expect(document.querySelectorAll).toHaveBeenCalledWith('[data-component]');
23
+ const expectedTrimmedNames = [
24
+ 'longComponentNameThatExceedsLimit.................…',
25
+ 'shortName',
26
+ ];
27
+ expect(event.renderedComponents).toEqual(expectedTrimmedNames);
28
+ });
29
+ it('should not modify event if no components are found', () => {
30
+ document.querySelectorAll = jest.fn().mockReturnValue([]);
31
+ const event = {};
32
+ appendComponentDataToEvent(event);
33
+ expect(event).not.toHaveProperty('renderedComponents');
34
+ });
35
+ it('should correctly trim component names exceeding maximum length', () => {
36
+ const componentNames = [
37
+ 'veryLongComponentName................................................',
38
+ 'shortName',
39
+ ];
40
+ const trimmedNames = trimComponentNamesToUrlSafeLength(componentNames);
41
+ expect(trimmedNames).toEqual([
42
+ 'veryLongComponentName.............................…',
43
+ 'shortName',
44
+ ]);
45
+ });
46
+ });
47
+ describe('getQueryParameterValue', () => {
48
+ const originalLocation = window.location;
49
+ beforeEach(() => {
50
+ delete window.location;
51
+ window.location = { search: '' };
52
+ });
53
+ afterEach(() => {
54
+ window.location = originalLocation;
55
+ });
56
+ it('should return the value of a given query parameter', () => {
57
+ window.location.search = '?param1=value1&param2=value2';
58
+ const value = getQueryParameterValue('param1');
59
+ expect(value).toBe('value1');
60
+ });
61
+ it('should return undefined if the parameter is not found', () => {
62
+ window.location.search = '?param1=value1&param2=value2';
63
+ const value = getQueryParameterValue('param3');
64
+ expect(value).toBeUndefined();
65
+ });
66
+ it('should correctly decode URI-encoded values', () => {
67
+ window.location.search = '?param=encoded%20value';
68
+ const value = getQueryParameterValue('param');
69
+ expect(value).toBe('encoded value');
70
+ });
71
+ it('should handle cases where the query string is empty', () => {
72
+ window.location.search = '';
73
+ const value = getQueryParameterValue('param');
74
+ expect(value).toBeUndefined();
75
+ });
76
+ });
77
+ describe('onLoadCapture', () => {
78
+ beforeEach(() => {
79
+ Object.defineProperty(document, 'readyState', {
80
+ configurable: true,
81
+ get() {
82
+ return this._readyState;
83
+ },
84
+ set(value) {
85
+ this._readyState = value;
86
+ },
87
+ });
88
+ document.readyState = 'loading';
89
+ onLoadCaptures.length = 0;
90
+ jest.clearAllMocks();
91
+ });
92
+ it('should add dataCapture to onLoadCaptures if document is not ready', () => {
93
+ const dataCapture = jest.fn();
94
+ onLoadCapture(dataCapture);
95
+ expect(onLoadCaptures).toContain(dataCapture);
96
+ });
97
+ it('should call dataCapture and sendMore if document is ready', () => {
98
+ document.readyState = 'complete';
99
+ const dataCapture = jest.fn().mockReturnValue({
100
+ performance: {
101
+ dns: 50,
102
+ connection: 100,
103
+ firstByte: 200,
104
+ lastByte: 300,
105
+ domContentLoadedEvent: 400,
106
+ loadEvent: 500,
107
+ navType: 1,
108
+ redirectCount: 2
109
+ }
110
+ });
111
+ onLoadCapture(dataCapture);
112
+ expect(dataCapture).toHaveBeenCalled();
113
+ expect(transmit.sendMore).toHaveBeenCalledWith({
114
+ performance: {
115
+ dns: 50,
116
+ connection: 100,
117
+ firstByte: 200,
118
+ lastByte: 300,
119
+ domContentLoadedEvent: 400,
120
+ loadEvent: 500,
121
+ navType: 1,
122
+ redirectCount: 2
123
+ }
124
+ });
125
+ });
126
+ });
127
+ describe('processCaptures', () => {
128
+ beforeEach(() => {
129
+ onLoadCaptures.length = 0;
130
+ jest.clearAllMocks();
131
+ });
132
+ it('should call all dataCaptures and merge their output into the sendMore function', () => {
133
+ onLoadCaptures.push(() => ({ performance: { domContentLoadedEvent: 400, loadEvent: 500 } }));
134
+ processCaptures();
135
+ expect(transmit.sendMore).toHaveBeenCalledWith({
136
+ performance: {
137
+ domContentLoadedEvent: 400,
138
+ loadEvent: 500
139
+ }
140
+ });
141
+ });
142
+ });
143
+ describe('appendContentType', () => {
144
+ it('should set the lowercase content type for next-gen platform', () => {
145
+ const event = {};
146
+ const guardian = { config: { page: { contentType: 'Article' } } };
147
+ appendContentType(event, 'next-gen', guardian);
148
+ expect(event.contentType).toBe('article');
149
+ });
150
+ it('should set the lowercase content type for other platforms', () => {
151
+ const event = {};
152
+ const guardian = { page: { contentTypes: 'LiveBlog' } };
153
+ appendContentType(event, 'other', guardian);
154
+ expect(event.contentType).toBe('liveblog');
155
+ });
156
+ it('should not set content type if it is not available', () => {
157
+ const event = {};
158
+ appendContentType(event, 'next-gen', {});
159
+ expect(event.contentType).toBeUndefined();
160
+ });
161
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=heatmap.test.d.ts.map
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ describe('Heatmap init', () => {
3
+ beforeEach(() => {
4
+ jest.resetModules();
5
+ });
6
+ it('should load the heatmap based on a query param', () => {
7
+ const heatmap = require('../heatmap.js');
8
+ const scriptSrc = '//dashboard.ophan.co.uk/assets/js/heatmap-bookmarklet.js';
9
+ Object.defineProperty(window, 'location', {
10
+ value: new URL('https://www.theguardian.com/uk?heatmap=show'),
11
+ writable: true,
12
+ });
13
+ heatmap.init();
14
+ const scripts = document.getElementsByTagName("script");
15
+ let heatmapScript = Array.from(scripts).find(item => item.src.endsWith(scriptSrc));
16
+ expect(heatmapScript).not.toBeNull();
17
+ });
18
+ it('should load the heatmap script once', () => {
19
+ const heatmap = require('../heatmap.js');
20
+ const scriptSrc = '//dashboard.ophan.co.uk/assets/js/react/heatphan/index.js';
21
+ Object.defineProperty(window, 'location', {
22
+ value: new URL('https://www.theguardian.com/uk?heatmap=experimental'),
23
+ writable: true,
24
+ });
25
+ for (let i = 0; i < 10; i++) {
26
+ heatmap.init();
27
+ }
28
+ const scripts = document.getElementsByTagName("script");
29
+ let heatmapScript = Array.from(scripts).filter(item => item.src.endsWith(scriptSrc));
30
+ expect(heatmapScript.length).toBe(1);
31
+ expect(heatmapScript[0]).not.toBeNull();
32
+ });
33
+ it('should load heatphan based on a query param', () => {
34
+ const heatmap = require('../heatmap.js');
35
+ const scriptSrc = '//dashboard.ophan.co.uk/assets/js/react/heatphan/index.js';
36
+ Object.defineProperty(window, 'location', {
37
+ value: new URL('https://www.theguardian.com/uk?heatmap=experimental'),
38
+ writable: true,
39
+ });
40
+ heatmap.init();
41
+ const scripts = document.getElementsByTagName("script");
42
+ let heatmapScript = Array.from(scripts).find(item => item.src.endsWith(scriptSrc));
43
+ expect(heatmapScript).not.toBeNull();
44
+ });
45
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=transmit.test.d.ts.map