import { test, expect, describe, beforeEach } from "bun:test"; // Simple mock function for tests function mockFn() { let callCount = 0; const fn = () => { callCount++; }; Object.defineProperty(fn, 'toHaveBeenCalledTimes', { value: (expected: number) => { if (callCount !== expected) { throw new Error(`Expected ${expected} calls, got ${callCount}`); } return true; } }); return fn; } // Mock HTML elements class MockHTMLElement { public textContent: string = ''; public className: string = ''; public disabled: boolean = false; private eventListeners: { [key: string]: (() => void)[] } = {}; addEventListener(event: string, handler: () => void) { if (!this.eventListeners[event]) { this.eventListeners[event] = []; } this.eventListeners[event].push(handler); } click() { const handlers = this.eventListeners['click'] || []; handlers.forEach(handler => handler()); } } // Mock document.getElementById const mockElements: { [id: string]: MockHTMLElement } = {}; const getElementByIdCalls: string[] = []; (global as any).document = { getElementById: (id: string) => { getElementByIdCalls.push(id); return mockElements[id] || null; } }; // Import after mocking import { UIController } from "../../public/js/services/UIController.ts"; describe("UIController", () => { let uiController: UIController; let statusElement: MockHTMLElement; let subscribersElement: MockHTMLElement; let startButton: MockHTMLElement; let stopButton: MockHTMLElement; beforeEach(() => { // Reset mock elements statusElement = new MockHTMLElement(); subscribersElement = new MockHTMLElement(); startButton = new MockHTMLElement(); stopButton = new MockHTMLElement(); mockElements['status'] = statusElement; mockElements['subscribers'] = subscribersElement; mockElements['startBtn'] = startButton; mockElements['stopBtn'] = stopButton; // Clear call history getElementByIdCalls.length = 0; uiController = new UIController('status', 'subscribers', 'startBtn', 'stopBtn'); }); test("should initialize with all elements", () => { expect(getElementByIdCalls).toContain('status'); expect(getElementByIdCalls).toContain('subscribers'); expect(getElementByIdCalls).toContain('startBtn'); expect(getElementByIdCalls).toContain('stopBtn'); }); test("should initialize with minimal elements", () => { getElementByIdCalls.length = 0; // Clear previous calls const minimalController = new UIController('status'); expect(getElementByIdCalls).toContain('status'); }); test("should update status correctly", () => { uiController.updateStatus('Connected', 'connected'); expect(statusElement.textContent).toBe('Connected'); expect(statusElement.className).toBe('status connected'); }); test("should update subscribers count", () => { uiController.updateSubscribersCount(5); expect(subscribersElement.textContent).toBe('Subscribers: 5'); }); test("should handle missing subscribers element", () => { const controllerWithoutSubs = new UIController('status'); expect(() => { controllerWithoutSubs.updateSubscribersCount(5); }).not.toThrow(); }); test("should set button states", () => { uiController.setButtonStates(false, true); expect(startButton.disabled).toBe(true); expect(stopButton.disabled).toBe(false); uiController.setButtonStates(true, false); expect(startButton.disabled).toBe(false); expect(stopButton.disabled).toBe(true); }); test("should handle missing buttons", () => { const controllerWithoutButtons = new UIController('status'); expect(() => { controllerWithoutButtons.setButtonStates(true, false); }).not.toThrow(); }); test("should add button click handlers", () => { const startHandler = mockFn(); const stopHandler = mockFn(); uiController.onButtonClick('startBtn', startHandler); uiController.onButtonClick('stopBtn', stopHandler); startButton.click(); expect(startHandler.toHaveBeenCalledTimes(1)).toBe(true); stopButton.click(); expect(stopHandler.toHaveBeenCalledTimes(1)).toBe(true); }); test("should handle non-existent button click handlers", () => { const handler = mockFn(); expect(() => { uiController.onButtonClick('nonExistentBtn', handler); }).not.toThrow(); }); test("should handle multiple clicks", () => { const handler = mockFn(); uiController.onButtonClick('startBtn', handler); startButton.click(); startButton.click(); startButton.click(); expect(handler.toHaveBeenCalledTimes(3)).toBe(true); }); test("should support multiple handlers on same button", () => { const handler1 = mockFn(); const handler2 = mockFn(); uiController.onButtonClick('startBtn', handler1); uiController.onButtonClick('startBtn', handler2); startButton.click(); expect(handler1.toHaveBeenCalledTimes(1)).toBe(true); expect(handler2.toHaveBeenCalledTimes(1)).toBe(true); }); test("should update status with different classes", () => { uiController.updateStatus('Connecting...', 'waiting'); expect(statusElement.className).toBe('status waiting'); uiController.updateStatus('Connected', 'connected'); expect(statusElement.className).toBe('status connected'); uiController.updateStatus('Error occurred', 'error'); expect(statusElement.className).toBe('status error'); }); test("should update subscribers count with zero", () => { uiController.updateSubscribersCount(0); expect(subscribersElement.textContent).toBe('Subscribers: 0'); }); test("should update subscribers count with large numbers", () => { uiController.updateSubscribersCount(1000); expect(subscribersElement.textContent).toBe('Subscribers: 1000'); }); });