Files
WebRTC-Broadcast/tests/frontend/UIController.test.ts
2025-09-05 00:36:54 -04:00

198 lines
5.8 KiB
TypeScript

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');
});
});