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,267 @@
|
|
|
1
|
+
/* globals describe */
|
|
2
|
+
/* globals it */
|
|
3
|
+
/* globals afterEach */
|
|
4
|
+
|
|
5
|
+
import { expect } from 'chai';
|
|
6
|
+
import sinon from 'sinon';
|
|
7
|
+
import Locals from '../src/server/locals.js';
|
|
8
|
+
import logger from '../src/server/logger.js';
|
|
9
|
+
import localsFixtures from './fixtures/locals.fixtures.js';
|
|
10
|
+
|
|
11
|
+
describe('server.locals merge', function () {
|
|
12
|
+
afterEach(function () {
|
|
13
|
+
sinon.restore();
|
|
14
|
+
Locals.session = undefined;
|
|
15
|
+
Locals.currentErrors = new Map();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('mergeLocals', function () {
|
|
19
|
+
// Deep clone, because stack gets modified by mergeLocals
|
|
20
|
+
// and we don't want to modify the test fixtures.
|
|
21
|
+
const cloneStack = (stack) => JSON.parse(JSON.stringify(stack));
|
|
22
|
+
|
|
23
|
+
function fakeSessionPostHandler(responses) {
|
|
24
|
+
return (command, options, callback) => {
|
|
25
|
+
let error;
|
|
26
|
+
let response;
|
|
27
|
+
|
|
28
|
+
if (command === 'Runtime.getProperties') {
|
|
29
|
+
response = { result: responses[options.objectId] };
|
|
30
|
+
} else {
|
|
31
|
+
error = new Error('Unexpected session.post command');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
callback(error, response);
|
|
36
|
+
}, 1);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
it('should callback with error when session.post() returns error', function (done) {
|
|
41
|
+
const locals = new Locals({}, logger);
|
|
42
|
+
const err = new Error('post error');
|
|
43
|
+
sinon.stub(Locals.session, 'post').yields(err);
|
|
44
|
+
|
|
45
|
+
const key = 'key';
|
|
46
|
+
const localsMap = new Map();
|
|
47
|
+
localsMap.set('key', localsFixtures.maps.simple);
|
|
48
|
+
|
|
49
|
+
const stack = localsFixtures.stacks.simple;
|
|
50
|
+
|
|
51
|
+
locals.mergeLocals(localsMap, stack, key, (err) => {
|
|
52
|
+
expect(err).to.be.instanceOf(Error);
|
|
53
|
+
expect(err.message).to.equal('post error');
|
|
54
|
+
expect(err.stack).to.include('Error: post error');
|
|
55
|
+
done();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should callback with merged locals when multiple/complex locals maps present', function (done) {
|
|
60
|
+
const getPropertiesResponses = {
|
|
61
|
+
objectId1: [
|
|
62
|
+
localsFixtures.locals.object1,
|
|
63
|
+
localsFixtures.locals.object2,
|
|
64
|
+
],
|
|
65
|
+
objectId2: [
|
|
66
|
+
localsFixtures.locals.boolean1,
|
|
67
|
+
localsFixtures.locals.boolean2,
|
|
68
|
+
],
|
|
69
|
+
objectId3: [
|
|
70
|
+
localsFixtures.locals.string1,
|
|
71
|
+
localsFixtures.locals.array1,
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const locals = new Locals({ depth: 0 }, logger);
|
|
76
|
+
sinon
|
|
77
|
+
.stub(Locals.session, 'post')
|
|
78
|
+
.callsFake(fakeSessionPostHandler(getPropertiesResponses));
|
|
79
|
+
|
|
80
|
+
const key1 = 'key1';
|
|
81
|
+
const key2 = 'key2';
|
|
82
|
+
const localsMap = new Map();
|
|
83
|
+
|
|
84
|
+
// Test with multiple maps present.
|
|
85
|
+
localsMap.set(key1, localsFixtures.maps.simple);
|
|
86
|
+
localsMap.set(key2, localsFixtures.maps.complex); // Stack will match the 2nd locals map added.
|
|
87
|
+
|
|
88
|
+
const stack = cloneStack(localsFixtures.stacks.complex);
|
|
89
|
+
|
|
90
|
+
locals.mergeLocals(localsMap, stack, key2, (err) => {
|
|
91
|
+
expect(err).to.not.exist;
|
|
92
|
+
|
|
93
|
+
expect(stack[0].locals.response).to.equal('success');
|
|
94
|
+
expect(stack[0].locals.args).to.equal('<Array object>');
|
|
95
|
+
expect(stack[1].locals.old).to.be.false;
|
|
96
|
+
expect(stack[1].locals.new).to.be.true;
|
|
97
|
+
expect(stack[2].locals.foo).to.equal('<FooClass object>');
|
|
98
|
+
expect(stack[2].locals.bar).to.equal('<BarClass object>');
|
|
99
|
+
|
|
100
|
+
done();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should callback with merged locals when simple locals maps present', function (done) {
|
|
105
|
+
const getPropertiesResponses = {
|
|
106
|
+
objectId1: [
|
|
107
|
+
localsFixtures.locals.object1,
|
|
108
|
+
localsFixtures.locals.object2,
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const locals = new Locals({ depth: 0 }, logger);
|
|
113
|
+
sinon
|
|
114
|
+
.stub(Locals.session, 'post')
|
|
115
|
+
.callsFake(fakeSessionPostHandler(getPropertiesResponses));
|
|
116
|
+
|
|
117
|
+
const key = 'key';
|
|
118
|
+
const localsMap = new Map();
|
|
119
|
+
localsMap.set(key, localsFixtures.maps.simple);
|
|
120
|
+
|
|
121
|
+
const stack = cloneStack(localsFixtures.stacks.simple);
|
|
122
|
+
|
|
123
|
+
locals.mergeLocals(localsMap, stack, key, (err) => {
|
|
124
|
+
expect(err).to.not.exist;
|
|
125
|
+
expect(stack[0].locals.foo).to.equal('<FooClass object>');
|
|
126
|
+
expect(stack[0].locals.bar).to.equal('<BarClass object>');
|
|
127
|
+
|
|
128
|
+
done();
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should callback with merged locals when depth = 1', function (done) {
|
|
133
|
+
const getPropertiesResponses = {
|
|
134
|
+
objectId1: [
|
|
135
|
+
localsFixtures.locals.object1,
|
|
136
|
+
localsFixtures.locals.object2,
|
|
137
|
+
],
|
|
138
|
+
nestedProps1: [
|
|
139
|
+
localsFixtures.locals.string1,
|
|
140
|
+
localsFixtures.locals.boolean1,
|
|
141
|
+
localsFixtures.locals.function1,
|
|
142
|
+
],
|
|
143
|
+
nestedProps2: [
|
|
144
|
+
localsFixtures.locals.array1,
|
|
145
|
+
localsFixtures.locals.null1,
|
|
146
|
+
localsFixtures.locals.function2,
|
|
147
|
+
],
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const locals = new Locals({ depth: 1 }, logger);
|
|
151
|
+
sinon
|
|
152
|
+
.stub(Locals.session, 'post')
|
|
153
|
+
.callsFake(fakeSessionPostHandler(getPropertiesResponses));
|
|
154
|
+
|
|
155
|
+
const key = 'key';
|
|
156
|
+
const localsMap = new Map();
|
|
157
|
+
localsMap.set(key, localsFixtures.maps.simple);
|
|
158
|
+
|
|
159
|
+
const stack = cloneStack(localsFixtures.stacks.simple);
|
|
160
|
+
|
|
161
|
+
locals.mergeLocals(localsMap, stack, key, (err) => {
|
|
162
|
+
expect(err).to.not.exist;
|
|
163
|
+
|
|
164
|
+
expect(stack[0].locals.foo).to.deep.equal({
|
|
165
|
+
response: 'success',
|
|
166
|
+
old: false,
|
|
167
|
+
func: '<Function object>',
|
|
168
|
+
});
|
|
169
|
+
expect(stack[0].locals.bar).to.deep.equal({
|
|
170
|
+
args: '<Array object>',
|
|
171
|
+
parent: null,
|
|
172
|
+
asyncFunc: '<AsyncFunction object>',
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
done();
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should succeed without merged locals when no locals maps present', function (done) {
|
|
180
|
+
const getPropertiesResponses = {
|
|
181
|
+
objectId1: [
|
|
182
|
+
localsFixtures.locals.object1,
|
|
183
|
+
localsFixtures.locals.object2,
|
|
184
|
+
],
|
|
185
|
+
objectId2: [
|
|
186
|
+
localsFixtures.locals.boolean1,
|
|
187
|
+
localsFixtures.locals.boolean2,
|
|
188
|
+
],
|
|
189
|
+
objectId3: [
|
|
190
|
+
localsFixtures.locals.string1,
|
|
191
|
+
localsFixtures.locals.array1,
|
|
192
|
+
],
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const locals = new Locals({}, logger);
|
|
196
|
+
sinon
|
|
197
|
+
.stub(Locals.session, 'post')
|
|
198
|
+
.callsFake(fakeSessionPostHandler(getPropertiesResponses));
|
|
199
|
+
|
|
200
|
+
// Test with no maps present. 'key' won't match anything.
|
|
201
|
+
const key = 'key';
|
|
202
|
+
const localsMap = new Map();
|
|
203
|
+
|
|
204
|
+
const stack = cloneStack(localsFixtures.stacks.complex);
|
|
205
|
+
|
|
206
|
+
locals.mergeLocals(localsMap, stack, key, (err) => {
|
|
207
|
+
expect(err).to.not.exist;
|
|
208
|
+
|
|
209
|
+
expect(stack[0].locals).to.not.exist;
|
|
210
|
+
expect(stack[1].locals).to.not.exist;
|
|
211
|
+
expect(stack[2].locals).to.not.exist;
|
|
212
|
+
|
|
213
|
+
done();
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('should succeed without merged locals when no local scopes in map', function (done) {
|
|
218
|
+
const getPropertiesResponses = {
|
|
219
|
+
objectId1: [
|
|
220
|
+
localsFixtures.locals.object1,
|
|
221
|
+
localsFixtures.locals.object2,
|
|
222
|
+
],
|
|
223
|
+
objectId2: [
|
|
224
|
+
localsFixtures.locals.boolean1,
|
|
225
|
+
localsFixtures.locals.boolean2,
|
|
226
|
+
],
|
|
227
|
+
objectId3: [
|
|
228
|
+
localsFixtures.locals.string1,
|
|
229
|
+
localsFixtures.locals.array1,
|
|
230
|
+
],
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const locals = new Locals({}, logger);
|
|
234
|
+
sinon
|
|
235
|
+
.stub(Locals.session, 'post')
|
|
236
|
+
.callsFake(fakeSessionPostHandler(getPropertiesResponses));
|
|
237
|
+
|
|
238
|
+
const localsMap = new Map();
|
|
239
|
+
localsMap.set('key', localsFixtures.maps.noLocalScope);
|
|
240
|
+
|
|
241
|
+
const stack = cloneStack(localsFixtures.stacks.complex);
|
|
242
|
+
|
|
243
|
+
locals.mergeLocals(localsMap, stack, 'key', (err) => {
|
|
244
|
+
expect(err).to.not.exist;
|
|
245
|
+
|
|
246
|
+
expect(stack[0].locals).to.not.exist;
|
|
247
|
+
expect(stack[1].locals).to.not.exist;
|
|
248
|
+
expect(stack[2].locals).to.not.exist;
|
|
249
|
+
|
|
250
|
+
done();
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
describe('currentLocalsMap', function () {
|
|
256
|
+
it('should return empty map when no local scopes in map', function () {
|
|
257
|
+
const locals = new Locals({}, logger);
|
|
258
|
+
|
|
259
|
+
// Ensure empty map, as singleton might have state from other tests
|
|
260
|
+
Locals.currentErrors = new Map();
|
|
261
|
+
|
|
262
|
+
const localsMap = locals.currentLocalsMap();
|
|
263
|
+
expect(localsMap).to.be.instanceOf(Map);
|
|
264
|
+
expect(localsMap).to.be.empty;
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common test utilities for server locals tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const nodeMajorVersion = parseInt(process.versions.node.split('.')[0]);
|
|
6
|
+
|
|
7
|
+
export async function wait(ms) {
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
setTimeout(resolve, ms);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function promiseReject() {
|
|
14
|
+
const error = new Error('promise reject');
|
|
15
|
+
Promise.reject(error);
|
|
16
|
+
await wait(500);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function nodeThrow() {
|
|
20
|
+
setTimeout(() => {
|
|
21
|
+
const error = new Error('node error');
|
|
22
|
+
throw error;
|
|
23
|
+
}, 1);
|
|
24
|
+
|
|
25
|
+
await wait(500);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function nodeThrowAndCatch(rollbar) {
|
|
29
|
+
setTimeout(() => {
|
|
30
|
+
const error = new Error('caught error');
|
|
31
|
+
try {
|
|
32
|
+
throw error;
|
|
33
|
+
} catch (e) {
|
|
34
|
+
rollbar.error(e);
|
|
35
|
+
}
|
|
36
|
+
}, 1);
|
|
37
|
+
|
|
38
|
+
await wait(500);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function nodeThrowNested() {
|
|
42
|
+
function nestedError(nestedMessage, _password) {
|
|
43
|
+
const nestedError = new Error(nestedMessage);
|
|
44
|
+
throw nestedError;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
setTimeout(() => {
|
|
48
|
+
const message = 'test err';
|
|
49
|
+
const newMessage = `nested ${message}`;
|
|
50
|
+
const password = '123456';
|
|
51
|
+
const err = new Error(message);
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
nestedError(newMessage, password);
|
|
55
|
+
} catch (e) {
|
|
56
|
+
err.nested = e;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
throw err;
|
|
60
|
+
}, 1);
|
|
61
|
+
|
|
62
|
+
await wait(500);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function nodeThrowWithNestedLocals() {
|
|
66
|
+
setTimeout(() => {
|
|
67
|
+
const arr = [{ zero: [0, 0] }, { one: 1 }, { two: 2 }, { three: 3 }];
|
|
68
|
+
const obj = { a: 'a', b: 'b', c: 'c', d: 'd', e: 'e', f: 'f' };
|
|
69
|
+
const password = 'password';
|
|
70
|
+
const sym = Symbol('foo');
|
|
71
|
+
const error = new Error('node error');
|
|
72
|
+
throw error;
|
|
73
|
+
}, 1);
|
|
74
|
+
|
|
75
|
+
await wait(500);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function nodeThrowRecursionError() {
|
|
79
|
+
function recurse(curr, limit) {
|
|
80
|
+
if (curr < limit) {
|
|
81
|
+
recurse(curr + 1, limit);
|
|
82
|
+
} else {
|
|
83
|
+
throw new Error('deep stack error, limit=' + limit);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
setTimeout(() => recurse(0, 3), 1);
|
|
88
|
+
await wait(500);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function verifyRejectedPromise(addItemStub) {
|
|
92
|
+
const traceChain = addItemStub.getCall(0).args[3].data.body.trace_chain;
|
|
93
|
+
const frames = traceChain[0].frames;
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
message: traceChain[0].exception.message,
|
|
97
|
+
hasLocals: nodeMajorVersion >= 10,
|
|
98
|
+
locals:
|
|
99
|
+
nodeMajorVersion >= 18
|
|
100
|
+
? {
|
|
101
|
+
topFrame: frames.at(-1).locals,
|
|
102
|
+
secondFrame: frames.at(-2).locals,
|
|
103
|
+
}
|
|
104
|
+
: nodeMajorVersion >= 10
|
|
105
|
+
? {
|
|
106
|
+
topFrame: frames.at(-1).locals,
|
|
107
|
+
secondFrame: frames.at(-2).locals,
|
|
108
|
+
}
|
|
109
|
+
: {
|
|
110
|
+
topFrame: undefined,
|
|
111
|
+
secondFrame: undefined,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* globals describe */
|
|
2
|
+
/* globals it */
|
|
3
|
+
/* globals beforeEach */
|
|
4
|
+
|
|
5
|
+
import { expect } from 'chai';
|
|
6
|
+
import * as p from '../src/server/parser.js';
|
|
7
|
+
|
|
8
|
+
describe('parser', function () {
|
|
9
|
+
describe('parseStack', function () {
|
|
10
|
+
describe('a valid stack trace', function () {
|
|
11
|
+
let frames;
|
|
12
|
+
let parseError;
|
|
13
|
+
|
|
14
|
+
beforeEach(function (done) {
|
|
15
|
+
const item = { diagnostic: {} };
|
|
16
|
+
const stack =
|
|
17
|
+
'ReferenceError: foo is not defined\n' +
|
|
18
|
+
' at MethodClass.method.<anonymous> (app/server.js:2:4)\n' +
|
|
19
|
+
' at /app/node_modules/client.js:321:23\n' +
|
|
20
|
+
' at (/app/node_modules/client.js:321:23)\n' +
|
|
21
|
+
' at MethodClass.method.(anonymous) (app/server.js:62:14)\n' +
|
|
22
|
+
' at MethodClass.method (app/server.ts:52:4)\n' +
|
|
23
|
+
' at MethodClass.method (app/server.js:62:14)\n';
|
|
24
|
+
|
|
25
|
+
p.parseStack(stack, {}, item, function (err, parsedFrames) {
|
|
26
|
+
parseError = err;
|
|
27
|
+
frames = parsedFrames;
|
|
28
|
+
done();
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should parse valid js frame', function () {
|
|
33
|
+
const frame = frames[0];
|
|
34
|
+
expect(parseError).to.be.null;
|
|
35
|
+
expect(frame.method).to.equal('MethodClass.method');
|
|
36
|
+
expect(frame.filename).to.equal('app/server.js');
|
|
37
|
+
expect(frame.lineno).to.equal(62);
|
|
38
|
+
expect(frame.colno).to.equal(14 - 1);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should parse valid ts frame', function () {
|
|
42
|
+
const frame = frames[1];
|
|
43
|
+
expect(parseError).to.be.null;
|
|
44
|
+
expect(frame.method).to.equal('MethodClass.method');
|
|
45
|
+
expect(frame.filename).to.equal('app/server.ts');
|
|
46
|
+
expect(frame.lineno).to.equal(52);
|
|
47
|
+
expect(frame.colno).to.equal(4 - 1);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should parse method with parens', function () {
|
|
51
|
+
const frame = frames[2];
|
|
52
|
+
expect(parseError).to.be.null;
|
|
53
|
+
expect(frame.method).to.equal('MethodClass.method.(anonymous)');
|
|
54
|
+
expect(frame.filename).to.equal('app/server.js');
|
|
55
|
+
expect(frame.lineno).to.equal(62);
|
|
56
|
+
expect(frame.colno).to.equal(14 - 1);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should parse without method and with leading slash', function () {
|
|
60
|
+
const frame = frames[3];
|
|
61
|
+
expect(parseError).to.be.null;
|
|
62
|
+
expect(frame.method).to.equal('<unknown>');
|
|
63
|
+
expect(frame.filename).to.equal('/app/node_modules/client.js');
|
|
64
|
+
expect(frame.lineno).to.equal(321);
|
|
65
|
+
expect(frame.colno).to.equal(23 - 1);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should parse without method or parens', function () {
|
|
69
|
+
const frame = frames[4];
|
|
70
|
+
expect(parseError).to.be.null;
|
|
71
|
+
expect(frame.method).to.equal('<unknown>');
|
|
72
|
+
expect(frame.filename).to.equal('/app/node_modules/client.js');
|
|
73
|
+
expect(frame.lineno).to.equal(321);
|
|
74
|
+
expect(frame.colno).to.equal(23 - 1);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should parse method with angle brackets', function () {
|
|
78
|
+
const frame = frames[5];
|
|
79
|
+
expect(parseError).to.be.null;
|
|
80
|
+
expect(frame.method).to.equal('MethodClass.method.<anonymous>');
|
|
81
|
+
expect(frame.filename).to.equal('app/server.js');
|
|
82
|
+
expect(frame.lineno).to.equal(2);
|
|
83
|
+
expect(frame.colno).to.equal(4 - 1);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/* globals describe */
|
|
2
|
+
/* globals it */
|
|
3
|
+
|
|
4
|
+
import { expect } from 'chai';
|
|
5
|
+
import * as p from '../src/predicates.js';
|
|
6
|
+
|
|
7
|
+
describe('predicates', function () {
|
|
8
|
+
describe('checkLevel', function () {
|
|
9
|
+
describe('an item without a level', function () {
|
|
10
|
+
const item = { body: 'nothing' };
|
|
11
|
+
|
|
12
|
+
it('should not send with a critical reportLevel', function () {
|
|
13
|
+
const settings = { reportLevel: 'critical' };
|
|
14
|
+
const result = p.checkLevel(item, settings);
|
|
15
|
+
expect(result).to.be.false;
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('an item with an unknown level', function () {
|
|
20
|
+
const item = { level: 'wooo' };
|
|
21
|
+
|
|
22
|
+
it('should not send with an error reportLevel', function () {
|
|
23
|
+
const settings = { reportLevel: 'error' };
|
|
24
|
+
const result = p.checkLevel(item, settings);
|
|
25
|
+
expect(result).to.be.false;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should send with an unknown reportLevel', function () {
|
|
29
|
+
const settings = { reportLevel: 'yesss' };
|
|
30
|
+
const result = p.checkLevel(item, settings);
|
|
31
|
+
expect(result).to.be.true;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should send with settings without a reportLevel', function () {
|
|
35
|
+
const settings = { nothing: 'to see here' };
|
|
36
|
+
const result = p.checkLevel(item, settings);
|
|
37
|
+
expect(result).to.be.true;
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('an item with a warning level', function () {
|
|
42
|
+
const item = { level: 'warning' };
|
|
43
|
+
|
|
44
|
+
it('should not send with an error reportLevel', function () {
|
|
45
|
+
const settings = { reportLevel: 'error' };
|
|
46
|
+
const result = p.checkLevel(item, settings);
|
|
47
|
+
expect(result).to.be.false;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should send with an info reportLevel', function () {
|
|
51
|
+
const settings = { reportLevel: 'info' };
|
|
52
|
+
const result = p.checkLevel(item, settings);
|
|
53
|
+
expect(result).to.be.true;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should send with a warning reportLevel', function () {
|
|
57
|
+
const settings = { reportLevel: 'warning' };
|
|
58
|
+
const result = p.checkLevel(item, settings);
|
|
59
|
+
expect(result).to.be.true;
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
});
|