@newrelic/browser-agent 1.233.1 → 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.
Files changed (97) hide show
  1. package/dist/cjs/common/constants/env.cdn.js +1 -1
  2. package/dist/cjs/common/constants/env.npm.js +1 -1
  3. package/dist/cjs/common/event-emitter/contextual-ee.test.js +10 -10
  4. package/dist/cjs/common/harvest/harvest-scheduler.test.js +2 -2
  5. package/dist/cjs/common/harvest/harvest.js +4 -11
  6. package/dist/cjs/common/harvest/harvest.test.js +224 -0
  7. package/dist/cjs/common/url/encode.js +2 -2
  8. package/dist/cjs/common/util/console.test.js +30 -0
  9. package/dist/cjs/common/util/get-or-set.js +8 -1
  10. package/dist/cjs/common/util/get-or-set.test.js +47 -0
  11. package/dist/cjs/common/util/stringify.test.js +48 -0
  12. package/dist/cjs/common/util/submit-data.js +15 -15
  13. package/dist/cjs/common/util/submit-data.test.js +221 -0
  14. package/dist/cjs/common/util/traverse.js +19 -27
  15. package/dist/cjs/common/util/traverse.test.js +44 -0
  16. package/dist/cjs/features/metrics/aggregate/endpoint-map.js +14 -0
  17. package/dist/cjs/features/metrics/aggregate/index.js +3 -2
  18. package/dist/cjs/features/metrics/instrument/index.js +0 -2
  19. package/dist/cjs/features/page_view_event/aggregate/index.js +58 -44
  20. package/dist/cjs/loaders/configure/configure.js +0 -1
  21. package/dist/esm/common/constants/env.cdn.js +1 -1
  22. package/dist/esm/common/constants/env.npm.js +1 -1
  23. package/dist/esm/common/event-emitter/contextual-ee.test.js +10 -10
  24. package/dist/esm/common/harvest/harvest-scheduler.test.js +2 -2
  25. package/dist/esm/common/harvest/harvest.js +4 -11
  26. package/dist/esm/common/harvest/harvest.test.js +222 -0
  27. package/dist/esm/common/url/encode.js +2 -2
  28. package/dist/esm/common/util/console.test.js +28 -0
  29. package/dist/esm/common/util/get-or-set.js +8 -1
  30. package/dist/esm/common/util/get-or-set.test.js +45 -0
  31. package/dist/esm/common/util/stringify.test.js +46 -0
  32. package/dist/esm/common/util/submit-data.js +15 -15
  33. package/dist/esm/common/util/submit-data.test.js +219 -0
  34. package/dist/esm/common/util/traverse.js +19 -27
  35. package/dist/esm/common/util/traverse.test.js +42 -0
  36. package/dist/esm/features/metrics/aggregate/endpoint-map.js +7 -0
  37. package/dist/esm/features/metrics/aggregate/index.js +3 -2
  38. package/dist/esm/features/metrics/instrument/index.js +0 -2
  39. package/dist/esm/features/page_view_event/aggregate/index.js +58 -44
  40. package/dist/esm/loaders/configure/configure.js +0 -1
  41. package/dist/types/common/config/state/init.d.ts.map +1 -1
  42. package/dist/types/common/context/shared-context.d.ts.map +1 -1
  43. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  44. package/dist/types/common/timer/timer.d.ts.map +1 -1
  45. package/dist/types/common/util/get-or-set.d.ts +9 -1
  46. package/dist/types/common/util/get-or-set.d.ts.map +1 -1
  47. package/dist/types/common/util/submit-data.d.ts +14 -10
  48. package/dist/types/common/util/submit-data.d.ts.map +1 -1
  49. package/dist/types/common/util/traverse.d.ts +10 -1
  50. package/dist/types/common/util/traverse.d.ts.map +1 -1
  51. package/dist/types/common/window/nreum.d.ts.map +1 -1
  52. package/dist/types/features/metrics/aggregate/endpoint-map.d.ts +8 -0
  53. package/dist/types/features/metrics/aggregate/endpoint-map.d.ts.map +1 -0
  54. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  55. package/dist/types/features/metrics/aggregate/polyfill-detection.es5.d.ts.map +1 -1
  56. package/dist/types/features/metrics/instrument/index.d.ts.map +1 -1
  57. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  58. package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
  59. package/dist/types/features/utils/handler-cache.d.ts.map +1 -1
  60. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  61. package/package.json +6 -2
  62. package/src/common/config/state/init.js +0 -1
  63. package/src/common/context/shared-context.js +0 -1
  64. package/src/common/event-emitter/contextual-ee.test.js +10 -10
  65. package/src/common/harvest/harvest-scheduler.test.js +2 -2
  66. package/src/common/harvest/harvest.js +5 -9
  67. package/src/common/harvest/harvest.test.js +169 -0
  68. package/src/common/session/session-entity.test.js +0 -1
  69. package/src/common/timer/timer.js +0 -1
  70. package/src/common/url/encode.js +2 -2
  71. package/src/common/util/console.test.js +34 -0
  72. package/src/common/util/get-or-set.js +8 -1
  73. package/src/common/util/get-or-set.test.js +58 -0
  74. package/src/common/util/stringify.test.js +49 -0
  75. package/src/common/util/submit-data.js +15 -16
  76. package/src/common/util/submit-data.test.js +218 -0
  77. package/src/common/util/traverse.js +18 -27
  78. package/src/common/util/traverse.test.js +50 -0
  79. package/src/common/window/nreum.js +0 -1
  80. package/src/features/metrics/aggregate/endpoint-map.js +7 -0
  81. package/src/features/metrics/aggregate/index.js +3 -2
  82. package/src/features/metrics/aggregate/polyfill-detection.es5.js +0 -1
  83. package/src/features/metrics/instrument/index.js +0 -2
  84. package/src/features/page_view_event/aggregate/index.js +48 -51
  85. package/src/features/page_view_event/instrument/index.js +0 -1
  86. package/src/features/utils/handler-cache.js +0 -1
  87. package/src/loaders/configure/configure.js +0 -1
  88. package/dist/cjs/common/util/s-hash.js +0 -19
  89. package/dist/cjs/features/metrics/instrument/workers-helper.js +0 -124
  90. package/dist/esm/common/util/s-hash.js +0 -13
  91. package/dist/esm/features/metrics/instrument/workers-helper.js +0 -119
  92. package/dist/types/common/util/s-hash.d.ts +0 -2
  93. package/dist/types/common/util/s-hash.d.ts.map +0 -1
  94. package/dist/types/features/metrics/instrument/workers-helper.d.ts +0 -7
  95. package/dist/types/features/metrics/instrument/workers-helper.d.ts.map +0 -1
  96. package/src/common/util/s-hash.js +0 -14
  97. package/src/features/metrics/instrument/workers-helper.js +0 -113
