@ray-js/lamp-schedule-core 1.0.1-beta-10 → 1.0.2-beta-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/conflict/ConflictResolver.js +48 -21
- package/lib/conflict/__test__/ConflictResolver.test.js +49 -0
- package/lib/conflict/__test__/scheduleDataManager.test.js +66 -0
- package/lib/conflict/__test__/transform.test.js +5 -1
- package/lib/conflict/index.js +27 -7
- package/lib/conflict/scheduleDataManager.js +28 -33
- package/lib/context/rhythms/actions.d.ts +13 -17
- package/lib/context/rhythms/reducer.d.ts +2 -23
- package/lib/context/rhythms/reducer.js +7 -8
- package/lib/context/schedule/reducer.d.ts +5 -2
- package/lib/dpParser/__test__/autoDispatch.test.d.ts +1 -0
- package/lib/dpParser/__test__/autoDispatch.test.js +45 -0
- package/lib/dpParser/__test__/rhythms.test.js +90 -14
- package/lib/dpParser/__test__/rtcTimer.test.js +35 -0
- package/lib/dpParser/__test__/sleep.test.js +17 -0
- package/lib/dpParser/__test__/wakeup.test.js +17 -4
- package/lib/dpParser/__test__/wakeupSigmesh.test.d.ts +1 -0
- package/lib/dpParser/__test__/wakeupSigmesh.test.js +126 -0
- package/lib/hooks/__test__/useBaseLightDp.test.d.ts +1 -0
- package/lib/hooks/__test__/useBaseLightDp.test.js +251 -0
- package/lib/hooks/__test__/useCommonSupport.test.d.ts +1 -0
- package/lib/hooks/__test__/useCommonSupport.test.js +204 -0
- package/lib/hooks/__test__/useTimerFlushList.test.d.ts +1 -0
- package/lib/hooks/__test__/useTimerFlushList.test.js +117 -0
- package/lib/hooks/__test__/useTimerOperate.test.js +239 -5
- package/lib/hooks/__test__/useTimerOperateLocal.test.js +161 -53
- package/lib/types/rhythms.d.ts +25 -13
- package/lib/types/ty.d.ts +3 -0
- package/lib/utils/ScheduleDataSync.js +4 -1
- package/lib/utils/ScheduleLogger.js +1 -1
- package/lib/utils/__test__/ScheduleDataSync.test.d.ts +11 -0
- package/lib/utils/__test__/ScheduleDataSync.test.js +137 -0
- package/lib/utils/__test__/ScheduleEmit.test.d.ts +1 -0
- package/lib/utils/__test__/ScheduleEmit.test.js +35 -0
- package/lib/utils/__test__/ScheduleSupport.test.js +288 -154
- package/package.json +1 -1
|
@@ -121,6 +121,26 @@ describe('RtcTimerFormatter', () => {
|
|
|
121
121
|
}
|
|
122
122
|
});
|
|
123
123
|
});
|
|
124
|
+
it('should parse a colour light timer with dual white params (dataType=09)', () => {
|
|
125
|
+
// ID: 85 (status=true, timerId=5)
|
|
126
|
+
// loops: 02 (Mon), time: 0064 (100 mins -> 01:40)
|
|
127
|
+
// deviceAction: 09 (dual white)
|
|
128
|
+
// bright_value: 0x46 (70), temp_value: 0x1E (30)
|
|
129
|
+
// bright_value1: 0x3C (60), temp_value1: 0x14 (20)
|
|
130
|
+
|
|
131
|
+
expect(formatterInstance.parser('85010200006409461E3C14')).toEqual({
|
|
132
|
+
timerId: '5',
|
|
133
|
+
status: true,
|
|
134
|
+
loops: '0100000',
|
|
135
|
+
time: '01:40',
|
|
136
|
+
dps: {
|
|
137
|
+
bright_value: 70,
|
|
138
|
+
temp_value: 30,
|
|
139
|
+
bright_value1: 60,
|
|
140
|
+
temp_value1: 20
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
});
|
|
124
144
|
});
|
|
125
145
|
describe('formatter', () => {
|
|
126
146
|
it('should format a power (switch_led) timer to DP string', () => {
|
|
@@ -200,6 +220,21 @@ describe('RtcTimerFormatter', () => {
|
|
|
200
220
|
}
|
|
201
221
|
})).toBe('8401020000640400f0501e4b28');
|
|
202
222
|
});
|
|
223
|
+
it('should format a timer with dual white params (dataType=09)', () => {
|
|
224
|
+
const result = formatterInstance.formatter({
|
|
225
|
+
timerId: '5',
|
|
226
|
+
status: true,
|
|
227
|
+
loops: '0100000',
|
|
228
|
+
time: '01:40',
|
|
229
|
+
dps: {
|
|
230
|
+
bright_value: 70,
|
|
231
|
+
temp_value: 30,
|
|
232
|
+
bright_value1: 60,
|
|
233
|
+
temp_value1: 20
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
expect(result.toLowerCase()).toBe('85010200006409461e3c14');
|
|
237
|
+
});
|
|
203
238
|
it('should format a timer to be deleted (status=false, no dps, specific id)', () => {
|
|
204
239
|
// The formatter is designed to create full timer strings.
|
|
205
240
|
// To delete a timer, one typically sends a short command with just ID and status=false.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SleepParser } from '../sleep';
|
|
2
|
+
import { sleepParser as sleepParserCommon } from '../sleep/sleepCommon';
|
|
2
3
|
describe('SleepParser', () => {
|
|
3
4
|
const parser = new SleepParser();
|
|
4
5
|
it('should return default value when parsing empty string', () => {
|
|
@@ -68,4 +69,20 @@ describe('SleepParser', () => {
|
|
|
68
69
|
'0120050e500028640a0000');
|
|
69
70
|
expect(result.nodes.length).toBe(4);
|
|
70
71
|
});
|
|
72
|
+
it('should delegate parser and formatter to sleepParserCommon implementation', () => {
|
|
73
|
+
const parserSpy = jest.spyOn(sleepParserCommon, 'parser');
|
|
74
|
+
const formatterSpy = jest.spyOn(sleepParserCommon, 'formatter');
|
|
75
|
+
const input = '0000';
|
|
76
|
+
parser.parser(input);
|
|
77
|
+
expect(parserSpy).toHaveBeenCalledWith(input);
|
|
78
|
+
const data = {
|
|
79
|
+
version: 0,
|
|
80
|
+
length: 0,
|
|
81
|
+
nodes: []
|
|
82
|
+
};
|
|
83
|
+
parser.formatter(data);
|
|
84
|
+
expect(formatterSpy).toHaveBeenCalledWith(data);
|
|
85
|
+
parserSpy.mockRestore();
|
|
86
|
+
formatterSpy.mockRestore();
|
|
87
|
+
});
|
|
71
88
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { WakeUpParser } from '../wakeup';
|
|
2
|
+
import { wakeupParser as wakeupParserCommon } from '../wakeup/wakeupCommon';
|
|
2
3
|
describe('WakeUpParser', () => {
|
|
3
4
|
const parser = new WakeUpParser();
|
|
4
5
|
it('should return default value when parsing empty string', () => {
|
|
@@ -62,8 +63,20 @@ describe('WakeUpParser', () => {
|
|
|
62
63
|
'0120050e500000640a000005');
|
|
63
64
|
expect(result.nodes.length).toBe(4);
|
|
64
65
|
});
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
it('should delegate parser and formatter to wakeupParserCommon implementation', () => {
|
|
67
|
+
const parserSpy = jest.spyOn(wakeupParserCommon, 'parser');
|
|
68
|
+
const formatterSpy = jest.spyOn(wakeupParserCommon, 'formatter');
|
|
69
|
+
const input = '0000';
|
|
70
|
+
parser.parser(input);
|
|
71
|
+
expect(parserSpy).toHaveBeenCalledWith(input);
|
|
72
|
+
const data = {
|
|
73
|
+
version: 0,
|
|
74
|
+
number: 0,
|
|
75
|
+
nodes: []
|
|
76
|
+
};
|
|
77
|
+
parser.formatter(data);
|
|
78
|
+
expect(formatterSpy).toHaveBeenCalledWith(data);
|
|
79
|
+
parserSpy.mockRestore();
|
|
80
|
+
formatterSpy.mockRestore();
|
|
81
|
+
});
|
|
69
82
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import "core-js/modules/esnext.iterator.map.js";
|
|
2
|
+
jest.mock('@ray-js/panel-sdk/lib/utils', () => ({
|
|
3
|
+
generateDpStrStep: jest.fn(str => {
|
|
4
|
+
let index = 0;
|
|
5
|
+
return len => {
|
|
6
|
+
const segment = str.slice(index, index + len);
|
|
7
|
+
index += len;
|
|
8
|
+
return {
|
|
9
|
+
value: parseInt(segment, 16)
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
}),
|
|
13
|
+
numToHexString: jest.fn(num => num.toString(16).padStart(2, '0'))
|
|
14
|
+
}));
|
|
15
|
+
import { wakeupParserSigmesh, WakeUpSigmesh } from '../wakeup/wakeupSigmesh';
|
|
16
|
+
const loopsToHex = loops => {
|
|
17
|
+
const binary = loops.split('').reverse().join('');
|
|
18
|
+
return parseInt(binary, 2);
|
|
19
|
+
};
|
|
20
|
+
const buildDpStr = function (nodes) {
|
|
21
|
+
let version = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
|
|
22
|
+
let dataMode = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
|
|
23
|
+
const header = [version, dataMode, nodes.length].map(v => v.toString(16).padStart(2, '0')).join('');
|
|
24
|
+
const body = nodes.map(node => [node.onOff, node.loops, node.step, node.hour, node.minute, node.brightness, node.temperature, node.duration].map(v => v.toString(16).padStart(2, '0')).join('')).join('');
|
|
25
|
+
return `${header}${body}`;
|
|
26
|
+
};
|
|
27
|
+
describe('WakeUpSigmesh parser & formatter', () => {
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
jest.clearAllMocks();
|
|
30
|
+
});
|
|
31
|
+
it('returns default value when dp string is empty', () => {
|
|
32
|
+
expect(wakeupParserSigmesh.parser('')).toEqual(new WakeUpSigmesh().defaultValue);
|
|
33
|
+
});
|
|
34
|
+
it('returns default value when dp string length invalid', () => {
|
|
35
|
+
// header + incomplete node data (not multiple of 8)
|
|
36
|
+
|
|
37
|
+
expect(wakeupParserSigmesh.parser('0102011234')).toEqual(new WakeUpSigmesh().defaultValue);
|
|
38
|
+
});
|
|
39
|
+
it('parses valid dp string into nodes', () => {
|
|
40
|
+
const nodes = [{
|
|
41
|
+
onOff: 1,
|
|
42
|
+
loops: loopsToHex('1000000'),
|
|
43
|
+
step: 3,
|
|
44
|
+
hour: 6,
|
|
45
|
+
minute: 30,
|
|
46
|
+
brightness: 80,
|
|
47
|
+
temperature: 90,
|
|
48
|
+
duration: 12
|
|
49
|
+
}, {
|
|
50
|
+
onOff: 0,
|
|
51
|
+
loops: loopsToHex('0100000'),
|
|
52
|
+
step: 5,
|
|
53
|
+
hour: 7,
|
|
54
|
+
minute: 45,
|
|
55
|
+
brightness: 60,
|
|
56
|
+
temperature: 70,
|
|
57
|
+
duration: 8
|
|
58
|
+
}];
|
|
59
|
+
const dpStr = buildDpStr(nodes, 1, 2);
|
|
60
|
+
const result = wakeupParserSigmesh.parser(dpStr);
|
|
61
|
+
expect(result.version).toBe(1);
|
|
62
|
+
expect(result.dataMode).toBe(2);
|
|
63
|
+
expect(result.length).toBe(2);
|
|
64
|
+
expect(result.nodes).toHaveLength(2);
|
|
65
|
+
expect(result.nodes[0]).toMatchObject({
|
|
66
|
+
onOff: true,
|
|
67
|
+
loops: '1000000',
|
|
68
|
+
step: 3,
|
|
69
|
+
hour: 6,
|
|
70
|
+
minute: 30,
|
|
71
|
+
brightness: 80,
|
|
72
|
+
temperature: 90,
|
|
73
|
+
duration: 12,
|
|
74
|
+
index: 0
|
|
75
|
+
});
|
|
76
|
+
expect(result.nodes[1]).toMatchObject({
|
|
77
|
+
onOff: false,
|
|
78
|
+
loops: '0100000',
|
|
79
|
+
index: 1
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
it('formats nodes back to dp string', () => {
|
|
83
|
+
const result = wakeupParserSigmesh.formatter({
|
|
84
|
+
version: 1,
|
|
85
|
+
dataMode: '03',
|
|
86
|
+
length: 1,
|
|
87
|
+
nodes: [{
|
|
88
|
+
onOff: true,
|
|
89
|
+
loops: '1000000',
|
|
90
|
+
step: 10,
|
|
91
|
+
hour: 5,
|
|
92
|
+
minute: 15,
|
|
93
|
+
brightness: 90,
|
|
94
|
+
temperature: 50,
|
|
95
|
+
duration: 20,
|
|
96
|
+
index: 0
|
|
97
|
+
}]
|
|
98
|
+
});
|
|
99
|
+
expect(result).toBe(buildDpStr([{
|
|
100
|
+
onOff: 1,
|
|
101
|
+
loops: loopsToHex('1000000'),
|
|
102
|
+
step: 10,
|
|
103
|
+
hour: 5,
|
|
104
|
+
minute: 15,
|
|
105
|
+
brightness: 90,
|
|
106
|
+
temperature: 50,
|
|
107
|
+
duration: 20
|
|
108
|
+
}], 1, 3));
|
|
109
|
+
});
|
|
110
|
+
it('round-trips dp string through parser and formatter', () => {
|
|
111
|
+
const nodes = [{
|
|
112
|
+
onOff: 1,
|
|
113
|
+
loops: loopsToHex('1111111'),
|
|
114
|
+
step: 2,
|
|
115
|
+
hour: 12,
|
|
116
|
+
minute: 0,
|
|
117
|
+
brightness: 100,
|
|
118
|
+
temperature: 40,
|
|
119
|
+
duration: 24
|
|
120
|
+
}];
|
|
121
|
+
const dpStr = buildDpStr(nodes, 2, 3);
|
|
122
|
+
const parsed = wakeupParserSigmesh.parser(dpStr);
|
|
123
|
+
const formatted = wakeupParserSigmesh.formatter(parsed);
|
|
124
|
+
expect(formatted).toBe(dpStr);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { act, renderHook } from '@testing-library/react-hooks';
|
|
2
|
+
import { useBaseLightDp, getDpDataByMesh, fetchDpData, useFetchDpDataByMesh, useFetchDpData } from '../useBaseLightDp';
|
|
3
|
+
import { DP_CHANGE_EVENT_KEY } from '../../constant';
|
|
4
|
+
import { devIdOrGroupIdCache } from '../../utils/ScheduleCache';
|
|
5
|
+
import { getDpIdByDpCode } from '../../utils/ScheduleUtils';
|
|
6
|
+
import { getDpState, setDpState } from '../../utils/dpState';
|
|
7
|
+
import { isInitFn, useSupport } from '../useCommonSupport';
|
|
8
|
+
import { updateDeviceDp } from '../../conflict/updateDeviceDp';
|
|
9
|
+
import { queryDps } from '@ray-js/ray';
|
|
10
|
+
jest.mock('../../utils/ScheduleLogger', () => ({
|
|
11
|
+
scheduleLogger: {
|
|
12
|
+
debug: jest.fn(),
|
|
13
|
+
warn: jest.fn(),
|
|
14
|
+
error: jest.fn(),
|
|
15
|
+
info: jest.fn()
|
|
16
|
+
}
|
|
17
|
+
}));
|
|
18
|
+
jest.mock('../../utils/ScheduleCache', () => ({
|
|
19
|
+
devIdOrGroupIdCache: {
|
|
20
|
+
get: jest.fn().mockReturnValue({
|
|
21
|
+
devId: 'device-1',
|
|
22
|
+
groupId: ''
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
}));
|
|
26
|
+
const listeners = {};
|
|
27
|
+
jest.mock('../../utils/ScheduleEmit', () => ({
|
|
28
|
+
emitter: {
|
|
29
|
+
on: jest.fn((event, cb) => {
|
|
30
|
+
listeners[event] = cb;
|
|
31
|
+
}),
|
|
32
|
+
off: jest.fn(event => {
|
|
33
|
+
delete listeners[event];
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
}));
|
|
37
|
+
jest.mock('../../conflict/updateDeviceDp', () => ({
|
|
38
|
+
updateDeviceDp: jest.fn()
|
|
39
|
+
}));
|
|
40
|
+
jest.mock('../../utils/ScheduleUtils', () => ({
|
|
41
|
+
getDpIdByDpCode: jest.fn()
|
|
42
|
+
}));
|
|
43
|
+
jest.mock('../../utils/dpState', () => ({
|
|
44
|
+
getDpState: jest.fn(),
|
|
45
|
+
setDpState: jest.fn()
|
|
46
|
+
}));
|
|
47
|
+
jest.mock('../useCommonSupport', () => ({
|
|
48
|
+
isInitFn: jest.fn(),
|
|
49
|
+
useSupport: jest.fn()
|
|
50
|
+
}));
|
|
51
|
+
jest.mock('@ray-js/ray', () => ({
|
|
52
|
+
queryDps: jest.fn()
|
|
53
|
+
}));
|
|
54
|
+
describe('useBaseLightDp', () => {
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
jest.clearAllMocks();
|
|
57
|
+
devIdOrGroupIdCache.get.mockReturnValue({
|
|
58
|
+
devId: 'device-1',
|
|
59
|
+
groupId: ''
|
|
60
|
+
});
|
|
61
|
+
isInitFn.mockReturnValue(true);
|
|
62
|
+
getDpIdByDpCode.mockReturnValue(10);
|
|
63
|
+
getDpState.mockReturnValue(undefined);
|
|
64
|
+
});
|
|
65
|
+
it('subscribes to emitter and updates state when dp changes', () => {
|
|
66
|
+
const {
|
|
67
|
+
result
|
|
68
|
+
} = renderHook(() => useBaseLightDp('bright_value', 0));
|
|
69
|
+
act(() => {
|
|
70
|
+
var _listeners$DP_CHANGE_;
|
|
71
|
+
(_listeners$DP_CHANGE_ = listeners[DP_CHANGE_EVENT_KEY]) === null || _listeners$DP_CHANGE_ === void 0 || _listeners$DP_CHANGE_.call(listeners, {
|
|
72
|
+
dps: {
|
|
73
|
+
10: 50
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
expect(setDpState).toHaveBeenCalledWith('bright_value', 50);
|
|
78
|
+
expect(result.current.dpValue).toBe(50);
|
|
79
|
+
});
|
|
80
|
+
it('filters duplicate emitter values', () => {
|
|
81
|
+
const {
|
|
82
|
+
result
|
|
83
|
+
} = renderHook(() => useBaseLightDp('bright_value', 0));
|
|
84
|
+
act(() => {
|
|
85
|
+
var _listeners$DP_CHANGE_2, _listeners$DP_CHANGE_3;
|
|
86
|
+
(_listeners$DP_CHANGE_2 = listeners[DP_CHANGE_EVENT_KEY]) === null || _listeners$DP_CHANGE_2 === void 0 || _listeners$DP_CHANGE_2.call(listeners, {
|
|
87
|
+
dps: {
|
|
88
|
+
10: 60
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
(_listeners$DP_CHANGE_3 = listeners[DP_CHANGE_EVENT_KEY]) === null || _listeners$DP_CHANGE_3 === void 0 || _listeners$DP_CHANGE_3.call(listeners, {
|
|
92
|
+
dps: {
|
|
93
|
+
10: 60
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
expect(result.current.dpValue).toBe(60);
|
|
98
|
+
expect(setDpState).toHaveBeenCalledTimes(1);
|
|
99
|
+
});
|
|
100
|
+
it('loads initial dp value from cache when available', () => {
|
|
101
|
+
getDpState.mockReturnValue(70);
|
|
102
|
+
const {
|
|
103
|
+
result
|
|
104
|
+
} = renderHook(() => useBaseLightDp('bright_value'));
|
|
105
|
+
expect(result.current.dpValue).toBe(70);
|
|
106
|
+
});
|
|
107
|
+
it('updateDp dispatches dp command when dpId exists', () => {
|
|
108
|
+
const {
|
|
109
|
+
result
|
|
110
|
+
} = renderHook(() => useBaseLightDp('bright_value'));
|
|
111
|
+
act(() => {
|
|
112
|
+
result.current.updateDp(90);
|
|
113
|
+
});
|
|
114
|
+
expect(updateDeviceDp).toHaveBeenCalledWith('device-1', '', {
|
|
115
|
+
10: 90
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
it('updateDp graceful when dpId missing', () => {
|
|
119
|
+
getDpIdByDpCode.mockReturnValue(undefined);
|
|
120
|
+
const errorSpy = jest.fn();
|
|
121
|
+
const {
|
|
122
|
+
result
|
|
123
|
+
} = renderHook(() => useBaseLightDp('unknown'));
|
|
124
|
+
act(() => {
|
|
125
|
+
result.current.updateDp(1, errorSpy);
|
|
126
|
+
});
|
|
127
|
+
expect(updateDeviceDp).not.toHaveBeenCalled();
|
|
128
|
+
expect(errorSpy).not.toHaveBeenCalled();
|
|
129
|
+
});
|
|
130
|
+
it('updateDp handles exception with callback', () => {
|
|
131
|
+
getDpIdByDpCode.mockImplementation(() => {
|
|
132
|
+
throw new Error('failed');
|
|
133
|
+
});
|
|
134
|
+
const errorSpy = jest.fn();
|
|
135
|
+
const {
|
|
136
|
+
result
|
|
137
|
+
} = renderHook(() => useBaseLightDp('error_dp'));
|
|
138
|
+
act(() => {
|
|
139
|
+
result.current.updateDp(1, errorSpy);
|
|
140
|
+
});
|
|
141
|
+
expect(errorSpy).toHaveBeenCalled();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
describe('getDpDataByMesh', () => {
|
|
145
|
+
beforeEach(() => {
|
|
146
|
+
jest.clearAllMocks();
|
|
147
|
+
devIdOrGroupIdCache.get.mockReturnValue({
|
|
148
|
+
devId: 'device-1',
|
|
149
|
+
groupId: ''
|
|
150
|
+
});
|
|
151
|
+
getDpIdByDpCode.mockImplementation(code => code === 'bright_value' ? 1 : null);
|
|
152
|
+
});
|
|
153
|
+
it('logs error when dpCodes is not array', () => {
|
|
154
|
+
getDpDataByMesh(null);
|
|
155
|
+
expect(queryDps).not.toHaveBeenCalled();
|
|
156
|
+
});
|
|
157
|
+
it('warns when groupId present', () => {
|
|
158
|
+
devIdOrGroupIdCache.get.mockReturnValue({
|
|
159
|
+
devId: 'device-1',
|
|
160
|
+
groupId: 'group-1'
|
|
161
|
+
});
|
|
162
|
+
getDpDataByMesh(['switch']);
|
|
163
|
+
expect(queryDps).not.toHaveBeenCalled();
|
|
164
|
+
});
|
|
165
|
+
it('warns when deviceId missing', () => {
|
|
166
|
+
devIdOrGroupIdCache.get.mockReturnValue({
|
|
167
|
+
devId: '',
|
|
168
|
+
groupId: ''
|
|
169
|
+
});
|
|
170
|
+
getDpDataByMesh(['switch']);
|
|
171
|
+
expect(queryDps).not.toHaveBeenCalled();
|
|
172
|
+
});
|
|
173
|
+
it('queries dp when ids resolved', () => {
|
|
174
|
+
getDpDataByMesh(['bright_value']);
|
|
175
|
+
expect(queryDps).toHaveBeenCalledWith(expect.objectContaining({
|
|
176
|
+
deviceId: 'device-1',
|
|
177
|
+
dpIds: ['1']
|
|
178
|
+
}));
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
describe('fetchDpData', () => {
|
|
182
|
+
beforeEach(() => {
|
|
183
|
+
jest.clearAllMocks();
|
|
184
|
+
devIdOrGroupIdCache.get.mockReturnValue({
|
|
185
|
+
devId: 'device-1',
|
|
186
|
+
groupId: ''
|
|
187
|
+
});
|
|
188
|
+
getDpIdByDpCode.mockImplementation(code => code === 'bright_value' ? 2 : null);
|
|
189
|
+
});
|
|
190
|
+
it('validates dpCodes argument', () => {
|
|
191
|
+
fetchDpData(null);
|
|
192
|
+
expect(queryDps).not.toHaveBeenCalled();
|
|
193
|
+
});
|
|
194
|
+
it('requires deviceId', () => {
|
|
195
|
+
devIdOrGroupIdCache.get.mockReturnValue({
|
|
196
|
+
devId: '',
|
|
197
|
+
groupId: ''
|
|
198
|
+
});
|
|
199
|
+
fetchDpData(['switch']);
|
|
200
|
+
expect(queryDps).not.toHaveBeenCalled();
|
|
201
|
+
});
|
|
202
|
+
it('runs query when dp ids exist', () => {
|
|
203
|
+
fetchDpData(['bright_value']);
|
|
204
|
+
expect(queryDps).toHaveBeenCalledWith(expect.objectContaining({
|
|
205
|
+
deviceId: 'device-1',
|
|
206
|
+
dpIds: ['2']
|
|
207
|
+
}));
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
describe('useFetch hooks', () => {
|
|
211
|
+
beforeEach(() => {
|
|
212
|
+
jest.clearAllMocks();
|
|
213
|
+
getDpIdByDpCode.mockReturnValue(3);
|
|
214
|
+
devIdOrGroupIdCache.get.mockReturnValue({
|
|
215
|
+
devId: 'device-1',
|
|
216
|
+
groupId: ''
|
|
217
|
+
});
|
|
218
|
+
useSupport.mockReturnValue({
|
|
219
|
+
isSigMeshDevice: () => true,
|
|
220
|
+
deviceType: 'sigmesh'
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
it('useFetchDpDataByMesh triggers fetch on mount and refresh', () => {
|
|
224
|
+
const {
|
|
225
|
+
result
|
|
226
|
+
} = renderHook(() => useFetchDpDataByMesh(['bright_value']));
|
|
227
|
+
expect(queryDps).toHaveBeenCalledTimes(1);
|
|
228
|
+
act(() => {
|
|
229
|
+
result.current.refresh();
|
|
230
|
+
});
|
|
231
|
+
expect(queryDps).toHaveBeenCalledTimes(2);
|
|
232
|
+
});
|
|
233
|
+
it('useFetchDpDataByMesh skips when not sigmesh', () => {
|
|
234
|
+
useSupport.mockReturnValue({
|
|
235
|
+
isSigMeshDevice: () => false,
|
|
236
|
+
deviceType: 'wifi'
|
|
237
|
+
});
|
|
238
|
+
renderHook(() => useFetchDpDataByMesh(['bright_value']));
|
|
239
|
+
expect(queryDps).not.toHaveBeenCalled();
|
|
240
|
+
});
|
|
241
|
+
it('useFetchDpData triggers fetch on mount and refresh', () => {
|
|
242
|
+
const {
|
|
243
|
+
result
|
|
244
|
+
} = renderHook(() => useFetchDpData(['bright_value']));
|
|
245
|
+
expect(queryDps).toHaveBeenCalledTimes(1);
|
|
246
|
+
act(() => {
|
|
247
|
+
result.current.refresh();
|
|
248
|
+
});
|
|
249
|
+
expect(queryDps).toHaveBeenCalledTimes(2);
|
|
250
|
+
});
|
|
251
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { act, renderHook } from '@testing-library/react-hooks';
|
|
2
|
+
import { useScheduleInit, useSupport, useCommonSupport, resetIsInit } from '../useCommonSupport';
|
|
3
|
+
import { emitter } from '../../utils/ScheduleEmit';
|
|
4
|
+
const initStorageMock = jest.fn().mockResolvedValue(undefined);
|
|
5
|
+
const getGroupDpsInfosMock = jest.fn().mockResolvedValue({});
|
|
6
|
+
const registerDeviceListListenerMock = jest.fn();
|
|
7
|
+
const registerGroupChangeMock = jest.fn();
|
|
8
|
+
const onDpDataChangeMock = jest.fn();
|
|
9
|
+
const onGroupDpDataChangeEventMock = jest.fn();
|
|
10
|
+
const offDpDataChangeMock = jest.fn();
|
|
11
|
+
const offGroupDpDataChangeEventMock = jest.fn();
|
|
12
|
+
const getDeviceInfoMock = jest.fn();
|
|
13
|
+
const getTimerListWithFlushMock = jest.fn();
|
|
14
|
+
const initDpStateMock = jest.fn();
|
|
15
|
+
jest.mock('../../context/timer/context', () => ({
|
|
16
|
+
useTimerContext: jest.fn().mockReturnValue({
|
|
17
|
+
dispatch: jest.fn(),
|
|
18
|
+
actions: {}
|
|
19
|
+
})
|
|
20
|
+
}));
|
|
21
|
+
const supportInitMock = jest.fn();
|
|
22
|
+
const supportIsSupportDpMock = jest.fn();
|
|
23
|
+
jest.mock('../../utils/ScheduleSupport', () => ({
|
|
24
|
+
Support: jest.fn(() => ({
|
|
25
|
+
init: supportInitMock,
|
|
26
|
+
isSupportDp: supportIsSupportDpMock
|
|
27
|
+
}))
|
|
28
|
+
}));
|
|
29
|
+
jest.mock('../../utils/storage', () => ({
|
|
30
|
+
initStorage: function () {
|
|
31
|
+
return initStorageMock(...arguments);
|
|
32
|
+
}
|
|
33
|
+
}));
|
|
34
|
+
jest.mock('../../utils/dpState', () => ({
|
|
35
|
+
initDpState: function () {
|
|
36
|
+
return initDpStateMock(...arguments);
|
|
37
|
+
}
|
|
38
|
+
}));
|
|
39
|
+
jest.mock('../../hooks/useTimerOperateLocal', () => ({
|
|
40
|
+
getTimerListWithFlush: function () {
|
|
41
|
+
return getTimerListWithFlushMock(...arguments);
|
|
42
|
+
}
|
|
43
|
+
}));
|
|
44
|
+
jest.mock('../../utils/ScheduleUtils', () => ({
|
|
45
|
+
getDpCodeByDpId: dpId => `code_${dpId}`
|
|
46
|
+
}));
|
|
47
|
+
jest.mock('@ray-js/ray', () => ({
|
|
48
|
+
getDeviceInfo: function () {
|
|
49
|
+
return getDeviceInfoMock(...arguments);
|
|
50
|
+
},
|
|
51
|
+
getGroupDpsInfos: function () {
|
|
52
|
+
return getGroupDpsInfosMock(...arguments);
|
|
53
|
+
},
|
|
54
|
+
registerDeviceListListener: function () {
|
|
55
|
+
return registerDeviceListListenerMock(...arguments);
|
|
56
|
+
},
|
|
57
|
+
registerGroupChange: function () {
|
|
58
|
+
return registerGroupChangeMock(...arguments);
|
|
59
|
+
},
|
|
60
|
+
onDpDataChange: function () {
|
|
61
|
+
return onDpDataChangeMock(...arguments);
|
|
62
|
+
},
|
|
63
|
+
onGroupDpDataChangeEvent: function () {
|
|
64
|
+
return onGroupDpDataChangeEventMock(...arguments);
|
|
65
|
+
},
|
|
66
|
+
offDpDataChange: function () {
|
|
67
|
+
return offDpDataChangeMock(...arguments);
|
|
68
|
+
},
|
|
69
|
+
offGroupDpDataChangeEvent: function () {
|
|
70
|
+
return offGroupDpDataChangeEventMock(...arguments);
|
|
71
|
+
}
|
|
72
|
+
}));
|
|
73
|
+
jest.mock('../../utils/ScheduleLogger', () => {
|
|
74
|
+
const loggerMock = {
|
|
75
|
+
info: jest.fn(),
|
|
76
|
+
debug: jest.fn(),
|
|
77
|
+
warn: jest.fn(),
|
|
78
|
+
error: jest.fn(),
|
|
79
|
+
setLogLevel: jest.fn(),
|
|
80
|
+
setLogPrefix: jest.fn(),
|
|
81
|
+
formatMessage: jest.fn()
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
scheduleLogger: loggerMock,
|
|
85
|
+
ScheduleLogger: class {
|
|
86
|
+
info = (() => loggerMock.info)();
|
|
87
|
+
debug = (() => loggerMock.debug)();
|
|
88
|
+
warn = (() => loggerMock.warn)();
|
|
89
|
+
error = (() => loggerMock.error)();
|
|
90
|
+
setLogLevel = (() => loggerMock.setLogLevel)();
|
|
91
|
+
setLogPrefix = (() => loggerMock.setLogPrefix)();
|
|
92
|
+
formatMessage = (() => loggerMock.formatMessage)();
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
describe('useCommonSupport suite', () => {
|
|
97
|
+
beforeEach(() => {
|
|
98
|
+
jest.clearAllMocks();
|
|
99
|
+
resetIsInit();
|
|
100
|
+
supportInitMock.mockResolvedValue(true);
|
|
101
|
+
supportIsSupportDpMock.mockImplementation(code => code === 'a' || code === 'code_1');
|
|
102
|
+
registerDeviceListListenerMock.mockImplementation(_ref => {
|
|
103
|
+
let {
|
|
104
|
+
success
|
|
105
|
+
} = _ref;
|
|
106
|
+
return success === null || success === void 0 ? void 0 : success();
|
|
107
|
+
});
|
|
108
|
+
registerGroupChangeMock.mockImplementation(_ref2 => {
|
|
109
|
+
let {
|
|
110
|
+
success
|
|
111
|
+
} = _ref2;
|
|
112
|
+
return success === null || success === void 0 ? void 0 : success();
|
|
113
|
+
});
|
|
114
|
+
getDeviceInfoMock.mockImplementation(_ref3 => {
|
|
115
|
+
let {
|
|
116
|
+
success
|
|
117
|
+
} = _ref3;
|
|
118
|
+
return success === null || success === void 0 ? void 0 : success({
|
|
119
|
+
dps: {
|
|
120
|
+
1: true
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
getGroupDpsInfosMock.mockResolvedValue({
|
|
125
|
+
1: true
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
it('initializes device support and sets ready state', async () => {
|
|
129
|
+
const {
|
|
130
|
+
result
|
|
131
|
+
} = renderHook(() => useScheduleInit({
|
|
132
|
+
devId: 'dev-1'
|
|
133
|
+
}));
|
|
134
|
+
await act(async () => {
|
|
135
|
+
await Promise.resolve();
|
|
136
|
+
});
|
|
137
|
+
expect(result.current.isReady).toBe(true);
|
|
138
|
+
expect(initStorageMock).toHaveBeenCalledWith('dev-1', undefined);
|
|
139
|
+
expect(getDeviceInfoMock).toHaveBeenCalled();
|
|
140
|
+
});
|
|
141
|
+
it('initializes group support and fetches group dps', async () => {
|
|
142
|
+
const {
|
|
143
|
+
result
|
|
144
|
+
} = renderHook(() => useScheduleInit({
|
|
145
|
+
devId: '',
|
|
146
|
+
groupId: 'group-1'
|
|
147
|
+
}));
|
|
148
|
+
await act(async () => {
|
|
149
|
+
await Promise.resolve();
|
|
150
|
+
});
|
|
151
|
+
expect(result.current.isReady).toBe(true);
|
|
152
|
+
expect(getGroupDpsInfosMock).toHaveBeenCalledWith('group-1');
|
|
153
|
+
expect(initDpStateMock).toHaveBeenCalled();
|
|
154
|
+
});
|
|
155
|
+
it('handles support init failure', async () => {
|
|
156
|
+
supportInitMock.mockRejectedValueOnce(new Error('fail'));
|
|
157
|
+
const {
|
|
158
|
+
result
|
|
159
|
+
} = renderHook(() => useScheduleInit({
|
|
160
|
+
devId: 'dev-1'
|
|
161
|
+
}));
|
|
162
|
+
await act(async () => {
|
|
163
|
+
await Promise.resolve();
|
|
164
|
+
});
|
|
165
|
+
expect(result.current.isReady).toBe(false);
|
|
166
|
+
});
|
|
167
|
+
it('useSupport updates after initDone event', async () => {
|
|
168
|
+
const {
|
|
169
|
+
result: initResult
|
|
170
|
+
} = renderHook(() => useScheduleInit({
|
|
171
|
+
devId: 'dev-1'
|
|
172
|
+
}));
|
|
173
|
+
await act(async () => {
|
|
174
|
+
await Promise.resolve();
|
|
175
|
+
});
|
|
176
|
+
expect(initResult.current.isReady).toBe(true);
|
|
177
|
+
const {
|
|
178
|
+
result
|
|
179
|
+
} = renderHook(() => useSupport());
|
|
180
|
+
act(() => {
|
|
181
|
+
emitter.emit('initDone');
|
|
182
|
+
});
|
|
183
|
+
expect(result.current).not.toBeNull();
|
|
184
|
+
});
|
|
185
|
+
it('useCommonSupport respects checkType and dp list', async () => {
|
|
186
|
+
const {
|
|
187
|
+
result: initResult
|
|
188
|
+
} = renderHook(() => useScheduleInit({
|
|
189
|
+
devId: 'dev-1'
|
|
190
|
+
}));
|
|
191
|
+
await act(async () => {
|
|
192
|
+
await Promise.resolve();
|
|
193
|
+
});
|
|
194
|
+
expect(initResult.current.isReady).toBe(true);
|
|
195
|
+
const {
|
|
196
|
+
result: someResult
|
|
197
|
+
} = renderHook(() => useCommonSupport(['a']));
|
|
198
|
+
expect(someResult.current.isSupport).toBe(true);
|
|
199
|
+
const {
|
|
200
|
+
result: everyResult
|
|
201
|
+
} = renderHook(() => useCommonSupport(['a', 'b'], ['a', 'b'], 'every'));
|
|
202
|
+
expect(everyResult.current.isSupport).toBe(false);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|