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.
- package/.claude/settings.local.json +3 -0
- package/.cursor/rules/guidelines.mdc +154 -0
- package/.github/workflows/ci.yml +4 -6
- package/CLAUDE.local.md +297 -0
- package/CLAUDE.md +201 -0
- package/CLAUDE.testrunner.md +470 -0
- package/Gruntfile.js +59 -16
- package/Makefile +3 -3
- package/SECURITY.md +5 -0
- package/babel.config.json +9 -0
- package/codex.md +148 -0
- package/dist/plugins/jquery.min.js +1 -1
- package/dist/rollbar.js +19332 -6596
- package/dist/rollbar.js.map +1 -1
- package/dist/rollbar.min.js +2 -1
- package/dist/rollbar.min.js.LICENSE.txt +1 -0
- package/dist/rollbar.min.js.map +1 -1
- package/dist/rollbar.named-amd.js +19332 -6596
- package/dist/rollbar.named-amd.js.map +1 -1
- package/dist/rollbar.named-amd.min.js +2 -1
- package/dist/rollbar.named-amd.min.js.LICENSE.txt +1 -0
- package/dist/rollbar.named-amd.min.js.map +1 -1
- package/dist/rollbar.noconflict.umd.js +19319 -6581
- package/dist/rollbar.noconflict.umd.js.map +1 -1
- package/dist/rollbar.noconflict.umd.min.js +2 -1
- package/dist/rollbar.noconflict.umd.min.js.LICENSE.txt +1 -0
- package/dist/rollbar.noconflict.umd.min.js.map +1 -1
- package/dist/rollbar.snippet.js +1 -1
- package/dist/rollbar.umd.js +19333 -6597
- package/dist/rollbar.umd.js.map +1 -1
- package/dist/rollbar.umd.min.js +2 -1
- package/dist/rollbar.umd.min.js.LICENSE.txt +1 -0
- package/dist/rollbar.umd.min.js.map +1 -1
- package/eslint.config.mjs +33 -0
- package/karma.conf.js +5 -14
- package/package.json +19 -20
- package/src/api.js +57 -4
- package/src/apiUtility.js +2 -3
- package/src/browser/core.js +37 -9
- package/src/browser/replay/defaults.js +70 -0
- package/src/browser/replay/recorder.js +194 -0
- package/src/browser/replay/replayMap.js +195 -0
- package/src/browser/rollbar.js +11 -7
- package/src/browser/telemetry.js +3 -3
- package/src/browser/transport/fetch.js +17 -4
- package/src/browser/transport/xhr.js +17 -1
- package/src/browser/transport.js +11 -8
- package/src/defaults.js +1 -1
- package/src/queue.js +65 -4
- package/src/react-native/rollbar.js +1 -1
- package/src/rollbar.js +52 -10
- package/src/server/rollbar.js +3 -2
- package/src/telemetry.js +76 -11
- package/src/tracing/context.js +24 -0
- package/src/tracing/contextManager.js +37 -0
- package/src/tracing/defaults.js +7 -0
- package/src/tracing/exporter.js +188 -0
- package/src/tracing/hrtime.js +98 -0
- package/src/tracing/id.js +24 -0
- package/src/tracing/session.js +55 -0
- package/src/tracing/span.js +92 -0
- package/src/tracing/spanProcessor.js +15 -0
- package/src/tracing/tracer.js +46 -0
- package/src/tracing/tracing.js +89 -0
- package/src/utility.js +34 -0
- package/test/api.test.js +57 -12
- package/test/apiUtility.test.js +5 -6
- package/test/browser.core.test.js +1 -10
- package/test/browser.domUtility.test.js +1 -1
- package/test/browser.predicates.test.js +1 -1
- package/test/browser.replay.recorder.test.js +430 -0
- package/test/browser.rollbar.test.js +58 -12
- package/test/browser.telemetry.test.js +1 -1
- package/test/browser.transforms.test.js +20 -13
- package/test/browser.transport.test.js +5 -4
- package/test/browser.url.test.js +1 -1
- package/test/fixtures/replay/index.js +20 -0
- package/test/fixtures/replay/payloads.fixtures.js +229 -0
- package/test/fixtures/replay/rrwebEvents.fixtures.js +251 -0
- package/test/fixtures/replay/rrwebSyntheticEvents.fixtures.js +328 -0
- package/test/notifier.test.js +1 -1
- package/test/predicates.test.js +1 -1
- package/test/queue.test.js +1 -1
- package/test/rateLimiter.test.js +1 -1
- package/test/react-native.rollbar.test.js +1 -1
- package/test/react-native.transforms.test.js +2 -2
- package/test/react-native.transport.test.js +3 -3
- package/test/replay/index.js +2 -0
- package/test/replay/integration/api.spans.test.js +136 -0
- package/test/replay/integration/e2e.test.js +228 -0
- package/test/replay/integration/index.js +9 -0
- package/test/replay/integration/queue.replayMap.test.js +332 -0
- package/test/replay/integration/replayMap.test.js +163 -0
- package/test/replay/integration/sessionRecording.test.js +390 -0
- package/test/replay/unit/api.postSpans.test.js +150 -0
- package/test/replay/unit/index.js +7 -0
- package/test/replay/unit/queue.replayMap.test.js +225 -0
- package/test/replay/unit/replayMap.test.js +348 -0
- package/test/replay/util/index.js +5 -0
- package/test/replay/util/mockRecordFn.js +80 -0
- package/test/server.lambda.mocha.test.mjs +172 -0
- package/test/server.locals.constructor.mocha.test.mjs +80 -0
- package/test/server.locals.error-handling.mocha.test.mjs +387 -0
- package/test/server.locals.merge.mocha.test.mjs +267 -0
- package/test/server.locals.test-utils.mjs +114 -0
- package/test/server.parser.mocha.test.mjs +87 -0
- package/test/server.predicates.mocha.test.mjs +63 -0
- package/test/server.rollbar.constructor.mocha.test.mjs +199 -0
- package/test/server.rollbar.handlers.mocha.test.mjs +253 -0
- package/test/server.rollbar.logging.mocha.test.mjs +326 -0
- package/test/server.rollbar.misc.mocha.test.mjs +44 -0
- package/test/server.rollbar.test-utils.mjs +57 -0
- package/test/server.telemetry.mocha.test.mjs +377 -0
- package/test/server.transforms.data.mocha.test.mjs +163 -0
- package/test/server.transforms.error.mocha.test.mjs +199 -0
- package/test/server.transforms.request.mocha.test.mjs +208 -0
- package/test/server.transforms.scrub.mocha.test.mjs +140 -0
- package/test/server.transforms.sourcemaps.mocha.test.mjs +122 -0
- package/test/server.transforms.test-utils.mjs +62 -0
- package/test/server.transport.mocha.test.mjs +269 -0
- package/test/telemetry.test.js +132 -1
- package/test/tracing/contextManager.test.js +28 -0
- package/test/tracing/exporter.toPayload.test.js +400 -0
- package/test/tracing/id.test.js +24 -0
- package/test/tracing/span.test.js +183 -0
- package/test/tracing/spanProcessor.test.js +73 -0
- package/test/tracing/tracing.test.js +105 -0
- package/test/transforms.test.js +2 -2
- package/test/truncation.test.js +2 -2
- package/test/utility.test.js +44 -6
- package/webpack.config.js +6 -44
- package/.eslintignore +0 -7
- package/test/server.lambda.test.js +0 -194
- package/test/server.locals.test.js +0 -1068
- package/test/server.parser.test.js +0 -78
- package/test/server.predicates.test.js +0 -91
- package/test/server.rollbar.test.js +0 -728
- package/test/server.telemetry.test.js +0 -443
- package/test/server.transforms.test.js +0 -1193
- package/test/server.transport.test.js +0 -269
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/* globals describe */
|
|
2
|
+
/* globals it */
|
|
3
|
+
/* globals beforeEach */
|
|
4
|
+
|
|
5
|
+
import { expect } from 'chai';
|
|
6
|
+
import sinon from 'sinon';
|
|
7
|
+
import Rollbar from '../src/server/rollbar.js';
|
|
8
|
+
|
|
9
|
+
function promisePending(promise, callback) {
|
|
10
|
+
const testValue = 'test-pending';
|
|
11
|
+
|
|
12
|
+
// Detect if a given promise is pending/unfulfilled using the
|
|
13
|
+
// behavior of Promise.race(), which always returns the first
|
|
14
|
+
// already resolved promise in the array.
|
|
15
|
+
Promise.race([promise, Promise.resolve(testValue)])
|
|
16
|
+
.then(function (value) {
|
|
17
|
+
if (value === testValue) {
|
|
18
|
+
return callback(true);
|
|
19
|
+
} else {
|
|
20
|
+
return callback(false);
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
.catch(function (_error) {
|
|
24
|
+
return callback(false);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function fakePostItem(_item, callback) {
|
|
29
|
+
// 1000ms simulates low API response, and allows testing the state before completion.
|
|
30
|
+
setTimeout(callback, 1000);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const stubContext = {
|
|
34
|
+
getRemainingTimeInMillis: () => 2000,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
describe('lambda', function () {
|
|
38
|
+
describe('async handler sends message', function () {
|
|
39
|
+
let rollbar;
|
|
40
|
+
let handler;
|
|
41
|
+
let requestStub;
|
|
42
|
+
let testPromise;
|
|
43
|
+
|
|
44
|
+
beforeEach(function () {
|
|
45
|
+
rollbar = new Rollbar({
|
|
46
|
+
accessToken: 'abc123',
|
|
47
|
+
captureUncaught: true,
|
|
48
|
+
captureLambdaTimeouts: false,
|
|
49
|
+
});
|
|
50
|
+
const api = rollbar.client.notifier.queue.api;
|
|
51
|
+
requestStub = sinon.stub(api, 'postItem').callsFake(fakePostItem);
|
|
52
|
+
|
|
53
|
+
handler = rollbar.lambdaHandler(async (_event, _context) => {
|
|
54
|
+
// Testing the condition where the client handler sends a message before exit.
|
|
55
|
+
// The Rollbar wrapper should send, and should not resolve until the API request
|
|
56
|
+
// has completed.
|
|
57
|
+
rollbar.info('lambda test message');
|
|
58
|
+
return 'done';
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
afterEach(function () {
|
|
63
|
+
requestStub.restore();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('invokes handler and receives promise', function () {
|
|
67
|
+
// rollbar.lambdaHandler should have returned a
|
|
68
|
+
// handler with the correct signature. (The callback handler is length = 3)
|
|
69
|
+
expect(handler.length).to.equal(2);
|
|
70
|
+
expect(handler.name).to.equal('rollbarAsyncLambdaHandler');
|
|
71
|
+
|
|
72
|
+
// The rollbar.wait() in the wrapper should prevent this from
|
|
73
|
+
// resolving until the response is received.
|
|
74
|
+
testPromise = handler({}, stubContext);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('promise is pending after handler invoked', function (done) {
|
|
78
|
+
testPromise = handler({}, stubContext);
|
|
79
|
+
|
|
80
|
+
// This timeout allows a few extra ticks so that without the wait in
|
|
81
|
+
// the wrapper this test should fail.
|
|
82
|
+
setTimeout(function () {
|
|
83
|
+
promisePending(testPromise, function (pending) {
|
|
84
|
+
expect(pending).to.be.true;
|
|
85
|
+
done();
|
|
86
|
+
});
|
|
87
|
+
}, 10);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('sends message before exit after promise resolved', function (done) {
|
|
91
|
+
testPromise = handler({}, stubContext);
|
|
92
|
+
|
|
93
|
+
testPromise.then(function (value) {
|
|
94
|
+
expect(value).to.equal('done');
|
|
95
|
+
|
|
96
|
+
// If the handler is allowed to exit prematurely, this will fail.
|
|
97
|
+
expect(requestStub.called).to.be.true;
|
|
98
|
+
const data = requestStub.getCall(0).args[0];
|
|
99
|
+
expect(data.body.message.body).to.equal('lambda test message');
|
|
100
|
+
|
|
101
|
+
requestStub.reset();
|
|
102
|
+
done();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('async handler catches exception', function () {
|
|
108
|
+
let rollbar;
|
|
109
|
+
let handler;
|
|
110
|
+
let requestStub;
|
|
111
|
+
let testPromise;
|
|
112
|
+
|
|
113
|
+
beforeEach(function () {
|
|
114
|
+
rollbar = new Rollbar({
|
|
115
|
+
accessToken: 'abc123',
|
|
116
|
+
captureUncaught: true,
|
|
117
|
+
captureLambdaTimeouts: false,
|
|
118
|
+
});
|
|
119
|
+
const api = rollbar.client.notifier.queue.api;
|
|
120
|
+
requestStub = sinon.stub(api, 'postItem').callsFake(fakePostItem);
|
|
121
|
+
|
|
122
|
+
handler = rollbar.lambdaHandler(async (_event, _context) => {
|
|
123
|
+
// Testing the condition where the client handler throws.
|
|
124
|
+
// The Rollbar wrapper should catch and report, and should not reject
|
|
125
|
+
// until the API request has completed.
|
|
126
|
+
throw new Error('lambda test error');
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
afterEach(function () {
|
|
131
|
+
requestStub.restore();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('invokes handler and receives promise', function () {
|
|
135
|
+
// rollbar.lambdaHandler should have returned a
|
|
136
|
+
// handler with the correct signature. (The callback handler is length = 3)
|
|
137
|
+
expect(handler.length).to.equal(2);
|
|
138
|
+
expect(handler.name).to.equal('rollbarAsyncLambdaHandler');
|
|
139
|
+
|
|
140
|
+
// The rollbar.wait() in the wrapper should prevent this from
|
|
141
|
+
// resolving until the response is received.
|
|
142
|
+
testPromise = handler({}, stubContext);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('promise is pending after handler invoked', function (done) {
|
|
146
|
+
testPromise = handler({}, stubContext);
|
|
147
|
+
|
|
148
|
+
promisePending(testPromise, function (pending) {
|
|
149
|
+
expect(pending).to.be.true;
|
|
150
|
+
done();
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('sends message before exit after promise rejected', function (done) {
|
|
155
|
+
testPromise = handler({}, stubContext);
|
|
156
|
+
|
|
157
|
+
testPromise.catch(function (error) {
|
|
158
|
+
expect(error.message).to.equal('lambda test error');
|
|
159
|
+
|
|
160
|
+
// If the handler is allowed to exit prematurely, this will fail.
|
|
161
|
+
expect(requestStub.called).to.be.true;
|
|
162
|
+
const data = requestStub.getCall(0).args[0];
|
|
163
|
+
expect(data.body.trace_chain[0].exception.message).to.equal(
|
|
164
|
+
'lambda test error',
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
requestStub.reset();
|
|
168
|
+
done();
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/* globals describe */
|
|
2
|
+
/* globals it */
|
|
3
|
+
|
|
4
|
+
import { expect } from 'chai';
|
|
5
|
+
import Locals from '../src/server/locals.js';
|
|
6
|
+
import logger from '../src/server/logger.js';
|
|
7
|
+
|
|
8
|
+
describe('server.locals constructor', function () {
|
|
9
|
+
it('should use defaults when passing true boolean', function () {
|
|
10
|
+
const locals = new Locals(true, logger);
|
|
11
|
+
expect(locals.options.enabled).to.be.true;
|
|
12
|
+
expect(locals.options.uncaughtOnly).to.be.true;
|
|
13
|
+
expect(locals.options.depth).to.equal(1);
|
|
14
|
+
expect(locals.options.maxProperties).to.equal(30);
|
|
15
|
+
expect(locals.options.maxArray).to.equal(5);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should use defaults when passing false boolean', function () {
|
|
19
|
+
const locals = new Locals(false, logger);
|
|
20
|
+
expect(locals.options.enabled).to.be.true;
|
|
21
|
+
expect(locals.options.uncaughtOnly).to.be.true;
|
|
22
|
+
expect(locals.options.depth).to.equal(1);
|
|
23
|
+
expect(locals.options.maxProperties).to.equal(30);
|
|
24
|
+
expect(locals.options.maxArray).to.equal(5);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should use defaults when passing empty object', function () {
|
|
28
|
+
const locals = new Locals({}, logger);
|
|
29
|
+
expect(locals.options.enabled).to.be.true;
|
|
30
|
+
expect(locals.options.uncaughtOnly).to.be.true;
|
|
31
|
+
expect(locals.options.depth).to.equal(1);
|
|
32
|
+
expect(locals.options.maxProperties).to.equal(30);
|
|
33
|
+
expect(locals.options.maxArray).to.equal(5);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should use updated depth with remaining defaults', function () {
|
|
37
|
+
const locals = new Locals({ depth: 0 }, logger);
|
|
38
|
+
expect(locals.options.enabled).to.be.true;
|
|
39
|
+
expect(locals.options.uncaughtOnly).to.be.true;
|
|
40
|
+
expect(locals.options.depth).to.equal(0);
|
|
41
|
+
expect(locals.options.maxProperties).to.equal(30);
|
|
42
|
+
expect(locals.options.maxArray).to.equal(5);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should use updated enabled with remaining defaults', function () {
|
|
46
|
+
const locals = new Locals({ enabled: false }, logger);
|
|
47
|
+
expect(locals.options.enabled).to.be.false;
|
|
48
|
+
expect(locals.options.uncaughtOnly).to.be.true;
|
|
49
|
+
expect(locals.options.depth).to.equal(1);
|
|
50
|
+
expect(locals.options.maxProperties).to.equal(30);
|
|
51
|
+
expect(locals.options.maxArray).to.equal(5);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should use updated uncaughtOnly with remaining defaults', function () {
|
|
55
|
+
const locals = new Locals({ uncaughtOnly: false }, logger);
|
|
56
|
+
expect(locals.options.enabled).to.be.true;
|
|
57
|
+
expect(locals.options.uncaughtOnly).to.be.false;
|
|
58
|
+
expect(locals.options.depth).to.equal(1);
|
|
59
|
+
expect(locals.options.maxProperties).to.equal(30);
|
|
60
|
+
expect(locals.options.maxArray).to.equal(5);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should use all updated options', function () {
|
|
64
|
+
const locals = new Locals(
|
|
65
|
+
{
|
|
66
|
+
enabled: false,
|
|
67
|
+
uncaughtOnly: false,
|
|
68
|
+
depth: 2,
|
|
69
|
+
maxProperties: 15,
|
|
70
|
+
maxArray: 10,
|
|
71
|
+
},
|
|
72
|
+
logger,
|
|
73
|
+
);
|
|
74
|
+
expect(locals.options.enabled).to.be.false;
|
|
75
|
+
expect(locals.options.uncaughtOnly).to.be.false;
|
|
76
|
+
expect(locals.options.depth).to.equal(2);
|
|
77
|
+
expect(locals.options.maxProperties).to.equal(15);
|
|
78
|
+
expect(locals.options.maxArray).to.equal(10);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/* globals describe */
|
|
2
|
+
/* globals it */
|
|
3
|
+
/* globals beforeEach */
|
|
4
|
+
/* globals afterEach */
|
|
5
|
+
|
|
6
|
+
import { expect } from 'chai';
|
|
7
|
+
import sinon from 'sinon';
|
|
8
|
+
import Rollbar from '../src/server/rollbar.js';
|
|
9
|
+
import Locals from '../src/server/locals.js';
|
|
10
|
+
import {
|
|
11
|
+
nodeMajorVersion,
|
|
12
|
+
nodeThrow,
|
|
13
|
+
nodeThrowNested,
|
|
14
|
+
nodeThrowAndCatch,
|
|
15
|
+
promiseReject,
|
|
16
|
+
nodeThrowWithNestedLocals,
|
|
17
|
+
nodeThrowRecursionError,
|
|
18
|
+
verifyRejectedPromise,
|
|
19
|
+
} from './server.locals.test-utils.mjs';
|
|
20
|
+
|
|
21
|
+
describe('server.locals error handling', function () {
|
|
22
|
+
let mochaExceptionHandlers;
|
|
23
|
+
let mochaRejectionHandlers;
|
|
24
|
+
|
|
25
|
+
beforeEach(function () {
|
|
26
|
+
// Remove Mocha's error handlers
|
|
27
|
+
mochaExceptionHandlers = process.listeners('uncaughtException');
|
|
28
|
+
mochaExceptionHandlers.forEach((handler) => {
|
|
29
|
+
process.removeListener('uncaughtException', handler);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
mochaRejectionHandlers = process.listeners('unhandledRejection');
|
|
33
|
+
mochaRejectionHandlers.forEach((handler) => {
|
|
34
|
+
process.removeListener('unhandledRejection', handler);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
afterEach(function () {
|
|
39
|
+
// Restore Mocha's error handlers
|
|
40
|
+
mochaExceptionHandlers.forEach((h) => process.on('uncaughtException', h));
|
|
41
|
+
mochaRejectionHandlers.forEach((h) => process.on('unhandledRejection', h));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('with locals enabled', function () {
|
|
45
|
+
let rollbar;
|
|
46
|
+
let addItemStub;
|
|
47
|
+
|
|
48
|
+
beforeEach(function () {
|
|
49
|
+
rollbar = new Rollbar({
|
|
50
|
+
accessToken: 'abc123',
|
|
51
|
+
captureUncaught: true,
|
|
52
|
+
captureUnhandledRejections: true,
|
|
53
|
+
locals: { module: Locals, uncaughtOnly: true, depth: 0 },
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
addItemStub = sinon.stub(rollbar.client.notifier.queue, 'addItem');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
afterEach(function () {
|
|
60
|
+
addItemStub.restore();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should include locals on uncaught error', async function () {
|
|
64
|
+
await nodeThrow();
|
|
65
|
+
expect(addItemStub.called).to.be.true;
|
|
66
|
+
|
|
67
|
+
const traceChain = addItemStub.getCall(0).args[3].data.body.trace_chain;
|
|
68
|
+
expect(traceChain[0].exception.message).to.equal('node error');
|
|
69
|
+
|
|
70
|
+
let frames = traceChain[0].frames;
|
|
71
|
+
expect(frames).to.have.length.above(1);
|
|
72
|
+
|
|
73
|
+
if (nodeMajorVersion >= 18) {
|
|
74
|
+
// Node >=18; locals only in top frame
|
|
75
|
+
expect(frames.at(-1).locals.error).to.equal('<Error object>');
|
|
76
|
+
expect(frames.at(-2).locals).to.not.exist;
|
|
77
|
+
} else if (nodeMajorVersion >= 10) {
|
|
78
|
+
// Node >=10; locals enabled
|
|
79
|
+
expect(frames.at(-1).locals.error).to.equal('<Error object>');
|
|
80
|
+
expect(frames.at(-2).locals.timer).to.equal('<Timeout object>');
|
|
81
|
+
} else {
|
|
82
|
+
// Node <=8; locals disabled
|
|
83
|
+
expect(frames.at(-1).locals).to.not.exist;
|
|
84
|
+
expect(frames.at(-2).locals).to.not.exist;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('then disabled', function () {
|
|
89
|
+
beforeEach(function () {
|
|
90
|
+
addItemStub?.restore();
|
|
91
|
+
rollbar.configure({ locals: { enabled: false } });
|
|
92
|
+
addItemStub = sinon.stub(rollbar.client.notifier.queue, 'addItem');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should not include locals', async function () {
|
|
96
|
+
await nodeThrowNested();
|
|
97
|
+
expect(addItemStub.called).to.be.true;
|
|
98
|
+
|
|
99
|
+
const traceChain = addItemStub.getCall(0).args[3].data.body.trace_chain;
|
|
100
|
+
expect(traceChain[0].exception.message).to.equal('test err');
|
|
101
|
+
expect(traceChain[1].exception.message).to.equal('nested test err');
|
|
102
|
+
|
|
103
|
+
expect(traceChain[0].frames).to.have.length.above(1);
|
|
104
|
+
expect(traceChain[0].frames.at(-1).locals).to.not.exist;
|
|
105
|
+
expect(traceChain[0].frames.at(-2).locals).to.not.exist;
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('then re-enabled', function () {
|
|
109
|
+
beforeEach(function () {
|
|
110
|
+
addItemStub?.restore();
|
|
111
|
+
rollbar.configure({ locals: { enabled: true, uncaughtOnly: false } });
|
|
112
|
+
addItemStub = sinon.stub(rollbar.client.notifier.queue, 'addItem');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should include locals', async function () {
|
|
116
|
+
await promiseReject();
|
|
117
|
+
|
|
118
|
+
const result = verifyRejectedPromise(addItemStub);
|
|
119
|
+
expect(result.message).to.equal('promise reject');
|
|
120
|
+
expect(result.hasLocals).to.be.true;
|
|
121
|
+
expect(result.locals.topFrame).to.exist;
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('on caught error', function () {
|
|
128
|
+
describe('with uncaughtOnly: true', function () {
|
|
129
|
+
let rollbar;
|
|
130
|
+
let addItemStub;
|
|
131
|
+
|
|
132
|
+
beforeEach(function () {
|
|
133
|
+
rollbar = new Rollbar({
|
|
134
|
+
accessToken: 'abc123',
|
|
135
|
+
captureUncaught: true,
|
|
136
|
+
captureUnhandledRejections: true,
|
|
137
|
+
locals: { module: Locals, uncaughtOnly: true, depth: 0 },
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
addItemStub = sinon.stub(rollbar.client.notifier.queue, 'addItem');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
afterEach(function () {
|
|
144
|
+
addItemStub.restore();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should not include locals', async function () {
|
|
148
|
+
await nodeThrowAndCatch(rollbar);
|
|
149
|
+
expect(addItemStub.called).to.be.true;
|
|
150
|
+
|
|
151
|
+
const traceChain = addItemStub.getCall(0).args[3].data.body.trace_chain;
|
|
152
|
+
expect(traceChain[0].exception.message).to.equal('caught error');
|
|
153
|
+
expect(traceChain[0].frames).to.have.length.above(1);
|
|
154
|
+
expect(traceChain[0].frames.at(-1).locals).to.not.exist;
|
|
155
|
+
expect(traceChain[0].frames.at(-2).locals).to.not.exist;
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe('then uncaughtOnly: false', function () {
|
|
159
|
+
beforeEach(function () {
|
|
160
|
+
addItemStub?.restore();
|
|
161
|
+
rollbar.configure({ locals: { uncaughtOnly: false } });
|
|
162
|
+
addItemStub = sinon.stub(rollbar.client.notifier.queue, 'addItem');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should include locals', async function () {
|
|
166
|
+
await nodeThrowAndCatch(rollbar);
|
|
167
|
+
expect(addItemStub.called).to.be.true;
|
|
168
|
+
|
|
169
|
+
const traceChain =
|
|
170
|
+
addItemStub.getCall(0).args[3].data.body.trace_chain;
|
|
171
|
+
expect(traceChain[0].exception.message).to.equal('caught error');
|
|
172
|
+
|
|
173
|
+
let frames = traceChain[0].frames;
|
|
174
|
+
expect(frames).to.have.length.above(1);
|
|
175
|
+
|
|
176
|
+
if (nodeMajorVersion >= 18) {
|
|
177
|
+
// Node >=18; locals only in top frame
|
|
178
|
+
expect(frames.at(-1).locals.error).to.equal('<Error object>');
|
|
179
|
+
expect(frames.at(-2).locals).to.not.exist;
|
|
180
|
+
} else if (nodeMajorVersion >= 10) {
|
|
181
|
+
// Node 10..<18; locals enabled
|
|
182
|
+
expect(frames.at(-1).locals.error).to.equal('<Error object>');
|
|
183
|
+
expect(frames.at(-2).locals.timer).to.equal('<Timeout object>');
|
|
184
|
+
} else {
|
|
185
|
+
expect(frames.at(-1).locals).to.not.exist;
|
|
186
|
+
expect(frames.at(-2).locals).to.not.exist;
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
describe('on nested exception', function () {
|
|
194
|
+
let rollbar;
|
|
195
|
+
let addItemStub;
|
|
196
|
+
|
|
197
|
+
beforeEach(function () {
|
|
198
|
+
rollbar = new Rollbar({
|
|
199
|
+
accessToken: 'abc123',
|
|
200
|
+
captureUncaught: true,
|
|
201
|
+
locals: { module: Locals, uncaughtOnly: false, depth: 0 },
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
addItemStub = sinon.stub(rollbar.client.notifier.queue, 'addItem');
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
afterEach(function () {
|
|
208
|
+
addItemStub.restore();
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('should include locals', async function () {
|
|
212
|
+
await nodeThrowNested();
|
|
213
|
+
expect(addItemStub.called).to.be.true;
|
|
214
|
+
|
|
215
|
+
const traceChain = addItemStub.getCall(0).args[3].data.body.trace_chain;
|
|
216
|
+
expect(traceChain[0].exception.message).to.equal('test err');
|
|
217
|
+
expect(traceChain[1].exception.message).to.equal('nested test err');
|
|
218
|
+
|
|
219
|
+
let frames;
|
|
220
|
+
|
|
221
|
+
if (nodeMajorVersion >= 18) {
|
|
222
|
+
// Node >=18; locals only in top frame
|
|
223
|
+
frames = traceChain[0].frames;
|
|
224
|
+
expect(frames).to.have.length.above(1);
|
|
225
|
+
expect(frames.at(-1).locals.message).to.equal('test err');
|
|
226
|
+
expect(frames.at(-1).locals.password).to.equal('********');
|
|
227
|
+
expect(frames.at(-1).locals.err).to.equal('<Error object>');
|
|
228
|
+
expect(frames.at(-1).locals.newMessage).to.equal('nested test err');
|
|
229
|
+
expect(frames.at(-2).locals).to.not.exist;
|
|
230
|
+
|
|
231
|
+
frames = traceChain[1].frames;
|
|
232
|
+
expect(frames).to.have.length.above(1);
|
|
233
|
+
expect(frames.at(-1).locals.nestedMessage).to.equal('nested test err');
|
|
234
|
+
expect(frames.at(-1).locals._password).to.equal('123456');
|
|
235
|
+
expect(frames.at(-1).locals.nestedError).to.equal('<Error object>');
|
|
236
|
+
expect(frames.at(-2).locals).to.not.exist;
|
|
237
|
+
} else if (nodeMajorVersion >= 10) {
|
|
238
|
+
// Node >=10; locals enabled
|
|
239
|
+
frames = traceChain[0].frames;
|
|
240
|
+
expect(frames).to.have.length.above(1);
|
|
241
|
+
expect(frames.at(-1).locals.err).to.equal('<Error object>');
|
|
242
|
+
expect(frames.at(-2).locals.timer).to.equal('<Timeout object>');
|
|
243
|
+
|
|
244
|
+
frames = traceChain[1].frames;
|
|
245
|
+
expect(frames).to.have.length.above(1);
|
|
246
|
+
expect(frames.at(-1).locals.nestedMessage).to.equal('nested test err');
|
|
247
|
+
expect(frames.at(-1).locals.nestedError).to.equal('<Error object>');
|
|
248
|
+
expect(frames.at(-2).locals.message).to.equal('test err');
|
|
249
|
+
expect(frames.at(-2).locals.password).to.equal('********');
|
|
250
|
+
expect(frames.at(-2).locals.err).to.equal('<Error object>');
|
|
251
|
+
expect(frames.at(-2).locals.newMessage).to.equal('nested test err');
|
|
252
|
+
} else {
|
|
253
|
+
// Node <=8; locals disabled
|
|
254
|
+
frames = traceChain[0].frames;
|
|
255
|
+
expect(frames).to.have.length.above(1);
|
|
256
|
+
expect(frames.at(-1).locals).to.not.exist;
|
|
257
|
+
expect(frames.at(-2).locals).to.not.exist;
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
describe('on promise rejection', function () {
|
|
263
|
+
let rollbar;
|
|
264
|
+
let addItemStub;
|
|
265
|
+
|
|
266
|
+
beforeEach(function () {
|
|
267
|
+
rollbar = new Rollbar({
|
|
268
|
+
accessToken: 'abc123',
|
|
269
|
+
captureUnhandledRejections: true,
|
|
270
|
+
locals: { module: Locals, uncaughtOnly: false, depth: 0 },
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
addItemStub = sinon.stub(rollbar.client.notifier.queue, 'addItem');
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
afterEach(function () {
|
|
277
|
+
addItemStub.restore();
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('should include locals', async function () {
|
|
281
|
+
await promiseReject();
|
|
282
|
+
const result = verifyRejectedPromise(addItemStub);
|
|
283
|
+
expect(result.message).to.equal('promise reject');
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
describe('with custom options', function () {
|
|
288
|
+
let rollbar;
|
|
289
|
+
let addItemStub;
|
|
290
|
+
|
|
291
|
+
beforeEach(function () {
|
|
292
|
+
rollbar = new Rollbar({
|
|
293
|
+
accessToken: 'abc123',
|
|
294
|
+
captureUncaught: true,
|
|
295
|
+
locals: { module: Locals, depth: 2, maxProperties: 5, maxArray: 2 },
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
addItemStub = sinon.stub(rollbar.client.notifier.queue, 'addItem');
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
afterEach(function () {
|
|
302
|
+
addItemStub.restore();
|
|
303
|
+
Locals.session = undefined;
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('should include locals', async function () {
|
|
307
|
+
await nodeThrowWithNestedLocals();
|
|
308
|
+
expect(addItemStub.called).to.be.true;
|
|
309
|
+
|
|
310
|
+
const traceChain = addItemStub.getCall(0).args[3].data.body.trace_chain;
|
|
311
|
+
expect(traceChain[0].exception.message).to.equal('node error');
|
|
312
|
+
|
|
313
|
+
if (nodeMajorVersion < 10) {
|
|
314
|
+
// Node 8; locals disabled
|
|
315
|
+
expect(traceChain[0].frames).to.have.length.above(1);
|
|
316
|
+
expect(traceChain[0].frames.at(-1).locals).to.not.exist;
|
|
317
|
+
} else {
|
|
318
|
+
expect(traceChain[0].frames).to.have.length.above(1);
|
|
319
|
+
|
|
320
|
+
const locals = traceChain[0].frames.at(-1).locals;
|
|
321
|
+
expect(Object.keys(locals.obj)).to.have.lengthOf(5);
|
|
322
|
+
expect(locals.obj).to.deep.equal({
|
|
323
|
+
a: 'a',
|
|
324
|
+
b: 'b',
|
|
325
|
+
c: 'c',
|
|
326
|
+
d: 'd',
|
|
327
|
+
e: 'e',
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
expect(locals.arr).to.have.lengthOf(2);
|
|
331
|
+
expect(locals.arr[0]).to.deep.equal({ zero: '<Array object>' });
|
|
332
|
+
expect(locals.arr[1]).to.deep.equal({ one: 1 });
|
|
333
|
+
|
|
334
|
+
expect(locals.password).to.equal('********');
|
|
335
|
+
expect(locals.sym).to.equal('Symbol(foo)');
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
describe('with recursive stack', function () {
|
|
341
|
+
let rollbar;
|
|
342
|
+
let addItemStub;
|
|
343
|
+
|
|
344
|
+
beforeEach(function () {
|
|
345
|
+
rollbar = new Rollbar({
|
|
346
|
+
accessToken: 'abc123',
|
|
347
|
+
captureUncaught: true,
|
|
348
|
+
locals: Locals,
|
|
349
|
+
});
|
|
350
|
+
const notifier = rollbar.client.notifier;
|
|
351
|
+
addItemStub = sinon.stub(notifier.queue, 'addItem');
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
afterEach(function () {
|
|
355
|
+
addItemStub.restore();
|
|
356
|
+
Locals.session = undefined;
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('should include locals in recursive frames', async function () {
|
|
360
|
+
await nodeThrowRecursionError();
|
|
361
|
+
expect(addItemStub.called).to.be.true;
|
|
362
|
+
|
|
363
|
+
const traceChain = addItemStub.getCall(0).args[3].data.body.trace_chain;
|
|
364
|
+
expect(traceChain[0].exception.message).to.equal(
|
|
365
|
+
'deep stack error, limit=3',
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
let frames = traceChain[0].frames;
|
|
369
|
+
expect(frames).to.have.length.above(1);
|
|
370
|
+
|
|
371
|
+
if (nodeMajorVersion >= 18) {
|
|
372
|
+
// Node >=18; locals only in top frame
|
|
373
|
+
expect(frames.at(-1).locals).to.deep.equal({ curr: 3, limit: 3 });
|
|
374
|
+
expect(frames.at(-2).locals).to.not.exist;
|
|
375
|
+
expect(frames.at(-3).locals).to.not.exist;
|
|
376
|
+
} else if (nodeMajorVersion >= 10) {
|
|
377
|
+
// Node >=10; locals enabled
|
|
378
|
+
expect(frames.at(-1).locals).to.deep.equal({ curr: 3, limit: 3 });
|
|
379
|
+
expect(frames.at(-2).locals).to.deep.equal({ curr: 2, limit: 3 });
|
|
380
|
+
expect(frames.at(-3).locals).to.deep.equal({ curr: 1, limit: 3 });
|
|
381
|
+
} else {
|
|
382
|
+
// Node <=8; locals disabled
|
|
383
|
+
expect(frames.at(-1).locals).to.not.exist;
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
});
|