@eohjsc/react-native-smart-city 0.3.32 → 0.3.35

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.
Files changed (104) hide show
  1. package/assets/images/AddNewDevice/add-gateway-icon.svg +13 -0
  2. package/assets/images/AddNewDevice/add-modbus-device-icon.svg +8 -0
  3. package/assets/images/AddNewDevice/add-wifi-device-icon.svg +4 -0
  4. package/assets/images/AddNewDevice/add-zigbee-device-icon.svg +4 -0
  5. package/assets/images/AddNewDevice/interact-smartphone-icon.svg +14 -0
  6. package/index.js +2 -0
  7. package/package.json +2 -1
  8. package/src/Images/SmartAccount/DienQuang.png +0 -0
  9. package/src/Images/SmartAccount/DienQuang@2x.png +0 -0
  10. package/src/Images/SmartAccount/DienQuang@3x.png +0 -0
  11. package/src/Images/SmartAccount/LG.png +0 -0
  12. package/src/Images/SmartAccount/LG@2x.png +0 -0
  13. package/src/Images/SmartAccount/LG@3x.png +0 -0
  14. package/src/Images/SmartAccount/Samsung.png +0 -0
  15. package/src/Images/SmartAccount/Samsung@2x.png +0 -0
  16. package/src/Images/SmartAccount/Samsung@3x.png +0 -0
  17. package/src/commons/Action/ItemQuickAction.js +18 -1
  18. package/src/commons/Action/__test__/ItemQuickAction.test.js +18 -2
  19. package/src/commons/ActionGroup/NumberUpDownActionTemplate.js +5 -2
  20. package/src/commons/ActionGroup/OnOffTemplate/index.js +28 -10
  21. package/src/commons/ActionGroup/OptionsDropdownActionTemplate.js +15 -8
  22. package/src/commons/ActionGroup/StatesGridActionTemplate.js +19 -5
  23. package/src/commons/ActionTemplate/OnOffButtonAction.js +14 -5
  24. package/src/commons/ActionTemplate/__test__/OnOffButtonAction.test.js +23 -2
  25. package/src/commons/ActionTemplate/__test__/index.test.js +2 -0
  26. package/src/commons/ActionTemplate/index.js +1 -0
  27. package/src/commons/Automate/ItemAutomate.js +10 -1
  28. package/src/commons/OneTapTemplate/NumberUpDownActionTemplate.js +1 -1
  29. package/src/commons/SelectSubUnit/index.js +18 -18
  30. package/src/commons/SelectSubUnit/styles.js +0 -3
  31. package/src/commons/SelectUnit/index.js +2 -3
  32. package/src/commons/SelectUnit/styles.js +1 -2
  33. package/src/commons/Tabbar/__test__/index.test.js +97 -0
  34. package/src/configs/API.js +10 -0
  35. package/src/configs/AccessibilityLabel.js +21 -0
  36. package/src/configs/Images.js +2 -0
  37. package/src/context/actionType.ts +3 -0
  38. package/src/context/reducer.ts +22 -0
  39. package/src/hoc/__test__/withRemoteControl.test.js +24 -0
  40. package/src/hooks/Common/__test__/useAndroidTranslucentStatusBar.test.js +55 -0
  41. package/src/hooks/Common/useDevicesStatus.js +19 -15
  42. package/src/hooks/__test__/useInitDeepLink.test.js +28 -0
  43. package/src/hooks/useReceiveNotifications.js +2 -4
  44. package/src/navigations/AddGatewayStack.js +5 -0
  45. package/src/navigations/SmartAccountStack.js +52 -0
  46. package/src/screens/AddCommon/SelectSubUnit.js +17 -13
  47. package/src/screens/AddCommon/SelectUnit.js +14 -7
  48. package/src/screens/AddCommon/__test__/SelectSubUnit.test.js +18 -11
  49. package/src/screens/AddCommon/__test__/SelectUnit.test.js +13 -6
  50. package/src/screens/AddNewAction/SelectAction.js +10 -1
  51. package/src/screens/AddNewAction/SetupSensor.js +6 -0
  52. package/src/screens/AddNewGateway/ConnectingWifiGuide.js +167 -0
  53. package/src/screens/AddNewGateway/ConnectingWifiGuideStyles.js +58 -0
  54. package/src/screens/AddNewGateway/PlugAndPlay/FirstWarning.js +0 -5
  55. package/src/screens/AddNewGateway/PlugAndPlay/__test__/FirstWarning.test.js +1 -1
  56. package/src/screens/AddNewGateway/ScanGatewayQR.js +30 -7
  57. package/src/screens/AddNewGateway/SelectDeviceType.js +138 -43
  58. package/src/screens/AddNewGateway/SelectDeviceTypeStyles.js +51 -0
  59. package/src/screens/AddNewGateway/SelectDeviceUnit.js +1 -1
  60. package/src/screens/AddNewGateway/ShareWifiPassword.js +6 -110
  61. package/src/screens/AddNewGateway/__test__/ConnectingWifiGuide.test.js +224 -0
  62. package/src/screens/AddNewGateway/__test__/ScanGatewayQR.test.js +6 -7
  63. package/src/screens/AddNewGateway/__test__/SelectDeviceType.test.js +46 -11
  64. package/src/screens/AddNewGateway/__test__/ShareWifiPassword.test.js +4 -143
  65. package/src/screens/EditActionsList/__tests__/index.test.js +120 -0
  66. package/src/screens/EditActionsList/index.js +15 -5
  67. package/src/screens/ScriptDetail/__test__/index.test.js +35 -34
  68. package/src/screens/ScriptDetail/index.js +19 -10
  69. package/src/screens/SharedUnit/__test__/ShareUnit.test.js +25 -0
  70. package/src/screens/SharedUnit/index.js +2 -0
  71. package/src/screens/Sharing/Components/{SensorItem.js → DeviceItem.js} +38 -28
  72. package/src/screens/Sharing/Components/Styles/{SensorItemStyles.js → DeviceItemStyles.js} +0 -0
  73. package/src/screens/Sharing/Components/__test__/{SensorItem.test.js → DeviceItem.test.js} +6 -6
  74. package/src/screens/Sharing/Components/index.js +2 -2
  75. package/src/screens/Sharing/SelectPermission.js +130 -119
  76. package/src/screens/Sharing/Styles/SelectPermissionStyles.js +2 -2
  77. package/src/screens/Sharing/__test__/SelectPermission.test.js +67 -14
  78. package/src/screens/SmartAccount/Connecting/index.js +171 -0
  79. package/src/screens/SmartAccount/Connecting/style.js +50 -0
  80. package/src/screens/SmartAccount/ListDevice/DeviceItem.js +45 -0
  81. package/src/screens/SmartAccount/ListDevice/__test__/DeviceItem.test.js +34 -0
  82. package/src/screens/SmartAccount/ListDevice/__test__/ListDevice.test.js +139 -0
  83. package/src/screens/SmartAccount/ListDevice/index.js +186 -0
  84. package/src/screens/SmartAccount/ListDevice/styles/DeviceItemStyles.js +70 -0
  85. package/src/screens/SmartAccount/ListDevice/styles/index.js +85 -0
  86. package/src/screens/SmartAccount/SuccessfullyConnected/DeviceItem.js +37 -0
  87. package/src/screens/SmartAccount/SuccessfullyConnected/DeviceItemStyles.js +49 -0
  88. package/src/screens/SmartAccount/SuccessfullyConnected/__test__/DeviceItem.test.js +65 -0
  89. package/src/screens/SmartAccount/SuccessfullyConnected/__test__/SuccessfullyConnected.test.js +88 -0
  90. package/src/screens/SmartAccount/SuccessfullyConnected/index.js +101 -0
  91. package/src/screens/SmartAccount/SuccessfullyConnected/styles.js +42 -0
  92. package/src/screens/SmartAccount/__test__/Connecting.test.js +86 -0
  93. package/src/screens/SmartAccount/__test__/SmartAccount.test.js +249 -0
  94. package/src/screens/SmartAccount/index.js +172 -0
  95. package/src/screens/SmartAccount/style.js +70 -0
  96. package/src/screens/SyncLGDevice/__test__/AddLGDevice.test.js +37 -10
  97. package/src/screens/Unit/AddMenu.js +4 -5
  98. package/src/screens/Unit/SmartAccount.js +8 -5
  99. package/src/screens/Unit/__test__/SmartAccount.test.js +8 -5
  100. package/src/utils/I18n/translations/en.json +27 -2
  101. package/src/utils/I18n/translations/vi.json +28 -2
  102. package/src/utils/Route/index.js +3 -0
  103. package/src/utils/Utils.js +6 -7
  104. package/src/utils/Validation.js +4 -0
