rollbar 2.26.3 → 3.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/.cursor/rules/guidelines.mdc +154 -0
  2. package/.github/workflows/ci.yml +32 -12
  3. package/.lgtm.yml +7 -7
  4. package/.prettierignore +18 -0
  5. package/.vscode/settings.json +39 -0
  6. package/CHANGELOG.md +121 -35
  7. package/CLAUDE.md +201 -0
  8. package/Gruntfile.js +101 -48
  9. package/Makefile +3 -3
  10. package/README.md +2 -4
  11. package/SECURITY.md +5 -0
  12. package/babel.config.json +9 -0
  13. package/bower.json +1 -3
  14. package/codex.md +148 -0
  15. package/defaults.js +17 -5
  16. package/dist/plugins/jquery.min.js +1 -1
  17. package/dist/rollbar.js +18748 -5375
  18. package/dist/rollbar.js.map +1 -1
  19. package/dist/rollbar.min.js +2 -1
  20. package/dist/rollbar.min.js.LICENSE.txt +1 -0
  21. package/dist/rollbar.min.js.map +1 -1
  22. package/dist/rollbar.named-amd.js +19368 -6000
  23. package/dist/rollbar.named-amd.js.map +1 -1
  24. package/dist/rollbar.named-amd.min.js +3 -1
  25. package/dist/rollbar.named-amd.min.js.LICENSE.txt +1 -0
  26. package/dist/rollbar.named-amd.min.js.map +1 -1
  27. package/dist/rollbar.noconflict.umd.js +18749 -5380
  28. package/dist/rollbar.noconflict.umd.js.map +1 -1
  29. package/dist/rollbar.noconflict.umd.min.js +3 -1
  30. package/dist/rollbar.noconflict.umd.min.js.LICENSE.txt +1 -0
  31. package/dist/rollbar.noconflict.umd.min.js.map +1 -1
  32. package/dist/rollbar.snippet.js +1 -1
  33. package/dist/rollbar.umd.js +19367 -6000
  34. package/dist/rollbar.umd.js.map +1 -1
  35. package/dist/rollbar.umd.min.js +3 -1
  36. package/dist/rollbar.umd.min.js.LICENSE.txt +1 -0
  37. package/dist/rollbar.umd.min.js.map +1 -1
  38. package/docs/extension-exceptions.md +35 -30
  39. package/docs/migration_v0_to_v1.md +41 -38
  40. package/eslint.config.mjs +33 -0
  41. package/get_versions.js +33 -0
  42. package/index.d.ts +270 -231
  43. package/karma.conf.js +18 -27
  44. package/package.json +21 -21
  45. package/prettier.config.js +7 -0
  46. package/src/api.js +78 -14
  47. package/src/apiUtility.js +14 -11
  48. package/src/browser/core.js +138 -72
  49. package/src/browser/defaults/scrubFields.js +3 -3
  50. package/src/browser/detection.js +7 -8
  51. package/src/browser/domUtility.js +18 -8
  52. package/src/browser/globalSetup.js +12 -6
  53. package/src/browser/logger.js +1 -1
  54. package/src/browser/plugins/jquery.js +35 -35
  55. package/src/browser/predicates.js +1 -1
  56. package/src/browser/replay/defaults.js +71 -0
  57. package/src/browser/replay/recorder.js +193 -0
  58. package/src/browser/replay/replayMap.js +195 -0
  59. package/src/browser/rollbar.js +12 -8
  60. package/src/browser/rollbarWrapper.js +8 -5
  61. package/src/browser/shim.js +43 -19
  62. package/src/browser/snippet_callback.js +6 -4
  63. package/src/browser/telemetry.js +573 -361
  64. package/src/browser/transforms.js +46 -27
  65. package/src/browser/transport/fetch.js +26 -14
  66. package/src/browser/transport/xhr.js +41 -14
  67. package/src/browser/transport.js +93 -33
  68. package/src/browser/url.js +16 -8
  69. package/src/browser/wrapGlobals.js +27 -8
  70. package/src/defaults.js +3 -3
  71. package/src/errorParser.js +14 -11
  72. package/src/merge.js +32 -23
  73. package/src/notifier.js +16 -13
  74. package/src/predicates.js +43 -23
  75. package/src/queue.js +133 -40
  76. package/src/rateLimiter.js +59 -18
  77. package/src/react-native/logger.js +1 -1
  78. package/src/react-native/rollbar.js +59 -55
  79. package/src/react-native/transforms.js +13 -9
  80. package/src/react-native/transport.js +44 -34
  81. package/src/rollbar.js +72 -21
  82. package/src/scrub.js +0 -1
  83. package/src/server/locals.js +69 -39
  84. package/src/server/logger.js +4 -4
  85. package/src/server/parser.js +72 -47
  86. package/src/server/rollbar.js +135 -56
  87. package/src/server/sourceMap/stackTrace.js +33 -18
  88. package/src/server/telemetry/urlHelpers.js +9 -11
  89. package/src/server/telemetry.js +68 -45
  90. package/src/server/transforms.js +37 -21
  91. package/src/server/transport.js +62 -32
  92. package/src/telemetry.js +162 -33
  93. package/src/tracing/context.js +24 -0
  94. package/src/tracing/contextManager.js +37 -0
  95. package/src/tracing/defaults.js +7 -0
  96. package/src/tracing/exporter.js +188 -0
  97. package/src/tracing/hrtime.js +98 -0
  98. package/src/tracing/id.js +24 -0
  99. package/src/tracing/session.js +55 -0
  100. package/src/tracing/span.js +92 -0
  101. package/src/tracing/spanProcessor.js +15 -0
  102. package/src/tracing/tracer.js +46 -0
  103. package/src/tracing/tracing.js +89 -0
  104. package/src/transforms.js +33 -21
  105. package/src/truncation.js +8 -5
  106. package/src/utility/headers.js +43 -43
  107. package/src/utility/replace.js +9 -0
  108. package/src/utility/traverse.js +1 -1
  109. package/src/utility.js +123 -52
  110. package/test/api.test.js +88 -41
  111. package/test/apiUtility.test.js +48 -50
  112. package/test/browser.core.test.js +142 -141
  113. package/test/browser.domUtility.test.js +53 -36
  114. package/test/browser.predicates.test.js +14 -14
  115. package/test/browser.replay.recorder.test.js +416 -0
  116. package/test/browser.rollbar.test.js +655 -515
  117. package/test/browser.telemetry.test.js +46 -39
  118. package/test/browser.transforms.test.js +164 -139
  119. package/test/browser.transport.test.js +59 -50
  120. package/test/browser.url.test.js +13 -12
  121. package/test/fixtures/locals.fixtures.js +245 -126
  122. package/test/fixtures/replay/index.js +20 -0
  123. package/test/fixtures/replay/payloads.fixtures.js +229 -0
  124. package/test/fixtures/replay/rrwebEvents.fixtures.js +251 -0
  125. package/test/fixtures/replay/rrwebSyntheticEvents.fixtures.js +328 -0
  126. package/test/notifier.test.js +91 -79
  127. package/test/predicates.test.js +261 -215
  128. package/test/queue.test.js +231 -215
  129. package/test/rateLimiter.test.js +51 -43
  130. package/test/react-native.rollbar.test.js +150 -116
  131. package/test/react-native.transforms.test.js +23 -25
  132. package/test/react-native.transport.test.js +26 -14
  133. package/test/replay/index.js +2 -0
  134. package/test/replay/integration/api.spans.test.js +136 -0
  135. package/test/replay/integration/e2e.test.js +228 -0
  136. package/test/replay/integration/index.js +9 -0
  137. package/test/replay/integration/queue.replayMap.test.js +332 -0
  138. package/test/replay/integration/replayMap.test.js +163 -0
  139. package/test/replay/integration/sessionRecording.test.js +390 -0
  140. package/test/replay/unit/api.postSpans.test.js +150 -0
  141. package/test/replay/unit/index.js +7 -0
  142. package/test/replay/unit/queue.replayMap.test.js +225 -0
  143. package/test/replay/unit/replayMap.test.js +348 -0
  144. package/test/replay/util/index.js +5 -0
  145. package/test/replay/util/mockRecordFn.js +80 -0
  146. package/test/server.lambda.mocha.test.mjs +172 -0
  147. package/test/server.locals.constructor.mocha.test.mjs +80 -0
  148. package/test/server.locals.error-handling.mocha.test.mjs +387 -0
  149. package/test/server.locals.merge.mocha.test.mjs +267 -0
  150. package/test/server.locals.test-utils.mjs +114 -0
  151. package/test/server.parser.mocha.test.mjs +87 -0
  152. package/test/server.predicates.mocha.test.mjs +63 -0
  153. package/test/server.rollbar.constructor.mocha.test.mjs +199 -0
  154. package/test/server.rollbar.handlers.mocha.test.mjs +253 -0
  155. package/test/server.rollbar.logging.mocha.test.mjs +326 -0
  156. package/test/server.rollbar.misc.mocha.test.mjs +44 -0
  157. package/test/server.rollbar.test-utils.mjs +57 -0
  158. package/test/server.telemetry.mocha.test.mjs +377 -0
  159. package/test/server.transforms.data.mocha.test.mjs +163 -0
  160. package/test/server.transforms.error.mocha.test.mjs +199 -0
  161. package/test/server.transforms.request.mocha.test.mjs +208 -0
  162. package/test/server.transforms.scrub.mocha.test.mjs +140 -0
  163. package/test/server.transforms.sourcemaps.mocha.test.mjs +122 -0
  164. package/test/server.transforms.test-utils.mjs +62 -0
  165. package/test/server.transport.mocha.test.mjs +269 -0
  166. package/test/telemetry.test.js +178 -38
  167. package/test/tracing/contextManager.test.js +28 -0
  168. package/test/tracing/exporter.toPayload.test.js +400 -0
  169. package/test/tracing/id.test.js +24 -0
  170. package/test/tracing/span.test.js +183 -0
  171. package/test/tracing/spanProcessor.test.js +73 -0
  172. package/test/tracing/tracing.test.js +105 -0
  173. package/test/transforms.test.js +70 -68
  174. package/test/truncation.test.js +57 -55
  175. package/test/utility.test.js +310 -228
  176. package/webpack.config.js +36 -70
  177. package/.eslintignore +0 -7
  178. package/.gitmodules +0 -3
  179. package/test/server.lambda.test.js +0 -177
  180. package/test/server.locals.test.js +0 -841
  181. package/test/server.parser.test.js +0 -72
  182. package/test/server.predicates.test.js +0 -89
  183. package/test/server.rollbar.test.js +0 -676
  184. package/test/server.telemetry.test.js +0 -318
  185. package/test/server.transforms.test.js +0 -1099
  186. package/test/server.transport.test.js +0 -201
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Integration tests for Queue and ReplayMap
3
+ */
4
+
5
+ /* globals describe */
6
+ /* globals it */
7
+ /* globals beforeEach */
8
+ /* globals afterEach */
9
+
10
+ import { expect } from 'chai';
11
+ import sinon from 'sinon';
12
+
13
+ import Queue from '../../../src/queue.js';
14
+ import Api from '../../../src/api.js';
15
+
16
+ describe('Queue ReplayMap Integration', function () {
17
+ let queue;
18
+ let replayMap;
19
+ let api;
20
+ let transport;
21
+
22
+ beforeEach(function () {
23
+ transport = {
24
+ post: sinon
25
+ .stub()
26
+ .callsFake((accessToken, transportOptions, payload, callback) => {
27
+ setTimeout(() => {
28
+ callback(
29
+ null,
30
+ { err: 0, result: { id: '12345' } },
31
+ { 'Rollbar-Replay-Enabled': 'true' }
32
+ );
33
+ }, 10);
34
+ }),
35
+ postJsonPayload: sinon.stub(),
36
+ };
37
+
38
+ const urlMock = { parse: sinon.stub().returns({}) };
39
+ const truncationMock = {
40
+ truncate: sinon.stub().returns({ error: null, value: '{}' }),
41
+ };
42
+
43
+ api = new Api(
44
+ { accessToken: 'test-token' },
45
+ transport,
46
+ urlMock,
47
+ truncationMock,
48
+ );
49
+
50
+ replayMap = {
51
+ add: sinon.stub().returnsArg(0),
52
+ send: sinon.stub().resolves(true),
53
+ discard: sinon.stub().returns(true),
54
+ getSpans: sinon.stub().returns([{ id: 'test-span' }]),
55
+ setSpans: sinon.stub(),
56
+ };
57
+
58
+ queue = new Queue(
59
+ { shouldSend: () => ({ shouldSend: true }) },
60
+ api,
61
+ console,
62
+ { transmit: true },
63
+ replayMap,
64
+ );
65
+ });
66
+
67
+ afterEach(function () {
68
+ sinon.restore();
69
+ });
70
+
71
+ it('should add replayId when processing an item', function (done) {
72
+ const item = {
73
+ body: {
74
+ trace: {
75
+ exception: {
76
+ message: 'Test error',
77
+ },
78
+ },
79
+ },
80
+ attributes: [
81
+ { key: 'replay_id', value: '1234567812345678' },
82
+ ],
83
+ };
84
+
85
+ queue.addItem(item, function () {
86
+ expect(item).to.have.property('replayId', '1234567812345678');
87
+ expect(replayMap.add.calledOnce).to.be.true;
88
+ done();
89
+ });
90
+ });
91
+
92
+ it('should call replayMap.send when API response is successful', function (done) {
93
+ const item = {
94
+ body: {
95
+ trace: {
96
+ exception: {
97
+ message: 'Test error',
98
+ },
99
+ },
100
+ },
101
+ attributes: [
102
+ { key: 'replay_id', value: '1234567812345678' },
103
+ ],
104
+ };
105
+
106
+ queue.addItem(item, function () {
107
+ setTimeout(function () {
108
+ expect(replayMap.send.calledWith('1234567812345678')).to.be.true;
109
+ done();
110
+ }, 50);
111
+ });
112
+ });
113
+
114
+ it('should call replayMap.discard when API response has error', function (done) {
115
+ transport.post.callsFake(
116
+ (accessToken, transportOptions, payload, callback) => {
117
+ setTimeout(() => {
118
+ callback(null, { err: 1, message: 'API Error' });
119
+ }, 10);
120
+ },
121
+ );
122
+
123
+ const item = {
124
+ body: {
125
+ trace: {
126
+ exception: {
127
+ message: 'Test error with API failure',
128
+ },
129
+ },
130
+ },
131
+ attributes: [
132
+ { key: 'replay_id', value: '1234567812345678' },
133
+ ],
134
+ };
135
+
136
+ queue.addItem(item, function () {
137
+ setTimeout(function () {
138
+ expect(replayMap.discard.calledWith('1234567812345678')).to.be.true;
139
+ expect(replayMap.send.called).to.be.false;
140
+ done();
141
+ }, 50);
142
+ });
143
+ });
144
+
145
+ it('should call replayMap.discard when replay is disabled', function (done) {
146
+ transport.post.callsFake(
147
+ (accessToken, transportOptions, payload, callback) => {
148
+ setTimeout(() => {
149
+ callback(
150
+ null,
151
+ { err: 0, result: { id: '12345' } },
152
+ { 'Rollbar-Replay-Enabled': 'false' }
153
+ );
154
+ }, 10);
155
+ },
156
+ );
157
+
158
+ const item = {
159
+ body: {
160
+ trace: {
161
+ exception: {
162
+ message: 'Test error with replay disabled',
163
+ },
164
+ },
165
+ },
166
+ attributes: [
167
+ { key: 'replay_id', value: '1234567812345678' },
168
+ ],
169
+ };
170
+
171
+ queue.addItem(item, function () {
172
+ setTimeout(function () {
173
+ expect(replayMap.discard.calledWith('1234567812345678')).to.be.true;
174
+ expect(replayMap.send.called).to.be.false;
175
+ done();
176
+ }, 50);
177
+ });
178
+ });
179
+
180
+ it('should call replayMap.discard when over quota', function (done) {
181
+ transport.post.callsFake(
182
+ (accessToken, transportOptions, payload, callback) => {
183
+ setTimeout(() => {
184
+ callback(
185
+ null,
186
+ { err: 0, result: { id: '12345' } },
187
+ { 'Rollbar-Replay-Enabled': 'true',
188
+ 'Rollbar-Replay-RateLimit-Remaining': '0'}
189
+ );
190
+ }, 10);
191
+ },
192
+ );
193
+
194
+ const item = {
195
+ body: {
196
+ trace: {
197
+ exception: {
198
+ message: 'Test error with replay over quota',
199
+ },
200
+ },
201
+ },
202
+ attributes: [
203
+ { key: 'replay_id', value: '1234567812345678' },
204
+ ],
205
+ };
206
+
207
+ queue.addItem(item, function () {
208
+ setTimeout(function () {
209
+ expect(replayMap.discard.calledWith('1234567812345678')).to.be.true;
210
+ expect(replayMap.send.called).to.be.false;
211
+ done();
212
+ }, 50);
213
+ });
214
+ });
215
+
216
+ it('should handle retrying items with replayId', function (done) {
217
+ let apiCallCount = 0;
218
+ transport.post.callsFake(
219
+ (accessToken, transportOptions, payload, callback) => {
220
+ apiCallCount++;
221
+ if (apiCallCount === 1) {
222
+ setTimeout(() => {
223
+ callback({ code: 'ECONNRESET' });
224
+ }, 10);
225
+ } else {
226
+ setTimeout(() => {
227
+ callback(
228
+ null,
229
+ { err: 0, result: { id: '12345' } },
230
+ { 'Rollbar-Replay-Enabled': 'true' }
231
+ );
232
+ }, 10);
233
+ }
234
+ },
235
+ );
236
+
237
+ queue.configure({ retryInterval: 50, maxRetries: 3 });
238
+
239
+ const item = {
240
+ body: {
241
+ trace: {
242
+ exception: {
243
+ message: 'Test error with retry',
244
+ },
245
+ },
246
+ },
247
+ attributes: [
248
+ { key: 'replay_id', value: '1234567812345678' },
249
+ ],
250
+ };
251
+
252
+ queue.addItem(item, function (err, resp) {
253
+ if (resp) {
254
+ expect(item).to.have.property('replayId', '1234567812345678');
255
+
256
+ setTimeout(function () {
257
+ expect(replayMap.send.calledWith('1234567812345678')).to.be.true;
258
+ done();
259
+ }, 50);
260
+ }
261
+ });
262
+ });
263
+
264
+ it('should not add replayId to items without a body', function (done) {
265
+ const item = {
266
+ level: 'error',
267
+ message: 'Test error without body',
268
+ attributes: [
269
+ { key: 'replay_id', value: '1234567812345678' },
270
+ ],
271
+ };
272
+
273
+ queue.addItem(item, function () {
274
+ expect(item).to.not.have.property('replayId');
275
+ expect(replayMap.add.called).to.be.false;
276
+ done();
277
+ });
278
+ });
279
+
280
+ it('should not add replayId to items without a replay_id attribute', function (done) {
281
+ const item = {
282
+ level: 'error',
283
+ message: 'Test error without body',
284
+ body: {
285
+ trace: {
286
+ exception: {
287
+ message: 'Test error with retry',
288
+ },
289
+ },
290
+ },
291
+ };
292
+
293
+ queue.addItem(item, function () {
294
+ expect(item).to.not.have.property('replayId');
295
+ expect(replayMap.add.called).to.be.false;
296
+ done();
297
+ });
298
+ });
299
+
300
+ it('should handle null response in _handleReplayResponse', function (done) {
301
+ transport.post.callsFake(
302
+ (accessToken, transportOptions, payload, callback) => {
303
+ setTimeout(() => {
304
+ callback(null, null);
305
+ }, 10);
306
+ },
307
+ );
308
+
309
+ const consoleWarnSpy = sinon.spy(console, 'warn');
310
+
311
+ const item = {
312
+ body: {
313
+ trace: {
314
+ exception: {
315
+ message: 'Test error with null response',
316
+ },
317
+ },
318
+ },
319
+ attributes: [
320
+ { key: 'replay_id', value: '1234567812345678' },
321
+ ],
322
+ };
323
+
324
+ queue.addItem(item, function () {
325
+ setTimeout(function () {
326
+ expect(replayMap.send.called).to.be.false;
327
+ expect(replayMap.discard.calledWith('1234567812345678')).to.be.false;
328
+ done();
329
+ }, 50);
330
+ });
331
+ });
332
+ });
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Integration tests for ReplayMap with API
3
+ */
4
+
5
+ /* globals describe */
6
+ /* globals it */
7
+ /* globals beforeEach */
8
+ /* globals afterEach */
9
+
10
+ import { expect } from 'chai';
11
+ import sinon from 'sinon';
12
+
13
+ import ReplayMap from '../../../src/browser/replay/replayMap.js';
14
+ import Recorder from '../../../src/browser/replay/recorder.js';
15
+ import Tracing from '../../../src/tracing/tracing.js';
16
+ import Api from '../../../src/api.js';
17
+ import { mockRecordFn } from '../util';
18
+
19
+ const options = {
20
+ enabled: true,
21
+ recorder: {
22
+ enabled: true,
23
+ emitEveryNms: 100,
24
+ },
25
+ };
26
+
27
+ describe('ReplayMap API Integration', function () {
28
+ let tracing;
29
+ let recorder;
30
+ let api;
31
+ let transport;
32
+ let replayMap;
33
+
34
+ beforeEach(function () {
35
+ transport = {
36
+ post: sinon
37
+ .stub()
38
+ .callsFake((accessToken, transportOptions, payload, callback) => {
39
+ setTimeout(() => {
40
+ callback(
41
+ null,
42
+ { err: 0, result: { id: '12345' } },
43
+ { 'Rollbar-Replay-Enabled': 'true' }
44
+ );
45
+ }, 10);
46
+ }),
47
+ postJsonPayload: sinon.stub(),
48
+ };
49
+
50
+ const urlMock = { parse: sinon.stub().returns({}) };
51
+ const truncationMock = {
52
+ truncate: sinon.stub().returns({ error: null, value: '{}' }),
53
+ };
54
+
55
+ tracing = new Tracing(window, options);
56
+ tracing.initSession();
57
+
58
+ const mockPayload = [{ id: 'span1', name: 'recording-span' }];
59
+
60
+ api = new Api(
61
+ { accessToken: 'test-token' },
62
+ transport,
63
+ urlMock,
64
+ truncationMock,
65
+ );
66
+
67
+ recorder = new Recorder(options.recorder, mockRecordFn);
68
+ sinon.stub(recorder, 'dump').returns(mockPayload);
69
+
70
+ replayMap = new ReplayMap({
71
+ recorder,
72
+ api,
73
+ tracing,
74
+ });
75
+
76
+ recorder.start();
77
+ });
78
+
79
+ afterEach(function () {
80
+ if (recorder) {
81
+ recorder.stop();
82
+ }
83
+ sinon.restore();
84
+ });
85
+
86
+ it('should add replay data to map', async function () {
87
+ const uuid = 'test-uuid';
88
+ const replayId = replayMap.add(null, uuid);
89
+ expect(replayId).to.be.a('string');
90
+ expect(replayId.length).to.equal(16); // 8 bytes as hex = 16 characters
91
+
92
+ await new Promise((r) => setTimeout(r, 1000));
93
+
94
+ expect(recorder.dump.calledOnce).to.be.true;
95
+ expect(recorder.dump.calledWith(tracing, replayId, uuid)).to.be.true;
96
+
97
+ const payload = replayMap.getSpans(replayId);
98
+ expect(payload).to.not.be.null;
99
+ expect(payload).to.be.an('array');
100
+ expect(payload[0]).to.have.property('name', 'recording-span');
101
+ });
102
+
103
+ it('should successfully send replay to API', async function () {
104
+ const postSpansSpy = sinon.spy(api, 'postSpans');
105
+
106
+ const replayId = 'test-replay-id';
107
+ const mockPayload = [{ id: 'test-span', name: 'recording-span' }];
108
+ replayMap.setSpans(replayId, mockPayload);
109
+
110
+ const result = await replayMap.send(replayId);
111
+
112
+ expect(result).to.be.true;
113
+ expect(postSpansSpy.calledOnce).to.be.true;
114
+ expect(postSpansSpy.calledWith(mockPayload)).to.be.true;
115
+
116
+ expect(replayMap.getSpans(replayId)).to.be.null;
117
+ });
118
+
119
+ it('should handle API errors during send', async function () {
120
+ const apiError = new Error('API failure');
121
+ sinon.stub(api, 'postSpans').rejects(apiError);
122
+
123
+ const consoleSpy = sinon.spy(console, 'error');
124
+
125
+ const replayId = 'error-replay-id';
126
+ const mockPayload = [{ id: 'test-span', name: 'recording-span' }];
127
+ replayMap.setSpans(replayId, mockPayload);
128
+
129
+ const result = await replayMap.send(replayId);
130
+
131
+ expect(result).to.be.false;
132
+ expect(replayMap.getSpans(replayId)).to.be.null;
133
+ });
134
+
135
+ it('should discard replay without sending', function () {
136
+ const postSpansSpy = sinon.spy(api, 'postSpans');
137
+
138
+ const replayId = 'discard-replay-id';
139
+ const mockPayload = [{ id: 'test-span', name: 'recording-span' }];
140
+ replayMap.setSpans(replayId, mockPayload);
141
+
142
+ const result = replayMap.discard(replayId);
143
+
144
+ expect(result).to.be.true;
145
+ expect(postSpansSpy.called).to.be.false;
146
+
147
+ expect(replayMap.getSpans(replayId)).to.be.null;
148
+ });
149
+
150
+ it('should generate unique replay IDs', function () {
151
+ const replayIds = new Set();
152
+
153
+ sinon.stub(replayMap, '_processReplay').resolves();
154
+
155
+ for (let i = 0; i < 100; i++) {
156
+ const replayId = replayMap.add();
157
+ expect(replayIds.has(replayId)).to.be.false;
158
+ replayIds.add(replayId);
159
+ }
160
+
161
+ expect(replayIds.size).to.equal(100);
162
+ });
163
+ });