You've already forked homehub
Release 1.3.0
This commit is contained in:
13
packages/homehub_app/tests/__entry__.js
Normal file
13
packages/homehub_app/tests/__entry__.js
Normal file
@@ -0,0 +1,13 @@
|
||||
require('regenerator-runtime');
|
||||
|
||||
const replaceAll = require('string.prototype.replaceall');
|
||||
replaceAll.shim();
|
||||
|
||||
window.crypto = {
|
||||
getRandomValues: require('polyfill-crypto.getrandomvalues'),
|
||||
};
|
||||
|
||||
require('tests/__setup__/enzyme.setup.js');
|
||||
|
||||
let testsContext = require.context('.', true, /\.spec\.js$/);
|
||||
testsContext.keys().forEach(testsContext);
|
||||
49
packages/homehub_app/tests/__fixtures__/dashboards.js
Normal file
49
packages/homehub_app/tests/__fixtures__/dashboards.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import {FakeWidget, FakeService} from 'tests/__fixtures__/services';
|
||||
|
||||
export const DashboardsFactory = () => {
|
||||
return [
|
||||
{
|
||||
id: 'testing',
|
||||
name: 'Testing',
|
||||
services: [
|
||||
new FakeService({
|
||||
instance: 'fake_instance',
|
||||
widgetComponent: FakeWidget,
|
||||
characteristics: {
|
||||
appearance: {
|
||||
coolor: 'red',
|
||||
},
|
||||
spam: true,
|
||||
},
|
||||
layout: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 1,
|
||||
h: 1,
|
||||
},
|
||||
}),
|
||||
new FakeService({
|
||||
instance: 'other_fake_instance',
|
||||
widgetComponent: null,
|
||||
characteristics: {
|
||||
appearance: {
|
||||
coolor: 'red',
|
||||
},
|
||||
spam: true,
|
||||
},
|
||||
layout: {
|
||||
x: 0,
|
||||
y: 1,
|
||||
w: 1,
|
||||
h: 1,
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'testing2',
|
||||
name: 'Testing2',
|
||||
services: [],
|
||||
},
|
||||
];
|
||||
};
|
||||
23
packages/homehub_app/tests/__fixtures__/dashboardsContext.js
Normal file
23
packages/homehub_app/tests/__fixtures__/dashboardsContext.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import {DEFAULT_DASHBOARDS_CONTEXT} from '@bthlabs/homehub-core';
|
||||
|
||||
import {DashboardsFactory} from 'tests/__fixtures__/dashboards';
|
||||
|
||||
export const DashboardsContextFactory = () => {
|
||||
return {
|
||||
...DEFAULT_DASHBOARDS_CONTEXT,
|
||||
currentDashboardId: 'testing',
|
||||
dashboards: DashboardsFactory(),
|
||||
nukeService: jasmine.createSpy(),
|
||||
saveServiceCharacteristics: jasmine.createSpy(),
|
||||
saveServiceLayout: jasmine.createSpy(),
|
||||
addService: jasmine.createSpy(),
|
||||
isLoading: false,
|
||||
lastSaveTimestamp: null,
|
||||
lastSaveError: null,
|
||||
isSaving: false,
|
||||
isWebSocketConnected: false,
|
||||
setCurrentDashboardId: jasmine.createSpy(),
|
||||
dashboardsHash: null,
|
||||
addDashboard: jasmine.createSpy(),
|
||||
};
|
||||
};
|
||||
39
packages/homehub_app/tests/__fixtures__/services.js
Normal file
39
packages/homehub_app/tests/__fixtures__/services.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import {BaseService} from '@bthlabs/homehub-core';
|
||||
import React from 'react';
|
||||
|
||||
export const FakeWidget = (props) => { // eslint-disable-line no-unused-vars
|
||||
return <span>FakeWidget</span>;
|
||||
};
|
||||
|
||||
export const FakeWidgetSettingsView = (props) => { // eslint-disable-line no-unused-vars
|
||||
return <span>FakeWidgetSettingsView</span>;
|
||||
};
|
||||
|
||||
FakeWidget.defaultLayout = {w: 1, h: 1};
|
||||
FakeWidget.layoutConstraints = {minW: 1, minH: 1};
|
||||
FakeWidget.settingsView = FakeWidgetSettingsView;
|
||||
|
||||
export class FakeService extends BaseService {
|
||||
static kind = 'FakeService';
|
||||
static widget = 'FakeWidget';
|
||||
async start () {
|
||||
}
|
||||
async stop () {
|
||||
}
|
||||
}
|
||||
|
||||
export const FakeWidgetWithoutSettings = (props) => { // eslint-disable-line no-unused-vars
|
||||
return <span>FakeWidgetWithoutSettings</span>;
|
||||
};
|
||||
|
||||
FakeWidgetWithoutSettings.defaultLayout = {w: 1, h: 1};
|
||||
FakeWidgetWithoutSettings.layoutConstraints = {minW: 1, minH: 1};
|
||||
|
||||
export class FakeServiceWithoutSettings extends BaseService {
|
||||
static kind = 'FakeServiceWithoutSettings';
|
||||
static widget = 'FakeWidgetWithoutSettings';
|
||||
async start () {
|
||||
}
|
||||
async stop () {
|
||||
}
|
||||
}
|
||||
29
packages/homehub_app/tests/__fixtures__/settings.js
Normal file
29
packages/homehub_app/tests/__fixtures__/settings.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import * as ServicesFixtures from 'tests/__fixtures__/services';
|
||||
|
||||
export const SettingsFactory = () => {
|
||||
return {
|
||||
DEBUG: false,
|
||||
HOMEHUB: {
|
||||
version: '1.0',
|
||||
build: 123,
|
||||
},
|
||||
INSTALLED_APPS: [],
|
||||
REDUX_MIDDLEWARES: [],
|
||||
LOCALES: {
|
||||
'en-us': 'English (United States)',
|
||||
'en-gb': 'English (United Kingdom)',
|
||||
},
|
||||
SERVICES: {
|
||||
FakeService: ServicesFixtures.FakeService,
|
||||
FakeServiceWithoutSettings: ServicesFixtures.FakeServiceWithoutSettings,
|
||||
},
|
||||
WIDGETS: {
|
||||
FakeWidget: ServicesFixtures.FakeWidget,
|
||||
FakeWidgetWithoutSettings: ServicesFixtures.FakeWidgetWithoutSettings,
|
||||
},
|
||||
WEBSOCKET: {
|
||||
url: 'ws://127.0.0.1:3010/backend/websocket',
|
||||
},
|
||||
OFFLINE_MODE: false,
|
||||
};
|
||||
};
|
||||
44
packages/homehub_app/tests/__fixtures__/weather.js
Normal file
44
packages/homehub_app/tests/__fixtures__/weather.js
Normal file
@@ -0,0 +1,44 @@
|
||||
export const WeatherFactory = () => {
|
||||
return {
|
||||
'coord': {
|
||||
'lon': 145.77,
|
||||
'lat': -16.92,
|
||||
},
|
||||
'weather': [
|
||||
{
|
||||
'id': 802,
|
||||
'main': 'Clouds',
|
||||
'description': 'scattered clouds',
|
||||
'icon': '03n',
|
||||
},
|
||||
],
|
||||
'base': 'stations',
|
||||
'main': {
|
||||
'temp': 300.15,
|
||||
'pressure': 1007,
|
||||
'humidity': 74,
|
||||
'temp_min': 300.15,
|
||||
'temp_max': 300.15,
|
||||
},
|
||||
'visibility': 10000,
|
||||
'wind': {
|
||||
'speed': 3.6,
|
||||
'deg': 160,
|
||||
},
|
||||
'clouds': {
|
||||
'all': 40,
|
||||
},
|
||||
'dt': 1485790200,
|
||||
'sys': {
|
||||
'type': 1,
|
||||
'id': 8166,
|
||||
'message': 0.2064,
|
||||
'country': 'AU',
|
||||
'sunrise': 1485720272,
|
||||
'sunset': 1485766550,
|
||||
},
|
||||
'id': 2172797,
|
||||
'name': 'Cairns',
|
||||
'cod': 200,
|
||||
};
|
||||
};
|
||||
7
packages/homehub_app/tests/__setup__/enzyme.setup.js
Normal file
7
packages/homehub_app/tests/__setup__/enzyme.setup.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import Enzyme from 'enzyme';
|
||||
import Adapter from 'enzyme-adapter-react-16';
|
||||
|
||||
Enzyme.configure({
|
||||
adapter: new Adapter(),
|
||||
disableLifecycleMethods: true,
|
||||
});
|
||||
86
packages/homehub_app/tests/lib/clock.spec.js
Normal file
86
packages/homehub_app/tests/lib/clock.spec.js
Normal file
@@ -0,0 +1,86 @@
|
||||
import * as luxon from 'luxon';
|
||||
|
||||
import * as ClockLib from 'src/lib/clock';
|
||||
|
||||
describe('src/lib/clock', () => {
|
||||
describe('Clock', () => {
|
||||
let fakeDateTime = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeDateTime = luxon.DateTime.local(1987, 10, 3, 8, 0, 1, 200);
|
||||
|
||||
spyOn(window, 'setTimeout').and.returnValue(123);
|
||||
spyOn(window, 'clearTimeout');
|
||||
spyOn(luxon.DateTime, 'local').and.returnValue(fakeDateTime);
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('initializes an instance', () => {
|
||||
const clock = new ClockLib.Clock();
|
||||
|
||||
expect(clock.now).toEqual(fakeDateTime);
|
||||
expect(clock.timeout).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('clearTimeout', () => {
|
||||
it('is a noop is timeout is null', () => {
|
||||
const clock = new ClockLib.Clock();
|
||||
|
||||
clock.clearTimeout();
|
||||
|
||||
expect(window.clearTimeout).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('clears the timeout', () => {
|
||||
const clock = new ClockLib.Clock();
|
||||
clock.timeout = 123;
|
||||
|
||||
clock.clearTimeout();
|
||||
|
||||
expect(window.clearTimeout).toHaveBeenCalledWith(123);
|
||||
expect(clock.timeout).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onTimeout', () => {
|
||||
it('handles the clock timeout', () => {
|
||||
const newFakeDateTime = fakeDateTime.plus({minutes: 1});
|
||||
luxon.DateTime.local.and.returnValue(newFakeDateTime);
|
||||
|
||||
const clock = new ClockLib.Clock();
|
||||
spyOn(clock, 'clearTimeout');
|
||||
spyOn(clock, 'fireEvent');
|
||||
spyOn(clock, 'start');
|
||||
|
||||
clock.onTimeout();
|
||||
|
||||
expect(clock.now).toEqual(newFakeDateTime);
|
||||
expect(clock.clearTimeout).toHaveBeenCalled();
|
||||
expect(clock.fireEvent).toHaveBeenCalledWith('tick');
|
||||
expect(clock.start).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('start', () => {
|
||||
it('starts the clock', () => {
|
||||
const clock = new ClockLib.Clock();
|
||||
|
||||
clock.start();
|
||||
|
||||
expect(window.setTimeout).toHaveBeenCalledWith(clock.onTimeout, 58810);
|
||||
});
|
||||
});
|
||||
|
||||
describe('stop', () => {
|
||||
it('stops the clock', () => {
|
||||
const clock = new ClockLib.Clock();
|
||||
spyOn(clock, 'clearTimeout');
|
||||
|
||||
clock.stop();
|
||||
|
||||
expect(clock.clearTimeout).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {Button, Modal} from 'src/components';
|
||||
import * as AboutModal from 'src/main/components/AboutModal';
|
||||
|
||||
describe('src/main/components/AboutModal', () => {
|
||||
describe('AboutModal', () => {
|
||||
it('configures and renders the about modal', () => {
|
||||
// Given
|
||||
const mockOnClose = jasmine.createSpy();
|
||||
const component = shallow(
|
||||
<AboutModal.AboutModal
|
||||
build={123}
|
||||
show={true}
|
||||
version='1.0'
|
||||
onClose={mockOnClose}
|
||||
/>
|
||||
);
|
||||
|
||||
// Then
|
||||
const modal = component.find(Modal);
|
||||
expect(modal.exists()).toBe(true);
|
||||
expect(modal.prop('show')).toBe(true);
|
||||
expect(modal.prop('onClose')).toEqual(mockOnClose);
|
||||
|
||||
const versionLine = component.find('p.text-muted');
|
||||
expect(versionLine.text()).toMatch('v1.0-123');
|
||||
|
||||
const button = component.find(Button).at(0);
|
||||
expect(button.exists()).toBe(true);
|
||||
expect(button.prop('onClick')).toEqual(mockOnClose);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,123 @@
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {Button, Form, Modal} from 'src/components';
|
||||
import * as AddDashboardModal from 'src/main/components/AddDashboardModal';
|
||||
|
||||
describe('src/main/components/AddDashboardModal', () => {
|
||||
describe('AddDashboardModal', () => {
|
||||
let mockOnClose = null;
|
||||
let mockOnSave = null;
|
||||
|
||||
beforeEach(() => {
|
||||
mockOnClose = jasmine.createSpy();
|
||||
mockOnSave = jasmine.createSpy();
|
||||
});
|
||||
|
||||
it('closes the modal when it hides', async () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<AddDashboardModal.AddDashboardModal
|
||||
show={true}
|
||||
onClose={mockOnClose}
|
||||
onSave={mockOnSave}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Form.Control).at(0).simulate(
|
||||
'change', {target: {value: 'spam'}}
|
||||
);
|
||||
|
||||
const modal = component.find(Modal).at(0);
|
||||
await modal.invoke('onHide')();
|
||||
|
||||
// Then
|
||||
expect(component.find(Form.Control).at(0).prop('value')).toEqual('');
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('configures and renders the name input', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<AddDashboardModal.AddDashboardModal
|
||||
show={true}
|
||||
onClose={mockOnClose}
|
||||
onSave={mockOnSave}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const nameInput = component.find(Form.Control).at(0);
|
||||
|
||||
// Then
|
||||
expect(nameInput.exists()).toBe(true);
|
||||
expect(nameInput.prop('value')).toEqual('');
|
||||
});
|
||||
|
||||
it('updates the state when name input changes', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<AddDashboardModal.AddDashboardModal
|
||||
show={true}
|
||||
onClose={mockOnClose}
|
||||
onSave={mockOnSave}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Form.Control).at(0).simulate(
|
||||
'change', {target: {value: 'spam'}}
|
||||
);
|
||||
|
||||
// Then
|
||||
const nameInput = component.find(Form.Control).at(0);
|
||||
expect(nameInput.prop('value')).toEqual('spam');
|
||||
});
|
||||
|
||||
it('closes the modal when cancel button is clicked', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<AddDashboardModal.AddDashboardModal
|
||||
show={true}
|
||||
onClose={mockOnClose}
|
||||
onSave={mockOnSave}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Form.Control).at(0).simulate(
|
||||
'change', {target: {value: 'spam'}}
|
||||
);
|
||||
component.find(Button).at(0).simulate('click');
|
||||
|
||||
// Then
|
||||
const nameInput = component.find(Form.Control).at(0);
|
||||
expect(nameInput.prop('value')).toEqual('');
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('saves and closes the modal when the save button is clicked', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<AddDashboardModal.AddDashboardModal
|
||||
show={true}
|
||||
onClose={mockOnClose}
|
||||
onSave={mockOnSave}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Form.Control).at(0).simulate(
|
||||
'change', {target: {value: 'spam'}}
|
||||
);
|
||||
component.find(Button).at(1).simulate('click');
|
||||
|
||||
// Then
|
||||
const nameInput = component.find(Form.Control).at(0);
|
||||
expect(nameInput.prop('value')).toEqual('');
|
||||
expect(mockOnSave).toHaveBeenCalledWith('spam');
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
18
packages/homehub_app/tests/main/components/AppLoader.spec.js
Normal file
18
packages/homehub_app/tests/main/components/AppLoader.spec.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import * as AppLoader from 'src/main/components/AppLoader';
|
||||
|
||||
describe('src/main/components/AppLoader', () => {
|
||||
describe('AppLoader', () => {
|
||||
it('allows passing an arbitrary class name', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<AppLoader.AppLoader className="test" />
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.hasClass('test')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
55
packages/homehub_app/tests/main/components/Dashboard.spec.js
Normal file
55
packages/homehub_app/tests/main/components/Dashboard.spec.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {DynamicGridLayout} from 'src/components';
|
||||
import * as Dashboard from 'src/main/components/Dashboard';
|
||||
|
||||
describe('src/main/components/Dashboard', () => {
|
||||
describe('Dashboard', () => {
|
||||
let layout = null;
|
||||
let mockOnGridLayoutChange = null;
|
||||
|
||||
beforeEach(() => {
|
||||
layout = [{i: 'test', x: 0, y: 0, w: 1, h: 1}];
|
||||
mockOnGridLayoutChange = jasmine.createSpy();
|
||||
});
|
||||
|
||||
it('configures and renders the DynamicGridLayout', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<Dashboard.Dashboard
|
||||
layout={layout}
|
||||
onGridLayoutChange={mockOnGridLayoutChange}
|
||||
>
|
||||
<span key="test">It works!</span>
|
||||
</Dashboard.Dashboard>
|
||||
);
|
||||
|
||||
// Then
|
||||
const dynamicGridLayout = component.find(DynamicGridLayout).at(0);
|
||||
expect(dynamicGridLayout.exists()).toBe(true);
|
||||
expect(dynamicGridLayout.prop('cols')).toEqual(12);
|
||||
expect(dynamicGridLayout.prop('layout')).toEqual(layout);
|
||||
expect(dynamicGridLayout.prop('rowHeight')).toEqual(30);
|
||||
expect(dynamicGridLayout.prop('onLayoutChange')).toEqual(
|
||||
mockOnGridLayoutChange
|
||||
);
|
||||
});
|
||||
|
||||
it('renders the children', () => {
|
||||
// Given
|
||||
const children = <span key="test">It works!</span>;
|
||||
const component = shallow(
|
||||
<Dashboard.Dashboard
|
||||
layout={layout}
|
||||
onGridLayoutChange={mockOnGridLayoutChange}
|
||||
>
|
||||
{children}
|
||||
</Dashboard.Dashboard>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.contains(children)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
531
packages/homehub_app/tests/main/components/DashboardItem.spec.js
Normal file
531
packages/homehub_app/tests/main/components/DashboardItem.spec.js
Normal file
@@ -0,0 +1,531 @@
|
||||
import Popup from '@bthlabs/react-custom-popup';
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {AppearancePopup, WidgetSettingsModal} from 'src/main/components';
|
||||
import * as DashboardItem from 'src/main/components/DashboardItem';
|
||||
|
||||
describe('src/main/components/DashboardItem', () => {
|
||||
describe('DashboardItem', () => {
|
||||
let fakeServiceState = null;
|
||||
let mockOnAppearancePopupColorChange = null;
|
||||
let mockOnServiceRestartButtonClick = null;
|
||||
let mockOnSettingsButtonClick = null;
|
||||
let mockOnSettingsModalClose = null;
|
||||
let mockOnSettingsModalNukeButtonClick = null;
|
||||
let mockOnSettingsModalSaveButtonClick = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeServiceState = jasmine.createSpyObj('ServiceState', ['isLoading']);
|
||||
fakeServiceState.isLoading.and.returnValue(false);
|
||||
mockOnAppearancePopupColorChange = jasmine.createSpy();
|
||||
mockOnServiceRestartButtonClick = jasmine.createSpy();
|
||||
mockOnSettingsButtonClick = jasmine.createSpy();
|
||||
mockOnSettingsModalClose = jasmine.createSpy();
|
||||
mockOnSettingsModalNukeButtonClick = jasmine.createSpy();
|
||||
mockOnSettingsModalSaveButtonClick = jasmine.createSpy();
|
||||
});
|
||||
|
||||
it('renders the children', () => {
|
||||
// Given
|
||||
const children = <span>It works!</span>;
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={false}
|
||||
serviceState={fakeServiceState}
|
||||
settingsView={null}
|
||||
settingsViewProps={{}}
|
||||
showSettingsModal={false}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
{children}
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.contains(children)).toBe(true);
|
||||
});
|
||||
|
||||
it('renders the draggable handle', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={false}
|
||||
serviceState={fakeServiceState}
|
||||
settingsView={null}
|
||||
settingsViewProps={{}}
|
||||
showSettingsModal={false}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
<span>It works!</span>
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.find('.draggable-handle').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('does not render the restart toolbar item if serviceState is null', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={false}
|
||||
serviceState={null}
|
||||
settingsView={null}
|
||||
settingsViewProps={{}}
|
||||
showSettingsModal={false}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
<span>It works!</span>
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.find('WidgetToolbarItem[_hint="Restart"]').exists()).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('does not render the restart toolbar item if service state is loading', () => {
|
||||
// Given
|
||||
fakeServiceState.isLoading.and.returnValue(true);
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={false}
|
||||
serviceState={fakeServiceState}
|
||||
settingsView={null}
|
||||
settingsViewProps={{}}
|
||||
showSettingsModal={false}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
<span>It works!</span>
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.find('WidgetToolbarItem[_hint="Restart"]').exists()).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('configures and renders the restart toolbar item', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={false}
|
||||
serviceState={fakeServiceState}
|
||||
settingsView={null}
|
||||
settingsViewProps={{}}
|
||||
showSettingsModal={false}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
<span>It works!</span>
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// When
|
||||
const restartItem = component.find('WidgetToolbarItem[_hint="Restart"]');
|
||||
|
||||
// Then
|
||||
expect(restartItem.exists()).toBe(true);
|
||||
expect(restartItem.prop('onClick')).toEqual(
|
||||
mockOnServiceRestartButtonClick
|
||||
);
|
||||
});
|
||||
|
||||
it('configures and renders the appearance toolbar item', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={false}
|
||||
serviceState={fakeServiceState}
|
||||
settingsView={null}
|
||||
settingsViewProps={{}}
|
||||
showSettingsModal={false}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
<span>It works!</span>
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// When
|
||||
const appearanceItem = component.find(
|
||||
'WidgetToolbarItem[_hint="Appearance"]'
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(appearanceItem.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('displays the appearance popup when appearance toolbar item is clicked', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={false}
|
||||
serviceState={fakeServiceState}
|
||||
settingsView={null}
|
||||
settingsViewProps={{}}
|
||||
showSettingsModal={false}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
<span>It works!</span>
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find('WidgetToolbarItem[_hint="Appearance"]').simulate(
|
||||
'click'
|
||||
);
|
||||
|
||||
// Then
|
||||
const popup = component.find(Popup).at(0);
|
||||
expect(popup.prop('visible')).toBe(true);
|
||||
});
|
||||
|
||||
it('does not render the settings toolbar item if hasSettingsView is false', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={false}
|
||||
serviceState={fakeServiceState}
|
||||
settingsView={null}
|
||||
settingsViewProps={{}}
|
||||
showSettingsModal={false}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
<span>It works!</span>
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.find('WidgetToolbarItem[_hint="Settings"]').exists()).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('configures and renders the settings toolbar item', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={true}
|
||||
serviceState={fakeServiceState}
|
||||
settingsView={<span>Settings</span>}
|
||||
settingsViewProps={{}}
|
||||
showSettingsModal={false}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
<span>It works!</span>
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// When
|
||||
const settingsItem = component.find(
|
||||
'WidgetToolbarItem[_hint="Settings"]'
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(settingsItem.exists()).toBe(true);
|
||||
expect(settingsItem.prop('onClick')).toEqual(mockOnSettingsButtonClick);
|
||||
});
|
||||
|
||||
it('does not render the delete toolbar item if hasSettingsView is true', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={true}
|
||||
serviceState={fakeServiceState}
|
||||
settingsView={<span>Settings</span>}
|
||||
settingsViewProps={{}}
|
||||
showSettingsModal={false}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
<span>It works!</span>
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.find('WidgetToolbarItem[_hint="Delete"]').exists()).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('configures and renders the delete toolbar item', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={false}
|
||||
serviceState={fakeServiceState}
|
||||
settingsView={null}
|
||||
settingsViewProps={{}}
|
||||
showSettingsModal={false}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
<span>It works!</span>
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// When
|
||||
const deleteItem = component.find(
|
||||
'WidgetToolbarItem[_hint="Delete"]'
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(deleteItem.exists()).toBe(true);
|
||||
expect(deleteItem.prop('onClick')).toEqual(
|
||||
mockOnSettingsModalNukeButtonClick
|
||||
);
|
||||
});
|
||||
|
||||
it('does not render the WidgetSettingsModal if hasSettingsView is false', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={false}
|
||||
serviceState={fakeServiceState}
|
||||
settingsView={null}
|
||||
settingsViewProps={{}}
|
||||
showSettingsModal={false}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
<span>It works!</span>
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// When
|
||||
const widgetSettingsModal = component.find(WidgetSettingsModal).at(0);
|
||||
|
||||
// Then
|
||||
expect(widgetSettingsModal.exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('configures and renders the WidgetSettingsModal', () => {
|
||||
// Given
|
||||
const settingsView = <span>Settings</span>;
|
||||
const settingsViewProps = {spam: true};
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={true}
|
||||
serviceState={fakeServiceState}
|
||||
settingsView={settingsView}
|
||||
settingsViewProps={settingsViewProps}
|
||||
showSettingsModal={true}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
<span>It works!</span>
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// When
|
||||
const widgetSettingsModal = component.find(WidgetSettingsModal).at(0);
|
||||
|
||||
// Then
|
||||
expect(widgetSettingsModal.exists()).toBe(true);
|
||||
expect(widgetSettingsModal.prop('buttons').length).toEqual(1);
|
||||
expect(widgetSettingsModal.prop('settingsView')).toEqual(settingsView);
|
||||
expect(widgetSettingsModal.prop('settingsViewProps')).toEqual(
|
||||
settingsViewProps
|
||||
);
|
||||
expect(widgetSettingsModal.prop('show')).toBe(true);
|
||||
expect(widgetSettingsModal.prop('onClose')).toEqual(
|
||||
mockOnSettingsModalClose
|
||||
);
|
||||
expect(widgetSettingsModal.prop('onCancelButtonClick')).toEqual(
|
||||
mockOnSettingsModalClose
|
||||
);
|
||||
expect(widgetSettingsModal.prop('onSaveButtonClick')).toEqual(
|
||||
mockOnSettingsModalSaveButtonClick
|
||||
);
|
||||
|
||||
const nukeButtonComponent = shallow(
|
||||
widgetSettingsModal.prop('buttons')[0]
|
||||
);
|
||||
|
||||
expect(nukeButtonComponent.isEmptyRender()).toBe(false);
|
||||
expect(nukeButtonComponent.prop('onClick')).toEqual(
|
||||
mockOnSettingsModalNukeButtonClick
|
||||
);
|
||||
});
|
||||
|
||||
it('configures and renders the appearance popup wrapper', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={true}
|
||||
serviceState={fakeServiceState}
|
||||
settingsView={<span>Settings</span>}
|
||||
settingsViewProps={{}}
|
||||
showSettingsModal={false}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
<span>It works!</span>
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// When
|
||||
const popup = component.find(Popup).at(0);
|
||||
|
||||
// Then
|
||||
expect(popup.exists()).toBe(true);
|
||||
expect(popup.prop('visible')).toBe(false);
|
||||
});
|
||||
|
||||
it('hides the appearance popup wrapper when the overlay is clicked', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={true}
|
||||
serviceState={fakeServiceState}
|
||||
settingsView={<span>Settings</span>}
|
||||
settingsViewProps={{}}
|
||||
showSettingsModal={false}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
<span>It works!</span>
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find('WidgetToolbarItem[_hint="Appearance"]').simulate(
|
||||
'click'
|
||||
);
|
||||
component.find(Popup).at(0).invoke('onOverlayClick')();
|
||||
|
||||
// Then
|
||||
const popup = component.find(Popup).at(0);
|
||||
expect(popup.prop('visible')).toBe(false);
|
||||
});
|
||||
|
||||
it('configures and renders the appearance popup view', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={true}
|
||||
serviceState={fakeServiceState}
|
||||
settingsView={<span>Settings</span>}
|
||||
settingsViewProps={{}}
|
||||
showSettingsModal={false}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
<span>It works!</span>
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// When
|
||||
const popupView = component.find(AppearancePopup).at(0);
|
||||
|
||||
// Then
|
||||
expect(popupView.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('handles the AppearancePopup color change', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<DashboardItem.DashboardItem
|
||||
hasSettingsView={true}
|
||||
serviceState={fakeServiceState}
|
||||
settingsView={<span>Settings</span>}
|
||||
settingsViewProps={{}}
|
||||
showSettingsModal={false}
|
||||
onAppearancePopupColorChange={mockOnAppearancePopupColorChange}
|
||||
onServiceRestartButtonClick={mockOnServiceRestartButtonClick}
|
||||
onSettingsButtonClick={mockOnSettingsButtonClick}
|
||||
onSettingsModalClose={mockOnSettingsModalClose}
|
||||
onSettingsModalNukeButtonClick={mockOnSettingsModalNukeButtonClick}
|
||||
onSettingsModalSaveButtonClick={mockOnSettingsModalSaveButtonClick}
|
||||
>
|
||||
<span>It works!</span>
|
||||
</DashboardItem.DashboardItem>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find('WidgetToolbarItem[_hint="Appearance"]').simulate(
|
||||
'click'
|
||||
);
|
||||
component.find(AppearancePopup).at(0).invoke('onColorChange')('red');
|
||||
|
||||
// Then
|
||||
expect(component.find(Popup).at(0).prop('visible')).toBe(false);
|
||||
expect(mockOnAppearancePopupColorChange).toHaveBeenCalledWith('red');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import * as DummyWidget from 'src/main/components/DummyWidget';
|
||||
import {UptimeService} from 'src/services/uptime';
|
||||
|
||||
describe('src/main/components/DummyWidget', () => {
|
||||
describe('DummyWidget', () => {
|
||||
it('renders the service information', () => {
|
||||
// Given
|
||||
const service = new UptimeService({instance: 'testing'});
|
||||
const component = shallow(
|
||||
<DummyWidget.DummyWidget service={service} />
|
||||
);
|
||||
|
||||
// When
|
||||
const code = component.find('code').at(0);
|
||||
|
||||
// Then
|
||||
expect(code.text()).toEqual(`${service.widget} (${service.instance})`);
|
||||
});
|
||||
});
|
||||
});
|
||||
544
packages/homehub_app/tests/main/components/StartMenu.spec.js
Normal file
544
packages/homehub_app/tests/main/components/StartMenu.spec.js
Normal file
@@ -0,0 +1,544 @@
|
||||
import {
|
||||
kServiceOfflineWeather, kServiceTime, kServiceUptime, kServiceWeather,
|
||||
kWidgetUptime, kWidgetWeather,
|
||||
} from '@bthlabs/homehub-core';
|
||||
import {Icon, IconBasicGear} from '@bthlabs/homehub-icons';
|
||||
import Popup from '@bthlabs/react-custom-popup';
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {AboutModal} from 'src/main/components/AboutModal';
|
||||
import * as StartMenu from 'src/main/components/StartMenu';
|
||||
import {SERVICES} from 'src/services';
|
||||
import {WIDGETS} from 'src/widgets';
|
||||
|
||||
import {SettingsFactory} from 'tests/__fixtures__/settings';
|
||||
|
||||
describe('src/main/components/StartMenu', () => {
|
||||
describe('ControlledStartMenu', () => {
|
||||
let mockOnAddDashboard = null;
|
||||
let mockOnAddService = null;
|
||||
let fakeSettings = null;
|
||||
|
||||
beforeEach(() => {
|
||||
mockOnAddService = jasmine.createSpy();
|
||||
mockOnAddDashboard = jasmine.createSpy();
|
||||
fakeSettings = SettingsFactory();
|
||||
});
|
||||
|
||||
it('renders the start button', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={SERVICES}
|
||||
settings={fakeSettings}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const startButton = component.find('a[_hint="StartButton"]').at(0);
|
||||
|
||||
// Then
|
||||
expect(startButton.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('displays the start menu popup when the start button is clicked', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={SERVICES}
|
||||
settings={fakeSettings}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find('a[_hint="StartButton"]').at(0).simulate('click');
|
||||
|
||||
// Then
|
||||
const popup = component.find(Popup).at(0);
|
||||
expect(popup.prop('visible')).toBe(true);
|
||||
});
|
||||
|
||||
it('configures and renders the start menu popup', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={SERVICES}
|
||||
settings={fakeSettings}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const popup = component.find(Popup).at(0);
|
||||
|
||||
// Then
|
||||
expect(popup.exists()).toBe(true);
|
||||
expect(popup.prop('visible')).toBe(false);
|
||||
});
|
||||
|
||||
it('hides the start menu popup when the overlay is clicked', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={SERVICES}
|
||||
settings={fakeSettings}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find('a[_hint="StartButton"]').at(0).simulate('click');
|
||||
component.find(Popup).at(0).invoke('onOverlayClick')();
|
||||
|
||||
// Then
|
||||
const popup = component.find(Popup).at(0);
|
||||
expect(popup.prop('visible')).toBe(false);
|
||||
});
|
||||
|
||||
it('renders the start menu view', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={SERVICES}
|
||||
settings={fakeSettings}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const startMenu = component.find('div[_hint="StartMenu"]').at(0);
|
||||
|
||||
// Then
|
||||
expect(startMenu.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('hides the start menu popup when the start menu is clicked', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={SERVICES}
|
||||
settings={fakeSettings}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find('a[_hint="StartButton"]').at(0).simulate('click');
|
||||
component.find('div[_hint="StartMenu"]').at(0).simulate('click');
|
||||
|
||||
// Then
|
||||
const popup = component.find(Popup).at(0);
|
||||
expect(popup.prop('visible')).toBe(false);
|
||||
});
|
||||
|
||||
it('renders the about toolbar item', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={SERVICES}
|
||||
settings={fakeSettings}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const aboutItem = component.find('div[_hint="About"]').at(0);
|
||||
|
||||
// Then
|
||||
expect(aboutItem.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('displays the about modal when the about toolbar item is clicked', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={SERVICES}
|
||||
settings={fakeSettings}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find('div[_hint="About"]').at(0).simulate('click');
|
||||
|
||||
// Then
|
||||
const aboutModal = component.find(AboutModal).at(0);
|
||||
expect(aboutModal.prop('show')).toBe(true);
|
||||
});
|
||||
|
||||
it('configures and renders the add dashboard toolbar item', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={SERVICES}
|
||||
settings={fakeSettings}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const addDashboardItem = component.find('div[_hint="AddDashboard"]').at(0);
|
||||
|
||||
// Then
|
||||
expect(addDashboardItem.exists()).toBe(true);
|
||||
expect(addDashboardItem.prop('onClick')).toEqual(mockOnAddDashboard);
|
||||
});
|
||||
|
||||
it('renders the reload toolbar item', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={SERVICES}
|
||||
settings={fakeSettings}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const addDashboardItem = component.find('div[_hint="Reload"]').at(0);
|
||||
|
||||
// Then
|
||||
expect(addDashboardItem.exists()).toBe(true);
|
||||
});
|
||||
|
||||
xit('reloads the page when the reload button is clicked', () => {
|
||||
// Given
|
||||
spyOn(window.location, 'reload');
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={SERVICES}
|
||||
settings={fakeSettings}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find('div[_hint="Reload"]').at(0).simulate('click');
|
||||
|
||||
// Then
|
||||
expect(window.location.reload).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('Widgets', () => {
|
||||
let fakeServices = null;
|
||||
let fakeWidgets = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeServices = {
|
||||
[kServiceOfflineWeather]: SERVICES.kServiceOfflineWeather,
|
||||
[kServiceTime]: SERVICES.kServiceTime,
|
||||
[kServiceUptime]: SERVICES.kServiceUptime,
|
||||
[kServiceWeather]: SERVICES.kServiceWeather,
|
||||
};
|
||||
fakeWidgets = {
|
||||
[kWidgetUptime]: WIDGETS.kWidgetUptime,
|
||||
[kWidgetWeather]: WIDGETS.kWidgetWeather,
|
||||
};
|
||||
});
|
||||
|
||||
it('renders the list of widgets available in online mode', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={fakeServices}
|
||||
settings={fakeSettings}
|
||||
widgets={fakeWidgets}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const widgets = component.find('div[_hint="Widgets"]').at(0);
|
||||
|
||||
// Then
|
||||
const serviceKeys = widgets.find('div[_hint="Widget"]').map((wrapper) => {
|
||||
return wrapper.prop('data-service');
|
||||
});
|
||||
expect(serviceKeys.length).toEqual(3);
|
||||
expect(serviceKeys).not.toContain(kServiceOfflineWeather);
|
||||
expect(serviceKeys).toContain(kServiceTime);
|
||||
expect(serviceKeys).toContain(kServiceUptime);
|
||||
expect(serviceKeys).toContain(kServiceWeather);
|
||||
});
|
||||
|
||||
it('renders the list of widgets available in offline mode', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={true}
|
||||
services={fakeServices}
|
||||
settings={fakeSettings}
|
||||
widgets={fakeWidgets}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const widgets = component.find('div[_hint="Widgets"]').at(0);
|
||||
|
||||
// Then
|
||||
const serviceKeys = widgets.find('div[_hint="Widget"]').map((wrapper) => {
|
||||
return wrapper.prop('data-service');
|
||||
});
|
||||
expect(serviceKeys.length).toEqual(2);
|
||||
expect(serviceKeys).toContain(kServiceOfflineWeather);
|
||||
expect(serviceKeys).toContain(kServiceTime);
|
||||
expect(serviceKeys).not.toContain(kServiceUptime);
|
||||
expect(serviceKeys).not.toContain(kServiceWeather);
|
||||
});
|
||||
|
||||
it('configures and renders a widget item for a known widget', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={fakeServices}
|
||||
settings={fakeSettings}
|
||||
widgets={fakeWidgets}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const widgetItem = component.find(
|
||||
`div[data-service="${kServiceWeather}"]`
|
||||
).at(
|
||||
0
|
||||
);
|
||||
const icon = widgetItem.find(Icon).at(0);
|
||||
const title = widgetItem.find('p').at(0);
|
||||
|
||||
// Then
|
||||
expect(widgetItem.hasClass('disabled')).toBe(false);
|
||||
expect(icon.prop('icon')).toEqual(fakeWidgets[kWidgetWeather].icon);
|
||||
expect(title.text()).toEqual(fakeWidgets[kWidgetWeather].title);
|
||||
});
|
||||
|
||||
it('configures and renders a widget item for an unknown widget', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={fakeServices}
|
||||
settings={fakeSettings}
|
||||
widgets={fakeWidgets}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const widgetItem = component.find(
|
||||
`div[data-service="${kServiceTime}"]`
|
||||
).at(
|
||||
0
|
||||
);
|
||||
const icon = widgetItem.find(Icon).at(0);
|
||||
const title = widgetItem.find('p').at(0);
|
||||
|
||||
// Then
|
||||
expect(icon.prop('icon')).toEqual(IconBasicGear);
|
||||
expect(title.text()).toEqual('Unnamed Widget');
|
||||
});
|
||||
|
||||
it('renders widgets as disabled if it has no dashboards', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={false}
|
||||
offlineMode={false}
|
||||
services={fakeServices}
|
||||
settings={fakeSettings}
|
||||
widgets={fakeWidgets}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const widgetItem = component.find(
|
||||
`div[data-service="${kServiceWeather}"]`
|
||||
).at(
|
||||
0
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(widgetItem.hasClass('disabled')).toBe(true);
|
||||
});
|
||||
|
||||
it('handles a widget item click', () => {
|
||||
// Given
|
||||
const mockEventPreventDefault = jasmine.createSpy();
|
||||
const mockEventStopPropagation = jasmine.createSpy();
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={fakeServices}
|
||||
settings={fakeSettings}
|
||||
widgets={fakeWidgets}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const widgetItem = component.find(
|
||||
`div[data-service="${kServiceTime}"]`
|
||||
).at(
|
||||
0
|
||||
);
|
||||
widgetItem.simulate('click', {
|
||||
currentTarget: {
|
||||
dataset: {service: kServiceTime},
|
||||
},
|
||||
preventDefault: mockEventPreventDefault,
|
||||
stopPropagation: mockEventStopPropagation,
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(mockOnAddService).toHaveBeenCalledWith(kServiceTime);
|
||||
expect(mockEventPreventDefault).not.toHaveBeenCalled();
|
||||
expect(mockEventStopPropagation).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('cancels the event when it does not have dashboards', () => {
|
||||
// Given
|
||||
const mockEventPreventDefault = jasmine.createSpy();
|
||||
const mockEventStopPropagation = jasmine.createSpy();
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={false}
|
||||
offlineMode={false}
|
||||
services={fakeServices}
|
||||
settings={fakeSettings}
|
||||
widgets={fakeWidgets}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const widgetItem = component.find(
|
||||
`div[data-service="${kServiceTime}"]`
|
||||
).at(
|
||||
0
|
||||
);
|
||||
widgetItem.simulate('click', {
|
||||
currentTarget: {
|
||||
dataset: {service: kServiceTime},
|
||||
},
|
||||
preventDefault: mockEventPreventDefault,
|
||||
stopPropagation: mockEventStopPropagation,
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(mockOnAddService).not.toHaveBeenCalled();
|
||||
expect(mockEventPreventDefault).toHaveBeenCalled();
|
||||
expect(mockEventStopPropagation).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('configures and renders the about modal', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={SERVICES}
|
||||
settings={fakeSettings}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const aboutModal = component.find(AboutModal).at(0);
|
||||
|
||||
// Then
|
||||
expect(aboutModal.exists()).toBe(true);
|
||||
expect(aboutModal.prop('build')).toEqual(123);
|
||||
expect(aboutModal.prop('show')).toBe(false);
|
||||
expect(aboutModal.prop('version')).toEqual('1.0');
|
||||
});
|
||||
|
||||
it('displays the about modal when it closes', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<StartMenu.ControlledStartMenu
|
||||
hasDashboards={true}
|
||||
offlineMode={false}
|
||||
services={SERVICES}
|
||||
settings={fakeSettings}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find('div[_hint="About"]').at(0).simulate('click');
|
||||
component.find(AboutModal).at(0).invoke('onClose')();
|
||||
|
||||
// Then
|
||||
const aboutModal = component.find(AboutModal).at(0);
|
||||
expect(aboutModal.prop('show')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
273
packages/homehub_app/tests/main/components/Taskbar.spec.js
Normal file
273
packages/homehub_app/tests/main/components/Taskbar.spec.js
Normal file
@@ -0,0 +1,273 @@
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {StartMenu} from 'src/main/components/StartMenu';
|
||||
import * as Taskbar from 'src/main/components/Taskbar';
|
||||
import {SERVICES} from 'src/services';
|
||||
import {WIDGETS} from 'src/widgets';
|
||||
|
||||
import {DashboardsFactory} from 'tests/__fixtures__/dashboards';
|
||||
|
||||
describe('src/main/components/Taskbar', () => {
|
||||
describe('Taskbar', () => {
|
||||
let mockOnAddDashboard = null;
|
||||
let mockOnAddService = null;
|
||||
let mockOnSelectDashboard = null;
|
||||
let fakeDashboards = null;
|
||||
let fakeSavingState = null;
|
||||
let fakeWebSocketState = null;
|
||||
|
||||
beforeEach(() => {
|
||||
mockOnAddDashboard = jasmine.createSpy();
|
||||
mockOnAddService = jasmine.createSpy();
|
||||
mockOnSelectDashboard = jasmine.createSpy();
|
||||
fakeDashboards = DashboardsFactory();
|
||||
fakeSavingState = {
|
||||
isSaving: false,
|
||||
lastSaveError: null,
|
||||
lastSaveTimestamp: new Date(1987, 9, 3, 8, 0, 0),
|
||||
};
|
||||
fakeWebSocketState = {
|
||||
isWebSocketConnected: true,
|
||||
};
|
||||
});
|
||||
|
||||
it('configures and renders the start menu', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<Taskbar.Taskbar
|
||||
currentDashboardId="testing"
|
||||
dashboards={fakeDashboards}
|
||||
offlineMode={false}
|
||||
savingState={fakeSavingState}
|
||||
services={SERVICES}
|
||||
webSocketState={fakeWebSocketState}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const startMenu = component.find(StartMenu).at(0);
|
||||
|
||||
// Then
|
||||
expect(startMenu.exists()).toBe(true);
|
||||
expect(startMenu.prop('offlineMode')).toBe(false);
|
||||
expect(startMenu.prop('services')).toEqual(SERVICES);
|
||||
expect(startMenu.prop('widgets')).toEqual(WIDGETS);
|
||||
expect(startMenu.prop('onAddDashboard')).toEqual(mockOnAddDashboard);
|
||||
expect(startMenu.prop('onAddService')).toEqual(mockOnAddService);
|
||||
});
|
||||
|
||||
it('configures renders the dashboard buttons', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<Taskbar.Taskbar
|
||||
currentDashboardId="testing"
|
||||
dashboards={fakeDashboards}
|
||||
offlineMode={false}
|
||||
savingState={fakeSavingState}
|
||||
services={SERVICES}
|
||||
webSocketState={fakeWebSocketState}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const dashboardButtons = component.find('a[_hint="Dashboard"]');
|
||||
|
||||
// Then
|
||||
expect(dashboardButtons.length).toEqual(2);
|
||||
|
||||
const dashboardButton = dashboardButtons.at(0);
|
||||
expect(dashboardButton.hasClass('active')).toBe(true);
|
||||
expect(dashboardButton.prop('data-dashboard')).toEqual(
|
||||
fakeDashboards[0].id
|
||||
);
|
||||
expect(dashboardButton.text()).toEqual(fakeDashboards[0].name);
|
||||
|
||||
expect(dashboardButtons.at(1).hasClass('active')).toBe(false);
|
||||
});
|
||||
|
||||
it('selects a dashboard when its button is clicked', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<Taskbar.Taskbar
|
||||
currentDashboardId="testing"
|
||||
dashboards={fakeDashboards}
|
||||
offlineMode={false}
|
||||
savingState={fakeSavingState}
|
||||
services={SERVICES}
|
||||
webSocketState={fakeWebSocketState}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
onSelectDashboard={mockOnSelectDashboard}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find('a[_hint="Dashboard"]').at(0).simulate('click', {
|
||||
currentTarget: {
|
||||
dataset: {dashboard: fakeDashboards[0].id},
|
||||
},
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(mockOnSelectDashboard).toHaveBeenCalledWith(fakeDashboards[0].id);
|
||||
});
|
||||
|
||||
it('does not render websocket status in offline mode', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<Taskbar.Taskbar
|
||||
currentDashboardId="testing"
|
||||
dashboards={fakeDashboards}
|
||||
offlineMode={true}
|
||||
savingState={fakeSavingState}
|
||||
services={SERVICES}
|
||||
webSocketState={fakeWebSocketState}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
onSelectDashboard={mockOnSelectDashboard}
|
||||
/>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.find('Icon[_hint="WebSocketConnected"]').exists()).toBe(
|
||||
false
|
||||
);
|
||||
expect(component.find('Icon[_hint="WebSocketNotConnected"]').exists()).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('renders the websocket connection status when websocket is connected', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<Taskbar.Taskbar
|
||||
currentDashboardId="testing"
|
||||
dashboards={fakeDashboards}
|
||||
offlineMode={false}
|
||||
savingState={fakeSavingState}
|
||||
services={SERVICES}
|
||||
webSocketState={fakeWebSocketState}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
onSelectDashboard={mockOnSelectDashboard}
|
||||
/>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.find('Icon[_hint="WebSocketConnected"]').exists()).toBe(
|
||||
true
|
||||
);
|
||||
expect(component.find('Icon[_hint="WebSocketNotConnected"]').exists()).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('renders the websocket connection status when websocket is not connected', () => {
|
||||
// Given
|
||||
fakeWebSocketState.isWebSocketConnected = false;
|
||||
const component = shallow(
|
||||
<Taskbar.Taskbar
|
||||
currentDashboardId="testing"
|
||||
dashboards={fakeDashboards}
|
||||
offlineMode={false}
|
||||
savingState={fakeSavingState}
|
||||
services={SERVICES}
|
||||
webSocketState={fakeWebSocketState}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
onSelectDashboard={mockOnSelectDashboard}
|
||||
/>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.find('Icon[_hint="WebSocketConnected"]').exists()).toBe(
|
||||
false
|
||||
);
|
||||
expect(component.find('Icon[_hint="WebSocketNotConnected"]').exists()).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it('renders the saving status when it is saving', () => {
|
||||
// Given
|
||||
fakeSavingState.isSaving = true;
|
||||
const component = shallow(
|
||||
<Taskbar.Taskbar
|
||||
currentDashboardId="testing"
|
||||
dashboards={fakeDashboards}
|
||||
offlineMode={false}
|
||||
savingState={fakeSavingState}
|
||||
services={SERVICES}
|
||||
webSocketState={fakeWebSocketState}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
onSelectDashboard={mockOnSelectDashboard}
|
||||
/>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.find('Icon[_hint="IsSaving"]').exists()).toBe(true);
|
||||
expect(component.find('Icon[_hint="SaveError"]').exists()).toBe(false);
|
||||
expect(component.find('Icon[_hint="SaveOK"]').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('renders the saving status when last save was an error', () => {
|
||||
// Given
|
||||
fakeSavingState.lastSaveError = 'FIAL';
|
||||
const component = shallow(
|
||||
<Taskbar.Taskbar
|
||||
currentDashboardId="testing"
|
||||
dashboards={fakeDashboards}
|
||||
offlineMode={false}
|
||||
savingState={fakeSavingState}
|
||||
services={SERVICES}
|
||||
webSocketState={fakeWebSocketState}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
onSelectDashboard={mockOnSelectDashboard}
|
||||
/>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.find('Icon[_hint="IsSaving"]').exists()).toBe(false);
|
||||
expect(component.find('Icon[_hint="SaveError"]').exists()).toBe(true);
|
||||
expect(component.find('Icon[_hint="SaveOK"]').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('renders the saving status when last save was OK', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<Taskbar.Taskbar
|
||||
currentDashboardId="testing"
|
||||
dashboards={fakeDashboards}
|
||||
offlineMode={false}
|
||||
savingState={fakeSavingState}
|
||||
services={SERVICES}
|
||||
webSocketState={fakeWebSocketState}
|
||||
widgets={WIDGETS}
|
||||
onAddDashboard={mockOnAddDashboard}
|
||||
onAddService={mockOnAddService}
|
||||
onSelectDashboard={mockOnSelectDashboard}
|
||||
/>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.find('Icon[_hint="IsSaving"]').exists()).toBe(false);
|
||||
expect(component.find('Icon[_hint="SaveError"]').exists()).toBe(false);
|
||||
expect(component.find('Icon[_hint="SaveOK"]').exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,133 @@
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {Button, Modal} from 'src/components';
|
||||
import * as WidgetSettingsModal from 'src/main/components/WidgetSettingsModal';
|
||||
|
||||
describe('src/main/components/WidgetSettingsModal', () => {
|
||||
describe('WidgetSettingsModal', () => {
|
||||
let mockOnCancelButtonClick = null;
|
||||
let mockOnClose = null;
|
||||
let mockOnSaveButtonClick = null;
|
||||
let fakeSettingsView = null;
|
||||
let fakeSettingsViewProps = null;
|
||||
|
||||
beforeEach(() => {
|
||||
mockOnCancelButtonClick = jasmine.createSpy();
|
||||
mockOnClose = jasmine.createSpy();
|
||||
mockOnSaveButtonClick = jasmine.createSpy();
|
||||
fakeSettingsView = jasmine.createSpy().and.returnValue(
|
||||
<span>Settings</span>
|
||||
);
|
||||
fakeSettingsView.displayName = 'FakeSettingsView';
|
||||
fakeSettingsViewProps = {spam: true};
|
||||
});
|
||||
|
||||
it('configures and renders the modal', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<WidgetSettingsModal.WidgetSettingsModal
|
||||
buttons={[]}
|
||||
settingsView={fakeSettingsView}
|
||||
settingsViewProps={fakeSettingsViewProps}
|
||||
show={false}
|
||||
onCancelButtonClick={mockOnCancelButtonClick}
|
||||
onClose={mockOnClose}
|
||||
onSaveButtonClick={mockOnSaveButtonClick}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const modal = component.find(Modal).at(0);
|
||||
|
||||
// Then
|
||||
expect(modal.exists()).toBe(true);
|
||||
expect(modal.prop('show')).toBe(false);
|
||||
expect(modal.prop('onHide')).toEqual(mockOnClose);
|
||||
});
|
||||
|
||||
it('configures and renders the settings view', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<WidgetSettingsModal.WidgetSettingsModal
|
||||
buttons={[]}
|
||||
settingsView={fakeSettingsView}
|
||||
settingsViewProps={fakeSettingsViewProps}
|
||||
show={false}
|
||||
onCancelButtonClick={mockOnCancelButtonClick}
|
||||
onClose={mockOnClose}
|
||||
onSaveButtonClick={mockOnSaveButtonClick}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const settingsView = component.find(fakeSettingsView).at(0);
|
||||
|
||||
// Then
|
||||
expect(settingsView.props()).toEqual(fakeSettingsViewProps);
|
||||
});
|
||||
|
||||
it('renders the additional buttons', () => {
|
||||
// Given
|
||||
const button = <Button key="testing">Test</Button>;
|
||||
const component = shallow(
|
||||
<WidgetSettingsModal.WidgetSettingsModal
|
||||
buttons={[button]}
|
||||
settingsView={fakeSettingsView}
|
||||
settingsViewProps={fakeSettingsViewProps}
|
||||
show={false}
|
||||
onCancelButtonClick={mockOnCancelButtonClick}
|
||||
onClose={mockOnClose}
|
||||
onSaveButtonClick={mockOnSaveButtonClick}
|
||||
/>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.contains(button)).toBe(true);
|
||||
});
|
||||
|
||||
it('configures and renders the cancel button', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<WidgetSettingsModal.WidgetSettingsModal
|
||||
buttons={[]}
|
||||
settingsView={fakeSettingsView}
|
||||
settingsViewProps={fakeSettingsViewProps}
|
||||
show={false}
|
||||
onCancelButtonClick={mockOnCancelButtonClick}
|
||||
onClose={mockOnClose}
|
||||
onSaveButtonClick={mockOnSaveButtonClick}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const button = component.find('Button[_hint="Cancel"]').at(0);
|
||||
|
||||
// Then
|
||||
expect(button.exists()).toBe(true);
|
||||
expect(button.prop('onClick')).toEqual(mockOnCancelButtonClick);
|
||||
});
|
||||
|
||||
it('configures and renders the save button', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<WidgetSettingsModal.WidgetSettingsModal
|
||||
buttons={[]}
|
||||
settingsView={fakeSettingsView}
|
||||
settingsViewProps={fakeSettingsViewProps}
|
||||
show={false}
|
||||
onCancelButtonClick={mockOnCancelButtonClick}
|
||||
onClose={mockOnClose}
|
||||
onSaveButtonClick={mockOnSaveButtonClick}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const button = component.find('Button[_hint="Save"]').at(0);
|
||||
|
||||
// Then
|
||||
expect(button.exists()).toBe(true);
|
||||
expect(button.prop('onClick')).toEqual(mockOnSaveButtonClick);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,98 @@
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import * as WidgetToolbar from 'src/main/components/WidgetToolbar';
|
||||
|
||||
describe('src/main/components/WidgetToolbar', () => {
|
||||
describe('WidgetToolbar', () => {
|
||||
it('allows passing an arbitrary class name', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<WidgetToolbar.WidgetToolbar className="test">
|
||||
<span>It works!</span>
|
||||
</WidgetToolbar.WidgetToolbar>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.hasClass('test')).toBe(true);
|
||||
});
|
||||
|
||||
it('renders a right-side toolbar', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<WidgetToolbar.WidgetToolbar right>
|
||||
<span>It works!</span>
|
||||
</WidgetToolbar.WidgetToolbar>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.hasClass('left')).toBe(false);
|
||||
expect(component.hasClass('right')).toBe(true);
|
||||
});
|
||||
|
||||
it('renders a left-side toolbar', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<WidgetToolbar.WidgetToolbar>
|
||||
<span>It works!</span>
|
||||
</WidgetToolbar.WidgetToolbar>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.hasClass('left')).toBe(true);
|
||||
expect(component.hasClass('right')).toBe(false);
|
||||
});
|
||||
|
||||
it('renders the children', () => {
|
||||
// Given
|
||||
const children = <span>It works!</span>;
|
||||
const component = shallow(
|
||||
<WidgetToolbar.WidgetToolbar right>
|
||||
{children}
|
||||
</WidgetToolbar.WidgetToolbar>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.contains(children)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('WidgetToolbar.Item', () => {
|
||||
it('allows passing an arbitrary class name', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<WidgetToolbar.WidgetToolbar.Item className="test">
|
||||
<span>It works!</span>
|
||||
</WidgetToolbar.WidgetToolbar.Item>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.hasClass('test')).toBe(true);
|
||||
});
|
||||
|
||||
it('renders a danger item', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<WidgetToolbar.WidgetToolbar.Item danger>
|
||||
<span>It works!</span>
|
||||
</WidgetToolbar.WidgetToolbar.Item>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.hasClass('danger')).toBe(true);
|
||||
});
|
||||
|
||||
it('renders the children', () => {
|
||||
// Given
|
||||
const children = <span>It works!</span>;
|
||||
const component = shallow(
|
||||
<WidgetToolbar.WidgetToolbar.Item>
|
||||
{children}
|
||||
</WidgetToolbar.WidgetToolbar.Item>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.contains(children)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,170 @@
|
||||
import {
|
||||
DashboardsContext, ServiceContainer, ServiceState,
|
||||
} from '@bthlabs/homehub-core';
|
||||
import {mount} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {Dashboard, DashboardItem, DummyWidget} from 'src/main/components';
|
||||
import * as DashboardContainer from 'src/main/containers/DashboardContainer';
|
||||
|
||||
import {DashboardsContextFactory} from 'tests/__fixtures__/dashboardsContext';
|
||||
import {FakeWidget} from 'tests/__fixtures__/services';
|
||||
import {SettingsFactory} from 'tests/__fixtures__/settings';
|
||||
|
||||
describe('src/main/containers/DashboardContainer', () => {
|
||||
describe('ControlledDashboardContainer', () => {
|
||||
let fakeDashboardsContext = null;
|
||||
let fakeSettings = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeDashboardsContext = DashboardsContextFactory();
|
||||
fakeSettings = SettingsFactory();
|
||||
});
|
||||
|
||||
it('handles grid layout change', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<DashboardContainer.ControlledDashboardContainer
|
||||
settings={fakeSettings}
|
||||
/>
|
||||
</DashboardsContext.Provider>
|
||||
);
|
||||
|
||||
// When
|
||||
const dashboard = component.find(Dashboard).at(0);
|
||||
dashboard.invoke('onGridLayoutChange')([
|
||||
{i: 'fake_instance', x: 0, y: 0, w: 1, h: 1},
|
||||
{i: 'other_fake_instance', x: 1, y: 1, w: 1, h: 1},
|
||||
]);
|
||||
|
||||
// Then
|
||||
expect(fakeDashboardsContext.saveServiceLayout).toHaveBeenCalledWith(
|
||||
'fake_instance', {x: 0, y: 0, w: 1, h: 1}
|
||||
);
|
||||
expect(fakeDashboardsContext.saveServiceLayout).toHaveBeenCalledWith(
|
||||
'other_fake_instance', {x: 1, y: 1, w: 1, h: 1}
|
||||
);
|
||||
});
|
||||
|
||||
it('configures and renders the dashboard', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<DashboardContainer.ControlledDashboardContainer
|
||||
settings={fakeSettings}
|
||||
/>
|
||||
</DashboardsContext.Provider>
|
||||
);
|
||||
|
||||
const expectedLayout = [
|
||||
{i: 'fake_instance', x: 0, y: 0, w: 1, h: 1, minW: 1, minH: 1},
|
||||
{i: 'other_fake_instance', x: 0, y: 1, w: 1, h: 1},
|
||||
];
|
||||
|
||||
// When
|
||||
const dashboard = component.find(Dashboard).at(0);
|
||||
|
||||
// Then
|
||||
expect(dashboard.exists()).toBe(true);
|
||||
expect(dashboard.prop('layout')).toEqual(expectedLayout);
|
||||
});
|
||||
|
||||
it('configures and render a ServiceContainer for services', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<DashboardContainer.ControlledDashboardContainer
|
||||
settings={fakeSettings}
|
||||
/>
|
||||
</DashboardsContext.Provider>
|
||||
);
|
||||
|
||||
// When
|
||||
const serviceContainers = component.find(ServiceContainer);
|
||||
|
||||
// Then
|
||||
expect(serviceContainers.length).toEqual(2);
|
||||
|
||||
const serviceContainer = serviceContainers.at(0);
|
||||
expect(serviceContainer.prop('instance')).toEqual(
|
||||
fakeDashboardsContext.dashboards[0].services[0].instance
|
||||
);
|
||||
expect(serviceContainer.prop('kind')).toEqual(
|
||||
fakeDashboardsContext.dashboards[0].services[0].kind
|
||||
);
|
||||
});
|
||||
|
||||
it('configures and renders a DashboardItem for services', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<DashboardContainer.ControlledDashboardContainer
|
||||
settings={fakeSettings}
|
||||
/>
|
||||
</DashboardsContext.Provider>
|
||||
);
|
||||
|
||||
// When
|
||||
const dashboardItems = component.find(DashboardItem);
|
||||
|
||||
// Then
|
||||
expect(dashboardItems.length).toEqual(2);
|
||||
|
||||
const dashboardItem = dashboardItems.at(0);
|
||||
expect(dashboardItem.prop('settingsView')).toEqual(
|
||||
FakeWidget.settingsView
|
||||
);
|
||||
expect(dashboardItem.prop('settingsViewProps').settings).toEqual(
|
||||
fakeSettings
|
||||
);
|
||||
|
||||
expect(dashboardItems.at(1).prop('settingsView')).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('configures and renders a widget for services', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<DashboardContainer.ControlledDashboardContainer
|
||||
settings={fakeSettings}
|
||||
/>
|
||||
</DashboardsContext.Provider>
|
||||
);
|
||||
|
||||
// When
|
||||
const widget = component.find(FakeWidget).at(0);
|
||||
|
||||
// Then
|
||||
expect(widget.exists()).toBe(true);
|
||||
expect(widget.prop('appearance')).toEqual(
|
||||
fakeDashboardsContext.dashboards[0].services[0].characteristics.appearance
|
||||
);
|
||||
expect(widget.prop('service')).toEqual(
|
||||
fakeDashboardsContext.dashboards[0].services[0]
|
||||
);
|
||||
expect(widget.prop('serviceState')).toBeInstanceOf(ServiceState);
|
||||
expect(widget.prop('setServiceState')).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders a DummyWidget for service with unknown widget', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<DashboardContainer.ControlledDashboardContainer
|
||||
settings={fakeSettings}
|
||||
/>
|
||||
</DashboardsContext.Provider>
|
||||
);
|
||||
|
||||
// When
|
||||
const widget = component.find(DummyWidget).at(0);
|
||||
|
||||
// Then
|
||||
expect(widget.exists()).toBe(true);
|
||||
expect(widget.prop('service')).toEqual(
|
||||
fakeDashboardsContext.dashboards[0].services[1]
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,270 @@
|
||||
import {DashboardsContext} from '@bthlabs/homehub-core';
|
||||
import {RangoAppContext} from '@bthlabs/rango';
|
||||
import {mount} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
AddDashboardModal, Taskbar, WidgetSettingsModal,
|
||||
} from 'src/main/components';
|
||||
import * as TaskbarContainer from 'src/main/containers/TaskbarContainer';
|
||||
|
||||
import {DashboardsContextFactory} from 'tests/__fixtures__/dashboardsContext';
|
||||
import {
|
||||
FakeService, FakeServiceWithoutSettings, FakeWidget,
|
||||
} from 'tests/__fixtures__/services';
|
||||
import {SettingsFactory} from 'tests/__fixtures__/settings';
|
||||
|
||||
describe('src/main/containers/TaskbarContainer', () => {
|
||||
describe('TaskbarContainer', () => {
|
||||
let fakeDashboardsContext = null;
|
||||
let fakeSettings = null;
|
||||
let fakeRangoAppContext = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeDashboardsContext = DashboardsContextFactory();
|
||||
fakeSettings = SettingsFactory();
|
||||
fakeRangoAppContext = {settings: fakeSettings};
|
||||
});
|
||||
|
||||
it('configures the WidgetSettingsModal to show settings view for added service', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<RangoAppContext.Provider value={fakeRangoAppContext}>
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<TaskbarContainer.TaskbarContainer />
|
||||
</DashboardsContext.Provider>
|
||||
</RangoAppContext.Provider>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Taskbar).at(0).invoke('onAddService')(FakeService.kind);
|
||||
component.setProps({});
|
||||
|
||||
// Then
|
||||
const widgetSettingsModal = component.find(WidgetSettingsModal).at(0);
|
||||
expect(widgetSettingsModal.prop('settingsView')).toEqual(
|
||||
FakeWidget.settingsView
|
||||
);
|
||||
expect(widgetSettingsModal.prop('settingsViewProps')).toEqual({
|
||||
nextCharacteristics: FakeService.emptyCharacteristics(),
|
||||
settings: fakeSettings,
|
||||
setNextCharacteristics: jasmine.any(Function),
|
||||
});
|
||||
expect(fakeDashboardsContext.addService).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('immediately adds a service that does not use settings view', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<RangoAppContext.Provider value={fakeRangoAppContext}>
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<TaskbarContainer.TaskbarContainer />
|
||||
</DashboardsContext.Provider>
|
||||
</RangoAppContext.Provider>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Taskbar).at(0).invoke('onAddService')(
|
||||
FakeServiceWithoutSettings.kind
|
||||
);
|
||||
component.setProps({});
|
||||
|
||||
// Then
|
||||
expect(fakeDashboardsContext.addService).toHaveBeenCalledWith(
|
||||
FakeServiceWithoutSettings.kind,
|
||||
FakeServiceWithoutSettings.emptyCharacteristics()
|
||||
);
|
||||
});
|
||||
|
||||
it('handles the WidgetSettingsModal being closed', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<RangoAppContext.Provider value={fakeRangoAppContext}>
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<TaskbarContainer.TaskbarContainer />
|
||||
</DashboardsContext.Provider>
|
||||
</RangoAppContext.Provider>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Taskbar).at(0).invoke('onAddService')(FakeService.kind);
|
||||
component.setProps({});
|
||||
|
||||
component.find(WidgetSettingsModal).at(0).invoke('onClose')();
|
||||
component.setProps({});
|
||||
|
||||
// Then
|
||||
const widgetSettingsModal = component.find(WidgetSettingsModal).at(0);
|
||||
expect(widgetSettingsModal.prop('settingsView')).toBe(null);
|
||||
expect(widgetSettingsModal.prop('settingsViewProps')).toEqual({});
|
||||
});
|
||||
|
||||
it('handles the WidgetSettingsModal save button click', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<RangoAppContext.Provider value={fakeRangoAppContext}>
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<TaskbarContainer.TaskbarContainer />
|
||||
</DashboardsContext.Provider>
|
||||
</RangoAppContext.Provider>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Taskbar).at(0).invoke('onAddService')(FakeService.kind);
|
||||
component.setProps({});
|
||||
|
||||
component.find(WidgetSettingsModal).at(0).invoke('onSaveButtonClick')();
|
||||
component.setProps({});
|
||||
|
||||
// Then
|
||||
expect(fakeDashboardsContext.addService).toHaveBeenCalledWith(
|
||||
FakeService.kind,
|
||||
FakeService.emptyCharacteristics()
|
||||
);
|
||||
});
|
||||
|
||||
it('displays the AddDashboardModal when add dashboard button is clicked', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<RangoAppContext.Provider value={fakeRangoAppContext}>
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<TaskbarContainer.TaskbarContainer />
|
||||
</DashboardsContext.Provider>
|
||||
</RangoAppContext.Provider>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Taskbar).at(0).invoke('onAddDashboard')();
|
||||
|
||||
// Then
|
||||
const addDashboardModal = component.find(AddDashboardModal).at(0);
|
||||
expect(addDashboardModal.prop('show')).toBe(true);
|
||||
});
|
||||
|
||||
it('hides the AddDashboardModal when it is closed', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<RangoAppContext.Provider value={fakeRangoAppContext}>
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<TaskbarContainer.TaskbarContainer />
|
||||
</DashboardsContext.Provider>
|
||||
</RangoAppContext.Provider>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Taskbar).at(0).invoke('onAddDashboard')();
|
||||
component.find(AddDashboardModal).at(0).invoke('onClose')();
|
||||
|
||||
// Then
|
||||
const addDashboardModal = component.find(AddDashboardModal).at(0);
|
||||
expect(addDashboardModal.prop('show')).toBe(false);
|
||||
});
|
||||
|
||||
it('handles the AddDashboardModal save button click', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<RangoAppContext.Provider value={fakeRangoAppContext}>
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<TaskbarContainer.TaskbarContainer />
|
||||
</DashboardsContext.Provider>
|
||||
</RangoAppContext.Provider>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(AddDashboardModal).at(0).invoke('onSave')('Testing2');
|
||||
|
||||
// Then
|
||||
expect(fakeDashboardsContext.addDashboard).toHaveBeenCalledWith(
|
||||
'Testing2'
|
||||
);
|
||||
});
|
||||
|
||||
it('sets a selected dashboard as current', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<RangoAppContext.Provider value={fakeRangoAppContext}>
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<TaskbarContainer.TaskbarContainer />
|
||||
</DashboardsContext.Provider>
|
||||
</RangoAppContext.Provider>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Taskbar).at(0).invoke('onSelectDashboard')('testing2');
|
||||
|
||||
// Then
|
||||
expect(fakeDashboardsContext.setCurrentDashboardId).toHaveBeenCalledWith(
|
||||
'testing2'
|
||||
);
|
||||
});
|
||||
|
||||
it('configures and renders the Taskbar', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<RangoAppContext.Provider value={fakeRangoAppContext}>
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<TaskbarContainer.TaskbarContainer />
|
||||
</DashboardsContext.Provider>
|
||||
</RangoAppContext.Provider>
|
||||
);
|
||||
|
||||
// When
|
||||
const taskbar = component.find(Taskbar).at(0);
|
||||
|
||||
// Then
|
||||
expect(taskbar.exists()).toBe(true);
|
||||
expect(taskbar.prop('currentDashboardId')).toEqual(
|
||||
fakeDashboardsContext.currentDashboardId
|
||||
);
|
||||
expect(taskbar.prop('dashboards')).toEqual(
|
||||
fakeDashboardsContext.dashboards
|
||||
);
|
||||
expect(taskbar.prop('offlineMode')).toEqual(fakeSettings.OFFLINE_MODE);
|
||||
expect(taskbar.prop('savingState')).toEqual({
|
||||
isSaving: false,
|
||||
lastSaveError: null,
|
||||
lastSaveTimestamp: null,
|
||||
});
|
||||
expect(taskbar.prop('services')).toEqual(fakeSettings.SERVICES);
|
||||
expect(taskbar.prop('webSocketState')).toEqual({
|
||||
isWebSocketConnected: false,
|
||||
});
|
||||
expect(taskbar.prop('widgets')).toEqual(fakeSettings.WIDGETS);
|
||||
});
|
||||
|
||||
it('configures and renders the WidgetSettingsModal', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<RangoAppContext.Provider value={fakeRangoAppContext}>
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<TaskbarContainer.TaskbarContainer />
|
||||
</DashboardsContext.Provider>
|
||||
</RangoAppContext.Provider>
|
||||
);
|
||||
|
||||
// When
|
||||
const widgetSettingsModal = component.find(WidgetSettingsModal).at(0);
|
||||
|
||||
// Then
|
||||
expect(widgetSettingsModal.exists()).toBe(true);
|
||||
expect(widgetSettingsModal.prop('show')).toBe(true);
|
||||
});
|
||||
|
||||
it('configures and renders the AddDashboardModal', () => {
|
||||
// Given
|
||||
const component = mount(
|
||||
<RangoAppContext.Provider value={fakeRangoAppContext}>
|
||||
<DashboardsContext.Provider value={fakeDashboardsContext}>
|
||||
<TaskbarContainer.TaskbarContainer />
|
||||
</DashboardsContext.Provider>
|
||||
</RangoAppContext.Provider>
|
||||
);
|
||||
|
||||
// When
|
||||
const addDashboardModal = component.find(AddDashboardModal).at(0);
|
||||
|
||||
// Then
|
||||
expect(addDashboardModal.exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,147 @@
|
||||
import * as HomeHubCore from '@bthlabs/homehub-core';
|
||||
|
||||
import * as StateDataSource from 'src/main/dataSources/StateDataSource';
|
||||
|
||||
describe('src/lib/main/dataSources/StateDataSource', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(HomeHubCore.API.State, 'get');
|
||||
spyOn(HomeHubCore.API.State, 'save');
|
||||
spyOn(HomeHubCore.LocalStorage, 'getItem');
|
||||
spyOn(HomeHubCore.LocalStorage, 'setItem');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
StateDataSource.setOfflineMode(false);
|
||||
});
|
||||
|
||||
describe('loadState', () => {
|
||||
it('throws an error if API call in online mode returns an error', async () => {
|
||||
// Given
|
||||
StateDataSource.setOfflineMode(false);
|
||||
HomeHubCore.API.State.get.and.resolveTo({
|
||||
error: 'FIAL',
|
||||
});
|
||||
|
||||
// When
|
||||
let result = null;
|
||||
try {
|
||||
await StateDataSource.loadState();
|
||||
} catch (error) {
|
||||
result = error;
|
||||
}
|
||||
|
||||
// Then
|
||||
expect(result).toEqual('FIAL');
|
||||
});
|
||||
|
||||
it('returns state loaded from the API in online mode', async () => {
|
||||
// Given
|
||||
StateDataSource.setOfflineMode(false);
|
||||
const data = {dashboards: []};
|
||||
HomeHubCore.API.State.get.and.resolveTo({
|
||||
data: data,
|
||||
});
|
||||
|
||||
// When
|
||||
const result = await StateDataSource.loadState();
|
||||
|
||||
// Then
|
||||
expect(result).toEqual(data);
|
||||
expect(HomeHubCore.API.State.get).toHaveBeenCalled();
|
||||
expect(HomeHubCore.LocalStorage.getItem).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns state loaded from local storage in offline mode', async () => {
|
||||
// Given
|
||||
StateDataSource.setOfflineMode(true);
|
||||
const data = {dashboards: []};
|
||||
HomeHubCore.LocalStorage.getItem.and.returnValue(JSON.stringify(data));
|
||||
|
||||
// When
|
||||
const result = await StateDataSource.loadState();
|
||||
|
||||
// Then
|
||||
expect(result).toEqual(data);
|
||||
expect(HomeHubCore.API.State.get).not.toHaveBeenCalled();
|
||||
expect(HomeHubCore.LocalStorage.getItem).toHaveBeenCalledWith('state');
|
||||
});
|
||||
|
||||
it('returns initial state if it is missing in local storage in offline mode', async () => {
|
||||
// Given
|
||||
StateDataSource.setOfflineMode(true);
|
||||
HomeHubCore.LocalStorage.getItem.and.returnValue(null);
|
||||
|
||||
// When
|
||||
const result = await StateDataSource.loadState();
|
||||
|
||||
// Then
|
||||
expect(result.dashboards.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('doSaveState', () => {
|
||||
let fakeState = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeState = {
|
||||
dashboards: [
|
||||
{
|
||||
id: 'testing',
|
||||
name: 'Testing',
|
||||
services: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
it('throws an error if API call in online mode returns an error', async () => {
|
||||
// Given
|
||||
StateDataSource.setOfflineMode(false);
|
||||
HomeHubCore.API.State.save.and.resolveTo({
|
||||
error: 'FIAL',
|
||||
});
|
||||
|
||||
// When
|
||||
let result = null;
|
||||
try {
|
||||
result = await StateDataSource.doSaveState(fakeState);
|
||||
} catch (error) {
|
||||
result = error;
|
||||
}
|
||||
|
||||
// Then
|
||||
expect(result).toEqual('FIAL');
|
||||
});
|
||||
|
||||
it('returns the result of the API call in online mode', async () => {
|
||||
// Given
|
||||
StateDataSource.setOfflineMode(false);
|
||||
HomeHubCore.API.State.save.and.resolveTo({
|
||||
data: true,
|
||||
});
|
||||
|
||||
// When
|
||||
const result = await StateDataSource.doSaveState(fakeState);
|
||||
|
||||
// Then
|
||||
expect(result).toBe(true);
|
||||
expect(HomeHubCore.API.State.save).toHaveBeenCalledWith(fakeState);
|
||||
expect(HomeHubCore.LocalStorage.setItem).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns the result of local storage save operation in offline mode', async () => {
|
||||
// Given
|
||||
StateDataSource.setOfflineMode(true);
|
||||
|
||||
// When
|
||||
const result = await StateDataSource.doSaveState(fakeState);
|
||||
|
||||
// Then
|
||||
expect(result).toBe(true);
|
||||
expect(HomeHubCore.API.State.save).not.toHaveBeenCalled();
|
||||
expect(HomeHubCore.LocalStorage.setItem).toHaveBeenCalledWith(
|
||||
'state', JSON.stringify(fakeState)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
101
packages/homehub_app/tests/main/views/AppView.spec.js
Normal file
101
packages/homehub_app/tests/main/views/AppView.spec.js
Normal file
@@ -0,0 +1,101 @@
|
||||
import {DashboardsProvider} from '@bthlabs/homehub-core';
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {DashboardContainer, TaskbarContainer} from 'src/main/containers';
|
||||
import {StateDataSource} from 'src/main/dataSources';
|
||||
import * as AppView from 'src/main/views/AppView';
|
||||
|
||||
import {SettingsFactory} from 'tests/__fixtures__/settings';
|
||||
|
||||
describe('src/main/views/AppView', () => {
|
||||
describe('AppView', () => {
|
||||
let fakeActions = null;
|
||||
let fakeSetings = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeActions = {
|
||||
setDocumentTitle: jasmine.createSpy(),
|
||||
};
|
||||
fakeSetings = SettingsFactory();
|
||||
});
|
||||
|
||||
describe('componentDidMount', () => {
|
||||
it('calls the setDocumentTitle action', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<AppView.AppView
|
||||
actions={fakeActions}
|
||||
settings={fakeSetings}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.instance().componentDidMount();
|
||||
|
||||
// Then
|
||||
expect(fakeActions.setDocumentTitle).toHaveBeenCalledWith(
|
||||
jasmine.any(String)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('render', () => {
|
||||
it('configures and renders the DashboardsProvider', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<AppView.AppView
|
||||
actions={fakeActions}
|
||||
settings={fakeSetings}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const dashboardsProvider = component.find(DashboardsProvider).at(0);
|
||||
|
||||
// Then
|
||||
expect(dashboardsProvider.exists()).toBe(true);
|
||||
expect(dashboardsProvider.prop('loader')).toBeDefined();
|
||||
expect(dashboardsProvider.prop('loadDashboards')).toEqual(
|
||||
StateDataSource.loadState
|
||||
);
|
||||
expect(dashboardsProvider.prop('saveDashboards')).toEqual(
|
||||
StateDataSource.saveState
|
||||
);
|
||||
expect(dashboardsProvider.prop('settings')).toEqual(fakeSetings);
|
||||
});
|
||||
|
||||
it('configures and renders the DashboardContainer', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<AppView.AppView
|
||||
actions={fakeActions}
|
||||
settings={fakeSetings}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const dashboardContainer = component.find(DashboardContainer).at(0);
|
||||
|
||||
// Then
|
||||
expect(dashboardContainer.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('configures and renders the TaskbarContainer', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<AppView.AppView
|
||||
actions={fakeActions}
|
||||
settings={fakeSetings}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const taskbarContainer = component.find(TaskbarContainer).at(0);
|
||||
|
||||
// Then
|
||||
expect(taskbarContainer.exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import * as ErrorBoundaryView from 'src/main/views/ErrorBoundaryView';
|
||||
|
||||
describe('src/main/views/ErrorBoundaryView', () => {
|
||||
describe('ErrorBoundaryView', () => {
|
||||
let fakeError = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeError = new Error('FIAL');
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('initializes the state', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<ErrorBoundaryView.ErrorBoundaryView>
|
||||
<span>It works!</span>
|
||||
</ErrorBoundaryView.ErrorBoundaryView>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.state('error')).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('componentDidCatch', () => {
|
||||
it('updates the state with error', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<ErrorBoundaryView.ErrorBoundaryView>
|
||||
<span>It works!</span>
|
||||
</ErrorBoundaryView.ErrorBoundaryView>
|
||||
);
|
||||
|
||||
// When
|
||||
component.instance().componentDidCatch(fakeError);
|
||||
|
||||
// Then
|
||||
expect(component.state('error')).toEqual(fakeError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('render', () => {
|
||||
let fakeChildren = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeChildren = <span>It works!</span>;
|
||||
});
|
||||
|
||||
it('renders the children when error is null', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<ErrorBoundaryView.ErrorBoundaryView>
|
||||
{fakeChildren}
|
||||
</ErrorBoundaryView.ErrorBoundaryView>
|
||||
);
|
||||
|
||||
// When
|
||||
component.setState({error: null});
|
||||
|
||||
// Then
|
||||
expect(component.contains(fakeChildren)).toBe(true);
|
||||
expect(component.contains('.ErrorBoundaryView')).toBe(false);
|
||||
});
|
||||
|
||||
it('renders the error view when error is not null', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<ErrorBoundaryView.ErrorBoundaryView>
|
||||
{fakeChildren}
|
||||
</ErrorBoundaryView.ErrorBoundaryView>
|
||||
);
|
||||
|
||||
// When
|
||||
component.setState({error: fakeError});
|
||||
|
||||
// Then
|
||||
expect(component.contains(fakeChildren)).toBe(false);
|
||||
expect(component.hasClass('ErrorBoundaryView')).toBe(true);
|
||||
expect(component.find('p').at(1).text()).toMatch(fakeError.message);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
273
packages/homehub_app/tests/services/time.spec.js
Normal file
273
packages/homehub_app/tests/services/time.spec.js
Normal file
@@ -0,0 +1,273 @@
|
||||
import {DateTime} from 'luxon';
|
||||
|
||||
import {Clock} from 'src/lib/clock';
|
||||
import * as TimeService from 'src/services/time';
|
||||
|
||||
describe('src/services/time', () => {
|
||||
describe('TimeService', () => {
|
||||
let fakeClock = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeClock = jasmine.createSpyObj('FakeClock', [
|
||||
'addEventListener', 'removeEventListener',
|
||||
]);
|
||||
fakeClock.now = DateTime.local();
|
||||
spyOn(Clock, 'instance').and.returnValue(fakeClock);
|
||||
});
|
||||
|
||||
describe('emptyCharacteristics', () => {
|
||||
it('returns empty characteristics', () => {
|
||||
// Given
|
||||
const result = TimeService.TimeService.emptyCharacteristics();
|
||||
|
||||
// Then
|
||||
expect(result).toEqual({
|
||||
locale: '',
|
||||
timeFormat: '',
|
||||
dateFormat: 'DATE_HUGE',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('payloadFromDateTime', () => {
|
||||
let now = null;
|
||||
|
||||
beforeEach(() => {
|
||||
now = DateTime.local(1985, 10, 3, 8, 0, 0);
|
||||
spyOn(now, 'setLocale').and.callFake(() => {
|
||||
return now;
|
||||
});
|
||||
spyOn(now, 'toFormat').and.returnValue('Formatted Time String');
|
||||
|
||||
spyOn(now, 'toLocaleString');
|
||||
now.toLocaleString.withArgs(DateTime.TIME_SIMPLE).and.returnValue(
|
||||
'Simple Time String'
|
||||
);
|
||||
now.toLocaleString.withArgs(DateTime.DATE_HUGE).and.returnValue(
|
||||
'Huge Date String'
|
||||
);
|
||||
now.toLocaleString.withArgs(DateTime.DATE_FULL).and.returnValue(
|
||||
'Full Date String'
|
||||
);
|
||||
});
|
||||
|
||||
it('sets locale if it is specified', () => {
|
||||
// Given
|
||||
const characteristics = TimeService.TimeService.emptyCharacteristics();
|
||||
characteristics.locale = 'en-us';
|
||||
const service = new TimeService.TimeService({
|
||||
instance: 'testing',
|
||||
characteristics: characteristics,
|
||||
});
|
||||
|
||||
// When
|
||||
service.payloadFromDateTime(now);
|
||||
|
||||
// Then
|
||||
expect(now.setLocale).toHaveBeenCalledWith('en-us');
|
||||
});
|
||||
|
||||
it('does not set locale if it is not specified', () => {
|
||||
// Given
|
||||
const characteristics = TimeService.TimeService.emptyCharacteristics();
|
||||
const service = new TimeService.TimeService({
|
||||
instance: 'testing',
|
||||
characteristics: characteristics,
|
||||
});
|
||||
|
||||
// When
|
||||
service.payloadFromDateTime(now);
|
||||
|
||||
// Then
|
||||
expect(now.setLocale).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('uses timeFormat to format the time string if it is specified', () => {
|
||||
// Given
|
||||
const characteristics = TimeService.TimeService.emptyCharacteristics();
|
||||
characteristics.timeFormat = 'hh:mm a';
|
||||
const service = new TimeService.TimeService({
|
||||
instance: 'testing',
|
||||
characteristics: characteristics,
|
||||
});
|
||||
|
||||
// When
|
||||
const result = service.payloadFromDateTime(now);
|
||||
|
||||
// Then
|
||||
expect(result.data.time).toEqual('Formatted Time String');
|
||||
expect(now.toFormat).toHaveBeenCalledWith('hh:mm a');
|
||||
expect(now.toLocaleString).not.toHaveBeenCalledWith(
|
||||
DateTime.TIME_SIMPLE
|
||||
);
|
||||
});
|
||||
|
||||
it('uses default time format to format the time string if timeFormat is not specified', () => {
|
||||
// Given
|
||||
const characteristics = TimeService.TimeService.emptyCharacteristics();
|
||||
const service = new TimeService.TimeService({
|
||||
instance: 'testing',
|
||||
characteristics: characteristics,
|
||||
});
|
||||
|
||||
// When
|
||||
const result = service.payloadFromDateTime(now);
|
||||
|
||||
// Then
|
||||
expect(result.data.time).toEqual('Simple Time String');
|
||||
expect(now.toFormat).not.toHaveBeenCalled();
|
||||
expect(now.toLocaleString).toHaveBeenCalledWith(DateTime.TIME_SIMPLE);
|
||||
});
|
||||
|
||||
it('uses default date format to format the date string if dateFormat is not specified', () => {
|
||||
// Given
|
||||
const characteristics = TimeService.TimeService.emptyCharacteristics();
|
||||
characteristics.dateFormat = '';
|
||||
const service = new TimeService.TimeService({
|
||||
instance: 'testing',
|
||||
characteristics: characteristics,
|
||||
});
|
||||
|
||||
// When
|
||||
const result = service.payloadFromDateTime(now);
|
||||
|
||||
// Then
|
||||
expect(result.data.date).toEqual('Huge Date String');
|
||||
expect(now.toLocaleString).toHaveBeenCalledWith(DateTime.DATE_HUGE);
|
||||
});
|
||||
|
||||
it('uses dateFormat to format the date string if it is specified', () => {
|
||||
// Given
|
||||
const characteristics = TimeService.TimeService.emptyCharacteristics();
|
||||
characteristics.dateFormat = 'DATE_FULL';
|
||||
const service = new TimeService.TimeService({
|
||||
instance: 'testing',
|
||||
characteristics: characteristics,
|
||||
});
|
||||
|
||||
// When
|
||||
const result = service.payloadFromDateTime(now);
|
||||
|
||||
// Then
|
||||
expect(result.data.date).toEqual('Full Date String');
|
||||
expect(now.toLocaleString).toHaveBeenCalledWith(DateTime.DATE_FULL);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onClockTick', () => {
|
||||
it('notfies subscribers with payload', () => {
|
||||
// Given
|
||||
const characteristics = TimeService.TimeService.emptyCharacteristics();
|
||||
const fakePayload = {
|
||||
data: {
|
||||
date: '1987-10-03',
|
||||
time: '08:00 AM',
|
||||
},
|
||||
};
|
||||
const service = new TimeService.TimeService({
|
||||
instance: 'testing',
|
||||
characteristics: characteristics,
|
||||
});
|
||||
spyOn(service, 'payloadFromDateTime').and.returnValue(fakePayload);
|
||||
spyOn(service, 'notify');
|
||||
|
||||
// When
|
||||
service.onClockTick(fakeClock);
|
||||
|
||||
// Then
|
||||
expect(service.notify).toHaveBeenCalledWith(fakePayload);
|
||||
expect(service.payloadFromDateTime).toHaveBeenCalledWith(
|
||||
fakeClock.now
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('initialState', () => {
|
||||
it('returns payload from the current timestamp', () => {
|
||||
// Given
|
||||
const characteristics = TimeService.TimeService.emptyCharacteristics();
|
||||
const fakePayload = {
|
||||
data: {
|
||||
date: '1987-10-03',
|
||||
time: '08:00 AM',
|
||||
},
|
||||
};
|
||||
const service = new TimeService.TimeService({
|
||||
instance: 'testing',
|
||||
characteristics: characteristics,
|
||||
});
|
||||
spyOn(service, 'payloadFromDateTime').and.returnValue(fakePayload);
|
||||
|
||||
// When
|
||||
const result = service.initialState();
|
||||
|
||||
// Then
|
||||
expect(result).toEqual(fakePayload);
|
||||
expect(service.payloadFromDateTime).toHaveBeenCalledWith(
|
||||
fakeClock.now
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setCharacteristics', () => {
|
||||
it('triggers the onClockTick to update', () => {
|
||||
// Given
|
||||
const characteristics = TimeService.TimeService.emptyCharacteristics();
|
||||
const newCharacteristics = {
|
||||
...characteristics,
|
||||
locale: 'en-us',
|
||||
};
|
||||
const service = new TimeService.TimeService({
|
||||
instance: 'testing',
|
||||
characteristics: characteristics,
|
||||
});
|
||||
spyOn(service, 'onClockTick');
|
||||
|
||||
// When
|
||||
service.setCharacteristics(newCharacteristics);
|
||||
|
||||
// Then
|
||||
expect(service.characteristics).toEqual(newCharacteristics);
|
||||
expect(service.onClockTick).toHaveBeenCalledWith(fakeClock);
|
||||
});
|
||||
});
|
||||
|
||||
describe('start', () => {
|
||||
it('adds event listener for the clock tick event', async () => {
|
||||
// Given
|
||||
const characteristics = TimeService.TimeService.emptyCharacteristics();
|
||||
const service = new TimeService.TimeService({
|
||||
instance: 'testing',
|
||||
characteristics: characteristics,
|
||||
});
|
||||
|
||||
// When
|
||||
await service.start();
|
||||
|
||||
// Then
|
||||
expect(fakeClock.addEventListener).toHaveBeenCalledWith(
|
||||
'tick', service.onClockTick
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('start', () => {
|
||||
it('removes event listener for the clock tick event', async () => {
|
||||
// Given
|
||||
const characteristics = TimeService.TimeService.emptyCharacteristics();
|
||||
const service = new TimeService.TimeService({
|
||||
instance: 'testing',
|
||||
characteristics: characteristics,
|
||||
});
|
||||
|
||||
// When
|
||||
await service.stop();
|
||||
|
||||
// Then
|
||||
expect(fakeClock.removeEventListener).toHaveBeenCalledWith(
|
||||
'tick', service.onClockTick
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
56
packages/homehub_app/tests/services/uptime.spec.js
Normal file
56
packages/homehub_app/tests/services/uptime.spec.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import * as HomeHubCore from '@bthlabs/homehub-core';
|
||||
|
||||
import * as UptimeService from 'src/services/uptime';
|
||||
|
||||
describe('src/services/uptime', () => {
|
||||
describe('UptimeService', () => {
|
||||
let fakeResult = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeResult = {
|
||||
data: 123,
|
||||
};
|
||||
|
||||
spyOn(HomeHubCore.API.Services, 'start').and.returnValue(fakeResult);
|
||||
spyOn(HomeHubCore.API.Services, 'stop').and.returnValue('ok');
|
||||
});
|
||||
|
||||
describe('start', () => {
|
||||
it('notifies subscribers with the result of start service API call', async () => {
|
||||
// Given
|
||||
const service = new UptimeService.UptimeService({
|
||||
instance: 'testing',
|
||||
characteristics: {},
|
||||
});
|
||||
spyOn(service, 'notify');
|
||||
|
||||
// When
|
||||
await service.start();
|
||||
|
||||
// Then
|
||||
expect(HomeHubCore.API.Services.start).toHaveBeenCalledWith(
|
||||
service.kind, 'testing', {}
|
||||
);
|
||||
expect(service.notify).toHaveBeenCalledWith(fakeResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('stop', () => {
|
||||
it('calls the stop service API method', async () => {
|
||||
// Given
|
||||
const service = new UptimeService.UptimeService({
|
||||
instance: 'testing',
|
||||
characteristics: {},
|
||||
});
|
||||
|
||||
// When
|
||||
await service.stop();
|
||||
|
||||
// Then
|
||||
expect(HomeHubCore.API.Services.stop).toHaveBeenCalledWith(
|
||||
service.kind, 'testing'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
578
packages/homehub_app/tests/services/weather.spec.js
Normal file
578
packages/homehub_app/tests/services/weather.spec.js
Normal file
@@ -0,0 +1,578 @@
|
||||
import * as HomeHubCore from '@bthlabs/homehub-core';
|
||||
import noop from 'lodash/noop';
|
||||
import {DateTime} from 'luxon';
|
||||
|
||||
import {Clock} from 'src/lib/clock';
|
||||
import * as WeatherService from 'src/services/weather';
|
||||
|
||||
import {WeatherFactory} from 'tests/__fixtures__/weather';
|
||||
|
||||
describe('src/services/weather', () => {
|
||||
describe('WeatherService', () => {
|
||||
let fakeCharacteristics = null;
|
||||
let fakeResult = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeCharacteristics = {
|
||||
city: 'Wroclaw,PL',
|
||||
units: 'metric',
|
||||
};
|
||||
fakeResult = {
|
||||
data: WeatherFactory(),
|
||||
};
|
||||
|
||||
spyOn(HomeHubCore.API.Services, 'start').and.returnValue(fakeResult);
|
||||
spyOn(HomeHubCore.API.Services, 'stop').and.returnValue('ok');
|
||||
});
|
||||
|
||||
describe('emptyCharacteristics', () => {
|
||||
it('returns empty characteristics', () => {
|
||||
// Given
|
||||
const result = WeatherService.WeatherService.emptyCharacteristics();
|
||||
|
||||
// Then
|
||||
expect(result).toEqual({
|
||||
city: '',
|
||||
units: 'metric',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('start', () => {
|
||||
it('notifies subscribers with the result of start service API call', async () => {
|
||||
// Given
|
||||
const service = new WeatherService.WeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
spyOn(service, 'notify');
|
||||
|
||||
// When
|
||||
await service.start();
|
||||
|
||||
// Then
|
||||
expect(HomeHubCore.API.Services.start).toHaveBeenCalledWith(
|
||||
service.kind, 'testing', fakeCharacteristics
|
||||
);
|
||||
expect(service.notify).toHaveBeenCalledWith(fakeResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('stop', () => {
|
||||
it('calls the stop service API method', async () => {
|
||||
// Given
|
||||
const service = new WeatherService.WeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
|
||||
// When
|
||||
await service.stop();
|
||||
|
||||
// Then
|
||||
expect(HomeHubCore.API.Services.stop).toHaveBeenCalledWith(
|
||||
service.kind, 'testing'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setCharacteristics', () => {
|
||||
it('updates the service characteristics', () => {
|
||||
// Given
|
||||
const newCharacteristics = {
|
||||
city: 'Poznan,PL',
|
||||
units: 'imperial',
|
||||
};
|
||||
const service = new WeatherService.WeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
spyOn(service, 'restart');
|
||||
|
||||
// When
|
||||
service.setCharacteristics(newCharacteristics);
|
||||
|
||||
// Then
|
||||
expect(service.characteristics).toEqual(newCharacteristics);
|
||||
});
|
||||
|
||||
it('restarts the service if city characteristic changed', () => {
|
||||
// Given
|
||||
const newCharacteristics = {
|
||||
...fakeCharacteristics,
|
||||
city: 'Poznan,PL',
|
||||
};
|
||||
const service = new WeatherService.WeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
spyOn(service, 'restart');
|
||||
|
||||
// When
|
||||
service.setCharacteristics(newCharacteristics);
|
||||
|
||||
// Then
|
||||
expect(service.restart).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('restarts the service if units characteristic changed', () => {
|
||||
// Given
|
||||
const newCharacteristics = {
|
||||
...fakeCharacteristics,
|
||||
units: 'imperial',
|
||||
};
|
||||
const service = new WeatherService.WeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
spyOn(service, 'restart');
|
||||
|
||||
// When
|
||||
service.setCharacteristics(newCharacteristics);
|
||||
|
||||
// Then
|
||||
expect(service.restart).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('OfflineWeatherService', () => {
|
||||
let fakeCharacteristics = null;
|
||||
let fakeClock = null;
|
||||
let fakeDateTime = null;
|
||||
let fakeResult = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeCharacteristics = {
|
||||
city: 'Wroclaw,PL',
|
||||
units: 'metric',
|
||||
apiKey: 'API_KEY',
|
||||
};
|
||||
|
||||
fakeClock = jasmine.createSpyObj(
|
||||
'FakeClock', ['addEventListener', 'removeEventListener']
|
||||
);
|
||||
fakeClock.now = DateTime.local(1987, 10, 3, 8, 0, 0);
|
||||
spyOn(Clock, 'instance').and.returnValue(fakeClock);
|
||||
|
||||
fakeDateTime = DateTime.local(1987, 10, 3, 8, 0, 0);
|
||||
spyOn(DateTime, 'fromSeconds').and.returnValue(fakeDateTime);
|
||||
|
||||
fakeResult = {
|
||||
data: WeatherFactory(),
|
||||
};
|
||||
|
||||
spyOn(document, 'createElement');
|
||||
spyOn(document.body, 'appendChild');
|
||||
spyOn(document.body, 'removeChild');
|
||||
spyOn(HomeHubCore.LocalStorage, 'getItem').and.returnValue(null);
|
||||
spyOn(HomeHubCore.LocalStorage, 'setItem');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
const kind = WeatherService.OfflineWeatherService.kind;
|
||||
const callbackKey = `${kind}_testing_callback`;
|
||||
window[callbackKey] = undefined;
|
||||
});
|
||||
|
||||
it('defines availability properties', () => {
|
||||
// Then
|
||||
expect(WeatherService.OfflineWeatherService.availableOnline).toBe(false);
|
||||
expect(WeatherService.OfflineWeatherService.availableOffline).toBe(true);
|
||||
});
|
||||
|
||||
describe('emptyCharacteristics', () => {
|
||||
it('returns empty characteristics', () => {
|
||||
// Given
|
||||
const result = WeatherService.OfflineWeatherService.emptyCharacteristics();
|
||||
|
||||
// Then
|
||||
expect(result).toEqual({
|
||||
city: '',
|
||||
units: 'metric',
|
||||
apiKey: '',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('initializes an instance', () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'this-should-be-an-uuid4',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(service.instanceKey).toEqual(
|
||||
`${service.kind}_this_should_be_an_uuid4`
|
||||
);
|
||||
expect(service.callbackKey).toEqual(
|
||||
`${service.kind}_this_should_be_an_uuid4_callback`
|
||||
);
|
||||
expect(service.lastScriptElement).toBe(null);
|
||||
expect(service.lastData).toBe(null);
|
||||
expect(service.lastRefreshDt).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('start', () => {
|
||||
it('installs the JSONP callback', async () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
spyOn(service, 'refresh');
|
||||
|
||||
// When
|
||||
await service.start();
|
||||
|
||||
// Then
|
||||
expect(window[service.callbackKey]).toEqual(service.onWeatherCallback);
|
||||
});
|
||||
|
||||
it('does not initialize the last data if it is missing from local storage', async () => {
|
||||
// Given
|
||||
HomeHubCore.LocalStorage.getItem.and.returnValue(null);
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
spyOn(service, 'refresh');
|
||||
|
||||
// When
|
||||
await service.start();
|
||||
|
||||
// Then
|
||||
expect(service.lastData).toBe(null);
|
||||
expect(service.lastRefreshDt).toBe(null);
|
||||
expect(HomeHubCore.LocalStorage.getItem).toHaveBeenCalledWith(
|
||||
`${service.instanceKey}_lastData`
|
||||
);
|
||||
});
|
||||
|
||||
it('initializes last data if it is present in local storage', async () => {
|
||||
// Given
|
||||
HomeHubCore.LocalStorage.getItem.and.returnValue(fakeResult);
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
spyOn(service, 'refresh');
|
||||
|
||||
// When
|
||||
await service.start();
|
||||
|
||||
// Then
|
||||
expect(service.lastData).toEqual(fakeResult);
|
||||
expect(service.lastRefreshDt).toEqual(fakeDateTime);
|
||||
expect(DateTime.fromSeconds).toHaveBeenCalledWith(fakeResult.dt);
|
||||
});
|
||||
|
||||
it('triggers a refresh', async () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
spyOn(service, 'refresh');
|
||||
|
||||
// When
|
||||
await service.start();
|
||||
|
||||
// Then
|
||||
expect(service.refresh).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('adds event listener for the clock tick event', async () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
spyOn(service, 'refresh');
|
||||
|
||||
// When
|
||||
await service.start();
|
||||
|
||||
// Then
|
||||
expect(Clock.instance().addEventListener).toHaveBeenCalledWith(
|
||||
'tick', service.onClockTick
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('stop', () => {
|
||||
it('replaces the JSONP callback with a noop', async () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
|
||||
// When
|
||||
await service.stop();
|
||||
|
||||
// Then
|
||||
expect(window[service.callbackKey]).toEqual(noop);
|
||||
});
|
||||
|
||||
it('removes event listener for the clock tick event', async () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
|
||||
// When
|
||||
await service.stop();
|
||||
|
||||
// Then
|
||||
expect(Clock.instance().removeEventListener).toHaveBeenCalledWith(
|
||||
'tick', service.onClockTick
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('initialState', () => {
|
||||
it('returns the initial state', () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
|
||||
// When
|
||||
const result = service.initialState();
|
||||
|
||||
// Then
|
||||
expect(result).toEqual({
|
||||
error: null,
|
||||
data: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns state with last data if it is present', () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
service.lastData = fakeResult;
|
||||
|
||||
// When
|
||||
const result = service.initialState();
|
||||
|
||||
// Then
|
||||
expect(result).toEqual({
|
||||
error: null,
|
||||
data: fakeResult,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('onClockTick', () => {
|
||||
it('does not request a refresh when it should not refresh', () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
spyOn(service, 'shouldRefresh').and.returnValue(false);
|
||||
spyOn(service, 'refresh');
|
||||
|
||||
// When
|
||||
service.onClockTick();
|
||||
|
||||
// Then
|
||||
expect(service.refresh).not.toHaveBeenCalled();
|
||||
expect(service.shouldRefresh).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('requests a refresh when it should refresh', () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
spyOn(service, 'shouldRefresh').and.returnValue(true);
|
||||
spyOn(service, 'refresh');
|
||||
|
||||
// When
|
||||
service.onClockTick();
|
||||
|
||||
// Then
|
||||
expect(service.refresh).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldRefresh', () => {
|
||||
it('returns true if last data is null', () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
|
||||
// When
|
||||
const result = service.shouldRefresh();
|
||||
|
||||
// Then
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true if last refresh was more than 600 secs ago', () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
service.lastData = fakeResult;
|
||||
service.lastRefreshDt = DateTime.local(1987, 10, 3, 7, 50, 0);
|
||||
|
||||
// When
|
||||
const result = service.shouldRefresh();
|
||||
|
||||
// Then
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false if last refresh was less than 600 secs ago', () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
service.lastData = fakeResult;
|
||||
service.lastRefreshDt = DateTime.local(1987, 10, 3, 7, 50, 1);
|
||||
|
||||
// When
|
||||
const result = service.shouldRefresh();
|
||||
|
||||
// Then
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onWeatherCallback', () => {
|
||||
it('removes the last script element', () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
service.lastScriptElement = jasmine.createSpy();
|
||||
spyOn(service, 'notify');
|
||||
|
||||
// When
|
||||
service.onWeatherCallback(fakeResult);
|
||||
|
||||
// Then
|
||||
expect(document.body.removeChild).toHaveBeenCalledWith(
|
||||
service.lastScriptElement
|
||||
);
|
||||
});
|
||||
|
||||
it('stores the data in local storage and instance', () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
service.lastScriptElement = jasmine.createSpy();
|
||||
spyOn(service, 'notify');
|
||||
|
||||
// When
|
||||
service.onWeatherCallback(fakeResult);
|
||||
|
||||
// Then
|
||||
expect(service.lastData).toEqual(fakeResult);
|
||||
expect(service.lastRefreshDt).toEqual(fakeClock.now);
|
||||
expect(HomeHubCore.LocalStorage.setItem).toHaveBeenCalledWith(
|
||||
`${service.instanceKey}_lastData`, fakeResult
|
||||
);
|
||||
});
|
||||
|
||||
it('notifies subscribers with the loaded data', () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
service.lastScriptElement = jasmine.createSpy();
|
||||
spyOn(service, 'notify');
|
||||
|
||||
// When
|
||||
service.onWeatherCallback(fakeResult);
|
||||
|
||||
// Then
|
||||
expect(service.notify).toHaveBeenCalledWith({
|
||||
error: null,
|
||||
data: fakeResult,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('refresh', () => {
|
||||
it('is a noop when it should not refresh', () => {
|
||||
// Given
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
spyOn(service, 'shouldRefresh').and.returnValue(false);
|
||||
|
||||
// When
|
||||
service.refresh();
|
||||
|
||||
// Then
|
||||
expect(document.createElement).not.toHaveBeenCalled();
|
||||
expect(document.body.appendChild).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls the API via JSONP when it should refresh', () => {
|
||||
// Given
|
||||
const fakeScriptElement = jasmine.createSpy();
|
||||
document.createElement.and.returnValue(fakeScriptElement);
|
||||
|
||||
const service = new WeatherService.OfflineWeatherService({
|
||||
instance: 'testing',
|
||||
characteristics: fakeCharacteristics,
|
||||
});
|
||||
spyOn(service, 'shouldRefresh').and.returnValue(true);
|
||||
|
||||
// When
|
||||
service.refresh();
|
||||
|
||||
// Then
|
||||
expect(document.createElement).toHaveBeenCalledWith('script');
|
||||
expect(fakeScriptElement.async).toBe(true);
|
||||
expect(fakeScriptElement.src).toBeDefined();
|
||||
expect(fakeScriptElement.type).toEqual('text/javascript');
|
||||
expect(service.lastScriptElement).toEqual(fakeScriptElement);
|
||||
expect(document.body.appendChild).toHaveBeenCalledWith(
|
||||
fakeScriptElement
|
||||
);
|
||||
|
||||
expect(fakeScriptElement.src.startsWith(
|
||||
WeatherService.OfflineWeatherService.apiURL
|
||||
)).toBe(
|
||||
true
|
||||
);
|
||||
|
||||
const scriptSrcURL = new URL(fakeScriptElement.src);
|
||||
const scriptSrcURLParams = {};
|
||||
for (let pair of scriptSrcURL.searchParams.entries()) {
|
||||
scriptSrcURLParams[pair[0]] = pair[1];
|
||||
}
|
||||
expect(scriptSrcURLParams).toEqual({
|
||||
APPID: fakeCharacteristics.apiKey,
|
||||
callback: service.callbackKey,
|
||||
q: fakeCharacteristics.city,
|
||||
units: fakeCharacteristics.units,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
285
packages/homehub_app/tests/widgets/TimeWidgetView.spec.js
Normal file
285
packages/homehub_app/tests/widgets/TimeWidgetView.spec.js
Normal file
@@ -0,0 +1,285 @@
|
||||
import {Widget} from '@bthlabs/homehub-components';
|
||||
import {ServiceState} from '@bthlabs/homehub-core';
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {Form} from 'src/components';
|
||||
import * as TimeWidgetView from 'src/widgets/TimeWidgetView';
|
||||
|
||||
import {FakeService} from 'tests/__fixtures__/services';
|
||||
import {SettingsFactory} from 'tests/__fixtures__/settings';
|
||||
|
||||
describe('src/widgets/TimeWidgetView', () => {
|
||||
describe('TimeWidgetSettingsView', () => {
|
||||
let fakeNextCharacteristics = null;
|
||||
let fakeSettings = null;
|
||||
let mockSetNextCharacteristics = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeNextCharacteristics = {
|
||||
locale: '',
|
||||
timeFormat: '',
|
||||
dateFormat: 'DATE_HUGE',
|
||||
};
|
||||
fakeSettings = SettingsFactory();
|
||||
mockSetNextCharacteristics = jasmine.createSpy();
|
||||
});
|
||||
|
||||
it('calls the setNextCharacteristics callback when locale input changes', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<TimeWidgetView.TimeWidgetSettingsView
|
||||
nextCharacteristics={fakeNextCharacteristics}
|
||||
settings={fakeSettings}
|
||||
setNextCharacteristics={mockSetNextCharacteristics}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Form.Control).at(0).simulate('change', {
|
||||
target: {value: 'en-gb'},
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(mockSetNextCharacteristics).toHaveBeenCalledWith({
|
||||
...fakeNextCharacteristics,
|
||||
locale: 'en-gb',
|
||||
});
|
||||
});
|
||||
|
||||
it('calls the setNextCharacteristics callback when time format input changes', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<TimeWidgetView.TimeWidgetSettingsView
|
||||
nextCharacteristics={fakeNextCharacteristics}
|
||||
settings={fakeSettings}
|
||||
setNextCharacteristics={mockSetNextCharacteristics}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Form.Control).at(1).simulate('change', {
|
||||
target: {value: 'hh:mm a'},
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(mockSetNextCharacteristics).toHaveBeenCalledWith({
|
||||
...fakeNextCharacteristics,
|
||||
timeFormat: 'hh:mm a',
|
||||
});
|
||||
});
|
||||
|
||||
it('calls the setNextCharacteristics callback when date format input changes', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<TimeWidgetView.TimeWidgetSettingsView
|
||||
nextCharacteristics={fakeNextCharacteristics}
|
||||
settings={fakeSettings}
|
||||
setNextCharacteristics={mockSetNextCharacteristics}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Form.Control).at(2).simulate('change', {
|
||||
target: {value: 'DATE_FULL'},
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(mockSetNextCharacteristics).toHaveBeenCalledWith({
|
||||
...fakeNextCharacteristics,
|
||||
dateFormat: 'DATE_FULL',
|
||||
});
|
||||
});
|
||||
|
||||
it('configures and renders the locale input', () => {
|
||||
// Given
|
||||
fakeNextCharacteristics = {
|
||||
...fakeNextCharacteristics,
|
||||
locale: 'en-gb',
|
||||
timeFormat: 'hh:mm a',
|
||||
dateFormat: 'DATE_FULL',
|
||||
};
|
||||
const component = shallow(
|
||||
<TimeWidgetView.TimeWidgetSettingsView
|
||||
nextCharacteristics={fakeNextCharacteristics}
|
||||
settings={fakeSettings}
|
||||
setNextCharacteristics={mockSetNextCharacteristics}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const input = component.find(Form.Control).at(0);
|
||||
|
||||
// Then
|
||||
expect(input.exists()).toBe(true);
|
||||
expect(input.prop('value')).toEqual(fakeNextCharacteristics.locale);
|
||||
|
||||
const options = input.find('option');
|
||||
expect(options.length).toEqual(3);
|
||||
expect(options.at(1).prop('value')).toEqual('en-us');
|
||||
});
|
||||
|
||||
it('configures and renders the time format input', () => {
|
||||
// Given
|
||||
fakeNextCharacteristics = {
|
||||
...fakeNextCharacteristics,
|
||||
locale: 'en-gb',
|
||||
timeFormat: 'hh:mm a',
|
||||
dateFormat: 'DATE_FULL',
|
||||
};
|
||||
const component = shallow(
|
||||
<TimeWidgetView.TimeWidgetSettingsView
|
||||
nextCharacteristics={fakeNextCharacteristics}
|
||||
settings={fakeSettings}
|
||||
setNextCharacteristics={mockSetNextCharacteristics}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const input = component.find(Form.Control).at(1);
|
||||
|
||||
// Then
|
||||
expect(input.exists()).toBe(true);
|
||||
expect(input.prop('value')).toEqual(fakeNextCharacteristics.timeFormat);
|
||||
|
||||
const options = input.find('option');
|
||||
expect(options.length).toEqual(3);
|
||||
expect(options.at(1).prop('value')).toEqual('hh:mm a');
|
||||
});
|
||||
|
||||
it('configures and renders the date format input', () => {
|
||||
// Given
|
||||
fakeNextCharacteristics = {
|
||||
...fakeNextCharacteristics,
|
||||
locale: 'en-gb',
|
||||
timeFormat: 'hh:mm a',
|
||||
dateFormat: 'DATE_FULL',
|
||||
};
|
||||
const component = shallow(
|
||||
<TimeWidgetView.TimeWidgetSettingsView
|
||||
nextCharacteristics={fakeNextCharacteristics}
|
||||
settings={fakeSettings}
|
||||
setNextCharacteristics={mockSetNextCharacteristics}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const input = component.find(Form.Control).at(2);
|
||||
|
||||
// Then
|
||||
expect(input.exists()).toBe(true);
|
||||
expect(input.prop('value')).toEqual(fakeNextCharacteristics.dateFormat);
|
||||
|
||||
const options = input.find('option');
|
||||
expect(options.length).toEqual(3);
|
||||
expect(options.at(0).prop('value')).toEqual('DATE_HUGE');
|
||||
});
|
||||
});
|
||||
|
||||
describe('TimeWidgetView', () => {
|
||||
let fakeAppearance = null;
|
||||
let fakeService = null;
|
||||
let fakeServiceState = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeAppearance = {color: 'red'};
|
||||
fakeService = new FakeService({
|
||||
instance: 'testing',
|
||||
layout: {x: 0, y: 0, h: 1, w: 1},
|
||||
});
|
||||
fakeServiceState = new ServiceState({
|
||||
data: {
|
||||
time: '08:00 AM',
|
||||
date: '3 October 1987',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('defines the widget attributes', () => {
|
||||
// Then
|
||||
expect(TimeWidgetView.TimeWidgetView.defaultLayout).toEqual({
|
||||
h: jasmine.any(Number),
|
||||
w: jasmine.any(Number),
|
||||
});
|
||||
expect(TimeWidgetView.TimeWidgetView.icon).toBeDefined();
|
||||
expect(TimeWidgetView.TimeWidgetView.layoutConstraints).toEqual({
|
||||
minH: jasmine.any(Number),
|
||||
minW: jasmine.any(Number),
|
||||
});
|
||||
expect(TimeWidgetView.TimeWidgetView.settingsView).toEqual(
|
||||
TimeWidgetView.TimeWidgetSettingsView
|
||||
);
|
||||
expect(TimeWidgetView.TimeWidgetView.title).toEqual('Time');
|
||||
});
|
||||
|
||||
it('renders empty when service state is null', () => {
|
||||
// Given
|
||||
fakeServiceState = null;
|
||||
const component = shallow(
|
||||
<TimeWidgetView.TimeWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.isEmptyRender()).toBe(true);
|
||||
});
|
||||
|
||||
it('configures and renders the Widget', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<TimeWidgetView.TimeWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const widget = component.find(Widget).at(0);
|
||||
|
||||
// Then
|
||||
expect(widget.exists()).toBe(true);
|
||||
expect(widget.prop('appearance')).toEqual(fakeAppearance);
|
||||
expect(widget.prop('instance')).toEqual(fakeService.instance);
|
||||
expect(widget.prop('kind')).toEqual(fakeService.kind);
|
||||
expect(widget.prop('layout')).toEqual(fakeService.layout);
|
||||
});
|
||||
|
||||
it('renders the time string', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<TimeWidgetView.TimeWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const h4 = component.find('h4').at(0);
|
||||
|
||||
// Then
|
||||
expect(h4.text()).toEqual(fakeServiceState.data().time);
|
||||
});
|
||||
|
||||
it('renders the date string', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<TimeWidgetView.TimeWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const h6 = component.find('h6').at(0);
|
||||
|
||||
// Then
|
||||
expect(h6.text()).toEqual(fakeServiceState.data().date);
|
||||
});
|
||||
});
|
||||
});
|
||||
226
packages/homehub_app/tests/widgets/UptimeWidgetView.spec.js
Executable file
226
packages/homehub_app/tests/widgets/UptimeWidgetView.spec.js
Executable file
@@ -0,0 +1,226 @@
|
||||
import {Widget} from '@bthlabs/homehub-components';
|
||||
import {ServiceState} from '@bthlabs/homehub-core';
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import * as UptimeWidgetView from 'src/widgets/UptimeWidgetView';
|
||||
|
||||
import {FakeService} from 'tests/__fixtures__/services';
|
||||
|
||||
describe('src/widgets/UptimeWidgetView', () => {
|
||||
describe('UptimeWidgetView', () => {
|
||||
let fakeAppearance = null;
|
||||
let fakeService = null;
|
||||
let fakeServiceState = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeAppearance = {color: 'red'};
|
||||
fakeService = new FakeService({
|
||||
instance: 'testing',
|
||||
layout: {x: 0, y: 0, h: 1, w: 1},
|
||||
});
|
||||
fakeServiceState = new ServiceState({
|
||||
data: 123,
|
||||
});
|
||||
});
|
||||
|
||||
it('defines the widget attributes', () => {
|
||||
// Then
|
||||
expect(UptimeWidgetView.UptimeWidgetView.defaultLayout).toEqual({
|
||||
h: jasmine.any(Number),
|
||||
w: jasmine.any(Number),
|
||||
});
|
||||
expect(UptimeWidgetView.UptimeWidgetView.icon).toBe(null);
|
||||
expect(UptimeWidgetView.UptimeWidgetView.layoutConstraints).toEqual({
|
||||
minH: jasmine.any(Number),
|
||||
minW: jasmine.any(Number),
|
||||
});
|
||||
expect(UptimeWidgetView.UptimeWidgetView.settingsView).toEqual(
|
||||
UptimeWidgetView.TimeWidgetSettingsView
|
||||
);
|
||||
expect(UptimeWidgetView.UptimeWidgetView.title).toEqual('Uptime');
|
||||
});
|
||||
|
||||
it('renders empty when service state is null', () => {
|
||||
// Given
|
||||
fakeServiceState = null;
|
||||
const component = shallow(
|
||||
<UptimeWidgetView.UptimeWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.isEmptyRender()).toBe(true);
|
||||
});
|
||||
|
||||
it('configures and renders the Widget', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<UptimeWidgetView.UptimeWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const widget = component.find(Widget).at(0);
|
||||
|
||||
// Then
|
||||
expect(widget.exists()).toBe(true);
|
||||
expect(widget.prop('appearance')).toEqual(fakeAppearance);
|
||||
expect(widget.prop('instance')).toEqual(fakeService.instance);
|
||||
expect(widget.prop('kind')).toEqual(fakeService.kind);
|
||||
expect(widget.prop('layout')).toEqual(fakeService.layout);
|
||||
});
|
||||
|
||||
it('rendes the formatted uptime interval for 1 day', () => {
|
||||
// Given
|
||||
fakeServiceState = fakeServiceState.update({data: 86401});
|
||||
const component = shallow(
|
||||
<UptimeWidgetView.UptimeWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const h4 = component.find('h4').at(0);
|
||||
|
||||
// Then
|
||||
expect(h4.text()).toEqual('1 day');
|
||||
});
|
||||
|
||||
it('rendes the formatted uptime interval for 2 days', () => {
|
||||
// Given
|
||||
fakeServiceState = fakeServiceState.update({data: 86401 * 2});
|
||||
const component = shallow(
|
||||
<UptimeWidgetView.UptimeWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const h4 = component.find('h4').at(0);
|
||||
|
||||
// Then
|
||||
expect(h4.text()).toEqual('2 days');
|
||||
});
|
||||
|
||||
it('rendes the formatted uptime interval for 1 hour', () => {
|
||||
// Given
|
||||
fakeServiceState = fakeServiceState.update({data: 3601});
|
||||
const component = shallow(
|
||||
<UptimeWidgetView.UptimeWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const h4 = component.find('h4').at(0);
|
||||
|
||||
// Then
|
||||
expect(h4.text()).toEqual('1 hour');
|
||||
});
|
||||
|
||||
it('rendes the formatted uptime interval for 2 hours', () => {
|
||||
// Given
|
||||
fakeServiceState = fakeServiceState.update({data: 3601 * 2});
|
||||
const component = shallow(
|
||||
<UptimeWidgetView.UptimeWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const h4 = component.find('h4').at(0);
|
||||
|
||||
// Then
|
||||
expect(h4.text()).toEqual('2 hours');
|
||||
});
|
||||
|
||||
it('rendes the formatted uptime interval for 1 minute', () => {
|
||||
// Given
|
||||
fakeServiceState = fakeServiceState.update({data: 61});
|
||||
const component = shallow(
|
||||
<UptimeWidgetView.UptimeWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const h4 = component.find('h4').at(0);
|
||||
|
||||
// Then
|
||||
expect(h4.text()).toEqual('1 minute');
|
||||
});
|
||||
|
||||
it('rendes the formatted uptime interval for 2 minutes', () => {
|
||||
// Given
|
||||
fakeServiceState = fakeServiceState.update({data: 61 * 2});
|
||||
const component = shallow(
|
||||
<UptimeWidgetView.UptimeWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const h4 = component.find('h4').at(0);
|
||||
|
||||
// Then
|
||||
expect(h4.text()).toEqual('2 minutes');
|
||||
});
|
||||
|
||||
it('rendes the formatted uptime interval for 1 second', () => {
|
||||
// Given
|
||||
fakeServiceState = fakeServiceState.update({data: 1});
|
||||
const component = shallow(
|
||||
<UptimeWidgetView.UptimeWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const h4 = component.find('h4').at(0);
|
||||
|
||||
// Then
|
||||
expect(h4.text()).toEqual('1 second');
|
||||
});
|
||||
|
||||
it('rendes the formatted uptime interval for 2 seconds', () => {
|
||||
// Given
|
||||
fakeServiceState = fakeServiceState.update({data: 2});
|
||||
const component = shallow(
|
||||
<UptimeWidgetView.UptimeWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const h4 = component.find('h4').at(0);
|
||||
|
||||
// Then
|
||||
expect(h4.text()).toEqual('2 seconds');
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
429
packages/homehub_app/tests/widgets/WeatherWidgetView.spec.js
Normal file
429
packages/homehub_app/tests/widgets/WeatherWidgetView.spec.js
Normal file
@@ -0,0 +1,429 @@
|
||||
import {FatalError, Widget} from '@bthlabs/homehub-components';
|
||||
import {ServiceState} from '@bthlabs/homehub-core';
|
||||
import {DateTime} from 'luxon';
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {Form} from 'src/components';
|
||||
import * as WeatherWidgetView from 'src/widgets/WeatherWidgetView';
|
||||
|
||||
import {FakeService} from 'tests/__fixtures__/services';
|
||||
import {SettingsFactory} from 'tests/__fixtures__/settings';
|
||||
import {WeatherFactory} from 'tests/__fixtures__/weather';
|
||||
|
||||
describe('src/widgets/WeatherWidgetView', () => {
|
||||
describe('WeatherWidgetSettingsView', () => {
|
||||
let fakeNextCharacteristics = null;
|
||||
let fakeSettings = null;
|
||||
let mockSetNextCharacteristics = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeNextCharacteristics = {
|
||||
city: '',
|
||||
units: '',
|
||||
apiKey: '',
|
||||
};
|
||||
fakeSettings = SettingsFactory();
|
||||
mockSetNextCharacteristics = jasmine.createSpy();
|
||||
});
|
||||
|
||||
it('calls the setNextCharacteristics callback when city input changes', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<WeatherWidgetView.WeatherWidgetSettingsView
|
||||
nextCharacteristics={fakeNextCharacteristics}
|
||||
settings={fakeSettings}
|
||||
setNextCharacteristics={mockSetNextCharacteristics}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Form.Control).at(0).simulate('change', {
|
||||
target: {value: 'Wroclaw,PL'},
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(mockSetNextCharacteristics).toHaveBeenCalledWith({
|
||||
...fakeNextCharacteristics,
|
||||
city: 'Wroclaw,PL',
|
||||
});
|
||||
});
|
||||
|
||||
it('calls the setNextCharacteristics callback when units input changes', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<WeatherWidgetView.WeatherWidgetSettingsView
|
||||
nextCharacteristics={fakeNextCharacteristics}
|
||||
settings={fakeSettings}
|
||||
setNextCharacteristics={mockSetNextCharacteristics}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Form.Control).at(1).simulate('change', {
|
||||
target: {value: 'imperial'},
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(mockSetNextCharacteristics).toHaveBeenCalledWith({
|
||||
...fakeNextCharacteristics,
|
||||
units: 'imperial',
|
||||
});
|
||||
});
|
||||
|
||||
it('calls the setNextCharacteristics callback when API key input changes', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<WeatherWidgetView.WeatherWidgetSettingsView
|
||||
nextCharacteristics={fakeNextCharacteristics}
|
||||
settings={fakeSettings}
|
||||
setNextCharacteristics={mockSetNextCharacteristics}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
component.find(Form.Control).at(2).simulate('change', {
|
||||
target: {value: 'thisisntright'},
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(mockSetNextCharacteristics).toHaveBeenCalledWith({
|
||||
...fakeNextCharacteristics,
|
||||
apiKey: 'thisisntright',
|
||||
});
|
||||
});
|
||||
|
||||
it('renders empty when nextCharacteristics is empty', () => {
|
||||
// Given
|
||||
fakeNextCharacteristics = null;
|
||||
const component = shallow(
|
||||
<WeatherWidgetView.WeatherWidgetSettingsView
|
||||
nextCharacteristics={fakeNextCharacteristics}
|
||||
settings={fakeSettings}
|
||||
setNextCharacteristics={mockSetNextCharacteristics}
|
||||
/>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.isEmptyRender()).toBe(true);
|
||||
});
|
||||
|
||||
it('configures and renders the city input', () => {
|
||||
// Given
|
||||
fakeNextCharacteristics = {
|
||||
...fakeNextCharacteristics,
|
||||
city: 'Wroclaw,PL',
|
||||
units: 'metric',
|
||||
apiKey: 'thisisntright',
|
||||
};
|
||||
const component = shallow(
|
||||
<WeatherWidgetView.WeatherWidgetSettingsView
|
||||
nextCharacteristics={fakeNextCharacteristics}
|
||||
settings={fakeSettings}
|
||||
setNextCharacteristics={mockSetNextCharacteristics}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const input = component.find(Form.Control).at(0);
|
||||
|
||||
// Then
|
||||
expect(input.exists()).toBe(true);
|
||||
expect(input.prop('value')).toEqual(fakeNextCharacteristics.city);
|
||||
});
|
||||
|
||||
it('configures and renders the units input', () => {
|
||||
// Given
|
||||
fakeNextCharacteristics = {
|
||||
...fakeNextCharacteristics,
|
||||
city: 'Wroclaw,PL',
|
||||
units: 'metric',
|
||||
apiKey: 'thisisntright',
|
||||
};
|
||||
const component = shallow(
|
||||
<WeatherWidgetView.WeatherWidgetSettingsView
|
||||
nextCharacteristics={fakeNextCharacteristics}
|
||||
settings={fakeSettings}
|
||||
setNextCharacteristics={mockSetNextCharacteristics}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const input = component.find(Form.Control).at(1);
|
||||
|
||||
// Then
|
||||
expect(input.exists()).toBe(true);
|
||||
expect(input.prop('value')).toEqual(fakeNextCharacteristics.units);
|
||||
});
|
||||
|
||||
it('configures and renders the API key input', () => {
|
||||
// Given
|
||||
fakeNextCharacteristics = {
|
||||
...fakeNextCharacteristics,
|
||||
city: 'Wroclaw,PL',
|
||||
units: 'metric',
|
||||
apiKey: 'thisisntright',
|
||||
};
|
||||
const component = shallow(
|
||||
<WeatherWidgetView.WeatherWidgetSettingsView
|
||||
nextCharacteristics={fakeNextCharacteristics}
|
||||
settings={fakeSettings}
|
||||
setNextCharacteristics={mockSetNextCharacteristics}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const input = component.find(Form.Control).at(2);
|
||||
|
||||
// Then
|
||||
expect(input.exists()).toBe(true);
|
||||
expect(input.prop('value')).toEqual(fakeNextCharacteristics.apiKey);
|
||||
});
|
||||
|
||||
it('does not render the API key input if it is missing in characteristics', () => {
|
||||
// Given
|
||||
fakeNextCharacteristics = {
|
||||
city: 'Wroclaw,PL',
|
||||
units: 'metric',
|
||||
};
|
||||
const component = shallow(
|
||||
<WeatherWidgetView.WeatherWidgetSettingsView
|
||||
nextCharacteristics={fakeNextCharacteristics}
|
||||
settings={fakeSettings}
|
||||
setNextCharacteristics={mockSetNextCharacteristics}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const input = component.find(Form.Control).at(2);
|
||||
|
||||
// Then
|
||||
expect(input.exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('WeatherWidgetView', () => {
|
||||
let fakeAppearance = null;
|
||||
let fakeDateTime = null;
|
||||
let fakeService = null;
|
||||
let fakeServiceState = null;
|
||||
let fakeWeather = null;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeAppearance = {color: 'red'};
|
||||
fakeDateTime = DateTime.local(1987, 10, 3, 8, 0, 0);
|
||||
fakeService = new FakeService({
|
||||
instance: 'testing',
|
||||
layout: {x: 0, y: 0, h: 1, w: 1},
|
||||
});
|
||||
fakeWeather = WeatherFactory();
|
||||
fakeServiceState = new ServiceState({
|
||||
data: fakeWeather,
|
||||
});
|
||||
|
||||
spyOn(DateTime, 'fromSeconds').and.returnValue(fakeDateTime);
|
||||
spyOn(fakeDateTime, 'toLocaleString').and.returnValue(
|
||||
'Formatted DateTime'
|
||||
);
|
||||
});
|
||||
|
||||
it('defines the widget attributes', () => {
|
||||
// Then
|
||||
expect(WeatherWidgetView.WeatherWidgetView.defaultLayout).toEqual({
|
||||
h: jasmine.any(Number),
|
||||
w: jasmine.any(Number),
|
||||
});
|
||||
expect(WeatherWidgetView.WeatherWidgetView.icon).toBeDefined();
|
||||
expect(WeatherWidgetView.WeatherWidgetView.layoutConstraints).toEqual({
|
||||
minH: jasmine.any(Number),
|
||||
minW: jasmine.any(Number),
|
||||
});
|
||||
expect(WeatherWidgetView.WeatherWidgetView.settingsView).toEqual(
|
||||
WeatherWidgetView.WeatherWidgetSettingsView
|
||||
);
|
||||
expect(WeatherWidgetView.WeatherWidgetView.title).toEqual('Weather');
|
||||
});
|
||||
|
||||
it('renders empty when service state is null', () => {
|
||||
// Given
|
||||
fakeServiceState = null;
|
||||
const component = shallow(
|
||||
<WeatherWidgetView.WeatherWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// Then
|
||||
expect(component.isEmptyRender()).toBe(true);
|
||||
});
|
||||
|
||||
it('configures and renders the Widget', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<WeatherWidgetView.WeatherWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const widget = component.find(Widget).at(0);
|
||||
|
||||
// Then
|
||||
expect(widget.exists()).toBe(true);
|
||||
expect(widget.prop('appearance')).toEqual(fakeAppearance);
|
||||
expect(widget.prop('instance')).toEqual(fakeService.instance);
|
||||
expect(widget.prop('kind')).toEqual(fakeService.kind);
|
||||
expect(widget.prop('layout')).toEqual(fakeService.layout);
|
||||
});
|
||||
|
||||
it('renders the loader when state is loading', () => {
|
||||
// Given
|
||||
spyOn(fakeServiceState, 'isLoading').and.returnValue(true);
|
||||
const component = shallow(
|
||||
<WeatherWidgetView.WeatherWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const loader = component.find('Icon[_hint="Loader"]').at(0);
|
||||
|
||||
// Then
|
||||
expect(loader.exists()).toBe(true);
|
||||
expect(loader.prop('icon')).toBeDefined();
|
||||
});
|
||||
|
||||
it('does not render the loader when state is not loading', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<WeatherWidgetView.WeatherWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const loader = component.find('Icon[_hint="Loader"]').at(0);
|
||||
|
||||
// Then
|
||||
expect(loader.exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not compute and render weather info when service state has no data', () => {
|
||||
// Given
|
||||
spyOn(fakeServiceState, 'hasData').and.returnValue(false);
|
||||
const component = shallow(
|
||||
<WeatherWidgetView.WeatherWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const weatherInfo = component.find('div[_hint="WeatherInfo"]').at(0);
|
||||
|
||||
// Then
|
||||
expect(weatherInfo.exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('computes and renders weather info when service state has data', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<WeatherWidgetView.WeatherWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const weatherInfo = component.find('div[_hint="WeatherInfo"]').at(0);
|
||||
|
||||
// Then
|
||||
expect(weatherInfo.exists()).toBe(true);
|
||||
|
||||
const weatherIcon = component.find('Icon[_hint="WeatherIcon"]').at(0);
|
||||
expect(weatherIcon.exists()).toBe(true);
|
||||
expect(weatherIcon.prop('icon')).toBeDefined();
|
||||
|
||||
const weatherName = component.find('h5[_hint="WeatherName"]').at(0);
|
||||
expect(weatherName.exists()).toBe(true);
|
||||
expect(weatherName.text()).toEqual(fakeWeather.name);
|
||||
|
||||
const weatherDescription = component.find('h6[_hint="WeatherDescription"]').at(0);
|
||||
expect(weatherDescription.exists()).toBe(true);
|
||||
expect(weatherDescription.text()).toEqual(
|
||||
fakeWeather.weather[0].description
|
||||
);
|
||||
|
||||
const weatherTemp = component.find('h5[_hint="WeatherTemp"]').at(0);
|
||||
expect(weatherTemp.exists()).toBe(true);
|
||||
expect(weatherTemp.text()).toEqual(
|
||||
`${Math.round(fakeWeather.main.temp)}°`
|
||||
);
|
||||
|
||||
const weatherTempRange = component.find('h6[_hint="WeatherTempRange"]').at(0);
|
||||
expect(weatherTempRange.exists()).toBe(true);
|
||||
expect(weatherTempRange.text()).toContain(
|
||||
`${Math.round(fakeWeather.main.temp_min)}°`
|
||||
);
|
||||
expect(weatherTempRange.text()).toContain(
|
||||
`${Math.round(fakeWeather.main.temp_max)}°`
|
||||
);
|
||||
|
||||
const lastUpdate = component.find('small[_hint="LastUpdate"]').at(0);
|
||||
expect(lastUpdate.exists()).toBe(true);
|
||||
expect(lastUpdate.text()).toContain('Formatted DateTime');
|
||||
|
||||
expect(DateTime.fromSeconds).toHaveBeenCalledWith(fakeWeather.dt);
|
||||
expect(fakeDateTime.toLocaleString).toHaveBeenCalledWith(
|
||||
DateTime.TIME_SIMPLE
|
||||
);
|
||||
});
|
||||
|
||||
it('does not render FatalError if service state has no fatal error', () => {
|
||||
// Given
|
||||
const component = shallow(
|
||||
<WeatherWidgetView.WeatherWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const fatalError = component.find(FatalError).at(0);
|
||||
|
||||
// Then
|
||||
expect(fatalError.exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('configures and renders FatalError if service state has fatal error', () => {
|
||||
// Given
|
||||
spyOn(fakeServiceState, 'hasFatalError').and.returnValue(true);
|
||||
const component = shallow(
|
||||
<WeatherWidgetView.WeatherWidgetView
|
||||
appearance={fakeAppearance}
|
||||
service={fakeService}
|
||||
serviceState={fakeServiceState}
|
||||
/>
|
||||
);
|
||||
|
||||
// When
|
||||
const fatalError = component.find(FatalError).at(0);
|
||||
|
||||
// Then
|
||||
expect(fatalError.exists()).toBe(true);
|
||||
expect(fatalError.prop('service')).toEqual(fakeService);
|
||||
expect(fatalError.prop('serviceState')).toEqual(fakeServiceState);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user