@@ -5,17 +5,23 @@ import { flareVue } from '../src/flareVue';
55import type { FlareVueContext, FlareVueOptions } from '../src/types';
66
77const mockReport = vi.fn();
8+ const mockReportMessage = vi.fn();
89
910vi.mock('@flareapp/js', () => ({
1011 flare: {
1112 report: (...args: unknown[]) => mockReport(...args),
13+ reportMessage: (...args: unknown[]) => mockReportMessage(...args),
1214 },
1315}));
1416
15- function createMockApp(initialHandler?: (...args: unknown[]) => void) {
17+ function createMockApp(options?: {
18+ errorHandler?: (...args: unknown[]) => void;
19+ warnHandler?: (...args: unknown[]) => void;
20+ }) {
1621 return {
1722 config: {
18- errorHandler: initialHandler ?? undefined,
23+ errorHandler: options?.errorHandler ?? undefined,
24+ warnHandler: options?.warnHandler ?? undefined,
1925 },
2026 };
2127}
@@ -45,6 +51,7 @@ function callHandler(
4551
4652beforeEach(() => {
4753 mockReport.mockReset();
54+ mockReportMessage.mockReset();
4855});
4956
5057describe('flareVue', () => {
@@ -143,7 +150,7 @@ describe('flareVue', () => {
143150
144151 test('calls initial error handler if one exists', () => {
145152 const initialHandler = vi.fn();
146- const app = createMockApp(initialHandler);
153+ const app = createMockApp({ errorHandler: initialHandler } );
147154 (flareVue as Function)(app);
148155
149156 const error = new Error('test');
@@ -157,7 +164,7 @@ describe('flareVue', () => {
157164
158165 test('passes original error (not converted) to initial handler', () => {
159166 const initialHandler = vi.fn();
160- const app = createMockApp(initialHandler);
167+ const app = createMockApp({ errorHandler: initialHandler } );
161168 (flareVue as Function)(app);
162169
163170 app.config.errorHandler!('string error', null, 'setup function');
@@ -167,7 +174,7 @@ describe('flareVue', () => {
167174
168175 test('does not throw when initial error handler exists', () => {
169176 const initialHandler = vi.fn();
170- const app = createMockApp(initialHandler);
177+ const app = createMockApp({ errorHandler: initialHandler } );
171178 (flareVue as Function)(app);
172179
173180 expect(() => {
@@ -198,7 +205,7 @@ describe('flareVue', () => {
198205 const initialHandler = vi.fn(() => callOrder.push('initialHandler'));
199206 mockReport.mockImplementation(() => callOrder.push('report'));
200207
201- const app = createMockApp(initialHandler);
208+ const app = createMockApp({ errorHandler: initialHandler } );
202209 (flareVue as Function)(app);
203210
204211 app.config.errorHandler!(new Error('test'), null, 'setup function');
@@ -236,7 +243,7 @@ describe('flareVue', () => {
236243
237244 test('reports each error independently when called multiple times', () => {
238245 const initialHandler = vi.fn();
239- const app = createMockApp(initialHandler);
246+ const app = createMockApp({ errorHandler: initialHandler } );
240247 (flareVue as Function)(app);
241248
242249 const error1 = new Error('first');
@@ -253,7 +260,7 @@ describe('flareVue', () => {
253260
254261 test('does not call initial handler when flare.report() throws', () => {
255262 const initialHandler = vi.fn();
256- const app = createMockApp(initialHandler);
263+ const app = createMockApp({ errorHandler: initialHandler } );
257264 (flareVue as Function)(app);
258265
259266 mockReport.mockImplementation(() => {
@@ -503,7 +510,7 @@ describe('flareVue', () => {
503510 const beforeSubmit = vi.fn((params: { context: FlareVueContext }) => params.context);
504511 const afterSubmit = vi.fn();
505512
506- const app = createMockApp(initialHandler);
513+ const app = createMockApp({ errorHandler: initialHandler } );
507514 (flareVue as Function)(app, { beforeEvaluate, beforeSubmit, afterSubmit } satisfies FlareVueOptions);
508515
509516 app.config.errorHandler!(new Error('first'), null, 'setup function');
@@ -521,11 +528,111 @@ describe('flareVue', () => {
521528 mockReport.mockImplementation(() => callOrder.push('report'));
522529 const afterSubmit = vi.fn(() => callOrder.push('afterSubmit'));
523530
524- const app = createMockApp(initialHandler);
531+ const app = createMockApp({ errorHandler: initialHandler } );
525532 (flareVue as Function)(app, { afterSubmit } satisfies FlareVueOptions);
526533
527534 app.config.errorHandler!(new Error('test'), null, 'setup function');
528535
529536 expect(callOrder).toEqual(['report', 'afterSubmit', 'initialHandler']);
530537 });
531538});
539+
540+ describe('flareVue captureWarnings', () => {
541+ test('does not set warnHandler when captureWarnings is not set', () => {
542+ const app = createMockApp();
543+ (flareVue as Function)(app);
544+
545+ expect(app.config.warnHandler).toBeUndefined();
546+ });
547+
548+ test('does not set warnHandler when captureWarnings is false', () => {
549+ const app = createMockApp();
550+ (flareVue as Function)(app, { captureWarnings: false } satisfies FlareVueOptions);
551+
552+ expect(app.config.warnHandler).toBeUndefined();
553+ });
554+
555+ test('sets warnHandler when captureWarnings is true', () => {
556+ const app = createMockApp();
557+ (flareVue as Function)(app, { captureWarnings: true } satisfies FlareVueOptions);
558+
559+ expect(typeof app.config.warnHandler).toBe('function');
560+ });
561+
562+ test('reports warning via flare.reportMessage with message, context, and VueWarning exception class', () => {
563+ const app = createMockApp();
564+ (flareVue as Function)(app, { captureWarnings: true } satisfies FlareVueOptions);
565+
566+ const instance = createMockInstance('Counter');
567+ app.config.warnHandler!('Invalid prop type', instance, 'found in\n---> <Counter>');
568+
569+ expect(mockReportMessage).toHaveBeenCalledOnce();
570+ expect(mockReportMessage).toHaveBeenCalledWith(
571+ 'Invalid prop type',
572+ { vue: { message: 'Invalid prop type', componentName: 'Counter', trace: 'found in\n---> <Counter>' } },
573+ 'VueWarning'
574+ );
575+ });
576+
577+ test('context includes component name and trace', () => {
578+ const app = createMockApp();
579+ (flareVue as Function)(app, { captureWarnings: true } satisfies FlareVueOptions);
580+
581+ const instance = createMockInstance('UserProfile');
582+ const trace = 'found in\n---> <UserProfile> at src/UserProfile.vue\n <App> at src/App.vue';
583+ app.config.warnHandler!('Missing required prop', instance, trace);
584+
585+ const context = mockReportMessage.mock.calls[0][1];
586+ expect(context.vue.componentName).toBe('UserProfile');
587+ expect(context.vue.trace).toBe(trace);
588+ expect(context.vue.message).toBe('Missing required prop');
589+ });
590+
591+ test('uses AnonymousComponent when instance is null', () => {
592+ const app = createMockApp();
593+ (flareVue as Function)(app, { captureWarnings: true } satisfies FlareVueOptions);
594+
595+ app.config.warnHandler!('Some warning', null, '');
596+
597+ const context = mockReportMessage.mock.calls[0][1];
598+ expect(context.vue.componentName).toBe('AnonymousComponent');
599+ });
600+
601+ test('calls initial warn handler after reporting', () => {
602+ const initialWarnHandler = vi.fn();
603+ const app = createMockApp({ warnHandler: initialWarnHandler });
604+ (flareVue as Function)(app, { captureWarnings: true } satisfies FlareVueOptions);
605+
606+ const instance = createMockInstance('Counter');
607+ app.config.warnHandler!('Invalid prop', instance, 'trace');
608+
609+ expect(mockReportMessage).toHaveBeenCalledOnce();
610+ expect(initialWarnHandler).toHaveBeenCalledOnce();
611+ expect(initialWarnHandler).toHaveBeenCalledWith('Invalid prop', instance, 'trace');
612+ });
613+
614+ test('calls initial warn handler after flare.reportMessage', () => {
615+ const callOrder: string[] = [];
616+ const initialWarnHandler = vi.fn(() => callOrder.push('initialWarnHandler'));
617+ mockReportMessage.mockImplementation(() => callOrder.push('reportMessage'));
618+
619+ const app = createMockApp({ warnHandler: initialWarnHandler });
620+ (flareVue as Function)(app, { captureWarnings: true } satisfies FlareVueOptions);
621+
622+ app.config.warnHandler!('Warning', null, '');
623+
624+ expect(callOrder).toEqual(['reportMessage', 'initialWarnHandler']);
625+ });
626+
627+ test('reports each warning independently', () => {
628+ const app = createMockApp();
629+ (flareVue as Function)(app, { captureWarnings: true } satisfies FlareVueOptions);
630+
631+ app.config.warnHandler!('First warning', null, 'trace1');
632+ app.config.warnHandler!('Second warning', null, 'trace2');
633+
634+ expect(mockReportMessage).toHaveBeenCalledTimes(2);
635+ expect(mockReportMessage.mock.calls[0][0]).toBe('First warning');
636+ expect(mockReportMessage.mock.calls[1][0]).toBe('Second warning');
637+ });
638+ });
0 commit comments