@@ -12,7 +12,7 @@ exports.VERSION = exports.DIST_METHOD = exports.BUILD_ENV = void 0;
12
12
  /**
13
13
  * Exposes the version of the agent
14
14
  */
15
- const VERSION = "1.233.1";
15
+ const VERSION = "1.234.0";
16
16
 
17
17
  /**
18
18
  * Exposes the build type of the agent
@@ -12,7 +12,7 @@ exports.VERSION = exports.DIST_METHOD = exports.BUILD_ENV = void 0;
12
12
  /**
13
13
  * Exposes the version of the agent
14
14
  */
15
- const VERSION = "1.233.1";
15
+ const VERSION = "1.234.0";
16
16
 
17
17
  /**
18
18
  * Exposes the build type of the agent
@@ -93,7 +93,7 @@ describe('event-emitter context', () => {
93
93
  });
94
94
  });
95
95
  describe('event-emitter buffer', () => {
96
- it('it should create a new buffer for the given group', async () => {
96
+ test('it should create a new buffer for the given group', async () => {
97
97
  const {
98
98
  ee
99
99
  } = await Promise.resolve().then(() => _interopRequireWildcard(require('./contextual-ee')));
@@ -105,7 +105,7 @@ describe('event-emitter buffer', () => {
105
105
  }));
106
106
  expect(ee.isBuffering(eventType)).toEqual(true);
107
107
  });
108
- it('it should default group to "feature"', async () => {
108
+ test('it should default group to "feature"', async () => {
109
109
  const {
110
110
  ee
111
111
  } = await Promise.resolve().then(() => _interopRequireWildcard(require('./contextual-ee')));
@@ -116,7 +116,7 @@ describe('event-emitter buffer', () => {
116
116
  }));
117
117
  expect(ee.isBuffering(eventType)).toEqual(true);
118
118
  });
119
- it('it should not create buffer if event-emitter is aborted', async () => {
119
+ test('it should not create buffer if event-emitter is aborted', async () => {
120
120
  const {
121
121
  ee
122
122
  } = await Promise.resolve().then(() => _interopRequireWildcard(require('./contextual-ee')));
@@ -156,7 +156,7 @@ describe('event-emitter abort', () => {
156
156
  });
157
157
  });
158
158
  describe('event-emitter emit', () => {
159
- it('should execute the listener', async () => {
159
+ test('should execute the listener', async () => {
160
160
  const {
161
161
  ee
162
162
  } = await Promise.resolve().then(() => _interopRequireWildcard(require('./contextual-ee')));
@@ -167,7 +167,7 @@ describe('event-emitter emit', () => {
167
167
  ee.emit(eventType, eventArgs);
168
168
  expect(mockListener).toHaveBeenCalledWith(eventArgs[0], eventArgs[1], eventArgs[2]);
169
169
  });
170
- it('should not execute the listener after removal', async () => {
170
+ test('should not execute the listener after removal', async () => {
171
171
  const {
172
172
  ee
173
173
  } = await Promise.resolve().then(() => _interopRequireWildcard(require('./contextual-ee')));
@@ -180,7 +180,7 @@ describe('event-emitter emit', () => {
180
180
  ee.emit(eventType, eventArgs);
181
181
  expect(mockListener).toHaveBeenCalledTimes(1);
182
182
  });
183
- it('should return early if global event-emitter is aborted', async () => {
183
+ test('should return early if global event-emitter is aborted', async () => {
184
184
  const {
185
185
  ee
186
186
  } = await Promise.resolve().then(() => _interopRequireWildcard(require('./contextual-ee')));
@@ -195,7 +195,7 @@ describe('event-emitter emit', () => {
195
195
  ee.emit(eventType, eventArgs);
196
196
  expect(mockListener).toHaveBeenCalledTimes(0);
197
197
  });
198
- it('should still emit if global event-emitter is aborted but force flag is true', async () => {
198
+ test('should still emit if global event-emitter is aborted but force flag is true', async () => {
199
199
  const {
200
200
  ee
201
201
  } = await Promise.resolve().then(() => _interopRequireWildcard(require('./contextual-ee')));
@@ -211,7 +211,7 @@ describe('event-emitter emit', () => {
211
211
  scopeEE.emit(eventType, eventArgs, {}, true);
212
212
  expect(mockScopeListener).toHaveBeenCalledTimes(1);
213
213
  });
214
- it('should bubble the event if bubble flag is true', async () => {
214
+ test('should bubble the event if bubble flag is true', async () => {
215
215
  const {
216
216
  ee
217
217
  } = await Promise.resolve().then(() => _interopRequireWildcard(require('./contextual-ee')));
@@ -226,7 +226,7 @@ describe('event-emitter emit', () => {
226
226
  expect(mockScopeListener).toHaveBeenCalledTimes(1);
227
227
  expect(mockListener).toHaveBeenCalledTimes(1);
228
228
  });
229
- it('should not bubble the event if bubble flag is false', async () => {
229
+ test('should not bubble the event if bubble flag is false', async () => {
230
230
  const {
231
231
  ee
232
232
  } = await Promise.resolve().then(() => _interopRequireWildcard(require('./contextual-ee')));
@@ -241,7 +241,7 @@ describe('event-emitter emit', () => {
241
241
  expect(mockScopeListener).toHaveBeenCalledTimes(1);
242
242
  expect(mockListener).not.toHaveBeenCalled();
243
243
  });
244
- it('should buffer the event on the scoped event-emitter', async () => {
244
+ test('should buffer the event on the scoped event-emitter', async () => {
245
245
  const {
246
246
  ee
247
247
  } = await Promise.resolve().then(() => _interopRequireWildcard(require('./contextual-ee')));
@@ -3,7 +3,7 @@
3
3
  var _init = require("../config/state/init");
4
4
  var _harvestScheduler = require("./harvest-scheduler");
5
5
  describe('runHarvest', () => {
6
- it('should re-schedule harvest even if there is no accumulated data', () => {
6
+ test('should re-schedule harvest even if there is no accumulated data', () => {
7
7
  (0, _init.setConfiguration)('asdf', {});
8
8
  const scheduler = new _harvestScheduler.HarvestScheduler('events', {
9
9
  getPayload: jest.fn()
@@ -19,7 +19,7 @@ describe('runHarvest', () => {
19
19
  expect(scheduler.opts.getPayload()).toBeFalsy();
20
20
  expect(scheduler.scheduleHarvest).toHaveBeenCalledTimes(1);
21
21
  });
22
- it('should also re-schedule harvest if there is accumulated data', () => {
22
+ test('should also re-schedule harvest if there is accumulated data', () => {
23
23
  (0, _init.setConfiguration)('asdf', {});
24
24
  const scheduler = new _harvestScheduler.HarvestScheduler('events', {
25
25
  getPayload: jest.fn().mockImplementation(() => 'payload')
@@ -131,7 +131,7 @@ class Harvest extends _sharedContext.SharedContext {
131
131
  var info = (0, _config.getInfo)(this.sharedContext.agentIdentifier);
132
132
  if (!info.errorBeacon) return false;
133
133
  var agentRuntime = (0, _config.getRuntime)(this.sharedContext.agentIdentifier);
134
- if (!payload.body) {
134
+ if (!payload.body && !opts?.sendEmptyBody) {
135
135
  // no payload body? nothing to send, just run onfinish stuff and return
136
136
  if (cbFinished) {
137
137
  cbFinished({
@@ -141,7 +141,7 @@ class Harvest extends _sharedContext.SharedContext {
141
141
  return false;
142
142
  }
143
143
  let url = '';
144
- if (customUrl) url = customUrl;else if (raw) url = "".concat(this.getScheme(), "://").concat(info.errorBeacon, "/").concat(endpoint);else url = "".concat(this.getScheme(), "://").concat(info.errorBeacon, "/").concat(endpoint, "/1/").concat(info.licenseKey);
144
+ if (customUrl) url = customUrl;else if (raw) url = "".concat(this.getScheme(), "://").concat(info.errorBeacon, "/").concat(endpoint);else url = "".concat(this.getScheme(), "://").concat(info.errorBeacon).concat(endpoint !== 'rum' ? "/".concat(endpoint) : '', "/1/").concat(info.licenseKey);
145
145
  var baseParams = !raw && includeBaseParams ? this.baseQueryString() : '';
146
146
  var payloadParams = payload.qs ? (0, _encode.obj)(payload.qs, agentRuntime.maxBytes) : '';
147
147
  if (!submitMethod) {
@@ -250,18 +250,11 @@ class Harvest extends _sharedContext.SharedContext {
250
250
  }
251
251
  }
252
252
  exports.Harvest = Harvest;
253
- function or(a, b) {
254
- return a || b;
255
- }
256
253
  function getSubmitMethod(endpoint, opts) {
257
254
  opts = opts || {};
258
255
  var method;
259
256
  var useBody;
260
- if (opts.needResponse) {
261
- // currently: only STN needs a response
262
- useBody = true;
263
- method = _submitData.submitData.xhr;
264
- } else if (opts.unload && _globalScope.isBrowserScope) {
257
+ if (opts.unload && _globalScope.isBrowserScope) {
265
258
  // all the features' final harvest; neither methods work outside window context
266
259
  useBody = haveSendBeacon;
267
260
  method = haveSendBeacon ? _submitData.submitData.beacon : _submitData.submitData.img; // really only IE doesn't have Beacon API for web browsers
@@ -291,7 +284,7 @@ function createAccumulator() {
291
284
  var accumulator = {};
292
285
  var hasData = false;
293
286
  return function (key, val) {
294
- if (val !== null && val !== undefined && val.length) {
287
+ if (val !== null && val !== undefined && val.toString()?.length) {
295
288
  accumulator[key] = val;
296
289
  hasData = true;
297
290
  }
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+
3
+ var _submitData = require("../util/submit-data");
4
+ var _harvest = require("./harvest");
5
+ jest.mock('../context/shared-context', () => ({
6
+ __esModule: true,
7
+ SharedContext: function () {
8
+ this.sharedContext = {
9
+ agentIdentifier: 'abcd'
10
+ };
11
+ }
12
+ }));
13
+ jest.mock('../config/config', () => ({
14
+ __esModule: true,
15
+ getConfigurationValue: jest.fn(),
16
+ getInfo: jest.fn().mockReturnValue({
17
+ errorBeacon: 'example.com',
18
+ licenseKey: 'abcd'
19
+ }),
20
+ getRuntime: jest.fn().mockReturnValue({
21
+ bytesSent: {},
22
+ queryBytesSent: {}
23
+ })
24
+ }));
25
+ jest.mock('../util/submit-data', () => ({
26
+ __esModule: true,
27
+ submitData: {
28
+ xhr: jest.fn(() => ({
29
+ addEventListener: jest.fn()
30
+ })),
31
+ beacon: jest.fn(),
32
+ img: jest.fn()
33
+ }
34
+ }));
35
+ describe('sendX', () => {
36
+ test.each([null, undefined, false])('should not send request when body is empty and sendEmptyBody is %s', sendEmptyBody => {
37
+ const sendCallback = jest.fn();
38
+ const harvester = new _harvest.Harvest();
39
+ harvester.on('jserrors', () => ({
40
+ body: {},
41
+ qs: {}
42
+ }));
43
+ harvester.sendX({
44
+ endpoint: 'jserrors',
45
+ cbFinished: sendCallback
46
+ });
47
+ expect(sendCallback).toHaveBeenCalledWith({
48
+ sent: false
49
+ });
50
+ expect(_submitData.submitData.xhr).not.toHaveBeenCalled();
51
+ expect(_submitData.submitData.img).not.toHaveBeenCalled();
52
+ expect(_submitData.submitData.beacon).not.toHaveBeenCalled();
53
+ });
54
+ test('should send request when body is empty and sendEmptyBody is true', () => {
55
+ const harvester = new _harvest.Harvest();
56
+ harvester.on('jserrors', () => ({
57
+ body: {},
58
+ qs: {}
59
+ }));
60
+ harvester.sendX({
61
+ endpoint: 'jserrors',
62
+ opts: {
63
+ sendEmptyBody: true
64
+ },
65
+ cbFinished: jest.fn()
66
+ });
67
+ expect(_submitData.submitData.xhr).toHaveBeenCalledWith(expect.objectContaining({
68
+ url: expect.stringContaining('https://example.com/jserrors/1/abcd?'),
69
+ body: undefined
70
+ }));
71
+ expect(_submitData.submitData.img).not.toHaveBeenCalled();
72
+ expect(_submitData.submitData.beacon).not.toHaveBeenCalled();
73
+ });
74
+ test.each([null, undefined, []])('should remove %s values from the body and query string when sending', emptyValue => {
75
+ const harvester = new _harvest.Harvest();
76
+ harvester.on('jserrors', () => ({
77
+ body: {
78
+ bar: 'foo',
79
+ empty: emptyValue
80
+ },
81
+ qs: {
82
+ foo: 'bar',
83
+ empty: emptyValue
84
+ }
85
+ }));
86
+ harvester.sendX({
87
+ endpoint: 'jserrors',
88
+ cbFinished: jest.fn()
89
+ });
90
+ expect(_submitData.submitData.xhr).toHaveBeenCalledWith(expect.objectContaining({
91
+ url: expect.stringContaining('&foo=bar'),
92
+ body: JSON.stringify({
93
+ bar: 'foo'
94
+ })
95
+ }));
96
+ expect(_submitData.submitData.xhr).toHaveBeenCalledWith(expect.objectContaining({
97
+ url: expect.not.stringContaining('&empty'),
98
+ body: expect.not.stringContaining('empty')
99
+ }));
100
+ expect(_submitData.submitData.img).not.toHaveBeenCalled();
101
+ expect(_submitData.submitData.beacon).not.toHaveBeenCalled();
102
+ });
103
+ test.each([1, false, true])('should not remove value %s (when it doesn\'t have a length) from the body and query string when sending', nonStringValue => {
104
+ const harvester = new _harvest.Harvest();
105
+ harvester.on('jserrors', () => ({
106
+ body: {
107
+ bar: 'foo',
108
+ nonString: nonStringValue
109
+ },
110
+ qs: {
111
+ foo: 'bar',
112
+ nonString: nonStringValue
113
+ }
114
+ }));
115
+ harvester.sendX({
116
+ endpoint: 'jserrors',
117
+ cbFinished: jest.fn()
118
+ });
119
+ expect(_submitData.submitData.xhr).toHaveBeenCalledWith(expect.objectContaining({
120
+ url: expect.stringContaining("&nonString=".concat(nonStringValue)),
121
+ body: JSON.stringify({
122
+ bar: 'foo',
123
+ nonString: nonStringValue
124
+ })
125
+ }));
126
+ expect(_submitData.submitData.img).not.toHaveBeenCalled();
127
+ expect(_submitData.submitData.beacon).not.toHaveBeenCalled();
128
+ });
129
+ });
130
+ describe('send', () => {
131
+ test.each([null, undefined, false])('should not send request when body is empty and sendEmptyBody is %s', sendEmptyBody => {
132
+ const sendCallback = jest.fn();
133
+ const harvester = new _harvest.Harvest();
134
+ harvester.send({
135
+ endpoint: 'rum',
136
+ payload: {
137
+ qs: {},
138
+ body: {}
139
+ },
140
+ opts: {
141
+ sendEmptyBody
142
+ },
143
+ cbFinished: sendCallback
144
+ });
145
+ expect(sendCallback).toHaveBeenCalledWith({
146
+ sent: false
147
+ });
148
+ expect(_submitData.submitData.xhr).not.toHaveBeenCalled();
149
+ expect(_submitData.submitData.img).not.toHaveBeenCalled();
150
+ expect(_submitData.submitData.beacon).not.toHaveBeenCalled();
151
+ });
152
+ test('should send request when body is empty and sendEmptyBody is true', () => {
153
+ const harvester = new _harvest.Harvest();
154
+ harvester.send({
155
+ endpoint: 'rum',
156
+ payload: {
157
+ qs: {},
158
+ body: {}
159
+ },
160
+ opts: {
161
+ sendEmptyBody: true
162
+ }
163
+ });
164
+ expect(_submitData.submitData.xhr).toHaveBeenCalledWith(expect.objectContaining({
165
+ url: expect.stringContaining('https://example.com/1/abcd?'),
166
+ body: undefined
167
+ }));
168
+ expect(_submitData.submitData.img).not.toHaveBeenCalled();
169
+ expect(_submitData.submitData.beacon).not.toHaveBeenCalled();
170
+ });
171
+ test.each([null, undefined, []])('should remove %s values from the body and query string when sending', emptyValue => {
172
+ const harvester = new _harvest.Harvest();
173
+ harvester.send({
174
+ endpoint: 'rum',
175
+ payload: {
176
+ qs: {
177
+ foo: 'bar',
178
+ empty: emptyValue
179
+ },
180
+ body: {
181
+ bar: 'foo',
182
+ empty: emptyValue
183
+ }
184
+ }
185
+ });
186
+ expect(_submitData.submitData.xhr).toHaveBeenCalledWith(expect.objectContaining({
187
+ url: expect.stringContaining('&foo=bar'),
188
+ body: JSON.stringify({
189
+ bar: 'foo'
190
+ })
191
+ }));
192
+ expect(_submitData.submitData.xhr).toHaveBeenCalledWith(expect.objectContaining({
193
+ url: expect.not.stringContaining('&empty'),
194
+ body: expect.not.stringContaining('empty')
195
+ }));
196
+ expect(_submitData.submitData.img).not.toHaveBeenCalled();
197
+ expect(_submitData.submitData.beacon).not.toHaveBeenCalled();
198
+ });
199
+ test.each([1, false, true])('should not remove value %s (when it doesn\'t have a length) from the body and query string when sending', nonStringValue => {
200
+ const harvester = new _harvest.Harvest();
201
+ harvester.send({
202
+ endpoint: 'rum',
203
+ payload: {
204
+ qs: {
205
+ foo: 'bar',
206
+ nonString: nonStringValue
207
+ },
208
+ body: {
209
+ bar: 'foo',
210
+ nonString: nonStringValue
211
+ }
212
+ }
213
+ });
214
+ expect(_submitData.submitData.xhr).toHaveBeenCalledWith(expect.objectContaining({
215
+ url: expect.stringContaining("&nonString=".concat(nonStringValue)),
216
+ body: JSON.stringify({
217
+ bar: 'foo',
218
+ nonString: nonStringValue
219
+ })
220
+ }));
221
+ expect(_submitData.submitData.img).not.toHaveBeenCalled();
222
+ expect(_submitData.submitData.beacon).not.toHaveBeenCalled();
223
+ });
224
+ });
@@ -52,11 +52,11 @@ function obj(payload, maxBytes) {
52
52
  var intermediate = [];
53
53
  var next;
54
54
  var i;
55
- if (typeof dataArray === 'string') {
55
+ if (typeof dataArray === 'string' || !Array.isArray(dataArray) && dataArray !== null && dataArray !== undefined && dataArray.toString().length) {
56
56
  next = '&' + feature + '=' + qs(dataArray);
57
57
  total += next.length;
58
58
  result += next;
59
- } else if (dataArray.length) {
59
+ } else if (Array.isArray(dataArray) && dataArray.length) {
60
60
  total += 9;
61
61
  for (i = 0; i < dataArray.length; i++) {
62
62
  next = qs((0, _stringify.stringify)(dataArray[i]));
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+
3
+ var _console = require("./console");
4
+ beforeEach(() => {
5
+ console.warn = jest.fn();
6
+ });
7
+ afterEach(() => {
8
+ jest.restoreAllMocks();
9
+ });
10
+ describe('warn', () => {
11
+ test('should not call console.warn if it is not a function', () => {
12
+ const spy = jest.spyOn(console, 'warn');
13
+ console.warn = undefined;
14
+ (0, _console.warn)('test message');
15
+ expect(spy).not.toHaveBeenCalled();
16
+ });
17
+ test('should call console.warn with a prefixed message', () => {
18
+ (0, _console.warn)('test message');
19
+ expect(console.warn).toHaveBeenCalledWith('New Relic: test message');
20
+ });
21
+ test('should call console.warn with secondary argument if provided', () => {
22
+ const secondary = 'secondary value';
23
+ (0, _console.warn)('test message', secondary);
24
+ expect(console.warn).toHaveBeenCalledWith(secondary);
25
+ });
26
+ test('should not call console.warn with secondary argument if not provided', () => {
27
+ (0, _console.warn)('test message');
28
+ expect(console.warn).toHaveBeenCalledTimes(1);
29
+ });
30
+ });
@@ -11,7 +11,14 @@ exports.getOrSet = getOrSet;
11
11
 
12
12
  var has = Object.prototype.hasOwnProperty;
13
13
 
14
- // Always returns the current value of obj[prop], even if it has to set it first
14
+ /**
15
+ * Always returns the current value of obj[prop], even if it has to set it first. Sets properties as non-enumerable if possible.
16
+ *
17
+ * @param {Object} obj - The object to get or set the property on.
18
+ * @param {string} prop - The name of the property.
19
+ * @param {Function} getVal - A function that returns the value to be set if the property does not exist.
20
+ * @returns {*} The value of the property in the object.
21
+ */
15
22
  function getOrSet(obj, prop, getVal) {
16
23
  // If the value exists return it.
17
24
  if (has.call(obj, prop)) return obj[prop];
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+
3
+ var _getOrSet = require("./get-or-set");
4
+ test('should return the current value of an existing property', () => {
5
+ const obj = {
6
+ foo: 'bar'
7
+ };
8
+ const prop = 'foo';
9
+ const getVal = jest.fn();
10
+ const result = (0, _getOrSet.getOrSet)(obj, prop, getVal);
11
+ expect(result).toBe('bar');
12
+ expect(getVal).not.toHaveBeenCalled();
13
+ });
14
+ test('should set and return the value from getVal if the property does not exist', () => {
15
+ const obj = {};
16
+ const prop = 'foo';
17
+ const getVal = jest.fn().mockReturnValue('baz');
18
+ const result = (0, _getOrSet.getOrSet)(obj, prop, getVal);
19
+ expect(result).toBe('baz');
20
+ expect(getVal).toHaveBeenCalled();
21
+ expect(obj.foo).toBe('baz');
22
+ });
23
+ test('should set the property as non-enumerable if Object.defineProperty is supported', () => {
24
+ const obj = {};
25
+ const prop = 'foo';
26
+ const getVal = jest.fn().mockReturnValue('baz');
27
+ jest.spyOn(Object, 'defineProperty');
28
+ const result = (0, _getOrSet.getOrSet)(obj, prop, getVal);
29
+ expect(result).toBe('baz');
30
+ expect(Object.defineProperty).toHaveBeenCalledWith(obj, prop, {
31
+ value: 'baz',
32
+ writable: true,
33
+ enumerable: false
34
+ });
35
+ });
36
+ test('should set the property from getVal if Object.defineProperty and Object.keys are not supported', () => {
37
+ const obj = {};
38
+ const prop = 'foo';
39
+ const getVal = jest.fn(() => 'baz');
40
+ const originalFn = Object.defineProperty;
41
+ Object.defineProperty = null;
42
+ const result = (0, _getOrSet.getOrSet)(obj, prop, getVal);
43
+ expect(result).toBe('baz');
44
+ expect(obj.foo).toBe('baz');
45
+ expect(Object.prototype.propertyIsEnumerable.call(obj, 'foo')).toBe(true);
46
+ Object.defineProperty = originalFn;
47
+ });
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+
3
+ var _stringify = require("./stringify");
4
+ var mockEmit = jest.fn();
5
+ jest.mock('../event-emitter/contextual-ee', () => ({
6
+ __esModule: true,
7
+ get ee() {
8
+ return {
9
+ emit: mockEmit
10
+ };
11
+ }
12
+ }));
13
+ test('should return a JSON string representation of the value', () => {
14
+ const obj = {
15
+ a: 1,
16
+ b: {
17
+ nested: true
18
+ }
19
+ };
20
+ const expected = '{"a":1,"b":{"nested":true}}';
21
+ const result = (0, _stringify.stringify)(obj);
22
+ expect(result).toBe(expected);
23
+ });
24
+ test('should handle circular references and exclude them from the JSON output', () => {
25
+ const obj = {
26
+ a: 1
27
+ };
28
+ obj.b = obj; // Create a circular reference
29
+ const expected = '{"a":1}';
30
+ const result = (0, _stringify.stringify)(obj);
31
+ expect(result).toBe(expected);
32
+ });
33
+ test('should handle non-object values and return their string representation', () => {
34
+ const value = 42;
35
+ const expected = '42';
36
+ const result = (0, _stringify.stringify)(value);
37
+ expect(result).toBe(expected);
38
+ });
39
+ test('should emit an "internal-error" event if an error occurs during JSON.stringify', () => {
40
+ const obj = {
41
+ a: 1
42
+ };
43
+ jest.spyOn(JSON, 'stringify').mockImplementation(() => {
44
+ throw new Error('message');
45
+ });
46
+ (0, _stringify.stringify)(obj);
47
+ expect(mockEmit).toHaveBeenCalledWith('internal-error', expect.any(Array));
48
+ });
@@ -50,6 +50,14 @@ submitData.jsonp = function jsonp(_ref) {
50
50
  // do nothing
51
51
  }
52
52
  };
