@availity/analytics-core 1.0.0

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.
@@ -0,0 +1,197 @@
1
+ import {
2
+ camelCase,
3
+ getComposedPath,
4
+ isLeftClickEvent,
5
+ isModifiedEvent,
6
+ isPluginEnabled,
7
+ isValidEventTypeOnTarget,
8
+ } from './util';
9
+
10
+ export default class AvAnalytics {
11
+ constructor(plugins, promise = Promise, pageTracking, autoTrack = true, options = {}) {
12
+ // if plugins or promise are undefined,
13
+ // or if either is skipped and pageTracking boolean is used in their place
14
+ if (!plugins || !promise) {
15
+ throw new Error('[plugins] and [promise] must be defined');
16
+ }
17
+
18
+ this.plugins = Array.isArray(plugins) ? plugins : [plugins];
19
+ this.pageTracking = !!pageTracking;
20
+
21
+ if (options.eventModifiers) {
22
+ this.eventModifiers = Array.isArray(options.eventModifiers) ? options.eventModifiers : [options.eventModifiers];
23
+ } else {
24
+ this.eventModifiers = ['action'];
25
+ }
26
+
27
+ this.Promise = promise;
28
+ this.recursive = !!options.recursive;
29
+ this.attributePrefix = options.attributePrefix || 'data-analytics';
30
+
31
+ this.isPageTracking = false;
32
+ this.hasInit = false;
33
+
34
+ if (autoTrack) {
35
+ this.startAutoTrack();
36
+ }
37
+ }
38
+
39
+ startAutoTrack = () => {
40
+ document.body.addEventListener('click', this.handleEvent, true);
41
+ document.body.addEventListener('focus', this.handleEvent, true);
42
+ document.body.addEventListener('blur', this.handleEvent, true);
43
+ };
44
+
45
+ stopAutoTrack = () => {
46
+ document.body.removeEventListener('click', this.handleEvent, true);
47
+ document.body.removeEventListener('focus', this.handleEvent, true);
48
+ document.body.removeEventListener('blur', this.handleEvent, true);
49
+ };
50
+
51
+ handleEvent = (event) => {
52
+ if (this.invalidEvent(event)) {
53
+ return;
54
+ }
55
+ const target = event.target || event.srcElement;
56
+ const path = getComposedPath(event.target);
57
+
58
+ let analyticAttrs = {};
59
+
60
+ if (this.recursive) {
61
+ // Reverse the array so we pull attributes from top down
62
+ for (const pth of path.reverse()) {
63
+ const attrs = this.getAnalyticAttrs(pth);
64
+
65
+ analyticAttrs = { ...analyticAttrs, ...attrs };
66
+
67
+ // To consider using the element it has to have analytics attrs
68
+ if (Object.keys(attrs).length > 0) {
69
+ analyticAttrs.elemId = pth.getAttribute('id') || pth.getAttribute('name') || undefined;
70
+ }
71
+ }
72
+ } else {
73
+ analyticAttrs = this.getAnalyticAttrs(target);
74
+ }
75
+
76
+ const actions = analyticAttrs ? this.eventModifiers.filter((mod) => analyticAttrs[mod] === event.type) : [];
77
+
78
+ if (Object.keys(analyticAttrs).length === 0 || (this.recursive && actions.length === 0) || actions.length === 0) {
79
+ return;
80
+ }
81
+
82
+ analyticAttrs.action = analyticAttrs.action || event.type;
83
+ analyticAttrs.event = event.type;
84
+ analyticAttrs.elemId =
85
+ analyticAttrs.elemId || target.getAttribute('id') || target.getAttribute('name') || undefined;
86
+
87
+ if (analyticAttrs.elemId === undefined) {
88
+ delete analyticAttrs.elemId;
89
+ }
90
+
91
+ // remove keys for the click listeners
92
+ for (const key of actions) {
93
+ if (key !== 'action' && key !== 'event') {
94
+ delete analyticAttrs[key];
95
+ }
96
+ }
97
+
98
+ this.trackEvent(analyticAttrs);
99
+ };
100
+
101
+ invalidEvent = (event) =>
102
+ isModifiedEvent(event) || (event.type === 'click' && !isLeftClickEvent(event)) || !isValidEventTypeOnTarget(event);
103
+
104
+ getAnalyticAttrs = (elem) => {
105
+ if (!elem.attributes) {
106
+ return {};
107
+ }
108
+
109
+ const attrs = elem.attributes;
110
+ const analyticAttrs = {};
111
+
112
+ if (elem.nodeType === 1) {
113
+ for (let i = attrs.length - 1; i >= 0; i--) {
114
+ const { name } = attrs[i];
115
+ if (name.indexOf(`${this.attributePrefix}-`) === 0) {
116
+ const camelName = camelCase(name.slice(this.attributePrefix.length + 1));
117
+ analyticAttrs[camelName] = elem.getAttribute(name);
118
+ }
119
+ }
120
+ }
121
+ return analyticAttrs;
122
+ };
123
+
124
+ startPageTracking = () => {
125
+ if (!this.pageListener) {
126
+ this.pageListener = this.trackPageView;
127
+ window.addEventListener('hashchange', this.pageListener, false);
128
+ }
129
+ };
130
+
131
+ stopPageTracking = () => {
132
+ if (this.pageListener) {
133
+ window.removeEventListener('hashchange', this.pageListener, false);
134
+ delete this.pageListener;
135
+ }
136
+ };
137
+
138
+ init = () => {
139
+ this.setPageTracking();
140
+
141
+ for (const plugin of this.plugins) {
142
+ if (isPluginEnabled(plugin) && typeof plugin.init === 'function') {
143
+ plugin.init();
144
+ }
145
+ }
146
+ };
147
+
148
+ setPageTracking = (value) => {
149
+ // eslint-disable-next-line eqeqeq
150
+ if (value != undefined) {
151
+ this.pageTracking = !!value;
152
+ }
153
+
154
+ const canPageTrack = typeof this.startPageTracking === 'function' && typeof this.stopPageTracking === 'function';
155
+
156
+ if (canPageTrack && this.pageTracking !== this.isPageTracking) {
157
+ if (this.pageTracking) {
158
+ this.startPageTracking();
159
+ } else {
160
+ this.stopPageTracking();
161
+ }
162
+ this.isPageTracking = this.pageTracking;
163
+ }
164
+ };
165
+
166
+ trackEvent = (properties) => {
167
+ const promises = [];
168
+ properties.url = properties.url || window.location.href || 'N/A';
169
+
170
+ for (const plugin of this.plugins) {
171
+ const props = {
172
+ ...properties,
173
+ };
174
+
175
+ if (isPluginEnabled(plugin) && typeof plugin.trackEvent === 'function') {
176
+ promises.push(plugin.trackEvent(props));
177
+ }
178
+ }
179
+ return this.Promise.all(promises);
180
+ };
181
+
182
+ trackPageView = (url) => {
183
+ // hashchanges are an object so we want to grab the new url from it
184
+ if (typeof url === 'object') {
185
+ url = url.newURL;
186
+ }
187
+
188
+ url = url || window.location.href;
189
+ const promises = [];
190
+ for (const plugin of this.plugins) {
191
+ if (isPluginEnabled(plugin) && typeof plugin.trackPageView === 'function') {
192
+ promises.push(plugin.trackPageView(url));
193
+ }
194
+ }
195
+ return this.Promise.all(promises);
196
+ };
197
+ }
package/src/dma.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ import AvAnalyticsPlugin from './plugin';
4
+
5
+ declare class AvDmaAnalytics extends AvAnalyticsPlugin {
6
+ constructor(AvLogMessages: any, enabled?: boolean);
7
+
8
+ trackEvent(properties: any): any;
9
+
10
+ trackPageView(url: string): any;
11
+ }
12
+
13
+ export default AvDmaAnalytics;
package/src/dma.js ADDED
@@ -0,0 +1,45 @@
1
+ import * as yup from 'yup';
2
+ import AvAnalyticsPlugin from './plugin';
3
+
4
+ const schema = yup
5
+ .object()
6
+ .shape({
7
+ level: yup.string().optional(),
8
+ applicationId: yup.string().optional(),
9
+ payerSpaceId: yup.string().optional(),
10
+ label: yup.string().optional(),
11
+ appName: yup.string().optional(),
12
+ category: yup.string().optional(),
13
+ section: yup.string().optional(),
14
+ url: yup.string().optional(),
15
+ value: yup.string().optional(),
16
+ raw: yup.string().optional(),
17
+ feed: yup.string().optional(),
18
+ feedback: yup.string().optional(),
19
+ feedbackName: yup.string().optional(),
20
+ additionalFeedback: yup.string().optional(),
21
+ smile: yup.string().optional(),
22
+ surveyId: yup.string().optional(),
23
+ })
24
+ .noUnknown(true);
25
+
26
+ export default class AvDmaAnalytics extends AvAnalyticsPlugin {
27
+ constructor(AvLogMessages, enabled) {
28
+ super(enabled);
29
+ this.AvLogMessages = AvLogMessages;
30
+ }
31
+
32
+ trackEvent(properties) {
33
+ properties.level = properties.level || 'info';
34
+ // joi validate the properties
35
+ schema.validateSync(properties, {
36
+ strict: true,
37
+ });
38
+
39
+ return this.AvLogMessages[properties.level](properties);
40
+ }
41
+
42
+ trackPageView(url) {
43
+ return this.trackEvent({ event: 'page', url });
44
+ }
45
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { default as AvAnalytics } from './analytics';
2
+ export { default as AvAnalyticsPlugin } from './plugin';
3
+ export { default as AvSplunkAnalytics } from './splunk';
4
+ export { default as AvDmaAnalytics } from './dma';
package/src/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { default as AvAnalytics } from './analytics';
2
+ export { default as AvAnalyticsPlugin } from './plugin';
3
+ export { default as AvSplunkAnalytics } from './splunk';
4
+ export { default as AvDmaAnalytics } from './dma';
@@ -0,0 +1,12 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ declare class AvAnalyticsPlugin {
3
+ constructor(enabled?: boolean);
4
+
5
+ trackEvent(): any;
6
+
7
+ trackPageView(): any;
8
+
9
+ isEnabled(): boolean;
10
+ }
11
+
12
+ export default AvAnalyticsPlugin;
package/src/plugin.js ADDED
@@ -0,0 +1,13 @@
1
+ export default class AvAnalyticsPlugin {
2
+ constructor(enabled = true) {
3
+ this.enabled = !!enabled;
4
+ }
5
+
6
+ trackEvent() {}
7
+
8
+ trackPageView() {}
9
+
10
+ isEnabled() {
11
+ return this.enabled;
12
+ }
13
+ }
@@ -0,0 +1,13 @@
1
+ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ import AvAnalyticsPlugin from './plugin';
4
+
5
+ declare class AvSplunkAnalytics extends AvAnalyticsPlugin {
6
+ constructor(AvLogMessages: any, enabled?: boolean);
7
+
8
+ trackEvent(properties: any): any;
9
+
10
+ trackPageView(url: string): any;
11
+ }
12
+
13
+ export default AvSplunkAnalytics;
package/src/splunk.js ADDED
@@ -0,0 +1,17 @@
1
+ import AvAnalyticsPlugin from './plugin';
2
+
3
+ export default class AvSplunkAnalytics extends AvAnalyticsPlugin {
4
+ constructor(AvLogMessages, enabled) {
5
+ super(enabled);
6
+ this.AvLogMessages = AvLogMessages;
7
+ }
8
+
9
+ trackEvent(properties) {
10
+ properties.level = properties.level || 'info';
11
+ return this.AvLogMessages[properties.level](properties);
12
+ }
13
+
14
+ trackPageView(url) {
15
+ return this.trackEvent({ event: 'page', url });
16
+ }
17
+ }
@@ -0,0 +1,187 @@
1
+ import { AvAnalytics } from '..';
2
+
3
+ type MockPlugin = {
4
+ isEnabled: jest.Mock;
5
+ init: jest.Mock | string;
6
+ trackEvent: jest.Mock | string;
7
+ trackPageView: jest.Mock | string;
8
+ };
9
+
10
+ function makePlugin() {
11
+ return {
12
+ isEnabled: jest.fn(() => true),
13
+ init: jest.fn(),
14
+ trackEvent: jest.fn(),
15
+ trackPageView: jest.fn(),
16
+ };
17
+ }
18
+
19
+ describe('AvAnalytics', () => {
20
+ let mockAvAnalytics: AvAnalytics;
21
+
22
+ test('AvAnalytics should be defined', () => {
23
+ const plugins = [makePlugin()];
24
+ mockAvAnalytics = new AvAnalytics(plugins, Promise, true);
25
+ expect(mockAvAnalytics).toBeDefined();
26
+ mockAvAnalytics = new AvAnalytics(plugins, Promise);
27
+ expect(mockAvAnalytics).toBeDefined();
28
+ });
29
+
30
+ test('AvAnalytics should throw error without plugins or Promise', () => {
31
+ expect(() => {
32
+ // @ts-expect-error: allow error for testing
33
+ mockAvAnalytics = new AvAnalytics();
34
+ }).toThrow('[plugins] and [promise] must be defined');
35
+ });
36
+
37
+ test('AvAnalytics should cast plugins to an array', () => {
38
+ const plugin = makePlugin();
39
+ mockAvAnalytics = new AvAnalytics(plugin, Promise);
40
+ expect(mockAvAnalytics.plugins).toEqual([plugin]);
41
+ });
42
+
43
+ test('AvAnalytics should use custom configs', () => {
44
+ mockAvAnalytics = new AvAnalytics([], Promise, true, true, {
45
+ attributePrefix: 'some-attr',
46
+ recursive: true,
47
+ });
48
+ expect(mockAvAnalytics.attributePrefix).toBe('some-attr');
49
+ expect(mockAvAnalytics.recursive).toBe(true);
50
+ });
51
+
52
+ describe('setPageTracking', () => {
53
+ beforeEach(() => {
54
+ const plugins = [makePlugin()];
55
+ mockAvAnalytics = new AvAnalytics(plugins, Promise);
56
+ });
57
+
58
+ test('passing in argument should change value of pageTracking', () => {
59
+ const initialTracking = true;
60
+ const setPageTracking = !initialTracking;
61
+
62
+ mockAvAnalytics.pageTracking = initialTracking;
63
+
64
+ mockAvAnalytics.setPageTracking(setPageTracking);
65
+ expect(mockAvAnalytics.pageTracking).toBe(setPageTracking);
66
+ });
67
+
68
+ test('with functions defined, will only call if pageTracking is opposite isPageTracking', () => {
69
+ mockAvAnalytics.startPageTracking = jest.fn();
70
+ mockAvAnalytics.stopPageTracking = jest.fn();
71
+
72
+ let testValue = true;
73
+ mockAvAnalytics.pageTracking = testValue;
74
+ mockAvAnalytics.isPageTracking = testValue;
75
+ mockAvAnalytics.setPageTracking();
76
+ expect(mockAvAnalytics.startPageTracking).not.toHaveBeenCalled();
77
+ expect(mockAvAnalytics.stopPageTracking).not.toHaveBeenCalled();
78
+
79
+ testValue = false;
80
+ mockAvAnalytics.pageTracking = testValue;
81
+ mockAvAnalytics.isPageTracking = testValue;
82
+ mockAvAnalytics.setPageTracking();
83
+ expect(mockAvAnalytics.startPageTracking).not.toHaveBeenCalled();
84
+ expect(mockAvAnalytics.stopPageTracking).not.toHaveBeenCalled();
85
+ });
86
+
87
+ test('with functions defined, will start/stop tracking based on pageTracking value', () => {
88
+ mockAvAnalytics.startPageTracking = jest.fn();
89
+ mockAvAnalytics.stopPageTracking = jest.fn();
90
+
91
+ let testValue = true;
92
+ mockAvAnalytics.pageTracking = testValue;
93
+ mockAvAnalytics.isPageTracking = !testValue;
94
+
95
+ mockAvAnalytics.setPageTracking();
96
+ expect(mockAvAnalytics.startPageTracking).toHaveBeenCalledTimes(1);
97
+ expect(mockAvAnalytics.stopPageTracking).toHaveBeenCalledTimes(0);
98
+ expect(mockAvAnalytics.isPageTracking).toBe(testValue);
99
+
100
+ testValue = false;
101
+ mockAvAnalytics.pageTracking = testValue;
102
+ mockAvAnalytics.isPageTracking = !testValue;
103
+
104
+ mockAvAnalytics.setPageTracking();
105
+ expect(mockAvAnalytics.startPageTracking).toHaveBeenCalledTimes(1);
106
+ expect(mockAvAnalytics.stopPageTracking).toHaveBeenCalledTimes(1);
107
+ expect(mockAvAnalytics.isPageTracking).toBe(testValue);
108
+ });
109
+ });
110
+
111
+ describe('init', () => {
112
+ let plugins: [MockPlugin, MockPlugin];
113
+ beforeEach(() => {
114
+ plugins = [makePlugin(), makePlugin()];
115
+ mockAvAnalytics = new AvAnalytics(plugins, Promise);
116
+ mockAvAnalytics.setPageTracking = jest.fn();
117
+ });
118
+
119
+ test('should call setPageTracking', () => {
120
+ mockAvAnalytics.init();
121
+ expect(mockAvAnalytics.setPageTracking).toHaveBeenCalled();
122
+ });
123
+
124
+ test('should check each plugin is enabled', () => {
125
+ mockAvAnalytics.init();
126
+ for (const plugin of plugins) {
127
+ expect(plugin.isEnabled).toHaveBeenCalled();
128
+ }
129
+ });
130
+
131
+ test('should call init on enabled plugins', () => {
132
+ mockAvAnalytics.init();
133
+ for (const plugin of plugins) {
134
+ expect(plugin.init).toHaveBeenCalledTimes(1);
135
+ }
136
+
137
+ plugins[0].isEnabled.mockImplementationOnce(() => false);
138
+ mockAvAnalytics.init();
139
+ expect(plugins[0].init).toHaveBeenCalledTimes(1);
140
+ expect(plugins[1].init).toHaveBeenCalledTimes(2);
141
+ });
142
+
143
+ test('should skip plugins without init function', () => {
144
+ plugins[1].init = 'test';
145
+ mockAvAnalytics.init();
146
+ expect(plugins[0].init).toHaveBeenCalled();
147
+ });
148
+ });
149
+
150
+ describe('event tracking', () => {
151
+ let plugins: [MockPlugin, MockPlugin, MockPlugin];
152
+ beforeEach(() => {
153
+ plugins = [makePlugin(), makePlugin(), makePlugin()];
154
+
155
+ mockAvAnalytics = new AvAnalytics(plugins, Promise);
156
+
157
+ plugins[0].isEnabled.mockImplementation(() => false);
158
+ plugins[1].trackEvent = 'test';
159
+ plugins[2].trackPageView = 'test';
160
+ });
161
+
162
+ test('trackEvent should call trackEvent on enabled plugins with properties', async () => {
163
+ const mockProperties = {};
164
+ await mockAvAnalytics.trackEvent(mockProperties);
165
+
166
+ expect(plugins[0].trackEvent).not.toHaveBeenCalled();
167
+ expect(plugins[2].trackEvent).toHaveBeenCalledWith(mockProperties);
168
+ });
169
+
170
+ test('trackEvent should call trackEvent on enabled plugins with url', async () => {
171
+ const mockProperties: { url?: string } = {};
172
+ await mockAvAnalytics.trackEvent(mockProperties);
173
+
174
+ expect(plugins[0].trackEvent).not.toHaveBeenCalled();
175
+ expect(mockProperties.url).toBeDefined();
176
+ expect(plugins[2].trackEvent).toHaveBeenCalledWith(mockProperties);
177
+ });
178
+
179
+ test('trackPageView should call trackPageView on enabled plugins with properties', async () => {
180
+ const mockUrl = 'testProperties';
181
+ await mockAvAnalytics.trackPageView(mockUrl);
182
+
183
+ expect(plugins[0].trackPageView).not.toHaveBeenCalled();
184
+ expect(plugins[1].trackPageView).toHaveBeenCalledWith(mockUrl);
185
+ });
186
+ });
187
+ });
@@ -0,0 +1,31 @@
1
+ import { avLogMessagesApiV2 } from '@availity/api-axios';
2
+ import { AvDmaAnalytics } from '..';
3
+
4
+ jest.mock('@availity/api-axios');
5
+
6
+ describe('AvSplunkAnalytics', () => {
7
+ let mockAvSplunkAnalytics: AvDmaAnalytics;
8
+
9
+ beforeEach(() => {
10
+ avLogMessagesApiV2.sendBeacon = jest.fn();
11
+ // avLogMessagesApiV2.info = jest.fn;
12
+ mockAvSplunkAnalytics = new AvDmaAnalytics(avLogMessagesApiV2);
13
+ });
14
+
15
+ test('AvSplunkAnalytics should be defined', () => {
16
+ expect(mockAvSplunkAnalytics).toBeDefined();
17
+ });
18
+
19
+ test('trackEvent should call AvLogMessages.send', () => {
20
+ const level = 'info';
21
+ mockAvSplunkAnalytics.trackEvent({ level, label: 'test' });
22
+ expect(avLogMessagesApiV2.info).toHaveBeenCalledTimes(1);
23
+ });
24
+
25
+ test('trackEvent should not allow unknown keys', () => {
26
+ const level = 'info';
27
+ expect(() => {
28
+ mockAvSplunkAnalytics.trackEvent({ level, test: 'test' });
29
+ }).toThrow();
30
+ });
31
+ });
@@ -0,0 +1,23 @@
1
+ import { AvAnalyticsPlugin } from '..';
2
+
3
+ describe('AvAnalyticsPlugin', () => {
4
+ let mockPlugin: AvAnalyticsPlugin;
5
+
6
+ beforeEach(() => {
7
+ mockPlugin = new AvAnalyticsPlugin();
8
+ });
9
+
10
+ test('AvAnalyticsPlugin should be defined', () => {
11
+ expect(mockPlugin).toBeDefined();
12
+ });
13
+
14
+ test('should default enabled to true', () => {
15
+ expect(mockPlugin.isEnabled()).toBe(true);
16
+ mockPlugin = new AvAnalyticsPlugin(false);
17
+ expect(mockPlugin.isEnabled()).toBe(false);
18
+ });
19
+
20
+ test('isEnabled should return enabled value', () => {
21
+ expect(mockPlugin.isEnabled()).toBe(mockPlugin.isEnabled());
22
+ });
23
+ });
@@ -0,0 +1,68 @@
1
+ import { AvSplunkAnalytics } from '..';
2
+
3
+ describe('AvSplunkAnalytics', () => {
4
+ let mockLog: { info: () => void; test: () => void };
5
+ let mockAvSplunkAnalytics: AvSplunkAnalytics;
6
+
7
+ beforeEach(() => {
8
+ mockLog = {
9
+ info: jest.fn(),
10
+ test: jest.fn(),
11
+ };
12
+
13
+ mockAvSplunkAnalytics = new AvSplunkAnalytics(mockLog);
14
+ });
15
+
16
+ test('AvSplunkAnalytics should be defined', () => {
17
+ expect(mockAvSplunkAnalytics).toBeDefined();
18
+ });
19
+
20
+ test('trackEvent should call AvLogMessages[level]', () => {
21
+ let level = 'info';
22
+ mockAvSplunkAnalytics.trackEvent({ level });
23
+ expect(mockLog.info).toHaveBeenCalledTimes(1);
24
+ expect(mockLog.test).toHaveBeenCalledTimes(0);
25
+ level = 'test';
26
+ mockAvSplunkAnalytics.trackEvent({ level });
27
+ expect(mockLog.info).toHaveBeenCalledTimes(1);
28
+ expect(mockLog.test).toHaveBeenCalledTimes(1);
29
+ });
30
+
31
+ test("trackEvent should default level to 'info'", () => {
32
+ mockAvSplunkAnalytics.trackEvent({});
33
+ expect(mockLog.info).toHaveBeenCalledTimes(1);
34
+ expect(mockLog.test).toHaveBeenCalledTimes(0);
35
+ });
36
+
37
+ test("trackEvent should default properties.url to location.href or 'N/A'", () => {
38
+ let startingObject: { message: string; url: string; level?: string } = {
39
+ message: 'hello world',
40
+ url: window.location.href || 'N/A',
41
+ };
42
+ const expectedCall = {
43
+ ...startingObject,
44
+ url: window.location.href || 'N/A',
45
+ level: 'info',
46
+ };
47
+ mockAvSplunkAnalytics.trackEvent(startingObject);
48
+ expect(mockLog.info).toHaveBeenCalledWith(expectedCall);
49
+
50
+ startingObject = {
51
+ message: 'hello world',
52
+ url: 'testUrl',
53
+ level: 'test',
54
+ };
55
+ mockAvSplunkAnalytics.trackEvent(startingObject);
56
+ expect(mockLog.test).toHaveBeenCalledWith(startingObject);
57
+ });
58
+
59
+ test("trackPageView should call trackEvent with event 'page' and passed in url", () => {
60
+ const testUrl = 'testUrl';
61
+ mockAvSplunkAnalytics.trackEvent = jest.fn();
62
+ mockAvSplunkAnalytics.trackPageView(testUrl);
63
+ expect(mockAvSplunkAnalytics.trackEvent).toHaveBeenCalledWith({
64
+ event: 'page',
65
+ url: testUrl,
66
+ });
67
+ });
68
+ });
@@ -0,0 +1,52 @@
1
+ import {
2
+ camelCase,
3
+ getComposedPath,
4
+ isLeftClickEvent,
5
+ isModifiedEvent,
6
+ isPluginEnabled,
7
+ isValidEventTypeOnTarget,
8
+ } from '../util';
9
+
10
+ describe('analytics-core utils', () => {
11
+ test('isLeftClickEvent', () => {
12
+ expect(isLeftClickEvent({ button: 0 })).toBeTruthy();
13
+ expect(isLeftClickEvent({ button: 1 })).toBeFalsy();
14
+ });
15
+
16
+ test('isModifiedEvent', () => {
17
+ expect(isModifiedEvent({ metaKey: 1 })).toBeTruthy();
18
+ expect(isModifiedEvent({ altKey: 1 })).toBeTruthy();
19
+ expect(isModifiedEvent({ ctrlKey: 1 })).toBeTruthy();
20
+ expect(isModifiedEvent({ shiftKey: 1 })).toBeTruthy();
21
+ expect(isModifiedEvent({ key: 1 })).toBeFalsy();
22
+ });
23
+
24
+ test('isValidEventTypeOnTarget', () => {
25
+ expect(isValidEventTypeOnTarget({ target: { nodeName: 'click' }, type: 'click' })).toBeTruthy();
26
+ expect(isValidEventTypeOnTarget({ target: { nodeName: 'select' }, type: 'click' })).toBeFalsy();
27
+ });
28
+
29
+ test('isPluginEnabled', () => {
30
+ expect(isPluginEnabled({ isEnabled: () => true })).toBeTruthy();
31
+ expect(isPluginEnabled({ isEnabled: () => false })).toBeFalsy();
32
+ expect(isPluginEnabled({ isEnabled: true })).toBeTruthy();
33
+ expect(isPluginEnabled({ isEnabled: false })).toBeFalsy();
34
+ });
35
+
36
+ test('camelCase', () => {
37
+ expect(camelCase('foo')).toBe('foo');
38
+ expect(camelCase('foo-bar')).toBe('fooBar');
39
+ expect(camelCase('foo-bar-baz')).toBe('fooBarBaz');
40
+ });
41
+
42
+ test('getComposedPath', () => {
43
+ expect(getComposedPath({}).length).toBe(1);
44
+ expect(getComposedPath({ parentNode: {} }).length).toBe(2);
45
+ expect(getComposedPath({ host: {} }).length).toBe(2);
46
+ expect(getComposedPath({ defaultView: {} }).length).toBe(2);
47
+
48
+ const result = '[{"parentNode":{"host":{"defaultView":{}}}},{"host":{"defaultView":{}}},{"defaultView":{}},{}]';
49
+ const nested = getComposedPath({ parentNode: { host: { defaultView: {} } } });
50
+ expect(JSON.stringify(nested)).toEqual(result);
51
+ });
52
+ });