-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathcatcher.global-handlers.test.ts
More file actions
109 lines (91 loc) · 3.62 KB
/
catcher.global-handlers.test.ts
File metadata and controls
109 lines (91 loc) · 3.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import Catcher from '../src/catcher';
import { BreadcrumbManager } from '../src/addons/breadcrumbs';
import { TEST_TOKEN, wait, createTransport, getLastPayload } from './catcher.helpers';
const mockParse = vi.hoisted(() => vi.fn().mockResolvedValue([]));
vi.mock('@hawk.so/core', async (importOriginal) => {
const actual = await importOriginal<typeof import('@hawk.so/core')>();
return { ...actual, StackParser: class { parse = mockParse; } };
});
describe('Catcher', () => {
beforeEach(() => {
localStorage.clear();
mockParse.mockResolvedValue([]);
(BreadcrumbManager as any).instance = null;
});
// ── Global error handlers ─────────────────────────────────────────────────
//
// When disableGlobalErrorsHandling is not set, the Catcher listens to
// window 'error' and 'unhandledrejection' events.
describe('global error handlers', () => {
const addedListeners: Array<[string, EventListenerOrEventListenerObject]> = [];
beforeEach(() => {
const origAddEL = window.addEventListener.bind(window);
vi.spyOn(window, 'addEventListener').mockImplementation(
(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions) => {
if (type === 'error' || type === 'unhandledrejection') {
addedListeners.push([type, listener]);
}
return origAddEL(type, listener, options as AddEventListenerOptions);
}
);
});
afterEach(() => {
vi.restoreAllMocks();
for (const [type, listener] of addedListeners) {
window.removeEventListener(type, listener as EventListener);
}
addedListeners.length = 0;
});
it('should capture errors from window error events', async () => {
const { sendSpy, transport } = createTransport();
new Catcher({
token: TEST_TOKEN,
breadcrumbs: false,
consoleTracking: false,
transport,
// disableGlobalErrorsHandling not set → handlers registered
});
window.dispatchEvent(
new ErrorEvent('error', { error: new Error('global error'), message: 'global error' })
);
await wait();
expect(sendSpy).toHaveBeenCalledOnce();
expect(getLastPayload(sendSpy).title).toBe('global error');
});
it('should capture CORS script errors where error object is unavailable', async () => {
const { sendSpy, transport } = createTransport();
new Catcher({
token: TEST_TOKEN,
breadcrumbs: false,
consoleTracking: false,
transport,
});
// CORS case: error property is undefined, only message is available.
window.dispatchEvent(
new ErrorEvent('error', { error: undefined, message: 'Script error.' })
);
await wait();
expect(sendSpy).toHaveBeenCalledOnce();
expect(getLastPayload(sendSpy).title).toBe('Script error.');
});
it('should capture unhandled promise rejections', async () => {
const { sendSpy, transport } = createTransport();
new Catcher({
token: TEST_TOKEN,
breadcrumbs: false,
consoleTracking: false,
transport,
});
window.dispatchEvent(
new PromiseRejectionEvent('unhandledrejection', {
promise: Promise.resolve() as Promise<never>,
reason: new Error('rejected'),
})
);
await wait();
expect(sendSpy).toHaveBeenCalledOnce();
expect(getLastPayload(sendSpy).title).toBe('rejected');
});
});
});