Release 1.3.0

This commit is contained in:
2021-08-26 12:33:15 +02:00
commit 9bb72f0207
1148 changed files with 92133 additions and 0 deletions

View File

@@ -0,0 +1,242 @@
import * as BaseLib from 'src/lib/base';
describe('src/lib/base', () => {
describe('SubscribableMixin', () => {
const TestClass = class extends BaseLib.SubscribableMixin(BaseLib.HomeHubBaseClass) {
};
describe('constructor', () => {
it('initializes the instance', () => {
// Given
const mixin = new TestClass();
// Then
expect(mixin.subscribers).toEqual([]);
});
});
describe('notify', () => {
it('notifies the subscribers', () => {
// Given
const mixin = new TestClass();
const mockSubscriber = jasmine.createSpy();
mixin.subscribers.push(mockSubscriber);
mixin.subscribers.push(null);
// When
mixin.notify('test');
// Then
expect(mockSubscriber).toHaveBeenCalledWith('test', {});
});
it('allows sending a notification with user info', () => {
// Given
const mixin = new TestClass();
const mockSubscriber = jasmine.createSpy();
mixin.subscribers.push(mockSubscriber);
// When
mixin.notify('test', {reset: true});
// Then
expect(mockSubscriber).toHaveBeenCalledWith('test', {reset: true});
});
});
describe('unsubscriberFactory', () => {
it('returns an unsubscriber function', () => {
// Given
const mixin = new TestClass();
// When
const result = mixin.unsubscriberFactory(0);
// Then
expect(result).toBeInstanceOf(Function);
});
it('configures the unsubscriber function to remove the bound subscriber', () => {
// Given
const mixin = new TestClass();
const mockSubscriber1 = jasmine.createSpy();
mixin.subscribers.push(mockSubscriber1);
const mockSubscriber2 = jasmine.createSpy();
mixin.subscribers.push(mockSubscriber2);
const mockSubscriber3 = jasmine.createSpy();
mixin.subscribers.push(mockSubscriber3);
const unsubscriber = mixin.unsubscriberFactory(1);
// When
unsubscriber();
// Then
expect(mixin.subscribers.length).toEqual(3);
expect(mixin.subscribers[0]).toBe(mockSubscriber1);
expect(mixin.subscribers[1]).toBe(null);
expect(mixin.subscribers[2]).toBe(mockSubscriber3);
});
});
describe('subscribe', () => {
it('adds a subscriber and returns its unsubscriber', () => {
// Given
const mixin = new TestClass();
const mockUnsubscriber = jasmine.createSpy();
spyOn(mixin, 'unsubscriberFactory').and.returnValue = mockUnsubscriber;
const mockSubscriber = jasmine.createSpy();
// When
mixin.subscribe(mockSubscriber);
// Then
expect(mixin.subscribers.length).toEqual(1);
expect(mixin.subscribers[0]).toBe(mockSubscriber);
expect(mixin.unsubscriberFactory).toHaveBeenCalledWith(0);
});
});
});
describe('EventSouceMixin', () => {
const Base = BaseLib.EventSourceMixin(BaseLib.HomeHubBaseClass, ['start']);
const TestClass = class extends Base {
};
describe('constructor', () => {
it('initializes the instance', () => {
// Given
const mixin = new TestClass();
// Then
expect(mixin.eventListeners).toEqual({start: []});
});
});
describe('addEventListener', () => {
let mockListener = null;
beforeEach(() => {
mockListener = jasmine.createSpy();
});
it('ignores an uknown event', () => {
// Given
const mixin = new TestClass();
// When
mixin.addEventListener('testing', mockListener);
// Then
expect(mixin.eventListeners.testing).not.toBeDefined();
});
it('adds a listener for an event', () => {
// Given
const mixin = new TestClass();
// When
mixin.addEventListener('start', mockListener);
// Then
expect(mixin.eventListeners.start.length).toEqual(1);
expect(mixin.eventListeners.start[0]).toBe(mockListener);
});
});
describe('addEventListener', () => {
let mockListener = null;
beforeEach(() => {
mockListener = jasmine.createSpy();
});
it('ignores an uknown event', () => {
// Given
const mixin = new TestClass();
mixin.addEventListener('start', mockListener);
// When
mixin.removeEventListener('testing', mockListener);
// Then
expect(mixin.eventListeners.testing).not.toBeDefined();
});
it('ignores an unknown listener', () => {
// Given
const mixin = new TestClass();
mixin.addEventListener('start', mockListener);
const mockListener2 = jasmine.createSpy();
// When
mixin.removeEventListener('start', mockListener2);
// Then
expect(mixin.eventListeners.start.length).toEqual(1);
expect(mixin.eventListeners.start[0]).toBe(mockListener);
});
it('removes a listener for an event', () => {
// Given
const mixin = new TestClass();
mixin.addEventListener('start', mockListener);
const mockListener2 = jasmine.createSpy();
mixin.addEventListener('start', mockListener2);
const mockListener3 = jasmine.createSpy();
mixin.addEventListener('start', mockListener3);
// When
mixin.removeEventListener('start', mockListener2);
// Then
expect(mixin.eventListeners.start.length).toEqual(3);
expect(mixin.eventListeners.start[0]).toBe(mockListener);
expect(mixin.eventListeners.start[1]).toBe(null);
expect(mixin.eventListeners.start[2]).toBe(mockListener3);
});
});
describe('fireEvent', () => {
let mockListener = null;
beforeEach(() => {
mockListener = jasmine.createSpy();
});
it('ignores an uknown event', () => {
// Given
const mixin = new TestClass();
mixin.addEventListener('start', mockListener);
// When
mixin.fireEvent('testing', mockListener);
// Then
expect(mockListener).not.toHaveBeenCalled();
});
it('calls event listeners for an event', () => {
// Given
const mixin = new TestClass();
mixin.addEventListener('start', mockListener);
// When
mixin.fireEvent('start', mockListener);
// Then
expect(mockListener).toHaveBeenCalledWith(mixin);
});
});
});
});

