Skip to content

Commit ccbcb22

Browse files
committed
add browser test
1 parent 345b6fd commit ccbcb22

File tree

1 file changed

+105
-6
lines changed

1 file changed

+105
-6
lines changed

packages/browser/test/unit/transports/offline.test.ts

Lines changed: 105 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,41 @@ import type {
66
EventEnvelope,
77
EventItem,
88
InternalBaseTransportOptions,
9+
ReplayEnvelope,
10+
ReplayEvent,
911
TransportMakeRequestResponse,
1012
} from '@sentry/types';
11-
import { createEnvelope } from '@sentry/utils';
13+
import {
14+
createEnvelope,
15+
createEventEnvelopeHeaders,
16+
dsnFromString,
17+
getSdkMetadataForEnvelopeHeader,
18+
parseEnvelope,
19+
} from '@sentry/utils';
20+
21+
// Credit for this awful hack: https://github.com/vitest-dev/vitest/issues/4043#issuecomment-1905172846
22+
class JSDOMCompatibleTextEncoder extends TextEncoder {
23+
encode(input: string) {
24+
if (typeof input !== 'string') {
25+
throw new TypeError('`input` must be a string');
26+
}
27+
28+
const decodedURI = decodeURIComponent(encodeURIComponent(input));
29+
const arr = new Uint8Array(decodedURI.length);
30+
const chars = decodedURI.split('');
31+
for (let i = 0; i < chars.length; i++) {
32+
arr[i] = decodedURI[i].charCodeAt(0);
33+
}
34+
return arr;
35+
}
36+
}
37+
38+
Object.defineProperty(global, 'TextEncoder', {
39+
value: JSDOMCompatibleTextEncoder,
40+
writable: true,
41+
});
1242

13-
import { MIN_DELAY } from '../../../../core/src/transports/offline';
43+
import { MIN_DELAY, START_DELAY } from '../../../../core/src/transports/offline';
1444
import { createStore, makeBrowserOfflineTransport, pop, push, unshift } from '../../../src/transports/offline';
1545

1646
function deleteDatabase(name: string): Promise<void> {
@@ -25,26 +55,62 @@ const ERROR_ENVELOPE = createEnvelope<EventEnvelope>({ event_id: 'aa3ff046696b4b
2555
[{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem,
2656
]);
2757

58+
function createReplayEnvelope(message: string) {
59+
const event: ReplayEvent = {
60+
type: 'replay_event',
61+
timestamp: 1670837008.634,
62+
error_ids: ['errorId'],
63+
trace_ids: ['traceId'],
64+
urls: ['https://example.com'],
65+
replay_id: 'MY_REPLAY_ID',
66+
segment_id: 3,
67+
replay_type: 'buffer',
68+
message,
69+
};
70+
71+
const data = 'nothing';
72+
73+
return createEnvelope<ReplayEnvelope>(
74+
createEventEnvelopeHeaders(
75+
event,
76+
getSdkMetadataForEnvelopeHeader(event),
77+
undefined,
78+
dsnFromString('https://[email protected]/1337'),
79+
),
80+
[
81+
[{ type: 'replay_event' }, event],
82+
[
83+
{
84+
type: 'replay_recording',
85+
length: data.length,
86+
},
87+
data,
88+
],
89+
],
90+
);
91+
}
92+
2893
const transportOptions = {
2994
recordDroppedEvent: () => undefined, // noop
3095
};
3196

3297
type MockResult<T> = T | Error;
3398

3499
export const createTestTransport = (...sendResults: MockResult<TransportMakeRequestResponse>[]) => {
35-
let sendCount = 0;
100+
const envelopes: Array<string | Uint8Array> = [];
36101

37102
return {
38-
getSendCount: () => sendCount,
103+
getSendCount: () => envelopes.length,
104+
getSentEnvelopes: () => envelopes,
39105
baseTransport: (options: InternalBaseTransportOptions) =>
40-
createTransport(options, () => {
106+
createTransport(options, ({ body }) => {
41107
return new Promise((resolve, reject) => {
42108
const next = sendResults.shift();
43109

44110
if (next instanceof Error) {
45111
reject(next);
46112
} else {
47-
sendCount += 1;
113+
envelopes.push(body);
48114
resolve(next as TransportMakeRequestResponse);
49115
}
50116
});
@@ -112,4 +178,37 @@ describe('makeOfflineTransport', () => {
112178
expect(queuedCount).toEqual(1);
113179
expect(getSendCount()).toEqual(2);
114180
});
181+
182+
it('Retains order of replay envelopes', async () => {
183+
const { getSentEnvelopes, baseTransport } = createTestTransport(
184+
{ statusCode: 200 },
185+
// We reject the second envelope to ensure the order is still retained
186+
new Error(),
187+
{ statusCode: 200 },
188+
{ statusCode: 200 },
189+
);
190+
191+
const transport = makeBrowserOfflineTransport(baseTransport)({
192+
...transportOptions,
193+
url: 'http://localhost',
194+
});
195+
196+
await transport.send(createReplayEnvelope('1'));
197+
// This one will fail and get resent in order
198+
await transport.send(createReplayEnvelope('2'));
199+
await transport.send(createReplayEnvelope('3'));
200+
201+
await delay(START_DELAY * 2);
202+
203+
const envelopes = getSentEnvelopes()
204+
.map(buf => (typeof buf === 'string' ? buf : new TextDecoder().decode(buf)))
205+
.map(parseEnvelope);
206+
207+
expect(envelopes).toHaveLength(3);
208+
209+
// Ensure they're still in the correct order
210+
expect((envelopes[0][1][0][1] as ErrorEvent).message).toEqual('1');
211+
expect((envelopes[1][1][0][1] as ErrorEvent).message).toEqual('2');
212+
expect((envelopes[2][1][0][1] as ErrorEvent).message).toEqual('3');
213+
}, 25_000);
115214
});

0 commit comments

Comments
 (0)