You've already forked homehub
Release 1.4.0
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bthlabs/homehub-core",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.0",
|
||||
"description": "BTHLabs HomeHub - Core",
|
||||
"main": "lib/index.js",
|
||||
"author": "BTHLabs <contact@bthlabs.pl> (https://bthlabs.pl/)",
|
||||
@@ -53,7 +53,6 @@
|
||||
"karma-webpack": "4.0.2",
|
||||
"lodash": "4.17.15",
|
||||
"luxon": "1.24.1",
|
||||
"node-sass": "4.14.1",
|
||||
"null-loader": "4.0.0",
|
||||
"polyfill-crypto.getrandomvalues": "1.0.0",
|
||||
"prop-types": "15.7.2",
|
||||
@@ -62,6 +61,7 @@
|
||||
"react-svg-loader": "3.0.3",
|
||||
"regenerator-runtime": "0.13.5",
|
||||
"sass-loader": "8.0.2",
|
||||
"sass": "^1.3.2",
|
||||
"shallow-with-context": "0.4.1",
|
||||
"style-loader": "1.2.1",
|
||||
"uuid": "8.2.0",
|
||||
|
||||
@@ -16,6 +16,7 @@ export const DEFAULT_DASHBOARDS_CONTEXT = {
|
||||
setCurrentDashboardId: noop,
|
||||
dashboardsHash: null,
|
||||
addDashboard: noop,
|
||||
loadError: null,
|
||||
};
|
||||
|
||||
export const DashboardsContext = React.createContext(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as localStorage from './localStorage';
|
||||
import * as rpc from './rpc';
|
||||
|
||||
export * from './base';
|
||||
export * from './dashboards';
|
||||
@@ -10,3 +11,7 @@ export const LocalStorage = {
|
||||
getItem: localStorage.getItem,
|
||||
setItem: localStorage.setItem,
|
||||
};
|
||||
|
||||
export const RPC = {
|
||||
callMethod: rpc.callMethod,
|
||||
};
|
||||
|
||||
@@ -9,16 +9,12 @@ const Base = SubscribableMixin(
|
||||
EventSourceMixin(HomeHubBaseClass, ['start', 'stop'])
|
||||
);
|
||||
|
||||
export class HomeHubWebSocket extends Base {
|
||||
export class BaseConnector extends Base {
|
||||
constructor (debug, settings) {
|
||||
super(debug, settings);
|
||||
|
||||
this.debug = debug;
|
||||
this.settings = settings;
|
||||
|
||||
this.socket = null;
|
||||
this.reconnectTimeout = null;
|
||||
this.reconnectCounter = 0;
|
||||
}
|
||||
logDebug (...args) {
|
||||
if (this.debug) {
|
||||
@@ -26,6 +22,20 @@ export class HomeHubWebSocket extends Base {
|
||||
console.log(...args);
|
||||
}
|
||||
}
|
||||
start = () => {
|
||||
}
|
||||
stop = () => {
|
||||
}
|
||||
}
|
||||
|
||||
export class HomeHubWebSocket extends BaseConnector {
|
||||
constructor (debug, settings) {
|
||||
super(debug, settings);
|
||||
|
||||
this.socket = null;
|
||||
this.reconnectTimeout = null;
|
||||
this.reconnectCounter = 0;
|
||||
}
|
||||
stopReconnect () {
|
||||
if (this.reconnectTimeout) {
|
||||
window.clearTimeout(this.reconnectTimeout);
|
||||
@@ -51,7 +61,7 @@ export class HomeHubWebSocket extends Base {
|
||||
this.socket.addEventListener('close', this.onSocketClose);
|
||||
this.socket.addEventListener('message', this.onSocketMessage);
|
||||
}
|
||||
stop () {
|
||||
stop = () => {
|
||||
this.socket.close();
|
||||
}
|
||||
onSocketOpen = () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import isError from 'lodash/isError';
|
||||
import React from 'react';
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
|
||||
@@ -23,6 +24,7 @@ export class DashboardsProvider extends React.PureComponent {
|
||||
lastSaveError: DEFAULT_DASHBOARDS_CONTEXT.lastSaveError,
|
||||
isSaving: DEFAULT_DASHBOARDS_CONTEXT.isSaving,
|
||||
isWebSocketConnected: DEFAULT_DASHBOARDS_CONTEXT.isWebSocketConnected,
|
||||
loadError: DEFAULT_DASHBOARDS_CONTEXT.loadError,
|
||||
};
|
||||
}
|
||||
handleSaveError = (error) => {
|
||||
@@ -241,6 +243,7 @@ export class DashboardsProvider extends React.PureComponent {
|
||||
const nextState = {
|
||||
isLoading: false,
|
||||
};
|
||||
|
||||
try {
|
||||
const state = await this.props.loadDashboards();
|
||||
nextState.dashboards = await this.handleLoadedState(state);
|
||||
@@ -251,7 +254,7 @@ export class DashboardsProvider extends React.PureComponent {
|
||||
|
||||
nextState.lastSaveTimestamp = new Date();
|
||||
} catch (error) {
|
||||
nextState.lastSaveError = this.handleSaveError(error);
|
||||
nextState.loadError = this.handleSaveError(error);
|
||||
}
|
||||
|
||||
this.setState(nextState);
|
||||
@@ -277,12 +280,23 @@ export class DashboardsProvider extends React.PureComponent {
|
||||
this.saveDashboards(nextDashboards);
|
||||
this.setState(nextState);
|
||||
}
|
||||
throwLoadError = (errorOrString) => {
|
||||
if (isError(errorOrString)) {
|
||||
throw errorOrString;
|
||||
}
|
||||
|
||||
throw new Error(errorOrString);
|
||||
}
|
||||
componentDidMount () {
|
||||
this.setState({
|
||||
isLoading: true,
|
||||
});
|
||||
|
||||
this.webSocket = new WebSocketLib.HomeHubWebSocket(
|
||||
const ConnectorKlass = (
|
||||
this.props.settings.CONNECTOR || WebSocketLib.HomeHubWebSocket
|
||||
);
|
||||
|
||||
this.webSocket = new ConnectorKlass(
|
||||
this.props.settings.DEBUG, this.props.settings.WEBSOCKET
|
||||
);
|
||||
this.webSocket.addEventListener('start', this.onWebSocketStart);
|
||||
@@ -325,8 +339,13 @@ export class DashboardsProvider extends React.PureComponent {
|
||||
setCurrentDashboardId: this.setCurrentDashboardId,
|
||||
dashboardsHash: this.dashboardsHash,
|
||||
addDashboard: this.addDashboard,
|
||||
loadError: this.state.loadError,
|
||||
};
|
||||
|
||||
if (!this.state.isLoading && this.state.loadError !== null) {
|
||||
this.throwLoadError(this.state.loadError);
|
||||
}
|
||||
|
||||
return (
|
||||
<DashboardsContext.Provider value={contextValue}>
|
||||
{this.state.isLoading && this.props.loader}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import * as WebSocketLib from 'src/lib/websocket';
|
||||
|
||||
describe('src/lib/websocket', () => {
|
||||
describe('HomeHubWebSocket', () => {
|
||||
describe('BaseConnector', () => {
|
||||
const settings = {
|
||||
url: '/websocket',
|
||||
spam: true,
|
||||
};
|
||||
|
||||
it('includes the subscribable mixin', () => {
|
||||
// Given
|
||||
const webSocket = new WebSocketLib.HomeHubWebSocket(false, settings);
|
||||
const webSocket = new WebSocketLib.BaseConnector(false, settings);
|
||||
|
||||
// Then
|
||||
expect(webSocket.__mixins__).toContain('SubscribableMixin');
|
||||
@@ -16,7 +16,7 @@ describe('src/lib/websocket', () => {
|
||||
|
||||
it('includes the event source mixin', () => {
|
||||
// Given
|
||||
const webSocket = new WebSocketLib.HomeHubWebSocket(false, settings);
|
||||
const webSocket = new WebSocketLib.BaseConnector(false, settings);
|
||||
|
||||
// Then
|
||||
expect(webSocket.__mixins__).toContain('EventSourceMixin');
|
||||
@@ -25,14 +25,11 @@ describe('src/lib/websocket', () => {
|
||||
describe('constructor', () => {
|
||||
it('initializes the instance', () => {
|
||||
// Given
|
||||
const webSocket = new WebSocketLib.HomeHubWebSocket(false, settings);
|
||||
const webSocket = new WebSocketLib.BaseConnector(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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -63,6 +60,24 @@ describe('src/lib/websocket', () => {
|
||||
expect(console.log).not.toHaveBeenCalled(); // eslint-disable-line no-console
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('HomeHubWebSocket', () => {
|
||||
const settings = {
|
||||
url: '/websocket',
|
||||
};
|
||||
|
||||
describe('constructor', () => {
|
||||
it('initializes the instance', () => {
|
||||
// Given
|
||||
const webSocket = new WebSocketLib.HomeHubWebSocket(false, settings);
|
||||
|
||||
// Then
|
||||
expect(webSocket.socket).toBe(null);
|
||||
expect(webSocket.reconnectTimeout).toBe(null);
|
||||
expect(webSocket.reconnectCounter).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('stopReconnect', () => {
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -94,6 +94,9 @@ describe('src/providers/DashboardsProvider', () => {
|
||||
expect(component.state('isWebSocketConnected')).toEqual(
|
||||
DEFAULT_DASHBOARDS_CONTEXT.isWebSocketConnected
|
||||
);
|
||||
expect(component.state('loadError')).toBe(
|
||||
DEFAULT_DASHBOARDS_CONTEXT.loadError
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -906,6 +909,7 @@ describe('src/providers/DashboardsProvider', () => {
|
||||
</DashboardsProvider>
|
||||
);
|
||||
spyOn(component.instance(), 'handleLoadedState').and.resolveTo([]);
|
||||
spyOn(component.instance(), 'throwLoadError');
|
||||
|
||||
// When
|
||||
await component.instance().loadDashboards();
|
||||
@@ -914,8 +918,11 @@ describe('src/providers/DashboardsProvider', () => {
|
||||
expect(component.state('dashboards')).toEqual([]);
|
||||
expect(component.state('currentDashboardId')).toBe(null);
|
||||
expect(component.state('lastSaveTimestamp')).toBe(null);
|
||||
expect(component.state('lastSaveError')).toEqual('FIAL');
|
||||
expect(component.state('loadError')).toEqual('FIAL');
|
||||
expect(component.instance().handleLoadedState).not.toHaveBeenCalled();
|
||||
expect(component.instance().throwLoadError).toHaveBeenCalledWith(
|
||||
component.state('loadError'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1018,6 +1025,46 @@ describe('src/providers/DashboardsProvider', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('throwLoadError', () => {
|
||||
it('throws the error as is if it is an Error', () => {
|
||||
// Given
|
||||
const error = new Error('FIAL');
|
||||
|
||||
const component = shallow(
|
||||
<DashboardsProvider
|
||||
loader={<Loader />}
|
||||
loadDashboards={loadDashboards}
|
||||
saveDashboards={saveDashboards}
|
||||
settings={settings}
|
||||
>
|
||||
<Children />
|
||||
</DashboardsProvider>
|
||||
);
|
||||
|
||||
// When
|
||||
expect(() => component.instance().throwLoadError(error)).toThrow(error);
|
||||
});
|
||||
|
||||
it('wraps a string in an error and throws it', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<DashboardsProvider
|
||||
loader={<Loader />}
|
||||
loadDashboards={loadDashboards}
|
||||
saveDashboards={saveDashboards}
|
||||
settings={settings}
|
||||
>
|
||||
<Children />
|
||||
</DashboardsProvider>
|
||||
);
|
||||
|
||||
// When
|
||||
expect(() => component.instance().throwLoadError('FIAL')).toThrowError(
|
||||
Error, 'FIAL',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('componentDidMount', () => {
|
||||
let fakeHomeHubWebSocket = null;
|
||||
let mockWebSocketUnsubscriber = null;
|
||||
@@ -1094,6 +1141,48 @@ describe('src/providers/DashboardsProvider', () => {
|
||||
expect(fakeHomeHubWebSocket.start).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('configures and starts the websocket connection using custom connector', () => {
|
||||
// Given
|
||||
settings.CONNECTOR = jasmine.createSpy().and.returnValue(
|
||||
fakeHomeHubWebSocket
|
||||
);
|
||||
|
||||
const component = shallow(
|
||||
<DashboardsProvider
|
||||
loader={<Loader />}
|
||||
loadDashboards={loadDashboards}
|
||||
saveDashboards={saveDashboards}
|
||||
settings={settings}
|
||||
>
|
||||
<Children />
|
||||
</DashboardsProvider>
|
||||
);
|
||||
spyOn(component.instance(), 'loadDashboards');
|
||||
|
||||
// When
|
||||
component.instance().componentDidMount();
|
||||
|
||||
// Then
|
||||
expect(component.instance().webSocket).toEqual(fakeHomeHubWebSocket);
|
||||
expect(WebSocketLib.HomeHubWebSocket).not.toHaveBeenCalled();
|
||||
expect(settings.CONNECTOR).toHaveBeenCalledWith(
|
||||
settings.DEBUG, settings.WEBSOCKET
|
||||
);
|
||||
expect(fakeHomeHubWebSocket.addEventListener).toHaveBeenCalledWith(
|
||||
'start', component.instance().onWebSocketStart
|
||||
);
|
||||
expect(fakeHomeHubWebSocket.addEventListener).toHaveBeenCalledWith(
|
||||
'stop', component.instance().onWebSocketStop
|
||||
);
|
||||
expect(component.instance().webSocketUnsubscriber).toEqual(
|
||||
mockWebSocketUnsubscriber
|
||||
);
|
||||
expect(fakeHomeHubWebSocket.subscribe).toHaveBeenCalledWith(
|
||||
component.instance().onWebSocketMessage
|
||||
);
|
||||
expect(fakeHomeHubWebSocket.start).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not start the websocket in offline mode', () => {
|
||||
// Given
|
||||
settings.OFFLINE_MODE = true;
|
||||
@@ -1235,6 +1324,7 @@ describe('src/providers/DashboardsProvider', () => {
|
||||
setCurrentDashboardId: component.instance().setCurrentDashboardId,
|
||||
dashboardsHash: component.instance().dashboardsHash,
|
||||
addDashboard: component.instance().addDashboard,
|
||||
loadError: component.state('loadError'),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user