Skip to content

Commit 01ea5af

Browse files
committed
test(catcher): Catcher beforeSend feature tests unified
1 parent 5ea8e14 commit 01ea5af

File tree

3 files changed

+151
-196
lines changed

3 files changed

+151
-196
lines changed

packages/javascript/tests/before-send.test.ts

Lines changed: 0 additions & 181 deletions
This file was deleted.
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import { describe, it, expect, vi } from 'vitest';
2+
import { createCatcher, createTransport, wait, getLastPayload } from './catcher.helpers';
3+
4+
describe('Catcher', () => {
5+
it('should send event as-is when beforeSend returns it unchanged', async () => {
6+
const { sendSpy, transport } = createTransport();
7+
const hawk = createCatcher(transport, { beforeSend: (event) => event });
8+
9+
hawk.send(new Error('hello'));
10+
await wait();
11+
12+
expect(sendSpy).toHaveBeenCalledOnce();
13+
expect(getLastPayload(sendSpy).title).toBe('hello');
14+
});
15+
16+
it('should send modified event when beforeSend mutates and returns it', async () => {
17+
const { sendSpy, transport } = createTransport();
18+
const hawk = createCatcher(transport, {
19+
beforeSend: (event) => {
20+
event.context = { sanitized: true };
21+
22+
return event;
23+
},
24+
});
25+
26+
hawk.send(new Error('modify'));
27+
await wait();
28+
29+
expect(sendSpy).toHaveBeenCalledOnce();
30+
expect(getLastPayload(sendSpy).context).toEqual({ sanitized: true });
31+
});
32+
33+
it('should not send event when beforeSend returns false', async () => {
34+
const { sendSpy, transport } = createTransport();
35+
const hawk = createCatcher(transport, { beforeSend: () => false });
36+
37+
hawk.send(new Error('drop'));
38+
await wait();
39+
40+
expect(sendSpy).not.toHaveBeenCalled();
41+
});
42+
43+
it.each([
44+
{ label: 'undefined', value: undefined },
45+
{ label: 'null', value: null },
46+
{ label: 'number (42)', value: 42 },
47+
{ label: 'string ("oops")', value: 'oops' },
48+
{ label: 'true', value: true },
49+
])('should send original event when beforeSend returns $label', async ({ value }) => {
50+
const { sendSpy, transport } = createTransport();
51+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
52+
const hawk = createCatcher(transport, { beforeSend: () => value as any });
53+
54+
hawk.send(new Error('invalid'));
55+
await wait();
56+
57+
expect(sendSpy).toHaveBeenCalledOnce();
58+
expect(getLastPayload(sendSpy).title).toBe('invalid');
59+
});
60+
61+
it('should send original event when beforeSend deletes required field (title)', async () => {
62+
const { sendSpy, transport } = createTransport();
63+
const hawk = createCatcher(transport, {
64+
beforeSend: (event) => {
65+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
66+
delete (event as any).title;
67+
68+
return event;
69+
},
70+
});
71+
72+
hawk.send(new Error('required-field'));
73+
await wait();
74+
75+
expect(sendSpy).toHaveBeenCalledOnce();
76+
expect(getLastPayload(sendSpy).title).toBe('required-field');
77+
});
78+
79+
it('should still send event when structuredClone throws (non-cloneable payload)', async () => {
80+
const { sendSpy, transport } = createTransport();
81+
const hawk = createCatcher(transport, { beforeSend: (event) => event });
82+
const cloneSpy = vi.spyOn(globalThis, 'structuredClone').mockImplementation(() => {
83+
throw new DOMException('could not be cloned', 'DataCloneError');
84+
});
85+
86+
hawk.send(new Error('non-cloneable'));
87+
await wait();
88+
89+
expect(sendSpy).toHaveBeenCalledOnce();
90+
expect(getLastPayload(sendSpy).title).toBe('non-cloneable');
91+
92+
cloneSpy.mockRestore();
93+
});
94+
95+
it('should send event without deleted optional fields', async () => {
96+
const { sendSpy, transport } = createTransport();
97+
const hawk = createCatcher(transport, {
98+
beforeSend: (event) => {
99+
delete event.release;
100+
101+
return event;
102+
},
103+
});
104+
105+
hawk.send(new Error('optional'));
106+
await wait();
107+
108+
expect(sendSpy).toHaveBeenCalledOnce();
109+
expect(getLastPayload(sendSpy).release).toBeUndefined();
110+
});
111+
112+
it('should not throw when beforeSend throws unexpectedly', async () => {
113+
const { transport } = createTransport();
114+
115+
const act = async () => {
116+
createCatcher(transport, {
117+
beforeSend: () => { throw new Error('beforeSend crashed'); },
118+
} as never).send(new Error('e'));
119+
await wait();
120+
};
121+
122+
await expect(act()).resolves.toBeUndefined();
123+
});
124+
125+
it('should send original event unchanged when beforeSend mutates clone and returns undefined', async () => {
126+
const { sendSpy, transport } = createTransport();
127+
const hawk = createCatcher(transport, {
128+
beforeSend: (event) => { (event as any).title = 'mutated'; return undefined as any; },
129+
});
130+
131+
hawk.send(new Error('original'));
132+
await wait();
133+
134+
expect(sendSpy).toHaveBeenCalledOnce();
135+
expect(getLastPayload(sendSpy).title).toBe('original');
136+
});
137+
138+
it('should send new event object when beforeSend returns a brand-new object', async () => {
139+
const { sendSpy, transport } = createTransport();
140+
const hawk = createCatcher(transport, {
141+
beforeSend: (event) => ({ ...event, title: 'brand new title' }),
142+
});
143+
144+
hawk.send(new Error('original'));
145+
await wait();
146+
147+
expect(sendSpy).toHaveBeenCalledOnce();
148+
expect(getLastPayload(sendSpy).title).toBe('brand new title');
149+
});
150+
});
Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { describe, it, expect, vi, beforeEach } from 'vitest';
22
import { BreadcrumbManager } from '../src/addons/breadcrumbs';
33
import type { Transport } from '../src';
4-
import { wait, createTransport, createCatcher } from './catcher.helpers';
4+
import { wait, createCatcher, createTransport } from './catcher.helpers';
55

66
const mockParse = vi.hoisted(() => vi.fn().mockResolvedValue([]));
77
vi.mock('../src/modules/stackParser', () => ({
@@ -15,7 +15,6 @@ describe('Catcher', () => {
1515
(BreadcrumbManager as any).instance = null;
1616
});
1717

18-
// ── Transport failure ─────────────────────────────────────────────────────
1918
describe('transport failure', () => {
2019
it('should not throw when transport.send rejects', async () => {
2120
const transport: Transport = {
@@ -29,18 +28,5 @@ describe('Catcher', () => {
2928

3029
await expect(act()).resolves.toBeUndefined();
3130
});
32-
33-
it('should not throw when beforeSend throws unexpectedly', async () => {
34-
const { transport } = createTransport();
35-
36-
const act = async () => {
37-
createCatcher(transport, {
38-
beforeSend: () => { throw new Error('beforeSend crashed'); },
39-
} as never).send(new Error('e'));
40-
await wait();
41-
};
42-
43-
await expect(act()).resolves.toBeUndefined();
44-
});
4531
});
4632
});

0 commit comments

Comments
 (0)