@newrelic/browser-agent 1.234.0 → 1.235.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.
Files changed (196) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/common/constants/env.cdn.js +1 -1
  3. package/dist/cjs/common/constants/env.npm.js +1 -1
  4. package/dist/cjs/common/constants/shared-channel.js +19 -0
  5. package/dist/cjs/common/harvest/harvest-scheduler.js +21 -5
  6. package/dist/cjs/common/session/{session-entity.test.js → session-entity.component-test.js} +79 -42
  7. package/dist/cjs/common/session/session-entity.js +19 -11
  8. package/dist/cjs/common/timer/interaction-timer.js +1 -1
  9. package/dist/cjs/common/url/canonicalize-url.test.js +26 -30
  10. package/dist/cjs/common/util/data-size.test.js +37 -20
  11. package/dist/cjs/common/util/feature-flags.js +23 -12
  12. package/dist/cjs/common/util/feature-flags.test.js +84 -0
  13. package/dist/cjs/common/util/global-scope.js +1 -32
  14. package/dist/cjs/common/util/global-scope.test.js +72 -0
  15. package/dist/cjs/common/util/obfuscate.component-test.js +129 -0
  16. package/dist/cjs/common/util/obfuscate.js +2 -2
  17. package/dist/cjs/common/util/submit-data.js +3 -3
  18. package/dist/cjs/common/util/submit-data.test.js +145 -121
  19. package/dist/cjs/common/wrap/wrap-raf.js +1 -1
  20. package/dist/cjs/common/wrap/wrap-timer.js +1 -1
  21. package/dist/cjs/features/jserrors/aggregate/index.js +4 -0
  22. package/dist/cjs/features/jserrors/instrument/index.js +2 -15
  23. package/dist/cjs/features/session_replay/aggregate/index.component-test.js +457 -0
  24. package/dist/cjs/features/session_replay/aggregate/index.js +99 -82
  25. package/dist/cjs/features/session_replay/replay-mode.js +28 -0
  26. package/dist/cjs/features/session_trace/aggregate/index.js +222 -99
  27. package/dist/cjs/features/session_trace/constants.js +1 -3
  28. package/dist/cjs/features/session_trace/instrument/index.js +0 -16
  29. package/dist/cjs/features/spa/constants.js +0 -1
  30. package/dist/cjs/features/utils/agent-session.js +20 -36
  31. package/dist/cjs/features/utils/agent-session.test.js +211 -0
  32. package/dist/cjs/features/utils/aggregate-base.js +7 -12
  33. package/dist/cjs/features/utils/aggregate-base.test.js +110 -0
  34. package/dist/cjs/features/utils/feature-base.test.js +42 -0
  35. package/dist/cjs/features/utils/handler-cache.js +28 -23
  36. package/dist/cjs/features/utils/handler-cache.test.js +53 -0
  37. package/dist/cjs/features/utils/instrument-base.js +58 -39
  38. package/dist/cjs/features/utils/instrument-base.test.js +179 -0
  39. package/dist/cjs/features/utils/lazy-feature-loader.test.js +30 -0
  40. package/dist/cjs/loaders/agent.js +0 -1
  41. package/dist/cjs/loaders/api/api.js +1 -1
  42. package/dist/cjs/loaders/features/featureDependencies.js +2 -0
  43. package/dist/esm/common/constants/env.cdn.js +1 -1
  44. package/dist/esm/common/constants/env.npm.js +1 -1
  45. package/dist/esm/common/constants/shared-channel.js +12 -0
  46. package/dist/esm/common/harvest/harvest-scheduler.js +21 -5
  47. package/dist/esm/common/session/{session-entity.test.js → session-entity.component-test.js} +77 -40
  48. package/dist/esm/common/session/session-entity.js +17 -11
  49. package/dist/esm/common/timer/interaction-timer.js +1 -1
  50. package/dist/esm/common/url/canonicalize-url.test.js +25 -29
  51. package/dist/esm/common/util/data-size.test.js +35 -20
  52. package/dist/esm/common/util/feature-flags.js +23 -12
  53. package/dist/esm/common/util/feature-flags.test.js +80 -0
  54. package/dist/esm/common/util/global-scope.js +1 -29
  55. package/dist/esm/common/util/global-scope.test.js +70 -0
  56. package/dist/esm/common/util/obfuscate.component-test.js +125 -0
  57. package/dist/esm/common/util/obfuscate.js +2 -2
  58. package/dist/esm/common/util/submit-data.js +3 -3
  59. package/dist/esm/common/util/submit-data.test.js +143 -121
  60. package/dist/esm/common/wrap/wrap-raf.js +1 -1
  61. package/dist/esm/common/wrap/wrap-timer.js +1 -1
  62. package/dist/esm/features/jserrors/aggregate/index.js +4 -0
  63. package/dist/esm/features/jserrors/instrument/index.js +2 -15
  64. package/dist/esm/features/session_replay/aggregate/index.component-test.js +453 -0
  65. package/dist/esm/features/session_replay/aggregate/index.js +92 -78
  66. package/dist/esm/features/session_replay/replay-mode.js +23 -0
  67. package/dist/esm/features/session_trace/aggregate/index.js +223 -100
  68. package/dist/esm/features/session_trace/constants.js +0 -1
  69. package/dist/esm/features/session_trace/instrument/index.js +1 -17
  70. package/dist/esm/features/spa/constants.js +0 -1
  71. package/dist/esm/features/utils/agent-session.js +21 -37
  72. package/dist/esm/features/utils/agent-session.test.js +207 -0
  73. package/dist/esm/features/utils/aggregate-base.js +7 -12
  74. package/dist/esm/features/utils/aggregate-base.test.js +108 -0
  75. package/dist/esm/features/utils/feature-base.test.js +40 -0
  76. package/dist/esm/features/utils/handler-cache.js +28 -23
  77. package/dist/esm/features/utils/handler-cache.test.js +51 -0
  78. package/dist/esm/features/utils/instrument-base.js +58 -39
  79. package/dist/esm/features/utils/instrument-base.test.js +175 -0
  80. package/dist/esm/features/utils/lazy-feature-loader.test.js +29 -0
  81. package/dist/esm/loaders/agent.js +0 -1
  82. package/dist/esm/loaders/api/api.js +2 -2
  83. package/dist/esm/loaders/features/featureDependencies.js +2 -0
  84. package/dist/types/common/constants/shared-channel.d.ts +5 -0
  85. package/dist/types/common/constants/shared-channel.d.ts.map +1 -0
  86. package/dist/types/common/harvest/harvest-scheduler.component-test.d.ts +2 -0
  87. package/dist/types/common/harvest/harvest-scheduler.component-test.d.ts.map +1 -0
  88. package/dist/types/common/harvest/harvest-scheduler.d.ts +4 -0
  89. package/dist/types/common/harvest/harvest-scheduler.d.ts.map +1 -1
  90. package/dist/types/common/harvest/harvest.component-test.d.ts +2 -0
  91. package/dist/types/common/harvest/harvest.component-test.d.ts.map +1 -0
  92. package/dist/types/common/session/session-entity.component-test.d.ts +2 -0
  93. package/dist/types/common/session/session-entity.component-test.d.ts.map +1 -0
  94. package/dist/types/common/session/session-entity.d.ts +9 -5
  95. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  96. package/dist/types/common/timer/interaction-timer.component-test.d.ts +2 -0
  97. package/dist/types/common/timer/interaction-timer.component-test.d.ts.map +1 -0
  98. package/dist/types/common/url/encode.component-test.d.ts +2 -0
  99. package/dist/types/common/url/encode.component-test.d.ts.map +1 -0
  100. package/dist/types/common/url/protocol.component-test.d.ts +2 -0
  101. package/dist/types/common/url/protocol.component-test.d.ts.map +1 -0
  102. package/dist/types/common/util/feature-flags.d.ts +1 -0
  103. package/dist/types/common/util/feature-flags.d.ts.map +1 -1
  104. package/dist/types/common/util/global-scope.d.ts +0 -9
  105. package/dist/types/common/util/global-scope.d.ts.map +1 -1
  106. package/dist/types/common/util/obfuscate.component-test.d.ts +2 -0
  107. package/dist/types/common/util/obfuscate.component-test.d.ts.map +1 -0
  108. package/dist/types/features/jserrors/aggregate/index.d.ts +1 -0
  109. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  110. package/dist/types/features/session_replay/aggregate/index.component-test.d.ts +2 -0
  111. package/dist/types/features/session_replay/aggregate/index.component-test.d.ts.map +1 -0
  112. package/dist/types/features/session_replay/aggregate/index.d.ts +14 -5
  113. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  114. package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
  115. package/dist/types/features/session_replay/replay-mode.d.ts +9 -0
  116. package/dist/types/features/session_replay/replay-mode.d.ts.map +1 -0
  117. package/dist/types/features/session_trace/aggregate/index.d.ts +21 -3
  118. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  119. package/dist/types/features/session_trace/constants.d.ts +0 -1
  120. package/dist/types/features/session_trace/constants.d.ts.map +1 -1
  121. package/dist/types/features/session_trace/instrument/index.d.ts +0 -2
  122. package/dist/types/features/session_trace/instrument/index.d.ts.map +1 -1
  123. package/dist/types/features/spa/constants.d.ts.map +1 -1
  124. package/dist/types/features/utils/agent-session.d.ts.map +1 -1
  125. package/dist/types/features/utils/aggregate-base.d.ts +6 -1
  126. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  127. package/dist/types/features/utils/handler-cache.d.ts +12 -11
  128. package/dist/types/features/utils/handler-cache.d.ts.map +1 -1
  129. package/dist/types/features/utils/instrument-base.d.ts +17 -1
  130. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  131. package/dist/types/loaders/agent.d.ts.map +1 -1
  132. package/dist/types/loaders/features/featureDependencies.d.ts.map +1 -1
  133. package/package.json +9 -7
  134. package/src/common/constants/shared-channel.js +13 -0
  135. package/src/common/harvest/harvest-scheduler.js +17 -6
  136. package/src/common/session/{session-entity.test.js → session-entity.component-test.js} +70 -47
  137. package/src/common/session/session-entity.js +15 -12
  138. package/src/common/timer/interaction-timer.js +1 -1
  139. package/src/common/url/canonicalize-url.test.js +32 -21
  140. package/src/common/util/data-size.test.js +27 -20
  141. package/src/common/util/feature-flags.js +24 -12
  142. package/src/common/util/feature-flags.test.js +98 -0
  143. package/src/common/util/global-scope.js +0 -26
  144. package/src/common/util/global-scope.test.js +87 -0
  145. package/src/common/util/obfuscate.component-test.js +173 -0
  146. package/src/common/util/obfuscate.js +2 -2
  147. package/src/common/util/submit-data.js +3 -3
  148. package/src/common/util/submit-data.test.js +123 -115
  149. package/src/common/wrap/wrap-raf.js +1 -1
  150. package/src/common/wrap/wrap-timer.js +1 -1
  151. package/src/features/jserrors/aggregate/index.js +5 -0
  152. package/src/features/jserrors/instrument/index.js +2 -15
  153. package/src/features/session_replay/aggregate/index.component-test.js +368 -0
  154. package/src/features/session_replay/aggregate/index.js +96 -71
  155. package/src/features/session_replay/instrument/index.js +0 -1
  156. package/src/features/session_replay/replay-mode.js +23 -0
  157. package/src/features/session_trace/aggregate/index.js +198 -79
  158. package/src/features/session_trace/constants.js +0 -1
  159. package/src/features/session_trace/instrument/index.js +2 -19
  160. package/src/features/spa/constants.js +0 -1
  161. package/src/features/utils/agent-session.js +22 -34
  162. package/src/features/utils/agent-session.test.js +194 -0
  163. package/src/features/utils/aggregate-base.js +12 -9
  164. package/src/features/utils/aggregate-base.test.js +122 -0
  165. package/src/features/utils/feature-base.test.js +45 -0
  166. package/src/features/utils/handler-cache.js +29 -23
  167. package/src/features/utils/handler-cache.test.js +72 -0
  168. package/src/features/utils/instrument-base.js +45 -29
  169. package/src/features/utils/instrument-base.test.js +190 -0
  170. package/src/features/utils/lazy-feature-loader.test.js +37 -0
  171. package/src/loaders/agent.js +0 -1
  172. package/src/loaders/api/api.js +2 -2
  173. package/src/loaders/features/featureDependencies.js +2 -0
  174. package/dist/cjs/common/storage/local-memory.js +0 -35
  175. package/dist/cjs/common/storage/local-memory.test.js +0 -20
  176. package/dist/esm/common/storage/local-memory.js +0 -28
  177. package/dist/esm/common/storage/local-memory.test.js +0 -18
  178. package/dist/types/common/storage/local-memory.d.ts +0 -8
  179. package/dist/types/common/storage/local-memory.d.ts.map +0 -1
  180. package/src/common/storage/local-memory.js +0 -30
  181. package/src/common/storage/local-memory.test.js +0 -19
  182. /package/dist/cjs/common/harvest/{harvest-scheduler.test.js → harvest-scheduler.component-test.js} +0 -0
  183. /package/dist/cjs/common/harvest/{harvest.test.js → harvest.component-test.js} +0 -0
  184. /package/dist/cjs/common/timer/{interaction-timer.test.js → interaction-timer.component-test.js} +0 -0
  185. /package/dist/cjs/common/url/{encode.test.js → encode.component-test.js} +0 -0
  186. /package/dist/cjs/common/url/{protocol.test.js → protocol.component-test.js} +0 -0
  187. /package/dist/esm/common/harvest/{harvest-scheduler.test.js → harvest-scheduler.component-test.js} +0 -0
  188. /package/dist/esm/common/harvest/{harvest.test.js → harvest.component-test.js} +0 -0
  189. /package/dist/esm/common/timer/{interaction-timer.test.js → interaction-timer.component-test.js} +0 -0
  190. /package/dist/esm/common/url/{encode.test.js → encode.component-test.js} +0 -0
  191. /package/dist/esm/common/url/{protocol.test.js → protocol.component-test.js} +0 -0
  192. /package/src/common/harvest/{harvest-scheduler.test.js → harvest-scheduler.component-test.js} +0 -0
  193. /package/src/common/harvest/{harvest.test.js → harvest.component-test.js} +0 -0
  194. /package/src/common/timer/{interaction-timer.test.js → interaction-timer.component-test.js} +0 -0
  195. /package/src/common/url/{encode.test.js → encode.component-test.js} +0 -0
  196. /package/src/common/url/{protocol.test.js → protocol.component-test.js} +0 -0
