rollbar 2.26.4 → 3.0.0-alpha.2

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 (140) hide show
  1. package/.claude/settings.local.json +3 -0
  2. package/.cursor/rules/guidelines.mdc +154 -0
  3. package/.github/workflows/ci.yml +4 -6
  4. package/CLAUDE.local.md +297 -0
  5. package/CLAUDE.md +201 -0
  6. package/CLAUDE.testrunner.md +470 -0
  7. package/Gruntfile.js +59 -16
  8. package/Makefile +3 -3
  9. package/SECURITY.md +5 -0
  10. package/babel.config.json +9 -0
  11. package/codex.md +148 -0
  12. package/dist/plugins/jquery.min.js +1 -1
  13. package/dist/rollbar.js +19332 -6596
  14. package/dist/rollbar.js.map +1 -1
  15. package/dist/rollbar.min.js +2 -1
  16. package/dist/rollbar.min.js.LICENSE.txt +1 -0
  17. package/dist/rollbar.min.js.map +1 -1
  18. package/dist/rollbar.named-amd.js +19332 -6596
  19. package/dist/rollbar.named-amd.js.map +1 -1
  20. package/dist/rollbar.named-amd.min.js +2 -1
  21. package/dist/rollbar.named-amd.min.js.LICENSE.txt +1 -0
  22. package/dist/rollbar.named-amd.min.js.map +1 -1
  23. package/dist/rollbar.noconflict.umd.js +19319 -6581
  24. package/dist/rollbar.noconflict.umd.js.map +1 -1
  25. package/dist/rollbar.noconflict.umd.min.js +2 -1
  26. package/dist/rollbar.noconflict.umd.min.js.LICENSE.txt +1 -0
  27. package/dist/rollbar.noconflict.umd.min.js.map +1 -1
  28. package/dist/rollbar.snippet.js +1 -1
  29. package/dist/rollbar.umd.js +19333 -6597
  30. package/dist/rollbar.umd.js.map +1 -1
  31. package/dist/rollbar.umd.min.js +2 -1
  32. package/dist/rollbar.umd.min.js.LICENSE.txt +1 -0
  33. package/dist/rollbar.umd.min.js.map +1 -1
  34. package/eslint.config.mjs +33 -0
  35. package/karma.conf.js +5 -14
  36. package/package.json +19 -20
  37. package/src/api.js +57 -4
  38. package/src/apiUtility.js +2 -3
  39. package/src/browser/core.js +37 -9
  40. package/src/browser/replay/defaults.js +70 -0
  41. package/src/browser/replay/recorder.js +194 -0
  42. package/src/browser/replay/replayMap.js +195 -0
  43. package/src/browser/rollbar.js +11 -7
  44. package/src/browser/telemetry.js +3 -3
  45. package/src/browser/transport/fetch.js +17 -4
  46. package/src/browser/transport/xhr.js +17 -1
  47. package/src/browser/transport.js +11 -8
  48. package/src/defaults.js +1 -1
  49. package/src/queue.js +65 -4
  50. package/src/react-native/rollbar.js +1 -1
  51. package/src/rollbar.js +52 -10
  52. package/src/server/rollbar.js +3 -2
  53. package/src/telemetry.js +76 -11
  54. package/src/tracing/context.js +24 -0
  55. package/src/tracing/contextManager.js +37 -0
  56. package/src/tracing/defaults.js +7 -0
  57. package/src/tracing/exporter.js +188 -0
  58. package/src/tracing/hrtime.js +98 -0
  59. package/src/tracing/id.js +24 -0
  60. package/src/tracing/session.js +55 -0
  61. package/src/tracing/span.js +92 -0
  62. package/src/tracing/spanProcessor.js +15 -0
  63. package/src/tracing/tracer.js +46 -0
  64. package/src/tracing/tracing.js +89 -0
  65. package/src/utility.js +34 -0
  66. package/test/api.test.js +57 -12
  67. package/test/apiUtility.test.js +5 -6
  68. package/test/browser.core.test.js +1 -10
  69. package/test/browser.domUtility.test.js +1 -1
  70. package/test/browser.predicates.test.js +1 -1
  71. package/test/browser.replay.recorder.test.js +430 -0
  72. package/test/browser.rollbar.test.js +58 -12
  73. package/test/browser.telemetry.test.js +1 -1
  74. package/test/browser.transforms.test.js +20 -13
  75. package/test/browser.transport.test.js +5 -4
  76. package/test/browser.url.test.js +1 -1
  77. package/test/fixtures/replay/index.js +20 -0
  78. package/test/fixtures/replay/payloads.fixtures.js +229 -0
  79. package/test/fixtures/replay/rrwebEvents.fixtures.js +251 -0
  80. package/test/fixtures/replay/rrwebSyntheticEvents.fixtures.js +328 -0
  81. package/test/notifier.test.js +1 -1
  82. package/test/predicates.test.js +1 -1
  83. package/test/queue.test.js +1 -1
  84. package/test/rateLimiter.test.js +1 -1
  85. package/test/react-native.rollbar.test.js +1 -1
  86. package/test/react-native.transforms.test.js +2 -2
  87. package/test/react-native.transport.test.js +3 -3
  88. package/test/replay/index.js +2 -0
  89. package/test/replay/integration/api.spans.test.js +136 -0
  90. package/test/replay/integration/e2e.test.js +228 -0
  91. package/test/replay/integration/index.js +9 -0
  92. package/test/replay/integration/queue.replayMap.test.js +332 -0
  93. package/test/replay/integration/replayMap.test.js +163 -0
  94. package/test/replay/integration/sessionRecording.test.js +390 -0
  95. package/test/replay/unit/api.postSpans.test.js +150 -0
  96. package/test/replay/unit/index.js +7 -0
  97. package/test/replay/unit/queue.replayMap.test.js +225 -0
  98. package/test/replay/unit/replayMap.test.js +348 -0
  99. package/test/replay/util/index.js +5 -0
  100. package/test/replay/util/mockRecordFn.js +80 -0
  101. package/test/server.lambda.mocha.test.mjs +172 -0
  102. package/test/server.locals.constructor.mocha.test.mjs +80 -0
  103. package/test/server.locals.error-handling.mocha.test.mjs +387 -0
  104. package/test/server.locals.merge.mocha.test.mjs +267 -0
  105. package/test/server.locals.test-utils.mjs +114 -0
  106. package/test/server.parser.mocha.test.mjs +87 -0
  107. package/test/server.predicates.mocha.test.mjs +63 -0
  108. package/test/server.rollbar.constructor.mocha.test.mjs +199 -0
  109. package/test/server.rollbar.handlers.mocha.test.mjs +253 -0
  110. package/test/server.rollbar.logging.mocha.test.mjs +326 -0
  111. package/test/server.rollbar.misc.mocha.test.mjs +44 -0
  112. package/test/server.rollbar.test-utils.mjs +57 -0
  113. package/test/server.telemetry.mocha.test.mjs +377 -0
  114. package/test/server.transforms.data.mocha.test.mjs +163 -0
  115. package/test/server.transforms.error.mocha.test.mjs +199 -0
  116. package/test/server.transforms.request.mocha.test.mjs +208 -0
  117. package/test/server.transforms.scrub.mocha.test.mjs +140 -0
  118. package/test/server.transforms.sourcemaps.mocha.test.mjs +122 -0
  119. package/test/server.transforms.test-utils.mjs +62 -0
  120. package/test/server.transport.mocha.test.mjs +269 -0
  121. package/test/telemetry.test.js +132 -1
  122. package/test/tracing/contextManager.test.js +28 -0
  123. package/test/tracing/exporter.toPayload.test.js +400 -0
  124. package/test/tracing/id.test.js +24 -0
  125. package/test/tracing/span.test.js +183 -0
  126. package/test/tracing/spanProcessor.test.js +73 -0
  127. package/test/tracing/tracing.test.js +105 -0
  128. package/test/transforms.test.js +2 -2
  129. package/test/truncation.test.js +2 -2
  130. package/test/utility.test.js +44 -6
  131. package/webpack.config.js +6 -44
  132. package/.eslintignore +0 -7
  133. package/test/server.lambda.test.js +0 -194
  134. package/test/server.locals.test.js +0 -1068
  135. package/test/server.parser.test.js +0 -78
  136. package/test/server.predicates.test.js +0 -91
  137. package/test/server.rollbar.test.js +0 -728
  138. package/test/server.telemetry.test.js +0 -443
  139. package/test/server.transforms.test.js +0 -1193
  140. package/test/server.transport.test.js +0 -269
