UNPKG

guardz-event

Version:

Type-safe event handling with runtime validation using guardz for unsafe data from 3rd parties

363 lines 17 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); /// <reference types="jest" /> const safe_event_never_throws_1 = require("../utils/safe-event-never-throws"); // Mock type guards for testing const isUserMessage = (data) => { return (typeof data === 'object' && data !== null && typeof data.id === 'number' && typeof data.name === 'string' && typeof data.email === 'string' && typeof data.isActive === 'boolean'); }; const isPartialUserMessage = (data) => { return typeof data === 'object' && data !== null; }; describe('Tolerance Mode', () => { describe('Legacy API - Tolerance Mode', () => { describe('safePostMessage with tolerance', () => { it('should return success for partial data in tolerance mode', () => { const onError = jest.fn(); const handler = (0, safe_event_never_throws_1.safePostMessage)({ guard: isPartialUserMessage, tolerance: true, onError, }); const partialEvent = new MessageEvent('message', { origin: 'https://trusted-domain.com', data: { id: 1, name: 'John' }, // Missing email and isActive }); const result = handler(partialEvent); expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS); if (result.status === safe_event_never_throws_1.Status.SUCCESS) { expect(result.data).toEqual({ id: 1, name: 'John' }); } expect(onError).not.toHaveBeenCalled(); }); it('should call onError for invalid data in tolerance mode', () => { const onError = jest.fn(); const handler = (0, safe_event_never_throws_1.safePostMessage)({ guard: isPartialUserMessage, tolerance: true, onError, }); const invalidEvent = new MessageEvent('message', { origin: 'https://trusted-domain.com', data: 'not-an-object', }); const result = handler(invalidEvent); expect(result.status).toBe(safe_event_never_throws_1.Status.ERROR); expect(onError).toHaveBeenCalledWith(expect.stringContaining('Validation failed'), expect.objectContaining({ identifier: 'postMessage', originalError: expect.any(Error), })); }); }); describe('safeWebSocket with tolerance', () => { it('should return success for partial data in tolerance mode', () => { const onError = jest.fn(); const handler = (0, safe_event_never_throws_1.safeWebSocket)({ guard: isPartialUserMessage, tolerance: true, onError, }); const partialEvent = new MessageEvent('message', { data: { id: 1, name: 'John' }, // Missing email and isActive }); const result = handler(partialEvent); expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS); if (result.status === safe_event_never_throws_1.Status.SUCCESS) { expect(result.data).toEqual({ id: 1, name: 'John' }); } expect(onError).not.toHaveBeenCalled(); }); }); describe('safeDOMEvent with tolerance', () => { it('should return success for partial data in tolerance mode', () => { const onError = jest.fn(); const handler = (0, safe_event_never_throws_1.safeDOMEvent)({ guard: isPartialUserMessage, tolerance: true, onError, }); const partialEvent = new MouseEvent('click', { clientX: 100, clientY: 200, }); const result = handler(partialEvent); expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS); if (result.status === safe_event_never_throws_1.Status.SUCCESS) { expect(result.data).toEqual({ clientX: 100, clientY: 200, button: 0, }); } expect(onError).not.toHaveBeenCalled(); }); }); }); describe('Ergonomic API - Tolerance Mode', () => { describe('safePostMessageListener with tolerance', () => { it('should call onSuccess for partial data in tolerance mode', () => { const onSuccess = jest.fn(); const onTypeMismatch = jest.fn(); const onError = jest.fn(); const handler = (0, safe_event_never_throws_1.safePostMessageListener)(isPartialUserMessage, { tolerance: true, allowedOrigins: ['https://trusted-domain.com'], onSuccess, onTypeMismatch, onError, }); const partialEvent = new MessageEvent('message', { origin: 'https://trusted-domain.com', data: { id: 1, name: 'John' }, // Missing email and isActive }); handler(partialEvent); expect(onSuccess).toHaveBeenCalledWith({ id: 1, name: 'John' }); expect(onTypeMismatch).not.toHaveBeenCalled(); expect(onError).not.toHaveBeenCalled(); }); it('should call onTypeMismatch for invalid data in tolerance mode', () => { const onSuccess = jest.fn(); const onTypeMismatch = jest.fn(); const onError = jest.fn(); const handler = (0, safe_event_never_throws_1.safePostMessageListener)(isPartialUserMessage, { tolerance: true, allowedOrigins: ['https://trusted-domain.com'], onSuccess, onTypeMismatch, onError, }); const invalidEvent = new MessageEvent('message', { origin: 'https://trusted-domain.com', data: 'not-an-object', }); handler(invalidEvent); expect(onSuccess).not.toHaveBeenCalled(); expect(onTypeMismatch).toHaveBeenCalledWith(expect.stringContaining('Validation failed')); expect(onError).not.toHaveBeenCalled(); }); }); describe('safeWebSocketListener with tolerance', () => { it('should call onSuccess for partial data in tolerance mode', () => { const onSuccess = jest.fn(); const onTypeMismatch = jest.fn(); const onError = jest.fn(); const handler = (0, safe_event_never_throws_1.safeWebSocketListener)(isPartialUserMessage, { tolerance: true, onSuccess, onTypeMismatch, onError, }); const partialEvent = new MessageEvent('message', { data: { id: 1, name: 'John' }, // Missing email and isActive }); handler(partialEvent); expect(onSuccess).toHaveBeenCalledWith({ id: 1, name: 'John' }); expect(onTypeMismatch).not.toHaveBeenCalled(); expect(onError).not.toHaveBeenCalled(); }); }); describe('safeDOMEventListener with tolerance', () => { it('should call onSuccess for partial data in tolerance mode', () => { const onSuccess = jest.fn(); const onTypeMismatch = jest.fn(); const onError = jest.fn(); const handler = (0, safe_event_never_throws_1.safeDOMEventListener)(isPartialUserMessage, { tolerance: true, onSuccess, onTypeMismatch, onError, }); const partialEvent = new MouseEvent('click', { clientX: 100, clientY: 200, }); handler(partialEvent); expect(onSuccess).toHaveBeenCalledWith({ clientX: 100, clientY: 200, button: 0, }); expect(onTypeMismatch).not.toHaveBeenCalled(); expect(onError).not.toHaveBeenCalled(); }); }); describe('safeEventSourceListener with tolerance', () => { it('should call onSuccess for partial data in tolerance mode', () => { const onSuccess = jest.fn(); const onTypeMismatch = jest.fn(); const onError = jest.fn(); const handler = (0, safe_event_never_throws_1.safeEventSourceListener)(isPartialUserMessage, { tolerance: true, onSuccess, onTypeMismatch, onError, }); const partialEvent = new MessageEvent('message', { data: { id: 1, name: 'John' }, // Missing email and isActive }); handler(partialEvent); expect(onSuccess).toHaveBeenCalledWith({ id: 1, name: 'John' }); expect(onTypeMismatch).not.toHaveBeenCalled(); expect(onError).not.toHaveBeenCalled(); }); }); describe('safeCustomEventListener with tolerance', () => { it('should call onSuccess for partial data in tolerance mode', () => { const onSuccess = jest.fn(); const onTypeMismatch = jest.fn(); const onError = jest.fn(); const handler = (0, safe_event_never_throws_1.safeCustomEventListener)(isPartialUserMessage, { tolerance: true, onSuccess, onTypeMismatch, onError, }); const partialEvent = new CustomEvent('user-update', { detail: { id: 1, name: 'John' }, // Missing email and isActive }); handler(partialEvent); expect(onSuccess).toHaveBeenCalledWith({ id: 1, name: 'John' }); expect(onTypeMismatch).not.toHaveBeenCalled(); expect(onError).not.toHaveBeenCalled(); }); }); describe('safeStorageEventListener with tolerance', () => { it('should call onSuccess for partial data in tolerance mode', () => { const onSuccess = jest.fn(); const onTypeMismatch = jest.fn(); const onError = jest.fn(); const handler = (0, safe_event_never_throws_1.safeStorageEventListener)(isPartialUserMessage, { tolerance: true, onSuccess, onTypeMismatch, onError, }); const partialEvent = new StorageEvent('storage', { key: 'user_preferences', oldValue: 'old_value', newValue: 'new_value', url: 'https://example.com', }); handler(partialEvent); expect(onSuccess).toHaveBeenCalledWith({ key: 'user_preferences', oldValue: 'old_value', newValue: 'new_value', url: 'https://example.com', storageArea: null, }); expect(onTypeMismatch).not.toHaveBeenCalled(); expect(onError).not.toHaveBeenCalled(); }); }); }); describe('Tolerance Mode Edge Cases', () => { it('should handle null data in tolerance mode', () => { const onSuccess = jest.fn(); const onTypeMismatch = jest.fn(); const onError = jest.fn(); const handler = (0, safe_event_never_throws_1.safePostMessageListener)(isPartialUserMessage, { tolerance: true, allowedOrigins: ['https://trusted-domain.com'], onSuccess, onTypeMismatch, onError, }); const nullEvent = new MessageEvent('message', { origin: 'https://trusted-domain.com', data: null, }); handler(nullEvent); expect(onSuccess).not.toHaveBeenCalled(); expect(onTypeMismatch).toHaveBeenCalledWith(expect.stringContaining('Validation failed')); expect(onError).not.toHaveBeenCalled(); }); it('should handle undefined data in tolerance mode', () => { const onSuccess = jest.fn(); const onTypeMismatch = jest.fn(); const onError = jest.fn(); const handler = (0, safe_event_never_throws_1.safePostMessageListener)(isPartialUserMessage, { tolerance: true, allowedOrigins: ['https://trusted-domain.com'], onSuccess, onTypeMismatch, onError, }); const undefinedEvent = new MessageEvent('message', { origin: 'https://trusted-domain.com', data: undefined, }); handler(undefinedEvent); expect(onSuccess).not.toHaveBeenCalled(); expect(onTypeMismatch).toHaveBeenCalledWith(expect.stringContaining('Validation failed')); expect(onError).not.toHaveBeenCalled(); }); it('should handle empty object in tolerance mode', () => { const onSuccess = jest.fn(); const onTypeMismatch = jest.fn(); const onError = jest.fn(); const handler = (0, safe_event_never_throws_1.safePostMessageListener)(isPartialUserMessage, { tolerance: true, allowedOrigins: ['https://trusted-domain.com'], onSuccess, onTypeMismatch, onError, }); const emptyEvent = new MessageEvent('message', { origin: 'https://trusted-domain.com', data: {}, }); handler(emptyEvent); expect(onSuccess).toHaveBeenCalledWith({}); expect(onTypeMismatch).not.toHaveBeenCalled(); expect(onError).not.toHaveBeenCalled(); }); }); describe('Tolerance Mode vs Strict Mode Comparison', () => { it('should behave differently in tolerance vs strict mode', () => { const partialData = { id: 1, name: 'John' }; // Missing required fields // Strict mode (tolerance: false) const strictOnSuccess = jest.fn(); const strictOnTypeMismatch = jest.fn(); const strictHandler = (0, safe_event_never_throws_1.safePostMessageListener)(isUserMessage, // Requires all fields { tolerance: false, allowedOrigins: ['https://trusted-domain.com'], onSuccess: strictOnSuccess, onTypeMismatch: strictOnTypeMismatch, onError: jest.fn(), }); const strictEvent = new MessageEvent('message', { origin: 'https://trusted-domain.com', data: partialData, }); strictHandler(strictEvent); expect(strictOnSuccess).not.toHaveBeenCalled(); expect(strictOnTypeMismatch).toHaveBeenCalled(); // Tolerance mode (tolerance: true) const toleranceOnSuccess = jest.fn(); const toleranceOnTypeMismatch = jest.fn(); const toleranceHandler = (0, safe_event_never_throws_1.safePostMessageListener)(isPartialUserMessage, // Allows partial data { tolerance: true, allowedOrigins: ['https://trusted-domain.com'], onSuccess: toleranceOnSuccess, onTypeMismatch: toleranceOnTypeMismatch, onError: jest.fn(), }); const toleranceEvent = new MessageEvent('message', { origin: 'https://trusted-domain.com', data: partialData, }); toleranceHandler(toleranceEvent); expect(toleranceOnSuccess).toHaveBeenCalledWith(partialData); expect(toleranceOnTypeMismatch).not.toHaveBeenCalled(); }); }); }); //# sourceMappingURL=tolerance-mode.test.js.map