UNPKG

guardz-event

Version:

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

355 lines 17.3 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 isStockUpdate = (data) => { return (typeof data === 'object' && data !== null && typeof data.symbol === 'string' && typeof data.price === 'number' && typeof data.change === 'number'); }; const isMouseEventData = (data) => { return (typeof data === 'object' && data !== null && typeof data.clientX === 'number' && typeof data.clientY === 'number' && typeof data.button === 'number'); }; describe('Fluent API Builder', () => { describe('safe() function', () => { it('should create a new SafeEventBuilder instance', () => { const builder = (0, safe_event_never_throws_1.safe)(); expect(builder).toBeInstanceOf(safe_event_never_throws_1.SafeEventBuilder); }); }); describe('SafeEventBuilder', () => { describe('postMessage()', () => { it('should set event type to postMessage', () => { const builder = (0, safe_event_never_throws_1.safe)().postMessage(); expect(builder.eventType).toBe('postmessage'); }); it('should return the builder for chaining', () => { const builder = (0, safe_event_never_throws_1.safe)().postMessage(); expect(builder).toBeInstanceOf(safe_event_never_throws_1.SafeEventBuilder); }); }); describe('webSocket()', () => { it('should set event type to webSocket', () => { const builder = (0, safe_event_never_throws_1.safe)().webSocket(); expect(builder.eventType).toBe('websocket'); }); it('should return the builder for chaining', () => { const builder = (0, safe_event_never_throws_1.safe)().webSocket(); expect(builder).toBeInstanceOf(safe_event_never_throws_1.SafeEventBuilder); }); }); describe('domEvent()', () => { it('should set event type to domEvent with custom type', () => { const builder = (0, safe_event_never_throws_1.safe)().domEvent('click'); expect(builder.eventType).toBe('click'); }); it('should return the builder for chaining', () => { const builder = (0, safe_event_never_throws_1.safe)().domEvent('click'); expect(builder).toBeInstanceOf(safe_event_never_throws_1.SafeEventBuilder); }); }); describe('eventSource()', () => { it('should set event type to eventSource', () => { const builder = (0, safe_event_never_throws_1.safe)().eventSource(); expect(builder.eventType).toBe('eventsource'); }); it('should return the builder for chaining', () => { const builder = (0, safe_event_never_throws_1.safe)().eventSource(); expect(builder).toBeInstanceOf(safe_event_never_throws_1.SafeEventBuilder); }); }); describe('customEvent()', () => { it('should set event type to customEvent with custom type', () => { const builder = (0, safe_event_never_throws_1.safe)().customEvent('stock-update'); expect(builder.eventType).toBe('stock-update'); }); it('should return the builder for chaining', () => { const builder = (0, safe_event_never_throws_1.safe)().customEvent('stock-update'); expect(builder).toBeInstanceOf(safe_event_never_throws_1.SafeEventBuilder); }); }); describe('storageEvent()', () => { it('should set event type to storageEvent', () => { const builder = (0, safe_event_never_throws_1.safe)().storageEvent(); expect(builder.eventType).toBe('storage'); }); it('should return the builder for chaining', () => { const builder = (0, safe_event_never_throws_1.safe)().storageEvent(); expect(builder).toBeInstanceOf(safe_event_never_throws_1.SafeEventBuilder); }); }); describe('guard()', () => { it('should set the guard function', () => { const builder = (0, safe_event_never_throws_1.safe)().guard(isUserMessage); expect(builder.config.guard).toBe(isUserMessage); }); it('should return the builder for chaining', () => { const builder = (0, safe_event_never_throws_1.safe)().guard(isUserMessage); expect(builder).toBeInstanceOf(safe_event_never_throws_1.SafeEventBuilder); }); }); describe('tolerance()', () => { it('should enable tolerance mode', () => { const builder = (0, safe_event_never_throws_1.safe)().tolerance(true); expect(builder.config.tolerance).toBe(true); }); it('should enable tolerance mode by default', () => { const builder = (0, safe_event_never_throws_1.safe)().tolerance(); expect(builder.config.tolerance).toBe(true); }); it('should return the builder for chaining', () => { const builder = (0, safe_event_never_throws_1.safe)().tolerance(true); expect(builder).toBeInstanceOf(safe_event_never_throws_1.SafeEventBuilder); }); }); describe('identifier()', () => { it('should set the identifier', () => { const builder = (0, safe_event_never_throws_1.safe)().identifier('test-id'); expect(builder.config.identifier).toBe('test-id'); }); it('should return the builder for chaining', () => { const builder = (0, safe_event_never_throws_1.safe)().identifier('test-id'); expect(builder).toBeInstanceOf(safe_event_never_throws_1.SafeEventBuilder); }); }); describe('timeout()', () => { it('should set the timeout', () => { const builder = (0, safe_event_never_throws_1.safe)().timeout(5000); expect(builder.config.timeout).toBe(5000); }); it('should return the builder for chaining', () => { const builder = (0, safe_event_never_throws_1.safe)().timeout(5000); expect(builder).toBeInstanceOf(safe_event_never_throws_1.SafeEventBuilder); }); }); describe('allowedOrigins()', () => { it('should set allowed origins', () => { const origins = ['https://trusted-domain.com']; const builder = (0, safe_event_never_throws_1.safe)().allowedOrigins(origins); expect(builder.config.allowedOrigins).toBe(origins); }); it('should return the builder for chaining', () => { const builder = (0, safe_event_never_throws_1.safe)().allowedOrigins(['https://trusted-domain.com']); expect(builder).toBeInstanceOf(safe_event_never_throws_1.SafeEventBuilder); }); }); describe('allowedSources()', () => { it('should set allowed sources', () => { const sources = ['trusted-source']; const builder = (0, safe_event_never_throws_1.safe)().allowedSources(sources); expect(builder.config.allowedSources).toBe(sources); }); it('should return the builder for chaining', () => { const builder = (0, safe_event_never_throws_1.safe)().allowedSources(['trusted-source']); expect(builder).toBeInstanceOf(safe_event_never_throws_1.SafeEventBuilder); }); }); describe('onError()', () => { it('should set the error callback', () => { const onError = jest.fn(); const builder = (0, safe_event_never_throws_1.safe)().onError(onError); expect(builder.config.onError).toBe(onError); }); it('should return the builder for chaining', () => { const onError = jest.fn(); const builder = (0, safe_event_never_throws_1.safe)().onError(onError); expect(builder).toBeInstanceOf(safe_event_never_throws_1.SafeEventBuilder); }); }); describe('createHandler()', () => { it('should create a PostMessage handler', () => { const handler = (0, safe_event_never_throws_1.safe)() .postMessage() .guard(isUserMessage) .allowedOrigins(['https://trusted-domain.com']) .createHandler(); expect(typeof handler).toBe('function'); const validEvent = new MessageEvent('message', { origin: 'https://trusted-domain.com', data: { id: 1, name: 'John', email: 'john@example.com', isActive: true, }, }); const result = handler(validEvent); expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS); }); it('should create a WebSocket handler', () => { const handler = (0, safe_event_never_throws_1.safe)() .webSocket() .guard(isStockUpdate) .createHandler(); expect(typeof handler).toBe('function'); const validEvent = new MessageEvent('message', { data: { symbol: 'AAPL', price: 150.25, change: 2.5 }, }); const result = handler(validEvent); expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS); }); it('should create a DOM event handler', () => { const handler = (0, safe_event_never_throws_1.safe)() .domEvent('click') .guard(isMouseEventData) .createHandler(); expect(typeof handler).toBe('function'); const validEvent = new MouseEvent('click', { clientX: 100, clientY: 200, button: 0, }); const result = handler(validEvent); expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS); }); it('should create an EventSource handler', () => { const handler = (0, safe_event_never_throws_1.safe)() .eventSource() .guard(isStockUpdate) .createHandler(); expect(typeof handler).toBe('function'); const validEvent = new MessageEvent('message', { data: { symbol: 'AAPL', price: 150.25, change: 2.5 }, }); const result = handler(validEvent); expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS); }); it('should create a CustomEvent handler', () => { const handler = (0, safe_event_never_throws_1.safe)() .customEvent('stock-update') .guard(isStockUpdate) .createHandler(); expect(typeof handler).toBe('function'); const validEvent = new CustomEvent('stock-update', { detail: { symbol: 'AAPL', price: 150.25, change: 2.5 }, }); const result = handler(validEvent); expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS); }); it('should create a StorageEvent handler', () => { const handler = (0, safe_event_never_throws_1.safe)() .storageEvent() .guard((data) => { return (typeof data === 'object' && data !== null && typeof data.key === 'string' && typeof data.url === 'string'); }) .createHandler(); expect(typeof handler).toBe('function'); const validEvent = new StorageEvent('storage', { key: 'user_preferences', oldValue: 'old_value', newValue: 'new_value', url: 'https://example.com', }); const result = handler(validEvent); expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS); }); it('should handle security violations in PostMessage', () => { const handler = (0, safe_event_never_throws_1.safe)() .postMessage() .guard(isUserMessage) .allowedOrigins(['https://trusted-domain.com']) .createHandler(); const maliciousEvent = new MessageEvent('message', { origin: 'https://malicious-site.com', data: { id: 1, name: 'Hacker', email: 'hack@evil.com', isActive: true, }, }); const result = handler(maliciousEvent); expect(result.status).toBe(safe_event_never_throws_1.Status.ERROR); if (result.status === safe_event_never_throws_1.Status.ERROR) { expect(result.code).toBe(403); } }); it('should handle validation errors', () => { const handler = (0, safe_event_never_throws_1.safe)() .postMessage() .guard(isUserMessage) .allowedOrigins(['https://trusted-domain.com']) .createHandler(); const invalidEvent = new MessageEvent('message', { origin: 'https://trusted-domain.com', data: { id: 'not-a-number', name: 'John', email: 'john@example.com', isActive: true, }, }); const result = handler(invalidEvent); expect(result.status).toBe(safe_event_never_throws_1.Status.ERROR); if (result.status === safe_event_never_throws_1.Status.ERROR) { expect(result.code).toBe(500); } }); }); }); describe('Complex chaining', () => { it('should support full configuration chain', () => { const onError = jest.fn(); const handler = (0, safe_event_never_throws_1.safe)() .postMessage() .guard(isUserMessage) .tolerance(true) .identifier('user-message-handler') .timeout(3000) .allowedOrigins(['https://trusted-domain.com']) .allowedSources(['trusted-source']) .onError(onError) .createHandler(); expect(typeof handler).toBe('function'); const validEvent = new MessageEvent('message', { origin: 'https://trusted-domain.com', data: { id: 1, name: 'John', email: 'john@example.com', isActive: true, }, }); // Manually set the source property since MessageEvent constructor doesn't support it Object.defineProperty(validEvent, 'source', { value: { 'trusted-source': true }, writable: false, configurable: false, }); const result = handler(validEvent); expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS); }); it('should handle null events gracefully', () => { const handler = (0, safe_event_never_throws_1.safe)() .postMessage() .guard(isUserMessage) .createHandler(); const nullEvent = null; const result = handler(nullEvent); expect(result.status).toBe(safe_event_never_throws_1.Status.ERROR); if (result.status === safe_event_never_throws_1.Status.ERROR) { expect(result.code).toBe(500); expect(result.message).toContain('Invalid event'); } }); }); }); //# sourceMappingURL=builder-api.test.js.map