@@ -1,1068 +0,0 @@
1
- 'use strict';
2
-
3
- var assert = require('assert');
4
- var vows = require('vows');
5
- var sinon = require('sinon');
6
-
7
- process.env.NODE_ENV = process.env.NODE_ENV || 'test-node-env';
8
- var Rollbar = require('../src/server/rollbar');
9
- var logger = require('../src/server/logger');
10
- var Locals = require('../src/server/locals');
11
- var localsFixtures = require('./fixtures/locals.fixtures');
12
-
13
- var nodeMajorVersion = process.versions.node.split('.')[0];
14
-
15
- async function wait(ms) {
16
- return new Promise((resolve) => {
17
- setTimeout(resolve, ms);
18
- });
19
- }
20
-
21
- async function promiseReject(rollbar, callback) {
22
- var error = new Error('promise reject');
23
-
24
- Promise.reject(error);
25
- await wait(500);
26
- callback(null, rollbar);
27
- }
28
-
29
- async function nodeThrow(rollbar, callback) {
30
- setTimeout(function () {
31
- var error = new Error('node error');
32
- throw error;
33
- }, 1);
34
- await wait(500);
35
- callback(null, rollbar);
36
- }
37
-
38
- async function nodeThrowAndCatch(rollbar, callback) {
39
- setTimeout(function () {
40
- var error = new Error('caught error');
41
- try {
42
- throw error;
43
- } catch (e) {
44
- rollbar.error(e);
45
- }
46
- }, 1);
47
- await wait(500);
48
- callback(null, rollbar);
49
- }
50
-
51
- function nestedError(nestedMessage, _password) {
52
- var nestedError = new Error(nestedMessage);
53
- throw nestedError;
54
- }
55
-
56
- async function nodeThrowNested(rollbar, callback) {
57
- setTimeout(function () {
58
- var message = 'test error';
59
- var password = '123456';
60
- var err = new Error(message);
61
-
62
- try {
63
- var newMessage = 'nested ' + message;
64
- nestedError(newMessage, password);
65
- } catch (e) {
66
- err.nested = e;
67
- }
68
-
69
- throw err;
70
- }, 1);
71
- await wait(500);
72
- callback(null, rollbar);
73
- }
74
-
75
- function fakeSessionPostHandler(responses) {
76
- return function fakeSessionPost(command, options, callback) {
77
- var error;
78
- var response;
79
-
80
- if (command === 'Runtime.getProperties') {
81
- response = { result: responses[options.objectId] };
82
- } else {
83
- error = new Error('Unexpected session.post command');
84
- }
85
-
86
- setTimeout(function () {
87
- callback(error, response);
88
- }, 1);
89
- };
90
- }
91
-
92
- async function nodeThrowWithNestedLocals(rollbar, callback) {
93
- setTimeout(function () {
94
- var arr = [{ zero: [0, 0] }, { one: 1 }, { two: 2 }, { three: 3 }];
95
- var obj = { a: 'a', b: 'b', c: 'c', d: 'd', e: 'e', f: 'f' };
96
- var password = 'password';
97
- var sym = Symbol('foo');
98
- var error = new Error('node error');
99
- throw error;
100
- }, 1);
101
- await wait(500);
102
- callback(null, rollbar);
103
- }
104
-
105
- function recurse(curr, limit) {
106
- if (curr < limit) {
107
- recurse(curr + 1, limit);
108
- } else {
109
- throw new Error('deep stack error, limit=' + limit);
110
- }
111
- }
112
-
113
- async function nodeThrowRecursionError(rollbar, callback) {
114
- setTimeout(function () {
115
- recurse(0, 3);
116
- }, 1);
117
- await wait(500);
118
- callback(null, rollbar);
119
- }
120
-
121
- function cloneStack(stack) {
122
- // Deep clone, because stack gets modified by mergeLocals
123
- // and we don't want to modify the test fixtures.
124
- return JSON.parse(JSON.stringify(stack));
125
- }
126
-
127
- function verifyThrownError(r) {
128
- var addItemStub = r.addItemStub;
129
-
130
- assert.isTrue(addItemStub.called);
131
- var data = addItemStub.getCall(0).args[3].data;
132
- assert.equal(data.body.trace_chain[0].exception.message, 'node error');
133
- var length = data.body.trace_chain[0].frames.length;
134
- assert.ok(length > 1);
135
-
136
- if (nodeMajorVersion >= 18) {
137
- // Node >=18; locals only in top frame
138
- assert.equal(
139
- data.body.trace_chain[0].frames[length - 1].locals.error,
140
- '<Error object>',
141
- );
142
- assert.equal(data.body.trace_chain[0].frames[length - 2].locals, undefined);
143
- } else if (nodeMajorVersion >= 10) {
144
- // Node >=10; locals enabled
145
- assert.equal(
146
- data.body.trace_chain[0].frames[length - 1].locals.error,
147
- '<Error object>',
148
- );
149
- assert.equal(
150
- data.body.trace_chain[0].frames[length - 2].locals.timer,
151
- '<Timeout object>',
152
- );
153
- } else {
154
- // Node <=8; locals disabled
155
- assert.equal(data.body.trace_chain[0].frames[length - 1].locals, undefined);
156
- assert.equal(data.body.trace_chain[0].frames[length - 2].locals, undefined);
157
- }
158
- addItemStub.restore();
159
- }
160
-
161
- function verifyCaughtError(r) {
162
- var addItemStub = r.addItemStub;
163
-
164
- assert.isTrue(addItemStub.called);
165
- var data = addItemStub.getCall(0).args[3].data;
166
- assert.equal(data.body.trace_chain[0].exception.message, 'caught error');
167
- var length = data.body.trace_chain[0].frames.length;
168
- assert.ok(length > 1);
169
-
170
- if (nodeMajorVersion >= 18) {
171
- // Node >=18; locals only in top frame
172
- assert.equal(
173
- data.body.trace_chain[0].frames[length - 1].locals.error,
174
- '<Error object>',
175
- );
176
- assert.equal(data.body.trace_chain[0].frames[length - 2].locals, undefined);
177
- } else if (nodeMajorVersion >= 10) {
178
- // Node 10..<18; locals enabled
179
- assert.equal(
180
- data.body.trace_chain[0].frames[length - 1].locals.error,
181
- '<Error object>',
182
- );
183
- assert.equal(
184
- data.body.trace_chain[0].frames[length - 2].locals.timer,
185
- '<Timeout object>',
186
- );
187
- } else {
188
- assert.equal(data.body.trace_chain[0].frames[length - 1].locals, undefined);
189
- assert.equal(data.body.trace_chain[0].frames[length - 2].locals, undefined);
190
- }
191
- addItemStub.restore();
192
- }
193
-
194
- function verifyNestedError(r) {
195
- var addItemStub = r.addItemStub;
196
-
197
- assert.isTrue(addItemStub.called);
198
- var data = addItemStub.getCall(0).args[3].data;
199
- assert.equal(data.body.trace_chain[0].exception.message, 'test error');
200
- assert.equal(data.body.trace_chain[1].exception.message, 'nested test error');
201
- var length = data.body.trace_chain[0].frames.length;
202
- assert.ok(length > 1);
203
-
204
- if (nodeMajorVersion >= 18) {
205
- // Node >=18; locals only in top frame
206
- assert.equal(
207
- data.body.trace_chain[0].frames[length - 1].locals.message,
208
- 'test error',
209
- );
210
- assert.equal(
211
- data.body.trace_chain[0].frames[length - 1].locals.password,
212
- '********',
213
- );
214
- assert.equal(
215
- data.body.trace_chain[0].frames[length - 1].locals.err,
216
- '<Error object>',
217
- );
218
- assert.equal(
219
- data.body.trace_chain[0].frames[length - 1].locals.newMessage,
220
- 'nested test error',
221
- );
222
- assert.equal(data.body.trace_chain[0].frames[length - 2].locals, undefined);
223
-
224
- length = data.body.trace_chain[1].frames.length;
225
- assert.ok(length > 1);
226
- assert.equal(
227
- data.body.trace_chain[1].frames[length - 1].locals.nestedMessage,
228
- 'nested test error',
229
- );
230
- assert.equal(
231
- data.body.trace_chain[1].frames[length - 1].locals._password,
232
- '123456',
233
- );
234
- assert.equal(
235
- data.body.trace_chain[1].frames[length - 1].locals.nestedError,
236
- '<Error object>',
237
- );
238
- assert.equal(data.body.trace_chain[1].frames[length - 2].locals, undefined);
239
- } else if (nodeMajorVersion >= 10) {
240
- // Node >=10; locals enabled
241
- assert.equal(
242
- data.body.trace_chain[0].frames[length - 1].locals.err,
243
- '<Error object>',
244
- );
245
- assert.equal(
246
- data.body.trace_chain[0].frames[length - 2].locals.timer,
247
- '<Timeout object>',
248
- );
249
-
250
- length = data.body.trace_chain[1].frames.length;
251
- assert.ok(length > 1);
252
- assert.equal(
253
- data.body.trace_chain[1].frames[length - 1].locals.nestedMessage,
254
- 'nested test error',
255
- );
256
- assert.equal(
257
- data.body.trace_chain[1].frames[length - 1].locals.nestedError,
258
- '<Error object>',
259
- );
260
- assert.equal(
261
- data.body.trace_chain[1].frames[length - 2].locals.message,
262
- 'test error',
263
- );
264
- assert.equal(
265
- data.body.trace_chain[1].frames[length - 2].locals.password,
266
- '********',
267
- );
268
- assert.equal(
269
- data.body.trace_chain[1].frames[length - 2].locals.err,
270
- '<Error object>',
271
- );
272
- assert.equal(
273
- data.body.trace_chain[1].frames[length - 2].locals.newMessage,
274
- 'nested test error',
275
- );
276
- } else {
277
- // Node <=8; locals disabled
278
- assert.equal(data.body.trace_chain[0].frames[length - 1].locals, undefined);
279
- assert.equal(data.body.trace_chain[0].frames[length - 2].locals, undefined);
280
- }
281
- addItemStub.restore();
282
- }
283
-
284
- function verifyRejectedPromise(r) {
285
- var addItemStub = r.addItemStub;
286
-
287
- assert.isTrue(addItemStub.called);
288
- var data = addItemStub.getCall(0).args[3].data;
289
- assert.equal(data.body.trace_chain[0].exception.message, 'promise reject');
290
- var length = data.body.trace_chain[0].frames.length;
291
- assert.ok(length > 1);
292
-
293
- if (nodeMajorVersion >= 18) {
294
- // Node >=18; locals only in top frame
295
- assert.equal(
296
- data.body.trace_chain[0].frames[length - 1].locals.error,
297
- '<Error object>',
298
- );
299
- assert.equal(
300
- data.body.trace_chain[0].frames[length - 1].locals.callback,
301
- '<Function object>',
302
- );
303
- assert.equal(
304
- data.body.trace_chain[0].frames[length - 1].locals.rollbar,
305
- '<Rollbar object>',
306
- );
307
- assert.equal(data.body.trace_chain[0].frames[length - 2].locals, undefined);
308
- } else if (nodeMajorVersion >= 10) {
309
- // Node >=10; locals enabled
310
- assert.equal(
311
- data.body.trace_chain[0].frames[length - 1].locals.error,
312
- '<Error object>',
313
- );
314
- assert.equal(
315
- data.body.trace_chain[0].frames[length - 1].locals.rollbar,
316
- '<Rollbar object>',
317
- );
318
- assert.equal(
319
- data.body.trace_chain[0].frames[length - 2].locals.notifier,
320
- '<Notifier object>',
321
- );
322
- assert.equal(
323
- data.body.trace_chain[0].frames[length - 2].locals.r,
324
- '<Rollbar object>',
325
- );
326
- } else {
327
- // Node <=8; locals disabled
328
- assert.equal(data.body.trace_chain[0].frames[length - 1].locals, undefined);
329
- assert.equal(data.body.trace_chain[0].frames[length - 2].locals, undefined);
330
- }
331
- addItemStub.restore();
332
- }
333
-
334
- function verifyDefaultOptions(options) {
335
- assert.equal(options.enabled, true);
336
- assert.equal(options.uncaughtOnly, true);
337
- assert.equal(options.depth, 1);
338
- assert.equal(options.maxProperties, 30);
339
- assert.equal(options.maxArray, 5);
340
- }
341
-
342
- vows
343
- .describe('locals')
344
- .addBatch({
345
- enabled: {
346
- topic: function () {
347
- var rollbar = new Rollbar({
348
- accessToken: 'abc123',
349
- captureUncaught: true,
350
- captureUnhandledRejections: true,
351
- locals: { module: Locals, uncaughtOnly: true, depth: 0 },
352
- });
353
- var notifier = rollbar.client.notifier;
354
- rollbar.addItemStub = sinon.stub(notifier.queue, 'addItem');
355
-
356
- nodeThrow(rollbar, this.callback);
357
- },
358
- 'should include locals': function (_err, r) {
359
- verifyThrownError(r);
360
- },
361
- 'then disabled': {
362
- topic: function (_err, r) {
363
- r.configure({ locals: { enabled: false } });
364
- var notifier = r.client.notifier;
365
- assert.ok(notifier);
366
- r.addItemStub = sinon.stub(notifier.queue, 'addItem');
367
-
368
- nodeThrowNested(r, this.callback);
369
- },
370
- 'should not include locals': function (_err, r) {
371
- var addItemStub = r.addItemStub;
372
- assert.ok(addItemStub);
373
-
374
- assert.isTrue(addItemStub.called);
375
- var data = addItemStub.getCall(0).args[3].data;
376
- assert.equal(
377
- data.body.trace_chain[0].exception.message,
378
- 'test error',
379
- );
380
- assert.equal(
381
- data.body.trace_chain[1].exception.message,
382
- 'nested test error',
383
- );
384
- var length = data.body.trace_chain[0].frames.length;
385
- assert.ok(length > 1);
386
- assert.equal(
387
- data.body.trace_chain[0].frames[length - 1].locals,
388
- undefined,
389
- );
390
- assert.equal(
391
- data.body.trace_chain[0].frames[length - 2].locals,
392
- undefined,
393
- );
394
- addItemStub.restore();
395
- },
396
- 'then enabled': {
397
- topic: function (_err, r) {
398
- r.configure({ locals: { enabled: true, uncaughtOnly: false } });
399
- var notifier = r.client.notifier;
400
- r.addItemStub = sinon.stub(notifier.queue, 'addItem');
401
-
402
- promiseReject(r, this.callback);
403
- },
404
- 'should include locals': function (_err, r) {
405
- verifyRejectedPromise(r);
406
- },
407
- },
408
- },
409
- },
410
- })
411
-
412
- .addBatch({
413
- 'on caught error': {
414
- 'uncaughtOnly: true': {
415
- topic: function () {
416
- var rollbar = new Rollbar({
417
- accessToken: 'abc123',
418
- captureUncaught: true,
419
- captureUnhandledRejections: true,
420
- locals: { module: Locals, uncaughtOnly: true, depth: 0 },
421
- });
422
- var notifier = rollbar.client.notifier;
423
- rollbar.addItemStub = sinon.stub(notifier.queue, 'addItem');
424
-
425
- nodeThrowAndCatch(rollbar, this.callback);
426
- },
427
- 'should not include locals': function (_err, r) {
428
- var addItemStub = r.addItemStub;
429
-
430
- assert.isTrue(addItemStub.called);
431
- var data = addItemStub.getCall(0).args[3].data;
432
- assert.equal(
433
- data.body.trace_chain[0].exception.message,
434
- 'caught error',
435
- );
436
- var length = data.body.trace_chain[0].frames.length;
437
- assert.ok(length > 1);
438
- assert.equal(
439
- data.body.trace_chain[0].frames[length - 1].locals,
440
- undefined,
441
- );
442
- assert.equal(
443
- data.body.trace_chain[0].frames[length - 2].locals,
444
- undefined,
445
- );
446
-
447
- addItemStub.restore();
448
- },
449
- 'then uncaughtOnly: false': {
450
- topic: function (_err, r) {
451
- r.configure({ locals: { uncaughtOnly: false } });
452
- var notifier = r.client.notifier;
453
- r.addItemStub = sinon.stub(notifier.queue, 'addItem');
454
-
455
- nodeThrowAndCatch(r, this.callback);
456
- },
457
- 'should include locals': function (_err, r) {
458
- verifyCaughtError(r);
459
- },
460
- },
461
- },
462
- },
463
- })
464
-
465
- .addBatch({
466
- 'on exception': {
467
- uncaught: {
468
- topic: function () {
469
- var rollbar = new Rollbar({
470
- accessToken: 'abc123',
471
- captureUncaught: true,
472
- captureUnhandledRejections: true,
473
- locals: { module: Locals, uncaughtOnly: true, depth: 0 },
474
- });
475
- var notifier = rollbar.client.notifier;
476
- rollbar.addItemStub = sinon.stub(notifier.queue, 'addItem');
477
-
478
- nodeThrow(rollbar, this.callback);
479
- },
480
- 'should include locals': function (_err, r) {
481
- verifyThrownError(r);
482
- },
483
- },
484
- },
485
- })
486
-
487
- .addBatch({
488
- 'on exception': {
489
- nested: {
490
- topic: function () {
491
- var rollbar = new Rollbar({
492
- accessToken: 'abc123',
493
- captureUncaught: true,
494
- locals: { module: Locals, uncaughtOnly: false, depth: 0 },
495
- });
496
- var notifier = rollbar.client.notifier;
497
- rollbar.addItemStub = sinon.stub(notifier.queue, 'addItem');
498
-
499
- nodeThrowNested(rollbar, this.callback);
500
- },
501
- 'should include locals': function (_err, r) {
502
- verifyNestedError(r);
503
- },
504
- },
505
- },
506
- })
507
-
508
- .addBatch({
509
- 'on exception': {
510
- 'promise rejection': {
511
- topic: function () {
512
- var r = new Rollbar({
513
- accessToken: 'abc123',
514
- captureUnhandledRejections: true,
515
- locals: { module: Locals, uncaughtOnly: false, depth: 0 },
516
- });
517
- var notifier = r.client.notifier;
518
- r.addItemStub = sinon.stub(notifier.queue, 'addItem');
519
-
520
- promiseReject(r, this.callback);
521
- },
522
- 'should include locals': function (_err, r) {
523
- verifyRejectedPromise(r);
524
- },
525
- },
526
- },
527
- })
528
-
529
- .addBatch({
530
- 'on exception': {
531
- 'with custom options': {
532
- topic: function () {
533
- var rollbar = new Rollbar({
534
- accessToken: 'abc123',
535
- captureUncaught: true,
536
- locals: { module: Locals, depth: 2, maxProperties: 5, maxArray: 2 },
537
- });
538
- var notifier = rollbar.client.notifier;
539
- rollbar.addItemStub = sinon.stub(notifier.queue, 'addItem');
540
-
541
- nodeThrowWithNestedLocals(rollbar, this.callback);
542
- },
543
- 'should include locals': function (_err, r) {
544
- var addItemStub = r.addItemStub;
545
-
546
- assert.isTrue(addItemStub.called);
547
- var data = addItemStub.getCall(0).args[3].data;
548
- assert.equal(
549
- data.body.trace_chain[0].exception.message,
550
- 'node error',
551
- );
552
- if (nodeMajorVersion < 10) {
553
- // Node 8; locals disabled
554
- var length = data.body.trace_chain[0].frames.length;
555
- assert.equal(
556
- data.body.trace_chain[0].frames[length - 1].locals,
557
- undefined,
558
- );
559
- } else {
560
- var length = data.body.trace_chain[0].frames.length;
561
- assert.equal(
562
- Object.keys(
563
- data.body.trace_chain[0].frames[length - 1].locals.obj,
564
- ).length,
565
- 5,
566
- );
567
- assert.deepEqual(
568
- data.body.trace_chain[0].frames[length - 1].locals.obj,
569
- { a: 'a', b: 'b', c: 'c', d: 'd', e: 'e' },
570
- );
571
- assert.equal(
572
- data.body.trace_chain[0].frames[length - 1].locals.arr.length,
573
- 2,
574
- );
575
- assert.deepEqual(
576
- data.body.trace_chain[0].frames[length - 1].locals.arr[0],
577
- { zero: '<Array object>' },
578
- );
579
- assert.deepEqual(
580
- data.body.trace_chain[0].frames[length - 1].locals.arr[1],
581
- { one: 1 },
582
- );
583
- assert.deepEqual(
584
- data.body.trace_chain[0].frames[length - 1].locals.password,
585
- '********',
586
- );
587
- assert.deepEqual(
588
- data.body.trace_chain[0].frames[length - 1].locals.sym,
589
- 'Symbol(foo)',
590
- );
591
- }
592
- addItemStub.reset();
593
- Locals.session = undefined;
594
- },
595
- },
596
- },
597
- })
598
-
599
- .addBatch({
600
- 'on exception': {
601
- 'with recursive stack': {
602
- topic: function () {
603
- var rollbar = new Rollbar({
604
- accessToken: 'abc123',
605
- captureUncaught: true,
606
- locals: Locals,
607
- });
608
- var notifier = rollbar.client.notifier;
609
- rollbar.addItemStub = sinon.stub(notifier.queue, 'addItem');
610
-
611
- nodeThrowRecursionError(rollbar, this.callback);
612
- },
613
- 'should include locals': function (_err, r) {
614
- var addItemStub = r.addItemStub;
615
-
616
- assert.isTrue(addItemStub.called);
617
- var data = addItemStub.getCall(0).args[3].data;
618
- assert.equal(
619
- data.body.trace_chain[0].exception.message,
620
- 'deep stack error, limit=3',
621
- );
622
- var length = data.body.trace_chain[0].frames.length;
623
- assert.ok(length > 1);
624
-
625
- if (nodeMajorVersion >= 18) {
626
- // Node >=18; locals only in top frame
627
- assert.deepEqual(
628
- data.body.trace_chain[0].frames[length - 1].locals,
629
- { curr: 3, limit: 3 },
630
- );
631
- assert.equal(
632
- data.body.trace_chain[0].frames[length - 2].locals,
633
- undefined,
634
- );
635
- assert.equal(
636
- data.body.trace_chain[0].frames[length - 3].locals,
637
- undefined,
638
- );
639
- } else if (nodeMajorVersion >= 10) {
640
- // Node >=10; locals enabled
641
- assert.deepEqual(
642
- data.body.trace_chain[0].frames[length - 1].locals,
643
- { curr: 3, limit: 3 },
644
- );
645
- assert.deepEqual(
646
- data.body.trace_chain[0].frames[length - 2].locals,
647
- { curr: 2, limit: 3 },
648
- );
649
- assert.deepEqual(
650
- data.body.trace_chain[0].frames[length - 3].locals,
651
- { curr: 1, limit: 3 },
652
- );
653
- } else {
654
- // Node <=8; locals disabled
655
- assert.equal(
656
- data.body.trace_chain[0].frames[length - 1].locals,
657
- undefined,
658
- );
659
- }
660
- addItemStub.reset();
661
- Locals.session = undefined;
662
- },
663
- },
664
- },
665
- })
666
-
667
- .addBatch({
668
- constructor: {
669
- 'passing true boolean': {
670
- topic: function () {
671
- return new Locals(true, logger);
672
- },
673
- 'should use defaults': function (locals) {
674
- verifyDefaultOptions(locals.options);
675
- },
676
- },
677
- 'passing false boolean': {
678
- topic: function () {
679
- return new Locals(false, logger);
680
- },
681
- 'should use defaults': function (locals) {
682
- verifyDefaultOptions(locals.options);
683
- },
684
- },
685
- 'passing empty object': {
686
- topic: function () {
687
- return new Locals({}, logger);
688
- },
689
- 'should use defaults': function (locals) {
690
- verifyDefaultOptions(locals.options);
691
- },
692
- },
693
- 'passing depth option': {
694
- topic: function () {
695
- return new Locals({ depth: 0 }, logger);
696
- },
697
- 'should use updated depth with remaining defaults': function (locals) {
698
- var options = locals.options;
699
-
700
- assert.equal(options.enabled, true);
701
- assert.equal(options.uncaughtOnly, true);
702
- assert.equal(options.depth, 0);
703
- assert.equal(options.maxProperties, 30);
704
- assert.equal(options.maxArray, 5);
705
- },
706
- },
707
- 'passing enabled option': {
708
- topic: function () {
709
- return new Locals({ enabled: false }, logger);
710
- },
711
- 'should use updated enabled with remaining defaults': function (
712
- locals,
713
- ) {
714
- var options = locals.options;
715
-
716
- assert.equal(options.enabled, false);
717
- assert.equal(options.uncaughtOnly, true);
718
- assert.equal(options.depth, 1);
719
- assert.equal(options.maxProperties, 30);
720
- assert.equal(options.maxArray, 5);
721
- },
722
- },
723
- 'passing uncaughtOnly option': {
724
- topic: function () {
725
- return new Locals({ uncaughtOnly: false }, logger);
726
- },
727
- 'should use updated uncaughtOnly with remaining defaults': function (
728
- locals,
729
- ) {
730
- var options = locals.options;
731
-
732
- assert.equal(options.enabled, true);
733
- assert.equal(options.uncaughtOnly, false);
734
- assert.equal(options.depth, 1);
735
- assert.equal(options.maxProperties, 30);
736
- assert.equal(options.maxArray, 5);
737
- },
738
- },
739
- 'passing all options': {
740
- topic: function () {
741
- return new Locals(
742
- {
743
- enabled: false,
744
- uncaughtOnly: false,
745
- depth: 2,
746
- maxProperties: 15,
747
- maxArray: 10,
748
- },
749
- logger,
750
- );
751
- },
752
- 'should use updated options': function (locals) {
753
- var options = locals.options;
754
-
755
- assert.equal(options.enabled, false);
756
- assert.equal(options.uncaughtOnly, false);
757
- assert.equal(options.depth, 2);
758
- assert.equal(options.maxProperties, 15);
759
- assert.equal(options.maxArray, 10);
760
- },
761
- },
762
- },
763
- })
764
-
765
- // The following tests stub a singleton (Locals.session.post()), and need to run sequentially.
766
- // One way to do this in vows is to put each in a separate batch.
767
- .addBatch({
768
- 'mergeLocals returns error from session.post()': {
769
- topic: function () {
770
- var locals = new Locals({}, logger);
771
- var err = new Error('post error');
772
- sinon.stub(Locals.session, 'post').yields(err);
773
-
774
- var key = 'key';
775
- var localsMap = new Map();
776
- localsMap.set('key', localsFixtures.maps.simple);
777
-
778
- var stack = localsFixtures.stacks.simple;
779
-
780
- locals.mergeLocals(localsMap, stack, key, this.callback);
781
- },
782
- 'should callback with error': function (err) {
783
- assert.instanceOf(err, Error);
784
- assert.isTrue(err.stack.startsWith('Error: post error'));
785
- assert.equal(err.message, 'post error');
786
- sinon.restore();
787
- },
788
- },
789
- })
790
- .addBatch({
791
- 'mergeLocals called with multiple/complex locals maps present': {
792
- // Sets up several conditions for test:
793
- // * URLs with and without 'file://' prefix.
794
- // * Intended locals map key isn't the first or only entry.
795
- // * Other scopes besides type: 'local' are present.
796
- // * Transpiled code (Typescript) present in stack
797
- topic: function () {
798
- var getPropertiesResponses = {
799
- objectId1: [
800
- localsFixtures.locals.object1,
801
- localsFixtures.locals.object2,
802
- ],
803
- objectId2: [
804
- localsFixtures.locals.boolean1,
805
- localsFixtures.locals.boolean2,
806
- ],
807
- objectId3: [
808
- localsFixtures.locals.string1,
809
- localsFixtures.locals.array1,
810
- ],
811
- };
812
-
813
- var locals = new Locals({ depth: 0 }, logger);
814
- sinon
815
- .stub(Locals.session, 'post')
816
- .callsFake(fakeSessionPostHandler(getPropertiesResponses));
817
-
818
- var key1 = 'key1';
819
- var key2 = 'key2';
820
- var localsMap = new Map();
821
-
822
- // Test with multiple maps present.
823
- localsMap.set(key1, localsFixtures.maps.simple);
824
- localsMap.set(key2, localsFixtures.maps.complex); // Stack will match the 2nd locals map added.
825
-
826
- var stack = cloneStack(localsFixtures.stacks.complex);
827
-
828
- var self = this;
829
- locals.mergeLocals(localsMap, stack, key2, function (err) {
830
- self.callback(err, stack);
831
- });
832
- },
833
- 'should callback with merged locals': function (err, stack) {
834
- if (err) {
835
- // Ensure unexpected error can be seen.
836
- console.log(err);
837
- }
838
- assert.isNull(err);
839
-
840
- assert.equal(stack[0].locals.response, 'success');
841
- assert.equal(stack[0].locals.args, '<Array object>');
842
- assert.equal(stack[1].locals.old, false);
843
- assert.equal(stack[1].locals.new, true);
844
- assert.equal(stack[2].locals.foo, '<FooClass object>');
845
- assert.equal(stack[2].locals.bar, '<BarClass object>');
846
-
847
- sinon.restore();
848
- },
849
- },
850
- })
851
- .addBatch({
852
- 'mergeLocals called with simple locals maps present': {
853
- topic: function () {
854
- var getPropertiesResponses = {
855
- objectId1: [
856
- localsFixtures.locals.object1,
857
- localsFixtures.locals.object2,
858
- ],
859
- };
860
-
861
- var locals = new Locals({ depth: 0 }, logger);
862
- sinon
863
- .stub(Locals.session, 'post')
864
- .callsFake(fakeSessionPostHandler(getPropertiesResponses));
865
-
866
- var key = 'key';
867
- var localsMap = new Map();
868
-
869
- localsMap.set(key, localsFixtures.maps.simple);
870
-
871
- var stack = cloneStack(localsFixtures.stacks.simple);
872
-
873
- var self = this;
874
- locals.mergeLocals(localsMap, stack, key, function (err) {
875
- self.callback(err, stack);
876
- });
877
- },
878
- 'should callback with merged locals': function (err, stack) {
879
- if (err) {
880
- // Ensure unexpected error can be seen.
881
- console.log(err);
882
- }
883
- assert.isNull(err);
884
-
885
- assert.equal(stack[0].locals.foo, '<FooClass object>');
886
- assert.equal(stack[0].locals.bar, '<BarClass object>');
887
-
888
- sinon.restore();
889
- },
890
- },
891
- })
892
- .addBatch({
893
- 'mergeLocals called with depth = 1': {
894
- topic: function () {
895
- var getPropertiesResponses = {
896
- objectId1: [
897
- localsFixtures.locals.object1,
898
- localsFixtures.locals.object2,
899
- ],
900
- nestedProps1: [
901
- localsFixtures.locals.string1,
902
- localsFixtures.locals.boolean1,
903
- localsFixtures.locals.function1,
904
- ],
905
- nestedProps2: [
906
- localsFixtures.locals.array1,
907
- localsFixtures.locals.null1,
908
- localsFixtures.locals.function2,
909
- ],
910
- };
911
-
912
- var locals = new Locals({ depth: 1 }, logger);
913
- sinon
914
- .stub(Locals.session, 'post')
915
- .callsFake(fakeSessionPostHandler(getPropertiesResponses));
916
-
917
- var key = 'key';
918
- var localsMap = new Map();
919
-
920
- localsMap.set(key, localsFixtures.maps.simple);
921
-
922
- var stack = cloneStack(localsFixtures.stacks.simple);
923
-
924
- var self = this;
925
- locals.mergeLocals(localsMap, stack, key, function (err) {
926
- self.callback(err, stack);
927
- });
928
- },
929
- 'should callback with merged locals': function (err, stack) {
930
- if (err) {
931
- // Ensure unexpected error can be seen.
932
- console.log(err);
933
- }
934
- assert.isNull(err);
935
-
936
- assert.deepEqual(stack[0].locals.foo, {
937
- response: 'success',
938
- old: false,
939
- func: '<Function object>',
940
- });
941
- assert.deepEqual(stack[0].locals.bar, {
942
- args: '<Array object>',
943
- parent: null,
944
- asyncFunc: '<AsyncFunction object>',
945
- });
946
-
947
- sinon.restore();
948
- },
949
- },
950
- })
951
- .addBatch({
952
- 'mergeLocals called with no locals maps present': {
953
- topic: function () {
954
- var getPropertiesResponses = {
955
- objectId1: [
956
- localsFixtures.locals.object1,
957
- localsFixtures.locals.object2,
958
- ],
959
- objectId2: [
960
- localsFixtures.locals.boolean1,
961
- localsFixtures.locals.boolean2,
962
- ],
963
- objectId3: [
964
- localsFixtures.locals.string1,
965
- localsFixtures.locals.array1,
966
- ],
967
- };
968
-
969
- var locals = new Locals({}, logger);
970
- sinon
971
- .stub(Locals.session, 'post')
972
- .callsFake(fakeSessionPostHandler(getPropertiesResponses));
973
-
974
- // Test with no maps present. 'key' won't match anything.
975
- var key = 'key';
976
- var localsMap = new Map();
977
-
978
- var stack = cloneStack(localsFixtures.stacks.complex);
979
-
980
- var self = this;
981
- locals.mergeLocals(localsMap, stack, key, function (err) {
982
- self.callback(err, stack);
983
- });
984
- },
985
- 'should succeed without merged locals': function (err, stack) {
986
- if (err) {
987
- // Ensure unexpected error can be seen.
988
- console.log(err);
989
- }
990
- assert.isNull(err);
991
-
992
- assert.equal(stack[0].locals, undefined);
993
- assert.equal(stack[1].locals, undefined);
994
- assert.equal(stack[2].locals, undefined);
995
-
996
- sinon.restore();
997
- },
998
- },
999
- })
1000
- .addBatch({
1001
- 'mergeLocals called with no local scopes in map': {
1002
- topic: function () {
1003
- var getPropertiesResponses = {
1004
- objectId1: [
1005
- localsFixtures.locals.object1,
1006
- localsFixtures.locals.object2,
1007
- ],
1008
- objectId2: [
1009
- localsFixtures.locals.boolean1,
1010
- localsFixtures.locals.boolean2,
1011
- ],
1012
- objectId3: [
1013
- localsFixtures.locals.string1,
1014
- localsFixtures.locals.array1,
1015
- ],
1016
- };
1017
-
1018
- var locals = new Locals({}, logger);
1019
- sinon
1020
- .stub(Locals.session, 'post')
1021
- .callsFake(fakeSessionPostHandler(getPropertiesResponses));
1022
-
1023
- var key = 'key';
1024
- var localsMap = new Map();
1025
-
1026
- localsMap.set(key, localsFixtures.maps.noLocalScope);
1027
-
1028
- var stack = cloneStack(localsFixtures.stacks.complex);
1029
-
1030
- var self = this;
1031
- locals.mergeLocals(localsMap, stack, key, function (err) {
1032
- self.callback(err, stack);
1033
- });
1034
- },
1035
- 'should succeed without merged locals': function (err, stack) {
1036
- if (err) {
1037
- // Ensure unexpected error can be seen.
1038
- console.log(err);
1039
- }
1040
- assert.isNull(err);
1041
-
1042
- assert.equal(stack[0].locals, undefined);
1043
- assert.equal(stack[1].locals, undefined);
1044
- assert.equal(stack[2].locals, undefined);
1045
-
1046
- sinon.restore();
1047
- },
1048
- },
1049
- })
1050
- .addBatch({
1051
- 'currentLocalsMap called with no local scopes in map': {
1052
- topic: function () {
1053
- var locals = new Locals({}, logger);
1054
-
1055
- // Ensure empty map, as vows uses the same class object between tests.
1056
- Locals.currentErrors = new Map();
1057
-
1058
- return locals;
1059
- },
1060
- 'should return empty map': function (locals) {
1061
- var localsMap = locals.currentLocalsMap();
1062
-
1063
- assert.instanceOf(localsMap, Map);
1064
- assert.equal(localsMap.size, 0);
1065
- },
1066
- },
1067
- })
1068
- .export(module, { error: false });