UNPKG

@reown/appkit-controllers

Version:

The full stack toolkit to build onchain app UX.

363 lines • 16.4 kB
import { beforeEach, describe, expect, it, vi } from 'vitest'; import { TelemetryController } from '../../src/controllers/TelemetryController.js'; import { AppKitError, withErrorBoundary } from '../../src/utils/withErrorBoundary.js'; // -- Setup -------------------------------------------------------------------- const sendErrorSpy = vi.spyOn(TelemetryController, 'sendError'); // -- Tests -------------------------------------------------------------------- describe('withErrorBoundary', () => { beforeEach(() => { sendErrorSpy.mockClear(); }); it('should use default INTERNAL_SDK_ERROR category when none specified', async () => { const mockController = { async errorMethod() { throw new Error('Test error'); } }; const wrappedController = withErrorBoundary(mockController); await expect(wrappedController.errorMethod()).rejects.toThrow(AppKitError); expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'INTERNAL_SDK_ERROR'); }); it('should use provided default category for non-AppKitError errors', async () => { const mockController = { async errorMethod() { throw new Error('Test error'); } }; const wrappedController = withErrorBoundary(mockController, 'API_ERROR'); try { await wrappedController.errorMethod(); } catch (err) { expect(err).toBeInstanceOf(AppKitError); expect(err.category).toBe('API_ERROR'); } expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'API_ERROR'); }); it('should preserve AppKitError instances regardless of default category', async () => { const mockController = { async errorMethod() { throw new AppKitError('Test error', 'DATA_PARSING_ERROR'); } }; const wrappedController = withErrorBoundary(mockController, 'API_ERROR'); await expect(wrappedController.errorMethod()).rejects.toThrow(AppKitError); expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'DATA_PARSING_ERROR'); }); it('should wrap controller methods with error handling', async () => { const mockController = { state: { value: 'test' }, async successMethod() { return 'success'; }, async errorMethod() { throw new Error('Test error'); }, syncMethod() { return 'sync'; } }; const wrappedController = withErrorBoundary(mockController, 'API_ERROR'); // Test successful async method const successResult = await wrappedController.successMethod(); expect(successResult).toBe('success'); // Test error handling in async method try { await wrappedController.errorMethod(); } catch (err) { expect(err).toBeInstanceOf(AppKitError); expect(err.category).toBe('API_ERROR'); } expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'API_ERROR'); // Test sync method expect(wrappedController.syncMethod()).toBe('sync'); // Test state access expect(wrappedController.state).toBe(mockController.state); }); it('should handle non-Error objects with default category', async () => { const mockController = { async errorMethod() { throw 'string error'; } }; const wrappedController = withErrorBoundary(mockController, 'SECURE_SITE_ERROR'); await expect(wrappedController.errorMethod()).rejects.toThrow(AppKitError); expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'SECURE_SITE_ERROR'); }); it('should preserve method context', async () => { const mockController = { value: 'test', async contextMethod() { return mockController.value; } }; const wrappedController = withErrorBoundary(mockController, 'API_ERROR'); const result = await wrappedController.contextMethod(); expect(result).toBe('test'); }); it('should handle multiple methods with different error types', async () => { const mockController = { async apiErrorMethod() { throw new AppKitError('API Error', 'API_ERROR'); }, async parsingErrorMethod() { throw new AppKitError('Parsing Error', 'DATA_PARSING_ERROR'); }, async internalErrorMethod() { throw new Error('Internal Error'); } }; const wrappedController = withErrorBoundary(mockController, 'SECURE_SITE_ERROR'); await expect(wrappedController.apiErrorMethod()).rejects.toThrow(AppKitError); expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'API_ERROR'); await expect(wrappedController.parsingErrorMethod()).rejects.toThrow(AppKitError); expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'DATA_PARSING_ERROR'); await expect(wrappedController.internalErrorMethod()).rejects.toThrow(AppKitError); expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'SECURE_SITE_ERROR'); }); describe('errorHandler error cases', () => { beforeEach(() => { sendErrorSpy.mockClear(); }); it('should handle Error instances correctly', async () => { const mockController = { async errorMethod() { throw new Error('Standard error message'); } }; const wrappedController = withErrorBoundary(mockController, 'API_ERROR'); try { await wrappedController.errorMethod(); } catch (err) { expect(err).toBeInstanceOf(AppKitError); const appKitError = err; expect(appKitError.message).toBe('Standard error message'); expect(appKitError.category).toBe('API_ERROR'); expect(appKitError.originalError).toBeInstanceOf(Error); expect(appKitError.originalError.message).toBe('Standard error message'); } expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'API_ERROR'); }); it('should handle string errors correctly', async () => { const mockController = { async errorMethod() { throw 'String error message'; } }; const wrappedController = withErrorBoundary(mockController, 'DATA_PARSING_ERROR'); try { await wrappedController.errorMethod(); } catch (err) { expect(err).toBeInstanceOf(AppKitError); const appKitError = err; expect(appKitError.message).toBe('String error message'); expect(appKitError.category).toBe('DATA_PARSING_ERROR'); expect(appKitError.originalError).toBe('String error message'); } expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'DATA_PARSING_ERROR'); }); it('should handle object errors correctly', async () => { const errorObject = { code: 500, message: 'Server error', details: { reason: 'timeout' } }; const mockController = { async errorMethod() { throw errorObject; } }; const wrappedController = withErrorBoundary(mockController, 'SECURE_SITE_ERROR'); try { await wrappedController.errorMethod(); } catch (err) { expect(err).toBeInstanceOf(AppKitError); const appKitError = err; expect(appKitError.message).toBe('Server error'); expect(appKitError.category).toBe('SECURE_SITE_ERROR'); expect(appKitError.originalError).toBe(errorObject); } expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'SECURE_SITE_ERROR'); // empty object const emptyObject = {}; const mockController2 = { async errorMethod() { throw emptyObject; } }; const wrappedController2 = withErrorBoundary(mockController2, 'SECURE_SITE_ERROR'); try { await wrappedController2.errorMethod(); } catch (err) { expect(err).toBeInstanceOf(AppKitError); const appKitError = err; expect(appKitError.message).toBe('Unknown error'); expect(appKitError.category).toBe('SECURE_SITE_ERROR'); expect(appKitError.originalError).toBe(emptyObject); } expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'SECURE_SITE_ERROR'); }); it('should handle null errors correctly', async () => { const mockController = { async errorMethod() { throw null; } }; const wrappedController = withErrorBoundary(mockController, 'INTERNAL_SDK_ERROR'); try { await wrappedController.errorMethod(); } catch (err) { expect(err).toBeInstanceOf(AppKitError); const appKitError = err; expect(appKitError.message).toBe('null'); expect(appKitError.category).toBe('INTERNAL_SDK_ERROR'); expect(appKitError.originalError).toBe(null); } expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'INTERNAL_SDK_ERROR'); }); it('should handle undefined errors correctly', async () => { const mockController = { async errorMethod() { throw undefined; } }; const wrappedController = withErrorBoundary(mockController, 'API_ERROR'); try { await wrappedController.errorMethod(); } catch (err) { expect(err).toBeInstanceOf(AppKitError); const appKitError = err; expect(appKitError.message).toBe('undefined'); expect(appKitError.category).toBe('API_ERROR'); expect(appKitError.originalError).toBe(undefined); } expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'API_ERROR'); }); it('should handle number errors correctly', async () => { const mockController = { async errorMethod() { throw 404; } }; const wrappedController = withErrorBoundary(mockController, 'API_ERROR'); try { await wrappedController.errorMethod(); } catch (err) { expect(err).toBeInstanceOf(AppKitError); const appKitError = err; expect(appKitError.message).toBe('404'); expect(appKitError.category).toBe('API_ERROR'); expect(appKitError.originalError).toBe(404); } expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'API_ERROR'); }); it('should handle boolean errors correctly', async () => { const mockController = { async errorMethod() { throw false; } }; const wrappedController = withErrorBoundary(mockController, 'INTERNAL_SDK_ERROR'); try { await wrappedController.errorMethod(); } catch (err) { expect(err).toBeInstanceOf(AppKitError); const appKitError = err; expect(appKitError.message).toBe('false'); expect(appKitError.category).toBe('INTERNAL_SDK_ERROR'); expect(appKitError.originalError).toBe(false); } expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'INTERNAL_SDK_ERROR'); }); it('should handle objects with non-serializable properties', async () => { const complexObject = { message: 'Complex error', timestamp: new Date(), function: () => 'test', symbol: Symbol('test'), undefined: undefined }; const mockController = { async errorMethod() { throw complexObject; } }; const wrappedController = withErrorBoundary(mockController, 'SECURE_SITE_ERROR'); try { await wrappedController.errorMethod(); } catch (err) { expect(err).toBeInstanceOf(AppKitError); const appKitError = err; // The object should be serialized successfully since Date and undefined are serializable expect(appKitError.message).toContain('Complex error'); expect(appKitError.category).toBe('SECURE_SITE_ERROR'); expect(appKitError.originalError).toBe(complexObject); } expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'SECURE_SITE_ERROR'); }); it('should handle objects if message doesnt exist', async () => { const complexObject = { timestamp: new Date(), function: () => 'test', symbol: Symbol('test'), undefined: undefined }; const mockController = { async errorMethod() { throw complexObject; } }; const wrappedController = withErrorBoundary(mockController, 'SECURE_SITE_ERROR'); try { await wrappedController.errorMethod(); } catch (err) { expect(err).toBeInstanceOf(AppKitError); const appKitError = err; // The object should be serialized successfully since Date and undefined are serializable expect(appKitError.message).toContain('timestamp'); expect(appKitError.category).toBe('SECURE_SITE_ERROR'); expect(appKitError.originalError).toBe(complexObject); } expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'SECURE_SITE_ERROR'); }); it('should handle objects that fail to serialize', async () => { // Create an object that will fail JSON.stringify due to functions and symbols const nonSerializableObject = { message: 'Non-serializable error', func: function () { return 'test'; }, symbol: Symbol('test'), get accessor() { return 'value'; } }; const mockController = { async errorMethod() { throw nonSerializableObject; } }; const wrappedController = withErrorBoundary(mockController, 'DATA_PARSING_ERROR'); try { await wrappedController.errorMethod(); } catch (err) { expect(err).toBeInstanceOf(AppKitError); const appKitError = err; // The object is actually serializable, so it should contain the message expect(appKitError.message).toContain('Non-serializable error'); expect(appKitError.category).toBe('DATA_PARSING_ERROR'); expect(appKitError.originalError).toBe(nonSerializableObject); } expect(sendErrorSpy).toHaveBeenCalledWith(expect.any(AppKitError), 'DATA_PARSING_ERROR'); }); }); }); //# sourceMappingURL=withErrorBoundary.test.js.map