@ray-js/lamp-schedule-core 1.0.1-beta-10 → 1.0.2-beta-2
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/hooks/useCommonSupport.js +16 -19
- 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
|
@@ -1,10 +1,23 @@
|
|
|
1
|
+
import "core-js/modules/esnext.iterator.constructor.js";
|
|
2
|
+
import "core-js/modules/esnext.iterator.for-each.js";
|
|
1
3
|
import { renderHook, act } from '@testing-library/react-hooks';
|
|
2
|
-
import { useLocalTimerAdd, useLocalTimerUpdate, useLocalTimerRemove, useLocalTimerList, useLocalTimerUpdateStatus } from '../useTimerOperateLocal';
|
|
3
|
-
import { useLocalTimerCloudProperty } from '../../utils/ScheduleCloudProperty';
|
|
4
|
+
import { useLocalTimerAdd, useLocalTimerUpdate, useLocalTimerRemove, useLocalTimerList, useLocalTimerUpdateStatus, getTimerListWithFlush } from '../useTimerOperateLocal';
|
|
4
5
|
import { LOCAL_TIMER_CATEGORY } from '../../constant';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const mockSet = jest.fn();
|
|
7
|
+
const mockUpdate = jest.fn();
|
|
8
|
+
const mockRemove = jest.fn();
|
|
9
|
+
const mockUpdateStatus = jest.fn();
|
|
10
|
+
const mockGetAllMethod = jest.fn();
|
|
11
|
+
const mockStandaloneGetAll = jest.fn();
|
|
12
|
+
const mockUseLocalTimerCloudProperty = jest.fn(() => ({
|
|
13
|
+
set: mockSet,
|
|
14
|
+
update: mockUpdate,
|
|
15
|
+
remove: mockRemove,
|
|
16
|
+
updateStatus: mockUpdateStatus,
|
|
17
|
+
getAll: mockGetAllMethod
|
|
18
|
+
}));
|
|
19
|
+
const mockGetTimerState = jest.fn();
|
|
20
|
+
const mockGetTimerReducer = jest.fn();
|
|
8
21
|
jest.mock('../../utils/ScheduleCache', () => ({
|
|
9
22
|
devIdOrGroupIdCache: {
|
|
10
23
|
get: jest.fn().mockReturnValue({
|
|
@@ -16,35 +29,58 @@ jest.mock('../../utils/ScheduleCache', () => ({
|
|
|
16
29
|
getScheduleCache: jest.fn()
|
|
17
30
|
}));
|
|
18
31
|
jest.mock('../../context/timer/', () => ({
|
|
19
|
-
getTimerState:
|
|
32
|
+
getTimerState: function () {
|
|
33
|
+
return mockGetTimerState(...arguments);
|
|
34
|
+
},
|
|
35
|
+
getTimerReducer: function () {
|
|
36
|
+
return mockGetTimerReducer(...arguments);
|
|
37
|
+
}
|
|
20
38
|
}));
|
|
21
|
-
|
|
22
|
-
// Mock ray functions
|
|
23
39
|
jest.mock('@ray-js/ray', () => ({
|
|
24
40
|
offDpDataChange: jest.fn(),
|
|
25
41
|
onDpDataChange: jest.fn()
|
|
26
42
|
}));
|
|
27
43
|
jest.mock('../../utils/ScheduleCloudProperty', () => ({
|
|
28
|
-
useLocalTimerCloudProperty:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
44
|
+
useLocalTimerCloudProperty: () => mockUseLocalTimerCloudProperty(),
|
|
45
|
+
getAll: () => mockStandaloneGetAll()
|
|
46
|
+
}));
|
|
47
|
+
jest.mock('../../utils/ScheduleLogger', () => ({
|
|
48
|
+
scheduleLogger: {
|
|
49
|
+
debug: jest.fn(),
|
|
50
|
+
error: jest.fn(),
|
|
51
|
+
warn: jest.fn(),
|
|
52
|
+
info: jest.fn()
|
|
53
|
+
}
|
|
37
54
|
}));
|
|
55
|
+
jest.mock('../../dpParser', () => ({
|
|
56
|
+
timerReportParser: {
|
|
57
|
+
parser: jest.fn()
|
|
58
|
+
}
|
|
59
|
+
}));
|
|
60
|
+
const mockedRay = jest.requireMock('@ray-js/ray');
|
|
61
|
+
const mockScheduleLogger = jest.requireMock('../../utils/ScheduleLogger').scheduleLogger;
|
|
62
|
+
const mockTimerReportParser = jest.requireMock('../../dpParser').timerReportParser.parser;
|
|
38
63
|
describe('useTimerOperateLocal Hooks', () => {
|
|
39
64
|
beforeEach(() => {
|
|
40
65
|
jest.clearAllMocks();
|
|
66
|
+
mockSet.mockReset().mockResolvedValue(true);
|
|
67
|
+
mockUpdate.mockReset().mockResolvedValue(true);
|
|
68
|
+
mockRemove.mockReset().mockResolvedValue(true);
|
|
69
|
+
mockUpdateStatus.mockReset().mockResolvedValue(true);
|
|
70
|
+
mockGetAllMethod.mockReset().mockResolvedValue({
|
|
71
|
+
properties: {}
|
|
72
|
+
});
|
|
73
|
+
mockStandaloneGetAll.mockReset().mockResolvedValue({
|
|
74
|
+
properties: {}
|
|
75
|
+
});
|
|
76
|
+
mockUseLocalTimerCloudProperty.mockClear();
|
|
77
|
+
mockGetTimerState.mockReset();
|
|
78
|
+
mockGetTimerReducer.mockReset();
|
|
79
|
+
mockTimerReportParser.mockReset();
|
|
80
|
+
Object.values(mockScheduleLogger).forEach(fn => fn.mockReset());
|
|
41
81
|
});
|
|
42
82
|
describe('useLocalTimerAdd', () => {
|
|
43
83
|
it('should add local timer successfully', async () => {
|
|
44
|
-
const mockSet = jest.fn().mockResolvedValue(true);
|
|
45
|
-
useLocalTimerCloudProperty.mockReturnValue({
|
|
46
|
-
set: mockSet
|
|
47
|
-
});
|
|
48
84
|
const {
|
|
49
85
|
result
|
|
50
86
|
} = renderHook(() => useLocalTimerAdd(true));
|
|
@@ -64,10 +100,8 @@ describe('useTimerOperateLocal Hooks', () => {
|
|
|
64
100
|
expect(result.current.isLoading).toBe(false);
|
|
65
101
|
});
|
|
66
102
|
it('should handle add timer error', async () => {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
set: mockSet
|
|
70
|
-
});
|
|
103
|
+
const error = new Error('Add failed');
|
|
104
|
+
mockSet.mockRejectedValueOnce(error);
|
|
71
105
|
const {
|
|
72
106
|
result
|
|
73
107
|
} = renderHook(() => useLocalTimerAdd(true));
|
|
@@ -80,17 +114,20 @@ describe('useTimerOperateLocal Hooks', () => {
|
|
|
80
114
|
aliasName: 'Test Timer',
|
|
81
115
|
isAppPush: true
|
|
82
116
|
};
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
117
|
+
let caught = null;
|
|
118
|
+
await act(async () => {
|
|
119
|
+
try {
|
|
120
|
+
await result.current.addTimer(mockTimer);
|
|
121
|
+
} catch (err) {
|
|
122
|
+
caught = err;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
expect(caught).toBe(error);
|
|
126
|
+
expect(result.current.isLoading).toBe(false);
|
|
86
127
|
});
|
|
87
128
|
});
|
|
88
129
|
describe('useLocalTimerUpdate', () => {
|
|
89
130
|
it('should update local timer successfully', async () => {
|
|
90
|
-
const mockUpdate = jest.fn().mockResolvedValue(true);
|
|
91
|
-
useLocalTimerCloudProperty.mockReturnValue({
|
|
92
|
-
update: mockUpdate
|
|
93
|
-
});
|
|
94
131
|
const {
|
|
95
132
|
result
|
|
96
133
|
} = renderHook(() => useLocalTimerUpdate(true));
|
|
@@ -112,10 +149,6 @@ describe('useTimerOperateLocal Hooks', () => {
|
|
|
112
149
|
});
|
|
113
150
|
describe('useLocalTimerRemove', () => {
|
|
114
151
|
it('should remove local timer successfully', async () => {
|
|
115
|
-
const mockRemove = jest.fn().mockResolvedValue(true);
|
|
116
|
-
useLocalTimerCloudProperty.mockReturnValue({
|
|
117
|
-
remove: mockRemove
|
|
118
|
-
});
|
|
119
152
|
const {
|
|
120
153
|
result
|
|
121
154
|
} = renderHook(() => useLocalTimerRemove(true));
|
|
@@ -127,7 +160,7 @@ describe('useTimerOperateLocal Hooks', () => {
|
|
|
127
160
|
});
|
|
128
161
|
describe('useLocalTimerList', () => {
|
|
129
162
|
it('should get local timer list successfully', async () => {
|
|
130
|
-
|
|
163
|
+
mockGetAllMethod.mockResolvedValueOnce({
|
|
131
164
|
properties: {
|
|
132
165
|
[`${LOCAL_TIMER_CATEGORY}_1`]: JSON.stringify({
|
|
133
166
|
time: '12:00',
|
|
@@ -139,9 +172,6 @@ describe('useTimerOperateLocal Hooks', () => {
|
|
|
139
172
|
})
|
|
140
173
|
}
|
|
141
174
|
});
|
|
142
|
-
useLocalTimerCloudProperty.mockReturnValue({
|
|
143
|
-
getAll: mockGetAll
|
|
144
|
-
});
|
|
145
175
|
const {
|
|
146
176
|
result
|
|
147
177
|
} = renderHook(() => useLocalTimerList(true));
|
|
@@ -153,16 +183,13 @@ describe('useTimerOperateLocal Hooks', () => {
|
|
|
153
183
|
});
|
|
154
184
|
});
|
|
155
185
|
it('should handle invalid timer data', async () => {
|
|
156
|
-
|
|
186
|
+
mockGetAllMethod.mockResolvedValueOnce({
|
|
157
187
|
properties: {
|
|
158
188
|
[`${LOCAL_TIMER_CATEGORY}_1`]: 'invalid_json',
|
|
159
189
|
[`${LOCAL_TIMER_CATEGORY}_2`]: '1',
|
|
160
190
|
[`${LOCAL_TIMER_CATEGORY}_3`]: 'null'
|
|
161
191
|
}
|
|
162
192
|
});
|
|
163
|
-
useLocalTimerCloudProperty.mockReturnValue({
|
|
164
|
-
getAll: mockGetAll
|
|
165
|
-
});
|
|
166
193
|
const {
|
|
167
194
|
result
|
|
168
195
|
} = renderHook(() => useLocalTimerList(true));
|
|
@@ -172,12 +199,9 @@ describe('useTimerOperateLocal Hooks', () => {
|
|
|
172
199
|
});
|
|
173
200
|
});
|
|
174
201
|
it('should get empty timer list when no timers exist', async () => {
|
|
175
|
-
|
|
202
|
+
mockGetAllMethod.mockResolvedValueOnce({
|
|
176
203
|
properties: {}
|
|
177
204
|
});
|
|
178
|
-
useLocalTimerCloudProperty.mockReturnValue({
|
|
179
|
-
getAll: mockGetAll
|
|
180
|
-
});
|
|
181
205
|
const {
|
|
182
206
|
result
|
|
183
207
|
} = renderHook(() => useLocalTimerList(false));
|
|
@@ -189,11 +213,7 @@ describe('useTimerOperateLocal Hooks', () => {
|
|
|
189
213
|
});
|
|
190
214
|
describe('useLocalTimerUpdateStatus', () => {
|
|
191
215
|
it('should update timer status successfully', async () => {
|
|
192
|
-
|
|
193
|
-
useLocalTimerCloudProperty.mockReturnValue({
|
|
194
|
-
updateStatus: mockUpdateStatus
|
|
195
|
-
});
|
|
196
|
-
getTimerState.mockReturnValue({
|
|
216
|
+
mockGetTimerState.mockReturnValue({
|
|
197
217
|
rtcTimerList: [{
|
|
198
218
|
timerId: '1',
|
|
199
219
|
time: '12:00',
|
|
@@ -213,4 +233,92 @@ describe('useTimerOperateLocal Hooks', () => {
|
|
|
213
233
|
expect(result.current.isLoading).toBe(false);
|
|
214
234
|
});
|
|
215
235
|
});
|
|
236
|
+
describe('event handling and utilities', () => {
|
|
237
|
+
it('should flush rtc list when timer_report dp changes', () => {
|
|
238
|
+
const flushRtcTimerListMock = jest.fn().mockReturnValue('flush-action');
|
|
239
|
+
const dispatchMock = jest.fn();
|
|
240
|
+
mockGetTimerState.mockReturnValue({
|
|
241
|
+
rtcTimerList: [{
|
|
242
|
+
timerId: '1',
|
|
243
|
+
status: false,
|
|
244
|
+
time: '08:00',
|
|
245
|
+
loops: '0000000',
|
|
246
|
+
dps: {}
|
|
247
|
+
}]
|
|
248
|
+
});
|
|
249
|
+
mockGetTimerReducer.mockReturnValue({
|
|
250
|
+
dispatch: dispatchMock,
|
|
251
|
+
actions: {
|
|
252
|
+
flushRtcTimerList: flushRtcTimerListMock
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
mockTimerReportParser.mockReturnValue([{
|
|
256
|
+
id: '1',
|
|
257
|
+
status: true
|
|
258
|
+
}]);
|
|
259
|
+
const {
|
|
260
|
+
unmount
|
|
261
|
+
} = renderHook(() => useLocalTimerList(true));
|
|
262
|
+
const dpCallback = mockedRay.onDpDataChange.mock.calls[0][0];
|
|
263
|
+
dpCallback({
|
|
264
|
+
dps: {
|
|
265
|
+
38: '[{"id":"1","status":true}]'
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
expect(mockTimerReportParser).toHaveBeenCalledWith('[{"id":"1","status":true}]');
|
|
269
|
+
expect(flushRtcTimerListMock).toHaveBeenCalledWith([{
|
|
270
|
+
timerId: '1',
|
|
271
|
+
status: true,
|
|
272
|
+
time: '08:00',
|
|
273
|
+
loops: '0000000',
|
|
274
|
+
dps: {}
|
|
275
|
+
}]);
|
|
276
|
+
expect(dispatchMock).toHaveBeenCalledWith('flush-action');
|
|
277
|
+
unmount();
|
|
278
|
+
});
|
|
279
|
+
it('should warn when updating status without matching timer', async () => {
|
|
280
|
+
mockGetTimerState.mockReturnValue({
|
|
281
|
+
rtcTimerList: []
|
|
282
|
+
});
|
|
283
|
+
const {
|
|
284
|
+
result
|
|
285
|
+
} = renderHook(() => useLocalTimerUpdateStatus(true));
|
|
286
|
+
await act(async () => {
|
|
287
|
+
result.current.updateTimerStatus('not-exist', true);
|
|
288
|
+
});
|
|
289
|
+
expect(mockScheduleLogger.warn).toHaveBeenCalledWith('useLocalTimerUpdateStatus: currentTimer with timerId not-exist is undefined');
|
|
290
|
+
expect(mockUpdateStatus).not.toHaveBeenCalled();
|
|
291
|
+
});
|
|
292
|
+
it('should fetch timers and dispatch via getTimerListWithFlush', async () => {
|
|
293
|
+
mockStandaloneGetAll.mockResolvedValueOnce({
|
|
294
|
+
properties: {
|
|
295
|
+
[`${LOCAL_TIMER_CATEGORY}_5`]: JSON.stringify({
|
|
296
|
+
time: '10:10',
|
|
297
|
+
loops: '0101010',
|
|
298
|
+
status: false,
|
|
299
|
+
dps: {
|
|
300
|
+
brightness: 10
|
|
301
|
+
}
|
|
302
|
+
})
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
const dispatchMock = jest.fn();
|
|
306
|
+
const flushActionMock = jest.fn().mockReturnValue('action');
|
|
307
|
+
await getTimerListWithFlush(dispatchMock, {
|
|
308
|
+
flushRtcTimerList: flushActionMock
|
|
309
|
+
});
|
|
310
|
+
expect(flushActionMock).toHaveBeenCalledWith([{
|
|
311
|
+
timerId: '5',
|
|
312
|
+
time: '10:10',
|
|
313
|
+
loops: '0101010',
|
|
314
|
+
status: false,
|
|
315
|
+
dps: {
|
|
316
|
+
brightness: 10
|
|
317
|
+
},
|
|
318
|
+
aliasName: '',
|
|
319
|
+
isAppPush: false
|
|
320
|
+
}]);
|
|
321
|
+
expect(dispatchMock).toHaveBeenCalledWith('action');
|
|
322
|
+
});
|
|
323
|
+
});
|
|
216
324
|
});
|
|
@@ -77,30 +77,27 @@ export const useScheduleInit = props => {
|
|
|
77
77
|
ScheduleLogger.info('useScheduleInit registerGroupChange groupId: ', props.groupId);
|
|
78
78
|
registerGroupChange({
|
|
79
79
|
groupIdList: [props.groupId],
|
|
80
|
-
success(
|
|
81
|
-
|
|
82
|
-
// 监听dp值变化 并触发
|
|
83
|
-
|
|
84
|
-
if (_isInit) {
|
|
85
|
-
ScheduleLogger.warn('useSupport: support is already initialized');
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
onGroupDpDataChangeEvent(res => {
|
|
89
|
-
if (!(res !== null && res !== void 0 && res.dps)) {
|
|
90
|
-
ScheduleLogger.warn(`useScheduleInit fail ${DP_CHANGE_EVENT_KEY}`, res);
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
ScheduleLogger.info(`useScheduleInit ${DP_CHANGE_EVENT_KEY}`, res);
|
|
94
|
-
emitter.emit(DP_CHANGE_EVENT_KEY, {
|
|
95
|
-
deviceId: res.devId || res.deviceId || res.groupId,
|
|
96
|
-
dps: res === null || res === void 0 ? void 0 : res.dps
|
|
97
|
-
});
|
|
98
|
-
});
|
|
80
|
+
success() {
|
|
81
|
+
// 安卓下有问题,不会触发到这里,所以 onGroupDpDataChangeEvent 挪到外面去了
|
|
99
82
|
},
|
|
100
83
|
fail(err) {
|
|
101
84
|
ScheduleLogger.error('useScheduleInit registerGroupChange fail', err);
|
|
102
85
|
}
|
|
103
86
|
});
|
|
87
|
+
ScheduleLogger.info('useScheduleInit registerGroupChange success: ', res);
|
|
88
|
+
// 监听dp值变化 并触发
|
|
89
|
+
|
|
90
|
+
onGroupDpDataChangeEvent(res => {
|
|
91
|
+
if (!(res !== null && res !== void 0 && res.dps)) {
|
|
92
|
+
ScheduleLogger.warn(`useScheduleInit fail ${DP_CHANGE_EVENT_KEY}`, res);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
ScheduleLogger.info(`useScheduleInit ${DP_CHANGE_EVENT_KEY}`, res);
|
|
96
|
+
emitter.emit(DP_CHANGE_EVENT_KEY, {
|
|
97
|
+
deviceId: res.devId || res.deviceId || res.groupId,
|
|
98
|
+
dps: res === null || res === void 0 ? void 0 : res.dps
|
|
99
|
+
});
|
|
100
|
+
});
|
|
104
101
|
} else if (props.devId) {
|
|
105
102
|
ScheduleLogger.info('useScheduleInit registerDeviceListListener devId: ', props.devId);
|
|
106
103
|
registerDeviceListListener({
|
package/lib/types/rhythms.d.ts
CHANGED
|
@@ -45,24 +45,36 @@ interface IProps {
|
|
|
45
45
|
onSave: (data: Record<string, any>[]) => void;
|
|
46
46
|
onCancel: (data: Record<string, any>[]) => void;
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
type: ERhythm;
|
|
48
|
+
export type RhythmAction = {
|
|
49
|
+
type: ERhythm.UPDATE_RHYTHMS_MESSAGE;
|
|
50
|
+
payload: RhythmMessage;
|
|
51
|
+
} | {
|
|
52
|
+
type: ERhythm.UPDATE_RHYTHMS;
|
|
53
|
+
payload: TRhythmData[];
|
|
54
|
+
} | {
|
|
55
|
+
type: ERhythm.UPDATE_LOCAL_MESSAGE;
|
|
56
|
+
payload: LocalMessage;
|
|
57
|
+
} | {
|
|
58
|
+
type: ERhythm.UPDATE_UI;
|
|
59
|
+
payload: Partial<IState>;
|
|
60
|
+
} | {
|
|
61
|
+
type: ERhythm.INIT_SYSTEM_INFO;
|
|
50
62
|
payload: any;
|
|
51
|
-
}
|
|
63
|
+
} | {
|
|
64
|
+
type: ERhythm.UPDATE_DP;
|
|
65
|
+
payload: Record<string, any>;
|
|
66
|
+
};
|
|
52
67
|
export interface IContext {
|
|
53
68
|
state: IState;
|
|
54
|
-
dispatch: Dispatch<
|
|
55
|
-
type: string;
|
|
56
|
-
payload?: Partial<IState>;
|
|
57
|
-
}>;
|
|
69
|
+
dispatch: Dispatch<RhythmAction>;
|
|
58
70
|
props: IProps;
|
|
59
71
|
actions?: {
|
|
60
|
-
updateRhythmsMessage: (data: RhythmMessage) =>
|
|
61
|
-
updateRhythms: (data: TRhythmData[]) =>
|
|
62
|
-
updateLocalMessage: (data: LocalMessage) =>
|
|
63
|
-
updateUI: (data:
|
|
64
|
-
initSystemInfo: (data: any) =>
|
|
65
|
-
updateDp: (data: any) =>
|
|
72
|
+
updateRhythmsMessage: (data: RhythmMessage) => RhythmAction;
|
|
73
|
+
updateRhythms: (data: TRhythmData[]) => RhythmAction;
|
|
74
|
+
updateLocalMessage: (data: LocalMessage) => RhythmAction;
|
|
75
|
+
updateUI: (data: Partial<IState>) => RhythmAction;
|
|
76
|
+
initSystemInfo: (data: any) => RhythmAction;
|
|
77
|
+
updateDp: (data: Record<string, any>) => RhythmAction;
|
|
66
78
|
};
|
|
67
79
|
}
|
|
68
80
|
export interface IState {
|
|
@@ -49,7 +49,10 @@ export const navigateToSchedule = (params, data, eventsMap) => {
|
|
|
49
49
|
groupId
|
|
50
50
|
} = params;
|
|
51
51
|
if (!deviceId && !groupId) {
|
|
52
|
-
throw new Error(
|
|
52
|
+
throw new Error(JSON.stringify({
|
|
53
|
+
params,
|
|
54
|
+
message: 'devId or groupId is required'
|
|
55
|
+
}));
|
|
53
56
|
}
|
|
54
57
|
let scheduleUrl = `${SCHEDULE_FUNCTIONAL_URL}?deviceId=${deviceId}`;
|
|
55
58
|
if (groupId) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/// <reference types="jest" />
|
|
2
|
+
declare const mockNavigateTo: jest.Mock<any, any, any>;
|
|
3
|
+
declare const mockUsePageInstance: jest.Mock<any, any, any>;
|
|
4
|
+
declare const mockLogger: {
|
|
5
|
+
debug: jest.Mock<any, any, any>;
|
|
6
|
+
error: jest.Mock<any, any, any>;
|
|
7
|
+
};
|
|
8
|
+
declare let navigateToSchedule: typeof import('../ScheduleDataSync').navigateToSchedule;
|
|
9
|
+
declare let navigateToMainApp: typeof import('../ScheduleDataSync').navigateToMainApp;
|
|
10
|
+
declare let functionalTransDataEventKey: typeof import('../ScheduleDataSync').functionalTransDataEventKey;
|
|
11
|
+
declare const loadModule: () => void;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
const mockNavigateTo = jest.fn();
|
|
2
|
+
const mockUsePageInstance = jest.fn();
|
|
3
|
+
const mockLogger = {
|
|
4
|
+
debug: jest.fn(),
|
|
5
|
+
error: jest.fn()
|
|
6
|
+
};
|
|
7
|
+
let navigateToSchedule;
|
|
8
|
+
let navigateToMainApp;
|
|
9
|
+
let functionalTransDataEventKey;
|
|
10
|
+
const loadModule = () => {
|
|
11
|
+
const moduleExports = require('../ScheduleDataSync');
|
|
12
|
+
({
|
|
13
|
+
navigateToSchedule,
|
|
14
|
+
navigateToMainApp,
|
|
15
|
+
functionalTransDataEventKey
|
|
16
|
+
} = moduleExports);
|
|
17
|
+
};
|
|
18
|
+
describe('ScheduleDataSync utilities', () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
jest.resetModules();
|
|
21
|
+
mockNavigateTo.mockReset();
|
|
22
|
+
mockUsePageInstance.mockReset();
|
|
23
|
+
mockLogger.debug.mockReset();
|
|
24
|
+
mockLogger.error.mockReset();
|
|
25
|
+
global.ty = {
|
|
26
|
+
presetFunctionalData: jest.fn()
|
|
27
|
+
};
|
|
28
|
+
jest.doMock('@ray-js/ray', () => ({
|
|
29
|
+
usePageInstance: mockUsePageInstance,
|
|
30
|
+
navigateTo: mockNavigateTo
|
|
31
|
+
}));
|
|
32
|
+
jest.doMock('../ScheduleLogger', () => ({
|
|
33
|
+
scheduleLogger: mockLogger
|
|
34
|
+
}));
|
|
35
|
+
loadModule();
|
|
36
|
+
});
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
delete global.ty;
|
|
39
|
+
});
|
|
40
|
+
describe('navigateToSchedule', () => {
|
|
41
|
+
it('throws when both deviceId and groupId are missing', () => {
|
|
42
|
+
expect(() => navigateToSchedule({
|
|
43
|
+
deviceId: '',
|
|
44
|
+
groupId: ''
|
|
45
|
+
}, {})).toThrow('devId or groupId is required');
|
|
46
|
+
});
|
|
47
|
+
it('presets data with deviceId and forwards events', () => {
|
|
48
|
+
var _navigateArgs$success, _navigateArgs$fail;
|
|
49
|
+
const data = {
|
|
50
|
+
forceSupport: {
|
|
51
|
+
countdown: true
|
|
52
|
+
},
|
|
53
|
+
i18nKeyMap: {
|
|
54
|
+
key: 'value'
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const eventsMap = {
|
|
58
|
+
timerDataChange: jest.fn()
|
|
59
|
+
};
|
|
60
|
+
navigateToSchedule({
|
|
61
|
+
deviceId: 'dev123',
|
|
62
|
+
groupId: ''
|
|
63
|
+
}, data, eventsMap);
|
|
64
|
+
expect(global.ty.presetFunctionalData).toHaveBeenCalledWith({
|
|
65
|
+
url: 'functional://LampScheduleSetFunction/home?deviceId=dev123',
|
|
66
|
+
data
|
|
67
|
+
});
|
|
68
|
+
expect(mockNavigateTo).toHaveBeenCalledTimes(1);
|
|
69
|
+
const navigateArgs = mockNavigateTo.mock.calls[0][0];
|
|
70
|
+
expect(navigateArgs.url).toBe('functional://LampScheduleSetFunction/home?deviceId=dev123');
|
|
71
|
+
expect(navigateArgs.events).toBe(eventsMap);
|
|
72
|
+
(_navigateArgs$success = navigateArgs.success) === null || _navigateArgs$success === void 0 || _navigateArgs$success.call(navigateArgs, 'ok');
|
|
73
|
+
expect(mockLogger.debug).toHaveBeenCalledWith(' success:', 'ok');
|
|
74
|
+
(_navigateArgs$fail = navigateArgs.fail) === null || _navigateArgs$fail === void 0 || _navigateArgs$fail.call(navigateArgs, 'err');
|
|
75
|
+
expect(mockLogger.error).toHaveBeenCalledWith('navigateToSchedule fail:', 'err');
|
|
76
|
+
});
|
|
77
|
+
it('uses groupId when provided', () => {
|
|
78
|
+
navigateToSchedule({
|
|
79
|
+
deviceId: 'dev123',
|
|
80
|
+
groupId: 'group456'
|
|
81
|
+
}, {});
|
|
82
|
+
expect(global.ty.presetFunctionalData).toHaveBeenCalledWith({
|
|
83
|
+
url: 'functional://LampScheduleSetFunction/home?groupId=group456',
|
|
84
|
+
data: {}
|
|
85
|
+
});
|
|
86
|
+
expect(mockNavigateTo).toHaveBeenCalledWith(expect.objectContaining({
|
|
87
|
+
url: 'functional://LampScheduleSetFunction/home?groupId=group456'
|
|
88
|
+
}));
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe('navigateToMainApp', () => {
|
|
92
|
+
it('throws when route does not start with slash', () => {
|
|
93
|
+
expect(() => navigateToMainApp('pages/index')).toThrow('route must start with /');
|
|
94
|
+
});
|
|
95
|
+
it('navigates with query params and triggers callback', () => {
|
|
96
|
+
const callback = jest.fn();
|
|
97
|
+
mockNavigateTo.mockImplementation(_ref => {
|
|
98
|
+
var _events$functionalTra;
|
|
99
|
+
let {
|
|
100
|
+
events,
|
|
101
|
+
success
|
|
102
|
+
} = _ref;
|
|
103
|
+
(_events$functionalTra = events[functionalTransDataEventKey]) === null || _events$functionalTra === void 0 || _events$functionalTra.call(events, {
|
|
104
|
+
dps: {
|
|
105
|
+
switch_led: true
|
|
106
|
+
},
|
|
107
|
+
foo: 'bar'
|
|
108
|
+
});
|
|
109
|
+
success === null || success === void 0 || success('success-result');
|
|
110
|
+
});
|
|
111
|
+
navigateToMainApp('/pages/index', {
|
|
112
|
+
foo: '1',
|
|
113
|
+
bar: '2'
|
|
114
|
+
}, callback);
|
|
115
|
+
expect(mockNavigateTo).toHaveBeenCalledTimes(1);
|
|
116
|
+
const navigateArgs = mockNavigateTo.mock.calls[0][0];
|
|
117
|
+
expect(navigateArgs.url).toBe('/pages/index?foo=1&bar=2&');
|
|
118
|
+
expect(callback).toHaveBeenCalledWith({
|
|
119
|
+
dps: {
|
|
120
|
+
switch_led: true
|
|
121
|
+
},
|
|
122
|
+
foo: 'bar'
|
|
123
|
+
});
|
|
124
|
+
expect(mockLogger.debug).toHaveBeenCalledWith('navigateToMainApp success: ', 'success-result');
|
|
125
|
+
});
|
|
126
|
+
it('logs error when navigation fails', () => {
|
|
127
|
+
mockNavigateTo.mockImplementation(_ref2 => {
|
|
128
|
+
let {
|
|
129
|
+
fail
|
|
130
|
+
} = _ref2;
|
|
131
|
+
return fail === null || fail === void 0 ? void 0 : fail('oops');
|
|
132
|
+
});
|
|
133
|
+
navigateToMainApp('/pages/index');
|
|
134
|
+
expect(mockLogger.error).toHaveBeenCalledWith('navigateToMainApp fail:', 'oops');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import emitt, { emitter } from '../ScheduleEmit';
|
|
2
|
+
describe('ScheduleEmit', () => {
|
|
3
|
+
it('registers and emits events to handlers', () => {
|
|
4
|
+
const localEmitter = emitt();
|
|
5
|
+
const handler = jest.fn();
|
|
6
|
+
localEmitter.on('event', handler);
|
|
7
|
+
localEmitter.emit('event', 1, 2);
|
|
8
|
+
expect(handler).toHaveBeenCalledWith(1, 2);
|
|
9
|
+
});
|
|
10
|
+
it('supports removing handlers', () => {
|
|
11
|
+
const localEmitter = emitt();
|
|
12
|
+
const handler = jest.fn();
|
|
13
|
+
localEmitter.on('event', handler);
|
|
14
|
+
localEmitter.off('event', handler);
|
|
15
|
+
localEmitter.emit('event', 'data');
|
|
16
|
+
expect(handler).not.toHaveBeenCalled();
|
|
17
|
+
});
|
|
18
|
+
it('invokes wildcard handlers after specific ones', () => {
|
|
19
|
+
const localEmitter = emitt();
|
|
20
|
+
const eventHandler = jest.fn();
|
|
21
|
+
const wildcardHandler = jest.fn();
|
|
22
|
+
localEmitter.on('event', eventHandler);
|
|
23
|
+
localEmitter.on('*', wildcardHandler);
|
|
24
|
+
localEmitter.emit('event', 'payload');
|
|
25
|
+
expect(eventHandler).toHaveBeenCalledWith('payload');
|
|
26
|
+
expect(wildcardHandler).toHaveBeenCalledWith('event', 'payload');
|
|
27
|
+
});
|
|
28
|
+
it('global emitter works as singleton', () => {
|
|
29
|
+
const handler = jest.fn();
|
|
30
|
+
emitter.on('global', handler);
|
|
31
|
+
emitter.emit('global', 123);
|
|
32
|
+
expect(handler).toHaveBeenCalledWith(123);
|
|
33
|
+
emitter.off('global', handler);
|
|
34
|
+
});
|
|
35
|
+
});
|