mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-08-27 21:20:03 +00:00
feat: Use Vitest for testing (#25284)
Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
This commit is contained in:
+153
-146
@@ -1,9 +1,18 @@
|
||||
import * as data from './mocks/data';
|
||||
import {mockLogger} from './mocks/logger';
|
||||
import {mockMQTT, mockMQTTConnectAsync, events as mockMQTTEvents} from './mocks/mqtt';
|
||||
import {flushPromises, JestMockAny} from './mocks/utils';
|
||||
import {
|
||||
mockMQTTConnectAsync,
|
||||
mockMQTTEndAsync,
|
||||
events as mockMQTTEvents,
|
||||
mockMQTTPublishAsync,
|
||||
mockMQTTSubscribeAsync,
|
||||
mockMQTTUnsubscribeAsync,
|
||||
} from './mocks/mqtt';
|
||||
import {flushPromises} from './mocks/utils';
|
||||
import {devices, mockController as mockZHController, events as mockZHEvents, returnDevices} from './mocks/zigbeeHerdsman';
|
||||
|
||||
import type {Mock, MockInstance} from 'vitest';
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
@@ -18,27 +27,21 @@ import * as settings from '../lib/util/settings';
|
||||
process.env.NOTIFY_SOCKET = 'mocked';
|
||||
const LOG_MQTT_NS = 'z2m:mqtt';
|
||||
|
||||
jest.mock(
|
||||
'sd-notify',
|
||||
() => {
|
||||
return {
|
||||
watchdogInterval: jest.fn(() => 3000),
|
||||
startWatchdogMode: jest.fn(),
|
||||
stopWatchdogMode: jest.fn(),
|
||||
ready: jest.fn(),
|
||||
stopping: jest.fn(),
|
||||
};
|
||||
},
|
||||
{virtual: true},
|
||||
);
|
||||
vi.mock('sd-notify', () => ({
|
||||
watchdogInterval: vi.fn(() => 3000),
|
||||
startWatchdogMode: vi.fn(),
|
||||
stopWatchdogMode: vi.fn(),
|
||||
ready: vi.fn(),
|
||||
stopping: vi.fn(),
|
||||
}));
|
||||
|
||||
const mocksClear = [
|
||||
mockZHController.stop,
|
||||
mockMQTT.endAsync,
|
||||
mockMQTT.publishAsync,
|
||||
mockMQTT.subscribeAsync,
|
||||
mockMQTT.unsubscribeAsync,
|
||||
mockMQTT.endAsync,
|
||||
mockMQTTEndAsync,
|
||||
mockMQTTPublishAsync,
|
||||
mockMQTTSubscribeAsync,
|
||||
mockMQTTUnsubscribeAsync,
|
||||
mockMQTTEndAsync,
|
||||
mockMQTTConnectAsync,
|
||||
devices.bulb_color.removeFromNetwork,
|
||||
devices.bulb.removeFromNetwork,
|
||||
@@ -50,25 +53,25 @@ const mocksClear = [
|
||||
|
||||
describe('Controller', () => {
|
||||
let controller: Controller;
|
||||
let mockExit: JestMockAny;
|
||||
let mockExit: Mock;
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers();
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
returnDevices.splice(0);
|
||||
mockExit = jest.fn();
|
||||
mockExit = vi.fn();
|
||||
data.writeDefaultConfiguration();
|
||||
settings.reRead();
|
||||
controller = new Controller(jest.fn(), mockExit);
|
||||
controller = new Controller(vi.fn(), mockExit);
|
||||
mocksClear.forEach((m) => m.mockClear());
|
||||
settings.reRead();
|
||||
data.writeDefaultState();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
jest.useRealTimers();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -104,12 +107,12 @@ describe('Controller', () => {
|
||||
will: {payload: Buffer.from('{"state":"offline"}'), retain: true, topic: 'zigbee2mqtt/bridge/state', qos: 1},
|
||||
properties: {maximumPacketSize: 1048576},
|
||||
});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({state: 'ON', brightness: 50, color_temp: 370, linkquality: 99}),
|
||||
{retain: true, qos: 0},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote', stringify({brightness: 255}), {retain: true, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote', stringify({brightness: 255}), {retain: true, qos: 0});
|
||||
});
|
||||
|
||||
it('Start controller with specific MQTT settings', async () => {
|
||||
@@ -160,9 +163,9 @@ describe('Controller', () => {
|
||||
settings.set(['advanced', 'ext_pan_id'], 'GENERATE');
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
expect((ZHController as unknown as jest.Mock).mock.calls[0][0].network.networkKey.length).toStrictEqual(16);
|
||||
expect((ZHController as unknown as jest.Mock).mock.calls[0][0].network.extendedPanID.length).toStrictEqual(8);
|
||||
expect((ZHController as unknown as jest.Mock).mock.calls[0][0].network.panID).toStrictEqual(expect.any(Number));
|
||||
expect((ZHController as unknown as MockInstance).mock.calls[0][0].network.networkKey.length).toStrictEqual(16);
|
||||
expect((ZHController as unknown as MockInstance).mock.calls[0][0].network.extendedPanID.length).toStrictEqual(8);
|
||||
expect((ZHController as unknown as MockInstance).mock.calls[0][0].network.panID).toStrictEqual(expect.any(Number));
|
||||
expect(data.read().advanced.network_key.length).toStrictEqual(16);
|
||||
expect(data.read().advanced.ext_pan_id.length).toStrictEqual(8);
|
||||
expect(data.read().advanced.pan_id).toStrictEqual(expect.any(Number));
|
||||
@@ -172,13 +175,13 @@ describe('Controller', () => {
|
||||
data.writeDefaultState();
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({state: 'ON', brightness: 50, color_temp: 370, linkquality: 99}),
|
||||
{qos: 0, retain: true},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote', stringify({brightness: 255}), {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote', stringify({brightness: 255}), {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Start controller should not publish cached states when disabled', async () => {
|
||||
@@ -186,7 +189,7 @@ describe('Controller', () => {
|
||||
data.writeDefaultState();
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
const publishedTopics = mockMQTT.publishAsync.mock.calls.map((m) => m[0]);
|
||||
const publishedTopics = mockMQTTPublishAsync.mock.calls.map((m) => m[0]);
|
||||
expect(publishedTopics).toEqual(expect.not.arrayContaining(['zigbee2mqtt/bulb', 'zigbee2mqtt/remote']));
|
||||
});
|
||||
|
||||
@@ -195,12 +198,12 @@ describe('Controller', () => {
|
||||
data.writeDefaultState();
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
`{"state":"ON","brightness":50,"color_temp":370,"linkquality":99}`,
|
||||
{qos: 0, retain: true},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/remote', `{"brightness":255}`, {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/remote', `{"brightness":255}`, {qos: 0, retain: true});
|
||||
});
|
||||
|
||||
it('Log when MQTT client is unavailable', async () => {
|
||||
@@ -209,7 +212,7 @@ describe('Controller', () => {
|
||||
mockLogger.error.mockClear();
|
||||
// @ts-expect-error private
|
||||
controller.mqtt.client.reconnecting = true;
|
||||
jest.advanceTimersByTime(11 * 1000);
|
||||
vi.advanceTimersByTime(11 * 1000);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith('Not connected to MQTT server!');
|
||||
// @ts-expect-error private
|
||||
controller.mqtt.client.reconnecting = false;
|
||||
@@ -289,7 +292,7 @@ describe('Controller', () => {
|
||||
it('Start controller and stop with restart', async () => {
|
||||
await controller.start();
|
||||
await controller.stop(true);
|
||||
expect(mockMQTT.endAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTEndAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockZHController.stop).toHaveBeenCalledTimes(1);
|
||||
expect(mockExit).toHaveBeenCalledTimes(1);
|
||||
expect(mockExit).toHaveBeenCalledWith(0, true);
|
||||
@@ -299,7 +302,7 @@ describe('Controller', () => {
|
||||
mockZHController.stop.mockRejectedValueOnce('failed');
|
||||
await controller.start();
|
||||
await controller.stop();
|
||||
expect(mockMQTT.endAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTEndAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockZHController.stop).toHaveBeenCalledTimes(1);
|
||||
expect(mockExit).toHaveBeenCalledTimes(1);
|
||||
expect(mockExit).toHaveBeenCalledWith(1, false);
|
||||
@@ -310,7 +313,7 @@ describe('Controller', () => {
|
||||
await controller.start();
|
||||
await mockZHEvents.adapterDisconnected();
|
||||
await flushPromises();
|
||||
expect(mockMQTT.endAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTEndAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockZHController.stop).toHaveBeenCalledTimes(1);
|
||||
expect(mockExit).toHaveBeenCalledTimes(1);
|
||||
expect(mockExit).toHaveBeenCalledWith(1, false);
|
||||
@@ -324,13 +327,15 @@ describe('Controller', () => {
|
||||
mockLogger.info.mockClear();
|
||||
|
||||
mockMQTTEvents.error(new Error('ECONNRESET'));
|
||||
mockMQTT.reconnecting = true;
|
||||
// @ts-expect-error private
|
||||
controller.mqtt.client.reconnecting = true;
|
||||
expect(mockLogger.error).toHaveBeenCalledWith('MQTT error: ECONNRESET');
|
||||
|
||||
await jest.advanceTimersByTimeAsync(11000);
|
||||
await vi.advanceTimersByTimeAsync(11000);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith('Not connected to MQTT server!');
|
||||
|
||||
mockMQTT.reconnecting = false;
|
||||
// @ts-expect-error private
|
||||
controller.mqtt.client.reconnecting = false;
|
||||
await mockMQTTEvents.connect();
|
||||
expect(mockLogger.info).toHaveBeenCalledWith('Connected to MQTT server');
|
||||
});
|
||||
@@ -346,13 +351,15 @@ describe('Controller', () => {
|
||||
reasonCode: 149,
|
||||
properties: {reasonString: 'Maximum packet size was exceeded'},
|
||||
});
|
||||
mockMQTT.disconnecting = true;
|
||||
// @ts-expect-error private
|
||||
controller.mqtt.client.disconnecting = true;
|
||||
expect(mockLogger.error).toHaveBeenCalledWith('MQTT disconnect: reason 149 (Maximum packet size was exceeded)');
|
||||
|
||||
await jest.advanceTimersByTimeAsync(11000);
|
||||
await vi.advanceTimersByTimeAsync(11000);
|
||||
expect(mockLogger.error).toHaveBeenCalledWith('Not connected to MQTT server!');
|
||||
|
||||
mockMQTT.disconnecting = false;
|
||||
// @ts-expect-error private
|
||||
controller.mqtt.client.disconnecting = false;
|
||||
await mockMQTTEvents.connect();
|
||||
expect(mockLogger.info).toHaveBeenCalledWith('Connected to MQTT server');
|
||||
});
|
||||
@@ -360,9 +367,9 @@ describe('Controller', () => {
|
||||
it('Handles MQTT publish error', async () => {
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
// fail on device_joined (has skipLog=false)
|
||||
mockMQTT.publishAsync.mockImplementationOnce(mockMQTT.publishAsync.getMockImplementation()!).mockImplementationOnce(() => {
|
||||
mockMQTTPublishAsync.mockImplementationOnce(mockMQTTPublishAsync.getMockImplementation()!).mockImplementationOnce(() => {
|
||||
throw new Error('client disconnecting');
|
||||
});
|
||||
await mockZHEvents.deviceJoined({device: devices.bulb});
|
||||
@@ -373,7 +380,7 @@ describe('Controller', () => {
|
||||
|
||||
it('Handle mqtt message', async () => {
|
||||
// @ts-expect-error private
|
||||
const spyEventbusEmitMQTTMessage = jest.spyOn(controller.eventBus, 'emitMQTTMessage').mockImplementation();
|
||||
const spyEventbusEmitMQTTMessage = vi.spyOn(controller.eventBus, 'emitMQTTMessage').mockImplementation(vi.fn());
|
||||
|
||||
await controller.start();
|
||||
mockLogger.debug.mockClear();
|
||||
@@ -384,7 +391,7 @@ describe('Controller', () => {
|
||||
|
||||
it('Skip MQTT messages on topic we published to', async () => {
|
||||
// @ts-expect-error private
|
||||
const spyEventbusEmitMQTTMessage = jest.spyOn(controller.eventBus, 'emitMQTTMessage').mockImplementation();
|
||||
const spyEventbusEmitMQTTMessage = vi.spyOn(controller.eventBus, 'emitMQTTMessage').mockImplementation(vi.fn());
|
||||
|
||||
await controller.start();
|
||||
mockLogger.debug.mockClear();
|
||||
@@ -450,7 +457,7 @@ describe('Controller', () => {
|
||||
const payload = {device};
|
||||
await mockZHEvents.deviceJoined(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/event',
|
||||
stringify({type: 'device_joined', data: {friendly_name: 'bulb', ieee_address: device.ieeeAddr}}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -461,7 +468,7 @@ describe('Controller', () => {
|
||||
await controller.start();
|
||||
const device = devices.bulb;
|
||||
settings.set(['blocklist'], [device.ieeeAddr]);
|
||||
const handler = (ZHController as unknown as jest.Mock).mock.calls[0][0].acceptJoiningDeviceHandler;
|
||||
const handler = (ZHController as unknown as MockInstance).mock.calls[0][0].acceptJoiningDeviceHandler;
|
||||
expect(await handler(device.ieeeAddr)).toBe(false);
|
||||
});
|
||||
|
||||
@@ -469,7 +476,7 @@ describe('Controller', () => {
|
||||
await controller.start();
|
||||
const device = devices.bulb;
|
||||
settings.set(['blocklist'], ['123']);
|
||||
const handler = (ZHController as unknown as jest.Mock).mock.calls[0][0].acceptJoiningDeviceHandler;
|
||||
const handler = (ZHController as unknown as MockInstance).mock.calls[0][0].acceptJoiningDeviceHandler;
|
||||
expect(await handler(device.ieeeAddr)).toBe(true);
|
||||
});
|
||||
|
||||
@@ -477,7 +484,7 @@ describe('Controller', () => {
|
||||
await controller.start();
|
||||
const device = devices.bulb;
|
||||
settings.set(['passlist'], [device.ieeeAddr]);
|
||||
const handler = (ZHController as unknown as jest.Mock).mock.calls[0][0].acceptJoiningDeviceHandler;
|
||||
const handler = (ZHController as unknown as MockInstance).mock.calls[0][0].acceptJoiningDeviceHandler;
|
||||
expect(await handler(device.ieeeAddr)).toBe(true);
|
||||
});
|
||||
|
||||
@@ -485,7 +492,7 @@ describe('Controller', () => {
|
||||
await controller.start();
|
||||
const device = devices.bulb;
|
||||
settings.set(['passlist'], ['123']);
|
||||
const handler = (ZHController as unknown as jest.Mock).mock.calls[0][0].acceptJoiningDeviceHandler;
|
||||
const handler = (ZHController as unknown as MockInstance).mock.calls[0][0].acceptJoiningDeviceHandler;
|
||||
expect(await handler(device.ieeeAddr)).toBe(false);
|
||||
});
|
||||
|
||||
@@ -494,14 +501,14 @@ describe('Controller', () => {
|
||||
const device = devices.bulb;
|
||||
settings.set(['passlist'], [device.ieeeAddr]);
|
||||
settings.set(['blocklist'], [device.ieeeAddr]);
|
||||
const handler = (ZHController as unknown as jest.Mock).mock.calls[0][0].acceptJoiningDeviceHandler;
|
||||
const handler = (ZHController as unknown as MockInstance).mock.calls[0][0].acceptJoiningDeviceHandler;
|
||||
expect(await handler(device.ieeeAddr)).toBe(true);
|
||||
});
|
||||
|
||||
it('acceptJoiningDeviceHandler accept when not on blocklist and passlist', async () => {
|
||||
await controller.start();
|
||||
const device = devices.bulb;
|
||||
const handler = (ZHController as unknown as jest.Mock).mock.calls[0][0].acceptJoiningDeviceHandler;
|
||||
const handler = (ZHController as unknown as MockInstance).mock.calls[0][0].acceptJoiningDeviceHandler;
|
||||
expect(await handler(device.ieeeAddr)).toBe(true);
|
||||
});
|
||||
|
||||
@@ -512,7 +519,7 @@ describe('Controller', () => {
|
||||
mockZHEvents.deviceJoined(payload);
|
||||
mockZHEvents.deviceJoined(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/event',
|
||||
stringify({type: 'device_joined', data: {friendly_name: 'bulb', ieee_address: device.ieeeAddr}}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -525,7 +532,7 @@ describe('Controller', () => {
|
||||
const payload = {device, status: 'started'};
|
||||
await mockZHEvents.deviceInterview(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/event',
|
||||
stringify({type: 'device_interview', data: {friendly_name: 'bulb', status: 'started', ieee_address: device.ieeeAddr}}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -538,7 +545,7 @@ describe('Controller', () => {
|
||||
const payload = {device, status: 'failed'};
|
||||
await mockZHEvents.deviceInterview(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/event',
|
||||
stringify({type: 'device_interview', data: {friendly_name: 'bulb', status: 'failed', ieee_address: device.ieeeAddr}}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -547,13 +554,13 @@ describe('Controller', () => {
|
||||
|
||||
it('On zigbee deviceInterview successful supported', async () => {
|
||||
await controller.start();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
const device = devices.bulb;
|
||||
const payload = {device, status: 'successful'};
|
||||
await mockZHEvents.deviceInterview(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/bridge/event');
|
||||
const parsedMessage = JSON.parse(mockMQTT.publishAsync.mock.calls[1][1]);
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/bridge/event');
|
||||
const parsedMessage = JSON.parse(mockMQTTPublishAsync.mock.calls[1][1]);
|
||||
expect(parsedMessage.type).toStrictEqual('device_interview');
|
||||
expect(parsedMessage.data.friendly_name).toStrictEqual('bulb');
|
||||
expect(parsedMessage.data.status).toStrictEqual('successful');
|
||||
@@ -564,18 +571,18 @@ describe('Controller', () => {
|
||||
expect(parsedMessage.data.definition.description).toStrictEqual('TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm');
|
||||
expect(parsedMessage.data.definition.exposes).toStrictEqual(expect.any(Array));
|
||||
expect(parsedMessage.data.definition.options).toStrictEqual(expect.any(Array));
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][2]).toStrictEqual({retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][2]).toStrictEqual({retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('On zigbee deviceInterview successful not supported', async () => {
|
||||
await controller.start();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
const device = devices.unsupported;
|
||||
const payload = {device, status: 'successful'};
|
||||
await mockZHEvents.deviceInterview(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/bridge/event');
|
||||
const parsedMessage = JSON.parse(mockMQTT.publishAsync.mock.calls[1][1]);
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/bridge/event');
|
||||
const parsedMessage = JSON.parse(mockMQTTPublishAsync.mock.calls[1][1]);
|
||||
expect(parsedMessage.type).toStrictEqual('device_interview');
|
||||
expect(parsedMessage.data.friendly_name).toStrictEqual(device.ieeeAddr);
|
||||
expect(parsedMessage.data.status).toStrictEqual('successful');
|
||||
@@ -586,7 +593,7 @@ describe('Controller', () => {
|
||||
expect(parsedMessage.data.definition.description).toStrictEqual('Automatically generated definition');
|
||||
expect(parsedMessage.data.definition.exposes).toStrictEqual(expect.any(Array));
|
||||
expect(parsedMessage.data.definition.options).toStrictEqual(expect.any(Array));
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][2]).toStrictEqual({retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][2]).toStrictEqual({retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('On zigbee event device announce', async () => {
|
||||
@@ -596,7 +603,7 @@ describe('Controller', () => {
|
||||
await mockZHEvents.deviceAnnounce(payload);
|
||||
await flushPromises();
|
||||
expect(mockLogger.debug).toHaveBeenCalledWith(`Device 'bulb' announced itself`);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/event',
|
||||
stringify({type: 'device_announce', data: {friendly_name: 'bulb', ieee_address: device.ieeeAddr}}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -607,12 +614,12 @@ describe('Controller', () => {
|
||||
await controller.start();
|
||||
returnDevices.push('0x00124b00120144ae');
|
||||
settings.set(['devices'], {});
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
const device = devices.bulb;
|
||||
const payload = {ieeeAddr: device.ieeeAddr};
|
||||
await mockZHEvents.deviceLeave(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/event',
|
||||
stringify({type: 'device_leave', data: {ieee_address: device.ieeeAddr, friendly_name: device.ieeeAddr}}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -623,11 +630,11 @@ describe('Controller', () => {
|
||||
await controller.start();
|
||||
returnDevices.push('0x00124b00120144ae');
|
||||
const device = devices.bulb;
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
const payload = {ieeeAddr: device.ieeeAddr};
|
||||
await mockZHEvents.deviceLeave(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/event',
|
||||
stringify({type: 'device_leave', data: {ieee_address: device.ieeeAddr, friendly_name: 'bulb'}}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -637,7 +644,7 @@ describe('Controller', () => {
|
||||
it('Publish entity state attribute output', async () => {
|
||||
await controller.start();
|
||||
settings.set(['advanced', 'output'], 'attribute');
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
// @ts-expect-error private
|
||||
const device = controller.zigbee.resolveEntity('bulb')!;
|
||||
await controller.publishEntityState(device, {
|
||||
@@ -650,30 +657,30 @@ describe('Controller', () => {
|
||||
brightness: 50,
|
||||
});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '50', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/color_temp', '370', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/color', '100,50,10', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/dummy-1', 'yes', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/dummy-2', 'no', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/test1', '', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/test', '', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '50', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/color_temp', '370', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/color', '100,50,10', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/dummy-1', 'yes', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/dummy-2', 'no', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/test1', '', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/test', '', {qos: 0, retain: true});
|
||||
});
|
||||
|
||||
it('Publish entity state attribute_json output', async () => {
|
||||
await controller.start();
|
||||
settings.set(['advanced', 'output'], 'attribute_and_json');
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
// @ts-expect-error private
|
||||
const device = controller.zigbee.resolveEntity('bulb')!;
|
||||
await controller.publishEntityState(device, {state: 'ON', brightness: 200, color_temp: 370, linkquality: 99});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(5);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/color_temp', '370', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/linkquality', '99', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(5);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/color_temp', '370', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/linkquality', '99', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({state: 'ON', brightness: 200, color_temp: 370, linkquality: 99}),
|
||||
{qos: 0, retain: true},
|
||||
@@ -684,37 +691,37 @@ describe('Controller', () => {
|
||||
await controller.start();
|
||||
settings.set(['advanced', 'output'], 'attribute_and_json');
|
||||
settings.set(['devices', devices.bulb.ieeeAddr, 'filtered_attributes'], ['color_temp', 'linkquality']);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
// @ts-expect-error private
|
||||
const device = controller.zigbee.resolveEntity('bulb')!;
|
||||
await controller.publishEntityState(device, {state: 'ON', brightness: 200, color_temp: 370, linkquality: 99});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 200}), {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 200}), {qos: 0, retain: true});
|
||||
});
|
||||
|
||||
it('Publish entity state attribute_json output filtered (device_options)', async () => {
|
||||
await controller.start();
|
||||
settings.set(['advanced', 'output'], 'attribute_and_json');
|
||||
settings.set(['device_options', 'filtered_attributes'], ['color_temp', 'linkquality']);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
// @ts-expect-error private
|
||||
const device = controller.zigbee.resolveEntity('bulb')!;
|
||||
await controller.publishEntityState(device, {state: 'ON', brightness: 200, color_temp: 370, linkquality: 99});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 200}), {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 200}), {qos: 0, retain: true});
|
||||
});
|
||||
|
||||
it('Publish entity state attribute_json output filtered cache', async () => {
|
||||
await controller.start();
|
||||
settings.set(['advanced', 'output'], 'attribute_and_json');
|
||||
settings.set(['devices', devices.bulb.ieeeAddr, 'filtered_cache'], ['linkquality']);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
// @ts-expect-error private
|
||||
const device = controller.zigbee.resolveEntity('bulb')!;
|
||||
@@ -726,11 +733,11 @@ describe('Controller', () => {
|
||||
|
||||
// @ts-expect-error private
|
||||
expect(controller.state.state[device.ieeeAddr]).toStrictEqual({brightness: 200, color_temp: 370, state: 'ON'});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(5);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/linkquality', '87', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(5);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/linkquality', '87', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({state: 'ON', brightness: 200, color_temp: 370, linkquality: 87}),
|
||||
{qos: 0, retain: true},
|
||||
@@ -741,7 +748,7 @@ describe('Controller', () => {
|
||||
await controller.start();
|
||||
settings.set(['advanced', 'output'], 'attribute_and_json');
|
||||
settings.set(['device_options', 'filtered_cache'], ['linkquality']);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
// @ts-expect-error private
|
||||
const device = controller.zigbee.resolveEntity('bulb')!;
|
||||
@@ -753,11 +760,11 @@ describe('Controller', () => {
|
||||
|
||||
// @ts-expect-error private
|
||||
expect(controller.state.state[device.ieeeAddr]).toStrictEqual({brightness: 200, color_temp: 370, state: 'ON'});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(5);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/linkquality', '87', {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(5);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/linkquality', '87', {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({state: 'ON', brightness: 200, color_temp: 370, linkquality: 87}),
|
||||
{qos: 0, retain: true},
|
||||
@@ -767,12 +774,12 @@ describe('Controller', () => {
|
||||
it('Publish entity state with device information', async () => {
|
||||
await controller.start();
|
||||
settings.set(['mqtt', 'include_device_information'], true);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
// @ts-expect-error private
|
||||
let device = controller.zigbee.resolveEntity('bulb')!;
|
||||
await controller.publishEntityState(device, {state: 'ON'});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({
|
||||
state: 'ON',
|
||||
@@ -797,7 +804,7 @@ describe('Controller', () => {
|
||||
device = controller.zigbee.resolveEntity('unsupported2')!;
|
||||
await controller.publishEntityState(device, {state: 'ON'});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/unsupported2',
|
||||
stringify({
|
||||
state: 'ON',
|
||||
@@ -818,12 +825,12 @@ describe('Controller', () => {
|
||||
it('Should publish entity state without retain', async () => {
|
||||
await controller.start();
|
||||
settings.set(['devices', devices.bulb.ieeeAddr, 'retain'], false);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
// @ts-expect-error private
|
||||
const device = controller.zigbee.resolveEntity('bulb')!;
|
||||
await controller.publishEntityState(device, {state: 'ON'});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({state: 'ON', brightness: 50, color_temp: 370, linkquality: 99}),
|
||||
{qos: 0, retain: false},
|
||||
@@ -833,12 +840,12 @@ describe('Controller', () => {
|
||||
it('Should publish entity state with retain', async () => {
|
||||
await controller.start();
|
||||
settings.set(['devices', devices.bulb.ieeeAddr, 'retain'], true);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
// @ts-expect-error private
|
||||
const device = controller.zigbee.resolveEntity('bulb')!;
|
||||
await controller.publishEntityState(device, {state: 'ON'});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({state: 'ON', brightness: 50, color_temp: 370, linkquality: 99}),
|
||||
{qos: 0, retain: true},
|
||||
@@ -850,12 +857,12 @@ describe('Controller', () => {
|
||||
settings.set(['mqtt', 'version'], 5);
|
||||
settings.set(['devices', devices.bulb.ieeeAddr, 'retain'], true);
|
||||
settings.set(['devices', devices.bulb.ieeeAddr, 'retention'], 37);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
// @ts-expect-error private
|
||||
const device = controller.zigbee.resolveEntity('bulb')!;
|
||||
await controller.publishEntityState(device, {state: 'ON'});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({state: 'ON', brightness: 50, color_temp: 370, linkquality: 99}),
|
||||
{qos: 0, retain: true, properties: {messageExpiryInterval: 37}},
|
||||
@@ -865,27 +872,27 @@ describe('Controller', () => {
|
||||
it('Publish entity state no empty messages', async () => {
|
||||
data.writeEmptyState();
|
||||
await controller.start();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
// @ts-expect-error private
|
||||
const device = controller.zigbee.resolveEntity('bulb')!;
|
||||
await controller.publishEntityState(device, {});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('Should allow to disable state persistency', async () => {
|
||||
settings.set(['advanced', 'cache_state_persistent'], false);
|
||||
data.removeState();
|
||||
await controller.start();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
// @ts-expect-error private
|
||||
const device = controller.zigbee.resolveEntity('bulb')!;
|
||||
await controller.publishEntityState(device, {state: 'ON'});
|
||||
await controller.publishEntityState(device, {brightness: 200});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON'}), {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 200}), {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON'}), {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 200}), {qos: 0, retain: true});
|
||||
await controller.stop();
|
||||
expect(data.stateExists()).toBeFalsy();
|
||||
});
|
||||
@@ -905,15 +912,15 @@ describe('Controller', () => {
|
||||
settings.set(['advanced', 'cache_state'], false);
|
||||
data.writeEmptyState();
|
||||
await controller.start();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
// @ts-expect-error private
|
||||
const device = controller.zigbee.resolveEntity('bulb')!;
|
||||
await controller.publishEntityState(device, {state: 'ON'});
|
||||
await controller.publishEntityState(device, {brightness: 200});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON'}), {qos: 0, retain: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({brightness: 200}), {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON'}), {qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({brightness: 200}), {qos: 0, retain: true});
|
||||
});
|
||||
|
||||
it('Should start when state is corrupted', async () => {
|
||||
@@ -945,44 +952,44 @@ describe('Controller', () => {
|
||||
controller.mqtt.retainedMessages,
|
||||
).length;
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
await jest.advanceTimersByTimeAsync(2500); // before any startup configure triggers
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await vi.advanceTimersByTimeAsync(2500); // before any startup configure triggers
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(retainedMessages);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(retainedMessages);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0});
|
||||
});
|
||||
|
||||
it('Should not republish retained messages on MQTT initial connect when retained message are sent', async () => {
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bridge/info', 'dummy');
|
||||
await jest.advanceTimersByTimeAsync(2500); // before any startup configure triggers
|
||||
await vi.advanceTimersByTimeAsync(2500); // before any startup configure triggers
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('Should prevent any message being published with retain flag when force_disable_retain is set', async () => {
|
||||
settings.set(['mqtt', 'force_disable_retain'], true);
|
||||
// @ts-expect-error private
|
||||
await controller.mqtt.connect();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
// @ts-expect-error private
|
||||
await controller.mqtt.publish('fo', 'bar', {retain: true});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/fo', 'bar', {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/fo', 'bar', {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should publish last seen changes', async () => {
|
||||
settings.set(['advanced', 'last_seen'], 'epoch');
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
const device = devices.remote;
|
||||
await mockZHEvents.lastSeenChanged({device, reason: 'deviceAnnounce'});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote', stringify({brightness: 255, last_seen: 1000}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote', stringify({brightness: 255, last_seen: 1000}), {
|
||||
qos: 0,
|
||||
retain: true,
|
||||
});
|
||||
@@ -992,10 +999,10 @@ describe('Controller', () => {
|
||||
settings.set(['advanced', 'last_seen'], 'epoch');
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
const device = devices.remote;
|
||||
await mockZHEvents.lastSeenChanged({device, reason: 'messageEmitted'});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('Ignore messages from coordinator', async () => {
|
||||
@@ -1034,7 +1041,7 @@ describe('Controller', () => {
|
||||
it('EventBus should handle errors', async () => {
|
||||
// @ts-expect-error private
|
||||
const eventbus = controller.eventBus;
|
||||
const callback = jest.fn().mockImplementation(async () => {
|
||||
const callback = vi.fn().mockImplementation(async () => {
|
||||
throw new Error('Whoops!');
|
||||
});
|
||||
eventbus.onStateChange({constructor: {name: 'Test'}}, callback);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as data from '../mocks/data';
|
||||
import {mockLogger} from '../mocks/logger';
|
||||
import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt';
|
||||
import {events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt';
|
||||
import {flushPromises} from '../mocks/utils';
|
||||
import {devices, events as mockZHEvents, returnDevices} from '../mocks/zigbeeHerdsman';
|
||||
|
||||
@@ -13,7 +13,7 @@ import Availability from '../../lib/extension/availability';
|
||||
import * as settings from '../../lib/util/settings';
|
||||
import utils from '../../lib/util/utils';
|
||||
|
||||
const mocksClear = [mockMQTT.publishAsync, mockLogger.warning, mockLogger.info];
|
||||
const mocksClear = [mockMQTTPublishAsync, mockLogger.warning, mockLogger.info];
|
||||
|
||||
returnDevices.push(
|
||||
devices.bulb_color.ieeeAddr,
|
||||
@@ -37,22 +37,22 @@ describe('Extension: Availability', () => {
|
||||
};
|
||||
|
||||
const setTimeAndAdvanceTimers = async (value: number): Promise<void> => {
|
||||
jest.setSystemTime(Date.now() + value);
|
||||
await jest.advanceTimersByTimeAsync(value);
|
||||
vi.setSystemTime(Date.now() + value);
|
||||
await vi.advanceTimersByTimeAsync(value);
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.spyOn(utils, 'sleep').mockImplementation();
|
||||
jest.useFakeTimers();
|
||||
vi.spyOn(utils, 'sleep').mockImplementation(vi.fn());
|
||||
vi.useFakeTimers();
|
||||
settings.reRead();
|
||||
settings.set(['availability'], {enabled: true});
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.setSystemTime(utils.minutes(1));
|
||||
vi.setSystemTime(utils.minutes(1));
|
||||
data.writeDefaultConfiguration();
|
||||
settings.reRead();
|
||||
settings.set(['availability'], {enabled: true});
|
||||
@@ -67,16 +67,16 @@ describe('Extension: Availability', () => {
|
||||
|
||||
afterAll(async () => {
|
||||
await controller.stop();
|
||||
jest.useRealTimers();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('Should publish availability on startup for device where it is enabled for', async () => {
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'online'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'online'}), {
|
||||
retain: true,
|
||||
qos: 1,
|
||||
});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote/availability', stringify({state: 'online'}), {retain: true, qos: 1});
|
||||
expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bulb_color_2/availability', stringify({state: 'online'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote/availability', stringify({state: 'online'}), {retain: true, qos: 1});
|
||||
expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bulb_color_2/availability', stringify({state: 'online'}), {
|
||||
retain: true,
|
||||
qos: 1,
|
||||
});
|
||||
@@ -106,14 +106,14 @@ describe('Extension: Availability', () => {
|
||||
});
|
||||
|
||||
it('Should publish offline for active device when not seen for 10 minutes', async () => {
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await setTimeAndAdvanceTimers(utils.minutes(5));
|
||||
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
|
||||
|
||||
await setTimeAndAdvanceTimers(utils.minutes(7));
|
||||
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
|
||||
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(1, true);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'offline'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'offline'}), {
|
||||
retain: true,
|
||||
qos: 1,
|
||||
});
|
||||
@@ -127,20 +127,20 @@ describe('Extension: Availability', () => {
|
||||
});
|
||||
|
||||
it('Should publish offline for passive device when not seen for 25 hours', async () => {
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await setTimeAndAdvanceTimers(utils.hours(26));
|
||||
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote/availability', stringify({state: 'offline'}), {retain: true, qos: 1});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote/availability', stringify({state: 'offline'}), {retain: true, qos: 1});
|
||||
});
|
||||
|
||||
it('Should reset ping timer when device last seen changes for active device', async () => {
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
await setTimeAndAdvanceTimers(utils.minutes(5));
|
||||
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
|
||||
|
||||
await mockZHEvents.lastSeenChanged({device: devices.bulb_color});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'offline'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'offline'}), {
|
||||
retain: true,
|
||||
qos: 1,
|
||||
});
|
||||
@@ -154,7 +154,7 @@ describe('Extension: Availability', () => {
|
||||
});
|
||||
|
||||
it('Should ping again when first ping fails', async () => {
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
await mockZHEvents.lastSeenChanged({device: devices.bulb_color});
|
||||
|
||||
@@ -169,13 +169,13 @@ describe('Extension: Availability', () => {
|
||||
});
|
||||
|
||||
it('Should reset ping timer when device last seen changes for passive device', async () => {
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
await setTimeAndAdvanceTimers(utils.hours(24));
|
||||
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
|
||||
|
||||
await mockZHEvents.lastSeenChanged({device: devices.remote});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote/availability', stringify({state: 'offline'}), {retain: true, qos: 1});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote/availability', stringify({state: 'offline'}), {retain: true, qos: 1});
|
||||
|
||||
await setTimeAndAdvanceTimers(utils.hours(25));
|
||||
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
|
||||
@@ -185,10 +185,10 @@ describe('Extension: Availability', () => {
|
||||
});
|
||||
|
||||
it('Should immediately mark device as online when it lastSeen changes', async () => {
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
await setTimeAndAdvanceTimers(utils.minutes(15));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'offline'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'offline'}), {
|
||||
retain: true,
|
||||
qos: 1,
|
||||
});
|
||||
@@ -196,7 +196,7 @@ describe('Extension: Availability', () => {
|
||||
devices.bulb_color.lastSeen = Date.now();
|
||||
await mockZHEvents.lastSeenChanged({device: devices.bulb_color});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'online'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'online'}), {
|
||||
retain: true,
|
||||
qos: 1,
|
||||
});
|
||||
@@ -310,18 +310,18 @@ describe('Extension: Availability', () => {
|
||||
});
|
||||
|
||||
it('Should republish availability when device is renamed', async () => {
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/rename', stringify({from: 'bulb_color', to: 'bulb_new_name'}));
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', '', {retain: true, qos: 1});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name/availability', stringify({state: 'online'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', '', {retain: true, qos: 1});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name/availability', stringify({state: 'online'}), {
|
||||
retain: true,
|
||||
qos: 1,
|
||||
});
|
||||
await setTimeAndAdvanceTimers(utils.hours(12));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name/availability', stringify({state: 'offline'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name/availability', stringify({state: 'offline'}), {
|
||||
retain: true,
|
||||
qos: 1,
|
||||
});
|
||||
@@ -330,10 +330,10 @@ describe('Extension: Availability', () => {
|
||||
it('Should publish availability payload in JSON format', async () => {
|
||||
await resetExtension();
|
||||
devices.remote.ping.mockClear();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await setTimeAndAdvanceTimers(utils.hours(26));
|
||||
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote/availability', stringify({state: 'offline'}), {retain: true, qos: 1});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote/availability', stringify({state: 'offline'}), {retain: true, qos: 1});
|
||||
});
|
||||
|
||||
it('Should publish availability for groups', async () => {
|
||||
@@ -341,21 +341,21 @@ describe('Extension: Availability', () => {
|
||||
await resetExtension();
|
||||
devices.bulb_color_2.ping.mockClear();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability', stringify({state: 'online'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability', stringify({state: 'online'}), {
|
||||
retain: true,
|
||||
qos: 1,
|
||||
});
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await setTimeAndAdvanceTimers(utils.minutes(12));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability', stringify({state: 'offline'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability', stringify({state: 'offline'}), {
|
||||
retain: true,
|
||||
qos: 1,
|
||||
});
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
devices.bulb_color_2.lastSeen = Date.now();
|
||||
await mockZHEvents.lastSeenChanged({device: devices.bulb_color_2});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability', stringify({state: 'online'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability', stringify({state: 'online'}), {
|
||||
retain: true,
|
||||
qos: 1,
|
||||
});
|
||||
@@ -365,7 +365,7 @@ describe('Extension: Availability', () => {
|
||||
// @ts-expect-error private
|
||||
const availability = controller.extensions.find((extension) => extension instanceof Availability)!;
|
||||
// @ts-expect-error private
|
||||
const publishAvailabilitySpy = jest.spyOn(availability, 'publishAvailability');
|
||||
const publishAvailabilitySpy = vi.spyOn(availability, 'publishAvailability');
|
||||
|
||||
devices.bulb_color.ping.mockImplementationOnce(() => new Promise((resolve) => setTimeout(resolve, 1000)));
|
||||
// @ts-expect-error private
|
||||
@@ -380,7 +380,7 @@ describe('Extension: Availability', () => {
|
||||
expect(availability.pingQueue).toEqual([]);
|
||||
// Validate the stop-interrupt implicitly by checking that it prevents further function invocations
|
||||
expect(publishAvailabilitySpy).not.toHaveBeenCalled();
|
||||
devices.bulb_color.ping = jest.fn(); // ensure reset
|
||||
devices.bulb_color.ping = vi.fn(); // ensure reset
|
||||
});
|
||||
|
||||
it('Should prevent instance restart', async () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as data from '../mocks/data';
|
||||
import {mockDebounce} from '../mocks/debounce';
|
||||
import {mockLogger} from '../mocks/logger';
|
||||
import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt';
|
||||
import {events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt';
|
||||
import {flushPromises} from '../mocks/utils';
|
||||
import {Device, devices, groups, events as mockZHEvents} from '../mocks/zigbeeHerdsman';
|
||||
|
||||
@@ -12,7 +12,7 @@ import * as settings from '../../lib/util/settings';
|
||||
|
||||
const mocksClear = [
|
||||
mockDebounce,
|
||||
mockMQTT.publishAsync,
|
||||
mockMQTTPublishAsync,
|
||||
devices.bulb_color.getEndpoint(1)!.configureReporting,
|
||||
devices.bulb_color.getEndpoint(1)!.bind,
|
||||
devices.bulb_color_2.getEndpoint(1)!.read,
|
||||
@@ -31,15 +31,15 @@ describe('Extension: Bind', () => {
|
||||
endpoint.read.mockClear();
|
||||
endpoint.write.mockClear();
|
||||
endpoint.configureReporting.mockClear();
|
||||
endpoint.bind = jest.fn();
|
||||
endpoint.bind = vi.fn();
|
||||
endpoint.bind.mockClear();
|
||||
endpoint.unbind.mockClear();
|
||||
}
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers();
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
vi.useFakeTimers();
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
});
|
||||
@@ -53,7 +53,7 @@ describe('Extension: Bind', () => {
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
jest.useRealTimers();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('Should bind to device and configure reporting', async () => {
|
||||
@@ -92,7 +92,7 @@ describe('Extension: Bind', () => {
|
||||
{attribute: 'currentX', minimumReportInterval: 5, maximumReportInterval: 3600, reportableChange: 1},
|
||||
{attribute: 'currentY', minimumReportInterval: 5, maximumReportInterval: 3600, reportableChange: 1},
|
||||
]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({
|
||||
transaction: '1234',
|
||||
@@ -108,7 +108,7 @@ describe('Extension: Bind', () => {
|
||||
{retain: false, qos: 0},
|
||||
);
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0});
|
||||
|
||||
// Teardown
|
||||
target.binds = originalTargetBinds;
|
||||
@@ -116,10 +116,10 @@ describe('Extension: Bind', () => {
|
||||
});
|
||||
|
||||
it('Should throw error on invalid payload', async () => {
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/bind', stringify({fromz: 'remote', to: 'bulb_color'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({data: {}, status: 'error', error: 'Invalid payload'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -168,7 +168,7 @@ describe('Extension: Bind', () => {
|
||||
{attribute: 'currentX', minimumReportInterval: 5, maximumReportInterval: 3600, reportableChange: 1},
|
||||
{attribute: 'currentY', minimumReportInterval: 5, maximumReportInterval: 3600, reportableChange: 1},
|
||||
]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({
|
||||
transaction: '1234',
|
||||
@@ -184,7 +184,7 @@ describe('Extension: Bind', () => {
|
||||
{retain: false, qos: 0},
|
||||
);
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0});
|
||||
|
||||
// Teardown
|
||||
target.binds = originalTargetBinds;
|
||||
@@ -238,7 +238,7 @@ describe('Extension: Bind', () => {
|
||||
{attribute: 'currentX', minimumReportInterval: 5, maximumReportInterval: 3600, reportableChange: 1},
|
||||
{attribute: 'currentY', minimumReportInterval: 5, maximumReportInterval: 3600, reportableChange: 1},
|
||||
]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({
|
||||
transaction: '1234',
|
||||
@@ -254,7 +254,7 @@ describe('Extension: Bind', () => {
|
||||
{retain: false, qos: 0},
|
||||
);
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0});
|
||||
|
||||
// Teardown
|
||||
target.configuredReportings = originalTargetCR;
|
||||
@@ -271,7 +271,7 @@ describe('Extension: Bind', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.bind).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.bind).toHaveBeenCalledWith('genOnOff', target);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({data: {from: 'remote', from_endpoint: 'default', to: 'bulb_color', clusters: ['genOnOff'], failed: []}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -286,7 +286,7 @@ describe('Extension: Bind', () => {
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote', to: 'button'}));
|
||||
await flushPromises();
|
||||
expect(endpoint.bind).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({data: {}, status: 'error', error: 'Nothing to bind'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -335,7 +335,7 @@ describe('Extension: Bind', () => {
|
||||
{attribute: 'currentY', minimumReportInterval: 5, maximumReportInterval: 0xffff, reportableChange: 1},
|
||||
]);
|
||||
expect(devices.bulb_color.meta.configured).toBe(332242049);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/unbind',
|
||||
stringify({
|
||||
data: {from: 'remote', from_endpoint: 'default', to: 'bulb_color', clusters: ['genScenes', 'genOnOff', 'genLevelCtrl'], failed: []},
|
||||
@@ -361,7 +361,7 @@ describe('Extension: Bind', () => {
|
||||
expect(endpoint.unbind).toHaveBeenCalledWith('genOnOff', target);
|
||||
expect(endpoint.unbind).toHaveBeenCalledWith('genLevelCtrl', target);
|
||||
expect(endpoint.unbind).toHaveBeenCalledWith('genScenes', target);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/unbind',
|
||||
stringify({
|
||||
data: {from: 'remote', from_endpoint: 'default', to: 'Coordinator', clusters: ['genScenes', 'genOnOff', 'genLevelCtrl'], failed: []},
|
||||
@@ -392,7 +392,7 @@ describe('Extension: Bind', () => {
|
||||
expect(target1Member.configureReporting).toHaveBeenCalledWith('genLevelCtrl', [
|
||||
{attribute: 'currentLevel', maximumReportInterval: 3600, minimumReportInterval: 5, reportableChange: 1},
|
||||
]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({
|
||||
data: {from: 'remote', from_endpoint: 'default', to: 'group_1', clusters: ['genScenes', 'genOnOff', 'genLevelCtrl'], failed: []},
|
||||
@@ -428,7 +428,7 @@ describe('Extension: Bind', () => {
|
||||
expect(endpoint.unbind).toHaveBeenCalledWith('genOnOff', target);
|
||||
expect(endpoint.unbind).toHaveBeenCalledWith('genLevelCtrl', target);
|
||||
expect(endpoint.unbind).toHaveBeenCalledWith('genScenes', target);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/unbind',
|
||||
stringify({
|
||||
data: {from: 'remote', from_endpoint: 'default', to: 'group_1', clusters: ['genScenes', 'genOnOff', 'genLevelCtrl'], failed: []},
|
||||
@@ -505,7 +505,7 @@ describe('Extension: Bind', () => {
|
||||
expect(endpoint.bind).toHaveBeenCalledWith('genOnOff', target);
|
||||
expect(endpoint.bind).toHaveBeenCalledWith('genLevelCtrl', target);
|
||||
expect(endpoint.bind).toHaveBeenCalledWith('genScenes', target);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({
|
||||
data: {from: 'remote', from_endpoint: 'default', to: '1', clusters: ['genScenes', 'genOnOff', 'genLevelCtrl'], failed: []},
|
||||
@@ -526,7 +526,7 @@ describe('Extension: Bind', () => {
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote', to: 'bulb_color'}));
|
||||
await flushPromises();
|
||||
expect(endpoint.bind).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({data: {}, status: 'error', error: 'Failed to bind'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -545,7 +545,7 @@ describe('Extension: Bind', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.bind).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.bind).toHaveBeenCalledWith('genOnOff', target);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({
|
||||
data: {from: 'remote', from_endpoint: 'ep2', to: 'wall_switch_double', to_endpoint: 'right', clusters: ['genOnOff'], failed: []},
|
||||
@@ -567,7 +567,7 @@ describe('Extension: Bind', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.bind).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.bind).toHaveBeenCalledWith('genOnOff', target);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({
|
||||
data: {from: 'remote', from_endpoint: 2, to: 'wall_switch_double', to_endpoint: 3, clusters: ['genOnOff'], failed: []},
|
||||
@@ -586,7 +586,7 @@ describe('Extension: Bind', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.bind).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.bind).toHaveBeenCalledWith('msTemperatureMeasurement', target);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({
|
||||
data: {
|
||||
@@ -611,7 +611,7 @@ describe('Extension: Bind', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.bind).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.bind).toHaveBeenCalledWith('genOnOff', target);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({data: {from: 'remote', from_endpoint: 'ep2', to: 'wall_switch', clusters: ['genOnOff'], failed: []}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -629,7 +629,7 @@ describe('Extension: Bind', () => {
|
||||
expect(endpoint.unbind).toHaveBeenCalledWith('genOnOff', 901);
|
||||
expect(endpoint.unbind).toHaveBeenCalledWith('genLevelCtrl', 901);
|
||||
expect(endpoint.unbind).toHaveBeenCalledWith('genScenes', 901);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/unbind',
|
||||
stringify({
|
||||
data: {
|
||||
@@ -650,7 +650,7 @@ describe('Extension: Bind', () => {
|
||||
mockClear(device);
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote_not_existing', to: 'bulb_color'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({data: {}, status: 'error', error: "Source device 'remote_not_existing' does not exist"}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -665,7 +665,7 @@ describe('Extension: Bind', () => {
|
||||
stringify({from: 'remote', from_endpoint: 'not_existing_endpoint', to: 'bulb_color'}),
|
||||
);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({data: {}, status: 'error', error: "Source device 'remote' does not have endpoint 'not_existing_endpoint'"}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -677,7 +677,7 @@ describe('Extension: Bind', () => {
|
||||
mockClear(device);
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote', to: 'bulb_color_not_existing'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({data: {}, status: 'error', error: "Target device or group 'bulb_color_not_existing' does not exist"}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -692,7 +692,7 @@ describe('Extension: Bind', () => {
|
||||
stringify({from: 'remote', to: 'bulb_color', to_endpoint: 'not_existing_endpoint'}),
|
||||
);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({data: {}, status: 'error', error: "Target device 'bulb_color' does not have endpoint 'not_existing_endpoint'"}),
|
||||
{retain: false, qos: 0},
|
||||
|
||||
+248
-234
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
import * as data from '../mocks/data';
|
||||
import {mockLogger} from '../mocks/logger';
|
||||
import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt';
|
||||
import {events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt';
|
||||
import {flushPromises} from '../mocks/utils';
|
||||
import {Device, devices, Endpoint, events as mockZHEvents} from '../mocks/zigbeeHerdsman';
|
||||
|
||||
@@ -9,7 +9,7 @@ import stringify from 'json-stable-stringify-without-jsonify';
|
||||
import {Controller} from '../../lib/controller';
|
||||
import * as settings from '../../lib/util/settings';
|
||||
|
||||
const mocksClear = [mockMQTT.publishAsync, mockLogger.warning, mockLogger.debug];
|
||||
const mocksClear = [mockMQTTPublishAsync, mockLogger.warning, mockLogger.debug];
|
||||
|
||||
describe('Extension: Configure', () => {
|
||||
let controller: Controller;
|
||||
@@ -65,10 +65,10 @@ describe('Extension: Configure', () => {
|
||||
const wait = async (ms: number): Promise<void> => await new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers();
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
vi.useFakeTimers();
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
await jest.runOnlyPendingTimersAsync();
|
||||
await vi.runOnlyPendingTimersAsync();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -77,11 +77,11 @@ describe('Extension: Configure', () => {
|
||||
mocksClear.forEach((m) => m.mockClear());
|
||||
coordinatorEndpoint = devices.coordinator.getEndpoint(1)!;
|
||||
await resetExtension();
|
||||
await jest.runOnlyPendingTimersAsync();
|
||||
await vi.runOnlyPendingTimersAsync();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
jest.useRealTimers();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('Should configure Router on startup', async () => {
|
||||
@@ -150,7 +150,7 @@ describe('Extension: Configure', () => {
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/configure', 'remote');
|
||||
await flushPromises();
|
||||
expectRemoteConfigured();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/configure',
|
||||
stringify({data: {id: 'remote'}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -160,7 +160,7 @@ describe('Extension: Configure', () => {
|
||||
it('Fail to configure via MQTT when device does not exist', async () => {
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/configure', stringify({id: 'not_existing_device'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/configure',
|
||||
stringify({data: {}, status: 'error', error: "Device 'not_existing_device' does not exist"}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -173,7 +173,7 @@ describe('Extension: Configure', () => {
|
||||
});
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/configure', stringify({id: 'remote'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/configure',
|
||||
stringify({data: {}, status: 'error', error: 'Failed to configure (Bind timeout after 10s)'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -183,7 +183,7 @@ describe('Extension: Configure', () => {
|
||||
it('Fail to configure via MQTT when device has no configure', async () => {
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/configure', stringify({id: '0x0017882104a44559', transaction: 20}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/configure',
|
||||
stringify({data: {}, status: 'error', error: "Device 'TS0601_thermostat' cannot be configured", transaction: 20}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -193,7 +193,7 @@ describe('Extension: Configure', () => {
|
||||
it('Handles invalid payload for configure via MQTT', async () => {
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/configure', stringify({idx: '0x0017882104a44559'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/configure',
|
||||
stringify({data: {}, status: 'error', error: 'Invalid payload'}),
|
||||
{retain: false, qos: 0},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as data from '../mocks/data';
|
||||
import {mockLogger} from '../mocks/logger';
|
||||
import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt';
|
||||
import {mockMQTTEndAsync, events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt';
|
||||
import {flushPromises} from '../mocks/utils';
|
||||
import {devices, mockController as mockZHController, returnDevices} from '../mocks/zigbeeHerdsman';
|
||||
|
||||
@@ -22,18 +22,18 @@ describe('Extension: ExternalConverters', () => {
|
||||
const mockBasePath = path.join(data.mockDir, BASE_DIR);
|
||||
let controller: Controller;
|
||||
|
||||
const existsSyncSpy = jest.spyOn(fs, 'existsSync');
|
||||
const readdirSyncSpy = jest.spyOn(fs, 'readdirSync');
|
||||
const mkdirSyncSpy = jest.spyOn(fs, 'mkdirSync');
|
||||
const rmSyncSpy = jest.spyOn(fs, 'rmSync');
|
||||
const writeFileSyncSpy = jest.spyOn(fs, 'writeFileSync');
|
||||
const existsSyncSpy = vi.spyOn(fs, 'existsSync');
|
||||
const readdirSyncSpy = vi.spyOn(fs, 'readdirSync');
|
||||
const mkdirSyncSpy = vi.spyOn(fs, 'mkdirSync');
|
||||
const rmSyncSpy = vi.spyOn(fs, 'rmSync');
|
||||
const writeFileSyncSpy = vi.spyOn(fs, 'writeFileSync');
|
||||
|
||||
const zhcAddDefinitionSpy = jest.spyOn(zhc, 'addDefinition');
|
||||
const zhcRemoveExternalDefinitionsSpy = jest.spyOn(zhc, 'removeExternalDefinitions');
|
||||
const zhcAddDefinitionSpy = vi.spyOn(zhc, 'addDefinition');
|
||||
const zhcRemoveExternalDefinitionsSpy = vi.spyOn(zhc, 'removeExternalDefinitions');
|
||||
|
||||
const mocksClear = [
|
||||
mockMQTT.endAsync,
|
||||
mockMQTT.publishAsync,
|
||||
mockMQTTEndAsync,
|
||||
mockMQTTPublishAsync,
|
||||
mockLogger.debug,
|
||||
mockLogger.error,
|
||||
mockZHController.stop,
|
||||
@@ -60,12 +60,21 @@ describe('Extension: ExternalConverters', () => {
|
||||
return controller.zigbee.resolveEntity(zhDevice)! as Device;
|
||||
};
|
||||
|
||||
const resetExtension = async (): Promise<void> => {
|
||||
await controller.enableDisableExtension(false, 'ExternalConverters');
|
||||
await controller.enableDisableExtension(true, 'ExternalConverters');
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers();
|
||||
vi.useFakeTimers();
|
||||
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
jest.useRealTimers();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -75,8 +84,6 @@ describe('Extension: ExternalConverters', () => {
|
||||
data.writeDefaultState();
|
||||
settings.reRead();
|
||||
returnDevices.push(devices.external_converter_device.ieeeAddr, devices.coordinator.ieeeAddr);
|
||||
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -85,143 +92,153 @@ describe('Extension: ExternalConverters', () => {
|
||||
await controller?.stop();
|
||||
});
|
||||
|
||||
it('loads nothing from folder', async () => {
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
|
||||
expect(existsSyncSpy).toHaveBeenCalledWith(mockBasePath);
|
||||
expect(readdirSyncSpy).not.toHaveBeenCalledWith(mockBasePath);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/converters', stringify([]), {retain: true, qos: 0});
|
||||
});
|
||||
|
||||
it('loads from folder', async () => {
|
||||
useAssets();
|
||||
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
|
||||
expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({
|
||||
description: 'external',
|
||||
model: 'external_converter_device',
|
||||
vendor: 'external',
|
||||
zigbeeModel: ['external_converter_device'],
|
||||
describe('from folder', () => {
|
||||
beforeEach(async () => {
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/converters',
|
||||
stringify([
|
||||
{name: 'mock-external-converter-multiple.js', code: getFileCode('mock-external-converter-multiple.js')},
|
||||
{name: 'mock-external-converter.js', code: getFileCode('mock-external-converter.js')},
|
||||
]),
|
||||
{retain: true, qos: 0},
|
||||
);
|
||||
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenCalledTimes(2);
|
||||
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(1, 'mock-external-converter-multiple.js');
|
||||
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(2, 'mock-external-converter.js');
|
||||
expect(zhcAddDefinitionSpy).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.objectContaining({
|
||||
mock: 1,
|
||||
model: 'external_converters_device_1',
|
||||
zigbeeModel: ['external_converter_device_1'],
|
||||
vendor: 'external_1',
|
||||
description: 'external_1',
|
||||
}),
|
||||
);
|
||||
expect(zhcAddDefinitionSpy).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({
|
||||
mock: 2,
|
||||
model: 'external_converters_device_2',
|
||||
zigbeeModel: ['external_converter_device_2'],
|
||||
vendor: 'external_2',
|
||||
description: 'external_2',
|
||||
}),
|
||||
);
|
||||
expect(zhcAddDefinitionSpy).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
expect.objectContaining({
|
||||
mock: true,
|
||||
zigbeeModel: ['external_converter_device'],
|
||||
vendor: 'external',
|
||||
model: 'external_converter_device',
|
||||
description: 'external',
|
||||
}),
|
||||
);
|
||||
|
||||
const bridgeDevices = mockMQTT.publishAsync.mock.calls.filter((c) => c[0] === 'zigbee2mqtt/bridge/devices');
|
||||
expect(bridgeDevices.length).toBe(1);
|
||||
expect(JSON.parse(bridgeDevices[0][1])).toEqual(
|
||||
expect.arrayContaining([
|
||||
it('loads nothing', async () => {
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
|
||||
expect(existsSyncSpy).toHaveBeenCalledWith(mockBasePath);
|
||||
expect(readdirSyncSpy).not.toHaveBeenCalledWith(mockBasePath);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/converters', stringify([]), {retain: true, qos: 0});
|
||||
});
|
||||
|
||||
it('loads converters', async () => {
|
||||
useAssets();
|
||||
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
|
||||
expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({
|
||||
description: 'external',
|
||||
model: 'external_converter_device',
|
||||
vendor: 'external',
|
||||
zigbeeModel: ['external_converter_device'],
|
||||
});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/converters',
|
||||
stringify([
|
||||
{name: 'mock-external-converter-multiple.js', code: getFileCode('mock-external-converter-multiple.js')},
|
||||
{name: 'mock-external-converter.js', code: getFileCode('mock-external-converter.js')},
|
||||
]),
|
||||
{retain: true, qos: 0},
|
||||
);
|
||||
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenCalledTimes(2);
|
||||
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(1, 'mock-external-converter-multiple.js');
|
||||
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(2, 'mock-external-converter.js');
|
||||
expect(zhcAddDefinitionSpy).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.objectContaining({
|
||||
model_id: 'external_converter_device',
|
||||
supported: true,
|
||||
definition: expect.objectContaining({
|
||||
description: 'external',
|
||||
model: 'external_converter_device',
|
||||
}),
|
||||
mock: 1,
|
||||
model: 'external_converters_device_1',
|
||||
zigbeeModel: ['external_converter_device_1'],
|
||||
vendor: 'external_1',
|
||||
description: 'external_1',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
);
|
||||
expect(zhcAddDefinitionSpy).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({
|
||||
mock: 2,
|
||||
model: 'external_converters_device_2',
|
||||
zigbeeModel: ['external_converter_device_2'],
|
||||
vendor: 'external_2',
|
||||
description: 'external_2',
|
||||
}),
|
||||
);
|
||||
expect(zhcAddDefinitionSpy).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
expect.objectContaining({
|
||||
mock: true,
|
||||
zigbeeModel: ['external_converter_device'],
|
||||
vendor: 'external',
|
||||
model: 'external_converter_device',
|
||||
description: 'external',
|
||||
}),
|
||||
);
|
||||
|
||||
it('saves and removes from MQTT', async () => {
|
||||
const converterName = 'foo.js';
|
||||
const converterCode = getFileCode('mock-external-converter.js');
|
||||
const converterFilePath = path.join(mockBasePath, converterName);
|
||||
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
mocksClear.forEach((m) => m.mockClear());
|
||||
|
||||
expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({
|
||||
description: 'Automatically generated definition',
|
||||
model: 'external_converter_device',
|
||||
vendor: '',
|
||||
zigbeeModel: ['external_converter_device'],
|
||||
const bridgeDevices = mockMQTTPublishAsync.mock.calls.filter((c) => c[0] === 'zigbee2mqtt/bridge/devices');
|
||||
expect(bridgeDevices.length).toBe(1);
|
||||
expect(JSON.parse(bridgeDevices[0][1])).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
model_id: 'external_converter_device',
|
||||
supported: true,
|
||||
definition: expect.objectContaining({
|
||||
description: 'external',
|
||||
model: 'external_converter_device',
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
//-- SAVE
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/save', stringify({name: converterName, code: converterCode}));
|
||||
await flushPromises();
|
||||
it('saves and removes from MQTT', async () => {
|
||||
const converterName = 'foo.js';
|
||||
const converterCode = getFileCode('mock-external-converter.js');
|
||||
const converterFilePath = path.join(mockBasePath, converterName);
|
||||
|
||||
expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({
|
||||
description: 'external',
|
||||
model: 'external_converter_device',
|
||||
vendor: 'external',
|
||||
zigbeeModel: ['external_converter_device'],
|
||||
});
|
||||
expect(mkdirSyncSpy).toHaveBeenCalledWith(mockBasePath, {recursive: true});
|
||||
expect(writeFileSyncSpy).toHaveBeenCalledWith(converterFilePath, converterCode, 'utf8');
|
||||
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenCalledTimes(1);
|
||||
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(1, converterName);
|
||||
expect(zhcAddDefinitionSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
mock: true,
|
||||
zigbeeModel: ['external_converter_device'],
|
||||
vendor: 'external',
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
mocksClear.forEach((m) => m.mockClear());
|
||||
|
||||
expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({
|
||||
description: 'Automatically generated definition',
|
||||
model: 'external_converter_device',
|
||||
vendor: '',
|
||||
zigbeeModel: ['external_converter_device'],
|
||||
});
|
||||
|
||||
//-- SAVE
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/save', stringify({name: converterName, code: converterCode}));
|
||||
await flushPromises();
|
||||
|
||||
expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({
|
||||
description: 'external',
|
||||
}),
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/converters', stringify([{name: converterName, code: converterCode}]), {
|
||||
retain: true,
|
||||
qos: 0,
|
||||
});
|
||||
model: 'external_converter_device',
|
||||
vendor: 'external',
|
||||
zigbeeModel: ['external_converter_device'],
|
||||
});
|
||||
expect(mkdirSyncSpy).toHaveBeenCalledWith(mockBasePath, {recursive: true});
|
||||
expect(writeFileSyncSpy).toHaveBeenCalledWith(converterFilePath, converterCode, 'utf8');
|
||||
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenCalledTimes(1);
|
||||
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(1, converterName);
|
||||
expect(zhcAddDefinitionSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
mock: true,
|
||||
zigbeeModel: ['external_converter_device'],
|
||||
vendor: 'external',
|
||||
model: 'external_converter_device',
|
||||
description: 'external',
|
||||
}),
|
||||
);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/converters',
|
||||
stringify([{name: converterName, code: converterCode}]),
|
||||
{
|
||||
retain: true,
|
||||
qos: 0,
|
||||
},
|
||||
);
|
||||
|
||||
//-- REMOVE
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/remove', stringify({name: converterName}));
|
||||
await flushPromises();
|
||||
//-- REMOVE
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/remove', stringify({name: converterName}));
|
||||
await flushPromises();
|
||||
|
||||
expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({
|
||||
description: 'Automatically generated definition',
|
||||
model: 'external_converter_device',
|
||||
vendor: '',
|
||||
zigbeeModel: ['external_converter_device'],
|
||||
expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({
|
||||
description: 'Automatically generated definition',
|
||||
model: 'external_converter_device',
|
||||
vendor: '',
|
||||
zigbeeModel: ['external_converter_device'],
|
||||
});
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(converterFilePath, {force: true});
|
||||
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenCalledTimes(2);
|
||||
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(2, converterName);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/converters', stringify([]), {retain: true, qos: 0});
|
||||
});
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(converterFilePath, {force: true});
|
||||
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenCalledTimes(2);
|
||||
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(2, converterName);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/converters', stringify([]), {retain: true, qos: 0});
|
||||
});
|
||||
|
||||
it('returns error on invalid code', async () => {
|
||||
@@ -229,14 +246,13 @@ describe('Extension: ExternalConverters', () => {
|
||||
const converterCode = 'definetly not a correct javascript code';
|
||||
const converterFilePath = path.join(mockBasePath, converterName);
|
||||
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
await resetExtension();
|
||||
mocksClear.forEach((m) => m.mockClear());
|
||||
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/save', stringify({name: converterName, code: converterCode}));
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/converter/save',
|
||||
expect.stringContaining(`"error":"foo.js contains invalid code`),
|
||||
{retain: false, qos: 0},
|
||||
@@ -248,14 +264,13 @@ describe('Extension: ExternalConverters', () => {
|
||||
const converterName = 'invalid.js';
|
||||
const converterFilePath = path.join(mockBasePath, converterName);
|
||||
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
await resetExtension();
|
||||
mocksClear.forEach((m) => m.mockClear());
|
||||
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/remove', stringify({name: converterName}));
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/converter/remove',
|
||||
stringify({data: {}, status: 'error', error: `${converterName} (${converterFilePath}) doesn't exists`}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -268,8 +283,7 @@ describe('Extension: ExternalConverters', () => {
|
||||
const converterCode = getFileCode('mock-external-converter.js');
|
||||
const converterFilePath = path.join(mockBasePath, converterName);
|
||||
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
await resetExtension();
|
||||
mocksClear.forEach((m) => m.mockClear());
|
||||
|
||||
const errorMsg = `Invalid definition`;
|
||||
@@ -281,7 +295,7 @@ describe('Extension: ExternalConverters', () => {
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/save', stringify({name: converterName, code: converterCode}));
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/converter/save', expect.stringContaining(errorMsg), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/converter/save', expect.stringContaining(errorMsg), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
@@ -293,8 +307,7 @@ describe('Extension: ExternalConverters', () => {
|
||||
const converterCode = getFileCode('mock-external-converter.js');
|
||||
const converterFilePath = path.join(mockBasePath, converterName);
|
||||
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
await resetExtension();
|
||||
mocksClear.forEach((m) => m.mockClear());
|
||||
|
||||
//-- SAVE
|
||||
@@ -311,7 +324,7 @@ describe('Extension: ExternalConverters', () => {
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/remove', stringify({name: converterName}));
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/converter/remove',
|
||||
stringify({data: {}, status: 'error', error: errorMsg}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -320,14 +333,13 @@ describe('Extension: ExternalConverters', () => {
|
||||
});
|
||||
|
||||
it('handles invalid payloads', async () => {
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
await resetExtension();
|
||||
mocksClear.forEach((m) => m.mockClear());
|
||||
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/save', stringify({name: 'test.js', transaction: 1 /* code */}));
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/converter/save',
|
||||
stringify({data: {}, status: 'error', error: `Invalid payload`, transaction: 1}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -336,7 +348,7 @@ describe('Extension: ExternalConverters', () => {
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/remove', stringify({namex: 'test.js', transaction: 2}));
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/converter/remove',
|
||||
stringify({data: {}, status: 'error', error: `Invalid payload`, transaction: 2}),
|
||||
{retain: false, qos: 0},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as data from '../mocks/data';
|
||||
import {mockLogger} from '../mocks/logger';
|
||||
import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt';
|
||||
import {mockMQTTEndAsync, events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt';
|
||||
import {flushPromises} from '../mocks/utils';
|
||||
import {devices, mockController as mockZHController, returnDevices} from '../mocks/zigbeeHerdsman';
|
||||
|
||||
@@ -18,15 +18,15 @@ describe('Extension: ExternalExtensions', () => {
|
||||
let controller: Controller;
|
||||
const mockBasePath = path.join(data.mockDir, BASE_DIR);
|
||||
|
||||
const existsSyncSpy = jest.spyOn(fs, 'existsSync');
|
||||
const readdirSyncSpy = jest.spyOn(fs, 'readdirSync');
|
||||
const mkdirSyncSpy = jest.spyOn(fs, 'mkdirSync');
|
||||
const rmSyncSpy = jest.spyOn(fs, 'rmSync');
|
||||
const writeFileSyncSpy = jest.spyOn(fs, 'writeFileSync');
|
||||
const existsSyncSpy = vi.spyOn(fs, 'existsSync');
|
||||
const readdirSyncSpy = vi.spyOn(fs, 'readdirSync');
|
||||
const mkdirSyncSpy = vi.spyOn(fs, 'mkdirSync');
|
||||
const rmSyncSpy = vi.spyOn(fs, 'rmSync');
|
||||
const writeFileSyncSpy = vi.spyOn(fs, 'writeFileSync');
|
||||
|
||||
const mocksClear = [
|
||||
mockMQTT.endAsync,
|
||||
mockMQTT.publishAsync,
|
||||
mockMQTTEndAsync,
|
||||
mockMQTTPublishAsync,
|
||||
mockLogger.debug,
|
||||
mockLogger.error,
|
||||
mockZHController.stop,
|
||||
@@ -46,12 +46,21 @@ describe('Extension: ExternalExtensions', () => {
|
||||
return fs.readFileSync(path.join(__dirname, '..', 'assets', BASE_DIR, fileName), 'utf8');
|
||||
};
|
||||
|
||||
const resetExtension = async (): Promise<void> => {
|
||||
await controller.enableDisableExtension(false, 'ExternalExtensions');
|
||||
await controller.enableDisableExtension(true, 'ExternalExtensions');
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers();
|
||||
vi.useFakeTimers();
|
||||
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
jest.useRealTimers();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -60,72 +69,80 @@ describe('Extension: ExternalExtensions', () => {
|
||||
data.writeDefaultState();
|
||||
settings.reRead();
|
||||
returnDevices.splice(0);
|
||||
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(mockBasePath, {recursive: true, force: true});
|
||||
});
|
||||
|
||||
it('loads nothing from folder', async () => {
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
|
||||
expect(existsSyncSpy).toHaveBeenCalledWith(mockBasePath);
|
||||
expect(readdirSyncSpy).not.toHaveBeenCalledWith(mockBasePath);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/extensions', stringify([]), {retain: true, qos: 0});
|
||||
});
|
||||
|
||||
it('loads from folder', async () => {
|
||||
useAssets();
|
||||
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/example2/extension', 'call2 from constructor', {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/example2/extension', 'call2 from start', {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from constructor', {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from start', {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/extensions',
|
||||
stringify([
|
||||
{name: 'example2Extension.js', code: getFileCode('example2Extension.js')},
|
||||
{name: 'exampleExtension.js', code: getFileCode('exampleExtension.js')},
|
||||
]),
|
||||
{retain: true, qos: 0},
|
||||
);
|
||||
});
|
||||
|
||||
it('saves and removes from MQTT', async () => {
|
||||
const extensionName = 'foo.js';
|
||||
const extensionCode = getFileCode('exampleExtension.js');
|
||||
const extensionFilePath = path.join(mockBasePath, extensionName);
|
||||
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
mocksClear.forEach((m) => m.mockClear());
|
||||
|
||||
//-- SAVE
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/extension/save', stringify({name: extensionName, code: extensionCode}));
|
||||
await flushPromises();
|
||||
|
||||
expect(mkdirSyncSpy).toHaveBeenCalledWith(mockBasePath, {recursive: true});
|
||||
expect(writeFileSyncSpy).toHaveBeenCalledWith(extensionFilePath, extensionCode, 'utf8');
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from constructor', {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from start', {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/extensions', stringify([{name: extensionName, code: extensionCode}]), {
|
||||
retain: true,
|
||||
qos: 0,
|
||||
describe('from folder', () => {
|
||||
beforeEach(async () => {
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
});
|
||||
|
||||
//-- REMOVE
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/extension/remove', stringify({name: extensionName}));
|
||||
await flushPromises();
|
||||
it('loads nothing', async () => {
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(extensionFilePath, {force: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from stop', {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/extensions', stringify([]), {retain: true, qos: 0});
|
||||
expect(existsSyncSpy).toHaveBeenCalledWith(mockBasePath);
|
||||
expect(readdirSyncSpy).not.toHaveBeenCalledWith(mockBasePath);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/extensions', stringify([]), {retain: true, qos: 0});
|
||||
});
|
||||
|
||||
it('loads extensions', async () => {
|
||||
useAssets();
|
||||
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/example2/extension', 'call2 from constructor', {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/example2/extension', 'call2 from start', {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from constructor', {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from start', {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/extensions',
|
||||
stringify([
|
||||
{name: 'example2Extension.js', code: getFileCode('example2Extension.js')},
|
||||
{name: 'exampleExtension.js', code: getFileCode('exampleExtension.js')},
|
||||
]),
|
||||
{retain: true, qos: 0},
|
||||
);
|
||||
});
|
||||
|
||||
it('saves and removes from MQTT', async () => {
|
||||
const extensionName = 'foo.js';
|
||||
const extensionCode = getFileCode('exampleExtension.js');
|
||||
const extensionFilePath = path.join(mockBasePath, extensionName);
|
||||
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
mocksClear.forEach((m) => m.mockClear());
|
||||
|
||||
//-- SAVE
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/extension/save', stringify({name: extensionName, code: extensionCode}));
|
||||
await flushPromises();
|
||||
|
||||
expect(mkdirSyncSpy).toHaveBeenCalledWith(mockBasePath, {recursive: true});
|
||||
expect(writeFileSyncSpy).toHaveBeenCalledWith(extensionFilePath, extensionCode, 'utf8');
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from constructor', {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from start', {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/extensions',
|
||||
stringify([{name: extensionName, code: extensionCode}]),
|
||||
{
|
||||
retain: true,
|
||||
qos: 0,
|
||||
},
|
||||
);
|
||||
|
||||
//-- REMOVE
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/extension/remove', stringify({name: extensionName}));
|
||||
await flushPromises();
|
||||
|
||||
expect(rmSyncSpy).toHaveBeenCalledWith(extensionFilePath, {force: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from stop', {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/extensions', stringify([]), {retain: true, qos: 0});
|
||||
});
|
||||
});
|
||||
|
||||
it('returns error on invalid code', async () => {
|
||||
@@ -133,14 +150,13 @@ describe('Extension: ExternalExtensions', () => {
|
||||
const extensionCode = 'definetly not a correct javascript code';
|
||||
const extensionFilePath = path.join(mockBasePath, extensionName);
|
||||
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
await resetExtension();
|
||||
mocksClear.forEach((m) => m.mockClear());
|
||||
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/extension/save', stringify({name: extensionName, code: extensionCode}));
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/extension/save',
|
||||
expect.stringContaining(`"error":"${extensionName} contains invalid code`),
|
||||
{retain: false, qos: 0},
|
||||
@@ -152,14 +168,13 @@ describe('Extension: ExternalExtensions', () => {
|
||||
const converterName = 'invalid.js';
|
||||
const converterFilePath = path.join(mockBasePath, converterName);
|
||||
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
await resetExtension();
|
||||
mocksClear.forEach((m) => m.mockClear());
|
||||
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/extension/remove', stringify({name: converterName}));
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/extension/remove',
|
||||
stringify({data: {}, status: 'error', error: `${converterName} (${converterFilePath}) doesn't exists`}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -168,14 +183,13 @@ describe('Extension: ExternalExtensions', () => {
|
||||
});
|
||||
|
||||
it('handles invalid payloads', async () => {
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
await resetExtension();
|
||||
mocksClear.forEach((m) => m.mockClear());
|
||||
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/extension/save', stringify({name: 'test.js', transaction: 1 /* code */}));
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/extension/save',
|
||||
stringify({data: {}, status: 'error', error: `Invalid payload`, transaction: 1}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -184,7 +198,7 @@ describe('Extension: ExternalExtensions', () => {
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/extension/remove', stringify({namex: 'test.js', transaction: 2}));
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/extension/remove',
|
||||
stringify({data: {}, status: 'error', error: `Invalid payload`, transaction: 2}),
|
||||
{retain: false, qos: 0},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as data from '../mocks/data';
|
||||
import {mockLogger} from '../mocks/logger';
|
||||
import {mockMQTT} from '../mocks/mqtt';
|
||||
import {mockMQTTPublishAsync} from '../mocks/mqtt';
|
||||
import {EventHandler, flushPromises} from '../mocks/utils';
|
||||
import {devices} from '../mocks/zigbeeHerdsman';
|
||||
|
||||
@@ -15,26 +15,26 @@ import * as settings from '../../lib/util/settings';
|
||||
let mockHTTPOnRequest: (request: {url: string}, response: number) => void;
|
||||
const mockHTTPEvents: Record<string, EventHandler> = {};
|
||||
const mockHTTP = {
|
||||
listen: jest.fn(),
|
||||
listen: vi.fn(),
|
||||
on: (event: string, handler: EventHandler): void => {
|
||||
mockHTTPEvents[event] = handler;
|
||||
},
|
||||
close: jest.fn().mockImplementation((cb) => cb()),
|
||||
close: vi.fn<(cb: (err?: Error) => void) => void>((cb) => cb()),
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
let mockHTTPSOnRequest: (request: {url: string}, response: number) => void;
|
||||
const mockHTTPSEvents: Record<string, EventHandler> = {};
|
||||
const mockHTTPS = {
|
||||
listen: jest.fn(),
|
||||
listen: vi.fn(),
|
||||
on: (event: string, handler: EventHandler): void => {
|
||||
mockHTTPSEvents[event] = handler;
|
||||
},
|
||||
close: jest.fn().mockImplementation((cb) => cb()),
|
||||
close: vi.fn<(cb: (err?: Error) => void) => void>((cb) => cb()),
|
||||
};
|
||||
|
||||
const mockWSocket = {
|
||||
close: jest.fn(),
|
||||
close: vi.fn<(cb: (err?: Error) => void) => void>(),
|
||||
};
|
||||
|
||||
const mockWSClientEvents: Record<string, EventHandler> = {};
|
||||
@@ -42,8 +42,8 @@ const mockWSClient = {
|
||||
on: (event: string, handler: EventHandler): void => {
|
||||
mockWSClientEvents[event] = handler;
|
||||
},
|
||||
send: jest.fn(),
|
||||
terminate: jest.fn(),
|
||||
send: vi.fn<(data: string) => void>(),
|
||||
terminate: vi.fn<() => void>(),
|
||||
readyState: 'close',
|
||||
};
|
||||
const mockWSEvents: Record<string, EventHandler> = {};
|
||||
@@ -53,57 +53,62 @@ const mockWS = {
|
||||
on: (event: string, handler: EventHandler): void => {
|
||||
mockWSEvents[event] = handler;
|
||||
},
|
||||
handleUpgrade: jest.fn().mockImplementation((request, socket, head, cb) => {
|
||||
handleUpgrade: vi.fn().mockImplementation((request, socket, head, cb) => {
|
||||
cb(mockWSocket);
|
||||
}),
|
||||
emit: jest.fn(),
|
||||
close: jest.fn(),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
emit: vi.fn<(eventName: string, ...args: any[]) => void>(),
|
||||
close: vi.fn<(code?: number, data?: string | Buffer) => void>(),
|
||||
};
|
||||
|
||||
let mockNodeStaticPath: string = '';
|
||||
const mockNodeStatic = jest.fn();
|
||||
const mockNodeStatic = vi.fn();
|
||||
|
||||
const mockFinalHandler = jest.fn();
|
||||
const mockFinalHandler = vi.fn();
|
||||
|
||||
jest.mock('node:http', () => ({
|
||||
createServer: jest.fn().mockImplementation((onRequest) => {
|
||||
vi.mock('node:http', () => ({
|
||||
createServer: vi.fn().mockImplementation((onRequest) => {
|
||||
mockHTTPOnRequest = onRequest;
|
||||
return mockHTTP;
|
||||
}),
|
||||
Agent: jest.fn(),
|
||||
Agent: vi.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('node:https', () => ({
|
||||
createServer: jest.fn().mockImplementation((onRequest) => {
|
||||
vi.mock('node:https', () => ({
|
||||
createServer: vi.fn().mockImplementation((onRequest) => {
|
||||
mockHTTPSOnRequest = onRequest;
|
||||
return mockHTTPS;
|
||||
}),
|
||||
Agent: jest.fn(),
|
||||
Agent: vi.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('express-static-gzip', () =>
|
||||
jest.fn().mockImplementation((path) => {
|
||||
vi.mock('express-static-gzip', () => ({
|
||||
default: vi.fn().mockImplementation((path) => {
|
||||
mockNodeStaticPath = path;
|
||||
return mockNodeStatic;
|
||||
}),
|
||||
);
|
||||
|
||||
jest.mock('zigbee2mqtt-frontend', () => ({
|
||||
getPath: (): string => 'my/dummy/path',
|
||||
}));
|
||||
|
||||
jest.mock('ws', () => ({
|
||||
OPEN: 'open',
|
||||
Server: jest.fn().mockImplementation(() => {
|
||||
return mockWS;
|
||||
}),
|
||||
vi.mock('zigbee2mqtt-frontend', () => ({
|
||||
default: {
|
||||
getPath: (): string => 'my/dummy/path',
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('finalhandler', () =>
|
||||
jest.fn().mockImplementation(() => {
|
||||
vi.mock('ws', () => ({
|
||||
default: {
|
||||
OPEN: 'open',
|
||||
Server: vi.fn().mockImplementation(() => {
|
||||
return mockWS;
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('finalhandler', () => ({
|
||||
default: vi.fn().mockImplementation(() => {
|
||||
return mockFinalHandler;
|
||||
}),
|
||||
);
|
||||
}));
|
||||
|
||||
const mocksClear = [
|
||||
mockHTTP.close,
|
||||
@@ -118,7 +123,7 @@ const mocksClear = [
|
||||
mockWSClient.terminate,
|
||||
mockNodeStatic,
|
||||
mockFinalHandler,
|
||||
mockMQTT.publishAsync,
|
||||
mockMQTTPublishAsync,
|
||||
mockLogger.error,
|
||||
];
|
||||
|
||||
@@ -126,7 +131,7 @@ describe('Extension: Frontend', () => {
|
||||
let controller: Controller;
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers();
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -142,7 +147,7 @@ describe('Extension: Frontend', () => {
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
jest.useRealTimers();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -150,7 +155,7 @@ describe('Extension: Frontend', () => {
|
||||
});
|
||||
|
||||
it('Start/stop with defaults', async () => {
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
expect(mockNodeStaticPath).toBe('my/dummy/path');
|
||||
expect(mockHTTP.listen).toHaveBeenCalledWith(8081, '127.0.0.1');
|
||||
@@ -163,7 +168,7 @@ describe('Extension: Frontend', () => {
|
||||
|
||||
it('Start/stop without host', async () => {
|
||||
settings.set(['frontend'], {enabled: true, port: 8081});
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
expect(mockNodeStaticPath).toBe('my/dummy/path');
|
||||
expect(mockHTTP.listen).toHaveBeenCalledWith(8081);
|
||||
@@ -176,7 +181,7 @@ describe('Extension: Frontend', () => {
|
||||
|
||||
it('Start/stop unix socket', async () => {
|
||||
settings.set(['frontend', 'host'], '/tmp/zigbee2mqtt.sock');
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
expect(mockNodeStaticPath).toBe('my/dummy/path');
|
||||
expect(mockHTTP.listen).toHaveBeenCalledWith('/tmp/zigbee2mqtt.sock');
|
||||
@@ -190,7 +195,7 @@ describe('Extension: Frontend', () => {
|
||||
it('Start/stop HTTPS valid', async () => {
|
||||
settings.set(['frontend', 'ssl_cert'], path.join(__dirname, '..', 'assets', 'certs', 'dummy.crt'));
|
||||
settings.set(['frontend', 'ssl_key'], path.join(__dirname, '..', 'assets', 'certs', 'dummy.key'));
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
expect(mockHTTP.listen).not.toHaveBeenCalledWith(8081, '127.0.0.1');
|
||||
expect(mockHTTPS.listen).toHaveBeenCalledWith(8081, '127.0.0.1');
|
||||
@@ -199,7 +204,7 @@ describe('Extension: Frontend', () => {
|
||||
|
||||
it('Start/stop HTTPS invalid : missing config', async () => {
|
||||
settings.set(['frontend', 'ssl_cert'], path.join(__dirname, '..', 'assets', 'certs', 'dummy.crt'));
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
expect(mockHTTP.listen).toHaveBeenCalledWith(8081, '127.0.0.1');
|
||||
expect(mockHTTPS.listen).not.toHaveBeenCalledWith(8081, '127.0.0.1');
|
||||
@@ -209,7 +214,7 @@ describe('Extension: Frontend', () => {
|
||||
it('Start/stop HTTPS invalid : missing file', async () => {
|
||||
settings.set(['frontend', 'ssl_cert'], 'filesNotExists.crt');
|
||||
settings.set(['frontend', 'ssl_key'], path.join(__dirname, '..', 'assets', 'certs', 'dummy.key'));
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
expect(mockHTTP.listen).toHaveBeenCalledWith(8081, '127.0.0.1');
|
||||
expect(mockHTTPS.listen).not.toHaveBeenCalledWith(8081, '127.0.0.1');
|
||||
@@ -217,25 +222,25 @@ describe('Extension: Frontend', () => {
|
||||
});
|
||||
|
||||
it('Websocket interaction', async () => {
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
mockWSClient.readyState = 'open';
|
||||
mockWS.clients.push(mockWSClient);
|
||||
await mockWSEvents.connection(mockWSClient);
|
||||
|
||||
const allTopics = mockWSClient.send.mock.calls.map((m) => JSON.parse(m).topic);
|
||||
const allTopics = mockWSClient.send.mock.calls.map(([m]) => JSON.parse(m).topic);
|
||||
expect(allTopics).toContain('bridge/devices');
|
||||
expect(allTopics).toContain('bridge/info');
|
||||
expect(mockWSClient.send).toHaveBeenCalledWith(stringify({topic: 'bridge/state', payload: {state: 'online'}}));
|
||||
expect(mockWSClient.send).toHaveBeenCalledWith(stringify({topic: 'remote', payload: {brightness: 255}}));
|
||||
|
||||
// Message
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockWSClient.send.mockClear();
|
||||
mockWSClientEvents.message(stringify({topic: 'bulb_color/set', payload: {state: 'ON'}}), false);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({
|
||||
state: 'ON',
|
||||
@@ -288,10 +293,10 @@ describe('Extension: Frontend', () => {
|
||||
});
|
||||
|
||||
it('onRequest/onUpgrade', async () => {
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
|
||||
const mockSocket = {destroy: jest.fn()};
|
||||
const mockSocket = {destroy: vi.fn()};
|
||||
mockHTTPEvents.upgrade({url: 'http://localhost:8080/api'}, mockSocket, 3);
|
||||
expect(mockWS.handleUpgrade).toHaveBeenCalledTimes(1);
|
||||
expect(mockSocket.destroy).toHaveBeenCalledTimes(0);
|
||||
@@ -305,7 +310,7 @@ describe('Extension: Frontend', () => {
|
||||
});
|
||||
|
||||
it('Static server', async () => {
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
|
||||
expect(mockHTTP.listen).toHaveBeenCalledWith(8081, '127.0.0.1');
|
||||
@@ -314,10 +319,10 @@ describe('Extension: Frontend', () => {
|
||||
it('Authentification', async () => {
|
||||
const authToken = 'sample-secure-token';
|
||||
settings.set(['frontend', 'auth_token'], authToken);
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
|
||||
const mockSocket = {destroy: jest.fn()};
|
||||
const mockSocket = {destroy: vi.fn()};
|
||||
mockHTTPEvents.upgrade({url: '/api'}, mockSocket, mockWSocket);
|
||||
expect(mockWS.handleUpgrade).toHaveBeenCalledTimes(1);
|
||||
expect(mockSocket.destroy).toHaveBeenCalledTimes(0);
|
||||
@@ -340,7 +345,7 @@ describe('Extension: Frontend', () => {
|
||||
|
||||
it.each(['/z2m/', '/z2m'])('Works with non-default base url %s', async (baseUrl) => {
|
||||
settings.set(['frontend', 'base_url'], baseUrl);
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
|
||||
expect(ws.Server).toHaveBeenCalledWith({noServer: true, path: '/z2m/api'});
|
||||
@@ -366,7 +371,7 @@ describe('Extension: Frontend', () => {
|
||||
it('Works with non-default complex base url', async () => {
|
||||
const baseUrl = '/z2m-more++/c0mplex.url/';
|
||||
settings.set(['frontend', 'base_url'], baseUrl);
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
|
||||
expect(ws.Server).toHaveBeenCalledWith({noServer: true, path: '/z2m-more++/c0mplex.url/api'});
|
||||
|
||||
+151
-151
@@ -1,6 +1,6 @@
|
||||
import * as data from '../mocks/data';
|
||||
import {mockLogger} from '../mocks/logger';
|
||||
import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt';
|
||||
import {events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt';
|
||||
import {flushPromises} from '../mocks/utils';
|
||||
import {devices, groups, events as mockZHEvents, resetGroupMembers, returnDevices} from '../mocks/zigbeeHerdsman';
|
||||
|
||||
@@ -25,21 +25,21 @@ describe('Extension: Groups', () => {
|
||||
let controller: Controller;
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers();
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
vi.useFakeTimers();
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
jest.useRealTimers();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
resetGroupMembers();
|
||||
data.writeDefaultConfiguration();
|
||||
settings.reRead();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
groups.gledopto_group.command.mockClear();
|
||||
zhcToZigbee.__clearStore__();
|
||||
// @ts-expect-error private
|
||||
@@ -52,21 +52,21 @@ describe('Extension: Groups', () => {
|
||||
const group = groups.group_1;
|
||||
group.members.push(endpoint);
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
const payload = {data: {onOff: 1}, cluster: 'genOnOff', device, endpoint, type: 'attributeReport', linkquality: 10};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should not republish identical optimistic group states', async () => {
|
||||
const device1 = devices.bulb_2;
|
||||
const device2 = devices.bulb_color_2;
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await mockZHEvents.message({
|
||||
data: {onOff: 1},
|
||||
cluster: 'genOnOff',
|
||||
@@ -84,13 +84,13 @@ describe('Extension: Groups', () => {
|
||||
linkquality: 10,
|
||||
});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(6);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_2', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color_2', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_with_tradfri', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/ha_discovery_group', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/switch_group', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(6);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_2', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color_2', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_with_tradfri', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/ha_discovery_group', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/switch_group', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should publish state change of all members when a group changes its state', async () => {
|
||||
@@ -99,12 +99,12 @@ describe('Extension: Groups', () => {
|
||||
const group = groups.group_1;
|
||||
group.members.push(endpoint);
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should not publish state change when group changes state and device is disabled', async () => {
|
||||
@@ -114,11 +114,11 @@ describe('Extension: Groups', () => {
|
||||
group.members.push(endpoint);
|
||||
settings.set(['devices', device.ieeeAddr, 'disabled'], true);
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should publish state change for group when members state change', async () => {
|
||||
@@ -128,37 +128,37 @@ describe('Extension: Groups', () => {
|
||||
const group = groups.group_1;
|
||||
group.members.push(endpoint);
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'ON'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'OFF'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'ON'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should publish state of device with endpoint name', async () => {
|
||||
const group = groups.gledopto_group;
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/gledopto_group/set', stringify({state: 'ON'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/GLEDOPTO_2ID', stringify({state_cct: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/gledopto_group', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/GLEDOPTO_2ID', stringify({state_cct: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/gledopto_group', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(group.command).toHaveBeenCalledTimes(1);
|
||||
expect(group.command).toHaveBeenCalledWith('genOnOff', 'on', {}, {});
|
||||
});
|
||||
@@ -166,12 +166,12 @@ describe('Extension: Groups', () => {
|
||||
it('Should publish state of group when specific state of specific endpoint is changed', async () => {
|
||||
const group = groups.gledopto_group;
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/GLEDOPTO_2ID/set', stringify({state_cct: 'ON'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/GLEDOPTO_2ID', stringify({state_cct: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/gledopto_group', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/GLEDOPTO_2ID', stringify({state_cct: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/gledopto_group', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(group.command).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
@@ -182,15 +182,15 @@ describe('Extension: Groups', () => {
|
||||
group.members.push(endpoint);
|
||||
settings.set(['groups'], {1: {friendly_name: 'group_1', retain: false, filtered_attributes: ['brightness']}});
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON', brightness: 100}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON', brightness: 100}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON', brightness: 100}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Shouldnt publish group state change when a group is not optimistic', async () => {
|
||||
@@ -200,13 +200,13 @@ describe('Extension: Groups', () => {
|
||||
group.members.push(endpoint);
|
||||
settings.set(['groups'], {1: {friendly_name: 'group_1', optimistic: false, retain: false}});
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
const payload = {data: {onOff: 1}, cluster: 'genOnOff', device, endpoint, type: 'attributeReport', linkquality: 10};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should publish state change of another group with shared device when a group changes its state', async () => {
|
||||
@@ -215,13 +215,13 @@ describe('Extension: Groups', () => {
|
||||
groups.group_1.members.push(endpoint);
|
||||
groups.group_2.members.push(endpoint);
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_2', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_2', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should not publish state change off if any lights within are still on when changed via device', async () => {
|
||||
@@ -235,12 +235,12 @@ describe('Extension: Groups', () => {
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
|
||||
await flushPromises();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should publish state change off if any lights within are still on when changed via device when off_state: last_member_state is used', async () => {
|
||||
@@ -257,13 +257,13 @@ describe('Extension: Groups', () => {
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
|
||||
await flushPromises();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(1, 'zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(2, 'zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(1, 'zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(2, 'zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should not publish state change off if any lights within with non default-ep are still on when changed via device', async () => {
|
||||
@@ -277,12 +277,12 @@ describe('Extension: Groups', () => {
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
|
||||
await flushPromises();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should not publish state change off if any lights within are still on when changed via device with non default-ep', async () => {
|
||||
@@ -299,12 +299,12 @@ describe('Extension: Groups', () => {
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
|
||||
await flushPromises();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/wall_switch_double/set', stringify({state_left: 'OFF'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/wall_switch_double', stringify({state_left: 'OFF', state_right: 'ON'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/wall_switch_double', stringify({state_left: 'OFF', state_right: 'ON'}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
@@ -324,15 +324,15 @@ describe('Extension: Groups', () => {
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
|
||||
await flushPromises();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'}));
|
||||
await mockMQTTEvents.message('zigbee2mqtt/wall_switch_double/set', stringify({state_left: 'OFF'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/wall_switch_double', stringify({state_left: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/wall_switch_double', stringify({state_left: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should not publish state change off if any lights within are still on when changed via shared group', async () => {
|
||||
@@ -346,13 +346,13 @@ describe('Extension: Groups', () => {
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
|
||||
await flushPromises();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_2/set', stringify({state: 'OFF'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_2', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_2', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should publish state change off if all lights within turn off', async () => {
|
||||
@@ -369,15 +369,15 @@ describe('Extension: Groups', () => {
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
|
||||
await flushPromises();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'}));
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb/set', stringify({state: 'OFF'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'OFF'}), {retain: true, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'OFF'}), {retain: true, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should only update group state with changed properties', async () => {
|
||||
@@ -391,26 +391,26 @@ describe('Extension: Groups', () => {
|
||||
settings.set(['groups'], {
|
||||
1: {friendly_name: 'group_1', retain: false},
|
||||
});
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF', color_temp: 200}));
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb/set', stringify({state: 'ON', color_temp: 250}));
|
||||
await flushPromises();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({color_temp: 300}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({color_mode: 'color_temp', color_temp: 300, state: 'OFF'}),
|
||||
{retain: false, qos: 0},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({color_mode: 'color_temp', color_temp: 300, state: 'ON'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({color_mode: 'color_temp', color_temp: 300, state: 'ON'}), {
|
||||
retain: true,
|
||||
qos: 0,
|
||||
});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/group_1',
|
||||
stringify({color_mode: 'color_temp', color_temp: 300, state: 'ON'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -431,16 +431,16 @@ describe('Extension: Groups', () => {
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
|
||||
await flushPromises();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
// @ts-expect-error private
|
||||
controller.state.state = {};
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'}));
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Add to group via MQTT', async () => {
|
||||
@@ -448,15 +448,15 @@ describe('Extension: Groups', () => {
|
||||
const endpoint = device.getEndpoint(1);
|
||||
const group = groups.group_1;
|
||||
expect(group.members.length).toBe(0);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message(
|
||||
'zigbee2mqtt/bridge/request/group/members/add',
|
||||
stringify({transaction: '123', group: 'group_1', device: 'bulb_color'}),
|
||||
);
|
||||
await flushPromises();
|
||||
expect(group.members).toStrictEqual([endpoint]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/add',
|
||||
stringify({data: {device: 'bulb_color', endpoint: 'default', group: 'group_1'}, transaction: '123', status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -472,12 +472,12 @@ describe('Extension: Groups', () => {
|
||||
throw new Error('timeout');
|
||||
});
|
||||
await flushPromises();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'bulb_color'}));
|
||||
await flushPromises();
|
||||
expect(group.members).toStrictEqual([]);
|
||||
expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/add',
|
||||
stringify({data: {}, status: 'error', error: 'Failed to add from group (timeout)'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -490,12 +490,12 @@ describe('Extension: Groups', () => {
|
||||
const group = groups['group/with/slashes'];
|
||||
settings.set(['groups'], {99: {friendly_name: 'group/with/slashes', retain: false}});
|
||||
expect(group.members.length).toBe(0);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group/with/slashes', device: 'bulb_color'}));
|
||||
await flushPromises();
|
||||
expect(group.members).toStrictEqual([endpoint]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/add',
|
||||
stringify({data: {device: 'bulb_color', endpoint: 'default', group: 'group/with/slashes'}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -507,15 +507,15 @@ describe('Extension: Groups', () => {
|
||||
const endpoint = device.getEndpoint(3)!;
|
||||
const group = groups.group_1;
|
||||
expect(group.members.length).toBe(0);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message(
|
||||
'zigbee2mqtt/bridge/request/group/members/add',
|
||||
stringify({group: 'group_1', device: 'wall_switch_double', endpoint: 'right'}),
|
||||
);
|
||||
await flushPromises();
|
||||
expect(group.members).toStrictEqual([endpoint]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/add',
|
||||
stringify({data: {device: 'wall_switch_double', endpoint: 'right', group: 'group_1'}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -527,7 +527,7 @@ describe('Extension: Groups', () => {
|
||||
const endpoint = device.getEndpoint(3)!;
|
||||
const group = groups.group_1;
|
||||
expect(group.members.length).toBe(0);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message(
|
||||
'zigbee2mqtt/bridge/request/group/members/add',
|
||||
stringify({group: 'group_1', device: 'wall_switch_double', endpoint: 'right'}),
|
||||
@@ -539,8 +539,8 @@ describe('Extension: Groups', () => {
|
||||
);
|
||||
await flushPromises();
|
||||
expect(group.members).toStrictEqual([endpoint]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/add',
|
||||
stringify({data: {device: 'wall_switch_double', endpoint: 'right', group: 'group_1'}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -552,12 +552,12 @@ describe('Extension: Groups', () => {
|
||||
const endpoint = device.getEndpoint(1)!;
|
||||
const group = groups.group_1;
|
||||
group.members.push(endpoint);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: 'bulb_color'}));
|
||||
await flushPromises();
|
||||
expect(group.members).toStrictEqual([]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/remove',
|
||||
stringify({data: {device: 'bulb_color', endpoint: 'default', group: 'group_1'}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -569,15 +569,15 @@ describe('Extension: Groups', () => {
|
||||
const endpoint = device.getEndpoint(1)!;
|
||||
const group = groups.group_1;
|
||||
group.members.push(endpoint);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message(
|
||||
'zigbee2mqtt/bridge/request/group/members/remove',
|
||||
stringify({group: 'group_1', device: 'bulb_color', skip_disable_reporting: true}),
|
||||
);
|
||||
await flushPromises();
|
||||
expect(group.members).toStrictEqual([]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/remove',
|
||||
stringify({data: {device: 'bulb_color', endpoint: 'default', group: 'group_1'}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -589,15 +589,15 @@ describe('Extension: Groups', () => {
|
||||
const endpoint = device.getEndpoint(3)!;
|
||||
const group = groups.group_1;
|
||||
group.members.push(endpoint);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message(
|
||||
'zigbee2mqtt/bridge/request/group/members/remove',
|
||||
stringify({group: 'group_1', device: '0x0017880104e45542', endpoint: '3'}),
|
||||
);
|
||||
await flushPromises();
|
||||
expect(group.members).toStrictEqual([]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/remove',
|
||||
stringify({data: {device: '0x0017880104e45542', endpoint: '3', group: 'group_1'}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -609,15 +609,15 @@ describe('Extension: Groups', () => {
|
||||
const endpoint = device.getEndpoint(3)!;
|
||||
const group = groups.group_1;
|
||||
group.members.push(endpoint);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message(
|
||||
'zigbee2mqtt/bridge/request/group/members/remove',
|
||||
stringify({group: 'group_1', device: 'wall_switch_double', endpoint: '3'}),
|
||||
);
|
||||
await flushPromises();
|
||||
expect(group.members).toStrictEqual([]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/remove',
|
||||
stringify({data: {device: 'wall_switch_double', endpoint: '3', group: 'group_1'}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -629,15 +629,15 @@ describe('Extension: Groups', () => {
|
||||
const endpoint = device.getEndpoint(3)!;
|
||||
const group = groups.group_1;
|
||||
group.members.push(endpoint);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message(
|
||||
'zigbee2mqtt/bridge/request/group/members/remove',
|
||||
stringify({group: 'group_1', device: '0x0017880104e45542', endpoint: 'right'}),
|
||||
);
|
||||
await flushPromises();
|
||||
expect(group.members).toStrictEqual([]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/remove',
|
||||
stringify({data: {device: '0x0017880104e45542', endpoint: 'right', group: 'group_1'}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -647,12 +647,12 @@ describe('Extension: Groups', () => {
|
||||
it('Remove from group all', async () => {
|
||||
const group = groups.group_1;
|
||||
groups.group_1.members.push(devices.QBKG03LM.endpoints[2]);
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/members/remove_all', stringify({device: '0x0017880104e45542', endpoint: 'right'}));
|
||||
await flushPromises();
|
||||
expect(group.members).toStrictEqual([]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/remove_all',
|
||||
stringify({data: {device: '0x0017880104e45542', endpoint: 'right'}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -661,11 +661,11 @@ describe('Extension: Groups', () => {
|
||||
|
||||
it('Error when adding to non-existing group', async () => {
|
||||
mockLogger.error.mockClear();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1_not_existing', device: 'bulb_color'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/remove',
|
||||
stringify({data: {}, status: 'error', error: "Group 'group_1_not_existing' does not exist"}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -674,11 +674,11 @@ describe('Extension: Groups', () => {
|
||||
|
||||
it('Error when adding a non-existing device', async () => {
|
||||
mockLogger.error.mockClear();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'bulb_color_not_existing'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/add',
|
||||
stringify({data: {}, status: 'error', error: "Device 'bulb_color_not_existing' does not exist"}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -687,14 +687,14 @@ describe('Extension: Groups', () => {
|
||||
|
||||
it('Error when adding a non-existing endpoint', async () => {
|
||||
mockLogger.error.mockClear();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message(
|
||||
'zigbee2mqtt/bridge/request/group/members/add',
|
||||
stringify({group: 'group_1', device: 'bulb_color', endpoint: 'not_existing_endpoint'}),
|
||||
);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/add',
|
||||
stringify({data: {}, status: 'error', error: "Device 'bulb_color' does not have endpoint 'not_existing_endpoint'"}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -703,11 +703,11 @@ describe('Extension: Groups', () => {
|
||||
|
||||
it('Error when invalid payload', async () => {
|
||||
mockLogger.error.mockClear();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', devicez: 'bulb_color'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/add',
|
||||
stringify({data: {}, status: 'error', error: 'Invalid payload'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -716,11 +716,11 @@ describe('Extension: Groups', () => {
|
||||
|
||||
it('Error when add/remove with invalid payload', async () => {
|
||||
mockLogger.error.mockClear();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/members/add', stringify({groupz: 'group_1', device: 'bulb_color'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object));
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/add',
|
||||
stringify({data: {}, status: 'error', error: 'Invalid payload'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -734,38 +734,38 @@ describe('Extension: Groups', () => {
|
||||
group.members.push(bulbColor.getEndpoint(1)!);
|
||||
group.members.push(bulbColorTemp.getEndpoint(1)!);
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({color_temp: 50}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({color_mode: 'color_temp', color_temp: 50}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({color_mode: 'color_temp', color_temp: 50}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({color_mode: 'color_temp', color_temp: 50}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({color_mode: 'color_temp', color_temp: 50}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({color_mode: 'color_temp', color_temp: 50}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({color_mode: 'color_temp', color_temp: 50}), {
|
||||
retain: true,
|
||||
qos: 0,
|
||||
});
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({color: {x: 0.5, y: 0.3}}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({color: {x: 0.5, y: 0.3}, color_mode: 'xy', color_temp: 548}),
|
||||
{retain: false, qos: 0},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/group_1',
|
||||
stringify({color: {x: 0.5, y: 0.3}, color_mode: 'xy', color_temp: 548}),
|
||||
{retain: false, qos: 0},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({color_mode: 'color_temp', color_temp: 548}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({color_mode: 'color_temp', color_temp: 548}), {
|
||||
retain: true,
|
||||
qos: 0,
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
import * as data from '../mocks/data';
|
||||
import {mockLogger} from '../mocks/logger';
|
||||
import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt';
|
||||
import {events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt';
|
||||
import * as mockSleep from '../mocks/sleep';
|
||||
import {flushPromises} from '../mocks/utils';
|
||||
import {devices, events as mockZHEvents, returnDevices} from '../mocks/zigbeeHerdsman';
|
||||
@@ -23,7 +23,7 @@ returnDevices.push(
|
||||
devices.external_converter_device.ieeeAddr,
|
||||
);
|
||||
|
||||
const mocksClear = [mockMQTT.publishAsync, mockLogger.warning, mockLogger.debug];
|
||||
const mocksClear = [mockMQTTPublishAsync, mockLogger.warning, mockLogger.debug];
|
||||
|
||||
describe('Extension: NetworkMap', () => {
|
||||
let controller: Controller;
|
||||
@@ -96,9 +96,9 @@ describe('Extension: NetworkMap', () => {
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers();
|
||||
vi.useFakeTimers();
|
||||
mockSleep.mock();
|
||||
jest.spyOn(Date, 'now').mockReturnValue(10000);
|
||||
vi.spyOn(Date, 'now').mockReturnValue(10000);
|
||||
data.writeDefaultConfiguration();
|
||||
settings.reRead();
|
||||
data.writeEmptyState();
|
||||
@@ -107,7 +107,7 @@ describe('Extension: NetworkMap', () => {
|
||||
path.join(__dirname, '..', 'assets', 'external_converters', 'mock-external-converter.js'),
|
||||
path.join(data.mockDir, 'external_converters', 'mock-external-converter.js'),
|
||||
);
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
});
|
||||
|
||||
@@ -124,15 +124,15 @@ describe('Extension: NetworkMap', () => {
|
||||
afterAll(async () => {
|
||||
mockSleep.restore();
|
||||
fs.rmSync(path.join(data.mockDir, 'external_converters'), {recursive: true});
|
||||
jest.useRealTimers();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('Output raw networkmap', async () => {
|
||||
mock();
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/networkmap', stringify({type: 'raw', routes: true}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
||||
|
||||
const expected = {
|
||||
data: {
|
||||
@@ -315,7 +315,7 @@ describe('Extension: NetworkMap', () => {
|
||||
},
|
||||
status: 'ok',
|
||||
};
|
||||
const actual = JSON.parse(mockMQTT.publishAsync.mock.calls[0][1]);
|
||||
const actual = JSON.parse(mockMQTTPublishAsync.mock.calls[0][1]);
|
||||
expect(actual).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
@@ -329,8 +329,8 @@ describe('Extension: NetworkMap', () => {
|
||||
await mockZHEvents.message(payload);
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/networkmap', stringify({type: 'graphviz', routes: true}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
||||
|
||||
const expected = `digraph G {
|
||||
node[shape=record];
|
||||
@@ -350,7 +350,7 @@ describe('Extension: NetworkMap', () => {
|
||||
}`;
|
||||
|
||||
const expectedLines = expected.split('\n');
|
||||
const actualLines = JSON.parse(mockMQTT.publishAsync.mock.calls[0][1]).data.value.split('\n');
|
||||
const actualLines = JSON.parse(mockMQTTPublishAsync.mock.calls[0][1]).data.value.split('\n');
|
||||
|
||||
for (let i = 0; i < expectedLines.length; i++) {
|
||||
expect(actualLines[i].trim()).toStrictEqual(expectedLines[i].trim());
|
||||
@@ -367,8 +367,8 @@ describe('Extension: NetworkMap', () => {
|
||||
await mockZHEvents.message(payload);
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/networkmap', stringify({type: 'plantuml', routes: true}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
||||
|
||||
const expected = `' paste into: https://www.planttext.com/
|
||||
|
||||
@@ -451,7 +451,7 @@ describe('Extension: NetworkMap', () => {
|
||||
@enduml`;
|
||||
|
||||
const expectedLines = expected.split('\n');
|
||||
const actualLines = JSON.parse(mockMQTT.publishAsync.mock.calls[0][1]).data.value.split('\n');
|
||||
const actualLines = JSON.parse(mockMQTTPublishAsync.mock.calls[0][1]).data.value.split('\n');
|
||||
|
||||
for (let i = 0; i < expectedLines.length; i++) {
|
||||
expect(actualLines[i].trim()).toStrictEqual(expectedLines[i].trim());
|
||||
@@ -460,11 +460,10 @@ describe('Extension: NetworkMap', () => {
|
||||
|
||||
it('Should throw error when requesting invalid type', async () => {
|
||||
mock();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/networkmap', 'not_existing');
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/networkmap',
|
||||
stringify({data: {}, status: 'error', error: "Type 'not_existing' not supported, allowed are: raw,graphviz,plantuml"}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -474,11 +473,10 @@ describe('Extension: NetworkMap', () => {
|
||||
it('Should exclude disabled devices from networkmap', async () => {
|
||||
settings.set(['devices', '0x000b57fffec6a5b2', 'disabled'], true);
|
||||
mock();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/networkmap', stringify({type: 'raw', routes: true}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
||||
|
||||
const expected = {
|
||||
data: {
|
||||
@@ -619,7 +617,7 @@ describe('Extension: NetworkMap', () => {
|
||||
},
|
||||
status: 'ok',
|
||||
};
|
||||
const actual = JSON.parse(mockMQTT.publishAsync.mock.calls[0][1]);
|
||||
const actual = JSON.parse(mockMQTTPublishAsync.mock.calls[0][1]);
|
||||
expect(actual).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
@@ -627,11 +625,10 @@ describe('Extension: NetworkMap', () => {
|
||||
settings.set(['devices', '0x000b57fffec6a5b2', 'disabled'], true);
|
||||
mock();
|
||||
devices.bulb.lqi.mockRejectedValueOnce('failed');
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/networkmap', stringify({type: 'raw', routes: true}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
||||
|
||||
const expected = {
|
||||
data: {
|
||||
@@ -772,7 +769,7 @@ describe('Extension: NetworkMap', () => {
|
||||
},
|
||||
status: 'ok',
|
||||
};
|
||||
const actual = JSON.parse(mockMQTT.publishAsync.mock.calls[0][1]);
|
||||
const actual = JSON.parse(mockMQTTPublishAsync.mock.calls[0][1]);
|
||||
expect(actual).toStrictEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as data from '../mocks/data';
|
||||
import {mockLogger} from '../mocks/logger';
|
||||
import {mockMQTT} from '../mocks/mqtt';
|
||||
import {mockMQTTPublishAsync} from '../mocks/mqtt';
|
||||
import {flushPromises} from '../mocks/utils';
|
||||
import {devices, events as mockZHEvents} from '../mocks/zigbeeHerdsman';
|
||||
|
||||
@@ -9,23 +9,23 @@ import * as zhc from 'zigbee-herdsman-converters';
|
||||
import {Controller} from '../../lib/controller';
|
||||
import * as settings from '../../lib/util/settings';
|
||||
|
||||
const mockOnEvent = jest.fn();
|
||||
const mockLivoloOnEvent = jest.fn();
|
||||
const mockOnEvent = vi.fn();
|
||||
const mockLivoloOnEvent = vi.fn();
|
||||
const mappedLivolo = zhc.findByModel('TI0001')!;
|
||||
mappedLivolo.onEvent = mockLivoloOnEvent;
|
||||
// @ts-expect-error mock
|
||||
zhc.onEvent = mockOnEvent;
|
||||
|
||||
const mocksClear = [mockMQTT.publishAsync, mockLogger.warning, mockLogger.debug];
|
||||
const mocksClear = [mockMQTTPublishAsync, mockLogger.warning, mockLogger.debug];
|
||||
|
||||
describe('Extension: OnEvent', () => {
|
||||
let controller: Controller;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.useFakeTimers();
|
||||
vi.useFakeTimers();
|
||||
data.writeDefaultConfiguration();
|
||||
settings.reRead();
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
});
|
||||
@@ -40,7 +40,7 @@ describe('Extension: OnEvent', () => {
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
jest.useRealTimers();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('Should call with start event', async () => {
|
||||
@@ -81,9 +81,9 @@ describe('Extension: OnEvent', () => {
|
||||
);
|
||||
|
||||
// Test deviceExposesChanged
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
console.log(mockLivoloOnEvent.mock.calls[0][5].deviceExposesChanged());
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bridge/devices');
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bridge/devices');
|
||||
});
|
||||
|
||||
it('Should call index onEvent with zigbee event', async () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as data from '../mocks/data';
|
||||
import {mockLogger} from '../mocks/logger';
|
||||
import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt';
|
||||
import {events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt';
|
||||
import * as mockSleep from '../mocks/sleep';
|
||||
import {flushPromises} from '../mocks/utils';
|
||||
import {devices, events as mockZHEvents} from '../mocks/zigbeeHerdsman';
|
||||
@@ -15,7 +15,7 @@ import * as zhc from 'zigbee-herdsman-converters';
|
||||
import {Controller} from '../../lib/controller';
|
||||
import * as settings from '../../lib/util/settings';
|
||||
|
||||
const mocksClear = [mockMQTT.publishAsync, devices.bulb.save, mockLogger.info];
|
||||
const mocksClear = [mockMQTTPublishAsync, devices.bulb.save, mockLogger.info];
|
||||
|
||||
const DEFAULT_CONFIG: zhc.Ota.Settings = {
|
||||
dataDir: data.mockDir,
|
||||
@@ -25,8 +25,8 @@ const DEFAULT_CONFIG: zhc.Ota.Settings = {
|
||||
|
||||
describe('Extension: OTAUpdate', () => {
|
||||
let controller: Controller;
|
||||
const updateSpy = jest.spyOn(zhc.ota, 'update');
|
||||
const isUpdateAvailableSpy = jest.spyOn(zhc.ota, 'isUpdateAvailable');
|
||||
const updateSpy = vi.spyOn(zhc.ota, 'update');
|
||||
const isUpdateAvailableSpy = vi.spyOn(zhc.ota, 'isUpdateAvailable');
|
||||
|
||||
const resetExtension = async (): Promise<void> => {
|
||||
await controller.enableDisableExtension(false, 'OTAUpdate');
|
||||
@@ -34,19 +34,19 @@ describe('Extension: OTAUpdate', () => {
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers();
|
||||
vi.useFakeTimers();
|
||||
mockSleep.mock();
|
||||
data.writeDefaultConfiguration();
|
||||
settings.reRead();
|
||||
settings.reRead();
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
mockSleep.restore();
|
||||
jest.useRealTimers();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -111,21 +111,21 @@ describe('Extension: OTAUpdate', () => {
|
||||
expect(devices.bulb.save).toHaveBeenCalledTimes(1);
|
||||
expect(devices.bulb.endpoints[0].read).toHaveBeenCalledWith('genBasic', ['dateCode', 'swBuildId'], {sendPolicy: 'immediate'});
|
||||
expect(devices.bulb.endpoints[0].read).toHaveBeenCalledWith('genBasic', ['dateCode', 'swBuildId'], {sendPolicy: undefined});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({update: {state: 'updating', progress: 0}}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({update: {state: 'updating', progress: 0}}), {
|
||||
retain: true,
|
||||
qos: 0,
|
||||
});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({update: {state: 'updating', progress: 10, remaining: 3600}}),
|
||||
{retain: true, qos: 0},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({update: {state: 'idle', installed_version: 90, latest_version: 90}}),
|
||||
{retain: true, qos: 0},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/ota_update/update',
|
||||
stringify({
|
||||
data: {
|
||||
@@ -137,7 +137,7 @@ describe('Extension: OTAUpdate', () => {
|
||||
}),
|
||||
{retain: false, qos: 0},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0});
|
||||
});
|
||||
|
||||
it('Should handle when OTA update fails', async () => {
|
||||
@@ -149,8 +149,8 @@ describe('Extension: OTAUpdate', () => {
|
||||
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/ota_update/update', stringify({id: 'bulb'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({update: {state: 'available'}}), {retain: true, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({update: {state: 'available'}}), {retain: true, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/ota_update/update',
|
||||
stringify({data: {}, status: 'error', error: "Update of 'bulb' failed (Update failed)"}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -164,20 +164,20 @@ describe('Extension: OTAUpdate', () => {
|
||||
expect(isUpdateAvailableSpy).toHaveBeenCalledTimes(1);
|
||||
expect(isUpdateAvailableSpy).toHaveBeenNthCalledWith(1, devices.bulb, {}, undefined, false);
|
||||
expect(updateSpy).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/ota_update/check',
|
||||
stringify({data: {id: 'bulb', update_available: false}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
);
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
isUpdateAvailableSpy.mockResolvedValueOnce({available: true, currentFileVersion: 10, otaFileVersion: 12});
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/ota_update/check', 'bulb');
|
||||
await flushPromises();
|
||||
expect(isUpdateAvailableSpy).toHaveBeenCalledTimes(2);
|
||||
expect(isUpdateAvailableSpy).toHaveBeenNthCalledWith(2, devices.bulb, {}, undefined, false);
|
||||
expect(updateSpy).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/ota_update/check',
|
||||
stringify({data: {id: 'bulb', update_available: true}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -188,7 +188,7 @@ describe('Extension: OTAUpdate', () => {
|
||||
expect(isUpdateAvailableSpy).toHaveBeenCalledTimes(3);
|
||||
expect(isUpdateAvailableSpy).toHaveBeenNthCalledWith(3, devices.bulb, {}, undefined, true);
|
||||
expect(updateSpy).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/ota_update/check',
|
||||
stringify({data: {id: 'bulb', update_available: false}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -199,14 +199,14 @@ describe('Extension: OTAUpdate', () => {
|
||||
const originalDefinition = device.definition;
|
||||
device.definition = Object.assign({}, originalDefinition, {ota: {suppressElementImageParseFailure: true}});
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
isUpdateAvailableSpy.mockResolvedValueOnce({available: true, currentFileVersion: 10, otaFileVersion: 12});
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/ota_update/check/downgrade', 'bulb');
|
||||
await flushPromises();
|
||||
expect(isUpdateAvailableSpy).toHaveBeenCalledTimes(4);
|
||||
expect(isUpdateAvailableSpy).toHaveBeenNthCalledWith(4, devices.bulb, {suppressElementImageParseFailure: true}, undefined, true);
|
||||
expect(updateSpy).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/ota_update/check',
|
||||
stringify({data: {id: 'bulb', update_available: true}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -222,7 +222,7 @@ describe('Extension: OTAUpdate', () => {
|
||||
await flushPromises();
|
||||
expect(isUpdateAvailableSpy).toHaveBeenCalledTimes(1);
|
||||
expect(updateSpy).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/ota_update/check',
|
||||
stringify({
|
||||
data: {},
|
||||
@@ -236,7 +236,7 @@ describe('Extension: OTAUpdate', () => {
|
||||
it('Should fail when device does not exist', async () => {
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/ota_update/check', 'not_existing_deviceooo');
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/ota_update/check',
|
||||
stringify({data: {}, status: 'error', error: `Device 'not_existing_deviceooo' does not exist`}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -246,7 +246,7 @@ describe('Extension: OTAUpdate', () => {
|
||||
it('Should not check for OTA when device does not support it', async () => {
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/ota_update/check', 'dimmer_wall_switch');
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/ota_update/check',
|
||||
stringify({data: {}, status: 'error', error: `Device 'dimmer_wall_switch' does not support OTA updates`}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -267,9 +267,9 @@ describe('Extension: OTAUpdate', () => {
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/ota_update/check', 'bulb');
|
||||
await flushPromises();
|
||||
expect(isUpdateAvailableSpy).toHaveBeenCalledTimes(1);
|
||||
jest.runOnlyPendingTimers();
|
||||
vi.runOnlyPendingTimers();
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/ota_update/check',
|
||||
stringify({data: {}, status: 'error', error: `Update or check for update already in progress for 'bulb'`}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -278,11 +278,11 @@ describe('Extension: OTAUpdate', () => {
|
||||
|
||||
it('Shouldnt crash when read modelID before/after OTA update fails', async () => {
|
||||
devices.bulb.endpoints[0].read.mockRejectedValueOnce('Failed from').mockRejectedValueOnce('Failed to');
|
||||
updateSpy.mockImplementation();
|
||||
updateSpy.mockImplementationOnce(vi.fn());
|
||||
|
||||
mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/ota_update/update', 'bulb');
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/ota_update/update',
|
||||
stringify({data: {id: 'bulb', from: undefined, to: undefined}, status: 'ok'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -319,7 +319,7 @@ describe('Extension: OTAUpdate', () => {
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockLogger.info).not.toHaveBeenCalledWith(`Update available for 'bulb'`);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({update: {state: 'available', installed_version: 10, latest_version: 12}}),
|
||||
{retain: true, qos: 0},
|
||||
@@ -344,7 +344,7 @@ describe('Extension: OTAUpdate', () => {
|
||||
expect(isUpdateAvailableSpy).toHaveBeenCalledWith(devices.bulb, {}, {imageType: 12382}, false);
|
||||
expect(devices.bulb.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
|
||||
expect(devices.bulb.endpoints[0].commandResponse).toHaveBeenCalledWith('genOta', 'queryNextImageResponse', {status: 0x98}, undefined, 10);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({update: {state: 'idle'}}), {retain: true, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({update: {state: 'idle'}}), {retain: true, qos: 0});
|
||||
});
|
||||
|
||||
it('Should check for update when device requests it and it is not available', async () => {
|
||||
@@ -365,7 +365,7 @@ describe('Extension: OTAUpdate', () => {
|
||||
expect(isUpdateAvailableSpy).toHaveBeenCalledWith(devices.bulb, {}, {imageType: 12382}, false);
|
||||
expect(devices.bulb.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
|
||||
expect(devices.bulb.endpoints[0].commandResponse).toHaveBeenCalledWith('genOta', 'queryNextImageResponse', {status: 0x98}, undefined, 10);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({update: {state: 'idle', installed_version: 13, latest_version: 13}}),
|
||||
{retain: true, qos: 0},
|
||||
@@ -427,7 +427,7 @@ describe('Extension: OTAUpdate', () => {
|
||||
});
|
||||
|
||||
it('Sets given configuration', async () => {
|
||||
const setConfiguration = jest.spyOn(zhc.ota, 'setConfiguration');
|
||||
const setConfiguration = vi.spyOn(zhc.ota, 'setConfiguration');
|
||||
settings.set(['ota', 'zigbee_ota_override_index_location'], 'local.index.json');
|
||||
settings.set(['ota', 'image_block_response_delay'], 10000);
|
||||
settings.set(['ota', 'default_maximum_data_size'], 10);
|
||||
|
||||
+194
-194
@@ -1,6 +1,6 @@
|
||||
import * as data from '../mocks/data';
|
||||
import {mockLogger} from '../mocks/logger';
|
||||
import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt';
|
||||
import {events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt';
|
||||
import * as mockSleep from '../mocks/sleep';
|
||||
import {flushPromises} from '../mocks/utils';
|
||||
import {devices, groups, events as mockZHEvents} from '../mocks/zigbeeHerdsman';
|
||||
@@ -13,7 +13,7 @@ import {Controller} from '../../lib/controller';
|
||||
import {loadTopicGetSetRegex} from '../../lib/extension/publish';
|
||||
import * as settings from '../../lib/util/settings';
|
||||
|
||||
const mocksClear = [mockMQTT.publishAsync, mockLogger.warning, mockLogger.debug];
|
||||
const mocksClear = [mockMQTTPublishAsync, mockLogger.warning, mockLogger.debug];
|
||||
|
||||
describe('Extension: Publish', () => {
|
||||
let controller: Controller;
|
||||
@@ -32,9 +32,9 @@ describe('Extension: Publish', () => {
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers();
|
||||
vi.useFakeTimers();
|
||||
data.writeEmptyState();
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
mockSleep.mock();
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
@@ -62,8 +62,8 @@ describe('Extension: Publish', () => {
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await jest.runOnlyPendingTimersAsync();
|
||||
jest.useRealTimers();
|
||||
await vi.runOnlyPendingTimersAsync();
|
||||
vi.useRealTimers();
|
||||
mockSleep.restore();
|
||||
});
|
||||
|
||||
@@ -73,10 +73,10 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genLevelCtrl', 'moveToLevelWithOnOff', {level: 200, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({brightness: 200, state: 'ON'});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({brightness: 200, state: 'ON'});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should corretly handle mallformed messages', async () => {
|
||||
@@ -106,10 +106,10 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genOnOff', 'on', {}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/wohnzimmer.light.wall.right');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({state: 'ON'});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/wohnzimmer.light.wall.right');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({state: 'ON'});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish messages to zigbee devices when brightness is in %', async () => {
|
||||
@@ -119,10 +119,10 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genLevelCtrl', 'moveToLevelWithOnOff', {level: 235, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({state: 'ON', brightness: 235});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({state: 'ON', brightness: 235});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish messages to zigbee devices when brightness is in number', async () => {
|
||||
@@ -132,10 +132,10 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genLevelCtrl', 'moveToLevelWithOnOff', {level: 230, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({state: 'ON', brightness: 230});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({state: 'ON', brightness: 230});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish messages to zigbee devices with color_temp', async () => {
|
||||
@@ -145,10 +145,10 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('lightingColorCtrl', 'moveToColorTemp', {colortemp: 222, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({color_mode: 'color_temp', color_temp: 222});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({color_mode: 'color_temp', color_temp: 222});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish messages to zigbee devices with color_temp in %', async () => {
|
||||
@@ -158,10 +158,10 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('lightingColorCtrl', 'moveToColorTemp', {colortemp: 500, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({color_mode: 'color_temp', color_temp: 500});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({color_mode: 'color_temp', color_temp: 500});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish messages to zigbee devices with non-default ep', async () => {
|
||||
@@ -171,10 +171,10 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genOnOff', 'off', {}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/wall_switch');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({state: 'OFF'});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/wall_switch');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({state: 'OFF'});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish messages to zigbee devices with non-default ep and postfix', async () => {
|
||||
@@ -184,10 +184,10 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genOnOff', 'off', {}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/wall_switch_double');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[1][1])).toStrictEqual({state_right: 'OFF'});
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/wall_switch_double');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1])).toStrictEqual({state_right: 'OFF'});
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish messages to zigbee devices with endpoint ID', async () => {
|
||||
@@ -197,7 +197,7 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genOnOff', 'off', {}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/wall_switch_double', stringify({state_right: 'OFF'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/wall_switch_double', stringify({state_right: 'OFF'}), {
|
||||
qos: 0,
|
||||
retain: false,
|
||||
});
|
||||
@@ -210,10 +210,10 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genOnOff', 'off', {}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/wall_switch_double');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({state_right: 'OFF'});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/wall_switch_double');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({state_right: 'OFF'});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish messages to zigbee devices to non default-ep with brightness_[EP]', async () => {
|
||||
@@ -223,7 +223,7 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genLevelCtrl', 'moveToLevelWithOnOff', {level: 50, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/QS-Zigbee-D02-TRIAC-2C-LN', stringify({brightness_l2: 50, state_l2: 'ON'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/QS-Zigbee-D02-TRIAC-2C-LN', stringify({brightness_l2: 50, state_l2: 'ON'}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
@@ -241,7 +241,7 @@ describe('Extension: Publish', () => {
|
||||
{dpValues: [{data: [1], datatype: 1, dp: 2}], seq: expect.any(Number)},
|
||||
{disableDefaultResponse: true},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/TS0601_switch', stringify({state_l2: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/TS0601_switch', stringify({state_l2: 'ON'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should publish messages to Tuya cover switch with dummy endpoints', async () => {
|
||||
@@ -270,7 +270,7 @@ describe('Extension: Publish', () => {
|
||||
{dpValues: [{data: [0], datatype: 1, dp: 101}], seq: expect.any(Number)},
|
||||
{disableDefaultResponse: true},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/TS0601_cover_switch',
|
||||
stringify({state_l2: 'OFF', state_l1: 'ON', state: 'OPEN'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -284,10 +284,10 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('lightingColorCtrl', 'moveToColor', {colorx: 6553500, colory: 3276750, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({color_mode: 'xy', color: {x: 100, y: 50}});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({color_mode: 'xy', color: {x: 100, y: 50}});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish messages to zigbee devices with color xy and state', async () => {
|
||||
@@ -298,8 +298,8 @@ describe('Extension: Publish', () => {
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(2);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genOnOff', 'on', {}, {});
|
||||
expect(endpoint.command).toHaveBeenCalledWith('lightingColorCtrl', 'moveToColor', {colorx: 6553500, colory: 3276750, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({color_mode: 'xy', color: {x: 100, y: 50}, state: 'ON'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -314,8 +314,8 @@ describe('Extension: Publish', () => {
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(2);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genLevelCtrl', 'moveToLevelWithOnOff', {level: 20, transtime: 0}, {});
|
||||
expect(endpoint.command).toHaveBeenCalledWith('lightingColorCtrl', 'moveToColor', {colorx: 6553500, colory: 3276750, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({color_mode: 'xy', color: {x: 100, y: 50}, state: 'ON', brightness: 20}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -330,8 +330,8 @@ describe('Extension: Publish', () => {
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(2);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genLevelCtrl', 'moveToLevelWithOnOff', {level: 20, transtime: 0}, {});
|
||||
expect(endpoint.command).toHaveBeenCalledWith('lightingColorCtrl', 'moveToColor', {colorx: 6553500, colory: 3276750, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({color: {x: 100, y: 50}, state: 'ON', brightness: 20, color_mode: 'xy'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -346,8 +346,8 @@ describe('Extension: Publish', () => {
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(2);
|
||||
expect(endpoint.command).toHaveBeenNthCalledWith(1, 'lightingColorCtrl', 'moveToColor', {colorx: 6553500, colory: 3276750, transtime: 0}, {});
|
||||
expect(endpoint.command).toHaveBeenNthCalledWith(2, 'genOnOff', 'off', {}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({color_mode: 'xy', color: {x: 100, y: 50}, state: 'OFF'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -361,9 +361,9 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('lightingColorCtrl', 'moveToColor', {colorx: 17806, colory: 43155, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({color: {x: 0.2717, y: 0.6585}, color_mode: 'xy'});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({color: {x: 0.2717, y: 0.6585}, color_mode: 'xy'});
|
||||
});
|
||||
|
||||
it('Should publish messages to zigbee devices with color rgb string', async () => {
|
||||
@@ -373,9 +373,9 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('lightingColorCtrl', 'moveToColor', {colorx: 17806, colory: 43155, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({color: {x: 0.2717, y: 0.6585}, color_mode: 'xy'});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({color: {x: 0.2717, y: 0.6585}, color_mode: 'xy'});
|
||||
});
|
||||
|
||||
it('Should publish messages to zigbee devices with brightness', async () => {
|
||||
@@ -385,9 +385,9 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genLevelCtrl', 'moveToLevelWithOnOff', {level: 50, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({state: 'ON', brightness: 50});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({state: 'ON', brightness: 50});
|
||||
});
|
||||
|
||||
it('Should publish messages groups', async () => {
|
||||
@@ -397,9 +397,9 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(group.command).toHaveBeenCalledTimes(1);
|
||||
expect(group.command).toHaveBeenCalledWith('genOnOff', 'on', {}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); // 'zigbee2mqtt/bulb_color' + below
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/group_1');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[1][1])).toStrictEqual({state: 'ON'});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); // 'zigbee2mqtt/bulb_color' + below
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/group_1');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1])).toStrictEqual({state: 'ON'});
|
||||
group.members.pop();
|
||||
});
|
||||
|
||||
@@ -410,9 +410,9 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(group.command).toHaveBeenCalledTimes(1);
|
||||
expect(group.command).toHaveBeenCalledWith('genLevelCtrl', 'moveToLevelWithOnOff', {level: 128, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); // 'zigbee2mqtt/bulb_color' + below
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/group_1');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[1][1])).toStrictEqual({state: 'ON', brightness: 128});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); // 'zigbee2mqtt/bulb_color' + below
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/group_1');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1])).toStrictEqual({state: 'ON', brightness: 128});
|
||||
group.members.pop();
|
||||
});
|
||||
|
||||
@@ -437,9 +437,9 @@ describe('Extension: Publish', () => {
|
||||
console.log(mockLogger.warning.mock.calls);
|
||||
expect(group.command).toHaveBeenCalledTimes(1);
|
||||
expect(group.command).toHaveBeenCalledWith('genOnOff', 'on', {}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/hue_twilight_group');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[1][1])).toStrictEqual({state: 'ON'});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/hue_twilight_group');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1])).toStrictEqual({state: 'ON'});
|
||||
});
|
||||
|
||||
it('Should publish messages to groups with on and brightness', async () => {
|
||||
@@ -449,9 +449,9 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(group.command).toHaveBeenCalledTimes(1);
|
||||
expect(group.command).toHaveBeenCalledWith('genLevelCtrl', 'moveToLevelWithOnOff', {level: 50, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); // 'zigbee2mqtt/bulb_color' + below
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/group_1');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[1][1])).toStrictEqual({state: 'ON', brightness: 50});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); // 'zigbee2mqtt/bulb_color' + below
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/group_1');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1])).toStrictEqual({state: 'ON', brightness: 50});
|
||||
group.members.pop();
|
||||
});
|
||||
|
||||
@@ -462,9 +462,9 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(group.command).toHaveBeenCalledTimes(1);
|
||||
expect(group.command).toHaveBeenCalledWith('genOnOff', 'off', {}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); // 'zigbee2mqtt/bulb_color' + below
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/group_1');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[1][1])).toStrictEqual({state: 'OFF'});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); // 'zigbee2mqtt/bulb_color' + below
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/group_1');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1])).toStrictEqual({state: 'OFF'});
|
||||
group.members.pop();
|
||||
});
|
||||
|
||||
@@ -475,9 +475,9 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(group.command).toHaveBeenCalledTimes(1);
|
||||
expect(group.command).toHaveBeenCalledWith('lightingColorCtrl', 'moveToColor', {colorx: 24248, colory: 18350, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); // 'zigbee2mqtt/bulb_color' + below
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/group_1');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[1][1])).toStrictEqual({color: {x: 0.37, y: 0.28}, color_mode: 'xy'});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); // 'zigbee2mqtt/bulb_color' + below
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/group_1');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1])).toStrictEqual({color: {x: 0.37, y: 0.28}, color_mode: 'xy'});
|
||||
group.members.pop();
|
||||
});
|
||||
|
||||
@@ -488,9 +488,9 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(group.command).toHaveBeenCalledTimes(1);
|
||||
expect(group.command).toHaveBeenCalledWith('lightingColorCtrl', 'moveToColorTemp', {colortemp: 100, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); // 'zigbee2mqtt/bulb_color' + below
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/group_1');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[1][1])).toStrictEqual({color_temp: 100, color_mode: 'color_temp'});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); // 'zigbee2mqtt/bulb_color' + below
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/group_1');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1])).toStrictEqual({color_temp: 100, color_mode: 'color_temp'});
|
||||
group.members.pop();
|
||||
});
|
||||
|
||||
@@ -515,7 +515,7 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genLevelCtrl', 'moveToLevelWithOnOff', {level: 200, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('Shouldnt publish new brightness state when filtered_optimistic is used', async () => {
|
||||
@@ -526,15 +526,15 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genLevelCtrl', 'moveToLevelWithOnOff', {level: 200, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({state: 'ON'});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({state: 'ON'});
|
||||
});
|
||||
|
||||
it('Shouldnt publish new state when optimistic = false for group', async () => {
|
||||
settings.set(['groups', '2', 'optimistic'], false);
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_2/set', stringify({brightness: '200'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('Should handle non-valid topics', async () => {
|
||||
@@ -681,9 +681,9 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('lightingColorCtrl', 'moveToColor', {colorx: 17806, colory: 43155, transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({color: {x: 0.2717, y: 0.6585}, color_mode: 'xy'});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({color: {x: 0.2717, y: 0.6585}, color_mode: 'xy'});
|
||||
});
|
||||
|
||||
it('Should parse set with ieeeAddr topic', async () => {
|
||||
@@ -730,13 +730,13 @@ describe('Extension: Publish', () => {
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(2);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genOnOff', 'on', {}, {});
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genOnOff', 'toggle', {}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0]).toEqual([
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0]).toEqual([
|
||||
'zigbee2mqtt/wall_switch_double',
|
||||
stringify({state_left: 'ON'}),
|
||||
{qos: 0, retain: false},
|
||||
]);
|
||||
expect(mockMQTT.publishAsync.mock.calls[1]).toEqual([
|
||||
expect(mockMQTTPublishAsync.mock.calls[1]).toEqual([
|
||||
'zigbee2mqtt/wall_switch_double',
|
||||
stringify({state_left: 'OFF'}),
|
||||
{qos: 0, retain: false},
|
||||
@@ -803,25 +803,25 @@ describe('Extension: Publish', () => {
|
||||
it('Should also use on/off cluster when controlling group with switch', async () => {
|
||||
const group = groups.group_with_switch;
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
group.command.mockClear();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/switch_group/set', stringify({state: 'ON', brightness: 100}));
|
||||
await flushPromises();
|
||||
expect(group.command).toHaveBeenCalledTimes(2);
|
||||
expect(group.command).toHaveBeenCalledWith('genLevelCtrl', 'moveToLevelWithOnOff', {level: 100, transtime: 0}, {});
|
||||
expect(group.command).toHaveBeenCalledWith('genOnOff', 'on', {}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/switch_group', stringify({state: 'ON', brightness: 100}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/switch_group', stringify({state: 'ON', brightness: 100}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
group.command.mockClear();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/switch_group/set', stringify({state: 'OFF'}));
|
||||
await flushPromises();
|
||||
expect(group.command).toHaveBeenCalledTimes(1);
|
||||
expect(group.command).toHaveBeenCalledWith('genOnOff', 'off', {}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/switch_group', stringify({state: 'OFF', brightness: 100}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/switch_group', stringify({state: 'OFF', brightness: 100}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
@@ -835,9 +835,9 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(group.command).toHaveBeenCalledTimes(1);
|
||||
expect(group.command).toHaveBeenCalledWith('genLevelCtrl', 'moveToLevelWithOnOff', {level: 100, transtime: 200}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); // zigbee2mqtt/bulb_color + below
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/group_1');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[1][1])).toStrictEqual({state: 'ON', brightness: 100});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); // zigbee2mqtt/bulb_color + below
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/group_1');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1])).toStrictEqual({state: 'ON', brightness: 100});
|
||||
group.members.pop();
|
||||
});
|
||||
|
||||
@@ -885,24 +885,24 @@ describe('Extension: Publish', () => {
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'ON', transition: 0}));
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(5);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(1, 'zigbee2mqtt/bulb_color', stringify({state: 'ON', brightness: 200}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(5);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(1, 'zigbee2mqtt/bulb_color', stringify({state: 'ON', brightness: 200}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(2, 'zigbee2mqtt/bulb_color', stringify({state: 'OFF', brightness: 200}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(2, 'zigbee2mqtt/bulb_color', stringify({state: 'OFF', brightness: 200}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(3, 'zigbee2mqtt/bulb_color', stringify({state: 'OFF', brightness: 1}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(3, 'zigbee2mqtt/bulb_color', stringify({state: 'OFF', brightness: 1}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(4, 'zigbee2mqtt/bulb_color', stringify({state: 'OFF', brightness: 1}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(4, 'zigbee2mqtt/bulb_color', stringify({state: 'OFF', brightness: 1}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(5, 'zigbee2mqtt/bulb_color', stringify({state: 'ON', brightness: 200}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(5, 'zigbee2mqtt/bulb_color', stringify({state: 'ON', brightness: 200}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
@@ -922,8 +922,8 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'ON', transition: 0.5}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(1, 'zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(2, 'zigbee2mqtt/bulb_color', stringify({state: 'ON', brightness: 254}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(1, 'zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(2, 'zigbee2mqtt/bulb_color', stringify({state: 'ON', brightness: 254}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
@@ -1014,18 +1014,18 @@ describe('Extension: Publish', () => {
|
||||
expect(endpoint.command.mock.calls[0]).toEqual(['genLevelCtrl', 'moveToLevelWithOnOff', {level: 50, transtime: 0}, {}]);
|
||||
expect(endpoint.command.mock.calls[1]).toEqual(['genLevelCtrl', 'moveToLevelWithOnOff', {level: 0, transtime: 0}, {}]);
|
||||
expect(endpoint.command.mock.calls[2]).toEqual(['genLevelCtrl', 'moveToLevelWithOnOff', {level: 50, transtime: 0}, {}]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0]).toEqual([
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0]).toEqual([
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({state: 'ON', brightness: 50}),
|
||||
{qos: 0, retain: false},
|
||||
]);
|
||||
expect(mockMQTT.publishAsync.mock.calls[1]).toEqual([
|
||||
expect(mockMQTTPublishAsync.mock.calls[1]).toEqual([
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({state: 'OFF', brightness: 0}),
|
||||
{qos: 0, retain: false},
|
||||
]);
|
||||
expect(mockMQTT.publishAsync.mock.calls[2]).toEqual([
|
||||
expect(mockMQTTPublishAsync.mock.calls[2]).toEqual([
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({state: 'ON', brightness: 50}),
|
||||
{qos: 0, retain: false},
|
||||
@@ -1045,18 +1045,18 @@ describe('Extension: Publish', () => {
|
||||
expect(endpoint.command.mock.calls[0]).toEqual(['genLevelCtrl', 'moveToLevelWithOnOff', {level: 50, transtime: 0}, {}]);
|
||||
expect(endpoint.command.mock.calls[1]).toEqual(['genLevelCtrl', 'moveToLevelWithOnOff', {level: 0, transtime: 30}, {}]);
|
||||
expect(endpoint.command.mock.calls[2]).toEqual(['genLevelCtrl', 'moveToLevelWithOnOff', {level: 50, transtime: 0}, {}]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0]).toEqual([
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0]).toEqual([
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({state: 'ON', brightness: 50}),
|
||||
{qos: 0, retain: false},
|
||||
]);
|
||||
expect(mockMQTT.publishAsync.mock.calls[1]).toEqual([
|
||||
expect(mockMQTTPublishAsync.mock.calls[1]).toEqual([
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({state: 'OFF', brightness: 0}),
|
||||
{qos: 0, retain: false},
|
||||
]);
|
||||
expect(mockMQTT.publishAsync.mock.calls[2]).toEqual([
|
||||
expect(mockMQTTPublishAsync.mock.calls[2]).toEqual([
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({state: 'ON', brightness: 50}),
|
||||
{qos: 0, retain: false},
|
||||
@@ -1076,9 +1076,9 @@ describe('Extension: Publish', () => {
|
||||
{direction: 0, enhancehue: 45510, saturation: 127, transtime: 0},
|
||||
{},
|
||||
]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({color: {hue: 250, saturation: 50}, color_mode: 'hs'});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({color: {hue: 250, saturation: 50}, color_mode: 'hs'});
|
||||
});
|
||||
|
||||
it('ZNCLDJ11LM open', async () => {
|
||||
@@ -1149,9 +1149,9 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command.mock.calls[0]).toEqual(['genLevelCtrl', 'moveToLevelWithOnOff', {level: 0, transtime: 10}, {}]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({state: 'OFF'});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({state: 'OFF'});
|
||||
});
|
||||
|
||||
it('When device is turned off and on with transition with report enabled it should restore correct brightness', async () => {
|
||||
@@ -1161,15 +1161,15 @@ describe('Extension: Publish', () => {
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({brightness: 200}));
|
||||
await flushPromises();
|
||||
endpoint.command.mockClear();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
// Turn off
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF', transition: 3}));
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command.mock.calls[0]).toEqual(['genLevelCtrl', 'moveToLevelWithOnOff', {level: 0, transtime: 30}, {}]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0]).toEqual([
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0]).toEqual([
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({state: 'OFF', brightness: 200}),
|
||||
{qos: 0, retain: false},
|
||||
@@ -1185,8 +1185,8 @@ describe('Extension: Publish', () => {
|
||||
linkquality: 10,
|
||||
});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync.mock.calls[1]).toEqual([
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync.mock.calls[1]).toEqual([
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({state: 'OFF', brightness: 1}),
|
||||
{qos: 0, retain: false},
|
||||
@@ -1197,8 +1197,8 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(2);
|
||||
expect(endpoint.command.mock.calls[1]).toEqual(['genLevelCtrl', 'moveToLevelWithOnOff', {level: 200, transtime: 30}, {}]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTT.publishAsync.mock.calls[2]).toEqual([
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTTPublishAsync.mock.calls[2]).toEqual([
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({state: 'ON', brightness: 200}),
|
||||
{qos: 0, retain: false},
|
||||
@@ -1212,15 +1212,15 @@ describe('Extension: Publish', () => {
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({brightness: 200}));
|
||||
await flushPromises();
|
||||
endpoint.command.mockClear();
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
|
||||
// Turn off
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF', transition: 3}));
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command.mock.calls[0]).toEqual(['genLevelCtrl', 'moveToLevelWithOnOff', {level: 0, transtime: 30}, {}]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0]).toEqual([
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0]).toEqual([
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({state: 'OFF', brightness: 200}),
|
||||
{qos: 0, retain: false},
|
||||
@@ -1231,8 +1231,8 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(2);
|
||||
expect(endpoint.command.mock.calls[1]).toEqual(['genLevelCtrl', 'moveToLevelWithOnOff', {level: 200, transtime: 0}, {}]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync.mock.calls[1]).toEqual([
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync.mock.calls[1]).toEqual([
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({state: 'ON', brightness: 200}),
|
||||
{qos: 0, retain: false},
|
||||
@@ -1248,9 +1248,9 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command.mock.calls[0]).toEqual(['genOnOff', 'on', {}, {}]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({state: 'ON'});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({state: 'ON'});
|
||||
});
|
||||
|
||||
it('Home Assistant: should not set state when color temperature is also set and device is already on', async () => {
|
||||
@@ -1267,9 +1267,9 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command.mock.calls[0]).toEqual(['lightingColorCtrl', 'moveToColorTemp', {colortemp: 100, transtime: 0}, {}]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({state: 'ON', color_temp: 100, color_mode: 'color_temp'});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({state: 'ON', color_temp: 100, color_mode: 'color_temp'});
|
||||
});
|
||||
|
||||
it('Home Assistant: should set state when color temperature is also set and device is off', async () => {
|
||||
@@ -1287,8 +1287,8 @@ describe('Extension: Publish', () => {
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(2);
|
||||
expect(endpoint.command.mock.calls[0]).toEqual(['genOnOff', 'on', {}, {}]);
|
||||
expect(endpoint.command.mock.calls[1]).toEqual(['lightingColorCtrl', 'moveToColorTemp', {colortemp: 100, transtime: 0}, {}]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({state: 'ON', color_temp: 100, color_mode: 'color_temp'}),
|
||||
{retain: false, qos: 0},
|
||||
@@ -1309,9 +1309,9 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command.mock.calls[0]).toEqual(['lightingColorCtrl', 'moveToColor', {colorx: 26869, colory: 16384, transtime: 0}, {}]);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({color: {x: 0.41, y: 0.25}, state: 'ON', color_mode: 'xy'});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({color: {x: 0.41, y: 0.25}, state: 'ON', color_mode: 'xy'});
|
||||
});
|
||||
|
||||
it('Should publish correct state on toggle command to zigbee bulb', async () => {
|
||||
@@ -1322,13 +1322,13 @@ describe('Extension: Publish', () => {
|
||||
// At this point the bulb has no state yet, so we cannot determine the next state and therefore shouldn't publish it to mockMQTT.
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genOnOff', 'toggle', {}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0);
|
||||
|
||||
// Turn bulb off so that the bulb gets a state.
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(1, 'zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(1, 'zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0});
|
||||
|
||||
// Toggle again, now that we have state it should publish state ON
|
||||
endpoint.read.mockImplementationOnce((cluster, attrs) => {
|
||||
@@ -1339,8 +1339,8 @@ describe('Extension: Publish', () => {
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'TOGGLE'}));
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(2, 'zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(2, 'zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should publish messages with options disableDefaultResponse', async () => {
|
||||
@@ -1350,20 +1350,20 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genOnOff', 'off', {}, {disableDefaultResponse: true});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/led_controller_1');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({state: 'OFF'});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/led_controller_1');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({state: 'OFF'});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish messages to zigbee devices', async () => {
|
||||
settings.set(['advanced', 'last_seen'], 'ISO_8601');
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({brightness: '200'}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(typeof JSON.parse(mockMQTT.publishAsync.mock.calls[0][1]).last_seen).toStrictEqual('string');
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(typeof JSON.parse(mockMQTTPublishAsync.mock.calls[0][1]).last_seen).toStrictEqual('string');
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish brightness_move up to zigbee devices', async () => {
|
||||
@@ -1451,10 +1451,10 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith('genLevelCtrl', 'moveToLevelWithOnOff', {level: '255', transtime: 0}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/roller_shutter');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({position: 100});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/roller_shutter');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({position: 100});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish to MKS-CM-W5', async () => {
|
||||
@@ -1469,10 +1469,10 @@ describe('Extension: Publish', () => {
|
||||
{dpValues: [{data: [1], datatype: 1, dp: 3}], seq: expect.any(Number)},
|
||||
{disableDefaultResponse: true},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/MKS-CM-W5');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({state_l3: 'ON'});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/MKS-CM-W5');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({state_l3: 'ON'});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish separate genOnOff to GL-S-007ZS when setting state and brightness as bulb doesnt turn on with moveToLevelWithOnOff', async () => {
|
||||
@@ -1481,11 +1481,11 @@ describe('Extension: Publish', () => {
|
||||
const endpoint = device.getEndpoint(1)!;
|
||||
await mockMQTTEvents.message('zigbee2mqtt/GL-S-007ZS/set', stringify({state: 'ON', brightness: 20}));
|
||||
await flushPromises();
|
||||
await jest.runOnlyPendingTimersAsync();
|
||||
await vi.runOnlyPendingTimersAsync();
|
||||
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(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/GL-S-007ZS', stringify({state: 'ON', brightness: 20}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/GL-S-007ZS', stringify({state: 'ON', brightness: 20}), {
|
||||
qos: 0,
|
||||
retain: false,
|
||||
});
|
||||
@@ -1568,19 +1568,19 @@ describe('Extension: Publish', () => {
|
||||
expect(endpoint.command.mock.calls[2]).toEqual(['genOnOff', 'off', {}, {}]);
|
||||
expect(endpoint.command.mock.calls[3]).toEqual(['genOnOff', 'on', {}, {}]);
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(4);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0]).toEqual(['zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {qos: 0, retain: false}]);
|
||||
expect(mockMQTT.publishAsync.mock.calls[1]).toEqual([
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(4);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0]).toEqual(['zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {qos: 0, retain: false}]);
|
||||
expect(mockMQTTPublishAsync.mock.calls[1]).toEqual([
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({state: 'ON', brightness: 150}),
|
||||
{qos: 0, retain: false},
|
||||
]);
|
||||
expect(mockMQTT.publishAsync.mock.calls[2]).toEqual([
|
||||
expect(mockMQTTPublishAsync.mock.calls[2]).toEqual([
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({state: 'OFF', brightness: 150}),
|
||||
{qos: 0, retain: false},
|
||||
]);
|
||||
expect(mockMQTT.publishAsync.mock.calls[3]).toEqual([
|
||||
expect(mockMQTTPublishAsync.mock.calls[3]).toEqual([
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({state: 'ON', brightness: 150}),
|
||||
{qos: 0, retain: false},
|
||||
@@ -1597,63 +1597,63 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
expect(group.command).toHaveBeenCalledTimes(1);
|
||||
expect(group.command).toHaveBeenCalledWith('genScenes', 'store', {groupid: 15071, sceneid: 1}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), {retain: true, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), {retain: true, qos: 0});
|
||||
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color_2/set', stringify({state: 'ON', brightness: 250, color_temp: 20}));
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_2/set', stringify({state: 'ON', brightness: 110}));
|
||||
await flushPromises();
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
group.command.mockClear();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/group_tradfri_remote/set', stringify({scene_recall: 1}));
|
||||
await flushPromises();
|
||||
expect(group.command).toHaveBeenCalledTimes(1);
|
||||
expect(group.command).toHaveBeenCalledWith('genScenes', 'recall', {groupid: 15071, sceneid: 1}, {});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(8);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(8);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'zigbee2mqtt/group_tradfri_remote',
|
||||
stringify({brightness: 50, color_temp: 290, state: 'ON', color_mode: 'color_temp'}),
|
||||
{retain: false, qos: 0},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'zigbee2mqtt/bulb_color_2',
|
||||
stringify({color_mode: 'color_temp', brightness: 50, color_temp: 290, state: 'ON'}),
|
||||
{retain: false, qos: 0},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
'zigbee2mqtt/ha_discovery_group',
|
||||
stringify({brightness: 50, color_mode: 'color_temp', color_temp: 290, state: 'ON'}),
|
||||
{retain: false, qos: 0},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(
|
||||
4,
|
||||
'zigbee2mqtt/group_tradfri_remote',
|
||||
stringify({brightness: 100, color_temp: 290, state: 'ON', color_mode: 'color_temp'}),
|
||||
{retain: false, qos: 0},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(
|
||||
5,
|
||||
'zigbee2mqtt/bulb_2',
|
||||
stringify({brightness: 100, color_mode: 'color_temp', color_temp: 290, state: 'ON'}),
|
||||
{retain: false, qos: 0},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(
|
||||
6,
|
||||
'zigbee2mqtt/group_with_tradfri',
|
||||
stringify({brightness: 100, color_mode: 'color_temp', color_temp: 290, state: 'ON'}),
|
||||
{retain: false, qos: 0},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(
|
||||
7,
|
||||
'zigbee2mqtt/switch_group',
|
||||
stringify({brightness: 100, color_mode: 'color_temp', color_temp: 290, state: 'ON'}),
|
||||
{retain: false, qos: 0},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(
|
||||
8,
|
||||
'zigbee2mqtt/ha_discovery_group',
|
||||
stringify({brightness: 100, color_mode: 'color_temp', color_temp: 290, state: 'ON'}),
|
||||
@@ -1668,18 +1668,18 @@ describe('Extension: Publish', () => {
|
||||
await flushPromises();
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({color_temp: 300}));
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(1, 'zigbee2mqtt/bulb_color', stringify({color_mode: 'color_temp', color_temp: 100}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(1, 'zigbee2mqtt/bulb_color', stringify({color_mode: 'color_temp', color_temp: 100}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({color: {x: 0.1, y: 0.5}, color_mode: 'xy', color_temp: 79}),
|
||||
{retain: false, qos: 0},
|
||||
);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(
|
||||
expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({color: {x: 0.4152, y: 0.3954}, color_mode: 'color_temp', color_temp: 300}),
|
||||
|
||||
+139
-142
@@ -1,6 +1,6 @@
|
||||
import * as data from '../mocks/data';
|
||||
import {mockLogger} from '../mocks/logger';
|
||||
import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt';
|
||||
import {events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt';
|
||||
import * as mockSleep from '../mocks/sleep';
|
||||
import {flushPromises} from '../mocks/utils';
|
||||
import {devices, events as mockZHEvents} from '../mocks/zigbeeHerdsman';
|
||||
@@ -10,17 +10,17 @@ import stringify from 'json-stable-stringify-without-jsonify';
|
||||
import {Controller} from '../../lib/controller';
|
||||
import * as settings from '../../lib/util/settings';
|
||||
|
||||
const mocksClear = [mockMQTT.publishAsync, mockLogger.warning, mockLogger.debug];
|
||||
const mocksClear = [mockMQTTPublishAsync, mockLogger.warning, mockLogger.debug];
|
||||
|
||||
describe('Extension: Receive', () => {
|
||||
let controller: Controller;
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers();
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
vi.useFakeTimers();
|
||||
controller = new Controller(vi.fn(), vi.fn());
|
||||
mockSleep.mock();
|
||||
await controller.start();
|
||||
await jest.runOnlyPendingTimersAsync();
|
||||
await vi.runOnlyPendingTimersAsync();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -33,7 +33,7 @@ describe('Extension: Receive', () => {
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
jest.useRealTimers();
|
||||
vi.useRealTimers();
|
||||
mockSleep.restore();
|
||||
});
|
||||
|
||||
@@ -44,8 +44,8 @@ describe('Extension: Receive', () => {
|
||||
const payload = {data, cluster: 'genOnOff', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/button', stringify({action: 'single', linkquality: 10}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/button', stringify({action: 'single', linkquality: 10}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
@@ -57,10 +57,10 @@ describe('Extension: Receive', () => {
|
||||
const payload = {data, cluster: 'genOnOff', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button_double_key');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({action: 'single_left'});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button_double_key');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({action: 'single_left'});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should handle a zigbee message which uses ep (right)', async () => {
|
||||
@@ -69,10 +69,10 @@ describe('Extension: Receive', () => {
|
||||
const payload = {data, cluster: 'genOnOff', device, endpoint: device.getEndpoint(2), type: 'attributeReport', linkquality: 10};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button_double_key');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({action: 'single_right'});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button_double_key');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({action: 'single_right'});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should handle a zigbee message with default precision', async () => {
|
||||
@@ -88,10 +88,10 @@ describe('Extension: Receive', () => {
|
||||
};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({temperature: -0.85});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 1, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({temperature: -0.85});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 1, retain: false});
|
||||
});
|
||||
|
||||
it('Should allow to invert cover', async () => {
|
||||
@@ -107,14 +107,14 @@ describe('Extension: Receive', () => {
|
||||
linkquality: 10,
|
||||
});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/J1_cover', stringify({position: 10, tilt: 20, state: 'OPEN'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/J1_cover', stringify({position: 10, tilt: 20, state: 'OPEN'}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
|
||||
// Inverted
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
settings.set(['devices', device.ieeeAddr, 'invert_cover'], true);
|
||||
await mockZHEvents.message({
|
||||
data: {currentPositionLiftPercentage: 90, currentPositionTiltPercentage: 80},
|
||||
@@ -125,8 +125,8 @@ describe('Extension: Receive', () => {
|
||||
linkquality: 10,
|
||||
});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/J1_cover', stringify({position: 90, tilt: 80, state: 'OPEN'}), {
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/J1_cover', stringify({position: 90, tilt: 80, state: 'OPEN'}), {
|
||||
retain: false,
|
||||
qos: 0,
|
||||
});
|
||||
@@ -139,8 +139,8 @@ describe('Extension: Receive', () => {
|
||||
const payload = {data, cluster: 'genOnOff', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/button', stringify({action: 'single'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/button', stringify({action: 'single'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should debounce messages', async () => {
|
||||
@@ -177,14 +177,14 @@ describe('Extension: Receive', () => {
|
||||
};
|
||||
await mockZHEvents.message(payload3);
|
||||
await flushPromises();
|
||||
jest.advanceTimersByTime(50);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0);
|
||||
jest.runOnlyPendingTimers();
|
||||
vi.advanceTimersByTime(50);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0);
|
||||
vi.runOnlyPendingTimers();
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({temperature: 0.08, humidity: 0.01, pressure: 2});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 1, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({temperature: 0.08, humidity: 0.01, pressure: 2});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 1, retain: false});
|
||||
});
|
||||
|
||||
it('Should debounce and retain messages when set via device_options', async () => {
|
||||
@@ -223,14 +223,14 @@ describe('Extension: Receive', () => {
|
||||
};
|
||||
await mockZHEvents.message(payload3);
|
||||
await flushPromises();
|
||||
jest.advanceTimersByTime(50);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0);
|
||||
jest.runOnlyPendingTimers();
|
||||
vi.advanceTimersByTime(50);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0);
|
||||
vi.runOnlyPendingTimers();
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({temperature: 0.08, humidity: 0.01, pressure: 2});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 1, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({temperature: 0.08, humidity: 0.01, pressure: 2});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 1, retain: true});
|
||||
});
|
||||
|
||||
it('Should debounce messages only with the same payload values for provided debounce_ignore keys', async () => {
|
||||
@@ -274,20 +274,19 @@ describe('Extension: Receive', () => {
|
||||
};
|
||||
await mockZHEvents.message(humidityMsg);
|
||||
await flushPromises();
|
||||
jest.advanceTimersByTime(50);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({temperature: 0.08, pressure: 2});
|
||||
jest.runOnlyPendingTimers();
|
||||
vi.advanceTimersByTime(50);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({temperature: 0.08, pressure: 2});
|
||||
vi.runOnlyPendingTimers();
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[1][1])).toStrictEqual({temperature: 0.07, pressure: 2, humidity: 0.03});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1])).toStrictEqual({temperature: 0.07, pressure: 2, humidity: 0.03});
|
||||
});
|
||||
|
||||
it('Should NOT publish old messages from State cache during debouncing', async () => {
|
||||
// Summary:
|
||||
// First send multiple measurements to device that is debouncing. Make sure only one message is sent out to mockMQTT. This also ensures first message is cached to "State".
|
||||
// Then send another measurement to that same device and trigger asynchronous event to push data from Cache. Newest value should be sent out.
|
||||
|
||||
const device = devices.WSDCGQ11LM;
|
||||
settings.set(['devices', device.ieeeAddr, 'debounce'], 0.1);
|
||||
await mockZHEvents.message({
|
||||
@@ -315,16 +314,15 @@ describe('Extension: Receive', () => {
|
||||
linkquality: 10,
|
||||
});
|
||||
await flushPromises();
|
||||
jest.advanceTimersByTime(50);
|
||||
vi.advanceTimersByTime(50);
|
||||
// Test that measurements are combined(=debounced)
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0);
|
||||
jest.runOnlyPendingTimers();
|
||||
await flushPromises();
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0);
|
||||
vi.runOnlyPendingTimers();
|
||||
|
||||
// Test that only one MQTT is sent out and test its values.
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({temperature: 0.08, humidity: 0.01, pressure: 2});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({temperature: 0.08, humidity: 0.01, pressure: 2});
|
||||
|
||||
// Send another Zigbee message...
|
||||
await mockZHEvents.message({
|
||||
@@ -340,16 +338,15 @@ describe('Extension: Receive', () => {
|
||||
|
||||
// Trigger asynchronous event while device is "debouncing" to trigger Message to be sent out from State cache.
|
||||
await controller.publishEntityState(realDevice, {});
|
||||
jest.runOnlyPendingTimers();
|
||||
await flushPromises();
|
||||
vi.runOnlyPendingTimers();
|
||||
|
||||
// Total of 3 messages should have triggered.
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3);
|
||||
|
||||
// Test that message pushed by asynchronous message contains NEW measurement and not old.
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[1][1])).toStrictEqual({temperature: 0.09, humidity: 0.01, pressure: 2});
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1])).toStrictEqual({temperature: 0.09, humidity: 0.01, pressure: 2});
|
||||
// Test that messages after debouncing contains NEW measurement and not old.
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[2][1])).toStrictEqual({temperature: 0.09, humidity: 0.01, pressure: 2});
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[2][1])).toStrictEqual({temperature: 0.09, humidity: 0.01, pressure: 2});
|
||||
});
|
||||
|
||||
it('Should throttle multiple messages from spamming devices', async () => {
|
||||
@@ -390,22 +387,22 @@ describe('Extension: Receive', () => {
|
||||
await mockZHEvents.message(payload3);
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/spammer1');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({temperature: 0.01});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/spammer1');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({temperature: 0.01});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: true});
|
||||
|
||||
// Now we try after elapsed time to see if it publishes next message
|
||||
const timeshift = throttle_for_testing * 2000;
|
||||
jest.advanceTimersByTime(timeshift);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
vi.advanceTimersByTime(timeshift);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/spammer1');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[1][1])).toStrictEqual({temperature: 0.03});
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][2]).toStrictEqual({qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/spammer1');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1])).toStrictEqual({temperature: 0.03});
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][2]).toStrictEqual({qos: 0, retain: true});
|
||||
|
||||
const data4 = {measuredValue: 4};
|
||||
const payload4 = {
|
||||
@@ -419,10 +416,10 @@ describe('Extension: Receive', () => {
|
||||
await mockZHEvents.message(payload4);
|
||||
await flushPromises();
|
||||
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTT.publishAsync.mock.calls[2][0]).toStrictEqual('zigbee2mqtt/spammer1');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[2][1])).toStrictEqual({temperature: 0.04});
|
||||
expect(mockMQTT.publishAsync.mock.calls[2][2]).toStrictEqual({qos: 0, retain: true});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3);
|
||||
expect(mockMQTTPublishAsync.mock.calls[2][0]).toStrictEqual('zigbee2mqtt/spammer1');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[2][1])).toStrictEqual({temperature: 0.04});
|
||||
expect(mockMQTTPublishAsync.mock.calls[2][2]).toStrictEqual({qos: 0, retain: true});
|
||||
});
|
||||
|
||||
it('Shouldnt republish old state', async () => {
|
||||
@@ -439,10 +436,10 @@ describe('Extension: Receive', () => {
|
||||
});
|
||||
await mockMQTTEvents.message('zigbee2mqtt/bulb/set', stringify({state: 'ON'}));
|
||||
await flushPromises();
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({state: 'ON'});
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[1][1])).toStrictEqual({state: 'ON'});
|
||||
vi.runOnlyPendingTimers();
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({state: 'ON'});
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1])).toStrictEqual({state: 'ON'});
|
||||
});
|
||||
|
||||
it('Should handle a zigbee message with 1 precision', async () => {
|
||||
@@ -459,10 +456,10 @@ describe('Extension: Receive', () => {
|
||||
};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({temperature: -0.8});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 1, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({temperature: -0.8});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 1, retain: false});
|
||||
});
|
||||
|
||||
it('Should handle a zigbee message with 0 precision', async () => {
|
||||
@@ -479,10 +476,10 @@ describe('Extension: Receive', () => {
|
||||
};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({temperature: -1});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 1, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({temperature: -1});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 1, retain: false});
|
||||
});
|
||||
|
||||
it('Should handle a zigbee message with 1 precision when set via device_options', async () => {
|
||||
@@ -499,10 +496,10 @@ describe('Extension: Receive', () => {
|
||||
};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({temperature: -0.8});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 1, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({temperature: -0.8});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 1, retain: false});
|
||||
});
|
||||
|
||||
it('Should handle a zigbee message with 2 precision when overrides device_options', async () => {
|
||||
@@ -520,10 +517,10 @@ describe('Extension: Receive', () => {
|
||||
};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({temperature: -1});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 1, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({temperature: -1});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 1, retain: false});
|
||||
});
|
||||
|
||||
it('Should handle a zigbee message with voltage 2990', async () => {
|
||||
@@ -532,10 +529,10 @@ describe('Extension: Receive', () => {
|
||||
const payload = {data, cluster: 'genBasic', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button_double_key');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({battery: 93, voltage: 2990});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button_double_key');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({battery: 93, voltage: 2990});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish 1 message when converted twice', async () => {
|
||||
@@ -544,16 +541,16 @@ describe('Extension: Receive', () => {
|
||||
const payload = {data, cluster: 'genBasic', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/occupancy_sensor');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/occupancy_sensor');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({
|
||||
battery: 100,
|
||||
illuminance: 381,
|
||||
voltage: 3045,
|
||||
device_temperature: 19,
|
||||
power_outage_count: 34,
|
||||
});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish 1 message when converted twice', async () => {
|
||||
@@ -562,7 +559,7 @@ describe('Extension: Receive', () => {
|
||||
const payload = {data, cluster: 'genBasic', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('Should publish last_seen epoch', async () => {
|
||||
@@ -572,10 +569,10 @@ describe('Extension: Receive', () => {
|
||||
const payload = {data, cluster: 'genOnOff', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button_double_key');
|
||||
expect(typeof JSON.parse(mockMQTT.publishAsync.mock.calls[0][1]).last_seen).toBe('number');
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button_double_key');
|
||||
expect(typeof JSON.parse(mockMQTTPublishAsync.mock.calls[0][1]).last_seen).toBe('number');
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish last_seen ISO_8601', async () => {
|
||||
@@ -585,10 +582,10 @@ describe('Extension: Receive', () => {
|
||||
const payload = {data, cluster: 'genOnOff', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button_double_key');
|
||||
expect(typeof JSON.parse(mockMQTT.publishAsync.mock.calls[0][1]).last_seen).toBe('string');
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button_double_key');
|
||||
expect(typeof JSON.parse(mockMQTTPublishAsync.mock.calls[0][1]).last_seen).toBe('string');
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should publish last_seen ISO_8601_local', async () => {
|
||||
@@ -598,10 +595,10 @@ describe('Extension: Receive', () => {
|
||||
const payload = {data, cluster: 'genOnOff', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button_double_key');
|
||||
expect(typeof JSON.parse(mockMQTT.publishAsync.mock.calls[0][1]).last_seen).toBe('string');
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button_double_key');
|
||||
expect(typeof JSON.parse(mockMQTTPublishAsync.mock.calls[0][1]).last_seen).toBe('string');
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should handle messages from Xiaomi router devices', async () => {
|
||||
@@ -610,9 +607,9 @@ describe('Extension: Receive', () => {
|
||||
const payload = {data, cluster: 'genOnOff', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 20};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/power_plug', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/switch_group', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/power_plug', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/switch_group', stringify({state: 'ON'}), {retain: false, qos: 0});
|
||||
});
|
||||
|
||||
it('Should not handle messages from coordinator', async () => {
|
||||
@@ -621,7 +618,7 @@ describe('Extension: Receive', () => {
|
||||
const payload = {data, cluster: 'genOnOff', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('Should not handle messages from still interviewing devices with unknown definition', async () => {
|
||||
@@ -631,7 +628,7 @@ describe('Extension: Receive', () => {
|
||||
const payload = {data, cluster: 'genOnOff', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0);
|
||||
expect(mockLogger.debug).toHaveBeenCalledWith(`Skipping message, still interviewing`);
|
||||
});
|
||||
|
||||
@@ -649,29 +646,29 @@ describe('Extension: Receive', () => {
|
||||
};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/ikea_onoff');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({action: 'brightness_stop'});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/ikea_onoff');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({action: 'brightness_stop'});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should add elapsed', async () => {
|
||||
settings.set(['advanced', 'elapsed'], true);
|
||||
const device = devices.E1743;
|
||||
const payload = {data: {}, cluster: 'genLevelCtrl', device, endpoint: device.getEndpoint(1), type: 'commandStopWithOnOff'};
|
||||
jest.spyOn(Date, 'now').mockReturnValueOnce(150).mockReturnValueOnce(200);
|
||||
vi.spyOn(Date, 'now').mockReturnValueOnce(150).mockReturnValueOnce(200);
|
||||
await mockZHEvents.message({...payload, meta: {zclTransactionSequenceNumber: 2}});
|
||||
await flushPromises();
|
||||
await mockZHEvents.message({...payload, meta: {zclTransactionSequenceNumber: 3}});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/ikea_onoff');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({action: 'brightness_stop'});
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/ikea_onoff');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[1][1])).toMatchObject({action: 'brightness_stop'});
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[1][1]).elapsed).toBe(50);
|
||||
expect(mockMQTT.publishAsync.mock.calls[1][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/ikea_onoff');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({action: 'brightness_stop'});
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][2]).toStrictEqual({qos: 0, retain: false});
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/ikea_onoff');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1])).toMatchObject({action: 'brightness_stop'});
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[1][1]).elapsed).toBe(50);
|
||||
expect(mockMQTTPublishAsync.mock.calls[1][2]).toStrictEqual({qos: 0, retain: false});
|
||||
});
|
||||
|
||||
it('Should log when message is from supported device but has no converters', async () => {
|
||||
@@ -680,7 +677,7 @@ describe('Extension: Receive', () => {
|
||||
const payload = {data, cluster: 'genBinaryOutput', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 20};
|
||||
await mockZHEvents.message(payload);
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0);
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0);
|
||||
expect(mockLogger.debug).toHaveBeenCalledWith(
|
||||
"No converter available for 'ZNCZ02LM' with cluster 'genBinaryOutput' and type 'attributeReport' and data '{\"inactiveText\":\"hello\"}'",
|
||||
);
|
||||
@@ -702,11 +699,11 @@ describe('Extension: Receive', () => {
|
||||
meta: {zclTransactionSequenceNumber: 1},
|
||||
});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/SP600_NEW');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({energy: 0.66, power: 49.6});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/SP600_NEW');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({energy: 0.66, power: 49.6});
|
||||
|
||||
mockMQTT.publishAsync.mockClear();
|
||||
mockMQTTPublishAsync.mockClear();
|
||||
const SP600_OLD = devices.SP600_OLD;
|
||||
await mockZHEvents.message({
|
||||
data,
|
||||
@@ -718,9 +715,9 @@ describe('Extension: Receive', () => {
|
||||
meta: {zclTransactionSequenceNumber: 2},
|
||||
});
|
||||
await flushPromises();
|
||||
expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/SP600_OLD');
|
||||
expect(JSON.parse(mockMQTT.publishAsync.mock.calls[0][1])).toStrictEqual({energy: 6.65, power: 496});
|
||||
expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1);
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/SP600_OLD');
|
||||
expect(JSON.parse(mockMQTTPublishAsync.mock.calls[0][1])).toStrictEqual({energy: 6.65, power: 496});
|
||||
});
|
||||
|
||||
it('Should emit DevicesChanged event when a converter announces changed exposes', async () => {
|
||||
@@ -728,6 +725,6 @@ describe('Extension: Receive', () => {
|
||||
const data = {deviceMode: 0};
|
||||
const payload = {data, cluster: 'boschSpecific', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
|
||||
await mockZHEvents.message(payload);
|
||||
expect(mockMQTT.publishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bridge/devices');
|
||||
expect(mockMQTTPublishAsync.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bridge/devices');
|
||||
});
|
||||
});
|
||||
|
||||
+11
-9
@@ -1,5 +1,7 @@
|
||||
import * as data from './mocks/data';
|
||||
|
||||
import type {MockInstance} from 'vitest';
|
||||
|
||||
import fs from 'node:fs';
|
||||
import {platform} from 'node:os';
|
||||
import path from 'node:path';
|
||||
@@ -12,7 +14,7 @@ import logger from '../lib/util/logger';
|
||||
import * as settings from '../lib/util/settings';
|
||||
|
||||
describe('Logger', () => {
|
||||
let consoleWriteSpy: jest.SpyInstance;
|
||||
let consoleWriteSpy: MockInstance;
|
||||
const dir = tmp.dirSync();
|
||||
|
||||
const getCachedNamespacedLevels = (): Record<string, string> => {
|
||||
@@ -22,7 +24,7 @@ describe('Logger', () => {
|
||||
|
||||
beforeAll(() => {
|
||||
// @ts-expect-error private
|
||||
consoleWriteSpy = jest.spyOn(console._stdout, 'write').mockImplementation(() => {});
|
||||
consoleWriteSpy = vi.spyOn(console._stdout, 'write').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
@@ -174,9 +176,9 @@ describe('Logger', () => {
|
||||
['warning', {higher: ['error'], lower: ['debug', 'info']}],
|
||||
['error', {higher: [], lower: ['debug', 'info', 'warning']}],
|
||||
])('Logs relevant levels for %s', (level, otherLevels) => {
|
||||
logger.setLevel(level);
|
||||
logger.setLevel(level as settings.LogLevel);
|
||||
|
||||
const logSpy = jest.spyOn(logger.winston, 'log');
|
||||
const logSpy = vi.spyOn(logger.winston, 'log');
|
||||
consoleWriteSpy.mockClear();
|
||||
let i = 1;
|
||||
|
||||
@@ -262,7 +264,7 @@ describe('Logger', () => {
|
||||
],
|
||||
])('Sets namespace ignore for debug level %s', (ignore, expected, tests) => {
|
||||
logger.setLevel('debug');
|
||||
const logSpy = jest.spyOn(logger.winston, 'log');
|
||||
const logSpy = vi.spyOn(logger.winston, 'log');
|
||||
logger.setDebugNamespaceIgnore(ignore);
|
||||
// @ts-expect-error private
|
||||
expect(logger.debugNamespaceIgnoreRegex).toStrictEqual(expected);
|
||||
@@ -290,7 +292,7 @@ describe('Logger', () => {
|
||||
expect(logger.getNamespacedLevels()).toStrictEqual({'z2m:mqtt': 'warning'});
|
||||
expect(logger.getLevel()).toStrictEqual('debug');
|
||||
|
||||
const logSpy = jest.spyOn(logger.winston, 'log');
|
||||
const logSpy = vi.spyOn(logger.winston, 'log');
|
||||
|
||||
consoleWriteSpy.mockClear();
|
||||
logger.info(`MQTT publish: topic 'abcd/efgh', payload '{"my": {"payload": "injson"}}'`, 'z2m:mqtt');
|
||||
@@ -311,7 +313,7 @@ describe('Logger', () => {
|
||||
expect(logger.getNamespacedLevels()).toStrictEqual({'z2m:mqtt': 'info'});
|
||||
expect(logger.getLevel()).toStrictEqual('warning');
|
||||
|
||||
const logSpy = jest.spyOn(logger.winston, 'log');
|
||||
const logSpy = vi.spyOn(logger.winston, 'log');
|
||||
|
||||
consoleWriteSpy.mockClear();
|
||||
logger.info(`MQTT publish: topic 'abcd/efgh', payload '{"my": {"payload": "injson"}}'`, 'z2m:mqtt');
|
||||
@@ -329,7 +331,7 @@ describe('Logger', () => {
|
||||
});
|
||||
|
||||
it('Logs with namespaced levels hierarchy', () => {
|
||||
const nsLevels = {'zh:zstack': 'debug', 'zh:zstack:unpi:writer': 'error'};
|
||||
const nsLevels = {'zh:zstack': 'debug' as const, 'zh:zstack:unpi:writer': 'error' as const};
|
||||
let cachedNSLevels;
|
||||
cachedNSLevels = Object.assign({}, nsLevels);
|
||||
logger.setNamespacedLevels(nsLevels);
|
||||
@@ -383,7 +385,7 @@ describe('Logger', () => {
|
||||
it('Ignores SPLAT chars', () => {
|
||||
logger.setLevel('debug');
|
||||
|
||||
const logSpy = jest.spyOn(logger.winston, 'log');
|
||||
const logSpy = vi.spyOn(logger.winston, 'log');
|
||||
consoleWriteSpy.mockClear();
|
||||
|
||||
let splatChars = '%d';
|
||||
|
||||
+10
-4
@@ -290,10 +290,16 @@ export function read(): ReturnType<typeof yaml.read> {
|
||||
return yaml.read(path.join(mockDir, 'configuration.yaml'));
|
||||
}
|
||||
|
||||
jest.mock('../../lib/util/data', () => ({
|
||||
joinPath: (file: string): string => jest.requireActual('node:path').join(mockDir, file),
|
||||
getPath: (): string => mockDir,
|
||||
}));
|
||||
vi.mock('../../lib/util/data', async () => {
|
||||
const nodePath = await vi.importActual<typeof import('node:path')>('node:path');
|
||||
|
||||
return {
|
||||
default: {
|
||||
joinPath: (file: string): string => nodePath.join(mockDir, file),
|
||||
getPath: (): string => mockDir,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
writeDefaultConfiguration();
|
||||
writeDefaultState();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export const mockDebounce = jest.fn((fn) => fn);
|
||||
export const mockDebounce = vi.fn((fn) => fn);
|
||||
|
||||
jest.mock('debounce', () => mockDebounce);
|
||||
vi.mock('debounce', () => ({
|
||||
default: mockDebounce,
|
||||
}));
|
||||
|
||||
+5
-5
@@ -1,11 +1,11 @@
|
||||
export const mockJSZipFile = jest.fn();
|
||||
export const mockJSZipGenerateAsync = jest.fn().mockReturnValue('THISISBASE64');
|
||||
export const mockJSZipFile = vi.fn();
|
||||
export const mockJSZipGenerateAsync = vi.fn().mockReturnValue('THISISBASE64');
|
||||
|
||||
jest.mock('jszip', () =>
|
||||
jest.fn().mockImplementation(() => {
|
||||
vi.mock('jszip', () => ({
|
||||
default: vi.fn().mockImplementation(() => {
|
||||
return {
|
||||
file: mockJSZipFile,
|
||||
generateAsync: mockJSZipGenerateAsync,
|
||||
};
|
||||
}),
|
||||
);
|
||||
}));
|
||||
|
||||
+13
-11
@@ -1,7 +1,7 @@
|
||||
import type {LogLevel} from 'lib/util/settings';
|
||||
import type Transport from 'winston-transport';
|
||||
|
||||
let level = 'info';
|
||||
let level: LogLevel = 'info';
|
||||
let debugNamespaceIgnore: string = '';
|
||||
let namespacedLevels: Record<string, LogLevel> = {};
|
||||
let transports: Transport[] = [];
|
||||
@@ -9,20 +9,20 @@ let transportsEnabled: boolean = false;
|
||||
const getMessage = (messageOrLambda: string | (() => string)): string => (messageOrLambda instanceof Function ? messageOrLambda() : messageOrLambda);
|
||||
|
||||
export const mockLogger = {
|
||||
log: jest.fn().mockImplementation((level, message, namespace = 'z2m') => {
|
||||
log: vi.fn().mockImplementation((level, message, namespace = 'z2m') => {
|
||||
if (transportsEnabled) {
|
||||
for (const transport of transports) {
|
||||
transport.log!({level, message, namespace}, () => {});
|
||||
}
|
||||
}
|
||||
}),
|
||||
init: jest.fn(),
|
||||
info: jest.fn().mockImplementation((messageOrLambda, namespace = 'z2m') => mockLogger.log('info', getMessage(messageOrLambda), namespace)),
|
||||
warning: jest.fn().mockImplementation((messageOrLambda, namespace = 'z2m') => mockLogger.log('warning', getMessage(messageOrLambda), namespace)),
|
||||
error: jest.fn().mockImplementation((messageOrLambda, namespace = 'z2m') => mockLogger.log('error', getMessage(messageOrLambda), namespace)),
|
||||
debug: jest.fn().mockImplementation((messageOrLambda, namespace = 'z2m') => mockLogger.log('debug', getMessage(messageOrLambda), namespace)),
|
||||
cleanup: jest.fn(),
|
||||
logOutput: jest.fn(),
|
||||
init: vi.fn(),
|
||||
info: vi.fn().mockImplementation((messageOrLambda, namespace = 'z2m') => mockLogger.log('info', getMessage(messageOrLambda), namespace)),
|
||||
warning: vi.fn().mockImplementation((messageOrLambda, namespace = 'z2m') => mockLogger.log('warning', getMessage(messageOrLambda), namespace)),
|
||||
error: vi.fn().mockImplementation((messageOrLambda, namespace = 'z2m') => mockLogger.log('error', getMessage(messageOrLambda), namespace)),
|
||||
debug: vi.fn().mockImplementation((messageOrLambda, namespace = 'z2m') => mockLogger.log('debug', getMessage(messageOrLambda), namespace)),
|
||||
cleanup: vi.fn(),
|
||||
logOutput: vi.fn(),
|
||||
add: (transport: Transport): void => {
|
||||
transports.push(transport);
|
||||
},
|
||||
@@ -47,7 +47,9 @@ export const mockLogger = {
|
||||
setTransportsEnabled: (value: boolean): void => {
|
||||
transportsEnabled = value;
|
||||
},
|
||||
end: jest.fn(),
|
||||
end: vi.fn(),
|
||||
};
|
||||
|
||||
jest.mock('../../lib/util/logger', () => mockLogger);
|
||||
vi.mock('../../lib/util/logger', () => ({
|
||||
default: mockLogger,
|
||||
}));
|
||||
|
||||
+19
-15
@@ -4,28 +4,32 @@ import {EventHandler} from './utils';
|
||||
|
||||
export const events: Record<string, EventHandler> = {};
|
||||
|
||||
export const mockMQTT = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
publishAsync: jest.fn(async (topic: string, message: string, opts?: IClientPublishOptions): Promise<void> => {}),
|
||||
endAsync: jest.fn(async (): Promise<void> => {}),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
subscribeAsync: jest.fn(async (topicObject: string): Promise<void> => {}),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
unsubscribeAsync: jest.fn(async (topic: string): Promise<void> => {}),
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const mockMQTTPublishAsync = vi.fn(async (topic: string, message: string, opts?: IClientPublishOptions): Promise<void> => {});
|
||||
export const mockMQTTEndAsync = vi.fn(async (): Promise<void> => {});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const mockMQTTSubscribeAsync = vi.fn(async (topicObject: string): Promise<void> => {});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const mockMQTTUnsubscribeAsync = vi.fn(async (topic: string): Promise<void> => {});
|
||||
|
||||
export const mockMQTTConnectAsync = vi.fn(() => ({
|
||||
reconnecting: false,
|
||||
disconnecting: false,
|
||||
disconnected: false,
|
||||
on: jest.fn((type, handler) => {
|
||||
publishAsync: mockMQTTPublishAsync,
|
||||
endAsync: mockMQTTEndAsync,
|
||||
subscribeAsync: mockMQTTSubscribeAsync,
|
||||
unsubscribeAsync: mockMQTTUnsubscribeAsync,
|
||||
on: vi.fn((type, handler) => {
|
||||
if (type === 'connect') {
|
||||
handler();
|
||||
}
|
||||
|
||||
events[type] = handler;
|
||||
}),
|
||||
stream: {setMaxListeners: jest.fn()},
|
||||
};
|
||||
export const mockMQTTConnectAsync = jest.fn(() => mockMQTT);
|
||||
stream: {setMaxListeners: vi.fn()},
|
||||
}));
|
||||
|
||||
jest.mock('mqtt', () => {
|
||||
return {connectAsync: mockMQTTConnectAsync};
|
||||
});
|
||||
vi.mock('mqtt', () => ({
|
||||
connectAsync: mockMQTTConnectAsync,
|
||||
}));
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
import utils from '../../lib/util/utils';
|
||||
|
||||
const spy = jest.spyOn(utils, 'sleep');
|
||||
const spy = vi.spyOn(utils, 'sleep');
|
||||
|
||||
export function mock(): void {
|
||||
spy.mockImplementation();
|
||||
spy.mockImplementation(vi.fn());
|
||||
}
|
||||
|
||||
export function restore(): void {
|
||||
|
||||
+3
-4
@@ -1,10 +1,9 @@
|
||||
export type EventHandler = (...args: unknown[]) => unknown;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type JestMockAny = jest.Mock<any, any, any>;
|
||||
export async function flushPromises(): Promise<void> {
|
||||
const nodeTimers = await vi.importActual<typeof import('node:timers')>('node:timers');
|
||||
|
||||
export function flushPromises(): Promise<void> {
|
||||
return new Promise(jest.requireActual('node:timers').setImmediate);
|
||||
return await new Promise(nodeTimers.setImmediate);
|
||||
}
|
||||
|
||||
// https://github.com/jestjs/jest/issues/6028#issuecomment-567669082
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import type {Mock} from 'vitest';
|
||||
|
||||
import assert from 'node:assert';
|
||||
|
||||
import {Zcl} from 'zigbee-herdsman';
|
||||
import {CoordinatorVersion, DeviceType, NetworkParameters, StartResult} from 'zigbee-herdsman/dist/adapter/tstype';
|
||||
|
||||
import {EventHandler, JestMockAny} from './utils';
|
||||
import {EventHandler} from './utils';
|
||||
|
||||
type ZHConfiguredReporting = {
|
||||
cluster: {name: string};
|
||||
@@ -94,22 +96,22 @@ export class Endpoint {
|
||||
ID: number;
|
||||
inputClusters: number[];
|
||||
outputClusters: number[];
|
||||
command: JestMockAny;
|
||||
commandResponse: JestMockAny;
|
||||
read: JestMockAny;
|
||||
write: JestMockAny;
|
||||
bind: JestMockAny;
|
||||
unbind: JestMockAny;
|
||||
save: JestMockAny;
|
||||
configureReporting: JestMockAny;
|
||||
command: Mock;
|
||||
commandResponse: Mock;
|
||||
read: Mock;
|
||||
write: Mock;
|
||||
bind: Mock;
|
||||
unbind: Mock;
|
||||
save: Mock;
|
||||
configureReporting: Mock;
|
||||
meta: Record<string, unknown>;
|
||||
binds: ZHBind[];
|
||||
profileID: number | undefined;
|
||||
deviceID: number | undefined;
|
||||
configuredReportings: ZHConfiguredReporting[];
|
||||
addToGroup: JestMockAny;
|
||||
removeFromGroup: JestMockAny;
|
||||
getClusterAttributeValue: JestMockAny;
|
||||
addToGroup: Mock;
|
||||
removeFromGroup: Mock;
|
||||
getClusterAttributeValue: Mock;
|
||||
|
||||
constructor(
|
||||
ID: number,
|
||||
@@ -128,33 +130,33 @@ export class Endpoint {
|
||||
this.ID = ID;
|
||||
this.inputClusters = inputClusters;
|
||||
this.outputClusters = outputClusters;
|
||||
this.command = jest.fn();
|
||||
this.commandResponse = jest.fn();
|
||||
this.read = jest.fn();
|
||||
this.write = jest.fn();
|
||||
this.bind = jest.fn();
|
||||
this.unbind = jest.fn();
|
||||
this.save = jest.fn();
|
||||
this.configureReporting = jest.fn();
|
||||
this.command = vi.fn();
|
||||
this.commandResponse = vi.fn();
|
||||
this.read = vi.fn();
|
||||
this.write = vi.fn();
|
||||
this.bind = vi.fn();
|
||||
this.unbind = vi.fn();
|
||||
this.save = vi.fn();
|
||||
this.configureReporting = vi.fn();
|
||||
this.meta = meta;
|
||||
this.binds = binds;
|
||||
this.profileID = profileID;
|
||||
this.deviceID = deviceID;
|
||||
this.configuredReportings = configuredReportings;
|
||||
|
||||
this.addToGroup = jest.fn((group: Group) => {
|
||||
this.addToGroup = vi.fn((group: Group) => {
|
||||
if (!group.members.includes(this)) {
|
||||
group.members.push(this);
|
||||
}
|
||||
});
|
||||
this.removeFromGroup = jest.fn((group: Group) => {
|
||||
this.removeFromGroup = vi.fn((group: Group) => {
|
||||
const index = group.members.indexOf(this);
|
||||
if (index != -1) {
|
||||
group.members.splice(index, 1);
|
||||
}
|
||||
});
|
||||
|
||||
this.getClusterAttributeValue = jest.fn((cluster: string, value: string) =>
|
||||
this.getClusterAttributeValue = vi.fn((cluster: string, value: string) =>
|
||||
!(cluster in this.clusterValues) ? undefined : this.clusterValues[cluster][value],
|
||||
);
|
||||
}
|
||||
@@ -231,21 +233,21 @@ export class Device {
|
||||
softwareBuildID: string | undefined;
|
||||
interviewCompleted: boolean;
|
||||
modelID: string | undefined;
|
||||
interview: JestMockAny;
|
||||
interview: Mock;
|
||||
interviewing: boolean;
|
||||
meta: Record<string, unknown>;
|
||||
ping: JestMockAny;
|
||||
removeFromNetwork: JestMockAny;
|
||||
removeFromDatabase: JestMockAny;
|
||||
ping: Mock;
|
||||
removeFromNetwork: Mock;
|
||||
removeFromDatabase: Mock;
|
||||
customClusters: Record<string, unknown>;
|
||||
addCustomCluster: JestMockAny;
|
||||
save: JestMockAny;
|
||||
addCustomCluster: Mock;
|
||||
save: Mock;
|
||||
manufacturerName: string | undefined;
|
||||
lastSeen: number | undefined;
|
||||
isDeleted: boolean;
|
||||
linkquality?: number;
|
||||
lqi: JestMockAny;
|
||||
routingTable: JestMockAny;
|
||||
lqi: Mock;
|
||||
routingTable: Mock;
|
||||
|
||||
constructor(
|
||||
type: string,
|
||||
@@ -272,20 +274,20 @@ export class Device {
|
||||
this.softwareBuildID = softwareBuildID;
|
||||
this.interviewCompleted = interviewCompleted;
|
||||
this.modelID = modelID;
|
||||
this.interview = jest.fn();
|
||||
this.interview = vi.fn();
|
||||
this.interviewing = interviewing;
|
||||
this.meta = {};
|
||||
this.ping = jest.fn();
|
||||
this.removeFromNetwork = jest.fn();
|
||||
this.removeFromDatabase = jest.fn();
|
||||
this.ping = vi.fn();
|
||||
this.removeFromNetwork = vi.fn();
|
||||
this.removeFromDatabase = vi.fn();
|
||||
this.customClusters = customClusters;
|
||||
this.addCustomCluster = jest.fn();
|
||||
this.save = jest.fn();
|
||||
this.addCustomCluster = vi.fn();
|
||||
this.save = vi.fn();
|
||||
this.manufacturerName = manufacturerName;
|
||||
this.lastSeen = 1000;
|
||||
this.isDeleted = false;
|
||||
this.lqi = jest.fn(() => ({neighbors: []}));
|
||||
this.routingTable = jest.fn(() => ({table: []}));
|
||||
this.lqi = vi.fn(() => ({neighbors: []}));
|
||||
this.routingTable = vi.fn(() => ({table: []}));
|
||||
}
|
||||
|
||||
getEndpoint(ID: number): Endpoint | undefined {
|
||||
@@ -309,19 +311,19 @@ export class Device {
|
||||
|
||||
export class Group {
|
||||
groupID: number;
|
||||
command: JestMockAny;
|
||||
command: Mock;
|
||||
meta: Record<string, unknown>;
|
||||
members: Endpoint[];
|
||||
removeFromDatabase: JestMockAny;
|
||||
removeFromNetwork: JestMockAny;
|
||||
removeFromDatabase: Mock;
|
||||
removeFromNetwork: Mock;
|
||||
|
||||
constructor(groupID: number, members: Endpoint[]) {
|
||||
this.groupID = groupID;
|
||||
this.command = jest.fn();
|
||||
this.command = vi.fn();
|
||||
this.meta = {};
|
||||
this.members = members;
|
||||
this.removeFromDatabase = jest.fn();
|
||||
this.removeFromNetwork = jest.fn();
|
||||
this.removeFromDatabase = vi.fn();
|
||||
this.removeFromNetwork = vi.fn();
|
||||
}
|
||||
|
||||
hasMember(endpoint: Endpoint): boolean {
|
||||
@@ -1121,25 +1123,23 @@ export const mockController = {
|
||||
on: (type: string, handler: EventHandler): void => {
|
||||
events[type] = handler;
|
||||
},
|
||||
start: jest.fn((): Promise<StartResult> => Promise.resolve('reset')),
|
||||
stop: jest.fn(),
|
||||
touchlinkIdentify: jest.fn(),
|
||||
touchlinkScan: jest.fn(),
|
||||
touchlinkFactoryReset: jest.fn(),
|
||||
touchlinkFactoryResetFirst: jest.fn(),
|
||||
addInstallCode: jest.fn(),
|
||||
permitJoin: jest.fn(),
|
||||
getPermitJoin: jest.fn((): boolean => false),
|
||||
getPermitJoinEnd: jest.fn((): number | undefined => undefined),
|
||||
isStopping: jest.fn((): boolean => false),
|
||||
backup: jest.fn(),
|
||||
coordinatorCheck: jest.fn(),
|
||||
getCoordinatorVersion: jest.fn((): Promise<CoordinatorVersion> => Promise.resolve({type: 'z-Stack', meta: {version: 1, revision: 20190425}})),
|
||||
getNetworkParameters: jest.fn(
|
||||
(): Promise<NetworkParameters> => Promise.resolve({panID: 0x162a, extendedPanID: '0x64c5fd698daf0c00', channel: 15}),
|
||||
),
|
||||
getDevices: jest.fn((): Device[] => []),
|
||||
getDevicesIterator: jest.fn(function* (predicate?: (value: Device) => boolean): Generator<Device> {
|
||||
start: vi.fn((): Promise<StartResult> => Promise.resolve('reset')),
|
||||
stop: vi.fn(),
|
||||
touchlinkIdentify: vi.fn(),
|
||||
touchlinkScan: vi.fn(),
|
||||
touchlinkFactoryReset: vi.fn(),
|
||||
touchlinkFactoryResetFirst: vi.fn(),
|
||||
addInstallCode: vi.fn(),
|
||||
permitJoin: vi.fn(),
|
||||
getPermitJoin: vi.fn((): boolean => false),
|
||||
getPermitJoinEnd: vi.fn((): number | undefined => undefined),
|
||||
isStopping: vi.fn((): boolean => false),
|
||||
backup: vi.fn(),
|
||||
coordinatorCheck: vi.fn(),
|
||||
getCoordinatorVersion: vi.fn((): Promise<CoordinatorVersion> => Promise.resolve({type: 'z-Stack', meta: {version: 1, revision: 20190425}})),
|
||||
getNetworkParameters: vi.fn((): Promise<NetworkParameters> => Promise.resolve({panID: 0x162a, extendedPanID: '0x64c5fd698daf0c00', channel: 15})),
|
||||
getDevices: vi.fn((): Device[] => []),
|
||||
getDevicesIterator: vi.fn(function* (predicate?: (value: Device) => boolean): Generator<Device> {
|
||||
for (const key in devices) {
|
||||
const device = devices[key as keyof typeof devices];
|
||||
|
||||
@@ -1148,24 +1148,24 @@ export const mockController = {
|
||||
}
|
||||
}
|
||||
}),
|
||||
getDevicesByType: jest.fn((type: DeviceType): Device[] =>
|
||||
getDevicesByType: vi.fn((type: DeviceType): Device[] =>
|
||||
Object.values(devices)
|
||||
.filter((d) => returnDevices.length === 0 || returnDevices.includes(d.ieeeAddr))
|
||||
.filter((d) => d.type === type),
|
||||
),
|
||||
getDeviceByIeeeAddr: jest.fn((ieeeAddr: string): Device | undefined =>
|
||||
getDeviceByIeeeAddr: vi.fn((ieeeAddr: string): Device | undefined =>
|
||||
Object.values(devices)
|
||||
.filter((d) => returnDevices.length === 0 || returnDevices.includes(d.ieeeAddr))
|
||||
.find((d) => d.ieeeAddr === ieeeAddr),
|
||||
),
|
||||
getDeviceByNetworkAddress: jest.fn((networkAddress: number): Device | undefined =>
|
||||
getDeviceByNetworkAddress: vi.fn((networkAddress: number): Device | undefined =>
|
||||
Object.values(devices)
|
||||
.filter((d) => returnDevices.length === 0 || returnDevices.includes(d.ieeeAddr))
|
||||
.find((d) => d.networkAddress === networkAddress),
|
||||
),
|
||||
getGroupByID: jest.fn((groupID: number): Group | undefined => Object.values(groups).find((g) => g.groupID === groupID)),
|
||||
getGroups: jest.fn((): Group[] => []),
|
||||
getGroupsIterator: jest.fn(function* (predicate?: (value: Group) => boolean): Generator<Group> {
|
||||
getGroupByID: vi.fn((groupID: number): Group | undefined => Object.values(groups).find((g) => g.groupID === groupID)),
|
||||
getGroups: vi.fn((): Group[] => []),
|
||||
getGroupsIterator: vi.fn(function* (predicate?: (value: Group) => boolean): Generator<Group> {
|
||||
for (const key in groups) {
|
||||
const group = groups[key as keyof typeof groups];
|
||||
|
||||
@@ -1174,14 +1174,14 @@ export const mockController = {
|
||||
}
|
||||
}
|
||||
}),
|
||||
createGroup: jest.fn((groupID: number): Group => {
|
||||
createGroup: vi.fn((groupID: number): Group => {
|
||||
const group = new Group(groupID, []);
|
||||
groups[`group_${groupID}` as keyof typeof groups] = group;
|
||||
return group;
|
||||
}),
|
||||
};
|
||||
|
||||
jest.mock('zigbee-herdsman', () => ({
|
||||
...jest.requireActual('zigbee-herdsman'),
|
||||
Controller: jest.fn().mockImplementation(() => mockController),
|
||||
vi.mock('zigbee-herdsman', async (importOriginal) => ({
|
||||
...(await importOriginal()),
|
||||
Controller: vi.fn().mockImplementation(() => mockController),
|
||||
}));
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"extends": "../tsconfig",
|
||||
"include": ["./**/*"],
|
||||
"include": ["./**/*", "vitest.config.mts"],
|
||||
"compilerOptions": {
|
||||
"types": ["jest"],
|
||||
"types": ["vitest/globals"],
|
||||
"rootDir": "..",
|
||||
"noEmit": true
|
||||
},
|
||||
|
||||
+8
-6
@@ -3,6 +3,12 @@ import path from 'node:path';
|
||||
|
||||
import utils from '../lib/util/utils';
|
||||
|
||||
const mockGetLastCommit = vi.fn<() => [boolean, {shortHash: string} | null]>(() => [false, {shortHash: '123'}]);
|
||||
|
||||
vi.mock('git-last-commit', () => ({
|
||||
getLastCommit: vi.fn((cb) => cb(...mockGetLastCommit())),
|
||||
}));
|
||||
|
||||
describe('Utils', () => {
|
||||
it('Object is empty', () => {
|
||||
expect(utils.objectIsEmpty({})).toBeTruthy();
|
||||
@@ -16,14 +22,10 @@ describe('Utils', () => {
|
||||
|
||||
it('git last commit', async () => {
|
||||
const version = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')).version;
|
||||
let mockReturnValue: [identical: boolean, result: {shortHash: string} | null] = [false, {shortHash: '123'}];
|
||||
jest.mock('git-last-commit', () => ({
|
||||
getLastCommit: (cb: (identical: boolean, result: {shortHash: string} | null) => void): void => cb(mockReturnValue[0], mockReturnValue[1]),
|
||||
}));
|
||||
|
||||
expect(await utils.getZigbee2MQTTVersion()).toStrictEqual({commitHash: '123', version: version});
|
||||
|
||||
mockReturnValue = [true, null];
|
||||
mockGetLastCommit.mockReturnValueOnce([true, null]);
|
||||
expect(await utils.getZigbee2MQTTVersion()).toStrictEqual({commitHash: expect.any(String), version: version});
|
||||
});
|
||||
|
||||
@@ -40,7 +42,7 @@ describe('Utils', () => {
|
||||
|
||||
it('To local iso string', async () => {
|
||||
const date = new Date('August 19, 1975 23:15:30 UTC+00:00').getTime();
|
||||
const getTzOffsetSpy = jest.spyOn(Date.prototype, 'getTimezoneOffset');
|
||||
const getTzOffsetSpy = vi.spyOn(Date.prototype, 'getTimezoneOffset');
|
||||
getTzOffsetSpy.mockReturnValueOnce(60);
|
||||
expect(utils.formatDate(date, 'ISO_8601_local').toString().endsWith('-01:00')).toBeTruthy();
|
||||
getTzOffsetSpy.mockReturnValueOnce(-60);
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import {defineConfig} from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
onConsoleLog(log: string, type: 'stdout' | 'stderr'): boolean | void {
|
||||
return false;
|
||||
},
|
||||
coverage: {
|
||||
enabled: false,
|
||||
provider: 'v8',
|
||||
include: ['lib/**'],
|
||||
extension: ['.ts'],
|
||||
// exclude: [],
|
||||
clean: true,
|
||||
cleanOnRerun: true,
|
||||
reportsDirectory: 'coverage',
|
||||
reporter: ['text', 'html'],
|
||||
reportOnFailure: false,
|
||||
thresholds: {
|
||||
100: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user