@newrelic/browser-agent 1.233.0 → 1.234.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/cdn/experimental.js +27 -0
- package/dist/cjs/common/config/state/init.js +1 -1
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/event-emitter/contextual-ee.test.js +10 -10
- package/dist/cjs/common/harvest/harvest-scheduler.js +18 -3
- package/dist/cjs/common/harvest/harvest-scheduler.test.js +39 -0
- package/dist/cjs/common/harvest/harvest.js +14 -34
- package/dist/cjs/common/harvest/harvest.test.js +224 -0
- package/dist/cjs/common/url/encode.js +2 -2
- package/dist/cjs/common/util/console.test.js +30 -0
- package/dist/cjs/common/util/get-or-set.js +8 -1
- package/dist/cjs/common/util/get-or-set.test.js +47 -0
- package/dist/cjs/common/util/stringify.test.js +48 -0
- package/dist/cjs/common/util/submit-data.js +15 -15
- package/dist/cjs/common/util/submit-data.test.js +221 -0
- package/dist/cjs/common/util/traverse.js +19 -27
- package/dist/cjs/common/util/traverse.test.js +44 -0
- package/dist/cjs/features/metrics/aggregate/endpoint-map.js +14 -0
- package/dist/cjs/features/metrics/aggregate/index.js +3 -2
- package/dist/cjs/features/metrics/instrument/index.js +0 -2
- package/dist/cjs/features/page_view_event/aggregate/index.js +58 -44
- package/dist/cjs/features/session_replay/aggregate/index.js +10 -7
- package/dist/cjs/loaders/configure/configure.js +0 -1
- package/dist/esm/cdn/experimental.js +24 -0
- package/dist/esm/common/config/state/init.js +1 -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/event-emitter/contextual-ee.test.js +10 -10
- package/dist/esm/common/harvest/harvest-scheduler.js +18 -3
- package/dist/esm/common/harvest/harvest-scheduler.test.js +37 -0
- package/dist/esm/common/harvest/harvest.js +14 -34
- package/dist/esm/common/harvest/harvest.test.js +222 -0
- package/dist/esm/common/url/encode.js +2 -2
- package/dist/esm/common/util/console.test.js +28 -0
- package/dist/esm/common/util/get-or-set.js +8 -1
- package/dist/esm/common/util/get-or-set.test.js +45 -0
- package/dist/esm/common/util/stringify.test.js +46 -0
- package/dist/esm/common/util/submit-data.js +15 -15
- package/dist/esm/common/util/submit-data.test.js +219 -0
- package/dist/esm/common/util/traverse.js +19 -27
- package/dist/esm/common/util/traverse.test.js +42 -0
- package/dist/esm/features/metrics/aggregate/endpoint-map.js +7 -0
- package/dist/esm/features/metrics/aggregate/index.js +3 -2
- package/dist/esm/features/metrics/instrument/index.js +0 -2
- package/dist/esm/features/page_view_event/aggregate/index.js +58 -44
- package/dist/esm/features/session_replay/aggregate/index.js +10 -7
- package/dist/esm/loaders/configure/configure.js +0 -1
- package/dist/types/cdn/experimental.d.ts +2 -0
- package/dist/types/cdn/experimental.d.ts.map +1 -0
- package/dist/types/common/config/state/init.d.ts.map +1 -1
- package/dist/types/common/context/shared-context.d.ts.map +1 -1
- package/dist/types/common/harvest/harvest-scheduler.d.ts +26 -3
- package/dist/types/common/harvest/harvest-scheduler.d.ts.map +1 -1
- package/dist/types/common/harvest/harvest.d.ts +2 -2
- package/dist/types/common/harvest/harvest.d.ts.map +1 -1
- package/dist/types/common/timer/timer.d.ts.map +1 -1
- package/dist/types/common/util/get-or-set.d.ts +9 -1
- package/dist/types/common/util/get-or-set.d.ts.map +1 -1
- package/dist/types/common/util/submit-data.d.ts +14 -10
- package/dist/types/common/util/submit-data.d.ts.map +1 -1
- package/dist/types/common/util/traverse.d.ts +10 -1
- package/dist/types/common/util/traverse.d.ts.map +1 -1
- package/dist/types/common/window/nreum.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/endpoint-map.d.ts +8 -0
- package/dist/types/features/metrics/aggregate/endpoint-map.d.ts.map +1 -0
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/polyfill-detection.es5.d.ts.map +1 -1
- package/dist/types/features/metrics/instrument/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +4 -0
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/utils/handler-cache.d.ts.map +1 -1
- package/dist/types/loaders/configure/configure.d.ts.map +1 -1
- package/package.json +8 -3
- package/src/cdn/experimental.js +36 -0
- package/src/common/config/state/init.js +1 -2
- package/src/common/context/shared-context.js +0 -1
- package/src/common/event-emitter/contextual-ee.test.js +10 -10
- package/src/common/harvest/harvest-scheduler.js +18 -3
- package/src/common/harvest/harvest-scheduler.test.js +25 -0
- package/src/common/harvest/harvest.js +15 -20
- package/src/common/harvest/harvest.test.js +169 -0
- package/src/common/session/session-entity.test.js +0 -1
- package/src/common/timer/timer.js +0 -1
- package/src/common/url/encode.js +2 -2
- package/src/common/util/console.test.js +34 -0
- package/src/common/util/get-or-set.js +8 -1
- package/src/common/util/get-or-set.test.js +58 -0
- package/src/common/util/stringify.test.js +49 -0
- package/src/common/util/submit-data.js +15 -16
- package/src/common/util/submit-data.test.js +218 -0
- package/src/common/util/traverse.js +18 -27
- package/src/common/util/traverse.test.js +50 -0
- package/src/common/window/nreum.js +0 -1
- package/src/features/metrics/aggregate/endpoint-map.js +7 -0
- package/src/features/metrics/aggregate/index.js +3 -2
- package/src/features/metrics/aggregate/polyfill-detection.es5.js +0 -1
- package/src/features/metrics/instrument/index.js +0 -2
- package/src/features/page_view_event/aggregate/index.js +48 -51
- package/src/features/page_view_event/instrument/index.js +0 -1
- package/src/features/session_replay/aggregate/index.js +12 -7
- package/src/features/utils/handler-cache.js +0 -1
- package/src/loaders/configure/configure.js +0 -1
- package/dist/cjs/common/util/s-hash.js +0 -19
- package/dist/cjs/features/metrics/instrument/workers-helper.js +0 -124
- package/dist/esm/common/util/s-hash.js +0 -13
- package/dist/esm/features/metrics/instrument/workers-helper.js +0 -119
- package/dist/types/common/util/s-hash.d.ts +0 -2
- package/dist/types/common/util/s-hash.d.ts.map +0 -1
- package/dist/types/features/metrics/instrument/workers-helper.d.ts +0 -7
- package/dist/types/features/metrics/instrument/workers-helper.d.ts.map +0 -1
- package/src/common/util/s-hash.js +0 -14
- package/src/features/metrics/instrument/workers-helper.js +0 -113
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { getOrSet } from './get-or-set';
|
|
2
|
+
test('should return the current value of an existing property', () => {
|
|
3
|
+
const obj = {
|
|
4
|
+
foo: 'bar'
|
|
5
|
+
};
|
|
6
|
+
const prop = 'foo';
|
|
7
|
+
const getVal = jest.fn();
|
|
8
|
+
const result = getOrSet(obj, prop, getVal);
|
|
9
|
+
expect(result).toBe('bar');
|
|
10
|
+
expect(getVal).not.toHaveBeenCalled();
|
|
11
|
+
});
|
|
12
|
+
test('should set and return the value from getVal if the property does not exist', () => {
|
|
13
|
+
const obj = {};
|
|
14
|
+
const prop = 'foo';
|
|
15
|
+
const getVal = jest.fn().mockReturnValue('baz');
|
|
16
|
+
const result = getOrSet(obj, prop, getVal);
|
|
17
|
+
expect(result).toBe('baz');
|
|
18
|
+
expect(getVal).toHaveBeenCalled();
|
|
19
|
+
expect(obj.foo).toBe('baz');
|
|
20
|
+
});
|
|
21
|
+
test('should set the property as non-enumerable if Object.defineProperty is supported', () => {
|
|
22
|
+
const obj = {};
|
|
23
|
+
const prop = 'foo';
|
|
24
|
+
const getVal = jest.fn().mockReturnValue('baz');
|
|
25
|
+
jest.spyOn(Object, 'defineProperty');
|
|
26
|
+
const result = getOrSet(obj, prop, getVal);
|
|
27
|
+
expect(result).toBe('baz');
|
|
28
|
+
expect(Object.defineProperty).toHaveBeenCalledWith(obj, prop, {
|
|
29
|
+
value: 'baz',
|
|
30
|
+
writable: true,
|
|
31
|
+
enumerable: false
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
test('should set the property from getVal if Object.defineProperty and Object.keys are not supported', () => {
|
|
35
|
+
const obj = {};
|
|
36
|
+
const prop = 'foo';
|
|
37
|
+
const getVal = jest.fn(() => 'baz');
|
|
38
|
+
const originalFn = Object.defineProperty;
|
|
39
|
+
Object.defineProperty = null;
|
|
40
|
+
const result = getOrSet(obj, prop, getVal);
|
|
41
|
+
expect(result).toBe('baz');
|
|
42
|
+
expect(obj.foo).toBe('baz');
|
|
43
|
+
expect(Object.prototype.propertyIsEnumerable.call(obj, 'foo')).toBe(true);
|
|
44
|
+
Object.defineProperty = originalFn;
|
|
45
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { stringify } from './stringify';
|
|
2
|
+
var mockEmit = jest.fn();
|
|
3
|
+
jest.mock('../event-emitter/contextual-ee', () => ({
|
|
4
|
+
__esModule: true,
|
|
5
|
+
get ee() {
|
|
6
|
+
return {
|
|
7
|
+
emit: mockEmit
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
}));
|
|
11
|
+
test('should return a JSON string representation of the value', () => {
|
|
12
|
+
const obj = {
|
|
13
|
+
a: 1,
|
|
14
|
+
b: {
|
|
15
|
+
nested: true
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const expected = '{"a":1,"b":{"nested":true}}';
|
|
19
|
+
const result = stringify(obj);
|
|
20
|
+
expect(result).toBe(expected);
|
|
21
|
+
});
|
|
22
|
+
test('should handle circular references and exclude them from the JSON output', () => {
|
|
23
|
+
const obj = {
|
|
24
|
+
a: 1
|
|
25
|
+
};
|
|
26
|
+
obj.b = obj; // Create a circular reference
|
|
27
|
+
const expected = '{"a":1}';
|
|
28
|
+
const result = stringify(obj);
|
|
29
|
+
expect(result).toBe(expected);
|
|
30
|
+
});
|
|
31
|
+
test('should handle non-object values and return their string representation', () => {
|
|
32
|
+
const value = 42;
|
|
33
|
+
const expected = '42';
|
|
34
|
+
const result = stringify(value);
|
|
35
|
+
expect(result).toBe(expected);
|
|
36
|
+
});
|
|
37
|
+
test('should emit an "internal-error" event if an error occurs during JSON.stringify', () => {
|
|
38
|
+
const obj = {
|
|
39
|
+
a: 1
|
|
40
|
+
};
|
|
41
|
+
jest.spyOn(JSON, 'stringify').mockImplementation(() => {
|
|
42
|
+
throw new Error('message');
|
|
43
|
+
});
|
|
44
|
+
stringify(obj);
|
|
45
|
+
expect(mockEmit).toHaveBeenCalledWith('internal-error', expect.any(Array));
|
|
46
|
+
});
|
|
@@ -42,6 +42,14 @@ submitData.jsonp = function jsonp(_ref) {
|
|
|
42
42
|
// do nothing
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Performs an asynchronous GET request using XMLHttpRequest.
|
|
48
|
+
*
|
|
49
|
+
* @param {Object} args - An object containing a `url` property.
|
|
50
|
+
* @param {string} args.url - The URL to send the GET request to.
|
|
51
|
+
* @returns {XMLHttpRequest} - An XMLHttpRequest object.
|
|
52
|
+
*/
|
|
45
53
|
submitData.xhrGet = function xhrGet(_ref2) {
|
|
46
54
|
let {
|
|
47
55
|
url
|
|
@@ -55,18 +63,18 @@ submitData.xhrGet = function xhrGet(_ref2) {
|
|
|
55
63
|
|
|
56
64
|
/**
|
|
57
65
|
* Send via XHR
|
|
58
|
-
* @param {Object} args - The args
|
|
59
|
-
* @param {string} args.url - The URL to send to
|
|
60
|
-
* @param {string=} args.body - The Stringified body
|
|
61
|
-
* @param {boolean=} args.sync - Run XHR
|
|
62
|
-
* @param {string=} [args.method=POST] - The XHR method to use
|
|
63
|
-
* @param {{key: string, value: string}[]} [args.headers] - The headers to attach
|
|
66
|
+
* @param {Object} args - The args.
|
|
67
|
+
* @param {string} args.url - The URL to send to.
|
|
68
|
+
* @param {string=} args.body - The Stringified body. Default null to prevent IE11 from breaking.
|
|
69
|
+
* @param {boolean=} args.sync - Run XHR synchronously.
|
|
70
|
+
* @param {string=} [args.method=POST] - The XHR method to use.
|
|
71
|
+
* @param {{key: string, value: string}[]} [args.headers] - The headers to attach.
|
|
64
72
|
* @returns {XMLHttpRequest}
|
|
65
73
|
*/
|
|
66
74
|
submitData.xhr = function xhr(_ref3) {
|
|
67
75
|
let {
|
|
68
76
|
url,
|
|
69
|
-
body,
|
|
77
|
+
body = null,
|
|
70
78
|
sync,
|
|
71
79
|
method = 'POST',
|
|
72
80
|
headers = [{
|
|
@@ -89,13 +97,6 @@ submitData.xhr = function xhr(_ref3) {
|
|
|
89
97
|
return request;
|
|
90
98
|
};
|
|
91
99
|
|
|
92
|
-
/**
|
|
93
|
-
* Unused at the moment -- DEPRECATED
|
|
94
|
-
*/
|
|
95
|
-
// submitData.xhrSync = function xhrSync (url, body) {
|
|
96
|
-
// return submitData.xhr(url, body, true)
|
|
97
|
-
// }
|
|
98
|
-
|
|
99
100
|
/**
|
|
100
101
|
* Send by appending an <img> element to the page. Do NOT call this function outside of a guaranteed web window environment.
|
|
101
102
|
* @param {Object} args - The args
|
|
@@ -106,7 +107,6 @@ submitData.img = function img(_ref4) {
|
|
|
106
107
|
let {
|
|
107
108
|
url
|
|
108
109
|
} = _ref4;
|
|
109
|
-
console.log('img url', url);
|
|
110
110
|
var element = new Image();
|
|
111
111
|
element.src = url;
|
|
112
112
|
return element;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
* @jest-environment-options {"html": "<html><head><script></script></head><body></body></html>", "url": "https://example.com/"}
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { submitData } from './submit-data';
|
|
7
|
+
const mockWorkerScope = jest.fn().mockImplementation(() => false);
|
|
8
|
+
jest.mock('./global-scope', () => ({
|
|
9
|
+
__esModule: true,
|
|
10
|
+
get isWorkerScope() {
|
|
11
|
+
return mockWorkerScope();
|
|
12
|
+
}
|
|
13
|
+
}));
|
|
14
|
+
const url = 'https://example.com/api';
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
jest.restoreAllMocks();
|
|
17
|
+
mockWorkerScope.mockReturnValue(false);
|
|
18
|
+
});
|
|
19
|
+
describe('submitData.jsonp', () => {
|
|
20
|
+
// This test requires a script tag to exist in the html set by this file's jest-environment-options header block.
|
|
21
|
+
test('should return an HTMLScriptElement when called from a web window environment', () => {
|
|
22
|
+
mockWorkerScope.mockReturnValue(false);
|
|
23
|
+
const jsonp = 'callback';
|
|
24
|
+
const result = submitData.jsonp({
|
|
25
|
+
url,
|
|
26
|
+
jsonp
|
|
27
|
+
});
|
|
28
|
+
expect(result).toBeInstanceOf(HTMLScriptElement);
|
|
29
|
+
expect(result.type).toBe('text/javascript');
|
|
30
|
+
expect(result.src).toBe(url + '&jsonp=' + jsonp);
|
|
31
|
+
});
|
|
32
|
+
test('should try to use importScripts when called from a worker scope', () => {
|
|
33
|
+
mockWorkerScope.mockReturnValueOnce(true);
|
|
34
|
+
const jsonp = 'callback';
|
|
35
|
+
global.importScripts = jest.fn();
|
|
36
|
+
submitData.jsonp({
|
|
37
|
+
url,
|
|
38
|
+
jsonp
|
|
39
|
+
});
|
|
40
|
+
expect(importScripts).toHaveBeenCalledWith(url + '&jsonp=' + jsonp);
|
|
41
|
+
delete global.importScripts;
|
|
42
|
+
});
|
|
43
|
+
test('should fall back to an xhrGet call and return false when called from a worker scope', () => {
|
|
44
|
+
mockWorkerScope.mockReturnValueOnce(true);
|
|
45
|
+
const jsonp = 'callback';
|
|
46
|
+
jest.spyOn(submitData, 'xhrGet').mockImplementation(jest.fn());
|
|
47
|
+
const result = submitData.jsonp({
|
|
48
|
+
url,
|
|
49
|
+
jsonp
|
|
50
|
+
});
|
|
51
|
+
expect(result).toBe(false);
|
|
52
|
+
expect(submitData.xhrGet).toHaveBeenCalledTimes(1);
|
|
53
|
+
});
|
|
54
|
+
test('should not throw an error if any error occurs during execution', () => {
|
|
55
|
+
jest.spyOn(document, 'createElement').mockImplementation(() => {
|
|
56
|
+
throw new Error('message');
|
|
57
|
+
});
|
|
58
|
+
const jsonp = 'callback';
|
|
59
|
+
expect(() => {
|
|
60
|
+
submitData.jsonp({
|
|
61
|
+
url,
|
|
62
|
+
jsonp
|
|
63
|
+
});
|
|
64
|
+
}).not.toThrow();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('submitData.xhrGet', () => {
|
|
68
|
+
test('should return an XMLHttpRequest object', () => {
|
|
69
|
+
const result = submitData.xhrGet({
|
|
70
|
+
url
|
|
71
|
+
});
|
|
72
|
+
expect(result).toBeInstanceOf(XMLHttpRequest);
|
|
73
|
+
});
|
|
74
|
+
test('should not throw an error if URL is not provided', () => {
|
|
75
|
+
expect(() => {
|
|
76
|
+
submitData.xhrGet({});
|
|
77
|
+
}).not.toThrow();
|
|
78
|
+
});
|
|
79
|
+
test('should not throw an error if an invalid URL is provided', () => {
|
|
80
|
+
expect(() => {
|
|
81
|
+
submitData.xhrGet({
|
|
82
|
+
url: 'invalid url'
|
|
83
|
+
});
|
|
84
|
+
}).not.toThrow();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe('submitData.xhr', () => {
|
|
88
|
+
test('should return an XMLHttpRequest object', () => {
|
|
89
|
+
const result = submitData.xhrGet({
|
|
90
|
+
url
|
|
91
|
+
});
|
|
92
|
+
expect(result).toBeInstanceOf(XMLHttpRequest);
|
|
93
|
+
});
|
|
94
|
+
test('should not throw an error if URL is not provided', () => {
|
|
95
|
+
expect(() => {
|
|
96
|
+
submitData.xhr({});
|
|
97
|
+
}).not.toThrow();
|
|
98
|
+
});
|
|
99
|
+
test('should not throw an error if an invalid URL is provided', () => {
|
|
100
|
+
expect(() => {
|
|
101
|
+
submitData.xhr({
|
|
102
|
+
url: 'invalid url'
|
|
103
|
+
});
|
|
104
|
+
}).not.toThrow();
|
|
105
|
+
});
|
|
106
|
+
test('should send a POST request by default', () => {
|
|
107
|
+
jest.spyOn(XMLHttpRequest.prototype, 'open');
|
|
108
|
+
submitData.xhr({
|
|
109
|
+
url
|
|
110
|
+
});
|
|
111
|
+
expect(XMLHttpRequest.prototype.open).toHaveBeenCalledWith('POST', url, true);
|
|
112
|
+
});
|
|
113
|
+
test('should send a GET request if specified', () => {
|
|
114
|
+
jest.spyOn(XMLHttpRequest.prototype, 'open');
|
|
115
|
+
submitData.xhr({
|
|
116
|
+
url,
|
|
117
|
+
method: 'GET'
|
|
118
|
+
});
|
|
119
|
+
expect(XMLHttpRequest.prototype.open).toHaveBeenCalledWith('GET', url, true);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// This test requires a same-origin url to be set by this file's jest-environment-options header block.
|
|
123
|
+
test('should send a request synchronously if specified', () => {
|
|
124
|
+
jest.spyOn(XMLHttpRequest.prototype, 'open');
|
|
125
|
+
submitData.xhr({
|
|
126
|
+
url,
|
|
127
|
+
sync: true
|
|
128
|
+
});
|
|
129
|
+
expect(XMLHttpRequest.prototype.open).toHaveBeenCalledWith('POST', url, false);
|
|
130
|
+
});
|
|
131
|
+
test('should set custom headers if provided', () => {
|
|
132
|
+
const headers = [{
|
|
133
|
+
key: 'Content-Type',
|
|
134
|
+
value: 'application/json'
|
|
135
|
+
}];
|
|
136
|
+
jest.spyOn(XMLHttpRequest.prototype, 'setRequestHeader');
|
|
137
|
+
submitData.xhr({
|
|
138
|
+
url,
|
|
139
|
+
headers
|
|
140
|
+
});
|
|
141
|
+
headers.forEach(header => {
|
|
142
|
+
expect(XMLHttpRequest.prototype.setRequestHeader).toHaveBeenCalledWith(header.key, header.value);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
test('should send a request with the specified body', () => {
|
|
146
|
+
const body = JSON.stringify({
|
|
147
|
+
key: 'value'
|
|
148
|
+
});
|
|
149
|
+
jest.spyOn(XMLHttpRequest.prototype, 'send');
|
|
150
|
+
submitData.xhr({
|
|
151
|
+
url,
|
|
152
|
+
body
|
|
153
|
+
});
|
|
154
|
+
expect(XMLHttpRequest.prototype.send).toHaveBeenCalledWith(body);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
describe('submitData.img', () => {
|
|
158
|
+
test('should return an HTMLImageElement', () => {
|
|
159
|
+
const imageUrl = 'https://example.com/image.png';
|
|
160
|
+
const result = submitData.img({
|
|
161
|
+
url: imageUrl
|
|
162
|
+
});
|
|
163
|
+
expect(result).toBeInstanceOf(HTMLImageElement);
|
|
164
|
+
});
|
|
165
|
+
test('should not throw an error if URL is not provided', () => {
|
|
166
|
+
expect(() => {
|
|
167
|
+
submitData.img({});
|
|
168
|
+
}).not.toThrow();
|
|
169
|
+
});
|
|
170
|
+
test('should set the src attribute of the image element to the provided URL', () => {
|
|
171
|
+
const imageUrl = 'https://example.com/image.png';
|
|
172
|
+
const result = submitData.img({
|
|
173
|
+
url: imageUrl
|
|
174
|
+
});
|
|
175
|
+
expect(result.src).toBe(imageUrl);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
describe('submitData.beacon', () => {
|
|
179
|
+
test('should return true when beacon request succeeds', () => {
|
|
180
|
+
const body = JSON.stringify({
|
|
181
|
+
key: 'value'
|
|
182
|
+
});
|
|
183
|
+
window.navigator.sendBeacon = {
|
|
184
|
+
bind: jest.fn(() => () => true)
|
|
185
|
+
};
|
|
186
|
+
const result = submitData.beacon({
|
|
187
|
+
url,
|
|
188
|
+
body
|
|
189
|
+
});
|
|
190
|
+
expect(result).toBe(true);
|
|
191
|
+
});
|
|
192
|
+
test('should return false when beacon request fails', () => {
|
|
193
|
+
const body = JSON.stringify({
|
|
194
|
+
key: 'value'
|
|
195
|
+
});
|
|
196
|
+
window.navigator.sendBeacon = {
|
|
197
|
+
bind: jest.fn(() => () => {
|
|
198
|
+
throw new Error('message');
|
|
199
|
+
})
|
|
200
|
+
};
|
|
201
|
+
const result = submitData.beacon({
|
|
202
|
+
url,
|
|
203
|
+
body
|
|
204
|
+
});
|
|
205
|
+
expect(result).toBe(false);
|
|
206
|
+
});
|
|
207
|
+
test('should error if sendBeacon is not supported', () => {
|
|
208
|
+
const body = JSON.stringify({
|
|
209
|
+
key: 'value'
|
|
210
|
+
});
|
|
211
|
+
window.navigator.sendBeacon = undefined;
|
|
212
|
+
expect(() => {
|
|
213
|
+
submitData.beacon({
|
|
214
|
+
url,
|
|
215
|
+
body
|
|
216
|
+
});
|
|
217
|
+
}).toThrow();
|
|
218
|
+
});
|
|
219
|
+
});
|
|
@@ -3,33 +3,25 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Applies a function to properties of a specified type in an object, recursively.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} obj - The object to apply the function to.
|
|
10
|
+
* @param {Function} fn - The function to apply to matching properties.
|
|
11
|
+
* @param {string} [type='string'] - The type of properties to apply the function to.
|
|
12
|
+
* @param {Array<string>} [ignoreKeys=[]] - The keys of properties to ignore and not modify.
|
|
13
|
+
* @returns {Object} - The object with function recursively applied.
|
|
14
|
+
*/
|
|
15
|
+
export function applyFnToProps(obj, fn) {
|
|
16
|
+
let type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'string';
|
|
17
|
+
let ignoreKeys = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
|
|
8
18
|
if (!obj || typeof obj !== 'object') return obj;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// eslint-disable-next-line
|
|
15
|
-
if (obj.hasOwnProperty(property)) {
|
|
16
|
-
if (typeof obj[property] === 'object') {
|
|
17
|
-
traverse(obj[property]);
|
|
18
|
-
} else {
|
|
19
|
-
if (typeof obj[property] === type && !shouldIgnore(property)) obj[property] = fn(obj[property]);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return obj;
|
|
24
|
-
}
|
|
25
|
-
function shouldIgnore(key) {
|
|
26
|
-
var ignore = false;
|
|
27
|
-
for (var i = 0; i < ignoreKeys.length; i++) {
|
|
28
|
-
if (ignoreKeys[i] === key) {
|
|
29
|
-
ignore = true;
|
|
30
|
-
break;
|
|
31
|
-
}
|
|
19
|
+
Object.keys(obj).forEach(property => {
|
|
20
|
+
if (typeof obj[property] === 'object') {
|
|
21
|
+
applyFnToProps(obj[property], fn, type, ignoreKeys);
|
|
22
|
+
} else {
|
|
23
|
+
if (typeof obj[property] === type && !ignoreKeys.includes(property)) obj[property] = fn(obj[property]);
|
|
32
24
|
}
|
|
33
|
-
|
|
34
|
-
|
|
25
|
+
});
|
|
26
|
+
return obj;
|
|
35
27
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { applyFnToProps } from './traverse';
|
|
2
|
+
test.each(['not an object', null, undefined])('should return input unchanged when input is %p', input => {
|
|
3
|
+
const result = applyFnToProps(input, jest.fn());
|
|
4
|
+
expect(result).toEqual(input);
|
|
5
|
+
});
|
|
6
|
+
test('should apply the provided function only to properties of the specified type', () => {
|
|
7
|
+
const obj = {
|
|
8
|
+
stringProp: 'Hello',
|
|
9
|
+
numberProp: 42,
|
|
10
|
+
arrayProp: [1, 2, 3],
|
|
11
|
+
objectProp: {
|
|
12
|
+
foo: 'bar'
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const fn = jest.fn(value => value.toUpperCase());
|
|
16
|
+
const result = applyFnToProps(obj, fn, 'string');
|
|
17
|
+
expect(result.stringProp).toBe('HELLO');
|
|
18
|
+
expect(result.numberProp).toBe(42);
|
|
19
|
+
expect(result.arrayProp).toEqual([1, 2, 3]);
|
|
20
|
+
expect(result.objectProp).toEqual({
|
|
21
|
+
foo: 'BAR'
|
|
22
|
+
});
|
|
23
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
24
|
+
expect(fn).toHaveBeenCalledWith('Hello');
|
|
25
|
+
expect(fn).toHaveBeenCalledWith('bar');
|
|
26
|
+
});
|
|
27
|
+
test('should ignore properties specified in ignoreKeys', () => {
|
|
28
|
+
const obj = {
|
|
29
|
+
a: 1,
|
|
30
|
+
b: 2,
|
|
31
|
+
c: 3
|
|
32
|
+
};
|
|
33
|
+
const fn = jest.fn(value => value + 1);
|
|
34
|
+
const ignoreKeys = ['c'];
|
|
35
|
+
const result = applyFnToProps(obj, fn, 'number', ignoreKeys);
|
|
36
|
+
expect(result.a).toBe(2);
|
|
37
|
+
expect(result.b).toBe(3);
|
|
38
|
+
expect(result.c).toBe(3);
|
|
39
|
+
expect(fn).toHaveBeenCalledTimes(2);
|
|
40
|
+
expect(fn).toHaveBeenCalledWith(1);
|
|
41
|
+
expect(fn).toHaveBeenCalledWith(2);
|
|
42
|
+
});
|
|
@@ -13,6 +13,7 @@ import { windowAddEventListener } from '../../../common/event-listener/event-lis
|
|
|
13
13
|
import { isBrowserScope } from '../../../common/util/global-scope';
|
|
14
14
|
import { AggregateBase } from '../../utils/aggregate-base';
|
|
15
15
|
import { stringify } from '../../../common/util/stringify';
|
|
16
|
+
import { endpointMap } from './endpoint-map';
|
|
16
17
|
export class Aggregate extends AggregateBase {
|
|
17
18
|
static featureName = FEATURE_NAME;
|
|
18
19
|
constructor(agentIdentifier, aggregator) {
|
|
@@ -131,12 +132,12 @@ export class Aggregate extends AggregateBase {
|
|
|
131
132
|
|
|
132
133
|
// Capture per-agent bytes sent for each endpoint (see harvest) and RUM call (see page_view_event aggregator).
|
|
133
134
|
Object.keys(agentRuntime.bytesSent).forEach(endpoint => {
|
|
134
|
-
this.storeSupportabilityMetrics("PageSession/Endpoint/".concat(endpoint
|
|
135
|
+
this.storeSupportabilityMetrics("PageSession/Endpoint/".concat(endpointMap[endpoint], "/BytesSent"), agentRuntime.bytesSent[endpoint]);
|
|
135
136
|
});
|
|
136
137
|
|
|
137
138
|
// Capture per-agent query bytes sent for each endpoint (see harvest) and RUM call (see page_view_event aggregator).
|
|
138
139
|
Object.keys(agentRuntime.bytesSent).forEach(endpoint => {
|
|
139
|
-
this.storeSupportabilityMetrics("PageSession/Endpoint/".concat(endpoint
|
|
140
|
+
this.storeSupportabilityMetrics("PageSession/Endpoint/".concat(endpointMap[endpoint], "/QueryBytesSent"), agentRuntime.queryBytesSent[endpoint]);
|
|
140
141
|
});
|
|
141
142
|
|
|
142
143
|
// Capture metrics for session trace if active (`ptid` is set when returned by replay ingest).
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { InstrumentBase } from '../../utils/instrument-base';
|
|
2
|
-
import { insertSupportMetrics } from './workers-helper';
|
|
3
2
|
import { FEATURE_NAME, SUPPORTABILITY_METRIC_CHANNEL } from '../constants';
|
|
4
3
|
import { handle } from '../../../common/event-emitter/handle';
|
|
5
4
|
import { FEATURE_NAMES } from '../../../loaders/features/features';
|
|
@@ -8,7 +7,6 @@ export class Instrument extends InstrumentBase {
|
|
|
8
7
|
constructor(agentIdentifier, aggregator) {
|
|
9
8
|
let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
10
9
|
super(agentIdentifier, aggregator, FEATURE_NAME, auto);
|
|
11
|
-
insertSupportMetrics(tag => handle(SUPPORTABILITY_METRIC_CHANNEL, [tag], undefined, FEATURE_NAMES.metrics, this.ee));
|
|
12
10
|
this.importAggregator();
|
|
13
11
|
}
|
|
14
12
|
}
|