@@ -0,0 +1,80 @@
1
+ import { faker } from '@faker-js/faker';
2
+ import * as eventEmitterModule from '../event-emitter/contextual-ee';
3
+ import * as handleModule from '../event-emitter/handle';
4
+ import * as drainModule from '../drain/drain';
5
+ import { activateFeatures, activatedFeatures } from './feature-flags';
6
+ import { FEATURE_NAMES } from '../../loaders/features/features';
7
+ jest.mock('../event-emitter/handle');
8
+ jest.mock('../drain/drain');
9
+ jest.mock('../event-emitter/contextual-ee', () => ({
10
+ __esModule: true,
11
+ ee: {
12
+ get: jest.fn(() => ({
13
+ foo: "bar_".concat(Math.random())
14
+ }))
15
+ }
16
+ }));
17
+ let agentIdentifier;
18
+ beforeEach(() => {
19
+ agentIdentifier = faker.datatype.uuid();
20
+ });
21
+ afterEach(() => {
22
+ Object.keys(activatedFeatures).forEach(key => delete activatedFeatures[key]);
23
+ });
24
+ test.each([null, undefined])('should not do anything when flags is %s', input => {
25
+ activateFeatures(input, agentIdentifier);
26
+ expect(handleModule.handle).not.toHaveBeenCalled();
27
+ expect(drainModule.drain).not.toHaveBeenCalled();
28
+ expect(activatedFeatures).toEqual({});
29
+ });
30
+ const bucketMap = {
31
+ stn: [FEATURE_NAMES.sessionTrace],
32
+ err: [FEATURE_NAMES.jserrors, FEATURE_NAMES.metrics],
33
+ ins: [FEATURE_NAMES.pageAction],
34
+ spa: [FEATURE_NAMES.spa],
35
+ sr: [FEATURE_NAMES.sessionReplay, FEATURE_NAMES.sessionTrace]
36
+ };
37
+ test('emits the right events when feature flag = 1', () => {
38
+ const flags = {};
39
+ Object.keys(bucketMap).forEach(flag => flags[flag] = 1);
40
+ activateFeatures(flags, agentIdentifier);
41
+ const sharedEE = jest.mocked(eventEmitterModule.ee.get).mock.results[0].value;
42
+
43
+ // each flag gets emitted to each of its mapped features, and a feat- AND a rumresp- for every emit, so (1+2+1+1+2)*2 = 14
44
+ expect(handleModule.handle).toHaveBeenCalledTimes(14);
45
+ expect(handleModule.handle).toHaveBeenNthCalledWith(1, 'feat-stn', [], undefined, FEATURE_NAMES.sessionTrace, sharedEE);
46
+ expect(handleModule.handle).toHaveBeenLastCalledWith('rumresp-sr', [true], undefined, FEATURE_NAMES.sessionTrace, sharedEE);
47
+ expect(drainModule.drain).toHaveBeenCalledWith(agentIdentifier, 'page_view_event');
48
+ Object.keys(flags).forEach(flag => flags[flag] = true);
49
+ expect(activatedFeatures).toEqual(flags);
50
+ });
51
+ test('emits the right events when feature flag = 0', () => {
52
+ const flags = {};
53
+ Object.keys(bucketMap).forEach(flag => flags[flag] = 0);
54
+ activateFeatures(flags, agentIdentifier);
55
+ const sharedEE = jest.mocked(eventEmitterModule.ee.get).mock.results[0].value;
56
+
57
+ // each flag gets emitted to each of its mapped features, and a block- AND a rumresp- for every emit, so (1+2+1+1+2)*2 = 14
58
+ expect(handleModule.handle).toHaveBeenCalledTimes(14);
59
+ expect(handleModule.handle).toHaveBeenNthCalledWith(1, 'block-stn', [], undefined, FEATURE_NAMES.sessionTrace, sharedEE);
60
+ expect(handleModule.handle).toHaveBeenLastCalledWith('rumresp-sr', [false], undefined, FEATURE_NAMES.sessionTrace, sharedEE);
61
+ expect(drainModule.drain).toHaveBeenCalledWith(agentIdentifier, 'page_view_event');
62
+ Object.keys(flags).forEach(flag => flags[flag] = false);
63
+ expect(activatedFeatures).toEqual(flags);
64
+ });
65
+ test('only the first activate of the same feature is respected', () => {
66
+ const flags = {
67
+ stn: 1
68
+ };
69
+ activateFeatures(flags, agentIdentifier);
70
+ flags.stn = 0;
71
+ activateFeatures(flags, agentIdentifier);
72
+ const sharedEE1 = jest.mocked(eventEmitterModule.ee.get).mock.results[0].value;
73
+ const sharedEE2 = jest.mocked(eventEmitterModule.ee.get).mock.results[1].value;
74
+ expect(handleModule.handle).toHaveBeenNthCalledWith(1, 'feat-stn', [], undefined, 'session_trace', sharedEE1);
75
+ expect(handleModule.handle).toHaveBeenNthCalledWith(2, 'rumresp-stn', [true], undefined, 'session_trace', sharedEE1);
76
+ expect(handleModule.handle).not.toHaveBeenNthCalledWith(1, 'feat-stn', [], undefined, 'session_trace', sharedEE2);
77
+ expect(drainModule.drain).toHaveBeenCalledWith(agentIdentifier, 'page_view_event');
78
+ expect(drainModule.drain).toHaveBeenCalledTimes(2);
79
+ expect(activatedFeatures.stn).toBeTruthy();
80
+ });
@@ -14,32 +14,4 @@ export let globalScope = (() => {
14
14
  }
15
15
  throw new Error('New Relic browser agent shutting down due to error: Unable to locate global scope. This is possibly due to code redefining browser global variables like "self" and "window".');
16
16
  })();
