@newrelic/browser-agent 1.242.0 → 1.243.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 (111) hide show
  1. package/CHANGELOG.md +1465 -0
  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/session/session-entity.js +20 -2
  5. package/dist/cjs/common/wrap/wrap-function.js +1 -1
  6. package/dist/cjs/features/ajax/aggregate/index.js +1 -1
  7. package/dist/cjs/features/session_replay/aggregate/index.js +84 -54
  8. package/dist/cjs/features/utils/feature-base.js +1 -2
  9. package/dist/cjs/loaders/api/api.js +2 -2
  10. package/dist/cjs/loaders/api/apiAsync.js +0 -37
  11. package/dist/cjs/loaders/configure/configure.js +1 -1
  12. package/dist/cjs/loaders/configure/public-path.js +6 -3
  13. package/dist/esm/common/constants/env.cdn.js +1 -1
  14. package/dist/esm/common/constants/env.npm.js +1 -1
  15. package/dist/esm/common/session/session-entity.js +18 -1
  16. package/dist/esm/common/wrap/wrap-function.js +1 -1
  17. package/dist/esm/features/ajax/aggregate/index.js +1 -1
  18. package/dist/esm/features/session_replay/aggregate/index.js +83 -54
  19. package/dist/esm/features/utils/feature-base.js +1 -2
  20. package/dist/esm/loaders/api/api.js +2 -2
  21. package/dist/esm/loaders/api/apiAsync.js +1 -36
  22. package/dist/esm/loaders/configure/configure.js +1 -1
  23. package/dist/esm/loaders/configure/public-path.js +6 -3
  24. package/dist/types/common/session/session-entity.d.ts +5 -0
  25. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  26. package/dist/types/features/session_replay/aggregate/index.d.ts +11 -14
  27. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  28. package/dist/types/features/utils/feature-base.d.ts.map +1 -1
  29. package/dist/types/loaders/api/api.d.ts.map +1 -1
  30. package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
  31. package/dist/types/loaders/configure/public-path.d.ts +1 -1
  32. package/dist/types/loaders/configure/public-path.d.ts.map +1 -1
  33. package/package.json +2 -2
  34. package/src/common/session/session-entity.js +20 -1
  35. package/src/common/wrap/wrap-function.js +1 -1
  36. package/src/features/ajax/aggregate/index.js +2 -2
  37. package/src/features/session_replay/aggregate/index.js +81 -37
  38. package/src/features/utils/feature-base.js +1 -2
  39. package/src/loaders/api/api.js +1 -2
  40. package/src/loaders/api/apiAsync.js +1 -39
  41. package/src/loaders/configure/configure.js +1 -1
  42. package/src/loaders/configure/public-path.js +6 -3
  43. package/src/common/aggregate/aggregator.test.js +0 -107
  44. package/src/common/config/state/configurable.test.js +0 -73
  45. package/src/common/config/state/info.test.js +0 -31
  46. package/src/common/config/state/init.test.js +0 -68
  47. package/src/common/config/state/loader-config.test.js +0 -21
  48. package/src/common/config/state/runtime.test.js +0 -21
  49. package/src/common/constants/env.cdn.test.js +0 -7
  50. package/src/common/constants/env.npm.test.js +0 -7
  51. package/src/common/constants/env.test.js +0 -7
  52. package/src/common/constants/runtime.test.js +0 -176
  53. package/src/common/deny-list/deny-list.test.js +0 -104
  54. package/src/common/dom/query-selector.test.js +0 -24
  55. package/src/common/drain/drain.test.js +0 -74
  56. package/src/common/event-emitter/contextual-ee.component-test.js +0 -293
  57. package/src/common/event-emitter/handle.test.js +0 -56
  58. package/src/common/event-emitter/register-handler.test.js +0 -61
  59. package/src/common/harvest/harvest-scheduler.test.js +0 -492
  60. package/src/common/harvest/harvest.test.js +0 -813
  61. package/src/common/ids/id.test.js +0 -92
  62. package/src/common/ids/unique-id.test.js +0 -58
  63. package/src/common/session/session-entity.component-test.js +0 -346
  64. package/src/common/storage/local-storage.test.js +0 -17
  65. package/src/common/timer/interaction-timer.component-test.js +0 -212
  66. package/src/common/timer/timer.test.js +0 -99
  67. package/src/common/timing/nav-timing.test.js +0 -161
  68. package/src/common/url/canonicalize-url.test.js +0 -45
  69. package/src/common/url/clean-url.test.js +0 -25
  70. package/src/common/url/encode.test.js +0 -81
  71. package/src/common/url/location.test.js +0 -15
  72. package/src/common/url/parse-url.test.js +0 -110
  73. package/src/common/url/protocol.test.js +0 -17
  74. package/src/common/util/console.test.js +0 -34
  75. package/src/common/util/data-size.test.js +0 -56
  76. package/src/common/util/feature-flags.test.js +0 -94
  77. package/src/common/util/get-or-set.test.js +0 -58
  78. package/src/common/util/invoke.test.js +0 -65
  79. package/src/common/util/map-own.test.js +0 -52
  80. package/src/common/util/obfuscate.component-test.js +0 -173
  81. package/src/common/util/stringify.test.js +0 -49
  82. package/src/common/util/submit-data.test.js +0 -183
  83. package/src/common/util/traverse.test.js +0 -50
  84. package/src/common/vitals/cumulative-layout-shift.test.js +0 -71
  85. package/src/common/vitals/first-contentful-paint.test.js +0 -124
  86. package/src/common/vitals/first-input-delay.test.js +0 -88
  87. package/src/common/vitals/first-paint.test.js +0 -127
  88. package/src/common/vitals/interaction-to-next-paint.test.js +0 -74
  89. package/src/common/vitals/largest-contentful-paint.test.js +0 -94
  90. package/src/common/vitals/long-task.test.js +0 -122
  91. package/src/common/vitals/time-to-first-byte.test.js +0 -147
  92. package/src/common/vitals/vital-metric.test.js +0 -171
  93. package/src/common/wrap/wrap-promise.component-test.js +0 -110
  94. package/src/features/ajax/instrument/distributed-tracing.test.js +0 -375
  95. package/src/features/jserrors/aggregate/canonical-function-name.test.js +0 -13
  96. package/src/features/jserrors/aggregate/compute-stack-trace.test.js +0 -414
  97. package/src/features/jserrors/aggregate/format-stack-trace.test.js +0 -39
  98. package/src/features/jserrors/aggregate/string-hash-code.test.js +0 -12
  99. package/src/features/metrics/aggregate/framework-detection.test.js +0 -332
  100. package/src/features/page_view_timing/aggregate/index.component-test.js +0 -86
  101. package/src/features/session_replay/aggregate/index.component-test.js +0 -317
  102. package/src/features/spa/aggregate/interaction-node.test.js +0 -17
  103. package/src/features/utils/agent-session.test.js +0 -194
  104. package/src/features/utils/aggregate-base.test.js +0 -123
  105. package/src/features/utils/feature-base.test.js +0 -45
  106. package/src/features/utils/handler-cache.test.js +0 -72
  107. package/src/features/utils/instrument-base.test.js +0 -216
  108. package/src/features/utils/lazy-feature-loader.test.js +0 -37
  109. package/src/loaders/api/api.component-test.js +0 -45
  110. package/src/loaders/api/api.test.js +0 -85
  111. package/src/loaders/api/apiAsync.test.js +0 -17
