light-sentry-sdk/tests/EventBus.test.ts

190 lines
5.9 KiB
TypeScript

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { EventBus } from '../src/core/EventBus';
describe('EventBus', () => {
let consoleSpy: ReturnType<typeof vi.spyOn>;
beforeEach(() => {
consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
});
afterEach(() => {
vi.restoreAllMocks();
});
describe('on / emit', () => {
it('should register a handler and call it on emit', () => {
const bus = new EventBus();
const handler = vi.fn();
bus.on('test-event', handler);
bus.emit('test-event', 'data1', 'data2');
expect(handler).toHaveBeenCalledTimes(1);
expect(handler).toHaveBeenCalledWith('data1', 'data2');
});
it('should call multiple handlers on same event', () => {
const bus = new EventBus();
const handler1 = vi.fn();
const handler2 = vi.fn();
bus.on('test-event', handler1);
bus.on('test-event', handler2);
bus.emit('test-event');
expect(handler1).toHaveBeenCalledTimes(1);
expect(handler2).toHaveBeenCalledTimes(1);
});
it('should not call handlers for different events', () => {
const bus = new EventBus();
const handler = vi.fn();
bus.on('event-a', handler);
bus.emit('event-b');
expect(handler).not.toHaveBeenCalled();
});
it('should handle emit on non-existent event', () => {
const bus = new EventBus();
expect(() => bus.emit('non-existent', 'data')).not.toThrow();
});
it('should pass multiple arguments to handler', () => {
const bus = new EventBus();
const handler = vi.fn();
bus.on('multi-arg', handler);
bus.emit('multi-arg', 1, 'two', { three: 3 }, [4, 5]);
expect(handler).toHaveBeenCalledWith(1, 'two', { three: 3 }, [4, 5]);
});
it('should not throw if a handler throws', () => {
const bus = new EventBus();
const handler1 = vi.fn(() => {
throw new Error('Handler error');
});
const handler2 = vi.fn();
bus.on('error-event', handler1);
bus.on('error-event', handler2);
expect(() => bus.emit('error-event')).not.toThrow();
expect(handler2).toHaveBeenCalledTimes(1);
expect(consoleSpy).toHaveBeenCalled();
});
});
describe('off', () => {
it('should remove a registered handler', () => {
const bus = new EventBus();
const handler = vi.fn();
bus.on('test-event', handler);
bus.off('test-event', handler);
bus.emit('test-event');
expect(handler).not.toHaveBeenCalled();
});
it('should not remove other handlers', () => {
const bus = new EventBus();
const handler1 = vi.fn();
const handler2 = vi.fn();
bus.on('test-event', handler1);
bus.on('test-event', handler2);
bus.off('test-event', handler1);
bus.emit('test-event');
expect(handler1).not.toHaveBeenCalled();
expect(handler2).toHaveBeenCalledTimes(1);
});
it('should handle off for non-existent event', () => {
const bus = new EventBus();
const handler = vi.fn();
expect(() => bus.off('non-existent', handler)).not.toThrow();
});
it('should handle off for non-existent handler', () => {
const bus = new EventBus();
const handler1 = vi.fn();
const handler2 = vi.fn();
bus.on('test-event', handler1);
bus.off('test-event', handler2);
bus.emit('test-event');
expect(handler1).toHaveBeenCalledTimes(1);
});
it('should only remove one instance if handler is registered multiple times', () => {
const bus = new EventBus();
const handler = vi.fn();
bus.on('test-event', handler);
bus.on('test-event', handler);
bus.off('test-event', handler);
bus.emit('test-event');
expect(handler).toHaveBeenCalledTimes(1);
});
});
describe('once', () => {
it('should call handler only once', () => {
const bus = new EventBus();
const handler = vi.fn();
bus.once('once-event', handler);
bus.emit('once-event');
bus.emit('once-event');
expect(handler).toHaveBeenCalledTimes(1);
});
it('should pass arguments to once handler', () => {
const bus = new EventBus();
const handler = vi.fn();
bus.once('once-event', handler);
bus.emit('once-event', 'arg1', 'arg2');
expect(handler).toHaveBeenCalledWith('arg1', 'arg2');
});
it('should work with on and once mixed', () => {
const bus = new EventBus();
const onHandler = vi.fn();
const onceHandler = vi.fn();
bus.on('mixed-event', onHandler);
bus.once('mixed-event', onceHandler);
bus.emit('mixed-event');
bus.emit('mixed-event');
expect(onHandler).toHaveBeenCalledTimes(2);
expect(onceHandler).toHaveBeenCalledTimes(1);
});
it('should not throw if once handler throws', () => {
const bus = new EventBus();
const handler = vi.fn(() => {
throw new Error('Once handler error');
});
bus.once('once-error', handler);
expect(() => bus.emit('once-error')).not.toThrow();
expect(consoleSpy).toHaveBeenCalled();
});
});
describe('destroy', () => {
it('should clear all handlers', () => {
const bus = new EventBus();
const handler1 = vi.fn();
const handler2 = vi.fn();
bus.on('event-a', handler1);
bus.on('event-b', handler2);
bus.destroy();
bus.emit('event-a');
bus.emit('event-b');
expect(handler1).not.toHaveBeenCalled();
expect(handler2).not.toHaveBeenCalled();
});
it('should handle destroy on empty bus', () => {
const bus = new EventBus();
expect(() => bus.destroy()).not.toThrow();
});
it('should work with once handlers after destroy', () => {
const bus = new EventBus();
const handler = vi.fn();
bus.once('event', handler);
bus.destroy();
bus.emit('event');
expect(handler).not.toHaveBeenCalled();
});
});
});