View File

@@ -0,0 +1,23 @@
import * as DashboardsLib from 'src/lib/dashboards';
describe('src/lib/dashboards', () => {
describe('DashboardFactory', () => {
it('initializes the dashboard with defaults', () => {
// Given
const result = DashboardsLib.DashboardFactory();
// Then
expect(result.name).toEqual(jasmine.any(String));
expect(result.id).toBeUUIDv4();
expect(result.services).toEqual([]);
});
it('allows specifying an arbitrary name', () => {
// Given
const result = DashboardsLib.DashboardFactory('Testing');
// Then
expect(result.name).toEqual('Testing');
});
});
});

View File

@@ -0,0 +1,15 @@
import * as HashLib from 'src/lib/hashlib';
describe('src/lib/hashlib', () => {
describe('sha256', () => {
it('generates a hex digest of a message', async () => {
// Given
const result = await HashLib.sha256('spam');
// Then
expect(result).toEqual(
'f10c5a90947d4313c6af600facfb0c259444e4932a217fc341be0b3776f40933'
);
});
});
});

View File

@@ -0,0 +1,48 @@
import * as LocalStorageLib from 'src/lib/localStorage';
describe('src/lib/localStorage', () => {
beforeEach(() => {
window.localStorage.setItem('test', '{"spam":true}');
});
afterEach(() => {
window.localStorage.removeItem('test');
});
describe('getItem', () => {
it('returns the parsed item', () => {
// Given
const result = LocalStorageLib.getItem('test');
// Then
expect(result).toEqual({'spam': true});
});
it('returns default if the item is not present', () => {
// Given
const result = LocalStorageLib.getItem('test2', 'default');
// Then
expect(result).toEqual('default');
});
it('defaults the default to null', () => {
// Given
const result = LocalStorageLib.getItem('test3');
// Then
expect(result).toBe(null);
});
});
describe('setItem', () => {
it('stores the serialized item', () => {
// Given
LocalStorageLib.setItem('test', {spam: false});
// Then
const storedItem = window.localStorage.getItem('test');
expect(storedItem).toEqual('{"spam":false}');
});
});
});

View File

