Release 1.3.0

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

View File

@@ -0,0 +1,389 @@
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 'react-bootstrap';
import {IFormicariumService} from 'src/IFormicariumService';
import * as IFormicariumWidgetView from 'src/IFormicariumWidgetView';
import {IFormicariumDataFactory} from 'tests/__fixtures__/iformicarium';
describe('src/widgets/IFormicariumWidgetView', () => {
describe('IFormicariumWidgetSettingsView', () => {
let fakeNextCharacteristics = null;
let mockSetNextCharacteristics = null;
beforeEach(() => {
fakeNextCharacteristics = {
mqtt: '',
name: '',
topic: '',
};
mockSetNextCharacteristics = jasmine.createSpy();
});
it('calls the setNextCharacteristics callback when mqtt input changes', () => {
// Given
const component = shallow(
<IFormicariumWidgetView.IFormicariumWidgetSettingsView
nextCharacteristics={fakeNextCharacteristics}
setNextCharacteristics={mockSetNextCharacteristics}
/>
);
// When
component.find(Form.Control).at(0).simulate('change', {
target: {value: 'thisisntright.local'},
});
// Then
expect(mockSetNextCharacteristics).toHaveBeenCalledWith({
...fakeNextCharacteristics,
mqtt: 'thisisntright.local',
});
});
it('calls the setNextCharacteristics callback when name input changes', () => {
// Given
const component = shallow(
<IFormicariumWidgetView.IFormicariumWidgetSettingsView
nextCharacteristics={fakeNextCharacteristics}
setNextCharacteristics={mockSetNextCharacteristics}
/>
);
// When
component.find(Form.Control).at(1).simulate('change', {
target: {value: 'iformicarium'},
});
// Then
expect(mockSetNextCharacteristics).toHaveBeenCalledWith({
...fakeNextCharacteristics,
name: 'iformicarium',
});
});
it('calls the setNextCharacteristics callback when topic input changes', () => {
// Given
const component = shallow(
<IFormicariumWidgetView.IFormicariumWidgetSettingsView
nextCharacteristics={fakeNextCharacteristics}
setNextCharacteristics={mockSetNextCharacteristics}
/>
);
// When
component.find(Form.Control).at(2).simulate('change', {
target: {value: 'iformicarium'},
});
// Then
expect(mockSetNextCharacteristics).toHaveBeenCalledWith({
...fakeNextCharacteristics,
topic: 'iformicarium',
});
});
it('renders empty when nextCharacteristics is empty', () => {
// Given
fakeNextCharacteristics = null;
const component = shallow(
<IFormicariumWidgetView.IFormicariumWidgetSettingsView
nextCharacteristics={fakeNextCharacteristics}
setNextCharacteristics={mockSetNextCharacteristics}
/>
);
// Then
expect(component.isEmptyRender()).toBe(true);
});
it('configures and renders the city input', () => {
// Given
fakeNextCharacteristics = {
...fakeNextCharacteristics,
mqtt: 'thisisntright.local',
name: 'iformicarium',
topic: 'iformicarium',
};
const component = shallow(
<IFormicariumWidgetView.IFormicariumWidgetSettingsView
nextCharacteristics={fakeNextCharacteristics}
setNextCharacteristics={mockSetNextCharacteristics}
/>
);
// When
const input = component.find(Form.Control).at(0);
// Then
expect(input.exists()).toBe(true);
expect(input.prop('value')).toEqual(fakeNextCharacteristics.mqtt);
});
it('configures and renders the name input', () => {
// Given
fakeNextCharacteristics = {
...fakeNextCharacteristics,
mqtt: 'thisisntright.local',
name: 'iformicarium',
topic: 'iformicarium',
};
const component = shallow(
<IFormicariumWidgetView.IFormicariumWidgetSettingsView
nextCharacteristics={fakeNextCharacteristics}
setNextCharacteristics={mockSetNextCharacteristics}
/>
);
// When
const input = component.find(Form.Control).at(1);
// Then
expect(input.exists()).toBe(true);
expect(input.prop('value')).toEqual(fakeNextCharacteristics.name);
});
it('configures and renders the topic input', () => {
// Given
fakeNextCharacteristics = {
...fakeNextCharacteristics,
mqtt: 'thisisntright.local',
name: 'iformicarium',
topic: 'iformicarium',
};
const component = shallow(
<IFormicariumWidgetView.IFormicariumWidgetSettingsView
nextCharacteristics={fakeNextCharacteristics}
setNextCharacteristics={mockSetNextCharacteristics}
/>
);
// When
const input = component.find(Form.Control).at(2);
// Then
expect(input.exists()).toBe(true);
expect(input.prop('value')).toEqual(fakeNextCharacteristics.topic);
});
});
describe('IFormicariumWidgetView', () => {
let fakeAppearance = null;
let fakeDateTime = null;
let fakeService = null;
let fakeServiceState = null;
let fakeData = null;
beforeEach(() => {
fakeAppearance = {color: 'red'};
fakeDateTime = DateTime.local(1987, 10, 3, 8, 0, 0);
fakeService = new IFormicariumService({
instance: 'testing',
layout: {x: 0, y: 0, h: 1, w: 1},
});
fakeData = IFormicariumDataFactory();
fakeServiceState = new ServiceState({
data: fakeData,
});
spyOn(DateTime, 'fromSeconds').and.returnValue(fakeDateTime);
spyOn(fakeDateTime, 'toLocaleString').and.returnValue(
'Formatted DateTime'
);
});
it('defines the widget attributes', () => {
// Then
expect(IFormicariumWidgetView.IFormicariumWidgetView.defaultLayout).toEqual({
h: jasmine.any(Number),
w: jasmine.any(Number),
});
expect(IFormicariumWidgetView.IFormicariumWidgetView.icon).toBeDefined();
expect(IFormicariumWidgetView.IFormicariumWidgetView.layoutConstraints).toEqual({
minH: jasmine.any(Number),
minW: jasmine.any(Number),
});
expect(IFormicariumWidgetView.IFormicariumWidgetView.settingsView).toEqual(
IFormicariumWidgetView.IFormicariumWidgetSettingsView
);
expect(IFormicariumWidgetView.IFormicariumWidgetView.title).toEqual('iFormicarium');
});
it('renders empty when service state is null', () => {
// Given
fakeServiceState = null;
const component = shallow(
<IFormicariumWidgetView.IFormicariumWidgetView
appearance={fakeAppearance}
service={fakeService}
serviceState={fakeServiceState}
/>
);
// Then
expect(component.isEmptyRender()).toBe(true);
});
it('configures and renders the Widget', () => {
// Given
const component = shallow(
<IFormicariumWidgetView.IFormicariumWidgetView
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);
});
it('renders the loader when state is loading', () => {
// Given
spyOn(fakeServiceState, 'isLoading').and.returnValue(true);
const component = shallow(
<IFormicariumWidgetView.IFormicariumWidgetView
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(
<IFormicariumWidgetView.IFormicariumWidgetView
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 instance info when service state has no data', () => {
// Given
spyOn(fakeServiceState, 'hasData').and.returnValue(false);
const component = shallow(
<IFormicariumWidgetView.IFormicariumWidgetView
appearance={fakeAppearance}
service={fakeService}
serviceState={fakeServiceState}
/>
);
// When
const instanceInfo = component.find('div[_hint="InstanceInfo"]').at(0);
// Then
expect(instanceInfo.exists()).toBe(false);
});
it('computes and renders instance info when service state has data', () => {
// Given
const component = shallow(
<IFormicariumWidgetView.IFormicariumWidgetView
appearance={fakeAppearance}
service={fakeService}
serviceState={fakeServiceState}
/>
);
// When
const instanceInfo = component.find('div[_hint="InstanceInfo"]').at(0);
// Then
expect(instanceInfo.exists()).toBe(true);
const instanceName = component.find('h5[_hint="InstanceName"]').at(0);
expect(instanceName.exists()).toBe(true);
expect(instanceName.text()).toEqual(fakeData.name);
const instanceMetrics = component.find('div[_hint="InstanceMetrics"]').at(0);
expect(instanceMetrics.exists()).toBe(true);
const instanceDHTMetric = instanceMetrics.find('div[_hint="InstanceDHTMetric"]').at(0);
expect(instanceDHTMetric.exists()).toBe(true);
expect(instanceDHTMetric.find('p').at(0).text()).toEqual(
fakeData.dht['0'].name
);
expect(instanceDHTMetric.find('p').at(1).text()).toContain(
`Temp: ${fakeData.dht['0'].temp}`
);
expect(instanceDHTMetric.find('p').at(2).text()).toEqual(
`Humi: ${fakeData.dht['0'].humi}%`
);
const instanceFanMetric = instanceMetrics.find('div[_hint="InstanceFanMetric"]').at(0);
expect(instanceFanMetric.exists()).toBe(true);
expect(instanceFanMetric.find('p').at(1).text()).toEqual('25%');
const lastUpdate = component.find('small[_hint="LastUpdate"]').at(0);
expect(lastUpdate.exists()).toBe(true);
expect(lastUpdate.text()).toContain('Formatted DateTime');
expect(DateTime.fromSeconds).toHaveBeenCalledWith(fakeData.dt);
expect(fakeDateTime.toLocaleString).toHaveBeenCalledWith(
DateTime.TIME_SIMPLE
);
});
it('does not render FatalError if service state has no fatal error', () => {
// Given
const component = shallow(
<IFormicariumWidgetView.IFormicariumWidgetView
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(
<IFormicariumWidgetView.IFormicariumWidgetView
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);
});
});
});

View File

@@ -0,0 +1,6 @@
require('regenerator-runtime');
require('tests/__setup__/enzyme.setup.js');
let testsContext = require.context('.', true, /\.spec\.js$/);
testsContext.keys().forEach(testsContext);

View File

@@ -0,0 +1,16 @@
export const IFormicariumDataFactory = () => {
return {
name: 'iformicarium',
uptime: 123,
heap: 65535,
dht: {
'0': {
name: 'dht1',
humi: 69,
temp: 25,
},
},
fan: 1,
dt: 560242800,
};
};

View File

@@ -0,0 +1,7 @@
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({
adapter: new Adapter(),
disableLifecycleMethods: true,
});

View File

@@ -0,0 +1,155 @@
import * as HomeHubCore from '@bthlabs/homehub-core';
import * as IFormicariumService from 'src/IFormicariumService';
import {IFormicariumDataFactory} from 'tests/__fixtures__/iformicarium';
describe('src/IFormicariumService', () => {
describe('IFormicariumService', () => {
let fakeCharacteristics = null;
let fakeResult = null;
beforeEach(() => {
fakeCharacteristics = {
mqtt: 'thisisntright.local',
name: 'iformicarium',
topic: 'iformicarium',
};
fakeResult = IFormicariumDataFactory();
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 = IFormicariumService.IFormicariumService.emptyCharacteristics();
// Then
expect(result).toEqual({
name: '',
mqtt: '',
topic: 'iformicarium',
});
});
});
describe('start', () => {
it('notifies subscribers with the result of start service API call', async () => {
// Given
const service = new IFormicariumService.IFormicariumService({
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 IFormicariumService.IFormicariumService({
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 = {
mqtt: 'thisisntrighttoo.local',
name: 'iformicarium2',
topic: 'iformicarium2',
};
const service = new IFormicariumService.IFormicariumService({
instance: 'testing',
characteristics: fakeCharacteristics,
});
spyOn(service, 'restart');
// When
service.setCharacteristics(newCharacteristics);
// Then
expect(service.characteristics).toEqual(newCharacteristics);
});
it('restarts the service if mqtt characteristic changed', () => {
// Given
const newCharacteristics = {
...fakeCharacteristics,
mqtt: 'thisisntrighttoo.local',
};
const service = new IFormicariumService.IFormicariumService({
instance: 'testing',
characteristics: fakeCharacteristics,
});
spyOn(service, 'restart');
// When
service.setCharacteristics(newCharacteristics);
// Then
expect(service.restart).toHaveBeenCalled();
});
it('restarts the service if name characteristic changed', () => {
// Given
const newCharacteristics = {
...fakeCharacteristics,
name: 'iformicarium2',
};
const service = new IFormicariumService.IFormicariumService({
instance: 'testing',
characteristics: fakeCharacteristics,
});
spyOn(service, 'restart');
// When
service.setCharacteristics(newCharacteristics);
// Then
expect(service.restart).toHaveBeenCalled();
});
it('restarts the service if topic characteristic changed', () => {
// Given
const newCharacteristics = {
...fakeCharacteristics,
topic: 'iformicarium2',
};
const service = new IFormicariumService.IFormicariumService({
instance: 'testing',
characteristics: fakeCharacteristics,
});
spyOn(service, 'restart');
// When
service.setCharacteristics(newCharacteristics);
// Then
expect(service.restart).toHaveBeenCalled();
});
});
});
});