17
- export const initialLocation = '' + globalScope.location;
18
-
19
- /**
20
- * The below methods are only used for testing and should be removed once the
21
- * reliant tests are moved to Jest.
22
- * tests/browser/protocol.browser.js
23
- * tests/browser/obfuscate.browser.js
24
- */
25
- export function setScope(obj) {
26
- globalScope = {
27
- ...obj
28
- };
29
- }
30
- export function resetScope() {
31
- if (isBrowserScope) {
32
- globalScope = window;
33
- } else if (isWorkerScope) {
34
- if (typeof globalThis !== 'undefined' && globalThis instanceof WorkerGlobalScope) {
35
- globalScope = globalThis;
36
- } else if (self instanceof WorkerGlobalScope) {
37
- globalScope = self;
38
- }
39
- } else {
40
- throw new Error('New Relic browser agent shutting down due to error: Unable to locate global scope. This is possibly due to code redefining browser global variables like "self" and "window".');
41
- }
42
- }
43
- export function getGlobalScope() {
44
- return globalScope;
45
- }
17
+ export const initialLocation = '' + globalScope.location;
@@ -0,0 +1,70 @@
1
+ /*
2
+ The global-scope module contains exports that are defined once at the time
3
+ of importing the module. For this reason, the module must be dynamically
4
+ imported in each test case.
5
+
6
+ A scope must always exist or importing the module will throw an error. Use
7
+ `enableWorkerScope` to enable the worker scope. Be sure to call `disableWorkerScope`
8
+ before any calls to `expect` or the test will fail with an error from Jest.
9
+ */
10
+
11
+ import { faker } from '@faker-js/faker';
12
+ afterEach(() => {
13
+ jest.restoreAllMocks();
14
+ jest.resetModules();
15
+ });
16
+ test('should indicate a browser scope', async () => {
17
+ jest.spyOn(global, 'window', 'get').mockReturnValue({
18
+ document: {}
19
+ });
20
+ const globalScopeModule = await import('./global-scope');
21
+ expect(globalScopeModule.isBrowserScope).toBe(true);
22
+ expect(globalScopeModule.isWorkerScope).toBe(false);
23
+ expect(globalScopeModule.globalScope).toBe(global.window);
24
+ });
25
+ test('should indicate a worker scope', async () => {
26
+ enableWorkerScope();
27
+ const globalScopeModule = await import('./global-scope');
28
+ const mockedGlobalThis = global.globalThis;
29
+ disableWorkerScope();
30
+ expect(globalScopeModule.isBrowserScope).toBe(false);
31
+ expect(globalScopeModule.isWorkerScope).toBe(true);
32
+ expect(globalScopeModule.globalScope).toBe(mockedGlobalThis);
33
+ });
34
+ test('should return the self global', async () => {
35
+ enableWorkerScope();
36
+ jest.replaceProperty(global, 'globalThis', null);
37
+ jest.spyOn(global, 'self', 'get').mockReturnValue(new global.WorkerGlobalScope());
38
+ const globalScopeModule = await import('./global-scope');
39
+ const mockedGlobalSelf = global.self;
40
+ disableWorkerScope();
41
+ expect(globalScopeModule.isBrowserScope).toBe(false);
42
+ expect(globalScopeModule.isWorkerScope).toBe(true);
43
+ expect(globalScopeModule.globalScope).toBe(mockedGlobalSelf);
44
+ });
45
+ test('should throw an error when a scope cannot be defined', async () => {
46
+ jest.spyOn(global, 'window', 'get').mockReturnValue(undefined);
47
+ await expect(() => import('./global-scope')).rejects.toThrow();
48
+ });
49
+ test('should immediately store the current location', async () => {
50
+ const url = faker.internet.url();
51
+ jest.spyOn(window, 'location', 'get').mockReturnValue(url);
52
+ const globalScopeModule = await import('./global-scope');
53
+ expect(globalScopeModule.initialLocation).toBe(url);
54
+ });
55
+ function enableWorkerScope() {
56
+ jest.spyOn(global, 'window', 'get').mockReturnValue(undefined);
57
+ class WorkerNavigator {}
58
+ class WorkerGlobalScope {
59
+ navigator = new WorkerNavigator();
60
+ }
61
+ global.WorkerGlobalScope = WorkerGlobalScope;
62
+ global.WorkerNavigator = WorkerNavigator;
63
+ jest.spyOn(global, 'navigator', 'get').mockReturnValue(new global.WorkerNavigator());
64
+ jest.replaceProperty(global, 'globalThis', new WorkerGlobalScope());
65
+ }
66
+ function disableWorkerScope() {
67
+ delete global.WorkerGlobalScope;
68
+ delete global.WorkerNavigator;
69
+ jest.restoreAllMocks();
70
+ }
@@ -0,0 +1,125 @@
1
+ import { faker } from '@faker-js/faker';
2
+ import * as configModule from '../config/config';
3
+ import * as urlProtocolModule from '../url/protocol';
4
+ import * as consolModule from './console';
5
+ import * as obfuscateModule from './obfuscate';
6
+ jest.mock('../config/config');
7
+ jest.mock('../context/shared-context');
8
+ jest.mock('../url/protocol');
9
+ jest.mock('./console');
10
+ let agentIdentifier;
11
+ const rules = [{
12
+ regex: /pii/g,
13
+ replacement: 'OBFUSCATED'
14
+ }];
15
+ beforeEach(() => {
16
+ agentIdentifier = faker.datatype.uuid();
17
+ });
18
+ afterEach(() => {
19
+ jest.resetAllMocks();
20
+ });
21
+ describe('Obfuscator', () => {
22
+ test('shouldObfuscate returns true when there are rules', () => {
23
+ jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue(rules);
24
+ const obfuscator = new obfuscateModule.Obfuscator();
25
+ obfuscator.sharedContext = {
26
+ agentIdentifier
27
+ };
28
+ expect(obfuscator.shouldObfuscate()).toEqual(true);
29
+ });
30
+ test('shouldObfuscate returns false when there are no rules', () => {
31
+ jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue([]);
32
+ const obfuscator = new obfuscateModule.Obfuscator();
33
+ obfuscator.sharedContext = {
34
+ agentIdentifier
35
+ };
36
+ expect(obfuscator.shouldObfuscate()).toEqual(false);
37
+ });
38
+ test('obfuscateString returns the input when there are no rules', () => {
39
+ jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue([]);
40
+ const input = faker.lorem.sentence();
41
+ const obfuscator = new obfuscateModule.Obfuscator();
42
+ obfuscator.sharedContext = {
43
+ agentIdentifier
44
+ };
45
+ expect(obfuscator.obfuscateString(input)).toEqual(input);
46
+ });
47
+ test('obfuscateString applies obfuscation rules to input', () => {
48
+ jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue(rules);
49
+ const input = 'pii';
50
+ const obfuscator = new obfuscateModule.Obfuscator();
51
+ obfuscator.sharedContext = {
52
+ agentIdentifier
53
+ };
54
+ expect(obfuscator.obfuscateString(input)).toEqual(rules[0].replacement);
55
+ });
56
+ test('obfuscateString replaces input with * when replacement is not set', () => {
57
+ const newRules = [{
58
+ regex: rules[0].regex
59
+ }];
60
+ jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue(newRules);
61
+ const input = 'pii';
62
+ const obfuscator = new obfuscateModule.Obfuscator();
63
+ obfuscator.sharedContext = {
64
+ agentIdentifier
65
+ };
66
+ expect(obfuscator.obfuscateString(input)).toEqual('*');
67
+ });
68
+ test.each([null, undefined, '', 123])('obfuscateString returns the input it is %s', input => {
69
+ jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue(rules);
70
+ const obfuscator = new obfuscateModule.Obfuscator();
71
+ obfuscator.sharedContext = {
72
+ agentIdentifier
73
+ };
74
+ expect(obfuscator.obfuscateString(input)).toEqual(input);
75
+ });
76
+ });
77
+ describe('getRules', () => {
78
+ test('should return configured rules', () => {
79
+ jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue(rules);
80
+ expect(obfuscateModule.getRules()).toEqual(rules);
81
+ });
82
+ test('should include the file protocol obfuscation', () => {
83
+ jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue(rules);
84
+ jest.spyOn(urlProtocolModule, 'isFileProtocol').mockReturnValue(rules);
85
+ expect(obfuscateModule.getRules()).toEqual(expect.arrayContaining([{
86
+ regex: /^file:\/\/(.*)/,
87
+ replacement: 'file://OBFUSCATED'
88
+ }]));
89
+ });
90
+ test.each([null, undefined])('should return an empty array when obfuscation rules are %s', input => {
91
+ jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue(input);
92
+ expect(obfuscateModule.getRules()).toEqual([]);
93
+ });
94
+ });
95
+ describe('validateRules', () => {
96
+ test('should return true for empty array', () => {
97
+ expect(obfuscateModule.validateRules([])).toEqual(true);
98
+ });
99
+ test('should return true for valid rules', () => {
100
+ expect(obfuscateModule.validateRules(rules)).toEqual(true);
101
+ });
102
+ test.each([null, 123, {}, []])('should warn about an invalid regex type %s', input => {
103
+ const newRules = [{
104
+ regex: input,
105
+ replacement: rules[0].replacement
106
+ }];
107
+ expect(obfuscateModule.validateRules(newRules)).toEqual(false);
108
+ expect(consolModule.warn).toHaveBeenCalledWith(expect.stringContaining('contains a "regex" value with an invalid type'));
109
+ });
110
+ test('should warn about a missing regex with value', () => {
111
+ const newRules = [{
112
+ replacement: rules[0].replacement
113
+ }];
114
+ expect(obfuscateModule.validateRules(newRules)).toEqual(false);
115
+ expect(consolModule.warn).toHaveBeenCalledWith(expect.stringContaining('missing a "regex" value'));
116
+ });
117
+ test.each([123, {}, []])('should warn about an invalid replacement type %s', input => {
118
+ const newRules = [{
119
+ regex: rules[0].regex,
120
+ replacement: input
121
+ }];
122
+ expect(obfuscateModule.validateRules(newRules)).toEqual(false);
123
+ expect(consolModule.warn).toHaveBeenCalledWith(expect.stringContaining('contains a "replacement" value with an invalid type'));
124
+ });
125
+ });
@@ -4,7 +4,7 @@ import { isFileProtocol } from '../url/protocol';
4
4
  import { warn } from './console';
