@newrelic/browser-agent 1.237.0 → 1.238.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/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/deny-list/deny-list.js +1 -1
- package/dist/cjs/common/event-emitter/contextual-ee.js +18 -26
- package/dist/cjs/common/event-emitter/event-context.js +12 -0
- package/dist/cjs/common/harvest/harvest.js +4 -4
- package/dist/cjs/common/ids/bundle-id.js +19 -0
- package/dist/cjs/common/wrap/wrap-events.js +3 -2
- package/dist/cjs/common/wrap/wrap-fetch.js +1 -3
- package/dist/cjs/common/wrap/wrap-function.js +15 -46
- package/dist/cjs/common/wrap/wrap-mutation.js +1 -2
- package/dist/cjs/common/wrap/wrap-promise.js +2 -3
- package/dist/cjs/common/wrap/wrap-xhr.js +23 -27
- package/dist/cjs/features/ajax/instrument/distributed-tracing.js +0 -4
- package/dist/cjs/features/ajax/instrument/index.js +19 -9
- package/dist/cjs/features/metrics/aggregate/index.js +8 -0
- package/dist/cjs/features/session_replay/aggregate/index.js +2 -0
- package/dist/cjs/features/session_trace/aggregate/index.js +14 -8
- package/dist/cjs/features/spa/aggregate/index.js +1 -1
- package/dist/cjs/loaders/agent-base.js +12 -0
- package/dist/cjs/loaders/api/api.js +14 -1
- package/dist/cjs/loaders/api/interaction-types.js +11 -4
- package/dist/cjs/loaders/configure/configure.js +2 -1
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/deny-list/deny-list.js +1 -1
- package/dist/esm/common/event-emitter/contextual-ee.js +16 -23
- package/dist/esm/common/event-emitter/event-context.js +5 -0
- package/dist/esm/common/harvest/harvest.js +4 -4
- package/dist/esm/common/ids/bundle-id.js +13 -0
- package/dist/esm/common/wrap/wrap-events.js +4 -3
- package/dist/esm/common/wrap/wrap-fetch.js +2 -4
- package/dist/esm/common/wrap/wrap-function.js +15 -44
- package/dist/esm/common/wrap/wrap-mutation.js +2 -3
- package/dist/esm/common/wrap/wrap-promise.js +3 -4
- package/dist/esm/common/wrap/wrap-xhr.js +23 -27
- package/dist/esm/features/ajax/instrument/distributed-tracing.js +0 -4
- package/dist/esm/features/ajax/instrument/index.js +20 -10
- package/dist/esm/features/metrics/aggregate/index.js +8 -0
- package/dist/esm/features/session_replay/aggregate/index.js +2 -0
- package/dist/esm/features/session_trace/aggregate/index.js +14 -8
- package/dist/esm/features/spa/aggregate/index.js +1 -1
- package/dist/esm/loaders/agent-base.js +12 -0
- package/dist/esm/loaders/api/api.js +14 -1
- package/dist/esm/loaders/api/interaction-types.js +11 -4
- package/dist/esm/loaders/configure/configure.js +3 -2
- package/dist/types/common/event-emitter/contextual-ee.d.ts +22 -2
- package/dist/types/common/event-emitter/contextual-ee.d.ts.map +1 -1
- package/dist/types/common/event-emitter/event-context.d.ts +5 -0
- package/dist/types/common/event-emitter/event-context.d.ts.map +1 -0
- package/dist/types/common/event-emitter/register-handler.d.ts +1 -1
- package/dist/types/common/harvest/harvest.d.ts.map +1 -1
- package/dist/types/common/ids/bundle-id.d.ts +5 -0
- package/dist/types/common/ids/bundle-id.d.ts.map +1 -0
- package/dist/types/common/session/session-entity.d.ts +6 -6
- package/dist/types/common/window/nreum.d.ts +2 -2
- package/dist/types/common/wrap/wrap-events.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-fetch.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-function.d.ts +1 -19
- package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-mutation.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-promise.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-xhr.d.ts.map +1 -1
- package/dist/types/features/ajax/instrument/distributed-tracing.d.ts +1 -1
- package/dist/types/features/ajax/instrument/distributed-tracing.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/endpoint-map.d.ts +5 -5
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts +5 -0
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/loaders/agent-base.d.ts +9 -0
- package/dist/types/loaders/agent-base.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts +6 -0
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/dist/types/loaders/api/interaction-types.d.ts +18 -7
- package/dist/types/loaders/api/interaction-types.d.ts.map +1 -1
- package/dist/types/loaders/configure/configure.d.ts +1 -0
- package/dist/types/loaders/configure/configure.d.ts.map +1 -1
- package/dist/types/loaders/features/features.d.ts +9 -9
- package/dist/types/loaders/micro-agent.d.ts +1 -1
- package/dist/types/loaders/micro-agent.d.ts.map +1 -1
- package/package.json +22 -27
- package/src/common/deny-list/deny-list.js +1 -1
- package/src/common/deny-list/deny-list.test.js +103 -30
- package/src/common/event-emitter/{contextual-ee.test.js → contextual-ee.component-test.js} +15 -32
- package/src/common/event-emitter/contextual-ee.js +20 -31
- package/src/common/event-emitter/event-context.js +5 -0
- package/src/common/harvest/harvest.js +4 -4
- package/src/common/harvest/harvest.test.js +1 -1
- package/src/common/ids/__mocks__/bundle-id.js +2 -0
- package/src/common/ids/__mocks__/unique-id.js +17 -0
- package/src/common/ids/bundle-id.js +13 -0
- package/src/common/url/__mocks__/parse-url.js +15 -0
- package/src/common/util/__mocks__/get-or-set.js +5 -0
- package/src/common/window/__mocks__/nreum.js +10 -0
- package/src/common/wrap/wrap-events.js +4 -3
- package/src/common/wrap/wrap-fetch.js +2 -4
- package/src/common/wrap/wrap-function.js +16 -44
- package/src/common/wrap/wrap-mutation.js +2 -3
- package/src/common/wrap/{wrap-promise.test.js → wrap-promise.component-test.js} +2 -32
- package/src/common/wrap/wrap-promise.js +3 -4
- package/src/common/wrap/wrap-xhr.js +24 -28
- package/src/features/ajax/instrument/distributed-tracing.js +0 -4
- package/src/features/ajax/instrument/distributed-tracing.test.js +375 -0
- package/src/features/ajax/instrument/index.js +22 -10
- package/src/features/metrics/aggregate/index.js +8 -0
- package/src/features/session_replay/aggregate/index.js +2 -0
- package/src/features/session_trace/aggregate/index.js +16 -7
- package/src/features/spa/aggregate/index.js +1 -1
- package/src/loaders/agent-base.js +12 -0
- package/src/loaders/api/api.component-test.js +45 -0
- package/src/loaders/api/api.js +14 -1
- package/src/loaders/api/interaction-types.js +11 -4
- package/src/loaders/configure/configure.js +11 -2
- /package/src/common/url/{encode.component-test.js → encode.test.js} +0 -0
- /package/src/common/url/{protocol.component-test.js → protocol.test.js} +0 -0
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { ee } from '../event-emitter/contextual-ee'
|
|
10
|
-
|
|
10
|
+
import { bundleId } from '../ids/bundle-id'
|
|
11
|
+
|
|
12
|
+
export const flag = `nr@original:${bundleId}`
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* A convenience alias of `hasOwnProperty`.
|
|
@@ -107,26 +109,24 @@ export function createWrapperWithEmitter (emitter, always) {
|
|
|
107
109
|
|
|
108
110
|
/**
|
|
109
111
|
* Creates wrapper functions around each of an array of methods on a specified object.
|
|
110
|
-
* @param {Object} obj
|
|
111
|
-
* @param {string[]} methods
|
|
112
|
-
* @param {string} [prefix='']
|
|
113
|
-
*
|
|
114
|
-
* @param {function|object} [getContext]
|
|
115
|
-
*
|
|
116
|
-
* @param {boolean} [bubble=false]
|
|
117
|
-
*
|
|
112
|
+
* @param {Object} obj The object to which the specified methods belong.
|
|
113
|
+
* @param {string[]} methods An array of method names to be wrapped.
|
|
114
|
+
* @param {string} [prefix=''] A prefix to add to the names of emitted events. If `-` is the first character, also
|
|
115
|
+
* adds the method name before the prefix.
|
|
116
|
+
* @param {function|object} [getContext] The function or object that will serve as the 'this' context for handlers
|
|
117
|
+
* of events emitted by this wrapper.
|
|
118
|
+
* @param {boolean} [bubble=false] If `true`, emitted events should also bubble up to the old emitter upon which
|
|
119
|
+
* the `emitter` in the current scope was based (if it defines one).
|
|
118
120
|
*/
|
|
119
121
|
function inPlace (obj, methods, prefix, getContext, bubble) {
|
|
120
122
|
if (!prefix) prefix = ''
|
|
123
|
+
|
|
121
124
|
// If prefix starts with '-' set this boolean to add the method name to the prefix before passing each one to wrap.
|
|
122
|
-
|
|
123
|
-
var fn
|
|
124
|
-
var method
|
|
125
|
-
var i
|
|
125
|
+
const prependMethodPrefix = (prefix.charAt(0) === '-')
|
|
126
126
|
|
|
127
|
-
for (i = 0; i < methods.length; i++) {
|
|
128
|
-
method = methods[i]
|
|
129
|
-
fn = obj[method]
|
|
127
|
+
for (let i = 0; i < methods.length; i++) {
|
|
128
|
+
const method = methods[i]
|
|
129
|
+
const fn = obj[method]
|
|
130
130
|
|
|
131
131
|
// Unless fn is both wrappable and unwrapped, bail so we don't add extra properties with undefined values.
|
|
132
132
|
if (notWrappable(fn)) continue
|
|
@@ -217,31 +217,3 @@ function copy (from, to, emitter) {
|
|
|
217
217
|
function notWrappable (fn) {
|
|
218
218
|
return !(fn && fn instanceof Function && fn.apply && !fn[flag])
|
|
219
219
|
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Creates a wrapped version of a function using a specified wrapper function. The new wrapped function references the
|
|
223
|
-
* original function. Also copies the properties of the original function to the wrapped function.
|
|
224
|
-
* @param {function} fn - A function to be wrapped.
|
|
225
|
-
* @param {function} wrapper - A higher order function that returns a new function, which executes the function passed
|
|
226
|
-
* to the higher order function as an argument.
|
|
227
|
-
* @returns {function} A wrapped function with an internal reference to the original function.
|
|
228
|
-
*/
|
|
229
|
-
export function wrapFunction (fn, wrapper) {
|
|
230
|
-
var wrapped = wrapper(fn)
|
|
231
|
-
wrapped[flag] = fn
|
|
232
|
-
copy(fn, wrapped, ee)
|
|
233
|
-
return wrapped
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Replaces a function with a wrapped version of itself. To preserve object references, rather than taking the function
|
|
238
|
-
* itself as an argument, takes the object on which the particular named function is a property.
|
|
239
|
-
* @param {Object} obj - The object on which the named function is a property.
|
|
240
|
-
* @param {string} fnName - The name of the function to be wrapped.
|
|
241
|
-
* @param {function} wrapper - A higher order function that returns a new function, which executes the function passed
|
|
242
|
-
* to the higher order function as an argument.
|
|
243
|
-
*/
|
|
244
|
-
export function wrapInPlace (obj, fnName, wrapper) {
|
|
245
|
-
var fn = obj[fnName]
|
|
246
|
-
obj[fnName] = wrapFunction(fn, wrapper)
|
|
247
|
-
}
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { ee as baseEE } from '../event-emitter/contextual-ee'
|
|
11
11
|
import { createWrapperWithEmitter as wfn } from './wrap-function'
|
|
12
|
-
import {
|
|
13
|
-
import { isBrowserScope } from '../constants/runtime'
|
|
12
|
+
import { globalScope, isBrowserScope } from '../constants/runtime'
|
|
14
13
|
|
|
15
14
|
const wrapped = {}
|
|
16
15
|
|
|
@@ -30,7 +29,7 @@ export function wrapMutation (sharedEE) {
|
|
|
30
29
|
wrapped[ee.debugId] = true // otherwise, first feature to wrap mutations
|
|
31
30
|
|
|
32
31
|
var wrapFn = wfn(ee)
|
|
33
|
-
var OriginalObserver =
|
|
32
|
+
var OriginalObserver = globalScope.MutationObserver
|
|
34
33
|
|
|
35
34
|
if (OriginalObserver) {
|
|
36
35
|
window.MutationObserver = function WrappedMutationObserver (cb) {
|
|
@@ -1,35 +1,5 @@
|
|
|
1
1
|
import { faker } from '@faker-js/faker'
|
|
2
2
|
import { globalScope } from '../constants/runtime'
|
|
3
|
-
import { originals } from '../config/config'
|
|
4
|
-
|
|
5
|
-
jest.mock('./wrap-function', () => ({
|
|
6
|
-
__esModule: true,
|
|
7
|
-
createWrapperWithEmitter: jest.fn(() => (fn) => (...args) => {
|
|
8
|
-
return fn.apply(null, args)
|
|
9
|
-
})
|
|
10
|
-
}))
|
|
11
|
-
jest.mock('../event-emitter/contextual-ee', () => ({
|
|
12
|
-
__esModule: true,
|
|
13
|
-
getOrSetContext: jest.fn(() => ({})),
|
|
14
|
-
ee: {
|
|
15
|
-
get: jest.fn((name) => ({
|
|
16
|
-
debugId: name,
|
|
17
|
-
on: jest.fn(),
|
|
18
|
-
context: jest.fn(() => ({})),
|
|
19
|
-
emit: jest.fn()
|
|
20
|
-
}))
|
|
21
|
-
}
|
|
22
|
-
}))
|
|
23
|
-
jest.mock('../config/config', () => ({
|
|
24
|
-
__esModule: true,
|
|
25
|
-
originals: {}
|
|
26
|
-
}))
|
|
27
|
-
jest.mock('../constants/runtime', () => ({
|
|
28
|
-
__esModule: true,
|
|
29
|
-
globalScope: {
|
|
30
|
-
NREUM: {}
|
|
31
|
-
}
|
|
32
|
-
}))
|
|
33
3
|
|
|
34
4
|
let promiseConstructorCalls
|
|
35
5
|
|
|
@@ -38,7 +8,7 @@ beforeEach(async () => {
|
|
|
38
8
|
|
|
39
9
|
// Proxy the global Promise to prevent the wrapping from
|
|
40
10
|
// messing with Jest internal promises
|
|
41
|
-
|
|
11
|
+
window.Promise = new Proxy(class extends Promise {}, {
|
|
42
12
|
construct (target, args) {
|
|
43
13
|
promiseConstructorCalls.push(args)
|
|
44
14
|
|
|
@@ -57,7 +27,7 @@ test('should wrap promise constructor', async () => {
|
|
|
57
27
|
const promiseInstance = new globalScope.Promise(jest.fn())
|
|
58
28
|
|
|
59
29
|
expect(promiseInstance).toBeInstanceOf(Promise)
|
|
60
|
-
expect(promiseConstructorCalls.length).
|
|
30
|
+
expect(promiseConstructorCalls.length).toBeGreaterThan(0)
|
|
61
31
|
expect(globalScope.Promise.toString()).toMatch(/\[native code\]/)
|
|
62
32
|
expect(globalScope.Promise.name).toEqual('Promise')
|
|
63
33
|
})
|
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { createWrapperWithEmitter as wrapFn, flag } from './wrap-function'
|
|
11
|
-
import { ee as baseEE
|
|
12
|
-
import { originals } from '../config/config'
|
|
11
|
+
import { ee as baseEE } from '../event-emitter/contextual-ee'
|
|
13
12
|
import { globalScope } from '../constants/runtime'
|
|
14
13
|
|
|
15
14
|
const wrapped = {}
|
|
@@ -29,9 +28,9 @@ export function wrapPromise (sharedEE) {
|
|
|
29
28
|
if (wrapped[promiseEE.debugId]) return promiseEE
|
|
30
29
|
wrapped[promiseEE.debugId] = true // otherwise, first feature to wrap promise
|
|
31
30
|
|
|
32
|
-
var getContext =
|
|
31
|
+
var getContext = promiseEE.context
|
|
33
32
|
var promiseWrapper = wrapFn(promiseEE)
|
|
34
|
-
var prevPromiseObj =
|
|
33
|
+
var prevPromiseObj = globalScope.Promise
|
|
35
34
|
|
|
36
35
|
if (prevPromiseObj) { // ensure there's a Promise API (native or otherwise) to even wrap
|
|
37
36
|
wrap()
|
|
@@ -11,7 +11,6 @@ import { wrapEvents } from './wrap-events'
|
|
|
11
11
|
import { ee as contextualEE } from '../event-emitter/contextual-ee'
|
|
12
12
|
import { eventListenerOpts } from '../event-listener/event-listener-opts'
|
|
13
13
|
import { createWrapperWithEmitter as wfn } from './wrap-function'
|
|
14
|
-
import { originals } from '../config/config'
|
|
15
14
|
import { globalScope } from '../constants/runtime'
|
|
16
15
|
import { warn } from '../util/console'
|
|
17
16
|
|
|
@@ -37,37 +36,33 @@ export function wrapXhr (sharedEE) {
|
|
|
37
36
|
wrapEvents(baseEE) // wrap-events patches XMLHttpRequest.prototype.addEventListener for us
|
|
38
37
|
var wrapFn = wfn(ee)
|
|
39
38
|
|
|
40
|
-
var OrigXHR =
|
|
41
|
-
var MutationObserver =
|
|
42
|
-
var Promise =
|
|
43
|
-
var setImmediate =
|
|
39
|
+
var OrigXHR = globalScope.XMLHttpRequest
|
|
40
|
+
var MutationObserver = globalScope.MutationObserver
|
|
41
|
+
var Promise = globalScope.Promise
|
|
42
|
+
var setImmediate = globalScope.setInterval
|
|
44
43
|
|
|
45
44
|
var READY_STATE_CHANGE = 'readystatechange'
|
|
46
45
|
|
|
47
46
|
var handlers = ['onload', 'onerror', 'onabort', 'onloadstart', 'onloadend', 'onprogress', 'ontimeout']
|
|
48
47
|
var pendingXhrs = []
|
|
49
48
|
|
|
50
|
-
var activeListeners = globalScope.XMLHttpRequest.listeners
|
|
51
|
-
|
|
52
49
|
var XHR = globalScope.XMLHttpRequest = newXHR
|
|
53
50
|
|
|
54
51
|
function newXHR (opts) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
const xhr = new OrigXHR(opts)
|
|
53
|
+
const context = ee.context(xhr)
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
ee.emit('new-xhr', [xhr], context)
|
|
57
|
+
xhr.addEventListener(READY_STATE_CHANGE, wrapXHR(context), eventListenerOpts(false))
|
|
58
|
+
} catch (e) {
|
|
59
|
+
warn('An error occurred while intercepting XHR', e)
|
|
58
60
|
try {
|
|
59
|
-
ee.emit('
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
warn('An error occured while intercepting XHR', e)
|
|
63
|
-
try {
|
|
64
|
-
ee.emit('internal-error', [e])
|
|
65
|
-
} catch (err) {
|
|
66
|
-
// do nothing
|
|
67
|
-
}
|
|
61
|
+
ee.emit('internal-error', [e])
|
|
62
|
+
} catch (err) {
|
|
63
|
+
// do nothing
|
|
68
64
|
}
|
|
69
65
|
}
|
|
70
|
-
this.listeners.forEach(listener => listener())
|
|
71
66
|
return xhr
|
|
72
67
|
}
|
|
73
68
|
|
|
@@ -87,16 +82,17 @@ export function wrapXhr (sharedEE) {
|
|
|
87
82
|
wrapFn.inPlace(xhr, ['onreadystatechange'], 'fn-', getObject)
|
|
88
83
|
}
|
|
89
84
|
|
|
90
|
-
function wrapXHR () {
|
|
91
|
-
|
|
92
|
-
|
|
85
|
+
function wrapXHR (ctx) {
|
|
86
|
+
return function () {
|
|
87
|
+
var xhr = this
|
|
93
88
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
89
|
+
if (xhr.readyState > 3 && !ctx.resolved) {
|
|
90
|
+
ctx.resolved = true
|
|
91
|
+
ee.emit('xhr-resolved', [], xhr)
|
|
92
|
+
}
|
|
98
93
|
|
|
99
|
-
|
|
94
|
+
wrapFn.inPlace(xhr, handlers, 'fn-', getObject)
|
|
95
|
+
}
|
|
100
96
|
}
|
|
101
97
|
|
|
102
98
|
// Wrapping the onreadystatechange property of XHRs takes some special tricks.
|
|
@@ -11,10 +11,6 @@ import { stringify } from '../../../common/util/stringify'
|
|
|
11
11
|
export class DT {
|
|
12
12
|
constructor (agentIdentifier) {
|
|
13
13
|
this.agentIdentifier = agentIdentifier
|
|
14
|
-
// Binds this class instance context to the following fn used in an external module (exported);
|
|
15
|
-
// Alternatively, can make them class field arrow functions, but requires experimental features/plugin for eslint.
|
|
16
|
-
this.generateTracePayload = this.generateTracePayload.bind(this)
|
|
17
|
-
this.shouldGenerateTrace = this.shouldGenerateTrace.bind(this)
|
|
18
14
|
}
|
|
19
15
|
|
|
20
16
|
generateTracePayload (parsedOrigin) {
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker'
|
|
2
|
+
import { DT } from './distributed-tracing'
|
|
3
|
+
import { getLoaderConfig, getConfiguration, getConfigurationValue } from '../../../common/config/config'
|
|
4
|
+
import * as runtimeModule from '../../../common/constants/runtime'
|
|
5
|
+
|
|
6
|
+
jest.enableAutomock()
|
|
7
|
+
jest.unmock('./distributed-tracing')
|
|
8
|
+
|
|
9
|
+
let agentIdentifier
|
|
10
|
+
let dtInstance
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
agentIdentifier = faker.datatype.uuid()
|
|
14
|
+
dtInstance = new DT(agentIdentifier)
|
|
15
|
+
|
|
16
|
+
jest.mocked(getLoaderConfig).mockReturnValue({
|
|
17
|
+
accountID: '1234',
|
|
18
|
+
agentID: '5678',
|
|
19
|
+
trustKey: '1'
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
jest.clearAllMocks()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('newrelic header has the correct format', () => {
|
|
28
|
+
const agentConfig = {
|
|
29
|
+
distributed_tracing: { enabled: true }
|
|
30
|
+
}
|
|
31
|
+
jest.mocked(getConfiguration).mockReturnValue(agentConfig)
|
|
32
|
+
jest.mocked(getConfigurationValue).mockReturnValue(agentConfig.distributed_tracing)
|
|
33
|
+
|
|
34
|
+
const payload = dtInstance.generateTracePayload({
|
|
35
|
+
sameOrigin: true
|
|
36
|
+
})
|
|
37
|
+
const header = JSON.parse(atob(payload.newrelicHeader))
|
|
38
|
+
|
|
39
|
+
expect(payload.spanId).toEqual(header.d.id)
|
|
40
|
+
expect(payload.traceId).toEqual(header.d.tr)
|
|
41
|
+
expect(payload.timestamp).toEqual(header.d.ti)
|
|
42
|
+
|
|
43
|
+
const loaderConfig = getLoaderConfig()
|
|
44
|
+
expect(header.d.ty).toEqual('Browser')
|
|
45
|
+
expect(header.d.ac).toEqual(loaderConfig.accountID)
|
|
46
|
+
expect(header.d.ap).toEqual(loaderConfig.agentID)
|
|
47
|
+
expect(header.d.tk).toEqual(loaderConfig.trustKey)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('newrelic header is not generated for same-origin calls when disabled in configuration', () => {
|
|
51
|
+
const agentConfig = {
|
|
52
|
+
distributed_tracing: {
|
|
53
|
+
enabled: true,
|
|
54
|
+
exclude_newrelic_header: true
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
jest.mocked(getConfiguration).mockReturnValue(agentConfig)
|
|
58
|
+
jest.mocked(getConfigurationValue).mockReturnValue(agentConfig.distributed_tracing)
|
|
59
|
+
|
|
60
|
+
const payload = dtInstance.generateTracePayload({
|
|
61
|
+
sameOrigin: true
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
expect(payload.newrelicHeader).toBeUndefined()
|
|
65
|
+
expect(typeof payload.traceContextParentHeader).toEqual('string')
|
|
66
|
+
expect(typeof payload.traceContextStateHeader).toEqual('string')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('newrelic header is added to cross-origin calls by default', () => {
|
|
70
|
+
const agentConfig = {
|
|
71
|
+
distributed_tracing: {
|
|
72
|
+
enabled: true,
|
|
73
|
+
allowed_origins: ['https://someotherdomain.com']
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
jest.mocked(getConfiguration).mockReturnValue(agentConfig)
|
|
77
|
+
jest.mocked(getConfigurationValue).mockReturnValue(agentConfig.distributed_tracing)
|
|
78
|
+
|
|
79
|
+
const payload = dtInstance.generateTracePayload({
|
|
80
|
+
sameOrigin: false,
|
|
81
|
+
hostname: 'someotherdomain.com',
|
|
82
|
+
protocol: 'https',
|
|
83
|
+
port: '443'
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
expect(payload.newrelicHeader).not.toBeUndefined()
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test('newrelic header is added to cross-origin calls when enabled in configuration', () => {
|
|
90
|
+
const agentConfig = {
|
|
91
|
+
distributed_tracing: {
|
|
92
|
+
enabled: true,
|
|
93
|
+
allowed_origins: ['https://someotherdomain.com'],
|
|
94
|
+
cors_use_newrelic_header: true
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
jest.mocked(getConfiguration).mockReturnValue(agentConfig)
|
|
98
|
+
jest.mocked(getConfigurationValue).mockReturnValue(agentConfig.distributed_tracing)
|
|
99
|
+
|
|
100
|
+
const payload = dtInstance.generateTracePayload({
|
|
101
|
+
sameOrigin: false,
|
|
102
|
+
hostname: 'someotherdomain.com',
|
|
103
|
+
protocol: 'https',
|
|
104
|
+
port: '443'
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
expect(payload.newrelicHeader).not.toBeUndefined()
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('newrelic header is not added to cross-origin calls when disabled in configuration', () => {
|
|
111
|
+
const agentConfig = {
|
|
112
|
+
distributed_tracing: {
|
|
113
|
+
enabled: true,
|
|
114
|
+
allowed_origins: ['https://someotherdomain.com'],
|
|
115
|
+
cors_use_newrelic_header: false
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
jest.mocked(getConfiguration).mockReturnValue(agentConfig)
|
|
119
|
+
jest.mocked(getConfigurationValue).mockReturnValue(agentConfig.distributed_tracing)
|
|
120
|
+
|
|
121
|
+
const payload = dtInstance.generateTracePayload({
|
|
122
|
+
sameOrigin: false,
|
|
123
|
+
hostname: 'someotherdomain.com',
|
|
124
|
+
protocol: 'https',
|
|
125
|
+
port: '443'
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
expect(payload.newrelicHeader).toBeUndefined()
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test('trace context headers are generated with the correct format', () => {
|
|
132
|
+
const agentConfig = {
|
|
133
|
+
distributed_tracing: {
|
|
134
|
+
enabled: true
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
jest.mocked(getConfiguration).mockReturnValue(agentConfig)
|
|
138
|
+
jest.mocked(getConfigurationValue).mockReturnValue(agentConfig.distributed_tracing)
|
|
139
|
+
|
|
140
|
+
const payload = dtInstance.generateTracePayload({
|
|
141
|
+
sameOrigin: true
|
|
142
|
+
})
|
|
143
|
+
const parentHeader = payload.traceContextParentHeader
|
|
144
|
+
const stateHeader = payload.traceContextStateHeader
|
|
145
|
+
|
|
146
|
+
const parentHeaderParts = parentHeader.split('-')
|
|
147
|
+
expect(parentHeaderParts[0]).toEqual('00')
|
|
148
|
+
expect(parentHeaderParts[1]).toEqual(payload.traceId)
|
|
149
|
+
expect(parentHeaderParts[2]).toEqual(payload.spanId)
|
|
150
|
+
expect(parentHeaderParts[3]).toEqual('01')
|
|
151
|
+
|
|
152
|
+
const loaderConfig = getLoaderConfig()
|
|
153
|
+
const stateHeaderKey = stateHeader.substring(0, stateHeader.indexOf('='))
|
|
154
|
+
expect(stateHeaderKey).toEqual(`${loaderConfig.trustKey}@nr`)
|
|
155
|
+
|
|
156
|
+
const stateHeaderParts = stateHeader.substring(stateHeader.indexOf('=') + 1).split('-')
|
|
157
|
+
expect(stateHeaderParts[0]).toEqual('0')
|
|
158
|
+
expect(stateHeaderParts[1]).toEqual('1')
|
|
159
|
+
expect(stateHeaderParts[2]).toEqual(loaderConfig.accountID)
|
|
160
|
+
expect(stateHeaderParts[3]).toEqual(loaderConfig.agentID)
|
|
161
|
+
expect(stateHeaderParts[4]).toEqual(payload.spanId)
|
|
162
|
+
expect(stateHeaderParts[5]).toEqual('')
|
|
163
|
+
expect(stateHeaderParts[6]).toEqual('')
|
|
164
|
+
expect(stateHeaderParts[7]).toEqual('')
|
|
165
|
+
expect(stateHeaderParts[8]).toEqual(payload.timestamp.toString())
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
test('trace context headers are not added to cross-origin calls by default', () => {
|
|
169
|
+
const agentConfig = {
|
|
170
|
+
distributed_tracing: {
|
|
171
|
+
enabled: true,
|
|
172
|
+
allowed_origins: ['https://someotherdomain.com']
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
jest.mocked(getConfiguration).mockReturnValue(agentConfig)
|
|
176
|
+
jest.mocked(getConfigurationValue).mockReturnValue(agentConfig.distributed_tracing)
|
|
177
|
+
|
|
178
|
+
const payload = dtInstance.generateTracePayload({
|
|
179
|
+
sameOrigin: false,
|
|
180
|
+
hostname: 'someotherdomain.com',
|
|
181
|
+
protocol: 'https',
|
|
182
|
+
port: '443'
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
expect(payload.traceContextParentHeader).toBeUndefined()
|
|
186
|
+
expect(payload.traceContextStateHeader).toBeUndefined()
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
test('trace context headers are added to cross-origin calls when enabled in configuration', () => {
|
|
190
|
+
const agentConfig = {
|
|
191
|
+
distributed_tracing: {
|
|
192
|
+
enabled: true,
|
|
193
|
+
allowed_origins: ['https://someotherdomain.com'],
|
|
194
|
+
cors_use_tracecontext_headers: true
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
jest.mocked(getConfiguration).mockReturnValue(agentConfig)
|
|
198
|
+
jest.mocked(getConfigurationValue).mockReturnValue(agentConfig.distributed_tracing)
|
|
199
|
+
|
|
200
|
+
const payload = dtInstance.generateTracePayload({
|
|
201
|
+
sameOrigin: false,
|
|
202
|
+
hostname: 'someotherdomain.com',
|
|
203
|
+
protocol: 'https',
|
|
204
|
+
port: '443'
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
expect(payload.traceContextParentHeader).not.toBeUndefined()
|
|
208
|
+
expect(payload.traceContextStateHeader).not.toBeUndefined()
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
test('trace context headers are not added to cross-origin calls when disabled in configuration', () => {
|
|
212
|
+
const agentConfig = {
|
|
213
|
+
distributed_tracing: {
|
|
214
|
+
enabled: true,
|
|
215
|
+
allowed_origins: ['https://someotherdomain.com'],
|
|
216
|
+
cors_use_tracecontext_headers: false
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
jest.mocked(getConfiguration).mockReturnValue(agentConfig)
|
|
220
|
+
jest.mocked(getConfigurationValue).mockReturnValue(agentConfig.distributed_tracing)
|
|
221
|
+
|
|
222
|
+
const payload = dtInstance.generateTracePayload({
|
|
223
|
+
sameOrigin: false,
|
|
224
|
+
hostname: 'someotherdomain.com',
|
|
225
|
+
protocol: 'https',
|
|
226
|
+
port: '443'
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
expect(payload.traceContextParentHeader).toBeUndefined()
|
|
230
|
+
expect(payload.traceContextStateHeader).toBeUndefined()
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
test('newrelic header is generated when configuration has numeric values', () => {
|
|
234
|
+
jest.mocked(getLoaderConfig).mockReturnValue({
|
|
235
|
+
accountID: 1234,
|
|
236
|
+
agentID: 5678,
|
|
237
|
+
trustKey: 1
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
const agentConfig = {
|
|
241
|
+
distributed_tracing: {
|
|
242
|
+
enabled: true
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
jest.mocked(getConfiguration).mockReturnValue(agentConfig)
|
|
246
|
+
jest.mocked(getConfigurationValue).mockReturnValue(agentConfig.distributed_tracing)
|
|
247
|
+
|
|
248
|
+
const payload = dtInstance.generateTracePayload({
|
|
249
|
+
sameOrigin: true
|
|
250
|
+
})
|
|
251
|
+
const header = JSON.parse(atob(payload.newrelicHeader))
|
|
252
|
+
|
|
253
|
+
expect(payload.spanId).toEqual(header.d.id)
|
|
254
|
+
expect(payload.traceId).toEqual(header.d.tr)
|
|
255
|
+
expect(payload.timestamp).toEqual(header.d.ti)
|
|
256
|
+
|
|
257
|
+
const loaderConfig = getLoaderConfig()
|
|
258
|
+
expect(header.d.ty).toEqual('Browser')
|
|
259
|
+
expect(header.d.ac).toEqual(loaderConfig.accountID.toString())
|
|
260
|
+
expect(header.d.ap).toEqual(loaderConfig.agentID.toString())
|
|
261
|
+
expect(header.d.tk).toEqual(loaderConfig.trustKey.toString())
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
test('no trace headers are generated when the loader config object is empty', () => {
|
|
265
|
+
const agentConfig = {
|
|
266
|
+
}
|
|
267
|
+
jest.mocked(getConfiguration).mockReturnValue(agentConfig)
|
|
268
|
+
jest.mocked(getConfigurationValue).mockReturnValue(agentConfig.distributed_tracing)
|
|
269
|
+
|
|
270
|
+
const payload = dtInstance.generateTracePayload({
|
|
271
|
+
sameOrigin: true
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
expect(payload).toBeNull()
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
test.each([null, undefined])('no trace headers are generated when the loader config accountID is %s', (accountID) => {
|
|
278
|
+
jest.mocked(getLoaderConfig).mockReturnValue({
|
|
279
|
+
accountID,
|
|
280
|
+
agentID: '5678',
|
|
281
|
+
trustKey: '1'
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
const agentConfig = {
|
|
285
|
+
distributed_tracing: {
|
|
286
|
+
enabled: true
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
jest.mocked(getConfiguration).mockReturnValue(agentConfig)
|
|
290
|
+
jest.mocked(getConfigurationValue).mockReturnValue(agentConfig.distributed_tracing)
|
|
291
|
+
|
|
292
|
+
const payload = dtInstance.generateTracePayload({
|
|
293
|
+
sameOrigin: true
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
expect(payload).toBeNull()
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
test.each([null, undefined])('no trace headers are generated when the loader config agentID is %s', (agentID) => {
|
|
300
|
+
jest.mocked(getLoaderConfig).mockReturnValue({
|
|
301
|
+
accountID: '1234',
|
|
302
|
+
agentID,
|
|
303
|
+
trustKey: '1'
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
const agentConfig = {
|
|
307
|
+
distributed_tracing: {
|
|
308
|
+
enabled: true
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
jest.mocked(getConfiguration).mockReturnValue(agentConfig)
|
|
312
|
+
jest.mocked(getConfigurationValue).mockReturnValue(agentConfig.distributed_tracing)
|
|
313
|
+
|
|
314
|
+
const payload = dtInstance.generateTracePayload({
|
|
315
|
+
sameOrigin: true
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
expect(payload).toBeNull()
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
test.each([null, undefined])('trace headers are generated without trust key when the loader config trustKey is %s', (trustKey) => {
|
|
322
|
+
jest.mocked(getLoaderConfig).mockReturnValue({
|
|
323
|
+
accountID: '1234',
|
|
324
|
+
agentID: '5678',
|
|
325
|
+
trustKey
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
const agentConfig = {
|
|
329
|
+
distributed_tracing: {
|
|
330
|
+
enabled: true
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
jest.mocked(getConfiguration).mockReturnValue(agentConfig)
|
|
334
|
+
jest.mocked(getConfigurationValue).mockReturnValue(agentConfig.distributed_tracing)
|
|
335
|
+
|
|
336
|
+
const payload = dtInstance.generateTracePayload({
|
|
337
|
+
sameOrigin: true
|
|
338
|
+
})
|
|
339
|
+
const header = JSON.parse(atob(payload.newrelicHeader))
|
|
340
|
+
|
|
341
|
+
expect(payload.spanId).toEqual(header.d.id)
|
|
342
|
+
expect(payload.traceId).toEqual(header.d.tr)
|
|
343
|
+
expect(payload.timestamp).toEqual(header.d.ti)
|
|
344
|
+
|
|
345
|
+
const loaderConfig = getLoaderConfig()
|
|
346
|
+
expect(header.d.ty).toEqual('Browser')
|
|
347
|
+
expect(header.d.ac).toEqual(loaderConfig.accountID.toString())
|
|
348
|
+
expect(header.d.ap).toEqual(loaderConfig.agentID.toString())
|
|
349
|
+
expect(header.d.tk).toBeUndefined()
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
test.each([null, undefined])('newrelic header is not added when btoa global is %s', (replacementBTOA) => {
|
|
353
|
+
jest.replaceProperty(runtimeModule, 'globalScope', {
|
|
354
|
+
btoa: replacementBTOA
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
const agentConfig = {
|
|
358
|
+
distributed_tracing: {
|
|
359
|
+
enabled: true
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
jest.mocked(getConfiguration).mockReturnValue(agentConfig)
|
|
363
|
+
jest.mocked(getConfigurationValue).mockReturnValue(agentConfig.distributed_tracing)
|
|
364
|
+
|
|
365
|
+
const payload = dtInstance.generateTracePayload({
|
|
366
|
+
sameOrigin: true
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
expect(typeof payload.spanId).toEqual('string')
|
|
370
|
+
expect(typeof payload.traceId).toEqual('string')
|
|
371
|
+
expect(typeof payload.timestamp).toEqual('number')
|
|
372
|
+
expect(typeof payload.traceContextParentHeader).toEqual('string')
|
|
373
|
+
expect(typeof payload.traceContextStateHeader).toEqual('string')
|
|
374
|
+
expect(payload.header).toBeUndefined()
|
|
375
|
+
})
|