Skip to content

Commit 5bc5ea7

Browse files
committed
add tests
1 parent b4abb07 commit 5bc5ea7

File tree

2 files changed

+117
-137
lines changed

2 files changed

+117
-137
lines changed

packages/nuxt/src/module.ts

+5
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,13 @@ export default defineNuxtModule<ModuleOptions>({
1616
nuxt: '>=3.7.0',
1717
},
1818
},
19+
1920
defaults: {},
2021
setup(moduleOptionsParam, nuxt) {
22+
if (moduleOptionsParam.debug) {
23+
return;
24+
}
25+
2126
const moduleOptions = {
2227
...moduleOptionsParam,
2328
autoInjectServerSentry: moduleOptionsParam.autoInjectServerSentry,

packages/vue/test/errorHandler.test.ts

+112-137
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { afterEach, describe, expect, test, vi } from 'vitest';
1+
import { afterEach, describe, expect, it, test, vi } from 'vitest';
22

33
import { setCurrentClient } from '@sentry/browser';
44

@@ -7,26 +7,29 @@ import type { Operation, Options, ViewModel, Vue } from '../src/types';
77
import { generateComponentTrace } from '../src/vendor/components';
88

99
describe('attachErrorHandler', () => {
10-
describe('attachProps', () => {
10+
describe('attach data to captureException', () => {
1111
afterEach(() => {
1212
vi.resetAllMocks();
13+
// we need timers to still call captureException wrapped inside setTimeout after the error throws
14+
vi.useRealTimers();
1315
});
1416

1517
describe("given I don't want to `attachProps`", () => {
1618
test('no `propsData` is added to the metadata', () => {
17-
// arrange
1819
const t = testHarness({
19-
enableErrorHandler: false,
2020
enableWarnHandler: false,
2121
attachProps: false,
2222
vm: null,
23+
enableConsole: true,
2324
});
2425

25-
// act
26-
t.run();
26+
vi.useFakeTimers();
27+
expect(() => t.run()).toThrow(DummyError);
28+
vi.runAllTimers();
2729

2830
// assert
2931
t.expect.errorToHaveBeenCaptured().withoutProps();
32+
t.expect.errorToHaveBeenCaptured().withMechanismMetadata({ handled: false, type: 'vue-errorHandler' });
3033
});
3134
});
3235

@@ -41,10 +44,13 @@ describe('attachErrorHandler', () => {
4144
});
4245

4346
// act
44-
t.run();
47+
vi.useFakeTimers();
48+
expect(() => t.run()).toThrow(DummyError);
49+
vi.runAllTimers();
4550

4651
// assert
4752
t.expect.errorToHaveBeenCaptured().withoutProps();
53+
t.expect.errorToHaveBeenCaptured().withMechanismMetadata({ handled: false, type: 'vue-errorHandler' });
4854
});
4955
});
5056

@@ -58,7 +64,9 @@ describe('attachErrorHandler', () => {
5864
});
5965

6066
// act
61-
t.run();
67+
vi.useFakeTimers();
68+
expect(() => t.run()).toThrow(DummyError);
69+
vi.runAllTimers();
6270

6371
// assert
6472
t.expect.errorToHaveBeenCaptured().withoutProps();
@@ -76,7 +84,9 @@ describe('attachErrorHandler', () => {
7684
});
7785

7886
// act
79-
t.run();
87+
vi.useFakeTimers();
88+
expect(() => t.run()).toThrow(DummyError);
89+
vi.runAllTimers();
8090

8191
// assert
8292
t.expect.errorToHaveBeenCaptured().withoutProps();
@@ -94,7 +104,9 @@ describe('attachErrorHandler', () => {
94104
});
95105

96106
// act
97-
t.run();
107+
vi.useFakeTimers();
108+
expect(() => t.run()).toThrow(DummyError);
109+
vi.runAllTimers();
98110

99111
// assert
100112
t.expect.errorToHaveBeenCaptured().withProps(props);
@@ -114,7 +126,9 @@ describe('attachErrorHandler', () => {
114126
});
115127

116128
// act
117-
t.run();
129+
vi.useFakeTimers();
130+
expect(() => t.run()).toThrow(DummyError);
131+
vi.runAllTimers();
118132

119133
// assert
120134
t.expect.errorToHaveBeenCaptured().withProps(props);
@@ -123,31 +137,22 @@ describe('attachErrorHandler', () => {
123137
});
124138
});
125139
});
126-
});
127140