@@ -4,8 +4,10 @@ import { act, create } from 'react-test-renderer';
4
4
  import SelectDeviceType from '../SelectDeviceType';
5
5
  import { SCProvider } from '../../../context';
6
6
  import { mockSCStore } from '../../../context/mockStore';
7
- import Button from '../../../commons/Button';
8
7
  import Routes from '../../../utils/Route';
8
+ import { TouchableOpacity } from 'react-native';
9
+ import AccessibilityLabel from '../../../configs/AccessibilityLabel';
10
+ import { ViewButtonBottom } from '../../../commons';
9
11
 
10
12
  const wrapComponent = (route) => (
11
13
  <SCProvider initState={mockSCStore({})}>
@@ -43,18 +45,42 @@ describe('Test select device type', () => {
43
45
  route = {
44
46
  params: {
45
47
  unitId: 1,
46
- stationId: 1,
48
+ unitName: 'Unit A',
47
49
  },
48
50
  };
49
51
  });
50
52
 
51
- test('render select type', async () => {
53
+ test('render select type without station', async () => {
54
+ await act(async () => {
55
+ tree = await create(wrapComponent(route));
56
+ });
57
+ const instance = tree.root;
58
+ const deviceTypeItems = instance.findAll(
59
+ (el) =>
60
+ el.type === TouchableOpacity &&
61
+ el.props.accessibilityLabel === AccessibilityLabel.ITEM_DEVICE_TYPE
62
+ );
63
+ expect(deviceTypeItems).toHaveLength(4);
64
+ });
65
+
66
+ test('render select type with station', async () => {
67
+ route = {
68
+ params: {
69
+ unitId: 1,
70
+ unitName: 'Unit A',
71
+ stationId: 1,
72
+ },
73
+ };
52
74
  await act(async () => {
53
75
  tree = await create(wrapComponent(route));
54
76
  });
55
77
  const instance = tree.root;
56
- const buttons = instance.findAllByType(Button);
57
- expect(buttons).toHaveLength(4);
78
+ const deviceTypeItems = instance.findAll(
79
+ (el) =>
80
+ el.type === TouchableOpacity &&
81
+ el.props.accessibilityLabel === AccessibilityLabel.ITEM_DEVICE_TYPE
82
+ );
83
+ expect(deviceTypeItems).toHaveLength(3);
58
84
  });
59
85
 
60
86
  const selectDeviceType = async (index) => {
@@ -62,9 +88,18 @@ describe('Test select device type', () => {
62
88
  tree = await create(wrapComponent(route));
63
89
  });
64
90
  const instance = tree.root;
65
- const buttons = instance.findAllByType(Button);
91
+ const deviceTypeItems = instance.findAll(
92
+ (el) =>
93
+ el.type === TouchableOpacity &&
94
+ el.props.accessibilityLabel === AccessibilityLabel.ITEM_DEVICE_TYPE
95
+ );
66
96
  await act(async () => {
67
- buttons[index].props.onPress();
97
+ deviceTypeItems[index].props.onPress();
98
+ });
99
+
100
+ const submitButton = instance.findByType(ViewButtonBottom);
101
+ await act(async () => {
102
+ submitButton.props.onRightClick();
68
103
  });
69
104
 
70
105
  expect(mockedNavigate).toBeCalled();
@@ -72,18 +107,18 @@ describe('Test select device type', () => {
72
107
 
73
108
  test('select modbus', async () => {
74
109
  await selectDeviceType(0);
75
- expect(mockedNavigate.mock.calls[0][0]).toEqual(Routes.SelectModbusGateway);
110
+ expect(mockedNavigate.mock.calls[0][0]).toEqual(Routes.ScanGatewayQR);
76
111
  });
77
112
  test('select zigbee', async () => {
78
113
  await selectDeviceType(1);
79
- expect(mockedNavigate.mock.calls[0][0]).toEqual(Routes.SelectZigbeeGateway);
114
+ expect(mockedNavigate.mock.calls[0][0]).toEqual(Routes.SelectDeviceSubUnit);
80
115
  });
81
116
  test('select gateway', async () => {
82
117
  await selectDeviceType(2);
83
- expect(mockedNavigate.mock.calls[0][0]).toEqual(Routes.ScanGatewayQR);
118
+ expect(mockedNavigate.mock.calls[0][0]).toEqual(Routes.SelectDeviceSubUnit);
84
119
  });
85
120
  test('select wifi', async () => {
86
121
  await selectDeviceType(3);
87
- expect(mockedNavigate.mock.calls[0][0]).toEqual(Routes.ScanWifiDeviceQR);
122
+ expect(mockedNavigate.mock.calls[0][0]).toEqual(Routes.SelectDeviceSubUnit);
88
123
  });
89
124
  });
@@ -1,16 +1,16 @@
1
1
  import React from 'react';
2
2
  import renderer, { act } from 'react-test-renderer';
3
+ import { Platform } from 'react-native';
4
+ import dgram from 'react-native-udp';
5
+ import Toast from 'react-native-toast-message';
6
+ import WifiManager from 'react-native-wifi-reborn';
3
7
 
4
8
  import ShareWifiPassword from '../ShareWifiPassword';
5
9
  import { SCProvider } from '../../../context';
6
10
  import { mockSCStore } from '../../../context/mockStore';
7
11
  import ButtonPopup from '../../../commons/ButtonPopup';
8
12
  import TextInputPassword from '../../../commons/Form/TextInputPassword';
9
- import { Platform, TouchableOpacity } from 'react-native';
10
- import WifiManager from 'react-native-wifi-reborn';
11
- import dgram from 'react-native-udp';
12
13
  import Routes from '../../../utils/Route';
13
- import Toast from 'react-native-toast-message';
14
14
 
15
15
  const mockedGoBack = jest.fn();
16
16
  const mockedNavigate = jest.fn();
@@ -55,145 +55,6 @@ describe('test share wifi password', () => {
55
55
  expect(passwordFields).toHaveLength(1);
56
56
  });
57
57
 
58
- test('auto connect to wifi prefix on ios', async () => {
59
- Platform.OS = 'ios';
60
- const route = { params: { prefix: 'robot' } };
61
- await act(async () => {
62
- tree = await renderer.create(wrapComponent(route));
63
- });
64
- expect(WifiManager.connectToProtectedSSIDPrefix).toBeCalled();
65
- expect(WifiManager.connectToProtectedSSIDPrefix.mock.calls[0][0]).toEqual(
66
- 'eoh.robot.'
67
- );
68
- const socket = dgram.createSocket();
69
- expect(socket.bind).toBeCalled();
70
- });
71
-
72
- test('fail connect to wifi prefix on ios', async () => {
73
- Platform.OS = 'ios';
74
- const route = { params: { prefix: 'robot' } };
75
- WifiManager.connectToProtectedSSIDPrefix.mockImplementationOnce(
76
- async () => {
77
- throw 'Cannot connect to protected prefix SSID';
78
- }
79
- );
80
- await act(async () => {
81
- tree = await renderer.create(wrapComponent(route));
82
- });
83
- const socket = dgram.createSocket();
84
- expect(socket.bind).not.toBeCalled();
85
- });
86
-
87
- test('auto connect to wifi android', async () => {
88
- Platform.OS = 'android';
89
- const route = { params: { prefix: 'robot' } };
90
- WifiManager.loadWifiList.mockImplementationOnce(async () => [
91
- { SSID: 'random-name' },
92
- { SSID: 'eoh.lite.xxx' },
93
- { SSID: 'eoh.robot.xxx' },
94
- ]);
95
- await act(async () => {
96
- tree = await renderer.create(wrapComponent(route));
97
- });
98
-
99
- expect(WifiManager.connectToProtectedSSID).toBeCalled();
100
- expect(WifiManager.connectToProtectedSSID.mock.calls[0][0]).toEqual(
101
- 'eoh.robot.xxx'
102
- );
103
- const socket = dgram.createSocket();
104
- expect(socket.bind).toBeCalled();
105
- });
106
-
107
- test('fail connect to wifi android', async () => {
108
- Platform.OS = 'android';
109
- const route = { params: { prefix: 'robot' } };
110
- WifiManager.reScanAndLoadWifiList.mockImplementationOnce(async () => [
111
- { SSID: 'random-name' },
112
- { SSID: 'eoh.lite.xxx' },
113
- { SSID: 'eoh.robot.xxx' },
114
- ]);
115
- WifiManager.connectToProtectedSSID.mockImplementationOnce(async () => {
116
- throw 'Cannot connect to wifi';
117
- });
118
- await act(async () => {
119
- tree = await renderer.create(wrapComponent(route));
120
- });
121
-
122
- const socket = dgram.createSocket();
123
- expect(socket.bind).not.toBeCalled();
124
- });
125
-
126
- test('fail load wifi list android', async () => {
127
- Platform.OS = 'android';
128
- const route = { params: { prefix: 'robot' } };
129
- WifiManager.reScanAndLoadWifiList.mockImplementationOnce(async () => {
130
- throw 'Cannot load wifi list to wifi';
131
- });
132
- await act(async () => {
133
- tree = await renderer.create(wrapComponent(route));
134
- });
135
-
136
- const socket = dgram.createSocket();
137
- expect(socket.bind).not.toBeCalled();
138
- });
139
-
140
- test('request wifi list from device', async () => {
141
- jest.useFakeTimers();
142
- Platform.OS = 'android';
143
- const route = { params: { prefix: 'robot' } };
144
- await act(async () => {
145
- tree = await renderer.create(wrapComponent(route));
146
- });
147
- jest.runOnlyPendingTimers();
148
- const socket = dgram.createSocket({});
149
- expect(socket.send).toBeCalled();
150
- const data = JSON.parse(socket.send.mock.calls[0][0]);
151
- expect(data.type).toEqual('scan');
152
- });
153
-
154
- test('got wifi list from device', async () => {
155
- Platform.OS = 'android';
156
- const route = { params: { prefix: 'robot' } };
157
- await act(async () => {
158
- tree = await renderer.create(wrapComponent(route));
159
- });
160
-
161
- const socket = dgram.createSocket({});
162
- expect(socket.on).toBeCalled();
163
- expect(socket.on.mock.calls[0][0]).toEqual('message');
164
-
165
- expect(
166
- tree.root.findAll((el) => el.type === TouchableOpacity)
167
- ).toHaveLength(4);
168
-
169
- await act(async () => {
170
- socket.on.mock.calls[0][1](
171
- JSON.stringify({
172
- wifi: [{ ssid: 'wifi 1' }, { ssid: 'wifi.2' }],
173
- })
174
- );
175
- });
176
- // todo Bang should test wifi display
177
- expect(socket.close).toBeCalled();
178
- });
179
-
180
- test('got error while request wifi list from device', async () => {
181
- Platform.OS = 'android';
182
- const route = { params: { prefix: 'robot' } };
183
- await act(async () => {
184
- tree = await renderer.create(wrapComponent(route));
185
- });
186
-
187
- const socket = dgram.createSocket({});
188
- expect(socket.on).toBeCalledTimes(2);
189
- expect(socket.on.mock.calls[1][0]).toEqual('error');
190
-
191
- await act(async () => {
192
- socket.on.mock.calls[1][1]();
193
- });
194
- expect(Toast.show).toBeCalled();
195
- });
196
-
197
58
  const sendPasswordToGateway = async () => {
198
59
  jest.useFakeTimers();
199
60
  Platform.OS = 'android';
@@ -0,0 +1,120 @@
1
+ import React from 'react';
2
+ import DraggableFlatList from 'react-native-draggable-flatlist';
3
+ import { create, act } from 'react-test-renderer';
4
+ import { SCProvider } from '../../../context';
5
+ import { mockSCStore } from '../../../context/mockStore';
6
+ import EditActionsList from '../index';
7
+ import { AccessibilityLabel } from '../../../configs/Constants';
8
+ import { TouchableOpacity } from 'react-native';
9
+ import api from '../../../utils/Apis/axios';
10
+ import MockAdapter from 'axios-mock-adapter';
11
+ import API from '../../../configs/API';
12
+
13
+ import { ModalBottom } from '../../../commons/Modal';
14
+
15
+ const mockSetdata = jest.fn();
16
+ const mockedNavigate = jest.fn();
17
+ const mockGoBack = jest.fn();
18
+
19
+ const wrapComponent = () => (
20
+ <SCProvider initState={mockSCStore({})}>
21
+ <EditActionsList />
22
+ </SCProvider>
23
+ );
24
+ const mock = new MockAdapter(api.axiosInstance);
25
+
26
+ jest.mock('@react-navigation/native', () => {
27
+ return {
28
+ ...jest.requireActual('@react-navigation/native'),
29
+ useRoute: jest.fn().mockReturnValue({
30
+ params: {
31
+ data: [{ id: 1, sensor_name: 'abc', station_name: 'abc' }],
32
+ id: 1,
33
+ setData: mockSetdata,
34
+ unit: { id: 1, name: 'unit' },
35
+ },
36
+ }),
37
+ useNavigation: () => ({
38
+ navigate: mockedNavigate,
39
+ goBack: mockGoBack,
40
+ }),
41
+ useIsFocused: () => true,
42
+ };
43
+ });
44
+
45
+ jest.mock('react-native-parsed-text', () => () => []);
46
+
47
+ describe('EditActionsList', () => {
48
+ let tree;
49
+
50
+ beforeEach(() => {
51
+ mockGoBack.mockClear();
52
+ });
53
+ test('render EditActionsList', async () => {
54
+ await act(async () => {
55
+ tree = await create(wrapComponent());
56
+ });
57
+ const instance = tree.root;
58
+ const draggableFlatList = instance.findAllByType(DraggableFlatList);
59
+ const modalBottom = instance.findAllByType(ModalBottom);
60
+ expect(draggableFlatList).toHaveLength(1);
61
+ expect(modalBottom).toHaveLength(1);
62
+ });
63
+ test('EditActionsList onPress cancel', async () => {
64
+ await act(async () => {
65
+ tree = await create(wrapComponent());
66
+ });
67
+ const instance = tree.root;
68
+ const buttonCancel = instance.find(
69
+ (el) =>
70
+ el.props.accessibilityLabel ===
71
+ AccessibilityLabel.BUTTON_CANCEL_EDIT_ACTION_LIST &&
72
+ el.type === TouchableOpacity
73
+ );
74
+ expect(buttonCancel).toBeDefined();
75
+ buttonCancel.props.onPress();
76
+ expect(mockGoBack).toHaveBeenCalled();
77
+ });
78
+ test('EditActionsList onPress save', async () => {
79
+ mock.onPut(API.AUTOMATE.ORDER_SCRIPT_ACTION(1)).reply(200);
80
+ await act(async () => {
81
+ tree = await create(wrapComponent());
82
+ });
83
+ const instance = tree.root;
84
+ const buttonSave = instance.find(
85
+ (el) =>
86
+ el.props.accessibilityLabel ===
87
+ AccessibilityLabel.BUTTON_SAVE_EDIT_ACTION_LIST &&
88
+ el.type === TouchableOpacity
89
+ );
90
+ expect(buttonSave).toBeDefined();
91
+ buttonSave.props.onPress();
92
+ expect(mock.history.put).toHaveLength(1);
93
+ });
94
+ test('EditActionsList onPress remove', async () => {
95
+ await act(async () => {
96
+ tree = await create(wrapComponent());
97
+ });
98
+ const instance = tree.root;
99
+ const buttonRemove = instance.find(
100
+ (el) =>
101
+ el.props.accessibilityLabel ===
102
+ AccessibilityLabel.BUTTON_REMOVE_EDIT_ACTION_LIST &&
103
+ el.type === TouchableOpacity
104
+ );
105
+ expect(buttonRemove).toBeDefined();
106
+ buttonRemove.props.onPress();
107
+ });
108
+ test('EditActionsList modal onPress remove', async () => {
109
+ await act(async () => {
110
+ tree = await create(wrapComponent());
111
+ });
112
+ const instance = tree.root;
113
+ const modalBottom = instance.findAllByType(ModalBottom);
114
+ modalBottom[0].props.onRemove();
115
+
116
+ mock.onDelete(API.AUTOMATE.DELETE_SCRIPT_ACTION(1, 1)).reply(200);
117
+ expect(mock.history.delete).toHaveLength(1);
118
+ modalBottom[0].props.onClose();
119
+ });
120
+ });
@@ -1,4 +1,4 @@
1
- import React, { useState, useCallback } from 'react';
1
+ import React, { memo, useState, useCallback } from 'react';
2
2
  import { View, TouchableOpacity } from 'react-native';
3
3
  import DraggableFlatList from 'react-native-draggable-flatlist';
4
4
  import { useNavigation, useRoute } from '@react-navigation/native';
@@ -15,8 +15,9 @@ import Close from '../../../assets/images/Close.svg';
15
15
  import { axiosDelete, axiosPut } from '../../utils/Apis/axios';
16
16
  import { ModalBottom } from '../../commons/Modal';
17
17
  import { ToastBottomHelper } from '../../utils/Utils';
18
+ import { AccessibilityLabel } from '../../configs/Constants';
18
19
 
19
- const EditActionsList = () => {
20
+ const EditActionsList = memo(() => {
20
21
  const t = useTranslations();
21
22
  const { goBack } = useNavigation();
22
23
  const { params = {} } = useRoute() || {};
@@ -115,6 +116,9 @@ const EditActionsList = () => {
115
116
  </Text>
116
117
  </View>
117
118
  <TouchableOpacity
119
+ accessibilityLabel={
120
+ AccessibilityLabel.BUTTON_REMOVE_EDIT_ACTION_LIST
121
+ }
118
122
  onPress={onPressRemove({
119
123
  id: item?.id,
120
124
  actionName: item?.sensor_name,
@@ -148,12 +152,18 @@ const EditActionsList = () => {
148
152
  />
149
153
  </View>
150
154
  <View style={styles.wrapBottom}>
151
- <TouchableOpacity onPress={onPressCancel}>
155
+ <TouchableOpacity
156
+ onPress={onPressCancel}
157
+ accessibilityLabel={AccessibilityLabel.BUTTON_CANCEL_EDIT_ACTION_LIST}
158
+ >
152
159
  <Text type="H4" hilight semibold>
153
160
  {t('cancel')}
154
161
  </Text>
155
162
  </TouchableOpacity>
156
- <TouchableOpacity onPress={onPressSave}>
163
+ <TouchableOpacity
164
+ onPress={onPressSave}
165
+ accessibilityLabel={AccessibilityLabel.BUTTON_SAVE_EDIT_ACTION_LIST}
166
+ >
157
167
  <Text type="H4" hilight semibold>
158
168
  {t('save')}
159
169
  </Text>
@@ -189,6 +199,6 @@ const EditActionsList = () => {
189
199
  </ModalBottom>
190
200
  </View>
191
201
  );
192
- };
202
+ });
193
203
 
194
204
  export default EditActionsList;
@@ -64,14 +64,16 @@ describe('Test ScriptDetail', () => {
64
64
  havePermission: true,
65
65
  automate: {
66
66
  author: 'Le Minh Tam',
67
- condition: '<',
68
- config: 'PM2.5 (Mr.Son Office)',
69
- config_id: 201,
70
- config_name: 'PM2.5 (Mr.Son Office)',
71
67
  id: 1009,
72
- sensor_id: 73,
73
68
  type: 'value_change',
74
- value: 35,
69
+ value_change: {
70
+ condition: '<',
71
+ config: 'PM2.5 (Mr.Son Office)',
72
+ config_id: 201,
73
+ config_name: 'PM2.5 (Mr.Son Office)',
74
+ sensor_id: 73,
75
+ value: 35,
76
+ },
75
77
  script: {
76
78
  id: 1,
77
79
  },
@@ -295,13 +297,11 @@ describe('Test ScriptDetail', () => {
295
297
  ...route.params,
296
298
  type: AUTOMATE_TYPE.VALUE_CHANGE,
297
299
  automate: {
298
- repeat: 'once',
299
- date_repeat: '2022-01-02',
300
- time_repeat: '09:10:00',
301
- weekday_repeat: ['1', '2', '4', '6'],
302
- config_name: 'Light Value',
303
- value: 3,
304
- condition: '>',
300
+ value_change: {
301
+ config_name: 'Light Value',
302
+ value: 3,
303
+ condition: '>',
304
+ },
305
305
  },
306
306
  };
307
307
  await act(() => {
@@ -312,6 +312,11 @@ describe('Test ScriptDetail', () => {
312
312
  expect(itemAutomate.props.textCondition).toEqual(
313
313
  'Light Value higher than 3'
314
314
  );
315
+
316
+ await act(async () => {
317
+ itemAutomate.findByType(TouchableOpacity).props.onPress();
318
+ });
319
+ expect(mockNavigate).toBeCalled();
315
320
  });
316
321
 
317
322
  test('Test render textCondition value change =', async () => {
@@ -319,13 +324,7 @@ describe('Test ScriptDetail', () => {
319
324
  ...route.params,
320
325
  type: AUTOMATE_TYPE.VALUE_CHANGE,
321
326
  automate: {
322
- repeat: 'once',
323
- date_repeat: '2022-01-02',
324
- time_repeat: '09:10:00',
325
- weekday_repeat: ['1', '2', '4', '6'],
326
- config_name: 'Light Value',
327
- value: 3,
328
- condition: '=',
327
+ value_change: { config_name: 'Light Value', value: 3, condition: '=' },
329
328
  },
330
329
  };
331
330
  await act(() => {
@@ -341,13 +340,11 @@ describe('Test ScriptDetail', () => {
341
340
  ...route.params,
342
341
  type: AUTOMATE_TYPE.VALUE_CHANGE,
343
342
  automate: {
344
- repeat: 'once',
345
- date_repeat: '2022-01-02',
346
- time_repeat: '09:10:00',
347
- weekday_repeat: ['1', '2', '4', '6'],
348
- config_name: 'Light Value',
349
- value: 3,
350
- condition: '<',
343
+ value_change: {
344
+ config_name: 'Light Value',
345
+ value: 3,
346
+ condition: '<',
347
+ },
351
348
  },
352
349
  };
353
350
  await act(() => {
@@ -365,9 +362,11 @@ describe('Test ScriptDetail', () => {
365
362
  ...route.params,
366
363
  type: AUTOMATE_TYPE.SCHEDULE,
367
364
  automate: {
368
- repeat: 'every_day',
369
- date_repeat: '2022-01-02',
370
- time_repeat: '19:00:00',
365
+ schedule: {
366
+ repeat: 'every_day',
367
+ date_repeat: '2022-01-02',
368
+ time_repeat: '19:00:00',
369
+ },
371
370
  },
372
371
  };
373
372
  await act(() => {
@@ -383,10 +382,12 @@ describe('Test ScriptDetail', () => {
383
382
  ...route.params,
384
383
  type: AUTOMATE_TYPE.SCHEDULE,
385
384
  automate: {
386
- repeat: 'every_week',
387
- date_repeat: '2022-01-02',
388
- time_repeat: '19:00:00',
389
- weekday_repeat: ['1', '2', '4', '6'],
385
+ schedule: {
386
+ repeat: 'every_week',
387
+ date_repeat: '2022-01-02',
388
+ time_repeat: '19:00:00',
389
+ weekday_repeat: ['1', '2', '4', '6'],
390
+ },
390
391
  },
391
392
  };
392
393
  await act(() => {
@@ -362,17 +362,9 @@ const ScriptDetail = ({ route }) => {
362
362
  const isHaveScriptActions = data?.length > 0;
363
363
 
364
364
  const textCondition = useMemo(() => {
365
- const {
366
- condition,
367
- config_name,
368
- value,
369
- repeat,
370
- date_repeat,
371
- time_repeat,
372
- weekday_repeat,
373
- sensor_type,
374
- } = automate;
375
365
  if (type === AUTOMATE_TYPE.VALUE_CHANGE) {
366
+ const { condition, config_name, value, sensor_type } =
367
+ automate?.value_change;
376
368
  const stateConditionData = STATE_VALUE_SENSOR_TYPES.find(
377
369
  (i) => i.type === sensor_type
378
370
  );
@@ -388,6 +380,8 @@ const ScriptDetail = ({ route }) => {
388
380
  }
389
381
  return `${config_name} ${t(text)} ${isNumberValue ? value : ''}`;
390
382
  } else if (type === AUTOMATE_TYPE.SCHEDULE) {
383
+ const { repeat, time_repeat, date_repeat, weekday_repeat } =
384
+ automate?.schedule;
391
385
  const time =
392
386
  time_repeat?.length >= 8
393
387
  ? time_repeat.substring(0, time_repeat?.length - 3)
@@ -424,6 +418,21 @@ const ScriptDetail = ({ route }) => {
424
418
  const textWeekday = sortWeekday.map((item) => weekday[item]).join(', ');
425
419
  return `${textWeekday} ${t('at')} ${time}`;
426
420
  }
421
+ } else if (type === AUTOMATE_TYPE.EVENT) {
422
+ const { action, end_device_name, config_name, sensor_type, value } =
423
+ automate?.event;
424
+ let textEvent = '';
425
+ if (config_name) {
426
+ const stateConditionData = STATE_VALUE_SENSOR_TYPES.find(
427
+ (i) => i.type === sensor_type
428
+ );
429
+ textEvent = `${config_name} - ${t(
430
+ stateConditionData.stateValue[value]
431
+ )}`;
432
+ } else {
433
+ textEvent = `${end_device_name} - ${action}`;
434
+ }
435
+ return textEvent;
427
436
  }
428
437
  return null;
429
438
  }, [t, automate, type]);
@@ -4,6 +4,12 @@ import SharedUnit from '../index';
4
4
  import Modal from 'react-native-modal';
5
5
  import { SCProvider } from '../../../context';
6
6
  import { mockSCStore } from '../../../context/mockStore';
7
+ import { AccessibilityLabel } from '../../../configs/Constants';
8
+ import MockAdapter from 'axios-mock-adapter';
9
+ import api from '../../../utils/Apis/axios';
10
+ import { API } from '../../../configs';
11
+
12
+ const mock = new MockAdapter(api.axiosInstance);
7
13
 
8
14
  const wrapComponent = () => (
9
15
  <SCProvider initState={mockSCStore({})}>
@@ -16,9 +22,11 @@ jest.mock('react-redux', () => ({
16
22
  useSelector: jest.fn(),
17
23
  }));
18
24
 
25
+ const mockSetSharedUnits = jest.fn();
19
26
  jest.mock('react', () => ({
20
27
  ...jest.requireActual('react'),
21
28
  useLayoutEffect: jest.fn(),
29
+ useState: jest.fn((init) => [init, mockSetSharedUnits]),
22
30
  }));
23
31
 
24
32
  jest.mock('react-native-onesignal', () => {
@@ -37,4 +45,21 @@ describe('test SharedUnit', () => {
37
45
  const modalShareds = instance.findAllByType(Modal);
38
46
  expect(modalShareds[0]).not.toBeUndefined();
39
47
  });
48
+
49
+ test('render touch item filter', async () => {
50
+ mock.onGet(API.UNIT.FILTER_SHARED_UNITS()).reply(200, [{}]);
51
+
52
+ await act(async () => {
53
+ tree = await create(wrapComponent());
54
+ });
55
+ const instance = tree.root;
56
+ const item = instance.findAll(
57
+ (el) =>
58
+ el.props.accessibilityLabel === AccessibilityLabel.FILTER_SHARING_ITEM
59
+ );
60
+ await act(async () => {
61
+ item[0].props.onPress();
62
+ });
63
+ expect(mockSetSharedUnits).toBeCalled();
64
+ });
40
65
  });
@@ -12,6 +12,7 @@ import {
12
12
  useBoolean,
13
13
  useBlockBackAndroid,
14
14
  } from '../../hooks/Common';
15
+ import { AccessibilityLabel } from '../../configs/Constants';
15
16
 
16
17
  import styles from './styles';
17
18
  import TabHeader from './TabHeader';
@@ -185,6 +186,7 @@ const Shared = () => {
185
186
  style={styles.row}
186
187
  onPress={() => onFilter(item.filter, item.textFilter)}
187
188
  key={`filter-${index}`}
189
+ accessibilityLabel={`${AccessibilityLabel.FILTER_SHARING_ITEM}`}
188
190
  >
189
191
  <Text
190
192
  style={