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,430 @@
|
|
|
1
|
+
/* globals expect */
|
|
2
|
+
/* globals describe */
|
|
3
|
+
/* globals it */
|
|
4
|
+
/* globals beforeEach */
|
|
5
|
+
/* globals sinon */
|
|
6
|
+
|
|
7
|
+
import { expect } from 'chai';
|
|
8
|
+
import { EventType } from '@rrweb/types';
|
|
9
|
+
|
|
10
|
+
import Recorder from '../src/browser/replay/recorder.js';
|
|
11
|
+
|
|
12
|
+
describe('Recorder', function () {
|
|
13
|
+
let mockTracing;
|
|
14
|
+
let mockSpan;
|
|
15
|
+
let stopFnSpy;
|
|
16
|
+
let recordFnStub;
|
|
17
|
+
let emitCallback;
|
|
18
|
+
let testReplayId;
|
|
19
|
+
|
|
20
|
+
beforeEach(function () {
|
|
21
|
+
mockSpan = {
|
|
22
|
+
addEvent: sinon.spy(),
|
|
23
|
+
setAttribute: sinon.spy(),
|
|
24
|
+
span: { startTime: null },
|
|
25
|
+
end: sinon.spy(),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
mockTracing = {
|
|
29
|
+
startSpan: sinon.stub().returns(mockSpan),
|
|
30
|
+
exporter: {
|
|
31
|
+
toPayload: sinon.stub().returns([{ id: 'span1' }]),
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
testReplayId = 'test-replay-id-123';
|
|
36
|
+
stopFnSpy = sinon.spy();
|
|
37
|
+
recordFnStub = sinon.stub().callsFake(function (options) {
|
|
38
|
+
emitCallback = options.emit;
|
|
39
|
+
return stopFnSpy;
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('constructor', function () {
|
|
44
|
+
it('should initialize with default properties', function () {
|
|
45
|
+
const recorder = new Recorder({}, recordFnStub);
|
|
46
|
+
|
|
47
|
+
expect(recorder.isRecording).to.equal(false);
|
|
48
|
+
expect(recorder.options).to.deep.equal({
|
|
49
|
+
enabled: undefined,
|
|
50
|
+
autoStart: undefined,
|
|
51
|
+
maxSeconds: undefined,
|
|
52
|
+
triggerOptions: undefined,
|
|
53
|
+
debug: undefined,
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should initialize removing disallowed options', function () {
|
|
58
|
+
const options = { enabled: true, checkoutEveryNms: 1000 };
|
|
59
|
+
const recorder = new Recorder(options, recordFnStub);
|
|
60
|
+
|
|
61
|
+
expect(recorder.options).to.deep.equal({
|
|
62
|
+
enabled: true,
|
|
63
|
+
autoStart: undefined,
|
|
64
|
+
maxSeconds: undefined,
|
|
65
|
+
triggerOptions: undefined,
|
|
66
|
+
debug: undefined,
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should throw error if no record function is passed', function () {
|
|
71
|
+
expect(() => new Recorder({}, null)).to.throw(
|
|
72
|
+
TypeError,
|
|
73
|
+
"Expected 'recordFn' to be provided",
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('recording management', function () {
|
|
79
|
+
it('should start recording correctly', function () {
|
|
80
|
+
const recorder = new Recorder(
|
|
81
|
+
{ enabled: true },
|
|
82
|
+
recordFnStub,
|
|
83
|
+
);
|
|
84
|
+
recorder.start();
|
|
85
|
+
|
|
86
|
+
expect(recorder.isRecording).to.be.true;
|
|
87
|
+
expect(recordFnStub.calledOnce).to.be.true;
|
|
88
|
+
expect(stopFnSpy.called).to.be.false;
|
|
89
|
+
|
|
90
|
+
const recordOptions = recordFnStub.firstCall.args[0];
|
|
91
|
+
expect(recordOptions.checkoutEveryNms).to.equal(5000);
|
|
92
|
+
expect(typeof recordOptions.emit).to.equal('function');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should not start if already recording', function () {
|
|
96
|
+
const recorder = new Recorder(
|
|
97
|
+
{ enabled: true },
|
|
98
|
+
recordFnStub,
|
|
99
|
+
);
|
|
100
|
+
recorder.start();
|
|
101
|
+
recorder.start();
|
|
102
|
+
|
|
103
|
+
expect(recordFnStub.calledOnce).to.be.true;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should not start if disabled', function () {
|
|
107
|
+
const recorder = new Recorder(
|
|
108
|
+
{ enabled: false },
|
|
109
|
+
recordFnStub,
|
|
110
|
+
);
|
|
111
|
+
recorder.start();
|
|
112
|
+
|
|
113
|
+
expect(recorder.isRecording).to.be.false;
|
|
114
|
+
expect(recordFnStub.called).to.be.false;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should stop recording correctly', function () {
|
|
118
|
+
const recorder = new Recorder(
|
|
119
|
+
{ enabled: true },
|
|
120
|
+
recordFnStub,
|
|
121
|
+
);
|
|
122
|
+
recorder.start();
|
|
123
|
+
recorder.stop();
|
|
124
|
+
|
|
125
|
+
expect(recorder.isRecording).to.be.false;
|
|
126
|
+
expect(recordFnStub.calledOnce).to.be.true;
|
|
127
|
+
expect(stopFnSpy.calledOnce).to.be.true;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should handle stop when not recording', function () {
|
|
131
|
+
const recorder = new Recorder({}, recordFnStub);
|
|
132
|
+
recorder.stop();
|
|
133
|
+
|
|
134
|
+
expect(recorder.isRecording).to.be.false;
|
|
135
|
+
expect(stopFnSpy.called).to.be.false;
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('event handling', function () {
|
|
140
|
+
it('should handle events correctly', function () {
|
|
141
|
+
const recorder = new Recorder({}, recordFnStub);
|
|
142
|
+
recorder.start();
|
|
143
|
+
|
|
144
|
+
const event1 = { timestamp: 1000, type: 'event1', data: { a: 1 } };
|
|
145
|
+
const event2 = { timestamp: 2000, type: 'event2', data: { b: 2 } };
|
|
146
|
+
|
|
147
|
+
emitCallback(event1, false);
|
|
148
|
+
emitCallback(event2, false);
|
|
149
|
+
|
|
150
|
+
const result = recorder.dump(mockTracing, testReplayId);
|
|
151
|
+
|
|
152
|
+
expect(mockTracing.startSpan.calledOnce).to.be.true;
|
|
153
|
+
expect(mockSpan.addEvent.calledTwice).to.be.true;
|
|
154
|
+
expect(mockSpan.setAttribute.calledOnce).to.be.true;
|
|
155
|
+
expect(mockSpan.setAttribute.calledWith('rollbar.replay.id', testReplayId)).to.be.true;
|
|
156
|
+
|
|
157
|
+
const firstCallData = mockSpan.addEvent.firstCall.args[1];
|
|
158
|
+
expect(firstCallData.eventType).to.equal('event1');
|
|
159
|
+
expect(JSON.parse(firstCallData.json)).to.deep.equal({ a: 1 });
|
|
160
|
+
expect(firstCallData['rollbar.replay.id']).to.equal(testReplayId);
|
|
161
|
+
|
|
162
|
+
const secondCallData = mockSpan.addEvent.secondCall.args[1];
|
|
163
|
+
expect(secondCallData.eventType).to.equal('event2');
|
|
164
|
+
expect(JSON.parse(secondCallData.json)).to.deep.equal({ b: 2 });
|
|
165
|
+
expect(secondCallData['rollbar.replay.id']).to.equal(testReplayId);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should handle checkout events correctly', function () {
|
|
169
|
+
const recorder = new Recorder({}, recordFnStub);
|
|
170
|
+
recorder.start();
|
|
171
|
+
|
|
172
|
+
// First checkout
|
|
173
|
+
emitCallback({ timestamp: 0, type: EventType.Meta, data: {} }, true);
|
|
174
|
+
emitCallback(
|
|
175
|
+
{ timestamp: 10, type: EventType.FullSnapshot, data: {} },
|
|
176
|
+
true,
|
|
177
|
+
);
|
|
178
|
+
emitCallback(
|
|
179
|
+
{
|
|
180
|
+
timestamp: 1000,
|
|
181
|
+
type: EventType.IncrementalSnapshot,
|
|
182
|
+
data: { a: 1 },
|
|
183
|
+
},
|
|
184
|
+
false,
|
|
185
|
+
);
|
|
186
|
+
emitCallback(
|
|
187
|
+
{
|
|
188
|
+
timestamp: 2000,
|
|
189
|
+
type: EventType.IncrementalSnapshot,
|
|
190
|
+
data: { b: 2 },
|
|
191
|
+
},
|
|
192
|
+
false,
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
// Second checkout
|
|
196
|
+
emitCallback({ timestamp: 3050, type: EventType.Meta, data: {} }, true);
|
|
197
|
+
emitCallback(
|
|
198
|
+
{ timestamp: 3100, type: EventType.FullSnapshot, data: {} },
|
|
199
|
+
true,
|
|
200
|
+
);
|
|
201
|
+
emitCallback(
|
|
202
|
+
{
|
|
203
|
+
timestamp: 4000,
|
|
204
|
+
type: EventType.IncrementalSnapshot,
|
|
205
|
+
data: { c: 3 },
|
|
206
|
+
},
|
|
207
|
+
false,
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
// Third checkout
|
|
211
|
+
emitCallback({ timestamp: 4500, type: EventType.Meta, data: {} }, true);
|
|
212
|
+
emitCallback(
|
|
213
|
+
{ timestamp: 5000, type: EventType.FullSnapshot, data: {} },
|
|
214
|
+
true,
|
|
215
|
+
);
|
|
216
|
+
emitCallback(
|
|
217
|
+
{
|
|
218
|
+
timestamp: 6000,
|
|
219
|
+
type: EventType.IncrementalSnapshot,
|
|
220
|
+
data: { d: 4 },
|
|
221
|
+
},
|
|
222
|
+
false,
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
recorder.dump(mockTracing, testReplayId);
|
|
226
|
+
|
|
227
|
+
// 2nd checkout (meta + fs) + event3 + 3rd checkout (meta + fs) + event4
|
|
228
|
+
expect(mockSpan.span.startTime).to.be.deep.equal([3, 50000000]); // otel time
|
|
229
|
+
expect(mockSpan.span.endTime).not.to.be.null;
|
|
230
|
+
|
|
231
|
+
expect(mockSpan.addEvent.callCount).to.equal(6);
|
|
232
|
+
|
|
233
|
+
[
|
|
234
|
+
{
|
|
235
|
+
name: 'rrweb-replay-events',
|
|
236
|
+
attributes: {
|
|
237
|
+
eventType: EventType.Meta,
|
|
238
|
+
json: JSON.stringify({}),
|
|
239
|
+
'rollbar.replay.id': testReplayId,
|
|
240
|
+
},
|
|
241
|
+
timestamp: [3, 50000000],
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: 'rrweb-replay-events',
|
|
245
|
+
attributes: {
|
|
246
|
+
eventType: EventType.FullSnapshot,
|
|
247
|
+
json: JSON.stringify({}),
|
|
248
|
+
'rollbar.replay.id': testReplayId,
|
|
249
|
+
},
|
|
250
|
+
timestamp: [3, 100000000],
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
name: 'rrweb-replay-events',
|
|
254
|
+
attributes: {
|
|
255
|
+
eventType: EventType.IncrementalSnapshot,
|
|
256
|
+
json: JSON.stringify({ c: 3 }),
|
|
257
|
+
'rollbar.replay.id': testReplayId,
|
|
258
|
+
},
|
|
259
|
+
timestamp: [4, 0],
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
name: 'rrweb-replay-events',
|
|
263
|
+
attributes: {
|
|
264
|
+
eventType: EventType.Meta,
|
|
265
|
+
json: JSON.stringify({}),
|
|
266
|
+
'rollbar.replay.id': testReplayId,
|
|
267
|
+
},
|
|
268
|
+
timestamp: [4, 500000000],
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
name: 'rrweb-replay-events',
|
|
272
|
+
attributes: {
|
|
273
|
+
eventType: EventType.FullSnapshot,
|
|
274
|
+
json: JSON.stringify({}),
|
|
275
|
+
'rollbar.replay.id': testReplayId,
|
|
276
|
+
},
|
|
277
|
+
timestamp: [5, 0],
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
name: 'rrweb-replay-events',
|
|
281
|
+
attributes: {
|
|
282
|
+
eventType: EventType.IncrementalSnapshot,
|
|
283
|
+
json: JSON.stringify({ d: 4 }),
|
|
284
|
+
'rollbar.replay.id': testReplayId,
|
|
285
|
+
},
|
|
286
|
+
timestamp: [6, 0],
|
|
287
|
+
},
|
|
288
|
+
].forEach((expected, index) => {
|
|
289
|
+
const call = mockSpan.addEvent.getCall(index);
|
|
290
|
+
expect(call.args[0]).to.equal(expected.name);
|
|
291
|
+
expect(call.args[1]).to.deep.equal(expected.attributes);
|
|
292
|
+
expect(call.args[2]).to.deep.equal(expected.timestamp);
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
describe('dump functionality', function () {
|
|
298
|
+
it('should create a span with events and return formatted payload', function () {
|
|
299
|
+
const recorder = new Recorder({}, recordFnStub);
|
|
300
|
+
recorder.start();
|
|
301
|
+
|
|
302
|
+
emitCallback({ timestamp: 1000, type: 'event1', data: { a: 1 } }, false);
|
|
303
|
+
emitCallback({ timestamp: 2000, type: 'event2', data: { b: 2 } }, false);
|
|
304
|
+
|
|
305
|
+
const result = recorder.dump(mockTracing, testReplayId);
|
|
306
|
+
|
|
307
|
+
expect(result).to.deep.equal([{ id: 'span1' }]);
|
|
308
|
+
expect(mockTracing.startSpan.calledOnce).to.be.true;
|
|
309
|
+
expect(mockSpan.addEvent.calledTwice).to.be.true;
|
|
310
|
+
expect(mockSpan.end.calledOnce).to.be.true;
|
|
311
|
+
expect(mockTracing.exporter.toPayload.calledOnce).to.be.true;
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('should create a span with the correct span name', function () {
|
|
315
|
+
const recorder = new Recorder({}, recordFnStub);
|
|
316
|
+
recorder.start();
|
|
317
|
+
|
|
318
|
+
emitCallback({ timestamp: 1000, type: 'event1', data: { a: 1 } }, false);
|
|
319
|
+
emitCallback({ timestamp: 2000, type: 'event2', data: { b: 2 } }, false);
|
|
320
|
+
|
|
321
|
+
recorder.dump(mockTracing, testReplayId);
|
|
322
|
+
|
|
323
|
+
expect(mockTracing.startSpan.calledOnce).to.be.true;
|
|
324
|
+
const spanName = mockTracing.startSpan.firstCall.args[0];
|
|
325
|
+
expect(spanName).to.equal('rrweb-replay-recording');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('should add events with the correct event name and replayId', function () {
|
|
329
|
+
const recorder = new Recorder({}, recordFnStub);
|
|
330
|
+
recorder.start();
|
|
331
|
+
|
|
332
|
+
emitCallback(
|
|
333
|
+
{ timestamp: 1000, type: 'fullSnapshot', data: { a: 1 } },
|
|
334
|
+
false,
|
|
335
|
+
);
|
|
336
|
+
emitCallback(
|
|
337
|
+
{ timestamp: 2000, type: 'mouseMove', data: { b: 2 } },
|
|
338
|
+
false,
|
|
339
|
+
);
|
|
340
|
+
emitCallback({ timestamp: 3000, type: 'input', data: { c: 3 } }, false);
|
|
341
|
+
|
|
342
|
+
recorder.dump(mockTracing, testReplayId);
|
|
343
|
+
|
|
344
|
+
expect(mockSpan.addEvent.callCount).to.equal(3);
|
|
345
|
+
|
|
346
|
+
for (let i = 0; i < mockSpan.addEvent.callCount; i++) {
|
|
347
|
+
const eventName = mockSpan.addEvent.getCall(i).args[0];
|
|
348
|
+
const eventAttrs = mockSpan.addEvent.getCall(i).args[1];
|
|
349
|
+
expect(eventName).to.equal(
|
|
350
|
+
'rrweb-replay-events',
|
|
351
|
+
`Event at index ${i} should have name "rrweb-replay-events"`
|
|
352
|
+
);
|
|
353
|
+
expect(eventAttrs['rollbar.replay.id']).to.equal(testReplayId);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it('should handle no events', function () {
|
|
358
|
+
const recorder = new Recorder({}, recordFnStub);
|
|
359
|
+
|
|
360
|
+
const result = recorder.dump(mockTracing, testReplayId);
|
|
361
|
+
|
|
362
|
+
expect(result).to.be.null;
|
|
363
|
+
expect(mockTracing.startSpan.called).to.be.false;
|
|
364
|
+
expect(mockTracing.exporter.toPayload.called).to.be.false;
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('should handle less than 2 events (invalid recording)', function () {
|
|
368
|
+
const recorder = new Recorder({}, recordFnStub);
|
|
369
|
+
recorder.start();
|
|
370
|
+
|
|
371
|
+
emitCallback({ timestamp: 1000, type: 'event1', data: { a: 1 } }, false);
|
|
372
|
+
|
|
373
|
+
const result = recorder.dump(mockTracing, testReplayId);
|
|
374
|
+
|
|
375
|
+
expect(result).to.be.null;
|
|
376
|
+
expect(mockTracing.startSpan.called).to.be.false;
|
|
377
|
+
expect(mockTracing.exporter.toPayload.called).to.be.false;
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
describe('configure', function () {
|
|
382
|
+
it('should update options', function () {
|
|
383
|
+
const recorder = new Recorder(
|
|
384
|
+
{ enabled: true },
|
|
385
|
+
recordFnStub,
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
recorder.configure({ enabled: false, maxSeconds: 20 });
|
|
389
|
+
|
|
390
|
+
expect(recorder.options).to.deep.equal({
|
|
391
|
+
enabled: false,
|
|
392
|
+
autoStart: undefined,
|
|
393
|
+
maxSeconds: 20,
|
|
394
|
+
triggerOptions: undefined,
|
|
395
|
+
debug: undefined,
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('should set correct checkoutEveryNms', function () {
|
|
400
|
+
const recorder = new Recorder({ enabled: false });
|
|
401
|
+
|
|
402
|
+
recorder.configure({ enabled: true, maxSeconds: 15 });
|
|
403
|
+
|
|
404
|
+
expect(recorder.options).to.deep.equal({
|
|
405
|
+
enabled: true,
|
|
406
|
+
autoStart: undefined,
|
|
407
|
+
maxSeconds: 15,
|
|
408
|
+
triggerOptions: undefined,
|
|
409
|
+
debug: undefined,
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
expect(recorder.checkoutEveryNms()).to.equal(7500);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it('should stop recording if enabled set to false', function () {
|
|
416
|
+
const recorder = new Recorder(
|
|
417
|
+
{ enabled: true },
|
|
418
|
+
recordFnStub,
|
|
419
|
+
);
|
|
420
|
+
recorder.start();
|
|
421
|
+
|
|
422
|
+
expect(recorder.isRecording).to.be.true;
|
|
423
|
+
|
|
424
|
+
recorder.configure({ enabled: false });
|
|
425
|
+
|
|
426
|
+
expect(recorder.isRecording).to.be.false;
|
|
427
|
+
expect(stopFnSpy.calledOnce).to.be.true;
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/* globals it */
|
|
4
4
|
/* globals sinon */
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
import Rollbar from '../src/browser/rollbar.js';
|
|
7
7
|
|
|
8
8
|
const DUMMY_TRACE_ID = 'some-trace-id';
|
|
9
9
|
const DUMMY_SPAN_ID = 'some-span-id';
|
|
@@ -115,11 +115,21 @@ describe('Rollbar()', function () {
|
|
|
115
115
|
var client = new (TestClientGen())();
|
|
116
116
|
var options = {
|
|
117
117
|
scrubFields: ['foobar'],
|
|
118
|
+
recorder: {
|
|
119
|
+
enabled: true,
|
|
120
|
+
},
|
|
121
|
+
tracing: {
|
|
122
|
+
endpoint: 'api.rollbar.com/api/1/tracing/',
|
|
123
|
+
},
|
|
118
124
|
};
|
|
119
125
|
var rollbar = (window.rollbar = new Rollbar(options, client));
|
|
120
126
|
|
|
121
127
|
expect(rollbar.options.scrubFields).to.contain('foobar');
|
|
122
128
|
expect(rollbar.options.scrubFields).to.contain('password');
|
|
129
|
+
expect(rollbar.options.recorder.enabled).to.eql(true);
|
|
130
|
+
expect(rollbar.options.recorder.triggerOptions.item.levels).to.eql(['error', 'critical']);
|
|
131
|
+
expect(rollbar.options.tracing.endpoint).to.eql('api.rollbar.com/api/1/tracing/');
|
|
132
|
+
expect(rollbar.options.tracing.enabled).to.eql(false);
|
|
123
133
|
done();
|
|
124
134
|
});
|
|
125
135
|
|
|
@@ -191,6 +201,7 @@ describe('Rollbar()', function () {
|
|
|
191
201
|
done();
|
|
192
202
|
});
|
|
193
203
|
|
|
204
|
+
// Legacy OpenTracing support
|
|
194
205
|
it('should have a tracer if valid tracer is provided', function (done) {
|
|
195
206
|
var options = { tracer: ValidOpenTracingTracerStub };
|
|
196
207
|
var rollbar = (window.rollbar = new Rollbar(options));
|
|
@@ -200,6 +211,7 @@ describe('Rollbar()', function () {
|
|
|
200
211
|
done();
|
|
201
212
|
});
|
|
202
213
|
|
|
214
|
+
// Legacy OpenTracing support
|
|
203
215
|
it('should not have a tracer if invalid tracer is provided', function (done) {
|
|
204
216
|
var options = { tracer: InvalidOpenTracingTracerStub };
|
|
205
217
|
var rollbar = (window.rollbar = new Rollbar(options));
|
|
@@ -351,7 +363,6 @@ describe('options.captureUncaught', function () {
|
|
|
351
363
|
|
|
352
364
|
var body = JSON.parse(server.requests[0].requestBody);
|
|
353
365
|
|
|
354
|
-
expect(body.access_token).to.eql('POST_CLIENT_ITEM_TOKEN');
|
|
355
366
|
expect(body.data.body.trace.exception.message).to.eql('test error');
|
|
356
367
|
expect(body.data.notifier.diagnostic.raw_error.message).to.eql(
|
|
357
368
|
'test error',
|
|
@@ -399,7 +410,6 @@ describe('options.captureUncaught', function () {
|
|
|
399
410
|
|
|
400
411
|
var body = JSON.parse(server.requests[0].requestBody);
|
|
401
412
|
|
|
402
|
-
expect(body.access_token).to.eql('POST_CLIENT_ITEM_TOKEN');
|
|
403
413
|
expect(body.data.body.trace.exception.message).to.eql('test error');
|
|
404
414
|
expect(body.data.notifier.diagnostic.is_anonymous).to.not.be.ok();
|
|
405
415
|
|
|
@@ -453,7 +463,6 @@ describe('options.captureUncaught', function () {
|
|
|
453
463
|
|
|
454
464
|
var body = JSON.parse(server.requests[0].requestBody);
|
|
455
465
|
|
|
456
|
-
expect(body.access_token).to.eql('POST_CLIENT_ITEM_TOKEN');
|
|
457
466
|
expect(body.data.body.trace.exception.message).to.eql('anon error');
|
|
458
467
|
expect(body.data.notifier.diagnostic.is_anonymous).to.eql(true);
|
|
459
468
|
|
|
@@ -494,7 +503,6 @@ describe('options.captureUncaught', function () {
|
|
|
494
503
|
|
|
495
504
|
var body = JSON.parse(server.requests[0].requestBody);
|
|
496
505
|
|
|
497
|
-
expect(body.access_token).to.eql('POST_CLIENT_ITEM_TOKEN');
|
|
498
506
|
expect(body.data.body.trace.exception.message).to.eql('test error');
|
|
499
507
|
|
|
500
508
|
// karma doesn't unload the browser between tests, so the onerror handler
|
|
@@ -535,7 +543,6 @@ describe('options.captureUncaught', function () {
|
|
|
535
543
|
|
|
536
544
|
var body = JSON.parse(server.requests[0].requestBody);
|
|
537
545
|
|
|
538
|
-
expect(body.access_token).to.eql('POST_CLIENT_ITEM_TOKEN');
|
|
539
546
|
expect(body.data.body.trace.exception.message).to.eql('test error');
|
|
540
547
|
|
|
541
548
|
// karma doesn't unload the browser between tests, so the onerror handler
|
|
@@ -567,7 +574,6 @@ describe('options.captureUncaught', function () {
|
|
|
567
574
|
|
|
568
575
|
var body = JSON.parse(server.requests[0].requestBody);
|
|
569
576
|
|
|
570
|
-
expect(body.access_token).to.eql('POST_CLIENT_ITEM_TOKEN');
|
|
571
577
|
expect(body.data.body.trace_chain[0].exception.message).to.eql(
|
|
572
578
|
'test DOMException',
|
|
573
579
|
);
|
|
@@ -604,7 +610,6 @@ describe('options.captureUncaught', function () {
|
|
|
604
610
|
|
|
605
611
|
var body = JSON.parse(server.requests[0].requestBody);
|
|
606
612
|
|
|
607
|
-
expect(body.access_token).to.eql('POST_CLIENT_ITEM_TOKEN');
|
|
608
613
|
expect(body.data.body.trace.exception.message).to.eql('deep stack error');
|
|
609
614
|
expect(body.data.body.trace.frames.length).to.be.above(20);
|
|
610
615
|
|
|
@@ -640,7 +645,6 @@ describe('options.captureUncaught', function () {
|
|
|
640
645
|
|
|
641
646
|
var body = JSON.parse(server.requests[0].requestBody);
|
|
642
647
|
|
|
643
|
-
expect(body.access_token).to.eql('POST_CLIENT_ITEM_TOKEN');
|
|
644
648
|
expect(body.data.body.trace.exception.message).to.eql(
|
|
645
649
|
'event handler error',
|
|
646
650
|
);
|
|
@@ -696,7 +700,6 @@ describe('options.captureUnhandledRejections', function () {
|
|
|
696
700
|
|
|
697
701
|
var body = JSON.parse(server.requests[0].requestBody);
|
|
698
702
|
|
|
699
|
-
expect(body.access_token).to.eql('POST_CLIENT_ITEM_TOKEN');
|
|
700
703
|
expect(body.data.body.trace.exception.message).to.eql('test reject');
|
|
701
704
|
expect(body.data.notifier.diagnostic.is_uncaught).to.eql(true);
|
|
702
705
|
|
|
@@ -731,7 +734,6 @@ describe('options.captureUnhandledRejections', function () {
|
|
|
731
734
|
|
|
732
735
|
var body = JSON.parse(server.requests[0].requestBody);
|
|
733
736
|
|
|
734
|
-
expect(body.access_token).to.eql('POST_CLIENT_ITEM_TOKEN');
|
|
735
737
|
expect(body.data.body.trace.exception.message).to.eql('test reject');
|
|
736
738
|
|
|
737
739
|
server.requests.length = 0;
|
|
@@ -881,6 +883,51 @@ describe('log', function () {
|
|
|
881
883
|
}, 1);
|
|
882
884
|
});
|
|
883
885
|
|
|
886
|
+
it('should add tracing attributes when called in an active span', function (done) {
|
|
887
|
+
const server = window.server;
|
|
888
|
+
stubResponse(server);
|
|
889
|
+
server.requests.length = 0;
|
|
890
|
+
|
|
891
|
+
const options = {
|
|
892
|
+
accessToken: 'POST_CLIENT_ITEM_TOKEN',
|
|
893
|
+
recorder: {
|
|
894
|
+
enabled: true,
|
|
895
|
+
},
|
|
896
|
+
};
|
|
897
|
+
const rollbar = (window.rollbar = new Rollbar(options));
|
|
898
|
+
|
|
899
|
+
const err = new Error('test error');
|
|
900
|
+
|
|
901
|
+
rollbar.tracing.withSpan('test', {}, () => {
|
|
902
|
+
rollbar.error(err);
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
setTimeout(function () {
|
|
906
|
+
try{
|
|
907
|
+
server.respond();
|
|
908
|
+
|
|
909
|
+
var body = JSON.parse(server.requests[0].requestBody);
|
|
910
|
+
|
|
911
|
+
expect(body.data.body.trace.exception.message).to.eql('test error');
|
|
912
|
+
expect(body.data.attributes).to.be.an('array');
|
|
913
|
+
expect(body.data.attributes.length).to.eql(4);
|
|
914
|
+
expect(body.data.attributes[0].key).to.eql('replay_id');
|
|
915
|
+
expect(body.data.attributes[0].value).to.match(/^[a-f0-9]{16}$/);
|
|
916
|
+
expect(body.data.attributes[1].key).to.eql('session_id');
|
|
917
|
+
expect(body.data.attributes[1].value).to.match(/^[a-f0-9]{32}$/);
|
|
918
|
+
expect(body.data.attributes[2].key).to.eql('span_id');
|
|
919
|
+
expect(body.data.attributes[2].value).to.match(/^[a-f0-9]{16}$/);
|
|
920
|
+
expect(body.data.attributes[3].key).to.eql('trace_id');
|
|
921
|
+
expect(body.data.attributes[3].value).to.match(/^[a-f0-9]{32}$/);
|
|
922
|
+
|
|
923
|
+
done();
|
|
924
|
+
} catch (e) {
|
|
925
|
+
done(e);
|
|
926
|
+
}
|
|
927
|
+
}, 1);
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
|
|
884
931
|
it('should send message when called with only null arguments', function (done) {
|
|
885
932
|
var server = window.server;
|
|
886
933
|
stubResponse(server);
|
|
@@ -1019,7 +1066,6 @@ describe('onerror', function () {
|
|
|
1019
1066
|
|
|
1020
1067
|
var body = JSON.parse(server.requests[0].requestBody);
|
|
1021
1068
|
|
|
1022
|
-
expect(body.access_token).to.eql('POST_CLIENT_ITEM_TOKEN');
|
|
1023
1069
|
expect(body.data.body.trace.exception.message).to.eql(
|
|
1024
1070
|
'testing window.onerror',
|
|
1025
1071
|
);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/* globals it */
|
|
4
4
|
/* globals sinon */
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
import Instrumenter from '../src/browser/telemetry.js';
|
|
7
7
|
|
|
8
8
|
describe('instrumentNetwork', function () {
|
|
9
9
|
it('should capture XHR requests with string URL', function (done) {
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
/* globals it */
|
|
4
4
|
/* globals sinon */
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
import Rollbar from '../src/browser/rollbar.js';
|
|
7
|
+
import t from '../src/browser/transforms.js';
|
|
8
8
|
|
|
9
9
|
function TestClientGen() {
|
|
10
10
|
var TestClient = function () {
|
|
@@ -527,7 +527,7 @@ describe('addBody', function () {
|
|
|
527
527
|
});
|
|
528
528
|
|
|
529
529
|
describe('scrubPayload', function () {
|
|
530
|
-
it('only scrubs payload data', function (
|
|
530
|
+
it('only scrubs payload data', async function () {
|
|
531
531
|
var args = [
|
|
532
532
|
'a message',
|
|
533
533
|
{ scooby: 'doo', okay: 'fizz=buzz&fuzz=baz', user: { id: 42 } },
|
|
@@ -547,16 +547,23 @@ describe('scrubPayload', function () {
|
|
|
547
547
|
expect(payload.data.custom.okay).to.eql('fizz=buzz&fuzz=baz');
|
|
548
548
|
expect(payload.data.custom.user.id).to.eql(42);
|
|
549
549
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
done(e);
|
|
550
|
+
const scrubModule = await import('../src/scrub.js');
|
|
551
|
+
const scrub = scrubModule.default;
|
|
552
|
+
|
|
553
|
+
const scrubberFn = t.addScrubber(scrub);
|
|
554
|
+
const result = await new Promise((resolve, reject) => {
|
|
555
|
+
scrubberFn(payload, options, (err, result) => {
|
|
556
|
+
if (err) reject(err);
|
|
557
|
+
else resolve(result);
|
|
558
|
+
});
|
|
560
559
|
});
|
|
560
|
+
|
|
561
|
+
expect(result.access_token).to.eql(accessToken);
|
|
562
|
+
expect(result.data.custom.scooby).to.not.eql('doo');
|
|
563
|
+
expect(payload.data.custom.okay).to.not.eql('fizz=buzz&fuzz=baz');
|
|
564
|
+
expect(payload.data.custom.okay).to.match(/fizz=\*+&fuzz=baz/);
|
|
565
|
+
expect(payload.data.custom.user.id).to.not.be.ok();
|
|
566
|
+
expect(payload.data.custom.user).to.match(/\*+/);
|
|
567
|
+
expect(result.data.message).to.eql('a message');
|
|
561
568
|
});
|
|
562
569
|
});
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
/* globals it */
|
|
4
4
|
/* globals sinon */
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
import truncation from '../src/truncation.js';
|
|
7
|
+
import Transport from '../src/browser/transport.js';
|
|
8
|
+
const t = new Transport(truncation);
|
|
9
|
+
import * as utility from '../src/utility.js';
|
|
10
10
|
utility.setupJSON();
|
|
11
11
|
|
|
12
12
|
describe('post', function () {
|
|
@@ -166,6 +166,7 @@ var TestRequest = function (response, status, shouldThrowOnSend) {
|
|
|
166
166
|
this.onreadystatechange = null;
|
|
167
167
|
this.readyState = 0;
|
|
168
168
|
this.shouldThrowOnSend = shouldThrowOnSend;
|
|
169
|
+
this.getResponseHeader = (key) => undefined;
|
|
169
170
|
};
|
|
170
171
|
TestRequest.prototype.open = function (m, u, a) {
|
|
171
172
|
this.method = m;
|