Make tests compatible with Jest 27

This commit is contained in:
Koen Kanters
2021-07-05 20:46:53 +02:00
parent 67391615d9
commit a76c13c461
29 changed files with 1323 additions and 2821 deletions
+9 -6
View File
@@ -50,9 +50,10 @@ class Controller {
this.addExtension = this.addExtension.bind(this);
// Initialize extensions.
const args = [this.zigbee, this.mqtt, this.state, this.publishEntityState, this.eventBus];
const args = [this.zigbee, this.mqtt, this.state, this.publishEntityState, this.eventBus,
this.enableDisableExtension, this.restartCallback, this.addExtension];
this.extensions = [
new ExtensionBridge(...args, this.enableDisableExtension, this.restartCallback),
new ExtensionBridge(...args),
new ExtensionPublish(...args),
new ExtensionReceive(...args),
new ExtensionDeviceGroupMembership(...args),
@@ -89,7 +90,7 @@ class Controller {
if (settings.get().advanced.availability_timeout) {
this.extensions.push(new ExtensionAvailability(...args));
}
this.extensions.push(new ExtensionExternalExtension(...args, this.addExtension, this.enableDisableExtension));
this.extensions.push(new ExtensionExternalExtension(...args));
}
async start() {
@@ -181,10 +182,12 @@ class Controller {
} else {
const Extension = AllExtensions.find((e) => e.name === name);
assert(Extension, `Extension '${name}' does not exist`);
const extension = new Extension(this.zigbee, this.mqtt, this.state, this.publishEntityState, this.eventBus);
const extension = new Extension(this.zigbee, this.mqtt, this.state, this.publishEntityState, this.eventBus,
this.enableDisableExtension, this.restartCallback, this.addExtension);
this.extensions.push(extension);
this.callExtensionMethod('onZigbeeStarted', [], [extension]);
this.callExtensionMethod('onMQTTConnected', [], [extension]);
await this.callExtensionMethod('onZigbeeStarted', [], [extension]);
await this.callExtensionMethod('onMQTTConnected', [], [extension]);
}
}
+4 -6
View File
@@ -23,13 +23,11 @@ class EventBus extends events.EventEmitter {
super.emit(event, data);
}
on(event, callback, extension=null) {
on(event, callback, extension) {
assert(allowedEvents.includes(event), `Event '${event}' not supported`);
if (extension) {
if (!this.callbackByExtension[extension]) this.callbackByExtension[extension] = [];
this.callbackByExtension[extension].push({event, callback});
}
assert(extension, `Extension cannot be null`);
if (!this.callbackByExtension[extension]) this.callbackByExtension[extension] = [];
this.callbackByExtension[extension].push({event, callback});
super.on(event, callback);
}
+2 -3
View File
@@ -182,9 +182,8 @@ class Availability extends Extension {
this.onReconnect(device);
}
const deviceSettings = settings.getDevice(ieeeAddr);
const name = deviceSettings ? deviceSettings.friendlyName : ieeeAddr;
const topic = `${name}/availability`;
const resolvedEntity = this.zigbee.resolveEntity(device);
const topic = `${resolvedEntity.name}/availability`;
const payload = available ? 'online' : 'offline';
if (this.state[ieeeAddr] !== available || force) {
this.state[ieeeAddr] = available;
+1 -1
View File
@@ -118,7 +118,7 @@ class Bind extends Extension {
constructor(zigbee, mqtt, state, publishEntityState, eventBus) {
super(zigbee, mqtt, state, publishEntityState, eventBus);
this.legacyApi = settings.get().advanced.legacy_api;
this.eventBus.on(`groupMembersChanged`, (d) => this.groupMembersChanged(d));
this.eventBus.on(`groupMembersChanged`, (d) => this.groupMembersChanged(d), this.constructor.name);
this.pollDebouncers = {};
}
+12 -5
View File
@@ -47,23 +47,30 @@ class Bridge extends Extension {
this.zigbee2mqttVersion = await utils.getZigbee2mqttVersion();
this.coordinatorVersion = await this.zigbee.getCoordinatorVersion();
this.eventBus.on(`groupMembersChanged`, () => this.publishGroups());
this.eventBus.on(`groupMembersChanged`, () => this.publishGroups(), this.constructor.name);
this.eventBus.on(`devicesChanged`, () => {
this.publishDevices();
this.publishInfo();
});
}, this.constructor.name);
this.eventBus.on(`deviceRenamed`, () => {
this.publishInfo();
});
}, this.constructor.name);
this.eventBus.on(`groupRenamed`, () => {
this.publishInfo();
});
this.zigbee.on('permitJoinChanged', (data) => this.permitJoinChanged(data));
}, this.constructor.name);
this.zigbee.removeListener('permitJoinChanged', this.permitJoinChanged);
this.permitJoinChanged = this.permitJoinChanged.bind(this);
this.zigbee.on('permitJoinChanged', this.permitJoinChanged);
await this.publishInfo();
await this.publishDevices();
await this.publishGroups();
}
async stop() {
super.stop();
this.zigbee.removeListener('permitJoinChanged', this.permitJoinChanged);
}
setupMQTTLogging() {
const mqtt = this.mqtt;
class EventTransport extends Transport {
+1 -1
View File
@@ -18,7 +18,7 @@ class Configure extends Extension {
this.topic = `${settings.get().mqtt.base_topic}/bridge/request/device/configure`;
this.legacyTopic = `${settings.get().mqtt.base_topic}/bridge/configure`;
this.eventBus.on(`reportingDisabled`, this.onReportingDisabled);
this.eventBus.on(`reportingDisabled`, this.onReportingDisabled, this.constructor.name);
}
onReportingDisabled(data) {
+2 -1
View File
@@ -9,7 +9,8 @@ const stringify = require('json-stable-stringify-without-jsonify');
const requestRegex = new RegExp(`${settings.get().mqtt.base_topic}/bridge/request/extension/(save|remove)`);
class ExternalExtension extends Extension {
constructor(zigbee, mqtt, state, publishEntityState, eventBus, addExtension, enableDisableExtension) {
constructor(zigbee, mqtt, state, publishEntityState, eventBus,
enableDisableExtension, restartCallback, addExtension) {
super(zigbee, mqtt, state, publishEntityState, eventBus);
this.args = [zigbee, mqtt, state, publishEntityState, eventBus];
this.addExtension = addExtension;
+1 -1
View File
@@ -35,7 +35,7 @@ class Groups extends Extension {
}
async onZigbeeStarted() {
this.eventBus.on('stateChange', this.onStateChange);
this.eventBus.on('stateChange', this.onStateChange, this.constructor.name);
await this.syncGroupsWithSettings();
}
+1 -1
View File
@@ -10,7 +10,7 @@ class Receive extends Extension {
super(zigbee, mqtt, state, publishEntityState, eventBus);
this.elapsed = {};
this.debouncers = {};
this.eventBus.on('publishEntityState', (data) => this.onPublishEntityState(data));
this.eventBus.on('publishEntityState', (data) => this.onPublishEntityState(data), this.constructor.name);
}
async onZigbeeStarted() {
+916 -2503
View File
File diff suppressed because it is too large Load Diff
+31 -60
View File
@@ -6,38 +6,41 @@ zigbeeHerdsman.returnDevices.push('0x000b57fffec6a5b3');
zigbeeHerdsman.returnDevices.push('0x00124b00120144ae');
zigbeeHerdsman.returnDevices.push('0x0017880104e45553');
zigbeeHerdsman.returnDevices.push('0x0017880104e45517');
const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const flushPromises = require('./lib/flushPromises');
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
const mocks = [MQTT.publish, logger.warn, logger.debug];
describe('Availability', () => {
let controller;
let extension;
function getExtension() {
return controller.extensions.find((e) => e.constructor.name === 'Availability');
let resetExtension = async () => {
await controller.enableDisableExtension(false, 'Availability');
await controller.enableDisableExtension(true, 'Availability');
extension = controller.extensions.find((e) => e.constructor.name === 'Availability');
}
beforeEach(async () => {
data.writeDefaultConfiguration();
settings.reRead();
data.writeEmptyState();
beforeAll(async () => {
jest.useFakeTimers();
settings.set(['advanced', 'availability_timeout'], 10);
controller = new Controller(jest.fn(), jest.fn());
mocksClear.forEach((m) => m.mockClear());
await controller.start();
await flushPromises();
});
afterEach(async () => {
await controller.stop();
await flushPromises();
beforeEach(async () => {
data.writeDefaultConfiguration();
settings.reRead();
settings.set(['advanced', 'availability_timeout'], 10);
mocks.forEach((m) => m.mockClear());
await resetExtension();
});
afterAll(async () => {
jest.useRealTimers();
})
it('Should publish availabilty on startup', async () => {
@@ -153,7 +156,7 @@ describe('Availability', () => {
it('Should retrieve the state when device is turned on/off within availability timeout', async () => {
MQTT.publish.mockClear();
getExtension().state = {};
extension.state = {};
const payload = {device: zigbeeHerdsman.devices.bulb_color};
await zigbeeHerdsman.events.deviceJoined(payload);
await flushPromises();
@@ -199,11 +202,7 @@ describe('Availability', () => {
it('Should not ping devices on blocklist by friendly name', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['advanced', 'availability_blocklist'], ['bulb_color'])
await controller.stop();
await flushPromises();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
await resetExtension();
device.ping.mockClear();
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
@@ -213,11 +212,7 @@ describe('Availability', () => {
it('Should not ping devices on blacklist by friendly name', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['advanced', 'availability_blacklist'], ['bulb_color'])
await controller.stop();
await flushPromises();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
await resetExtension();
device.ping.mockClear();
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
@@ -227,11 +222,7 @@ describe('Availability', () => {
it('Should not ping devices on blocklist by IEEE address', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['advanced', 'availability_blocklist'], [device.ieeeAddr]);
await controller.stop();
await flushPromises();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
await resetExtension();
device.ping.mockClear();
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
@@ -254,11 +245,7 @@ describe('Availability', () => {
it('Should ping devices on passlist by friendly name if availability_passlist is set', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['advanced', 'availability_passlist'], ['bulb_color']);
await controller.stop();
await flushPromises();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
await resetExtension();
device.ping.mockClear();
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
@@ -268,11 +255,7 @@ describe('Availability', () => {
it('Should ping devices on whitelist by friendly name if availability_whitelist is set', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['advanced', 'availability_whitelist'], ['bulb_color']);
await controller.stop();
await flushPromises();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
await resetExtension();
device.ping.mockClear();
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
@@ -282,11 +265,7 @@ describe('Availability', () => {
it('Should ping devices on passlist by IEEE address if availability_passlist is set', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['advanced', 'availability_passlist'], [device.ieeeAddr]);
await controller.stop();
await flushPromises();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
await resetExtension();
device.ping.mockClear();
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
@@ -295,13 +274,9 @@ describe('Availability', () => {
it('Should not ping devices not in passlist if availability_passlist is set', async () => {
const device = zigbeeHerdsman.devices.bulb;
getExtension().state[device.ieeeAddr] = false;
extension.state[device.ieeeAddr] = false;
settings.set(['advanced', 'availability_passlist'], ['0x000b57fffec6a5b3'])
await controller.stop();
await flushPromises();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
await resetExtension();
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(device.ping).toHaveBeenCalledTimes(0);
@@ -311,7 +286,7 @@ describe('Availability', () => {
it('Should not read when device has no modelID and reconnects', async () => {
const device = zigbeeHerdsman.devices.nomodel;
getExtension().state[device.ieeeAddr] = true;
extension.state[device.ieeeAddr] = true;
const endpoint = device.getEndpoint(1);
await zigbeeHerdsman.events.deviceAnnounce({device});
await flushPromises();
@@ -320,7 +295,7 @@ describe('Availability', () => {
it('Should not read when device has is unsupported', async () => {
const device = zigbeeHerdsman.devices.unsupported_router;
getExtension().state[device.ieeeAddr] = true;
extension.state[device.ieeeAddr] = true;
const endpoint = device.getEndpoint(1);
await zigbeeHerdsman.events.deviceAnnounce({device});
await flushPromises();
@@ -382,13 +357,9 @@ describe('Availability', () => {
it('Should republish existing state on MQTT connected', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
await controller.stop();
await flushPromises();
MQTT.publish.mockClear();
controller = new Controller(jest.fn(), jest.fn());
getExtension().state[device.ieeeAddr] = false;
await controller.start();
await flushPromises();
extension.state[device.ieeeAddr] = false;
await extension.onMQTTConnected();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb_color/availability',
'offline',
+19 -6
View File
@@ -4,7 +4,7 @@ const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const flushPromises = require('./lib/flushPromises');
const stringify = require('json-stable-stringify-without-jsonify');
jest.mock('debounce', () => jest.fn(fn => fn));
const debounce = require('debounce');
@@ -23,21 +23,34 @@ describe('Bind', () => {
}
}
let resetExtension = async () => {
await controller.enableDisableExtension(false, 'Bind');
await controller.enableDisableExtension(true, 'Bind');
}
beforeAll(async () => {
jest.useFakeTimers();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
this.coordinatorEndoint = zigbeeHerdsman.devices.coordinator.getEndpoint(1);
});
beforeEach(async () => {
data.writeDefaultConfiguration();
settings.reRead();
data.writeEmptyState();
zigbeeHerdsman.groups.group_1.members = [];
zigbeeHerdsman.devices.bulb_color.getEndpoint(1).configureReporting.mockClear();
zigbeeHerdsman.devices.bulb_color.getEndpoint(1).bind.mockClear();
zigbeeHerdsman.devices.bulb_color_2.getEndpoint(1).read.mockClear();
debounce.mockClear();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
this.coordinatorEndoint = zigbeeHerdsman.devices.coordinator.getEndpoint(1);
await resetExtension();
MQTT.publish.mockClear();
});
afterAll(async () => {
jest.useRealTimers();
})
it('Should bind to device and configure reporting', async () => {
const device = zigbeeHerdsman.devices.remote;
+25 -11
View File
File diff suppressed because one or more lines are too long
+20 -8
View File
@@ -4,7 +4,7 @@ const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const flushPromises = require('./lib/flushPromises');
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const stringify = require('json-stable-stringify-without-jsonify');
@@ -55,18 +55,30 @@ describe('Configure', () => {
}
}
beforeEach(async () => {
jest.useRealTimers();
data.writeDefaultConfiguration();
settings.reRead();
data.writeEmptyState();
let resetExtension = async () => {
await controller.enableDisableExtension(false, 'Configure');
await controller.enableDisableExtension(true, 'Configure');
}
beforeAll(async () => {
jest.useFakeTimers();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
mocksClear.forEach((m) => m.mockClear());
await flushPromises();
this.coordinatorEndoint = zigbeeHerdsman.devices.coordinator.getEndpoint(1);
});
beforeEach(async () => {
data.writeDefaultConfiguration();
settings.reRead();
mocksClear.forEach((m) => m.mockClear());
this.coordinatorEndoint = zigbeeHerdsman.devices.coordinator.getEndpoint(1);
await resetExtension();
});
afterAll(async () => {
jest.useRealTimers();
})
it('Should configure Router on startup', async () => {
expectBulbConfigured();
});
+9 -3
View File
@@ -6,7 +6,7 @@ const path = require('path');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const stringify = require('json-stable-stringify-without-jsonify');
const flushPromises = () => new Promise(setImmediate);
const flushPromises = require('./lib/flushPromises');
const tmp = require('tmp');
const mocksClear = [
zigbeeHerdsman.permitJoin, MQTT.end, zigbeeHerdsman.stop, logger.debug,
@@ -20,6 +20,10 @@ describe('Controller', () => {
let controller;
let mockExit;
beforeAll(async () => {
jest.useFakeTimers();
});
beforeEach(() => {
zigbeeHerdsman.returnDevices.splice(0);
mockExit = jest.fn();
@@ -30,6 +34,10 @@ describe('Controller', () => {
data.writeDefaultState();
});
afterAll(async () => {
jest.useRealTimers();
})
it('Start controller', async () => {
await controller.start();
expect(zigbeeHerdsman.constructor).toHaveBeenCalledWith({"network":{"panID":6754,"extendedPanID":[221,221,221,221,221,221,221,221],"channelList":[11],"networkKey":[1,3,5,7,9,11,13,15,0,2,4,6,8,10,12,13]},"databasePath":path.join(data.mockDir, "database.db"), "databaseBackupPath":path.join(data.mockDir, "database.db.backup"),"backupPath":path.join(data.mockDir, "coordinator_backup.json"),"acceptJoiningDeviceHandler": expect.any(Function),adapter: {concurrent: null, delay: null, disableLED: false}, "serialPort":{"baudRate":undefined,"rtscts":undefined,"path":"/dev/dummy"}}, logger);
@@ -132,14 +140,12 @@ describe('Controller', () => {
});
it('Log when MQTT client is unavailable', async () => {
jest.useFakeTimers();
await controller.start();
await flushPromises();
logger.error.mockClear();
controller.mqtt.client.reconnecting = true;
jest.advanceTimersByTime(11 * 1000);
await flushPromises();
expect(logger.error).toHaveBeenCalledTimes(1);
expect(logger.error).toHaveBeenCalledWith("Not connected to MQTT server!");
controller.mqtt.client.reconnecting = false;
});
+25 -20
View File
@@ -6,7 +6,7 @@ const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const flushPromises = require('./lib/flushPromises');
const path = require('path');
const fs = require('fs');
@@ -41,20 +41,33 @@ jest.mock(
describe('Loads external converters', () => {
let controller;
beforeEach(async () => {
jest.useRealTimers();
let resetExtension = async () => {
await controller.enableDisableExtension(false, 'ExternalConverters');
await controller.enableDisableExtension(true, 'ExternalConverters');
}
beforeAll(async () => {
jest.useFakeTimers();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
mocksClear.forEach((m) => m.mockClear());
});
beforeEach(async () => {
data.writeDefaultConfiguration();
data.writeEmptyState();
settings.reRead();
mocksClear.forEach((m) => m.mockClear());
this.coordinatorEndoint = zigbeeHerdsman.devices.coordinator.getEndpoint(1);
await resetExtension();
});
afterAll(async () => {
jest.useRealTimers();
});
it('Does not load external converters', async () => {
settings.set(['external_converters'], []);
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
await resetExtension();
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledTimes(0);
});
@@ -62,9 +75,7 @@ describe('Loads external converters', () => {
fs.copyFileSync(path.join(__dirname, 'assets', 'mock-external-converter.js'), path.join(data.mockDir, 'mock-external-converter.js'));
const devicesCount = zigbeeHerdsman.devices.lenght;
settings.set(['external_converters'], ['mock-external-converter.js']);
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
await resetExtension();
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledWith({
mock: true,
@@ -82,9 +93,7 @@ describe('Loads external converters', () => {
fs.copyFileSync(path.join(__dirname, 'assets', 'mock-external-converter-multiple.js'), path.join(data.mockDir, 'mock-external-converter-multiple.js'));
const devicesCount = zigbeeHerdsman.devices.lenght;
settings.set(['external_converters'], ['mock-external-converter-multiple.js']);
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
await resetExtension();
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledTimes(2);
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenNthCalledWith(1, {
mock: 1,
@@ -110,9 +119,7 @@ describe('Loads external converters', () => {
it('Loads external converters from package', async () => {
settings.set(['external_converters'], ['mock-external-converter-module']);
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
await resetExtension();
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledWith({
mock: true
@@ -121,9 +128,7 @@ describe('Loads external converters', () => {
it('Loads multiple external converters from package', async () => {
settings.set(['external_converters'], ['mock-multiple-external-converter-module']);
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
await resetExtension();
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledTimes(2);
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenNthCalledWith(1, {
mock: 1
+16 -5
View File
@@ -6,8 +6,7 @@ const path = require('path');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const stringify = require('json-stable-stringify-without-jsonify');
const flushPromises = () => new Promise(setImmediate);
const tmp = require('tmp');
const flushPromises = require('./lib/flushPromises');
const mocksClear = [
zigbeeHerdsman.permitJoin, MQTT.end, zigbeeHerdsman.stop, logger.debug,
MQTT.publish, MQTT.connect, zigbeeHerdsman.devices.bulb_color.removeFromNetwork,
@@ -20,12 +19,24 @@ const unlinkSyncSpy = jest.spyOn(fs, 'unlinkSync');
describe('User extensions', () => {
let controller;
let mockExit;
beforeAll(async () => {
jest.useFakeTimers();
});
beforeEach(async () => {
data.writeDefaultConfiguration();
settings.reRead();
mocksClear.forEach((m) => m.mockClear());
});
afterAll(async () => {
jest.useRealTimers();
});
beforeEach(() => {
zigbeeHerdsman.returnDevices.splice(0);
mockExit = jest.fn();
controller = new Controller(jest.fn(), mockExit);
controller = new Controller(jest.fn(), jest.fn());
mocksClear.forEach((m) => m.mockClear());
data.writeDefaultConfiguration();
settings.reRead();
+9 -1
View File
@@ -5,7 +5,7 @@ const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const stringify = require('json-stable-stringify-without-jsonify');
const flushPromises = () => new Promise(setImmediate);
const flushPromises = require('./lib/flushPromises');
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
jest.spyOn(process, 'exit').mockImplementation(() => {});
@@ -70,6 +70,10 @@ jest.mock('ws', () => ({
describe('Frontend', () => {
let controller;
beforeAll(async () => {
jest.useFakeTimers();
});
beforeEach(async () => {
mockWS.implementation.clients = [];
data.writeDefaultConfiguration();
@@ -80,6 +84,10 @@ describe('Frontend', () => {
zigbeeHerdsman.devices.bulb.linkquality = 10;
});
afterAll(async () => {
jest.useRealTimers();
});
afterEach(async() => {
delete zigbeeHerdsman.devices.bulb.linkquality;
});
+69 -103
View File
@@ -13,28 +13,42 @@ zigbeeHerdsman.returnDevices.push('0x0017880104e45724');
const MQTT = require('./stub/mqtt');
const Controller = require('../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const flushPromises = require('./lib/flushPromises');
const settings = require('../lib/util/settings');
describe('Groups', () => {
let controller;
beforeEach(() => {
data.writeEmptyState();
let resetExtension = async () => {
await controller.enableDisableExtension(false, 'Groups');
await controller.enableDisableExtension(true, 'Groups');
}
beforeAll(async () => {
jest.useFakeTimers();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
});
afterAll(async () => {
jest.useRealTimers();
});
beforeEach(() => {
Object.values(zigbeeHerdsman.groups).forEach((g) => g.members = []);
data.writeDefaultConfiguration();
settings.reRead();
MQTT.publish.mockClear();
zigbeeHerdsman.groups.gledopto_group.command.mockClear();
zigbeeHerdsmanConverters.toZigbeeConverters.__clearStore__();
controller.state.state = {};
})
it('Apply group updates add', async () => {
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: ['bulb', 'bulb_color']}});
zigbeeHerdsman.groups.group_1.members.push(zigbeeHerdsman.devices.bulb.getEndpoint(1))
await controller.start();
await flushPromises();
await resetExtension();
expect(zigbeeHerdsman.groups.group_1.members).toStrictEqual([
zigbeeHerdsman.devices.bulb.getEndpoint(1),
zigbeeHerdsman.devices.bulb_color.getEndpoint(1)
@@ -46,8 +60,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false,}});
await controller.start();
await flushPromises();
await resetExtension();
expect(zigbeeHerdsman.groups.group_1.members).toStrictEqual([]);
});
@@ -58,8 +71,7 @@ describe('Groups', () => {
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false,}});
logger.error.mockClear();
await controller.start();
await flushPromises();
await resetExtension();
expect(logger.error).toHaveBeenCalledWith(`Failed to remove 'bulb_color' from 'group_1'`);
expect(zigbeeHerdsman.groups.group_1.members).toStrictEqual([endpoint]);
});
@@ -70,32 +82,28 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'3': {friendly_name: 'group_3', retain: false, devices: [device.ieeeAddr]}});
await controller.start();
await flushPromises();
await resetExtension();
expect(zigbeeHerdsman.groups.group_1.members).toStrictEqual([]);
});
it('Add non standard endpoint to group with name', async () => {
const QBKG03LM = zigbeeHerdsman.devices.QBKG03LM;
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: ['0x0017880104e45542/right']}});
await controller.start();
await flushPromises();
await resetExtension();
expect(zigbeeHerdsman.groups.group_1.members).toStrictEqual([QBKG03LM.getEndpoint(3)]);
});
it('Add non standard endpoint to group with number', async () => {
const QBKG03LM = zigbeeHerdsman.devices.QBKG03LM;
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: ['wall_switch_double/2']}});
await controller.start();
await flushPromises();
await resetExtension();
expect(zigbeeHerdsman.groups.group_1.members).toStrictEqual([QBKG03LM.getEndpoint(2)]);
});
it('Shouldnt crash on non-existing devices', async () => {
logger.error.mockClear();
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: ['not_existing_bla']}});
await controller.start();
await flushPromises();
await resetExtension();
expect(zigbeeHerdsman.groups.group_1.members).toStrictEqual([]);
expect(logger.error).toHaveBeenCalledWith("Cannot find 'not_existing_bla' of group 'group_1'");
});
@@ -103,8 +111,7 @@ describe('Groups', () => {
it('Should resolve device friendly names', async () => {
settings.set(['devices', zigbeeHerdsman.devices.bulb.ieeeAddr, 'friendly_name'], 'bulb_friendly_name');
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: ['bulb_friendly_name', 'bulb_color']}});
await controller.start();
await flushPromises();
await resetExtension();
expect(zigbeeHerdsman.groups.group_1.members).toStrictEqual([
zigbeeHerdsman.devices.bulb.getEndpoint(1),
zigbeeHerdsman.devices.bulb_color.getEndpoint(1)
@@ -117,8 +124,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: []}});
expect(group.members.length).toBe(0);
await controller.start();
await flushPromises();
await resetExtension();
MQTT.events.message('zigbee2mqtt/bridge/group/group_1/add', 'bulb_color');
await flushPromises();
expect(group.members).toStrictEqual([endpoint]);
@@ -132,8 +138,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups["group/with/slashes"];
settings.set(['groups'], {'99': {friendly_name: 'group/with/slashes', retain: false, devices: []}});
expect(group.members.length).toBe(0);
await controller.start();
await flushPromises();
await resetExtension();
MQTT.events.message('zigbee2mqtt/bridge/group/group/with/slashes/add', 'bulb_color');
await flushPromises();
expect(group.members).toStrictEqual([endpoint]);
@@ -146,8 +151,7 @@ describe('Groups', () => {
const endpoint = device.getEndpoint(3);
const group = zigbeeHerdsman.groups.group_1;
expect(group.members.length).toBe(0);
await controller.start();
await flushPromises();
await resetExtension();
await MQTT.events.message('zigbee2mqtt/bridge/group/group_1/add', 'wall_switch_double/right');
await flushPromises();
expect(group.members).toStrictEqual([endpoint]);
@@ -159,8 +163,7 @@ describe('Groups', () => {
const endpoint = device.getEndpoint(3);
const group = zigbeeHerdsman.groups.group_1;
expect(group.members.length).toBe(0);
await controller.start();
await flushPromises();
await resetExtension();
await MQTT.events.message('zigbee2mqtt/bridge/group/group_1/add', 'wall_switch_double/right');
await flushPromises();
await MQTT.events.message('zigbee2mqtt/bridge/group/group_1/add', '0x0017880104e45542/3');
@@ -175,8 +178,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [device.ieeeAddr]}});
await controller.start();
await flushPromises();
await resetExtension();
await MQTT.events.message('zigbee2mqtt/bridge/group/group_1/remove', 'bulb_color');
await flushPromises();
expect(group.members).toStrictEqual([]);
@@ -190,8 +192,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: ['dummy']}});
await controller.start();
await flushPromises();
await resetExtension();
await MQTT.events.message('zigbee2mqtt/bridge/group/group_1/remove', 'bulb_color');
await flushPromises();
expect(group.members).toStrictEqual([]);
@@ -204,8 +205,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [`wall_switch_double/right`]}});
await controller.start();
await flushPromises();
await resetExtension();
await MQTT.events.message('zigbee2mqtt/bridge/group/group_1/remove', '0x0017880104e45542/3');
await flushPromises();
expect(group.members).toStrictEqual([]);
@@ -218,8 +218,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [`0x0017880104e45542/right`]}});
await controller.start();
await flushPromises();
await resetExtension();
await MQTT.events.message('zigbee2mqtt/bridge/group/group_1/remove', 'wall_switch_double/3');
await flushPromises();
expect(group.members).toStrictEqual([]);
@@ -232,8 +231,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [`wall_switch_double/3`]}});
await controller.start();
await flushPromises();
await resetExtension();
await MQTT.events.message('zigbee2mqtt/bridge/group/group_1/remove', '0x0017880104e45542/right');
await flushPromises();
expect(group.members).toStrictEqual([]);
@@ -246,8 +244,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [`wall_switch_double/3`]}});
await controller.start();
await flushPromises();
await resetExtension();
await MQTT.events.message('zigbee2mqtt/bridge/group/remove_all', '0x0017880104e45542/right');
await flushPromises();
expect(group.members).toStrictEqual([]);
@@ -261,8 +258,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [`wall_switch_double/3`]}});
await controller.start();
await flushPromises();
await resetExtension();
await MQTT.events.message('zigbee2mqtt/bridge/group/group_1/remove_all', '0x0017880104e45542/right');
await flushPromises();
expect(group.members).toStrictEqual([]);
@@ -270,8 +266,7 @@ describe('Groups', () => {
});
it('Legacy api: Log when adding to non-existing group', async () => {
await controller.start();
await flushPromises();
await resetExtension();
logger.error.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/group/group_1_not_existing/add', 'bulb_color');
await flushPromises();
@@ -279,8 +274,7 @@ describe('Groups', () => {
});
it('Legacy api: Log when adding to non-existing device', async () => {
await controller.start();
await flushPromises();
await resetExtension();
logger.error.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/group/group_1/add', 'bulb_color_not_existing');
await flushPromises();
@@ -293,8 +287,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [device.ieeeAddr]}});
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
const payload = {data: {onOff: 1}, cluster: 'genOnOff', device, endpoint, type: 'attributeReport', linkquality: 10};
@@ -310,8 +303,7 @@ describe('Groups', () => {
const device1 = zigbeeHerdsman.devices.bulb_2;
const device2 = zigbeeHerdsman.devices.bulb_color_2;
const group = zigbeeHerdsman.groups.group_tradfri_remote;
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
await zigbeeHerdsman.events.message({data: {onOff: 1}, cluster: 'genOnOff', device: device1, endpoint: device1.getEndpoint(1), type: 'attributeReport', linkquality: 10});
@@ -332,8 +324,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [device.ieeeAddr]}});
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
@@ -350,8 +341,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [device.ieeeAddr]}});
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'ON'}));
@@ -377,8 +367,7 @@ describe('Groups', () => {
it('Should publish state of device with endpoint name', async () => {
const group = zigbeeHerdsman.groups.gledopto_group;
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/gledopto_group/set', stringify({state: 'ON'}));
@@ -392,8 +381,7 @@ describe('Groups', () => {
it('Should publish state of group when specific state of specific endpoint is changed', async () => {
const group = zigbeeHerdsman.groups.gledopto_group;
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/GLEDOPTO_2ID/set', stringify({state_cct: 'ON'}));
@@ -410,8 +398,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, filtered_attributes: ['brightness'], devices: [device.ieeeAddr]}});
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON', brightness: 100}));
@@ -427,8 +414,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', devices: [device.ieeeAddr], optimistic: false, retain: false}});
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
const payload = {data: {onOff: 1}, cluster: 'genOnOff', device, endpoint, type: 'attributeReport', linkquality: 10};
@@ -449,8 +435,7 @@ describe('Groups', () => {
'2': {friendly_name: 'group_2', retain: false, devices: [device.ieeeAddr]},
'3': {friendly_name: 'group_3', retain: false, devices: []}
});
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
@@ -472,8 +457,7 @@ describe('Groups', () => {
settings.set(['groups'], {
'1': {friendly_name: 'group_1', devices: [device_1.ieeeAddr, device_2.ieeeAddr], retain: false}
});
await controller.start();
await flushPromises();
await resetExtension();
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
await flushPromises();
@@ -497,8 +481,7 @@ describe('Groups', () => {
'1': {friendly_name: 'group_1', devices: [device_1.ieeeAddr, device_2.ieeeAddr], retain: false},
'2': {friendly_name: 'group_2', retain: false, devices: [device_1.ieeeAddr]},
});
await controller.start();
await flushPromises();
await resetExtension();
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
await flushPromises();
@@ -522,8 +505,7 @@ describe('Groups', () => {
settings.set(['groups'], {
'1': {friendly_name: 'group_1', devices: [device_1.ieeeAddr, device_2.ieeeAddr], retain: false}
});
await controller.start();
await flushPromises();
await resetExtension();
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
await flushPromises();
@@ -549,8 +531,7 @@ describe('Groups', () => {
settings.set(['groups'], {
'1': {friendly_name: 'group_1', devices: [device_1.ieeeAddr, device_2.ieeeAddr], retain: false}
});
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF', color_temp: 200}));
@@ -577,8 +558,7 @@ describe('Groups', () => {
settings.set(['groups'], {
'1': {friendly_name: 'group_1', devices: [device_1.ieeeAddr, device_2.ieeeAddr], retain: false}
});
await controller.start();
await flushPromises();
await resetExtension();
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
await flushPromises();
@@ -599,8 +579,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: []}});
expect(group.members.length).toBe(0);
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', stringify({transaction: "123", group: 'group_1', device: 'bulb_color'}));
await flushPromises();
@@ -620,7 +599,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: []}});
expect(group.members.length).toBe(0);
await controller.start();
await resetExtension();
endpoint.addToGroup.mockImplementationOnce(() => {throw new Error('timeout')});
await flushPromises();
MQTT.publish.mockClear();
@@ -642,8 +621,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups["group/with/slashes"];
settings.set(['groups'], {'99': {friendly_name: 'group/with/slashes', retain: false, devices: []}});
expect(group.members.length).toBe(0);
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group/with/slashes', device: 'bulb_color'}));
await flushPromises();
@@ -662,8 +640,7 @@ describe('Groups', () => {
const endpoint = device.getEndpoint(3);
const group = zigbeeHerdsman.groups.group_1;
expect(group.members.length).toBe(0);
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'wall_switch_double/right'}));
await flushPromises();
@@ -682,8 +659,7 @@ describe('Groups', () => {
const endpoint = device.getEndpoint(3);
const group = zigbeeHerdsman.groups.group_1;
expect(group.members.length).toBe(0);
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'wall_switch_double/right'}));
await flushPromises();
@@ -705,8 +681,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [device.ieeeAddr]}});
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: 'bulb_color'}));
await flushPromises();
@@ -726,8 +701,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [device.ieeeAddr]}});
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: 'bulb_color', skip_disable_reporting: true}));
await flushPromises();
@@ -747,8 +721,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: ['dummy']}});
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: 'bulb_color'}));
await flushPromises();
@@ -768,8 +741,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [`wall_switch_double/right`]}});
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: '0x0017880104e45542/3'}));
await flushPromises();
@@ -789,8 +761,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [`0x0017880104e45542/right`]}});
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: 'wall_switch_double/3'}));
await flushPromises();
@@ -810,8 +781,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [`wall_switch_double/3`]}});
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: '0x0017880104e45542/right'}));
await flushPromises();
@@ -831,8 +801,7 @@ describe('Groups', () => {
const group = zigbeeHerdsman.groups.group_1;
group.members.push(endpoint);
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [`wall_switch_double/3`]}});
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove_all', stringify({device: '0x0017880104e45542/right'}));
await flushPromises();
@@ -847,8 +816,7 @@ describe('Groups', () => {
});
it('Error when adding to non-existing group', async () => {
await controller.start();
await flushPromises();
await resetExtension();
logger.error.mockClear();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1_not_existing', device: 'bulb_color'}));
@@ -862,8 +830,7 @@ describe('Groups', () => {
});
it('Error when adding to non-existing device', async () => {
await controller.start();
await flushPromises();
await resetExtension();
logger.error.mockClear();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'bulb_color_not_existing'}));
@@ -876,15 +843,14 @@ describe('Groups', () => {
);
});
it('onlythis Should only include relevant properties when publishing member states', async () => {
it('Should only include relevant properties when publishing member states', async () => {
const bulbColor = zigbeeHerdsman.devices.bulb_color;
const bulbColorTemp = zigbeeHerdsman.devices.bulb;
const group = zigbeeHerdsman.groups.group_1;
group.members.push(bulbColor.getEndpoint(1));
group.members.push(bulbColorTemp.getEndpoint(1));
settings.set(['groups'], {'1': {friendly_name: 'group_1', retain: false, devices: [bulbColor.ieeeAddr, bulbColorTemp.ieeeAddr]}});
await controller.start();
await flushPromises();
await resetExtension();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({color_temp: 50}));
+9 -6
View File
@@ -3,7 +3,7 @@ const settings = require('../lib/util/settings');
const stringify = require('json-stable-stringify-without-jsonify');
const logger = require('./stub/logger');
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const flushPromises = () => new Promise(setImmediate);
const flushPromises = require('./lib/flushPromises');
const MQTT = require('./stub/mqtt');
const Controller = require('../lib/controller');
const fs = require('fs');
@@ -16,7 +16,6 @@ describe('HomeAssistant extension', () => {
beforeEach(async () => {
this.version = await require('../lib/util/utils').getZigbee2mqttVersion();
this.version = `Zigbee2MQTT ${this.version.version}`;
jest.useRealTimers();
data.writeDefaultConfiguration();
settings.reRead();
data.writeEmptyState();
@@ -24,6 +23,14 @@ describe('HomeAssistant extension', () => {
settings.set(['homeassistant'], true);
});
beforeAll(async () => {
jest.useFakeTimers();
});
afterAll(async () => {
jest.useRealTimers();
});
it('Should not have duplicate type/object_ids in a mapping', () => {
const duplicated = [];
const ha = new HomeAssistant(null, null, null, null, {on: () => {}});
@@ -946,7 +953,6 @@ describe('HomeAssistant extension', () => {
});
it('Should send all status when home assistant comes online (default topic)', async () => {
jest.useFakeTimers();
data.writeDefaultState();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
@@ -972,7 +978,6 @@ describe('HomeAssistant extension', () => {
});
it('Should send all status when home assistant comes online', async () => {
jest.useFakeTimers();
data.writeDefaultState();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
@@ -998,7 +1003,6 @@ describe('HomeAssistant extension', () => {
});
it('Shouldnt send all status when home assistant comes offline', async () => {
jest.useFakeTimers();
data.writeDefaultState();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
@@ -1012,7 +1016,6 @@ describe('HomeAssistant extension', () => {
});
it('Shouldnt send all status when home assistant comes online with different topic', async () => {
jest.useFakeTimers();
data.writeDefaultState();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
+6 -2
View File
@@ -7,13 +7,14 @@ const path = require('path');
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
const settings = require('../../lib/util/settings');
const Controller = require('../../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const flushPromises = require('../lib/flushPromises');
describe('Bridge legacy', () => {
let controller;
beforeAll(async () => {
jest.useFakeTimers();
this.version = await require('../../lib/util/utils').getZigbee2mqttVersion();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
@@ -22,11 +23,14 @@ describe('Bridge legacy', () => {
beforeEach(() => {
data.writeDefaultConfiguration();
settings.reRead();
data.writeDefaultState();
logger.info.mockClear();
logger.warn.mockClear();
});
afterAll(async () => {
jest.useRealTimers();
});
it('Should publish bridge configuration on startup', async () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/config',
+23 -18
View File
@@ -13,13 +13,14 @@ zigbeeHerdsman.returnDevices.push('0x90fd9ffffe4b64ax');
const MQTT = require('../stub/mqtt');
const settings = require('../../lib/util/settings');
const Controller = require('../../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const flushPromises = require('../lib/flushPromises');
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
describe('Report', () => {
let controller;
let extension;
function expectOnOffBrightnessColorReport(endpoint, colorXY) {
const coordinatorEndpoint = zigbeeHerdsman.devices.coordinator.getEndpoint(1);
@@ -69,23 +70,34 @@ describe('Report', () => {
}
}
beforeAll(async () => {
jest.useFakeTimers();
settings.set(['advanced', 'report'], true);
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
extension = controller.extensions.find((e) => e.constructor.name === 'Report');
});
beforeEach(async () => {
extension.enabled = true;
data.writeDefaultConfiguration();
settings.reRead();
data.writeEmptyState();
settings.set(['advanced', 'report'], true);
for (const device of Object.values(zigbeeHerdsman.devices)) {
mockClear(device);
delete device.meta.reporting;
}
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
mocksClear.forEach((m) => m.mockClear());
await flushPromises();
extension.queue = new Set();
extension.failed = new Set();
await extension.onZigbeeStarted();
});
afterAll(async () => {
jest.useRealTimers();
});
it('Should configure reporting on startup', async () => {
await extension.onZigbeeStarted();
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
expectOnOffBrightnessColorReport(endpoint, true);
@@ -96,10 +108,8 @@ describe('Report', () => {
const endpoint = device.getEndpoint(1);
mockClear(device);
delete device.meta.report;
settings.set(['advanced', 'report'], false);
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
extension.enabled = false;
await extension.onZigbeeStarted();
expect(device.meta.reporting).toBe(undefined);
expect(endpoint.bind).toHaveBeenCalledTimes(0);
});
@@ -108,11 +118,9 @@ describe('Report', () => {
const device = zigbeeHerdsman.devices.bulb_color;
device.meta.reporting = 1;
const endpoint = device.getEndpoint(1);
settings.set(['advanced', 'report'], false);
extension.enabled = false;
mockClear(device);
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
await extension.onZigbeeStarted();
expectOnOffBrightnessColorReportDisabled(endpoint, true);
});
@@ -131,7 +139,6 @@ describe('Report', () => {
});
it('Should not configure reporting when still configuring', async () => {
jest.useFakeTimers();
const device = zigbeeHerdsman.devices.bulb;
const endpoint = device.getEndpoint(1);
endpoint.bind.mockImplementationOnce(async () => await wait(1000));
@@ -143,8 +150,6 @@ describe('Report', () => {
await zigbeeHerdsman.events.message(payload);
await flushPromises();
expect(endpoint.bind).toHaveBeenCalledTimes(1);
jest.runAllTimers();
jest.useRealTimers();
});
it('Should not mark as configured when reporting setup fails', async () => {
+2
View File
@@ -0,0 +1,2 @@
const globalSetImmediate = setImmediate;
module.exports = () => new Promise(globalSetImmediate);
+8 -3
View File
@@ -16,9 +16,7 @@ zigbeeHerdsman.returnDevices.push(external_converter_device.ieeeAddr)
const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = () => new Promise(setImmediate);
Date.now = jest.fn()
Date.now.mockReturnValue(10000);
const flushPromises = require('./lib/flushPromises');
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
const setTimeoutNative = setTimeout;
@@ -26,6 +24,9 @@ describe('Networkmap', () => {
let controller;
beforeAll(async () => {
jest.useFakeTimers();
Date.now = jest.fn()
Date.now.mockReturnValue(10000);
data.writeDefaultConfiguration();
settings.reRead();
data.writeEmptyState();
@@ -50,6 +51,10 @@ describe('Networkmap', () => {
global.setTimeout = setTimeoutNative;
});
afterAll(async () => {
jest.useRealTimers();
});
function mock() {
/**
* Topology
+13 -3
View File
@@ -6,7 +6,7 @@ zigbeeHerdsman.returnDevices.push('0x0017880104e45560');
const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const flushPromises = require('./lib/flushPromises');
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
@@ -22,14 +22,24 @@ describe('On event', () => {
const device = zigbeeHerdsman.devices.LIVOLO;
beforeEach(async () => {
jest.useFakeTimers();
data.writeDefaultConfiguration();
settings.reRead();
data.writeEmptyState();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
});
beforeEach(async () => {
controller.state.state = {};
data.writeDefaultConfiguration();
settings.reRead();
mocksClear.forEach((m) => m.mockClear());
zigbeeHerdsmanConverters.onEvent.mockClear();
await flushPromises();
});
afterAll(async () => {
jest.useRealTimers();
});
it('Should call with start event', async () => {
+46 -9
View File
@@ -4,7 +4,7 @@ const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const flushPromises = require('./lib/flushPromises');
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const stringify = require('json-stable-stringify-without-jsonify');
@@ -15,15 +15,33 @@ describe('OTA update', () => {
mapped.ota.updateToLatest = jest.fn();
mapped.ota.isUpdateAvailable = jest.fn();
}
beforeEach(async () => {
beforeAll(async () => {
data.writeDefaultConfiguration();
settings.reRead();
data.writeDefaultConfiguration();
settings.set(['advanced', 'ikea_ota_use_test_url'], true);
data.writeEmptyState();
settings.reRead();
jest.useFakeTimers();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
await flushPromises();
});
afterEach(async () => {
settings.set(['ota', 'disable_automatic_update_check'], false);
jest.runOnlyPendingTimers();
});
afterAll(async () => {
jest.useRealTimers();
});
beforeEach(async () => {
const extension = controller.extensions.find((e) => e.constructor.name === 'OTAUpdate');
extension.lastChecked = {};
extension.inProgress = new Set();
controller.state.state = {};
MQTT.publish.mockClear();
});
@@ -176,7 +194,6 @@ describe('OTA update', () => {
});
it('Should refuse to check/update when already in progress', async () => {
jest.useFakeTimers();
const device = zigbeeHerdsman.devices.bulb;
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
mockClear(mapped);
@@ -189,7 +206,7 @@ describe('OTA update', () => {
MQTT.events.message('zigbee2mqtt/bridge/request/device/ota_update/check', "bulb");
await flushPromises();
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
jest.runAllTimers();
jest.runOnlyPendingTimers();
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/ota_update/check',
@@ -201,7 +218,6 @@ describe('OTA update', () => {
it('Shouldnt crash when read modelID before/after OTA update fails', async () => {
const device = zigbeeHerdsman.devices.bulb;
const endpoint = device.endpoints[0];
let count = 0;
endpoint.read.mockImplementation(() => {throw new Error('Failed!')});
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
@@ -217,6 +233,7 @@ describe('OTA update', () => {
it('Should check for update when device requests it', async () => {
const device = zigbeeHerdsman.devices.bulb;
device.endpoints[0].commandResponse.mockClear();
const data = {imageType: 12382};
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
mockClear(mapped);
@@ -236,8 +253,6 @@ describe('OTA update', () => {
await flushPromises();
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
const extension = controller.extensions.find((e) => e.constructor.name === 'OTAUpdate');
extension.lastChecked = {};
logger.info.mockClear();
mapped.ota.isUpdateAvailable.mockReturnValueOnce(false);
await zigbeeHerdsman.events.message(payload);
@@ -250,6 +265,28 @@ describe('OTA update', () => {
);
});
it('Should check for update when device requests it and it is not available', async () => {
const device = zigbeeHerdsman.devices.bulb;
device.endpoints[0].commandResponse.mockClear();
const data = {imageType: 12382};
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
mockClear(mapped);
mapped.ota.isUpdateAvailable.mockReturnValueOnce(false);
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10};
logger.info.mockClear();
await zigbeeHerdsman.events.message(payload);
await flushPromises();
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, logger, {"imageType": 12382});
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x95});
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb',
stringify({"update_available":false,"update":{"state":"idle"}}),
{retain: true, qos: 0}, expect.any(Function)
);
});
it('Should not check for update when device requests it and disable_automatic_update_check is set to true', async () => {
settings.set(['ota', 'disable_automatic_update_check'], true);
const device = zigbeeHerdsman.devices.bulb;
+9 -8
View File
@@ -6,7 +6,7 @@ const stringify = require('json-stable-stringify-without-jsonify');
const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const flushPromises = require('./lib/flushPromises');
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
@@ -27,6 +27,7 @@ describe('Publish', () => {
let controller;
beforeAll(async () => {
jest.useFakeTimers();
data.writeEmptyState();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
@@ -34,8 +35,6 @@ describe('Publish', () => {
});
beforeEach(async () => {
jest.useRealTimers();
await flushPromises();
data.writeDefaultConfiguration();
controller.state.state = {};
settings.reRead();
@@ -54,6 +53,11 @@ describe('Publish', () => {
zigbeeHerdsmanConverters.toZigbeeConverters.__clearStore__();
});
afterAll(async () => {
jest.runOnlyPendingTimers();
jest.useRealTimers();
});
it('Should publish messages to zigbee devices', async () => {
const endpoint = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({brightness: '200'}));
@@ -682,14 +686,13 @@ describe('Publish', () => {
});
it('Should read after write when enabled', async () => {
jest.useFakeTimers();
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['devices', device.ieeeAddr, 'retrieve_state'], true);
const endpoint = device.getEndpoint(1);
const payload = {'state': 'ON', 'color': {'x': 0.701, 'y': 0.299}};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
await flushPromises();
jest.runAllTimers();
jest.runOnlyPendingTimers();
expect(endpoint.command).toHaveBeenCalledTimes(2);
expect(endpoint.command.mock.calls[0]).toEqual(["genOnOff", "on", {}, {}]);
expect(endpoint.command.mock.calls[1]).toEqual(["lightingColorCtrl", "moveToColor", {"colorx": 45940, "colory": 19595, "transtime": 0}, {}]);
@@ -1278,19 +1281,17 @@ describe('Publish', () => {
it('Should publish separate genOnOff to GL-S-007ZS when setting state and brightness as bulb doesnt turn on with moveToLevelWithOnOff', async () => {
// https://github.com/Koenkk/zigbee2mqtt/issues/2757
jest.useFakeTimers();
const device = zigbeeHerdsman.devices['GL-S-007ZS'];
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/GL-S-007ZS/set', stringify({state: 'ON', brightness: 20}));
await flushPromises();
jest.runAllTimers();
jest.runOnlyPendingTimers();
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(2);
expect(endpoint.command.mock.calls[0]).toEqual([ 'genOnOff', 'on', {}, {} ]);
expect(endpoint.command.mock.calls[1]).toEqual([ 'genLevelCtrl', 'moveToLevelWithOnOff', { level: 20, transtime: 0 }, {} ]);
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish.mock.calls[0]).toEqual([ 'zigbee2mqtt/GL-S-007ZS', stringify({"state":"ON","brightness":20}), { qos: 0, retain: false }, expect.any(Function)]);
jest.useRealTimers();
});
it('Should log as error when setting property with no defined converter', async () => {
+17 -14
View File
@@ -5,24 +5,31 @@ const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const flushPromises = require('./lib/flushPromises');
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
describe('Receive', () => {
let controller;
beforeEach(async () => {
jest.useRealTimers();
data.writeDefaultConfiguration();
settings.reRead();
data.writeEmptyState();
beforeAll(async () => {
jest.useFakeTimers();
controller = new Controller(jest.fn(), jest.fn());
await controller.start();
});
beforeEach(async () => {
controller.state.state = {};
data.writeDefaultConfiguration();
settings.reRead();
mocksClear.forEach((m) => m.mockClear());
delete zigbeeHerdsman.devices.WXKG11LM.linkquality;
});
afterAll(async () => {
jest.useRealTimers();
});
it('Should handle a zigbee message', async () => {
const device = zigbeeHerdsman.devices.WXKG11LM;
device.linkquality = 10;
@@ -100,7 +107,6 @@ describe('Receive', () => {
});
it('Should debounce messages', async () => {
jest.useFakeTimers();
const device = zigbeeHerdsman.devices.WSDCGQ11LM;
settings.set(['devices', device.ieeeAddr, 'debounce'], 0.1);
const data1 = {measuredValue: 8}
@@ -115,7 +121,7 @@ describe('Receive', () => {
await flushPromises();
jest.advanceTimersByTime(50);
expect(MQTT.publish).toHaveBeenCalledTimes(0);
jest.runAllTimers();
jest.runOnlyPendingTimers();
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
@@ -124,7 +130,6 @@ describe('Receive', () => {
});
it('Should debounce and retain messages when set via device_options', async () => {
jest.useFakeTimers();
const device = zigbeeHerdsman.devices.WSDCGQ11LM;
settings.set(['device_options', 'debounce'], 0.1);
settings.set(['device_options', 'retain'], true);
@@ -141,7 +146,7 @@ describe('Receive', () => {
await flushPromises();
jest.advanceTimersByTime(50);
expect(MQTT.publish).toHaveBeenCalledTimes(0);
jest.runAllTimers();
jest.runOnlyPendingTimers();
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
@@ -150,7 +155,6 @@ describe('Receive', () => {
});
it('Should debounce messages only with the same payload values for provided debounce_ignore keys', async () => {
jest.useFakeTimers();
const device = zigbeeHerdsman.devices.WSDCGQ11LM;
settings.set(['devices', device.ieeeAddr, 'debounce'], 0.1);
settings.set(['devices', device.ieeeAddr, 'debounce_ignore'], ['temperature']);
@@ -166,7 +170,7 @@ describe('Receive', () => {
jest.advanceTimersByTime(50);
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({temperature: 0.08, pressure: 2});
jest.runAllTimers();
jest.runOnlyPendingTimers();
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(JSON.parse(MQTT.publish.mock.calls[1][1])).toStrictEqual({temperature: 0.07, pressure: 2, humidity: 0.03});
@@ -174,13 +178,12 @@ describe('Receive', () => {
it('Shouldnt republish old state', async () => {
// https://github.com/Koenkk/zigbee2mqtt/issues/3572
jest.useFakeTimers();
const device = zigbeeHerdsman.devices.bulb;
settings.set(['devices', device.ieeeAddr, 'debounce'], 0.1);
await zigbeeHerdsman.events.message({data: {onOff: 0}, cluster: 'genOnOff', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10});
await MQTT.events.message('zigbee2mqtt/bulb/set', stringify({state: 'ON'}));
await flushPromises();
jest.runAllTimers();
jest.runOnlyPendingTimers();
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({state: 'ON'});
expect(JSON.parse(MQTT.publish.mock.calls[1][1])).toStrictEqual({state: 'ON'});
+18 -13
View File
@@ -233,20 +233,24 @@ function stateExists() {
return fs.existsSync(stateFile);
}
function writeDefaultState() {
const state = {
"0x000b57fffec6a5b2": {
"state": "ON",
"brightness": 50,
"color_temp": 370,
"linkquality": 99,
},
"0x0017880104e45517": {
"brightness": 255
},
}
const defaultState = {
"0x000b57fffec6a5b2": {
"state": "ON",
"brightness": 50,
"color_temp": 370,
"linkquality": 99,
},
"0x0017880104e45517": {
"brightness": 255
},
}
fs.writeFileSync(path.join(mockDir, 'state.json'), stringify(state));
function getDefaultState() {
return defaultState;
}
function writeDefaultState() {
fs.writeFileSync(path.join(mockDir, 'state.json'), stringify(defaultState));
}
jest.mock('../../lib/util/data', () => ({
@@ -265,4 +269,5 @@ module.exports = {
removeState,
writeEmptyState,
stateExists,
getDefaultState,
};