import * as WebSocketLib from 'src/lib/websocket'; describe('src/lib/websocket', () => { describe('HomeHubWebSocket', () => { const settings = { url: '/websocket', }; it('includes the subscribable mixin', () => { // Given const webSocket = new WebSocketLib.HomeHubWebSocket(false, settings); // Then expect(webSocket.__mixins__).toContain('SubscribableMixin'); }); it('includes the event source mixin', () => { // Given const webSocket = new WebSocketLib.HomeHubWebSocket(false, settings); // Then expect(webSocket.__mixins__).toContain('EventSourceMixin'); }); describe('constructor', () => { it('initializes the instance', () => { // Given const webSocket = new WebSocketLib.HomeHubWebSocket(false, settings); // Then expect(webSocket.debug).toBe(false); expect(webSocket.settings).toEqual(settings); expect(webSocket.socket).toBe(null); expect(webSocket.reconnectTimeout).toBe(null); expect(webSocket.reconnectCounter).toEqual(0); }); }); describe('logDebug', () => { beforeEach(() => { spyOn(console, 'log'); }); it('logs a message if debug is true', () => { // Given const webSocket = new WebSocketLib.HomeHubWebSocket(true, settings); // When webSocket.logDebug('Testing'); // Then expect(console.log).toHaveBeenCalledWith('Testing'); // eslint-disable-line no-console }); it('does not log a message if debug is false', () => { // Given const webSocket = new WebSocketLib.HomeHubWebSocket(false, settings); // When webSocket.logDebug('Testing'); // Then expect(console.log).not.toHaveBeenCalled(); // eslint-disable-line no-console }); }); describe('stopReconnect', () => { beforeEach(() => { spyOn(window, 'clearTimeout'); }); it('stops the reconnect process', () => { // Given const webSocket = new WebSocketLib.HomeHubWebSocket(false, settings); webSocket.reconnectTimeout = 123; webSocket.reconnectCounter = 5; // When webSocket.stopReconnect(); // Then expect(webSocket.reconnectTimeout).toBe(null); expect(webSocket.reconnectCounter).toEqual(0); expect(window.clearTimeout).toHaveBeenCalledWith(123); }); }); describe('startReconnect', () => { beforeEach(() => { spyOn(window, 'setTimeout').and.returnValue(123); }); it('starts the reconnect process', () => { // Given const webSocket = new WebSocketLib.HomeHubWebSocket(false, settings); webSocket.reconnectCounter = 5; // When webSocket.startReconnect(); // Then expect(webSocket.reconnectCounter).toEqual(6); expect(webSocket.reconnectTimeout).toEqual(123); expect(window.setTimeout).toHaveBeenCalledWith(webSocket.start, 1000); }); it('breaks the reconnect process when retry count reaches limit', () => { // Given const webSocket = new WebSocketLib.HomeHubWebSocket(false, settings); webSocket.reconnectCounter = 30; spyOn(webSocket, 'stopReconnect'); // When let error = null; try { webSocket.startReconnect(); } catch (exc) { error = exc; } // Then expect(error).toBeInstanceOf(Error); expect(webSocket.stopReconnect).toHaveBeenCalled(); }); }); describe('start', () => { let fakeWebSocket = null; beforeEach(() => { fakeWebSocket = jasmine.createSpyObj(['addEventListener', 'close']); spyOn(window, 'WebSocket').and.returnValue(fakeWebSocket); }); it('configures and opens the websocket connection', () => { // Given let fullSettings = { ...settings, protocols: ['spam', 'eggs'], }; const webSocket = new WebSocketLib.HomeHubWebSocket( false, fullSettings ); // When webSocket.start(); // Then expect(window.WebSocket).toHaveBeenCalledWith( fullSettings.url, fullSettings.protocols ); expect(fakeWebSocket.addEventListener).toHaveBeenCalledWith( 'open', webSocket.onSocketOpen ); expect(fakeWebSocket.addEventListener).toHaveBeenCalledWith( 'close', webSocket.onSocketClose ); expect(fakeWebSocket.addEventListener).toHaveBeenCalledWith( 'message', webSocket.onSocketMessage ); }); }); describe('stop', () => { let fakeWebSocket = null; beforeEach(() => { fakeWebSocket = jasmine.createSpyObj(['addEventListener', 'close']); }); it('closes the websocket connection', () => { // Given const webSocket = new WebSocketLib.HomeHubWebSocket(false, settings); webSocket.socket = fakeWebSocket; // When webSocket.stop(); // Then expect(fakeWebSocket.close).toHaveBeenCalled(); }); }); describe('onSocketOpen', () => { let fakeWebSocket = null; beforeEach(() => { fakeWebSocket = jasmine.createSpyObj(['addEventListener', 'close']); fakeWebSocket.readyState = 1; }); it('handles the open websocket event', () => { // Given const webSocket = new WebSocketLib.HomeHubWebSocket(false, settings); webSocket.socket = fakeWebSocket; spyOn(webSocket, 'logDebug'); spyOn(webSocket, 'stopReconnect'); spyOn(webSocket, 'fireEvent'); // When webSocket.onSocketOpen(); // Then expect(webSocket.logDebug).toHaveBeenCalled(); expect(webSocket.stopReconnect).toHaveBeenCalled(); expect(webSocket.fireEvent).toHaveBeenCalledWith('start'); }); }); describe('onSocketClose', () => { it('handles the open websocket event', () => { // Given const webSocket = new WebSocketLib.HomeHubWebSocket(false, settings); spyOn(webSocket, 'logDebug'); spyOn(webSocket, 'startReconnect'); spyOn(webSocket, 'fireEvent'); // When webSocket.onSocketClose({code: 1000}); // Then expect(webSocket.logDebug).toHaveBeenCalled(); expect(webSocket.startReconnect).toHaveBeenCalled(); expect(webSocket.fireEvent).toHaveBeenCalledWith('stop'); }); }); describe('onSocketMessage', () => { beforeEach(() => { spyOn(console, 'error'); }); it('gracefully handles JSON parse error', () => { // Given const webSocket = new WebSocketLib.HomeHubWebSocket(false, settings); spyOn(webSocket, 'notify'); // When webSocket.onSocketMessage({data: 'spam'}); // Then expect(console.error).toHaveBeenCalledWith( jasmine.any(String), jasmine.any(Error) ); expect(webSocket.notify).not.toHaveBeenCalled(); }); it('parses the event data and notifies the subscribers', () => { // Given const webSocket = new WebSocketLib.HomeHubWebSocket(false, settings); spyOn(webSocket, 'notify'); const message = { type: 'TESTING', data: { spam: true, }, }; // When webSocket.onSocketMessage({data: JSON.stringify(message)}); // Then expect(webSocket.notify).toHaveBeenCalledWith(message); }); }); }); });