@newrelic/browser-agent 1.241.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.
- package/CHANGELOG.md +1465 -0
- package/dist/cjs/cdn/polyfills/lite.js +13 -1
- package/dist/cjs/cdn/polyfills/pro.js +17 -1
- package/dist/cjs/cdn/polyfills/spa.js +18 -1
- package/dist/cjs/common/config/state/init.js +32 -5
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/dom/query-selector.js +16 -0
- package/dist/cjs/common/session/session-entity.js +20 -2
- package/dist/cjs/common/wrap/wrap-function.js +1 -1
- package/dist/cjs/features/ajax/aggregate/index.js +1 -1
- package/dist/cjs/features/session_replay/aggregate/index.js +84 -50
- package/dist/cjs/features/utils/feature-base.js +1 -2
- package/dist/cjs/features/utils/instrument-base.js +1 -0
- package/dist/cjs/loaders/api/api.js +2 -2
- package/dist/cjs/loaders/api/apiAsync.js +0 -37
- package/dist/cjs/loaders/configure/configure.js +1 -1
- package/dist/cjs/loaders/configure/public-path.js +6 -3
- package/dist/esm/cdn/polyfills/lite.js +8 -1
- package/dist/esm/cdn/polyfills/pro.js +13 -2
- package/dist/esm/cdn/polyfills/spa.js +13 -1
- package/dist/esm/common/config/state/init.js +32 -5
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/dom/query-selector.js +9 -0
- package/dist/esm/common/session/session-entity.js +18 -1
- package/dist/esm/common/wrap/wrap-function.js +1 -1
- package/dist/esm/features/ajax/aggregate/index.js +1 -1
- package/dist/esm/features/session_replay/aggregate/index.js +83 -50
- package/dist/esm/features/utils/feature-base.js +1 -2
- package/dist/esm/features/utils/instrument-base.js +1 -0
- package/dist/esm/loaders/api/api.js +2 -2
- package/dist/esm/loaders/api/apiAsync.js +1 -36
- package/dist/esm/loaders/configure/configure.js +1 -1
- package/dist/esm/loaders/configure/public-path.js +6 -3
- package/dist/types/common/config/state/init.d.ts.map +1 -1
- package/dist/types/common/dom/query-selector.d.ts +2 -0
- package/dist/types/common/dom/query-selector.d.ts.map +1 -0
- package/dist/types/common/session/session-entity.d.ts +5 -0
- package/dist/types/common/session/session-entity.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +11 -14
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/utils/feature-base.d.ts.map +1 -1
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
- package/dist/types/loaders/configure/public-path.d.ts +1 -1
- package/dist/types/loaders/configure/public-path.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/cdn/polyfills/lite.js +14 -1
- package/src/cdn/polyfills/pro.js +23 -2
- package/src/cdn/polyfills/spa.js +24 -1
- package/src/common/config/state/init.js +33 -4
- package/src/common/dom/query-selector.js +9 -0
- package/src/common/session/session-entity.js +20 -1
- package/src/common/wrap/wrap-function.js +1 -1
- package/src/features/ajax/aggregate/index.js +2 -2
- package/src/features/session_replay/aggregate/index.js +82 -34
- package/src/features/utils/feature-base.js +1 -2
- package/src/features/utils/instrument-base.js +1 -0
- package/src/loaders/api/api.js +1 -2
- package/src/loaders/api/apiAsync.js +1 -39
- package/src/loaders/configure/configure.js +1 -1
- package/src/loaders/configure/public-path.js +6 -3
- package/src/common/aggregate/aggregator.test.js +0 -107
- package/src/common/config/state/configurable.test.js +0 -73
- package/src/common/config/state/info.test.js +0 -31
- package/src/common/config/state/init.test.js +0 -28
- package/src/common/config/state/loader-config.test.js +0 -21
- package/src/common/config/state/runtime.test.js +0 -21
- package/src/common/constants/env.cdn.test.js +0 -7
- package/src/common/constants/env.npm.test.js +0 -7
- package/src/common/constants/env.test.js +0 -7
- package/src/common/constants/runtime.test.js +0 -176
- package/src/common/deny-list/deny-list.test.js +0 -104
- package/src/common/drain/drain.test.js +0 -74
- package/src/common/event-emitter/contextual-ee.component-test.js +0 -293
- package/src/common/event-emitter/handle.test.js +0 -56
- package/src/common/event-emitter/register-handler.test.js +0 -61
- package/src/common/harvest/harvest-scheduler.test.js +0 -492
- package/src/common/harvest/harvest.test.js +0 -813
- package/src/common/ids/id.test.js +0 -92
- package/src/common/ids/unique-id.test.js +0 -58
- package/src/common/session/session-entity.component-test.js +0 -346
- package/src/common/storage/local-storage.test.js +0 -17
- package/src/common/timer/interaction-timer.component-test.js +0 -212
- package/src/common/timer/timer.test.js +0 -99
- package/src/common/timing/nav-timing.test.js +0 -161
- package/src/common/url/canonicalize-url.test.js +0 -45
- package/src/common/url/clean-url.test.js +0 -25
- package/src/common/url/encode.test.js +0 -81
- package/src/common/url/location.test.js +0 -15
- package/src/common/url/parse-url.test.js +0 -110
- package/src/common/url/protocol.test.js +0 -17
- package/src/common/util/console.test.js +0 -34
- package/src/common/util/data-size.test.js +0 -56
- package/src/common/util/feature-flags.test.js +0 -94
- package/src/common/util/get-or-set.test.js +0 -58
- package/src/common/util/invoke.test.js +0 -65
- package/src/common/util/map-own.test.js +0 -52
- package/src/common/util/obfuscate.component-test.js +0 -173
- package/src/common/util/stringify.test.js +0 -49
- package/src/common/util/submit-data.test.js +0 -183
- package/src/common/util/traverse.test.js +0 -50
- package/src/common/vitals/cumulative-layout-shift.test.js +0 -71
- package/src/common/vitals/first-contentful-paint.test.js +0 -124
- package/src/common/vitals/first-input-delay.test.js +0 -88
- package/src/common/vitals/first-paint.test.js +0 -127
- package/src/common/vitals/interaction-to-next-paint.test.js +0 -74
- package/src/common/vitals/largest-contentful-paint.test.js +0 -94
- package/src/common/vitals/long-task.test.js +0 -122
- package/src/common/vitals/time-to-first-byte.test.js +0 -147
- package/src/common/vitals/vital-metric.test.js +0 -171
- package/src/common/wrap/wrap-promise.component-test.js +0 -110
- package/src/features/ajax/instrument/distributed-tracing.test.js +0 -375
- package/src/features/jserrors/aggregate/canonical-function-name.test.js +0 -13
- package/src/features/jserrors/aggregate/compute-stack-trace.test.js +0 -414
- package/src/features/jserrors/aggregate/format-stack-trace.test.js +0 -39
- package/src/features/jserrors/aggregate/string-hash-code.test.js +0 -12
- package/src/features/metrics/aggregate/framework-detection.test.js +0 -332
- package/src/features/page_view_timing/aggregate/index.component-test.js +0 -86
- package/src/features/session_replay/aggregate/index.component-test.js +0 -317
- package/src/features/spa/aggregate/interaction-node.test.js +0 -17
- package/src/features/utils/agent-session.test.js +0 -194
- package/src/features/utils/aggregate-base.test.js +0 -123
- package/src/features/utils/feature-base.test.js +0 -45
- package/src/features/utils/handler-cache.test.js +0 -72
- package/src/features/utils/instrument-base.test.js +0 -216
- package/src/features/utils/lazy-feature-loader.test.js +0 -37
- package/src/loaders/api/api.component-test.js +0 -45
- package/src/loaders/api/api.test.js +0 -85
- package/src/loaders/api/apiAsync.test.js +0 -17
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
import { Aggregate as SessionReplayAgg, AVG_COMPRESSION, MAX_PAYLOAD_SIZE, IDEAL_PAYLOAD_SIZE } from '.'
|
|
2
|
-
import { Aggregator } from '../../../common/aggregate/aggregator'
|
|
3
|
-
import { SESSION_EVENTS, SessionEntity, MODE } from '../../../common/session/session-entity'
|
|
4
|
-
import { setConfiguration } from '../../../common/config/config'
|
|
5
|
-
import { configure } from '../../../loaders/configure/configure'
|
|
6
|
-
|
|
7
|
-
class LocalMemory {
|
|
8
|
-
constructor (initialState = {}) {
|
|
9
|
-
this.state = initialState
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
get (key) {
|
|
13
|
-
try {
|
|
14
|
-
return this.state[key]
|
|
15
|
-
} catch (err) {
|
|
16
|
-
// Error is ignored
|
|
17
|
-
return ''
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
set (key, value) {
|
|
22
|
-
try {
|
|
23
|
-
if (value === undefined || value === null) return this.remove(key)
|
|
24
|
-
this.state[key] = value
|
|
25
|
-
} catch (err) {
|
|
26
|
-
// Error is ignored
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
remove (key) {
|
|
31
|
-
try {
|
|
32
|
-
delete this.state[key]
|
|
33
|
-
} catch (err) {
|
|
34
|
-
// Error is ignored
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
let sr, session
|
|
40
|
-
const agentIdentifier = 'abcd'
|
|
41
|
-
const info = { licenseKey: 1234, applicationID: 9876 }
|
|
42
|
-
const init = { session_replay: { enabled: true, sampling_rate: 100, error_sampling_rate: 0 } }
|
|
43
|
-
|
|
44
|
-
const anyQuery = {
|
|
45
|
-
browser_monitoring_key: info.licenseKey,
|
|
46
|
-
type: 'SessionReplay',
|
|
47
|
-
app_id: Number(info.applicationID),
|
|
48
|
-
protocol_version: '0',
|
|
49
|
-
attributes: expect.any(String)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
describe('Session Replay', () => {
|
|
53
|
-
beforeEach(async () => {
|
|
54
|
-
primeSessionAndReplay()
|
|
55
|
-
})
|
|
56
|
-
afterEach(async () => {
|
|
57
|
-
sr.abort()
|
|
58
|
-
jest.clearAllMocks()
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
describe('Session Replay Session Behavior', () => {
|
|
62
|
-
test('When Session Ends', async () => {
|
|
63
|
-
const xhrMockClass = () => ({
|
|
64
|
-
open: jest.fn(),
|
|
65
|
-
send: jest.fn(),
|
|
66
|
-
setRequestHeader: jest.fn(),
|
|
67
|
-
addEventListener: jest.fn()
|
|
68
|
-
})
|
|
69
|
-
global.XMLHttpRequest = jest.fn().mockImplementation(xhrMockClass)
|
|
70
|
-
|
|
71
|
-
setConfiguration(agentIdentifier, { ...init })
|
|
72
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
73
|
-
await wait(1)
|
|
74
|
-
expect(sr.initialized).toBeTruthy()
|
|
75
|
-
expect(sr.recording).toBeTruthy()
|
|
76
|
-
sr.ee.emit(SESSION_EVENTS.RESET)
|
|
77
|
-
expect(global.XMLHttpRequest).toHaveBeenCalled()
|
|
78
|
-
expect(sr.recording).toBeFalsy()
|
|
79
|
-
expect(sr.blocked).toBeTruthy()
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
test('When Session Is Paused/Resumed', async () => {
|
|
83
|
-
setConfiguration(agentIdentifier, { ...init })
|
|
84
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
85
|
-
await wait(1)
|
|
86
|
-
expect(sr.initialized).toBeTruthy()
|
|
87
|
-
expect(sr.recording).toBeTruthy()
|
|
88
|
-
sr.ee.emit(SESSION_EVENTS.PAUSE)
|
|
89
|
-
expect(sr.recording).toBeFalsy()
|
|
90
|
-
sr.ee.emit(SESSION_EVENTS.RESUME)
|
|
91
|
-
expect(sr.recording).toBeTruthy()
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
test('Session SR mode matches SR mode -- FULL', async () => {
|
|
95
|
-
setConfiguration(agentIdentifier, { ...init })
|
|
96
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
97
|
-
await wait(1)
|
|
98
|
-
expect(session.state.sessionReplay).toEqual(sr.mode)
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
test('Session SR mode matches SR mode -- ERROR', async () => {
|
|
102
|
-
setConfiguration(agentIdentifier, { session_replay: { sampling_rate: 0, error_sampling_rate: 100 } })
|
|
103
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
104
|
-
await wait(1)
|
|
105
|
-
expect(session.state.sessionReplay).toEqual(sr.mode)
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
test('Session SR mode matches SR mode -- OFF', async () => {
|
|
109
|
-
setConfiguration(agentIdentifier, { session_replay: { sampling_rate: 0, error_sampling_rate: 0 } })
|
|
110
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
111
|
-
await wait(1)
|
|
112
|
-
expect(session.state.sessionReplay).toEqual(sr.mode)
|
|
113
|
-
})
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
describe('Session Replay Initialization Behavior', () => {
|
|
117
|
-
test('Waits for SR', async () => {
|
|
118
|
-
setConfiguration(agentIdentifier, { ...init })
|
|
119
|
-
// do not emit sr flag
|
|
120
|
-
await wait(1000)
|
|
121
|
-
expect(sr.initialized).toEqual(false)
|
|
122
|
-
expect(sr.recording).toEqual(false)
|
|
123
|
-
|
|
124
|
-
// emit a false flag
|
|
125
|
-
sr.ee.emit('rumresp-sr', [false])
|
|
126
|
-
await wait(1)
|
|
127
|
-
expect(sr.initialized).toEqual(true)
|
|
128
|
-
expect(sr.recording).toEqual(false)
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
test('Does not run if cookies_enabled is false', async () => {
|
|
132
|
-
setConfiguration(agentIdentifier, { ...init, privacy: { cookies_enabled: false } })
|
|
133
|
-
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
|
|
134
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
135
|
-
await wait(1)
|
|
136
|
-
expect(sr.initialized).toEqual(false)
|
|
137
|
-
expect(sr.recording).toEqual(false)
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
test('Does not run if session_trace is disabled', async () => {
|
|
141
|
-
setConfiguration(agentIdentifier, { ...init, session_trace: { enabled: false } })
|
|
142
|
-
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
|
|
143
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
144
|
-
await wait(1)
|
|
145
|
-
expect(sr.initialized).toEqual(false)
|
|
146
|
-
expect(sr.recording).toEqual(false)
|
|
147
|
-
})
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
describe('Session Replay Sample -> Mode Behaviors', () => {
|
|
151
|
-
test('New Session -- Full 1 Error 1 === FULL', async () => {
|
|
152
|
-
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 100, sampling_rate: 100 } })
|
|
153
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
154
|
-
await wait(1)
|
|
155
|
-
expect(sr.mode).toEqual(MODE.FULL)
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
test('New Session -- Full 1 Error 0 === FULL', async () => {
|
|
159
|
-
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 0, sampling_rate: 100 } })
|
|
160
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
161
|
-
await wait(1)
|
|
162
|
-
expect(sr.mode).toEqual(MODE.FULL)
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
test('New Session -- Full 0 Error 1 === ERROR', async () => {
|
|
166
|
-
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 100, sampling_rate: 0 } })
|
|
167
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
168
|
-
await wait(1)
|
|
169
|
-
expect(sr.mode).toEqual(MODE.ERROR)
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
test('New Session -- Full 0 Error 0 === OFF', async () => {
|
|
173
|
-
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 0, sampling_rate: 0 } })
|
|
174
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
175
|
-
await wait(1)
|
|
176
|
-
expect(sr.mode).toEqual(MODE.OFF)
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
test('Existing Session -- Should inherit mode from session entity and ignore samples', async () => {
|
|
180
|
-
const storage = new LocalMemory({ NRBA_SESSION: { value: 'abcdefghijklmnop', expiresAt: Date.now() + 10000, inactiveAt: Date.now() + 10000, updatedAt: Date.now(), sessionReplay: MODE.FULL, sessionTraceMode: MODE.FULL, custom: {} } })
|
|
181
|
-
session = new SessionEntity({ agentIdentifier, key: 'SESSION', storage })
|
|
182
|
-
expect(session.isNew).toBeFalsy()
|
|
183
|
-
primeSessionAndReplay(session)
|
|
184
|
-
// configure to get "error" sample ---> but should inherit FULL from session manager
|
|
185
|
-
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 100, sampling_rate: 0 } })
|
|
186
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
187
|
-
await wait(1)
|
|
188
|
-
expect(sr.mode).toEqual(MODE.FULL)
|
|
189
|
-
})
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
describe('Session Replay Error Mode Behaviors', () => {
|
|
193
|
-
test('An error BEFORE rrweb import starts running in FULL from beginning', async () => {
|
|
194
|
-
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 100, sampling_rate: 0 } })
|
|
195
|
-
sr.ee.emit('errorAgg')
|
|
196
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
197
|
-
await wait(1)
|
|
198
|
-
expect(sr.mode).toEqual(MODE.FULL)
|
|
199
|
-
expect(sr.scheduler.started).toEqual(true)
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
test('An error AFTER rrweb import changes mode and starts harvester', async () => {
|
|
203
|
-
setConfiguration(agentIdentifier, { session_replay: { error_sampling_rate: 100, sampling_rate: 0 } })
|
|
204
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
205
|
-
await wait(1)
|
|
206
|
-
expect(sr.mode).toEqual(MODE.ERROR)
|
|
207
|
-
expect(sr.scheduler.started).toEqual(false)
|
|
208
|
-
sr.ee.emit('errorAgg')
|
|
209
|
-
expect(sr.mode).toEqual(MODE.FULL)
|
|
210
|
-
expect(sr.scheduler.started).toEqual(true)
|
|
211
|
-
})
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
describe('Session Replay Payload Validation', () => {
|
|
215
|
-
test('Payload', async () => {
|
|
216
|
-
setConfiguration(agentIdentifier, { ...init })
|
|
217
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
218
|
-
await wait(1)
|
|
219
|
-
const harvestContents = sr.getHarvestContents()
|
|
220
|
-
// query attrs
|
|
221
|
-
expect(harvestContents.qs).toMatchObject(anyQuery)
|
|
222
|
-
|
|
223
|
-
expect(harvestContents.body).toEqual(expect.any(Array))
|
|
224
|
-
|
|
225
|
-
expect(harvestContents.body.length).toBeGreaterThan(0)
|
|
226
|
-
})
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
describe('Session Replay Harvest Behaviors', () => {
|
|
230
|
-
test('Compressed payload is provided to harvester', async () => {
|
|
231
|
-
const { gunzipSync, strFromU8 } = await import('fflate')
|
|
232
|
-
setConfiguration(agentIdentifier, { ...init })
|
|
233
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
234
|
-
await wait(1)
|
|
235
|
-
const [harvestContents] = sr.prepareHarvest()
|
|
236
|
-
expect(harvestContents.qs).toMatchObject(anyQuery)
|
|
237
|
-
expect(harvestContents.qs.attributes.includes('content_encoding=gzip')).toEqual(true)
|
|
238
|
-
expect(harvestContents.body).toEqual(expect.any(Uint8Array))
|
|
239
|
-
expect(JSON.parse(strFromU8(gunzipSync(harvestContents.body)))).toMatchObject(expect.any(Array))
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
test('Uncompressed payload is provided to harvester', async () => {
|
|
243
|
-
jest.doMock('fflate', () => ({
|
|
244
|
-
__esModule: true,
|
|
245
|
-
gzipSync: jest.fn().mockImplementation(() => { throw new Error() })
|
|
246
|
-
}))
|
|
247
|
-
|
|
248
|
-
setConfiguration(agentIdentifier, { ...init })
|
|
249
|
-
sr.shouldCompress = false
|
|
250
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
251
|
-
await wait(1)
|
|
252
|
-
|
|
253
|
-
const [harvestContents] = sr.prepareHarvest()
|
|
254
|
-
expect(harvestContents.qs).toMatchObject({
|
|
255
|
-
protocol_version: '0',
|
|
256
|
-
browser_monitoring_key: info.licenseKey
|
|
257
|
-
})
|
|
258
|
-
expect(harvestContents.qs.attributes.includes('content_encoding')).toEqual(false)
|
|
259
|
-
expect(harvestContents.body).toEqual(expect.any(Array))
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
test('Clears the event buffer when staged for harvesting', async () => {
|
|
263
|
-
setConfiguration(agentIdentifier, { ...init })
|
|
264
|
-
sr.shouldCompress = false
|
|
265
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
266
|
-
await wait(1)
|
|
267
|
-
|
|
268
|
-
sr.prepareHarvest()
|
|
269
|
-
expect(sr.events.length).toEqual(0)
|
|
270
|
-
})
|
|
271
|
-
|
|
272
|
-
test('Harvests early if exceeds limit', async () => {
|
|
273
|
-
let after = 0
|
|
274
|
-
const spy = jest.spyOn(sr.scheduler, 'runHarvest').mockImplementation(() => { after = Date.now() })
|
|
275
|
-
setConfiguration(agentIdentifier, { ...init })
|
|
276
|
-
sr.payloadBytesEstimation = IDEAL_PAYLOAD_SIZE / AVG_COMPRESSION
|
|
277
|
-
const before = Date.now()
|
|
278
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
279
|
-
await wait(1)
|
|
280
|
-
expect(spy).toHaveBeenCalled()
|
|
281
|
-
expect(after - before).toBeLessThan(sr.harvestTimeSeconds * 1000)
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
test('Aborts if exceeds total limit', async () => {
|
|
285
|
-
const spy = jest.spyOn(sr.scheduler, 'runHarvest')
|
|
286
|
-
setConfiguration(agentIdentifier, { ...init })
|
|
287
|
-
sr.payloadBytesEstimation = (MAX_PAYLOAD_SIZE + 1) / AVG_COMPRESSION
|
|
288
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
289
|
-
await wait(1)
|
|
290
|
-
expect(spy).not.toHaveBeenCalled()
|
|
291
|
-
expect(sr.blocked).toEqual(true)
|
|
292
|
-
expect(sr.mode).toEqual(MODE.OFF)
|
|
293
|
-
})
|
|
294
|
-
|
|
295
|
-
test('Aborts if 429 response', async () => {
|
|
296
|
-
setConfiguration(agentIdentifier, { ...init })
|
|
297
|
-
sr.ee.emit('rumresp-sr', [true])
|
|
298
|
-
await wait(1)
|
|
299
|
-
expect(sr.mode).toEqual(MODE.FULL)
|
|
300
|
-
sr.onHarvestFinished({ status: 429 })
|
|
301
|
-
expect(sr.blocked).toEqual(true)
|
|
302
|
-
expect(sr.mode).toEqual(MODE.OFF)
|
|
303
|
-
})
|
|
304
|
-
})
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
function wait (ms = 0) {
|
|
308
|
-
return new Promise((resolve) => {
|
|
309
|
-
setTimeout(resolve, ms)
|
|
310
|
-
})
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
function primeSessionAndReplay (sess = new SessionEntity({ agentIdentifier, key: 'SESSION', storage: new LocalMemory() })) {
|
|
314
|
-
session = sess
|
|
315
|
-
configure(agentIdentifier, { info, runtime: { session }, init: {} }, 'test', true)
|
|
316
|
-
sr = new SessionReplayAgg(agentIdentifier, new Aggregator({}))
|
|
317
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { faker } from '@faker-js/faker'
|
|
2
|
-
import { InteractionNode } from './interaction-node'
|
|
3
|
-
|
|
4
|
-
test('finishing node with cancelled parent should not throw an error', () => {
|
|
5
|
-
const interaction = {
|
|
6
|
-
remaining: 0,
|
|
7
|
-
onNodeAdded: jest.fn(),
|
|
8
|
-
checkFinish: jest.fn()
|
|
9
|
-
}
|
|
10
|
-
const interactionRootNode = new InteractionNode(interaction, null, 'interaction', faker.date.past().getUTCSeconds())
|
|
11
|
-
const interactionNode = interactionRootNode.child('test', faker.date.past().getUTCSeconds(), 'test', false)
|
|
12
|
-
|
|
13
|
-
interactionRootNode.cancel()
|
|
14
|
-
interactionNode.finish()
|
|
15
|
-
|
|
16
|
-
expect(interactionRootNode.children.length).toEqual(0)
|
|
17
|
-
})
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import { faker } from '@faker-js/faker'
|
|
2
|
-
|
|
3
|
-
let agentIdentifier
|
|
4
|
-
let agentSession
|
|
5
|
-
let mockEE
|
|
6
|
-
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
agentIdentifier = faker.datatype.uuid()
|
|
9
|
-
mockEE = { [faker.datatype.uuid()]: faker.lorem.sentence() }
|
|
10
|
-
agentSession = {
|
|
11
|
-
state: {
|
|
12
|
-
[faker.datatype.uuid()]: faker.lorem.sentence()
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
jest.doMock('../../common/config/config', () => ({
|
|
17
|
-
__esModule: true,
|
|
18
|
-
getConfigurationValue: jest.fn(),
|
|
19
|
-
getConfiguration: jest.fn().mockImplementation(() => ({})),
|
|
20
|
-
getInfo: jest.fn(),
|
|
21
|
-
getRuntime: jest.fn().mockImplementation(() => ({ session: agentSession })),
|
|
22
|
-
setInfo: jest.fn()
|
|
23
|
-
}))
|
|
24
|
-
jest.doMock('../../common/drain/drain', () => ({
|
|
25
|
-
__esModule: true,
|
|
26
|
-
drain: jest.fn()
|
|
27
|
-
}))
|
|
28
|
-
jest.doMock('../../common/event-emitter/contextual-ee', () => ({
|
|
29
|
-
__esModule: true,
|
|
30
|
-
ee: {
|
|
31
|
-
get: jest.fn().mockReturnValue(mockEE)
|
|
32
|
-
}
|
|
33
|
-
}))
|
|
34
|
-
jest.mock('../../common/event-emitter/register-handler', () => ({
|
|
35
|
-
__esModule: true,
|
|
36
|
-
registerHandler: jest.fn()
|
|
37
|
-
}))
|
|
38
|
-
jest.doMock('../../common/constants/runtime', () => ({
|
|
39
|
-
__esModule: true,
|
|
40
|
-
isBrowserScope: true
|
|
41
|
-
}))
|
|
42
|
-
jest.doMock('../../common/session/session-entity', () => ({
|
|
43
|
-
__esModule: true,
|
|
44
|
-
SessionEntity: jest.fn().mockReturnValue({
|
|
45
|
-
state: agentSession.state,
|
|
46
|
-
syncCustomAttribute: jest.fn()
|
|
47
|
-
})
|
|
48
|
-
}))
|
|
49
|
-
jest.doMock('../../common/storage/local-storage.js', () => ({
|
|
50
|
-
__esModule: true,
|
|
51
|
-
LocalStorage: jest.fn()
|
|
52
|
-
}))
|
|
53
|
-
jest.doMock('../../common/storage/first-party-cookies', () => ({
|
|
54
|
-
__esModule: true,
|
|
55
|
-
FirstPartyCookies: jest.fn()
|
|
56
|
-
}))
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
afterEach(() => {
|
|
60
|
-
jest.resetModules()
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
test('should register handlers and drain the session feature', async () => {
|
|
64
|
-
const { setupAgentSession } = await import('./agent-session')
|
|
65
|
-
const result = setupAgentSession(agentIdentifier)
|
|
66
|
-
|
|
67
|
-
const { registerHandler } = await import('../../common/event-emitter/register-handler')
|
|
68
|
-
const { drain } = await import('../../common/drain/drain')
|
|
69
|
-
|
|
70
|
-
expect(result).toEqual(expect.objectContaining(agentSession))
|
|
71
|
-
expect(registerHandler).toHaveBeenNthCalledWith(1, 'api-setCustomAttribute', expect.any(Function), 'session', mockEE)
|
|
72
|
-
expect(registerHandler).toHaveBeenNthCalledWith(2, 'api-setUserId', expect.any(Function), 'session', mockEE)
|
|
73
|
-
expect(drain).toHaveBeenCalledWith(agentIdentifier, 'session')
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
test('should not drain the session feature more than once', async () => {
|
|
77
|
-
const { setupAgentSession } = await import('./agent-session')
|
|
78
|
-
const result1 = setupAgentSession(agentIdentifier)
|
|
79
|
-
const result2 = setupAgentSession(agentIdentifier)
|
|
80
|
-
|
|
81
|
-
const { registerHandler } = await import('../../common/event-emitter/register-handler')
|
|
82
|
-
const { drain } = await import('../../common/drain/drain')
|
|
83
|
-
|
|
84
|
-
expect(result1).toEqual(expect.objectContaining(agentSession))
|
|
85
|
-
expect(result2).toEqual(expect.objectContaining(agentSession))
|
|
86
|
-
expect(registerHandler).toHaveBeenCalledTimes(2)
|
|
87
|
-
expect(drain).toHaveBeenCalledTimes(1)
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
test('should use the local storage class and instantiate a new session when cookies are enabled', async () => {
|
|
91
|
-
const expiresMs = faker.datatype.number()
|
|
92
|
-
const inactiveMs = faker.datatype.number()
|
|
93
|
-
const { getConfiguration } = await import('../../common/config/config')
|
|
94
|
-
jest.mocked(getConfiguration).mockReturnValue({
|
|
95
|
-
session: {
|
|
96
|
-
expiresMs,
|
|
97
|
-
inactiveMs
|
|
98
|
-
}
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
const { LocalStorage } = await import('../../common/storage/local-storage')
|
|
102
|
-
const { SessionEntity } = await import('../../common/session/session-entity')
|
|
103
|
-
const { setupAgentSession } = await import('./agent-session')
|
|
104
|
-
setupAgentSession(agentIdentifier)
|
|
105
|
-
|
|
106
|
-
expect(LocalStorage).toHaveBeenCalledTimes(1)
|
|
107
|
-
expect(SessionEntity).toHaveBeenCalledWith({
|
|
108
|
-
agentIdentifier,
|
|
109
|
-
key: 'SESSION',
|
|
110
|
-
storage: expect.any(LocalStorage),
|
|
111
|
-
expiresMs,
|
|
112
|
-
inactiveMs
|
|
113
|
-
})
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
test('should use the first party cookie storage class and instantiate a new session when cookies are enabled and a domain is set', async () => {
|
|
117
|
-
const expiresMs = faker.datatype.number()
|
|
118
|
-
const inactiveMs = faker.datatype.number()
|
|
119
|
-
const domain = faker.internet.domainName()
|
|
120
|
-
const { getConfiguration } = await import('../../common/config/config')
|
|
121
|
-
jest.mocked(getConfiguration).mockReturnValue({
|
|
122
|
-
session: {
|
|
123
|
-
expiresMs,
|
|
124
|
-
inactiveMs,
|
|
125
|
-
domain
|
|
126
|
-
}
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
const { FirstPartyCookies } = await import('../../common/storage/first-party-cookies')
|
|
130
|
-
const { SessionEntity } = await import('../../common/session/session-entity')
|
|
131
|
-
const { setupAgentSession } = await import('./agent-session')
|
|
132
|
-
setupAgentSession(agentIdentifier)
|
|
133
|
-
|
|
134
|
-
expect(FirstPartyCookies).toHaveBeenCalledTimes(1)
|
|
135
|
-
expect(SessionEntity).toHaveBeenCalledWith({
|
|
136
|
-
agentIdentifier,
|
|
137
|
-
key: 'SESSION',
|
|
138
|
-
storage: expect.any(FirstPartyCookies),
|
|
139
|
-
expiresMs,
|
|
140
|
-
inactiveMs
|
|
141
|
-
})
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
test('should set custom session data', async () => {
|
|
145
|
-
const { getInfo } = await import('../../common/config/config')
|
|
146
|
-
const agentInfo = {
|
|
147
|
-
[faker.datatype.uuid()]: faker.lorem.sentence(),
|
|
148
|
-
jsAttributes: {
|
|
149
|
-
[faker.datatype.uuid()]: faker.lorem.sentence()
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
jest.mocked(getInfo).mockReturnValue(agentInfo)
|
|
153
|
-
agentSession.state.custom = {
|
|
154
|
-
[faker.datatype.uuid()]: faker.lorem.sentence()
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const { setupAgentSession } = await import('./agent-session')
|
|
158
|
-
setupAgentSession(agentIdentifier)
|
|
159
|
-
|
|
160
|
-
expect(agentInfo.jsAttributes).toEqual(expect.objectContaining(agentSession.state.custom))
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
test('should not set custom session data in worker scope', async () => {
|
|
164
|
-
const globalScope = await import('../../common/constants/runtime')
|
|
165
|
-
jest.replaceProperty(globalScope, 'isBrowserScope', false)
|
|
166
|
-
|
|
167
|
-
const { setInfo } = await import('../../common/config/config')
|
|
168
|
-
const { setupAgentSession } = await import('./agent-session')
|
|
169
|
-
setupAgentSession(agentIdentifier)
|
|
170
|
-
|
|
171
|
-
expect(setInfo).not.toHaveBeenCalled()
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
test('should sync custom attributes', async () => {
|
|
175
|
-
const { registerHandler } = await import('../../common/event-emitter/register-handler')
|
|
176
|
-
agentSession.syncCustomAttribute = jest.fn()
|
|
177
|
-
|
|
178
|
-
const customProps = [
|
|
179
|
-
[faker.date.recent(), faker.datatype.uuid(), faker.lorem.sentence()],
|
|
180
|
-
[faker.date.recent(), faker.datatype.uuid(), faker.lorem.sentence()]
|
|
181
|
-
]
|
|
182
|
-
|
|
183
|
-
const { setupAgentSession } = await import('./agent-session')
|
|
184
|
-
const retVal = setupAgentSession(agentIdentifier)
|
|
185
|
-
|
|
186
|
-
const setCustomAttributeHandler = jest.mocked(registerHandler).mock.calls[0][1]
|
|
187
|
-
const setUserIdHandler = jest.mocked(registerHandler).mock.calls[1][1]
|
|
188
|
-
|
|
189
|
-
setCustomAttributeHandler(...customProps[0])
|
|
190
|
-
setUserIdHandler(...customProps[1])
|
|
191
|
-
|
|
192
|
-
expect(retVal.syncCustomAttribute).toHaveBeenNthCalledWith(1, customProps[0][1], customProps[0][2])
|
|
193
|
-
expect(retVal.syncCustomAttribute).toHaveBeenNthCalledWith(2, customProps[1][1], customProps[1][2])
|
|
194
|
-
})
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { faker } from '@faker-js/faker'
|
|
2
|
-
import { AggregateBase } from './aggregate-base'
|
|
3
|
-
import { registerHandler } from '../../common/event-emitter/register-handler'
|
|
4
|
-
import { getInfo, isConfigured, getRuntime } from '../../common/config/config'
|
|
5
|
-
import { configure } from '../../loaders/configure/configure'
|
|
6
|
-
import { gosCDN } from '../../common/window/nreum'
|
|
7
|
-
|
|
8
|
-
jest.enableAutomock()
|
|
9
|
-
jest.unmock('./aggregate-base')
|
|
10
|
-
jest.mock('./feature-base', () => ({
|
|
11
|
-
__esModule: true,
|
|
12
|
-
FeatureBase: jest.fn(function (...args) {
|
|
13
|
-
this.agentIdentifier = args[0]
|
|
14
|
-
this.aggregator = args[1]
|
|
15
|
-
this.featureName = args[2]
|
|
16
|
-
})
|
|
17
|
-
}))
|
|
18
|
-
jest.mock('../../common/event-emitter/register-handler', () => ({
|
|
19
|
-
__esModule: true,
|
|
20
|
-
registerHandler: jest.fn()
|
|
21
|
-
}))
|
|
22
|
-
jest.mock('../../common/config/config', () => ({
|
|
23
|
-
__esModule: true,
|
|
24
|
-
getInfo: jest.fn(),
|
|
25
|
-
isConfigured: jest.fn().mockReturnValue(false),
|
|
26
|
-
getRuntime: jest.fn()
|
|
27
|
-
}))
|
|
28
|
-
jest.mock('../../loaders/configure/configure', () => ({
|
|
29
|
-
__esModule: true,
|
|
30
|
-
configure: jest.fn()
|
|
31
|
-
}))
|
|
32
|
-
jest.mock('../../common/window/nreum', () => ({
|
|
33
|
-
__esModule: true,
|
|
34
|
-
gosCDN: jest.fn().mockReturnValue({}),
|
|
35
|
-
gosNREUM: jest.fn().mockReturnValue({})
|
|
36
|
-
}))
|
|
37
|
-
|
|
38
|
-
let agentIdentifier
|
|
39
|
-
let aggregator
|
|
40
|
-
let featureName
|
|
41
|
-
|
|
42
|
-
beforeEach(() => {
|
|
43
|
-
agentIdentifier = faker.datatype.uuid()
|
|
44
|
-
aggregator = {}
|
|
45
|
-
featureName = faker.datatype.uuid()
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
test('should merge info, jsattributes, and runtime objects', () => {
|
|
49
|
-
const mockInfo1 = {
|
|
50
|
-
[faker.datatype.uuid()]: faker.lorem.sentence(),
|
|
51
|
-
jsAttributes: {
|
|
52
|
-
[faker.datatype.uuid()]: faker.lorem.sentence()
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
jest.mocked(gosCDN).mockReturnValue({ info: mockInfo1 })
|
|
56
|
-
|
|
57
|
-
const mockInfo2 = {
|
|
58
|
-
jsAttributes: {
|
|
59
|
-
[faker.datatype.uuid()]: faker.lorem.sentence()
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
jest.mocked(getInfo).mockReturnValue(mockInfo2)
|
|
63
|
-
|
|
64
|
-
const mockRuntime = {
|
|
65
|
-
[faker.datatype.uuid()]: faker.lorem.sentence()
|
|
66
|
-
}
|
|
67
|
-
jest.mocked(getRuntime).mockReturnValue(mockRuntime)
|
|
68
|
-
|
|
69
|
-
new AggregateBase(agentIdentifier, aggregator, featureName)
|
|
70
|
-
|
|
71
|
-
expect(isConfigured).toHaveBeenCalledWith(agentIdentifier)
|
|
72
|
-
expect(gosCDN).toHaveBeenCalledTimes(3)
|
|
73
|
-
expect(getInfo).toHaveBeenCalledWith(agentIdentifier)
|
|
74
|
-
expect(getRuntime).toHaveBeenCalledWith(agentIdentifier)
|
|
75
|
-
expect(configure).toHaveBeenCalledWith(agentIdentifier, {
|
|
76
|
-
info: {
|
|
77
|
-
...mockInfo1,
|
|
78
|
-
jsAttributes: {
|
|
79
|
-
...mockInfo1.jsAttributes,
|
|
80
|
-
...mockInfo2.jsAttributes
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
runtime: mockRuntime
|
|
84
|
-
})
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
test('should only configure the agent once', () => {
|
|
88
|
-
jest.mocked(isConfigured).mockReturnValue(true)
|
|
89
|
-
|
|
90
|
-
new AggregateBase(agentIdentifier, aggregator, featureName)
|
|
91
|
-
|
|
92
|
-
expect(isConfigured).toHaveBeenCalledWith(agentIdentifier)
|
|
93
|
-
expect(gosCDN).not.toHaveBeenCalled()
|
|
94
|
-
expect(getInfo).not.toHaveBeenCalled()
|
|
95
|
-
expect(getRuntime).not.toHaveBeenCalled()
|
|
96
|
-
expect(configure).not.toHaveBeenCalled()
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
test('should resolve waitForFlags correctly based on flags', async () => {
|
|
100
|
-
const flagNames = [faker.datatype.uuid(), faker.datatype.uuid()]
|
|
101
|
-
const aggregateBase = new AggregateBase(agentIdentifier, aggregator, featureName)
|
|
102
|
-
aggregateBase.ee = {
|
|
103
|
-
[faker.datatype.uuid()]: faker.lorem.sentence()
|
|
104
|
-
}
|
|
105
|
-
aggregateBase.feature = {
|
|
106
|
-
[faker.datatype.uuid()]: faker.lorem.sentence()
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const flagWait = aggregateBase.waitForFlags(flagNames)
|
|
110
|
-
jest.mocked(registerHandler).mock.calls[0][1](true)
|
|
111
|
-
jest.mocked(registerHandler).mock.calls[1][1](false)
|
|
112
|
-
|
|
113
|
-
expect(registerHandler).toHaveBeenCalledWith(`rumresp-${flagNames[0]}`, expect.any(Function), featureName, aggregateBase.ee)
|
|
114
|
-
expect(registerHandler).toHaveBeenCalledWith(`rumresp-${flagNames[1]}`, expect.any(Function), featureName, aggregateBase.ee)
|
|
115
|
-
await expect(flagWait).resolves.toEqual([true, false])
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
test('should not register any handlers when flagNames is empty', async () => {
|
|
119
|
-
const aggregateBase = new AggregateBase(agentIdentifier, aggregator, featureName)
|
|
120
|
-
|
|
121
|
-
await expect(aggregateBase.waitForFlags()).resolves.toEqual([])
|
|
122
|
-
expect(registerHandler).not.toHaveBeenCalled()
|
|
123
|
-
})
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { faker } from '@faker-js/faker'
|
|
2
|
-
import { FeatureBase } from './feature-base'
|
|
3
|
-
import { getRuntime } from '../../common/config/config'
|
|
4
|
-
import { ee } from '../../common/event-emitter/contextual-ee'
|
|
5
|
-
|
|
6
|
-
jest.enableAutomock()
|
|
7
|
-
jest.unmock('./feature-base')
|
|
8
|
-
jest.mock('../../common/config/config', () => ({
|
|
9
|
-
__esModule: true,
|
|
10
|
-
getRuntime: jest.fn().mockReturnValue({
|
|
11
|
-
isolatedBacklog: true
|
|
12
|
-
})
|
|
13
|
-
}))
|
|
14
|
-
jest.mock('../../common/event-emitter/contextual-ee', () => ({
|
|
15
|
-
__esModule: true,
|
|
16
|
-
ee: {
|
|
17
|
-
get: jest.fn()
|
|
18
|
-
}
|
|
19
|
-
}))
|
|
20
|
-
|
|
21
|
-
let agentIdentifier
|
|
22
|
-
let aggregator
|
|
23
|
-
let featureName
|
|
24
|
-
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
agentIdentifier = faker.datatype.uuid()
|
|
27
|
-
aggregator = {}
|
|
28
|
-
featureName = faker.datatype.uuid()
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('should set instance defaults', () => {
|
|
32
|
-
const mockEE = { [faker.datatype.uuid()]: faker.lorem.sentence() }
|
|
33
|
-
jest.mocked(ee.get).mockReturnValue(mockEE)
|
|
34
|
-
|
|
35
|
-
const feature = new FeatureBase(agentIdentifier, aggregator, featureName)
|
|
36
|
-
|
|
37
|
-
expect(feature.agentIdentifier).toEqual(agentIdentifier)
|
|
38
|
-
expect(feature.aggregator).toEqual(aggregator)
|
|
39
|
-
expect(feature.featureName).toEqual(featureName)
|
|
40
|
-
expect(feature.blocked).toEqual(false)
|
|
41
|
-
expect(feature.ee).toEqual(mockEE)
|
|
42
|
-
|
|
43
|
-
expect(getRuntime).toHaveBeenCalledWith(agentIdentifier)
|
|
44
|
-
expect(ee.get).toHaveBeenCalledWith(agentIdentifier, true)
|
|
45
|
-
})
|