@@ -0,0 +1,117 @@
import * as RPCLib from 'src/lib/rpc';
describe('src/lib/rpc', () => {
describe('callMethod', () => {
beforeEach(() => {
spyOn(window, 'fetch');
});
it('formats and sends a POST request to the backend RPC URL', async () => {
// Given
window.fetch.and.resolveTo({
ok: true,
status: 200,
statusText: 'OK',
json: jasmine.createSpy().and.resolveTo({
result: 'ok',
}),
});
// When
await RPCLib.callMethod('testing', ['spam']);
// Then
expect(window.fetch).toHaveBeenCalledWith('/backend/rpc', {
method: 'POST',
body: jasmine.any(String),
headers: {
'Content-Type': 'application/json',
},
});
const callArgs = window.fetch.calls.argsFor(0);
const body = JSON.parse(callArgs[1].body);
expect(body.jsonrpc).toEqual('2.0');
expect(body.method).toEqual('testing');
expect(body.params).toEqual(['spam']);
expect(body.id).toBeUUIDv4();
});
it('formats a notification call', async () => {
// Given
window.fetch.and.resolveTo({
ok: true,
status: 200,
statusText: 'OK',
json: jasmine.createSpy().and.resolveTo({
result: 'ok',
}),
});
// When
await RPCLib.callMethod('testing', ['spam'], true);
// Then
const callArgs = window.fetch.calls.argsFor(0);
const body = JSON.parse(callArgs[1].body);
expect(body.id).not.toBeDefined();
});
it('returns an error is the response was not OK', async () => {
// Given
window.fetch.and.resolveTo({
ok: false,
status: 400,
statusText: 'Bad Request',
});
// When
const result = await RPCLib.callMethod('testing');
// Then
expect(result).not.toContain('data');
expect(result.error).toMatch('HTTP 400 Bad Request');
});
it('returns an error is the response was a JSONRPC error', async () => {
// Given
window.fetch.and.resolveTo({
ok: true,
status: 200,
statusText: 'OK',
json: jasmine.createSpy().and.resolveTo({
error: {
message: 'Internal error',
data: 'Test',
},
}),
});
// When
const result = await RPCLib.callMethod('testing');
// Then
expect(result).not.toContain('data');
expect(result.error).toMatch('RPC Error: Internal error Test');
});
it('returns an result is the response was successful', async () => {
// Given
window.fetch.and.resolveTo({
ok: true,
status: 200,
statusText: 'OK',
json: jasmine.createSpy().and.resolveTo({
result: 'ok',
}),
});
// When
const result = await RPCLib.callMethod('testing');
// Then
expect(result.data).toEqual('ok');
expect(result).not.toContain('error');
});
});
});

View File