128-
describe('provided errorHandler', () => {
129-
describe('given I did not provide an `errorHandler`', () => {
130-
test('it is not called', () => {
141+
describe('attach mechanism metadata', () => {
142+
it('should mark error as unhandled and capture correct metadata', () => {
131143
// arrange
132-
const t = testHarness({
133-
enableErrorHandler: false,
134-
vm: {
135-
$options: {
136-
name: 'stub-vm',
137-
},
138-
},
139-
});
144+
const t = testHarness({ vm: null });
140145

141146
// act
142-
t.run();
147+
vi.useFakeTimers();
148+
expect(() => t.run()).toThrow(DummyError);
149+
vi.runAllTimers();
143150

144151
// assert
145-
t.expect.errorHandlerSpy.not.toHaveBeenCalled();
152+
t.expect.errorToHaveBeenCaptured().withMechanismMetadata({ handled: false, type: 'vue-errorHandler' });
146153
});
147-
});
148154

149-
describe('given I provided an `errorHandler`', () => {
150-
test('it is called', () => {
155+
it('should mark error as handled and properly delegate to error handler', () => {
151156
// arrange
152157
const vm = {
153158
$options: {
@@ -156,6 +161,7 @@ describe('attachErrorHandler', () => {
156161
};
157162
const t = testHarness({
158163
enableErrorHandler: true,
164+
enableConsole: true,
159165
vm,
160166
});
161167

@@ -164,137 +170,102 @@ describe('attachErrorHandler', () => {
164170

165171
// assert
166172
t.expect.errorHandlerSpy.toHaveBeenCalledWith(expect.any(Error), vm, 'stub-lifecycle-hook');
173+
t.expect.errorToHaveBeenCaptured().withMechanismMetadata({ handled: true, type: 'vue-errorHandler' });
167174
});
168175
});
169176
});
170177

171-
describe('error logging', () => {
172-
describe('given I disabled error logging', () => {
173-
describe('when an error is captured', () => {
174-
test('it logs nothing', () => {
175-
// arrange
176-
const vm = {
177-
$options: {
178-
name: 'stub-vm',
179-
},
180-
};
181-
const t = testHarness({
182-
enableWarnHandler: false,
183-
logErrors: false,
184-
vm,
185-
});
186-
187-
// act
188-
t.run();
178+
describe('error re-throwing and logging', () => {
179+
afterEach(() => {
180+
vi.resetAllMocks();
181+
});
189182

190-
// assert
191-
t.expect.consoleErrorSpy.not.toHaveBeenCalled();
192-
t.expect.warnHandlerSpy.not.toHaveBeenCalled();
183+
describe('error re-throwing', () => {
184+
it('should re-throw error when no error handler exists', () => {
185+
const t = testHarness({
186+
enableErrorHandler: false,
187+
enableConsole: true,
188+
vm: { $options: { name: 'stub-vm' } },
193189
});
190+
191+
expect(() => t.run()).toThrow(DummyError);
194192
});
195-
});
196193

197-
describe('given I enabled error logging', () => {
198-
describe('when I provide a `warnHandler`', () => {
199-
describe('when a error is captured', () => {
200-
test.each([
201-
[
202-
'with wm',
203-
{
204-
$options: {
205-
name: 'stub-vm',
206-
},
207-
},
208-
generateComponentTrace({
209-
$options: {
210-
name: 'stub-vm',
211-
},
212-
} as ViewModel),
213-
],
214-
['without vm', null, ''],
215-
])('it calls my `warnHandler` (%s)', (_, vm, expectedTrace) => {
216-
// arrange
217-
const t = testHarness({
218-
vm,
219-
logErrors: true,
220-
enableWarnHandler: true,
221-
});
194+
it('should call user-defined error handler when provided', () => {
195+
const vm = { $options: { name: 'stub-vm' } };
196+
const t = testHarness({
197+
enableErrorHandler: true,
198+
enableConsole: true,
199+
vm,
200+
});
222201

223-
// act
224-
t.run();
202+
t.run();
225203

226-
// assert
227-
t.expect.consoleErrorSpy.not.toHaveBeenCalled();
228-
t.expect.warnHandlerSpy.toHaveBeenCalledWith(
229-
'Error in stub-lifecycle-hook: "DummyError: just an error"',
230-
vm,
231-
expectedTrace,
232-
);
233-
});
234-
});
204+
t.expect.errorHandlerSpy.toHaveBeenCalledWith(expect.any(Error), vm, 'stub-lifecycle-hook');
235205
});
206+
});
236207

237-
describe('when I do not provide a `warnHandler`', () => {
238-
describe("and I don't have a console", () => {
239-
test('it logs nothing', () => {
240-
// arrange
241-
const vm = {
242-
$options: {
243-
name: 'stub-vm',
244-
},
245-
};
246-
const t = testHarness({
247-
vm,
248-
logErrors: true,
249-
enableConsole: false,
250-
});
208+
describe('error logging enabled', () => {
209+
it('should use console.error when an `errorHandler` is available', () => {
210+
const t = testHarness({
211+
vm: null,
212+
logErrors: true,
213+
enableConsole: true,
214+
enableErrorHandler: true,
215+
enableWarnHandler: false,
216+
});
251217

252-
// act
253-
t.run();
218+
t.run();
254219

255-
// assert
256-
t.expect.consoleErrorSpy.not.toHaveBeenCalled();
257-
});
220+
t.expect.consoleErrorSpy.toHaveBeenCalledWith(
221+
'[Vue warn]: Error in stub-lifecycle-hook: "DummyError: just an error"',
222+
);
223+
});
224+
225+
it('should prefer warn handler over console.error when both are available', () => {
226+
const vm = { $options: { name: 'stub-vm' } };
227+
const t = testHarness({
228+
vm,
229+
logErrors: true,
230+
enableErrorHandler: true,
231+
enableWarnHandler: true,
232+
enableConsole: true,
258233
});
259234

260-
describe('and I silenced logging in Vue', () => {
261-
test('it logs nothing', () => {
262-
// arrange
263-
const vm = {
264-
$options: {
265-
name: 'stub-vm',
266-
},
267-
};
268-
const t = testHarness({
269-
vm,
270-
logErrors: true,
271-
silent: true,
272-
});
235+
t.run();
273236

274-
// act
275-
t.run();
237+
t.expect.consoleErrorSpy.not.toHaveBeenCalled();
238+
t.expect.warnHandlerSpy.toHaveBeenCalledWith(
239+
'Error in stub-lifecycle-hook: "DummyError: just an error"',
240+
vm,
241+
generateComponentTrace(vm as ViewModel),
242+
);
243+
});
276244

277-
// assert
278-
t.expect.consoleErrorSpy.not.toHaveBeenCalled();
279-
});
245+
it('should throw error when no handler is available', () => {
246+
const vm = { $options: { name: 'stub-vm' } };
247+
const t = testHarness({
248+
vm,
249+
logErrors: true,
250+
silent: true,
280251
});
281252

282-
test('it call `console.error`', () => {
283-
// arrange
284-
const t = testHarness({
285-
vm: null,
286-
logErrors: true,
287-
enableConsole: true,
288-
});
289-
290-
// act
291-
t.run();
253+
expect(() => t.run()).toThrow(DummyError);
254+
});
292255

293-
// assert
294-
t.expect.consoleErrorSpy.toHaveBeenCalledWith(
295-
'[Vue warn]: Error in stub-lifecycle-hook: "DummyError: just an error"',
296-
);
256+
it('should fallback to console.error when warn handler is not available', () => {
257+
const t = testHarness({
258+
vm: null,
259+
logErrors: true,
260+
enableConsole: true,
261+
enableErrorHandler: true,
297262
});
263+
264+
t.run();
265+
266+
t.expect.consoleErrorSpy.toHaveBeenCalledWith(
267+
'[Vue warn]: Error in stub-lifecycle-hook: "DummyError: just an error"',
268+
);
298269
});
299270
});
300271
});
@@ -393,6 +364,7 @@ const testHarness = ({
393364
expect(captureExceptionSpy).toHaveBeenCalledTimes(1);
394365
const error = captureExceptionSpy.mock.calls[0][0];
395366
const contexts = captureExceptionSpy.mock.calls[0][1]?.captureContext.contexts;
367+
const mechanismMetadata = captureExceptionSpy.mock.calls[0][1]?.mechanism;
396368

397369
expect(error).toBeInstanceOf(DummyError);
398370

@@ -403,6 +375,9 @@ const testHarness = ({
403375
withoutProps: () => {
404376
expect(contexts).not.toHaveProperty('vue.propsData');
405377
},
378+
withMechanismMetadata: (mechanism: { handled: boolean; type: 'vue-errorHandler' }) => {
379+
expect(mechanismMetadata).toEqual(mechanism);
380+
},
406381
};
407382
},
408383
},

0 commit comments

Comments
 (0)