1281 lines
39 KiB
JavaScript
1281 lines
39 KiB
JavaScript
import {shallow} from 'enzyme';
|
|
import React from 'react';
|
|
|
|
import {
|
|
DEFAULT_DASHBOARDS_CONTEXT, DashboardsContext,
|
|
} from 'src/context/DashboardsContext';
|
|
import * as HashLib from 'src/lib/hashlib';
|
|
import * as ServicesLib from 'src/lib/services';
|
|
import * as WebSocketLib from 'src/lib/websocket';
|
|
import {DashboardsProvider} from 'src/providers/DashboardsProvider';
|
|
|
|
import {
|
|
DashboardsFactory, DashboardsJSONFactory,
|
|
} from 'tests/__fixtures__/dashboards';
|
|
import {FakeService, FakeWidget} from 'tests/__fixtures__/services';
|
|
|
|
describe('src/providers/DashboardsProvider', () => {
|
|
describe('DashboardsProvider', () => {
|
|
const Loader = (props) => <span>LOADER</span>; // eslint-disable-line no-unused-vars
|
|
const Children = (props) => <span>LOADER</span>; // eslint-disable-line no-unused-vars
|
|
let loadDashboards = null;
|
|
let saveDashboards = null;
|
|
let settings = null;
|
|
|
|
beforeEach(() => {
|
|
loadDashboards = jasmine.createSpy();
|
|
saveDashboards = jasmine.createSpy();
|
|
settings = {
|
|
DEBUG: false,
|
|
SERVICES: {
|
|
[FakeService.kind]: FakeService,
|
|
},
|
|
WIDGETS: {
|
|
[FakeService.widget]: FakeWidget,
|
|
},
|
|
WEBSOCKET: {
|
|
url: '/backend/websocket',
|
|
},
|
|
};
|
|
});
|
|
|
|
describe('constructor', () => {
|
|
it('initializes the instance', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
|
|
// Then
|
|
expect(component.instance().webSocket).toBe(null);
|
|
expect(component.instance().webSocketUnsubscriber).toBe(null);
|
|
expect(component.instance().dashboardsHash).toBe(null);
|
|
});
|
|
|
|
it('initializes the state', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
|
|
// Then
|
|
expect(component.state('currentDashboardId')).toEqual(
|
|
DEFAULT_DASHBOARDS_CONTEXT.currentDashboardId
|
|
);
|
|
expect(component.state('dashboards')).toEqual(
|
|
DEFAULT_DASHBOARDS_CONTEXT.dashboards
|
|
);
|
|
expect(component.state('isLoading')).toEqual(
|
|
DEFAULT_DASHBOARDS_CONTEXT.isLoading
|
|
);
|
|
expect(component.state('lastSaveTimestamp')).toEqual(
|
|
DEFAULT_DASHBOARDS_CONTEXT.lastSaveTimestamp
|
|
);
|
|
expect(component.state('lastSaveError')).toEqual(
|
|
DEFAULT_DASHBOARDS_CONTEXT.lastSaveError
|
|
);
|
|
expect(component.state('isSaving')).toEqual(
|
|
DEFAULT_DASHBOARDS_CONTEXT.isSaving
|
|
);
|
|
expect(component.state('isWebSocketConnected')).toEqual(
|
|
DEFAULT_DASHBOARDS_CONTEXT.isWebSocketConnected
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('handleSaveError', () => {
|
|
beforeEach(() => {
|
|
spyOn(console, 'error');
|
|
});
|
|
|
|
it('handles a save error', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
const error = new Error('FIAL');
|
|
|
|
// When
|
|
const result = component.instance().handleSaveError(error);
|
|
|
|
// Then
|
|
expect(result).toEqual('FIAL');
|
|
expect(console.error).toHaveBeenCalledWith(jasmine.any(String), error);
|
|
});
|
|
});
|
|
|
|
describe('onWebSocketStart', () => {
|
|
it('handles websocket start event', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
|
|
// When
|
|
component.instance().onWebSocketStart();
|
|
|
|
// Then
|
|
expect(component.state('isWebSocketConnected')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('onWebSocketStop', () => {
|
|
it('handles websocket stop event', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
component.setState({isWebSocketConnected: true});
|
|
|
|
// When
|
|
component.instance().onWebSocketStop();
|
|
|
|
// Then
|
|
expect(component.state('isWebSocketConnected')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('onWebSocketMessage', () => {
|
|
let fakeDate = null;
|
|
|
|
beforeEach(() => {
|
|
fakeDate = new Date(1987, 9, 3, 8, 0, 0);
|
|
jasmine.clock().install();
|
|
jasmine.clock().mockDate(fakeDate);
|
|
});
|
|
|
|
afterEach(() => {
|
|
jasmine.clock().uninstall();
|
|
});
|
|
|
|
it('handles a service notification for a single service', async () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
|
|
const dashboards = DashboardsFactory();
|
|
dashboards[0].services.forEach((service) => {
|
|
spyOn(service, 'onMessage');
|
|
});
|
|
component.instance().setState({dashboards: dashboards});
|
|
|
|
const data = {spam: true};
|
|
|
|
// When
|
|
await component.instance().onWebSocketMessage({
|
|
type: WebSocketLib.kWebSocketMsgTypeServiceNotification,
|
|
kind: FakeService.kind,
|
|
instance: 'fake_instance',
|
|
data: data,
|
|
});
|
|
|
|
// Then
|
|
expect(dashboards[0].services[0].onMessage).toHaveBeenCalledWith(data);
|
|
expect(dashboards[0].services[1].onMessage).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('handles a service notification for multiple services', async () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
|
|
const dashboards = DashboardsFactory();
|
|
dashboards[0].services.forEach((service) => {
|
|
spyOn(service, 'onMessage');
|
|
});
|
|
component.instance().setState({dashboards: dashboards});
|
|
|
|
const data = {spam: true};
|
|
|
|
// When
|
|
await component.instance().onWebSocketMessage({
|
|
type: WebSocketLib.kWebSocketMsgTypeServiceNotification,
|
|
kind: FakeService.kind,
|
|
data: data,
|
|
});
|
|
|
|
// Then
|
|
expect(dashboards[0].services[0].onMessage).toHaveBeenCalledWith(data);
|
|
expect(dashboards[0].services[1].onMessage).toHaveBeenCalledWith(data);
|
|
});
|
|
|
|
it('handles a state notification', async () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
|
|
const dashboards = DashboardsFactory();
|
|
const data = DashboardsJSONFactory(dashboards);
|
|
|
|
spyOn(component.instance(), 'handleLoadedState').and.resolveTo(dashboards);
|
|
|
|
// When
|
|
await component.instance().onWebSocketMessage({
|
|
type: WebSocketLib.kWebSocketMsgTypeStateNotification,
|
|
data: data,
|
|
});
|
|
|
|
// Then
|
|
expect(component.state('dashboards')).toEqual(dashboards);
|
|
expect(component.state('lastSaveTimestamp')).toEqual(fakeDate);
|
|
expect(component.instance().handleLoadedState).toHaveBeenCalledWith(data);
|
|
});
|
|
|
|
it('does not handle a state notification if the dashboards hash did not change', async () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
|
|
const dashboards = DashboardsFactory();
|
|
const data = DashboardsJSONFactory(dashboards);
|
|
component.instance().dashboardsHash = await HashLib.sha256(
|
|
JSON.stringify(data)
|
|
);
|
|
|
|
spyOn(component.instance(), 'handleLoadedState').and.resolveTo(dashboards);
|
|
|
|
// When
|
|
await component.instance().onWebSocketMessage({
|
|
type: WebSocketLib.kWebSocketMsgTypeStateNotification,
|
|
data: data,
|
|
});
|
|
|
|
// Then
|
|
expect(component.state('dashboards')).not.toEqual(dashboards);
|
|
expect(component.state('lastSaveTimestamp')).not.toEqual(fakeDate);
|
|
expect(component.instance().handleLoadedState).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('currentDashboard', () => {
|
|
it('returns the current dashboard object', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
|
|
const dashboards = DashboardsFactory();
|
|
component.setState({
|
|
dashboards: dashboards,
|
|
currentDashboardId: 'testing',
|
|
});
|
|
|
|
// When
|
|
const result = component.instance().currentDashboard();
|
|
|
|
// Then
|
|
expect(result).toEqual(dashboards[0]);
|
|
});
|
|
});
|
|
|
|
describe('mutateCurrentDashboardServices', () => {
|
|
it('returns the mutated current dashboard object', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
|
|
const dashboards = DashboardsFactory();
|
|
dashboards.push({
|
|
id: 'testing2',
|
|
name: 'Testing2',
|
|
services: [
|
|
new FakeService({
|
|
instance: 'other_dashboard_instance',
|
|
widgetComponent: FakeWidget,
|
|
characteristics: {
|
|
spam: true,
|
|
},
|
|
layout: {
|
|
x: 0,
|
|
y: 0,
|
|
w: 1,
|
|
h: 1,
|
|
},
|
|
}),
|
|
],
|
|
});
|
|
component.setState({
|
|
dashboards: dashboards,
|
|
currentDashboardId: 'testing',
|
|
});
|
|
|
|
// When
|
|
const result = component.instance().mutateCurrentDashboardServices(
|
|
(services) => {
|
|
return services.map((service) => {
|
|
service.setCharacteristics({id: service.instance});
|
|
return service;
|
|
});
|
|
}
|
|
);
|
|
|
|
// Then
|
|
expect(result.length).toEqual(2);
|
|
expect(result[0].services[0].characteristics).toEqual({
|
|
id: result[0].services[0].instance,
|
|
});
|
|
expect(result[0].services[1].characteristics).toEqual({
|
|
id: result[0].services[1].instance,
|
|
});
|
|
expect(result[1].services[0].characteristics).toEqual({
|
|
spam: true,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('serviceFromSpec', () => {
|
|
let spec = null;
|
|
|
|
beforeEach(() => {
|
|
spec = {
|
|
kind: 'FakeService',
|
|
instance: 'fake_instance',
|
|
characteristics: {
|
|
spam: true,
|
|
},
|
|
layout: {
|
|
x: 0,
|
|
y: 0,
|
|
w: 1,
|
|
h: 1,
|
|
},
|
|
};
|
|
});
|
|
|
|
it('creates a dummy service is the service is uknown', () => {
|
|
// Given
|
|
settings.SERVICES = {};
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
|
|
// When
|
|
const result = component.instance().serviceFromSpec(spec);
|
|
|
|
// Then
|
|
expect(result).toBeInstanceOf(ServicesLib.DummyService);
|
|
});
|
|
|
|
it('instantiates a service from the spec', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
|
|
// When
|
|
const result = component.instance().serviceFromSpec(spec);
|
|
|
|
// Then
|
|
expect(result).toBeInstanceOf(FakeService);
|
|
expect(result.instance).toEqual(spec.instance);
|
|
expect(result.characteristics).toEqual(spec.characteristics);
|
|
expect(result.layout).toEqual(spec.layout);
|
|
expect(result.widgetComponent).toEqual(FakeWidget);
|
|
});
|
|
|
|
it('nullifies the widgetComponent if the widget is not known', () => {
|
|
// Given
|
|
settings.WIDGETS = {};
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
|
|
// When
|
|
const result = component.instance().serviceFromSpec(spec);
|
|
|
|
// Then
|
|
expect(result).toBeInstanceOf(FakeService);
|
|
expect(result.widgetComponent).toBe(null);
|
|
});
|
|
});
|
|
|
|
describe('saveDashboards', () => {
|
|
let fakeDate = null;
|
|
|
|
beforeEach(() => {
|
|
saveDashboards = jasmine.createSpy().and.resolveTo('ok');
|
|
|
|
fakeDate = new Date(1987, 9, 3, 8, 0, 0);
|
|
jasmine.clock().install();
|
|
jasmine.clock().mockDate(fakeDate);
|
|
|
|
spyOn(console, 'error');
|
|
});
|
|
|
|
afterEach(() => {
|
|
jasmine.clock().uninstall();
|
|
});
|
|
|
|
it('does not save the dashboards when dashboards hash did not change', async () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
component.setState({lastSaveError: 'FIAL'});
|
|
|
|
const dashboards = DashboardsFactory();
|
|
const data = DashboardsJSONFactory(dashboards);
|
|
|
|
component.instance().dashboardsHash = await HashLib.sha256(
|
|
JSON.stringify({dashboards: data})
|
|
);
|
|
|
|
// When
|
|
await component.instance().saveDashboards(dashboards);
|
|
|
|
// Then
|
|
expect(saveDashboards).not.toHaveBeenCalled();
|
|
expect(component.state('lastSaveTimestamp')).not.toEqual(fakeDate);
|
|
expect(component.state('lastSaveError')).toEqual('FIAL');
|
|
});
|
|
|
|
it('saves the dashboards', async () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
component.setState({lastSaveError: 'FIAL'});
|
|
|
|
const dashboards = DashboardsFactory();
|
|
|
|
// When
|
|
await component.instance().saveDashboards(dashboards);
|
|
|
|
// Then
|
|
expect(saveDashboards).toHaveBeenCalled();
|
|
const savedDashboards = saveDashboards.calls.first().args[0];
|
|
expect(savedDashboards.dashboards.length).toEqual(1);
|
|
expect(savedDashboards.dashboards[0].id).toEqual(dashboards[0].id);
|
|
expect(savedDashboards.dashboards[0].name).toEqual(dashboards[0].name);
|
|
expect(savedDashboards.dashboards[0].services.length).toEqual(2);
|
|
expect(savedDashboards.dashboards[0].services[0]).toEqual(
|
|
dashboards[0].services[0].toJSON()
|
|
);
|
|
expect(savedDashboards.dashboards[0].services[1]).toEqual(
|
|
dashboards[0].services[1].toJSON()
|
|
);
|
|
|
|
expect(component.state('lastSaveTimestamp')).toEqual(fakeDate);
|
|
expect(component.state('lastSaveError')).toBe(null);
|
|
});
|
|
|
|
it('gracefully handles save error', async () => {
|
|
// Given
|
|
saveDashboards = jasmine.createSpy().and.rejectWith(new Error('FIAL'));
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
|
|
const dashboards = DashboardsFactory();
|
|
|
|
component.instance().dashboardsHash = 'spam';
|
|
component.setState({lastSaveTimestamp: fakeDate});
|
|
|
|
// When
|
|
await component.instance().saveDashboards(dashboards);
|
|
|
|
// Then
|
|
expect(component.instance().dashboardsHash).toBe(null);
|
|
expect(component.state('lastSaveTimestamp')).toBe(null);
|
|
expect(component.state('lastSaveError')).toEqual('FIAL');
|
|
});
|
|
});
|
|
|
|
describe('nukeService', () => {
|
|
it('stops and removes the specified service', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
spyOn(component.instance(), 'saveDashboards');
|
|
|
|
const dashboards = DashboardsFactory();
|
|
const serviceToRemove = dashboards[0].services[0];
|
|
spyOn(serviceToRemove, 'stop');
|
|
|
|
component.setState({
|
|
dashboards: dashboards,
|
|
currentDashboardId: dashboards[0].id,
|
|
});
|
|
|
|
// When
|
|
component.instance().nukeService('FakeService', 'fake_instance');
|
|
|
|
// Then
|
|
const newDashboards = component.state('dashboards');
|
|
expect(newDashboards[0].services.length).toEqual(1);
|
|
expect(newDashboards[0].services).not.toContain(serviceToRemove);
|
|
|
|
expect(serviceToRemove.stop).toHaveBeenCalled();
|
|
expect(component.instance().saveDashboards).toHaveBeenCalledWith(
|
|
newDashboards
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('saveServiceCharacteristics', () => {
|
|
it('saves new characteristics for the specified service', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
spyOn(component.instance(), 'saveDashboards');
|
|
|
|
const dashboards = DashboardsFactory();
|
|
const serviceToModify = dashboards[0].services[0];
|
|
spyOn(serviceToModify, 'setCharacteristics');
|
|
|
|
component.setState({
|
|
dashboards: dashboards,
|
|
currentDashboardId: dashboards[0].id,
|
|
});
|
|
|
|
const newCharacteristics = {spam: false};
|
|
|
|
// When
|
|
component.instance().saveServiceCharacteristics(
|
|
'FakeService', 'fake_instance', newCharacteristics
|
|
);
|
|
|
|
// Then
|
|
const newDashboards = component.state('dashboards');
|
|
expect(newDashboards[0].services.length).toEqual(2);
|
|
|
|
expect(serviceToModify.setCharacteristics).toHaveBeenCalledWith(
|
|
newCharacteristics
|
|
);
|
|
expect(component.instance().saveDashboards).toHaveBeenCalledWith(
|
|
newDashboards
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('saveServiceLayout', () => {
|
|
it('saves new characteristics for the specified service', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
spyOn(component.instance(), 'saveDashboards');
|
|
|
|
const dashboards = DashboardsFactory();
|
|
const serviceToModify = dashboards[0].services[0];
|
|
spyOn(serviceToModify, 'setLayout');
|
|
|
|
component.setState({
|
|
dashboards: dashboards,
|
|
currentDashboardId: dashboards[0].id,
|
|
});
|
|
|
|
const newLayout = {x: 1, y: 1, w: 1, h: 1};
|
|
|
|
// When
|
|
component.instance().saveServiceLayout('fake_instance', newLayout);
|
|
|
|
// Then
|
|
const newDashboards = component.state('dashboards');
|
|
expect(newDashboards[0].services.length).toEqual(2);
|
|
|
|
expect(serviceToModify.setLayout).toHaveBeenCalledWith(newLayout);
|
|
expect(component.instance().saveDashboards).toHaveBeenCalledWith(
|
|
newDashboards
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('addService', () => {
|
|
it('is a noop if no dashboard is selected', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
spyOn(component.instance(), 'saveDashboards');
|
|
|
|
const dashboards = DashboardsFactory();
|
|
component.setState({dashboards: dashboards});
|
|
|
|
// When
|
|
component.instance().addService('FakeService', {new: true});
|
|
|
|
// Then
|
|
expect(component.state('dashboards')).toBe(dashboards);
|
|
expect(component.instance().saveDashboards).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('adds a new service', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
spyOn(component.instance(), 'saveDashboards');
|
|
|
|
const dashboards = DashboardsFactory();
|
|
component.setState({
|
|
dashboards: dashboards,
|
|
currentDashboardId: dashboards[0].id,
|
|
});
|
|
|
|
const newServiceCharacteristics = {new: true};
|
|
|
|
// When
|
|
component.instance().addService(
|
|
'FakeService', newServiceCharacteristics
|
|
);
|
|
|
|
// Then
|
|
const newDashboards = component.state('dashboards');
|
|
expect(newDashboards[0].services.length).toEqual(3);
|
|
expect(component.instance().saveDashboards).toHaveBeenCalledWith(
|
|
newDashboards
|
|
);
|
|
|
|
const newService = newDashboards[0].services[2];
|
|
expect(newService).toBeInstanceOf(FakeService);
|
|
expect(newService.instance).toBeUUIDv4();
|
|
expect(newService.characteristics).toEqual(newServiceCharacteristics);
|
|
expect(newService.layout).toEqual({x: 0, y: 2, w: 1, h: 1});
|
|
});
|
|
});
|
|
|
|
describe('handleLoadedState', () => {
|
|
it('handles loaded state', async () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
spyOn(component.instance(), 'serviceFromSpec').and.returnValue(
|
|
new ServicesLib.DummyService()
|
|
);
|
|
|
|
const loadedState = {dashboards: DashboardsJSONFactory()};
|
|
const dashboardsHash = await HashLib.sha256(
|
|
JSON.stringify(loadedState)
|
|
);
|
|
|
|
// When
|
|
const result = await component.instance().handleLoadedState(
|
|
loadedState
|
|
);
|
|
|
|
// Then
|
|
expect(component.instance().dashboardsHash).toEqual(dashboardsHash);
|
|
expect(result.length).toEqual(1);
|
|
expect(result[0].id).toEqual(loadedState.dashboards[0].id);
|
|
expect(result[0].name).toEqual(loadedState.dashboards[0].name);
|
|
expect(result[0].services.length).toEqual(
|
|
loadedState.dashboards[0].services.length
|
|
);
|
|
expect(component.instance().serviceFromSpec.calls.count()).toEqual(2);
|
|
expect(component.instance().serviceFromSpec).toHaveBeenCalledWith(
|
|
loadedState.dashboards[0].services[0]
|
|
);
|
|
expect(component.instance().serviceFromSpec).toHaveBeenCalledWith(
|
|
loadedState.dashboards[0].services[1]
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('loadDashboards', () => {
|
|
let fakeDate = null;
|
|
let loadedDashboards = null;
|
|
let loadedState = null;
|
|
|
|
beforeEach(() => {
|
|
loadedDashboards = DashboardsFactory();
|
|
loadedState = {dashboards: DashboardsJSONFactory(loadedDashboards)};
|
|
loadDashboards = jasmine.createSpy().and.resolveTo(loadedState);
|
|
|
|
fakeDate = new Date(1987, 9, 3, 8, 0, 0);
|
|
jasmine.clock().install();
|
|
jasmine.clock().mockDate(fakeDate);
|
|
|
|
spyOn(console, 'error');
|
|
});
|
|
|
|
afterEach(() => {
|
|
jasmine.clock().uninstall();
|
|
});
|
|
|
|
it('loads the dashboards', async () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
spyOn(component.instance(), 'handleLoadedState').and.resolveTo(
|
|
loadedDashboards
|
|
);
|
|
|
|
// When
|
|
await component.instance().loadDashboards();
|
|
|
|
// Then
|
|
expect(component.state('dashboards')).toEqual(loadedDashboards);
|
|
expect(component.state('currentDashboardId')).toEqual(
|
|
loadedDashboards[0].id
|
|
);
|
|
expect(component.state('lastSaveTimestamp')).toEqual(fakeDate);
|
|
expect(component.instance().handleLoadedState).toHaveBeenCalledWith(
|
|
loadedState
|
|
);
|
|
});
|
|
|
|
it('gracefully handles load error', async () => {
|
|
// Given
|
|
loadDashboards = jasmine.createSpy().and.rejectWith(new Error('FIAL'));
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
spyOn(component.instance(), 'handleLoadedState').and.resolveTo([]);
|
|
|
|
// When
|
|
await component.instance().loadDashboards();
|
|
|
|
// Then
|
|
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.instance().handleLoadedState).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('setCurrentDashboardId', () => {
|
|
it('sets the current dashboard id', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
|
|
// When
|
|
component.instance().setCurrentDashboardId('spam');
|
|
|
|
// Then
|
|
expect(component.state('currentDashboardId')).toEqual('spam');
|
|
});
|
|
});
|
|
|
|
describe('addDashboard', () => {
|
|
it('adds a dashboard', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
spyOn(component.instance(), 'saveDashboards');
|
|
|
|
// When
|
|
component.instance().addDashboard('New');
|
|
|
|
// Then
|
|
const newDashboards = component.state('dashboards');
|
|
expect(newDashboards.length).toEqual(1);
|
|
expect(newDashboards[0].name).toEqual('New');
|
|
expect(component.instance().saveDashboards).toHaveBeenCalledWith(
|
|
newDashboards
|
|
);
|
|
});
|
|
|
|
it('auto selects the first added dashboard', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
component.setState({dashboards: []});
|
|
|
|
// When
|
|
component.instance().addDashboard('New');
|
|
|
|
// Then
|
|
expect(component.state('currentDashboardId')).toEqual(
|
|
component.state('dashboards')[0].id
|
|
);
|
|
});
|
|
|
|
it('does not auto select the other added dashboard', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
|
|
const dashboards = DashboardsFactory();
|
|
component.setState({
|
|
dashboards: dashboards,
|
|
currentDashboardId: 'testing',
|
|
});
|
|
|
|
// When
|
|
component.instance().addDashboard('New');
|
|
|
|
// Then
|
|
expect(component.state('currentDashboardId')).toEqual('testing');
|
|
});
|
|
});
|
|
|
|
describe('componentDidMount', () => {
|
|
let fakeHomeHubWebSocket = null;
|
|
let mockWebSocketUnsubscriber = null;
|
|
|
|
beforeEach(() => {
|
|
fakeHomeHubWebSocket = jasmine.createSpyObj('HomeHubWebSocket', [
|
|
'start', 'stop', 'addEventListener', 'removeEventListener',
|
|
'subscribe',
|
|
]);
|
|
mockWebSocketUnsubscriber = jasmine.createSpy();
|
|
fakeHomeHubWebSocket.subscribe.and.returnValue(
|
|
mockWebSocketUnsubscriber
|
|
);
|
|
spyOn(WebSocketLib, 'HomeHubWebSocket').and.returnValue(
|
|
fakeHomeHubWebSocket
|
|
);
|
|
});
|
|
|
|
it('sets the isLoading state to true', () => {
|
|
// Given
|
|
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.state('isLoading')).toBe(true);
|
|
});
|
|
|
|
it('configures and starts the websocket connection', () => {
|
|
// Given
|
|
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).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;
|
|
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(fakeHomeHubWebSocket.start).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('loads the dashboards', () => {
|
|
// Given
|
|
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().loadDashboards).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('componentWillUnmount', () => {
|
|
let fakeHomeHubWebSocket = null;
|
|
let mockWebSocketUnsubscriber = null;
|
|
|
|
beforeEach(() => {
|
|
fakeHomeHubWebSocket = jasmine.createSpyObj('HomeHubWebSocket', [
|
|
'start', 'stop', 'addEventListener', 'removeEventListener',
|
|
'subscribe',
|
|
]);
|
|
mockWebSocketUnsubscriber = jasmine.createSpy();
|
|
});
|
|
|
|
it('unsubscribes from the websocket', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
component.instance().webSocketUnsubscriber = mockWebSocketUnsubscriber;
|
|
|
|
// When
|
|
component.instance().componentWillUnmount();
|
|
|
|
// Then
|
|
expect(component.instance().webSocketUnsubscriber).toBe(null);
|
|
expect(mockWebSocketUnsubscriber).toHaveBeenCalled();
|
|
});
|
|
|
|
it('deconfigures and stops the websocket connection', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
component.instance().webSocket = fakeHomeHubWebSocket;
|
|
|
|
// When
|
|
component.instance().componentWillUnmount();
|
|
|
|
// Then
|
|
expect(fakeHomeHubWebSocket.removeEventListener).toHaveBeenCalledWith(
|
|
'start', component.instance().onWebSocketStart
|
|
);
|
|
expect(fakeHomeHubWebSocket.removeEventListener).toHaveBeenCalledWith(
|
|
'stop', component.instance().onWebSocketStop
|
|
);
|
|
expect(fakeHomeHubWebSocket.stop).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('render', () => {
|
|
it('configures and renders the DashboardsContext provider', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
|
|
// When
|
|
const provider = component.find(DashboardsContext.Provider);
|
|
|
|
// Then
|
|
expect(provider.exists());
|
|
expect(provider.prop('value')).toEqual({
|
|
currentDashboardId: component.state('currentDashboardId'),
|
|
dashboards: component.state('dashboards'),
|
|
nukeService: component.instance().nukeService,
|
|
saveServiceCharacteristics: component.instance().saveServiceCharacteristics,
|
|
saveServiceLayout: component.instance().saveServiceLayout,
|
|
addService: component.instance().addService,
|
|
isLoading: component.state('isLoading'),
|
|
lastSaveTimestamp: component.state('lastSaveTimestamp'),
|
|
lastSaveError: component.state('lastSaveError'),
|
|
isSaving: component.state('isSaving'),
|
|
isWebSocketConnected: component.state('isWebSocketConnected'),
|
|
setCurrentDashboardId: component.instance().setCurrentDashboardId,
|
|
dashboardsHash: component.instance().dashboardsHash,
|
|
addDashboard: component.instance().addDashboard,
|
|
});
|
|
});
|
|
|
|
it('renders the Loader if it is loading dashboards', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
component.setState({isLoading: true});
|
|
|
|
// Then
|
|
expect(component.find(Loader).exists()).toBe(true);
|
|
expect(component.find(Children).exists()).toBe(false);
|
|
});
|
|
|
|
it('renders the Children if it is not loading', () => {
|
|
// Given
|
|
const component = shallow(
|
|
<DashboardsProvider
|
|
loader={<Loader />}
|
|
loadDashboards={loadDashboards}
|
|
saveDashboards={saveDashboards}
|
|
settings={settings}
|
|
>
|
|
<Children />
|
|
</DashboardsProvider>
|
|
);
|
|
component.setState({isLoading: false});
|
|
|
|
// Then
|
|
expect(component.find(Loader).exists()).toBe(false);
|
|
expect(component.find(Children).exists()).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
});
|