@@ -0,0 +1,495 @@
import * as ServicesLib from 'src/lib/services';
import {DashboardsFactory} from 'tests/__fixtures__/dashboards';
import {FakeService, FakeWidget} from 'tests/__fixtures__/services';
describe('src/lib/services', () => {
describe('ServiceState', () => {
describe('constructor', () => {
it('initializes the instance with a payload', () => {
// Given
const serviceState = new ServicesLib.ServiceState({
data: {
spam: true,
},
error: {
message: 'FIAL',
},
});
// Then
expect(serviceState.payload.data).toEqual({spam: true});
expect(serviceState.payload.error).toEqual({message: 'FIAL'});
});
it('initializes the instance with the default payload', () => {
// Given
const serviceState = new ServicesLib.ServiceState();
// Then
expect(serviceState.payload.data).toBe(null);
expect(serviceState.payload.error).toBe(null);
});
});
describe('isLoading', () => {
it('returns true if both data and error fields are null', () => {
// Given
const serviceState = new ServicesLib.ServiceState();
// Then
expect(serviceState.isLoading()).toBe(true);
});
it('returns false if data is null and error is not null', () => {
// Given
const serviceState = new ServicesLib.ServiceState({
error: {
message: 'FIAL',
},
});
// Then
expect(serviceState.isLoading()).toBe(false);
});
it('returns false if data is not null and error is null', () => {
// Given
const serviceState = new ServicesLib.ServiceState({
data: {
spam: true,
},
});
// Then
expect(serviceState.isLoading()).toBe(false);
});
it('returns false if both data and error fields are not null', () => {
// Given
const serviceState = new ServicesLib.ServiceState({
data: {
spam: true,
},
error: {
message: 'FIAL',
},
});
// Then
expect(serviceState.isLoading()).toBe(false);
});
});
describe('hasData', () => {
it('returns false if isLoading is true and data is not null', () => {
// Given
const serviceState = new ServicesLib.ServiceState({
data: {
spam: true,
},
});
spyOn(serviceState, 'isLoading').and.returnValue(true);
// Then
expect(serviceState.hasData()).toBe(false);
});
it('returns false if isLoading is false and data is null', () => {
// Given
const serviceState = new ServicesLib.ServiceState();
spyOn(serviceState, 'isLoading').and.returnValue(true);
// Then
expect(serviceState.hasData()).toBe(false);
});
it('returns true if isLoading is false and data is not null', () => {
// Given
const serviceState = new ServicesLib.ServiceState({
data: {
spam: true,
},
});
spyOn(serviceState, 'isLoading').and.returnValue(false);
// Then
expect(serviceState.hasData()).toBe(true);
});
});
describe('hasError', () => {
it('returns false if isLoading is true and error is not null', () => {
// Given
const serviceState = new ServicesLib.ServiceState({
error: {
message: 'FIAL',
},
});
spyOn(serviceState, 'isLoading').and.returnValue(true);
// Then
expect(serviceState.hasError()).toBe(false);
});
it('returns false if isLoading is false and error is null', () => {
// Given
const serviceState = new ServicesLib.ServiceState();
spyOn(serviceState, 'isLoading').and.returnValue(true);
// Then
expect(serviceState.hasError()).toBe(false);
});
it('returns true if isLoading is false and error is not null', () => {
// Given
const serviceState = new ServicesLib.ServiceState({
error: {
message: 'FIAL',
},
});
spyOn(serviceState, 'isLoading').and.returnValue(false);
// Then
expect(serviceState.hasError()).toBe(true);
});
});
describe('hasFatalError', () => {
it('returns true if hasData is false and hasError is true', () => {
// Given
const serviceState = new ServicesLib.ServiceState();
spyOn(serviceState, 'hasData').and.returnValue(false);
spyOn(serviceState, 'hasError').and.returnValue(true);
// Then
expect(serviceState.hasFatalError()).toBe(true);
});
it('returns false if hasData is true and hasError is true', () => {
// Given
const serviceState = new ServicesLib.ServiceState();
spyOn(serviceState, 'hasData').and.returnValue(true);
spyOn(serviceState, 'hasError').and.returnValue(true);
// Then
expect(serviceState.hasFatalError()).toBe(false);
});
it('returns false if hasData is true and hasError is false', () => {
// Given
const serviceState = new ServicesLib.ServiceState();
spyOn(serviceState, 'hasData').and.returnValue(true);
spyOn(serviceState, 'hasError').and.returnValue(false);
// Then
expect(serviceState.hasFatalError()).toBe(false);
});
});
describe('update', () => {
it('returns a new ServiceState with updated payload', () => {
// Given
const serviceState = new ServicesLib.ServiceState();
// When
const result = serviceState.update({
data: {
spam: true,
},
error: {
message: 'FIAL',
},
});
// Then
expect(result).not.toBe(serviceState);
expect(result.payload.data).toEqual({spam: true});
expect(result.payload.error).toEqual({message: 'FIAL'});
expect(serviceState.payload).toEqual({
data: null,
error: null,
});
});
});
describe('data', () => {
it('returns the data payload field', () => {
// Given
const serviceState = new ServicesLib.ServiceState({
data: {
spam: true,
},
error: {
message: 'FIAL',
},
});
// Then
expect(serviceState.data()).toEqual(serviceState.payload.data);
});
});
describe('error', () => {
it('returns the error payload field', () => {
// Given
const serviceState = new ServicesLib.ServiceState({
data: {
spam: true,
},
error: {
message: 'FIAL',
},
});
// Then
expect(serviceState.error()).toEqual(serviceState.payload.error);
});
});
});
describe('BaseService', () => {
let spec = null;
beforeEach(() => {
spec = {
instance: 'fake_instance',
characteristics: {
spam: true,
},
widgetComponent: FakeWidget,
layout: {
x: 0,
y: 0,
w: 1,
h: 1,
},
};
});
it('includes the subscribable mixin', () => {
// Given
const service = new FakeService(spec);
// Then
expect(service.__mixins__).toContain('SubscribableMixin');
});
describe('emptyCharacteristics', () => {
it('returns the empty characteristics', () => {
// Given
const result = FakeService.emptyCharacteristics();
// Then
expect(result).toEqual({});
});
});
describe('constructor', () => {
it('initializes the instance', () => {
// Given
const service = new FakeService(spec);
// Then
expect(service.kind).toEqual(FakeService.kind);
expect(service.instance).toEqual(spec.instance);
expect(service.characteristics).toEqual(spec.characteristics);
expect(service.widget).toEqual(FakeService.widget);
expect(service.widgetComponent).toEqual(spec.widgetComponent);
expect(service.layout).toEqual(spec.layout);
});
});
describe('restart', () => {
it('restarts the service', async () => {
// Given
const service = new FakeService(spec);
spyOn(service, 'notify');
spyOn(service, 'start').and.resolveTo(null);
spyOn(service, 'stop').and.resolveTo(null);
// When
await service.restart();
// Then
expect(service.notify).toHaveBeenCalledWith(null, {reset: true});
expect(service.stop).toHaveBeenCalledBefore(service.start);
expect(service.start).toHaveBeenCalled();
});
});
describe('isDummy', () => {
it('returns false', () => {
// Given
const service = new FakeService(spec);
// Then
expect(service.isDummy()).toBe(false);
});
});
describe('initialState', () => {
it('returns null', () => {
// Given
const service = new FakeService(spec);
// Then
expect(service.initialState()).toBe(null);
});
});
describe('setCharacteristics', () => {
it('sets the new characteristics', () => {
// Given
const service = new FakeService(spec);
const newCharacteristics = {spam: false};
// When
service.setCharacteristics(newCharacteristics);
// Then
expect(service.characteristics).not.toBe(newCharacteristics);
expect(service.characteristics).toEqual(newCharacteristics);
});
});
describe('setLayout', () => {
it('sets the new layout', () => {
// Given
const service = new FakeService(spec);
const newLayout = {x: 1, y: 1, w: 2, h: 2};
// When
service.setLayout(newLayout);
// Then
expect(service.layout).not.toBe(newLayout);
expect(service.layout).toEqual(newLayout);
});
});
describe('toJSON', () => {
it('returns a JSON-serializable representation of the service', () => {
// Given
const service = new FakeService(spec);
// When
const result = service.toJSON();
// Then
expect(result).toEqual({
kind: service.kind,
instance: service.instance,
characteristics: service.characteristics,
layout: service.layout,
});
});
});
});
describe('DummyService', () => {
let spec = null;
beforeEach(() => {
spec = {
instance: 'fake_instance',
characteristics: {
spam: true,
},
widgetComponent: FakeWidget,
layout: {
x: 0,
y: 0,
w: 1,
h: 1,
},
};
});
describe('constructor', () => {
it('initializes the instance', () => {
// Given
const service = new ServicesLib.DummyService(spec);
// Then
expect(service.instance).toBe(null);
expect(service.widgetComponent).toBe(null);
});
});
describe('isDummy', () => {
it('returns true', () => {
// Given
const service = new ServicesLib.DummyService(spec);
// Then
expect(service.isDummy()).toBe(true);
});
});
});
describe('lookupService', () => {
let dashboards = null;
beforeEach(() => {
dashboards = DashboardsFactory();
});
it('returns DummyService instance if the specified kind does not exist', () => {
// Given
const result = ServicesLib.lookupService(
dashboards, 'Testing', 'fake_instance'
);
// Then
expect(result).toBeInstanceOf(ServicesLib.DummyService);
expect(result.isDummy()).toBe(true);
});
it('returns DummyService instance if the specified instance does not exist', () => {
// Given
const result = ServicesLib.lookupService(
dashboards, 'FakeService', 'testing'
);
// Then
expect(result).toBeInstanceOf(ServicesLib.DummyService);
expect(result.isDummy()).toBe(true);
});
it('returns the service that matches the specified kind and instace', () => {
// Given
const result = ServicesLib.lookupService(
dashboards, 'FakeService', 'fake_instance'
);
// Then
expect(result).toBe(dashboards[0].services[0]);
});
});
describe('lookupServices', () => {
let dashboards = null;
beforeEach(() => {
dashboards = DashboardsFactory();
dashboards[0].services.push(new ServicesLib.DummyService());
dashboards[0].services.push(new FakeService({
instance: 'fake_instance2',
}));
});
it('returns all instances of services specified by kind', () => {
// Givem
const result = ServicesLib.lookupServices(dashboards, 'FakeService');
// Then
expect(result.length).toEqual(3);
expect(result[0]).toBe(dashboards[0].services[0]);
expect(result[1]).toBe(dashboards[0].services[1]);
expect(result[2]).toBe(dashboards[0].services[3]);
});
});
});

View File

@@ -0,0 +1,268 @@
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);
});
});
});
});