5
5
  var fileProtocolRule = {
6
6
  regex: /^file:\/\/(.*)/,
7
- replacement: 'file://OBFUSCATED'
7
+ replacement: atob('ZmlsZTovL09CRlVTQ0FURUQ=')
8
8
  };
9
9
  export class Obfuscator extends SharedContext {
10
10
  shouldObfuscate() {
@@ -47,7 +47,7 @@ export function validateRules(rules) {
47
47
  if (!('regex' in rules[i])) {
48
48
  warn('An obfuscation replacement rule was detected missing a "regex" value.');
49
49
  invalidRegexDetected = true;
50
- } else if (typeof rules[i].regex !== 'string' && !(rules[i].regex.constructor === RegExp)) {
50
+ } else if (typeof rules[i].regex !== 'string' && !(rules[i].regex instanceof RegExp)) {
51
51
  warn('An obfuscation replacement rule contains a "regex" value with an invalid type (must be a string or RegExp)');
52
52
  invalidRegexDetected = true;
53
53
  }
@@ -124,10 +124,10 @@ submitData.beacon = function (_ref5) {
124
124
  url,
125
125
  body
126
126
  } = _ref5;
127
- // Navigator has to be bound to ensure it does not error in some browsers
128
- // https://xgwang.me/posts/you-may-not-know-beacon/#it-may-throw-error%2C-be-sure-to-catch
129
- const send = window.navigator.sendBeacon.bind(window.navigator);
130
127
  try {
128
+ // Navigator has to be bound to ensure it does not error in some browsers
129
+ // https://xgwang.me/posts/you-may-not-know-beacon/#it-may-throw-error%2C-be-sure-to-catch
130
+ const send = window.navigator.sendBeacon.bind(window.navigator);
131
131
  return send(url, body);
132
132
  } catch (err) {
133
133
  // if sendBeacon still trys to throw an illegal invocation error,