@@ -1,110 +0,0 @@
1
- afterEach(() => {
2
- jest.resetModules()
3
- jest.clearAllMocks()
4
- })
5
-
6
- const urlTests = [
7
- {
8
- input: 'http://example.com/path/name?qs=5&a=b',
9
- expected: {
10
- hostname: 'example.com',
11
- pathname: '/path/name',
12
- protocol: 'http',
13
- port: '80',
14
- sameOrigin: false
15
- }
16
- },
17
- {
18
- input: 'http://foo:bar@example.com:8080/path/@name?qs=5&a=b',
19
- expected: {
20
- hostname: 'example.com',
21
- pathname: '/path/@name',
22
- protocol: 'http',
23
- port: '8080',
24
- sameOrigin: false
25
- }
26
- },
27
- {
28
- input: 'https://foo:bar@example.com/path/name?qs=5&a=b',
29
- expected: {
30
- hostname: 'example.com',
31
- pathname: '/path/name',
32
- protocol: 'https',
33
- port: '443',
34
- sameOrigin: false
35
- }
36
- },
37
- {
38
- input: '/path/name?qs=5&a=b',
39
- expected: {
40
- hostname: location.hostname,
41
- pathname: '/path/name',
42
- protocol: location.protocol.split(':')[0],
43
- port: '80',
44
- sameOrigin: true
45
- }
46
- },
47
- {
48
- input: location.protocol + '//' + location.hostname + ':' + location.port + '/path/name?qs=5&a=b',
49
- expected: {
50
- hostname: location.hostname,
51
- pathname: '/path/name',
52
- protocol: location.protocol.split(':')[0],
53
- port: '80',
54
- sameOrigin: true
55
- }
56
- },
57
- {
58
- input: 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==',
59
- expected: {
60
- protocol: 'data'
61
- }
62
- }
63
- ]
64
-
65
- test.each(urlTests)('verify url parsing inside browser scope', async ({ input, expected }) => {
66
- jest.doMock('../constants/runtime', () => ({
67
- __esModule: true,
68
- isBrowserScope: true,
69
- globalScope: global
70
- }))
71
-
72
- const { parseUrl } = await import('./parse-url')
73
- expect(parseUrl(input)).toEqual(expected)
74
- })
75
-
76
- test.each(urlTests)('verify url parsing outside browser scope', async ({ input, expected }) => {
77
- jest.doMock('../constants/runtime', () => ({
78
- __esModule: true,
79
- isBrowserScope: false,
80
- globalScope: global
81
- }))
82
-
83
- const { parseUrl } = await import('./parse-url')
84
- expect(parseUrl(input)).toEqual(expected)
85
- })
86
-
87
- test('should cache parsed urls', async () => {
88
- jest.doMock('../constants/runtime', () => ({
89
- __esModule: true,
90
- isBrowserScope: true,
91
- globalScope: global
92
- }))
93
-
94
- const input = 'http://example.com/'
95
- const expected = {
96
- hostname: 'example.com',
97
- pathname: '/',
98
- protocol: 'http',
99
- port: '80',
100
- sameOrigin: false
101
- }
102
-
103
- jest.spyOn(document, 'createElement')
104
-
105
- const { parseUrl } = await import('./parse-url')
106
- parseUrl(input)
107
-
108
- expect(parseUrl(input)).toEqual(expected)
109
- expect(document.createElement).toHaveBeenCalledTimes(1)
110
- })
@@ -1,17 +0,0 @@
1
- import { isFileProtocol } from './protocol'
2
-
3
- test('should return true when location url contains file protocol', () => {
4
- jest.spyOn(window, 'location', 'get').mockReturnValue({
5
- protocol: 'file:'
6
- })
7
-
8
- expect(isFileProtocol()).toEqual(true)
9
- })
10
-
11
- test('should return false when location url does not contains file protocol', () => {
12
- jest.spyOn(window, 'location', 'get').mockReturnValue({
13
- protocol: 'http:'
14
- })
15
-
16
- expect(isFileProtocol()).toEqual(false)
17
- })
@@ -1,34 +0,0 @@
1
- import { warn } from './console'
2
-
3
- beforeEach(() => {
4
- console.warn = jest.fn()
5
- })
6
-
7
- afterEach(() => {
8
- jest.restoreAllMocks()
9
- })
10
-
11
- describe('warn', () => {
12
- test('should not call console.warn if it is not a function', () => {
13
- const spy = jest.spyOn(console, 'warn')
14
- console.warn = undefined
15
- warn('test message')
16
- expect(spy).not.toHaveBeenCalled()
17
- })
18
-
19
- test('should call console.warn with a prefixed message', () => {
20
- warn('test message')
21
- expect(console.warn).toHaveBeenCalledWith('New Relic: test message')
22
- })
23
-
24
- test('should call console.warn with secondary argument if provided', () => {
25
- const secondary = 'secondary value'
26
- warn('test message', secondary)
27
- expect(console.warn).toHaveBeenCalledWith(secondary)
28
- })
29
-
30
- test('should not call console.warn with secondary argument if not provided', () => {
31
- warn('test message')
32
- expect(console.warn).toHaveBeenCalledTimes(1)
33
- })
34
- })
@@ -1,56 +0,0 @@
1
- import { faker } from '@faker-js/faker'
2
- import * as stringifyModule from './stringify'
3
- import { dataSize } from './data-size'
4
-
5
- jest.mock('./stringify')
6
-
7
- describe('dataSize', () => {
8
- test('returns length of string', () => {
9
- const str = faker.lorem.sentence()
10
- expect(dataSize(str)).toBe(str.length)
11
- })
12
-
13
- test('returns undefined for non-object, number, or empty string', () => {
14
- expect(dataSize(Infinity)).toBeUndefined()
15
- expect(dataSize(NaN)).toBeUndefined()
16
- expect(dataSize(12345)).toBeUndefined()
17
- expect(dataSize('')).toBeUndefined()
18
- })
19
-
20
- test('returns byte length of ArrayBuffer object', () => {
21
- const buffer = new ArrayBuffer(faker.datatype.number({ min: 10, max: 100 }))
22
- expect(dataSize(buffer)).toBe(buffer.byteLength)
23
- })
24
-
25
- test('returns size of Blob object', () => {
26
- const blob = new Blob([faker.lorem.sentence()], { type: 'text/plain' })
27
- expect(dataSize(blob)).toBe(blob.size)
28
- })
29
-
30
- test('returns undefined for FormData object', () => {
31
- const formData = new FormData()
32
- expect(dataSize(formData)).toBeUndefined()
33
- })
34
-
35
- test('uses stringify to get the length of an object', () => {
36
- const input = {
37
- [faker.datatype.uuid()]: faker.lorem.sentence()
38
- }
39
- const expectedSize = faker.datatype.number({ min: 1000, max: 10000 })
40
-
41
- jest.spyOn(stringifyModule, 'stringify').mockReturnValue({ length: expectedSize })
42
-
43
- expect(dataSize(input)).toBe(expectedSize)
44
- })
45
-
46
- test('should not throw an exception if stringify throws an exception', () => {
47
- const input = {
48
- [faker.datatype.uuid()]: faker.lorem.sentence()
49
- }
50
-
51
- jest.spyOn(stringifyModule, 'stringify').mockImplementation(() => { throw new Error(faker.lorem.sentence()) })
52
-
53
- expect(() => dataSize(input)).not.toThrow()
54
- expect(dataSize(input)).toBeUndefined()
55
- })
56
- })
@@ -1,94 +0,0 @@
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
-
8
- jest.mock('../event-emitter/handle')
9
- jest.mock('../drain/drain')
10
- jest.mock('../event-emitter/contextual-ee', () => ({
11
- __esModule: true,
12
- ee: {
13
- get: jest.fn(() => ({
14
- foo: `bar_${Math.random()}`
15
- }))
16
- }
17
- }))
18
-
19
- let agentIdentifier
20
-
21
- beforeEach(() => {
22
- agentIdentifier = faker.datatype.uuid()
23
- })
24
-
25
- afterEach(() => {
26
- Object.keys(activatedFeatures)
27
- .forEach(key => delete activatedFeatures[key])
28
- })
29
-
30
- test.each([
31
- null,
32
- undefined
33
- ])('should not do anything when flags is %s', (input) => {
34
- activateFeatures(input, agentIdentifier)
35
-
36
- expect(handleModule.handle).not.toHaveBeenCalled()
37
- expect(drainModule.drain).not.toHaveBeenCalled()
38
- expect(activatedFeatures).toEqual({})
39
- })
40
-
41
- const bucketMap = {
42
- stn: [FEATURE_NAMES.sessionTrace],
43
- err: [FEATURE_NAMES.jserrors, FEATURE_NAMES.metrics],
44
- ins: [FEATURE_NAMES.pageAction],
45
- spa: [FEATURE_NAMES.spa],
46
- sr: [FEATURE_NAMES.sessionReplay, FEATURE_NAMES.sessionTrace]
47
- }
48
-
49
- test('emits the right events when feature flag = 1', () => {
50
- const flags = {}
51
- Object.keys(bucketMap).forEach(flag => { flags[flag] = 1 })
52
- activateFeatures(flags, agentIdentifier)
53
-
54
- const sharedEE = jest.mocked(eventEmitterModule.ee.get).mock.results[0].value
55
-
56
- // 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
57
- expect(handleModule.handle).toHaveBeenCalledTimes(14)
58
- expect(handleModule.handle).toHaveBeenNthCalledWith(1, 'feat-stn', [], undefined, FEATURE_NAMES.sessionTrace, sharedEE)
59
- expect(handleModule.handle).toHaveBeenLastCalledWith('rumresp-sr', [true], undefined, FEATURE_NAMES.sessionTrace, sharedEE)
60
-
61
- Object.keys(flags).forEach(flag => { flags[flag] = true })
62
- expect(activatedFeatures).toEqual(flags)
63
- })
64
-
65
- test('emits the right events when feature flag = 0', () => {
66
- const flags = {}
67
- Object.keys(bucketMap).forEach(flag => { flags[flag] = 0 })
68
- activateFeatures(flags, agentIdentifier)
69
-
70
- const sharedEE = jest.mocked(eventEmitterModule.ee.get).mock.results[0].value
71
-
72
- // 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
73
- expect(handleModule.handle).toHaveBeenCalledTimes(14)
74
- expect(handleModule.handle).toHaveBeenNthCalledWith(1, 'block-stn', [], undefined, FEATURE_NAMES.sessionTrace, sharedEE)
75
- expect(handleModule.handle).toHaveBeenLastCalledWith('rumresp-sr', [false], undefined, FEATURE_NAMES.sessionTrace, sharedEE)
76
-
77
- Object.keys(flags).forEach(flag => { flags[flag] = false })
78
- expect(activatedFeatures).toEqual(flags)
79
- })
80
-
81
- test('only the first activate of the same feature is respected', () => {
82
- const flags = { stn: 1 }
83
- activateFeatures(flags, agentIdentifier)
84
- flags.stn = 0
85
- activateFeatures(flags, agentIdentifier)
86
-
87
- const sharedEE1 = jest.mocked(eventEmitterModule.ee.get).mock.results[0].value
88
- const sharedEE2 = jest.mocked(eventEmitterModule.ee.get).mock.results[1].value
89
-
90
- expect(handleModule.handle).toHaveBeenNthCalledWith(1, 'feat-stn', [], undefined, 'session_trace', sharedEE1)
91
- expect(handleModule.handle).toHaveBeenNthCalledWith(2, 'rumresp-stn', [true], undefined, 'session_trace', sharedEE1)
92
- expect(handleModule.handle).not.toHaveBeenNthCalledWith(1, 'feat-stn', [], undefined, 'session_trace', sharedEE2)
93
- expect(activatedFeatures.stn).toBeTruthy()
94
- })
@@ -1,58 +0,0 @@
1
- import { getOrSet } from './get-or-set'
2
-
3
- test('should return the current value of an existing property', () => {
4
- const obj = { foo: 'bar' }
5
- const prop = 'foo'
6
- const getVal = jest.fn()
7
-
8
- const result = getOrSet(obj, prop, getVal)
9
-
10
- expect(result).toBe('bar')
11
- expect(getVal).not.toHaveBeenCalled()
12
- })
13
-
14
- test('should set and return the value from getVal if the property does not exist', () => {
15
- const obj = {}
16
- const prop = 'foo'
17
- const getVal = jest.fn().mockReturnValue('baz')
18
-
19
- const result = getOrSet(obj, prop, getVal)
20
-
21
- expect(result).toBe('baz')
22
- expect(getVal).toHaveBeenCalled()
23
- expect(obj.foo).toBe('baz')
24
- })
25
-
26
- test('should set the property as non-enumerable if Object.defineProperty is supported', () => {
27
- const obj = {}
28
- const prop = 'foo'
29
- const getVal = jest.fn().mockReturnValue('baz')
30
-
31
- jest.spyOn(Object, 'defineProperty')
32
-
33
- const result = getOrSet(obj, prop, getVal)
34
-
35
- expect(result).toBe('baz')
36
- expect(Object.defineProperty).toHaveBeenCalledWith(obj, prop, {
37
- value: 'baz',
38
- writable: true,
39
- enumerable: false
40
- })
41
- })
42
-
43
- test('should set the property from getVal if Object.defineProperty and Object.keys are not supported', () => {
44
- const obj = {}
45
- const prop = 'foo'
46
- const getVal = jest.fn(() => 'baz')
47
-
48
- const originalFn = Object.defineProperty
49
- Object.defineProperty = null
50
-
51
- const result = getOrSet(obj, prop, getVal)
52
-
53
- expect(result).toBe('baz')
54
- expect(obj.foo).toBe('baz')
55
- expect(Object.prototype.propertyIsEnumerable.call(obj, 'foo')).toBe(true)
56
-
57
- Object.defineProperty = originalFn
58
- })
@@ -1,65 +0,0 @@
1
- import { debounce, single } from './invoke'
2
-
3
- jest.useFakeTimers()
4
-
5
- describe('debounce', () => {
6
- test('should run the supplied function after 100ms', () => {
7
- let mockCallback = jest.fn()
8
-
9
- let debouncedMethod = debounce(mockCallback, 100)
10
- execFnTimes(debouncedMethod, 100)
11
-
12
- expect(mockCallback).not.toHaveBeenCalled()
13
-
14
- jest.advanceTimersByTime(1000)
15
- expect(mockCallback).toHaveBeenCalledTimes(1)
16
- })
17
-
18
- test('should rerun the supplied function when called again after 100ms', () => {
19
- let mockCallback = jest.fn()
20
-
21
- let debouncedMethod = debounce(mockCallback, 100)
22
-
23
- execFnTimes(debouncedMethod, 100)
24
- jest.advanceTimersByTime(200)
25
- execFnTimes(debouncedMethod, 100)
26
- jest.advanceTimersByTime(2000)
27
-
28
- expect(mockCallback).toHaveBeenCalledTimes(2)
29
- })
30
-
31
- test('should run the supplied function on the first event and debounce subsequent events', () => {
32
- let mockCallback = jest.fn()
33
-
34
- let debouncedMethod = debounce(mockCallback, 100, { leading: true })
35
-
36
- execFnTimes(debouncedMethod, 100)
37
- expect(mockCallback).toHaveBeenCalledTimes(1)
38
-
39
- jest.advanceTimersByTime(200)
40
-
41
- expect(mockCallback).toHaveBeenCalledTimes(1)
42
-
43
- execFnTimes(debouncedMethod, 100)
44
- jest.advanceTimersByTime(200)
45
-
46
- expect(mockCallback).toHaveBeenCalledTimes(2)
47
- })
48
- })
49
-
50
- describe('single', () => {
51
- test('should run the supplied function only once', () => {
52
- let mockCallback = jest.fn()
53
-
54
- let singleMethod = single(mockCallback, 100)
55
- execFnTimes(singleMethod, 100)
56
-
57
- expect(mockCallback).toHaveBeenCalledTimes(1)
58
- })
59
- })
60
-
61
- function execFnTimes (fn, count) {
62
- for (let i = 0; i < count; i++) {
63
- fn()
64
- }
65
- }
@@ -1,52 +0,0 @@
1
- import { mapOwn } from './map-own'
2
-
3
- test('enumerates the object properties', () => {
4
- const callback = jest.fn()
5
- const input = { foo: 'bar' }
6
-
7
- mapOwn(input, callback)
8
-
9
- expect(callback).toHaveBeenCalledWith('foo', 'bar')
10
- })
11
-
12
- test('return array of results from callback invocation', () => {
13
- const callback = jest.fn((key, value) => `${key}:${value}`)
14
- const input = { foo: 'bar', biz: 'baz' }
15
-
16
- const result = mapOwn(input, callback)
17
-
18
- expect(result.length).toEqual(2)
19
- expect(result).toContain('foo:bar')
20
- expect(result).toContain('biz:baz')
21
- })
22
-
23
- test('does not iterate symbol properties', () => {
24
- const callback = jest.fn((key, value) => `${key}:${value}`)
25
- const input = { foo: 'bar', [Symbol.for('biz')]: 'baz' }
26
-
27
- const result = mapOwn(input, callback)
28
-
29
- expect(result.length).toEqual(1)
30
- })
31
-
32
- test('does not iterate inherited properties', () => {
33
- function F () {}
34
- F.prototype = { biz: 'baz' }
35
-
36
- const callback = jest.fn((key, value) => `${key}:${value}`)
37
- const input = new F()
38
- input.foo = 'bar'
39
-
40
- const result = mapOwn(input, callback)
41
-
42
- expect(result.length).toEqual(1)
43
- })
44
-
45
- test.each([null, undefined])('returns empty array when passed object is null or undefined', (input) => {
46
- const callback = jest.fn()
47
- const result = mapOwn(input, callback)
48
-
49
- expect(Array.isArray(result)).toEqual(true)
50
- expect(result.length).toEqual(0)
51
- expect(callback).toHaveBeenCalledTimes(0)
52
- })
@@ -1,173 +0,0 @@
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
-
7
- jest.mock('../config/config')
8
- jest.mock('../context/shared-context')
9
- jest.mock('../url/protocol')
10
- jest.mock('./console')
11
-
12
- let agentIdentifier
13
- const rules = [{
14
- regex: /pii/g,
15
- replacement: 'OBFUSCATED'
16
- }]
17
-
18
- beforeEach(() => {
19
- agentIdentifier = faker.datatype.uuid()
20
- })
21
-
22
- afterEach(() => {
23
- jest.resetAllMocks()
24
- })
25
-
26
- describe('Obfuscator', () => {
27
- test('shouldObfuscate returns true when there are rules', () => {
28
- jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue(rules)
29
-
30
- const obfuscator = new obfuscateModule.Obfuscator()
31
- obfuscator.sharedContext = { agentIdentifier }
32
-
33
- expect(obfuscator.shouldObfuscate()).toEqual(true)
34
- })
35
-
36
- test('shouldObfuscate returns false when there are no rules', () => {
37
- jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue([])
38
-
39
- const obfuscator = new obfuscateModule.Obfuscator()
40
- obfuscator.sharedContext = { agentIdentifier }
41
-
42
- expect(obfuscator.shouldObfuscate()).toEqual(false)
43
- })
44
-
45
- test('obfuscateString returns the input when there are no rules', () => {
46
- jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue([])
47
-
48
- const input = faker.lorem.sentence()
49
- const obfuscator = new obfuscateModule.Obfuscator()
50
- obfuscator.sharedContext = { agentIdentifier }
51
-
52
- expect(obfuscator.obfuscateString(input)).toEqual(input)
53
- })
54
-
55
- test('obfuscateString applies obfuscation rules to input', () => {
56
- jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue(rules)
57
-
58
- const input = 'pii'
59
- const obfuscator = new obfuscateModule.Obfuscator()
60
- obfuscator.sharedContext = { agentIdentifier }
61
-
62
- expect(obfuscator.obfuscateString(input)).toEqual(rules[0].replacement)
63
- })
64
-
65
- test('obfuscateString replaces input with * when replacement is not set', () => {
66
- const newRules = [{
67
- regex: rules[0].regex
68
- }]
69
-
70
- jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue(newRules)
71
-
72
- const input = 'pii'
73
- const obfuscator = new obfuscateModule.Obfuscator()
74
- obfuscator.sharedContext = { agentIdentifier }
75
-
76
- expect(obfuscator.obfuscateString(input)).toEqual('*')
77
- })
78
-
79
- test.each([
80
- null,
81
- undefined,
82
- '',
83
- 123
84
- ])('obfuscateString returns the input it is %s', (input) => {
85
- jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue(rules)
86
-
87
- const obfuscator = new obfuscateModule.Obfuscator()
88
- obfuscator.sharedContext = { agentIdentifier }
89
-
90
- expect(obfuscator.obfuscateString(input)).toEqual(input)
91
- })
92
- })
93
-
94
- describe('getRules', () => {
95
- test('should return configured rules', () => {
96
- jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue(rules)
97
-
98
- expect(obfuscateModule.getRules()).toEqual(rules)
99
- })
100
-
101
- test('should include the file protocol obfuscation', () => {
102
- jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue(rules)
103
- jest.spyOn(urlProtocolModule, 'isFileProtocol').mockReturnValue(rules)
104
-
105
- expect(obfuscateModule.getRules()).toEqual(expect.arrayContaining([{
106
- regex: /^file:\/\/(.*)/,
107
- replacement: 'file://OBFUSCATED'
108
- }]))
109
- })
110
-
111
- test.each([
112
- null,
113
- undefined
114
- ])('should return an empty array when obfuscation rules are %s', (input) => {
115
- jest.spyOn(configModule, 'getConfigurationValue').mockReturnValue(input)
116
-
117
- expect(obfuscateModule.getRules()).toEqual([])
118
- })
119
- })
120
-
121
- describe('validateRules', () => {
122
- test('should return true for empty array', () => {
123
- expect(obfuscateModule.validateRules([])).toEqual(true)
124
- })
125
-
126
- test('should return true for valid rules', () => {
127
- expect(obfuscateModule.validateRules(rules)).toEqual(true)
128
- })
129
-
130
- test.each([
131
- null,
132
- 123,
133
- {},
134
- []
135
- ])('should warn about an invalid regex type %s', (input) => {
136
- const newRules = [{
137
- regex: input,
138
- replacement: rules[0].replacement
139
- }]
140
-
141
- expect(obfuscateModule.validateRules(newRules)).toEqual(false)
142
- expect(consolModule.warn).toHaveBeenCalledWith(expect.stringContaining(
143
- 'contains a "regex" value with an invalid type'
144
- ))
145
- })
146
-
147
- test('should warn about a missing regex with value', () => {
148
- const newRules = [{
149
- replacement: rules[0].replacement
150
- }]
151
-
152
- expect(obfuscateModule.validateRules(newRules)).toEqual(false)
153
- expect(consolModule.warn).toHaveBeenCalledWith(expect.stringContaining(
154
- 'missing a "regex" value'
155
- ))
156
- })
157
-
158
- test.each([
159
- 123,
160
- {},
161
- []
162
- ])('should warn about an invalid replacement type %s', (input) => {
163
- const newRules = [{
164
- regex: rules[0].regex,
165
- replacement: input
166
- }]
167
-
168
- expect(obfuscateModule.validateRules(newRules)).toEqual(false)
169
- expect(consolModule.warn).toHaveBeenCalledWith(expect.stringContaining(
170
- 'contains a "replacement" value with an invalid type'
171
- ))
172
- })
173
- })