53
+
54
+ /**
55
+ * Performs an asynchronous GET request using XMLHttpRequest.
56
+ *
57
+ * @param {Object} args - An object containing a `url` property.
58
+ * @param {string} args.url - The URL to send the GET request to.
59
+ * @returns {XMLHttpRequest} - An XMLHttpRequest object.
60
+ */
53
61
  submitData.xhrGet = function xhrGet(_ref2) {
54
62
  let {
55
63
  url
@@ -63,18 +71,18 @@ submitData.xhrGet = function xhrGet(_ref2) {
63
71
 
64
72
  /**
65
73
  * Send via XHR
66
- * @param {Object} args - The args
67
- * @param {string} args.url - The URL to send to
68
- * @param {string=} args.body - The Stringified body
69
- * @param {boolean=} args.sync - Run XHR as Synchronous
70
- * @param {string=} [args.method=POST] - The XHR method to use
71
- * @param {{key: string, value: string}[]} [args.headers] - The headers to attach
74
+ * @param {Object} args - The args.
75
+ * @param {string} args.url - The URL to send to.
76
+ * @param {string=} args.body - The Stringified body. Default null to prevent IE11 from breaking.
77
+ * @param {boolean=} args.sync - Run XHR synchronously.
78
+ * @param {string=} [args.method=POST] - The XHR method to use.
79
+ * @param {{key: string, value: string}[]} [args.headers] - The headers to attach.
72
80
  * @returns {XMLHttpRequest}
73
81
  */
74
82
  submitData.xhr = function xhr(_ref3) {
75
83
  let {
76
84
  url,
77
- body,
85
+ body = null,
78
86
  sync,
79
87
  method = 'POST',
80
88
  headers = [{
@@ -97,13 +105,6 @@ submitData.xhr = function xhr(_ref3) {
97
105
  return request;
98
106
  };
99
107
 
100
- /**
101
- * Unused at the moment -- DEPRECATED
102
- */
103
- // submitData.xhrSync = function xhrSync (url, body) {
104
- // return submitData.xhr(url, body, true)
105
- // }
106
-
107
108
  /**
108
109
  * Send by appending an <img> element to the page. Do NOT call this function outside of a guaranteed web window environment.
109
110
  * @param {Object} args - The args
@@ -114,7 +115,6 @@ submitData.img = function img(_ref4) {
114
115
  let {
115
116
  url
116
117
  } = _ref4;
117
- console.log('img url', url);
118
118
  var element = new Image();
119
119
  element.src = url;
120
120
  return element;