@eohjsc/react-native-smart-city 0.7.27 → 0.7.31

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 (71) hide show
  1. package/index.js +2 -0
  2. package/package.json +2 -1
  3. package/src/commons/Dashboard/MyDashboardDevice/__test__/index.test.js +68 -0
  4. package/src/commons/Dashboard/MyDashboardDevice/index.js +46 -11
  5. package/src/commons/Dashboard/MyUnit/__test__/MyUnit.test.js +43 -11
  6. package/src/commons/Dashboard/MyUnit/index.js +40 -32
  7. package/src/commons/ModalAlert/index.js +51 -0
  8. package/src/commons/ModalAlert/styles.js +54 -0
  9. package/src/commons/SubUnit/ShortDetail.js +20 -4
  10. package/src/commons/SubUnit/__test__/ShortDetail.test.js +46 -1
  11. package/src/configs/API.js +8 -0
  12. package/src/configs/AccessibilityLabel.js +3 -0
  13. package/src/configs/Constants.js +7 -0
  14. package/src/configs/SCConfig.js +6 -0
  15. package/src/context/SCContext.tsx +12 -1
  16. package/src/context/SCStore.ts +14 -0
  17. package/src/context/actionType.ts +10 -0
  18. package/src/context/mockStore.ts +30 -1
  19. package/src/context/reducer.ts +35 -0
  20. package/src/hooks/IoT/useRemoteControl.js +4 -1
  21. package/src/hooks/IoT/useWatchSharedChips.js +130 -0
  22. package/src/hooks/Review/__test__/useInAppReview.test.js +99 -0
  23. package/src/hooks/Review/useInAppReview.js +70 -0
  24. package/src/hooks/useMqtt.js +78 -27
  25. package/src/iot/Monitor.js +149 -26
  26. package/src/iot/UpdateStates.js +60 -0
  27. package/src/iot/mqtt.js +177 -22
  28. package/src/navigations/UnitStack.js +16 -0
  29. package/src/screens/ActivityLog/ItemLog.js +1 -0
  30. package/src/screens/AddNewGateway/RenameNewDevices.js +5 -0
  31. package/src/screens/AddNewGateway/__test__/RenameNewDevices.test.js +18 -0
  32. package/src/screens/Automate/AddNewAction/ReceiverSelect.js +210 -0
  33. package/src/screens/Automate/AddNewAction/SetupScriptEmail.js +1 -1
  34. package/src/screens/Automate/AddNewAction/SetupScriptNotify.js +18 -28
  35. package/src/screens/Automate/AddNewAction/SetupScriptReceiverEmail.js +22 -129
  36. package/src/screens/Automate/AddNewAction/SetupScriptReceiverNotify.js +59 -0
  37. package/src/screens/Automate/AddNewAction/SetupScriptReceiverSms.js +22 -129
  38. package/src/screens/Automate/AddNewAction/SetupScriptSms.js +1 -1
  39. package/src/screens/Automate/AddNewAction/Styles/{SetupScriptReceiverEmailStyles.js → ReceiverSelectStyles.js} +18 -1
  40. package/src/screens/Automate/AddNewAction/__test__/SetupScriptNotify.test.js +16 -33
  41. package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverEmail.test.js +10 -8
  42. package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverNotify.test.js +217 -0
  43. package/src/screens/Automate/AddNewAction/__test__/SetupScriptReceiverSms.test.js +10 -8
  44. package/src/screens/Automate/Components/InputName.js +5 -1
  45. package/src/screens/Automate/EditActionsList/UpdateReceiverEmailScript.js +4 -3
  46. package/src/screens/Automate/EditActionsList/UpdateReceiverSmsScript.js +5 -4
  47. package/src/screens/Automate/OneTap/__test__/AddNewOneTap.test.js +18 -0
  48. package/src/screens/Automate/ScriptDetail/Styles/indexStyles.js +1 -1
  49. package/src/screens/Automate/ScriptDetail/__test__/index.test.js +116 -2
  50. package/src/screens/Automate/ScriptDetail/index.js +47 -9
  51. package/src/screens/CreatePassword/__test__/index.test.js +133 -0
  52. package/src/screens/CreatePassword/index.js +134 -0
  53. package/src/screens/CreatePassword/styles.js +45 -0
  54. package/src/screens/Device/__test__/DeviceDetail-3rdparty.test.js +447 -0
  55. package/src/screens/Device/__test__/DeviceDetail-arduino.test.js +344 -0
  56. package/src/screens/Device/__test__/{mqttDetail.test.js → DeviceDetail-modbus.test.js} +287 -320
  57. package/src/screens/Device/__test__/DeviceDetail-zigbee.test.js +451 -0
  58. package/src/screens/Device/__test__/DeviceDetail.test.js +502 -0
  59. package/src/screens/Device/__test__/detail.test.js +61 -3
  60. package/src/screens/Device/__test__/sensorDisplayItem.test.js +28 -3
  61. package/src/screens/Device/detail.js +14 -6
  62. package/src/screens/Device/hooks/useDeviceWatchConfigControl.js +3 -2
  63. package/src/screens/EnterPassword/__test__/EnterPassword.test.js +76 -1
  64. package/src/screens/EnterPassword/index.js +34 -4
  65. package/src/screens/EnterPassword/styles.js +1 -1
  66. package/src/utils/FactoryGateway.js +597 -0
  67. package/src/utils/I18n/translations/en.js +11 -0
  68. package/src/utils/I18n/translations/vi.js +11 -0
  69. package/src/utils/Route/index.js +3 -1
  70. package/src/utils/Validation.js +5 -0
  71. package/src/utils/store.js +5 -0
@@ -0,0 +1,217 @@
1
+ import { useNavigation, useRoute } from '@react-navigation/native';
2
+ import { FlatList } from 'react-native';
3
+ import MockAdapter from 'axios-mock-adapter';
4
+ import React from 'react';
5
+ import renderer, { act } from 'react-test-renderer';
6
+ import BottomButtonView from '../../../../commons/BottomButtonView';
7
+ import API from '../../../../configs/API';
8
+ import { SCProvider } from '../../../../context';
9
+ import { mockSCStore } from '../../../../context/mockStore';
10
+ import { Search } from '../../../../commons/DevMode';
11
+ import api from '../../../../utils/Apis/axios';
12
+ import Routes from '../../../../utils/Route';
13
+ import { getTranslate } from '../../../../utils/I18n';
14
+ import { ToastBottomHelper } from '../../../../utils/Utils';
15
+ import ReceiverSelect from '../ReceiverSelect';
16
+ import SetupScriptReceiverNotify from '../SetupScriptReceiverNotify';
17
+ import CheckBox from '@react-native-community/checkbox';
18
+
19
+ const mock = new MockAdapter(api.axiosInstance);
20
+
21
+ const wrapComponent = (route) => (
22
+ <SCProvider initState={mockSCStore({})}>
23
+ <SetupScriptReceiverNotify route={route} />
24
+ </SCProvider>
25
+ );
26
+
27
+ describe('Test SetupScriptReceiverNotify', () => {
28
+ const mockedNavigate = useNavigation().navigate;
29
+ const spyToastSuccess = jest.spyOn(ToastBottomHelper, 'success');
30
+ const spyToastError = jest.spyOn(ToastBottomHelper, 'error');
31
+
32
+ let tree;
33
+ const route = {
34
+ params: {
35
+ unitId: 1,
36
+ automateId: 1,
37
+ automate: {
38
+ id: 1,
39
+ sensor_id: 1,
40
+ },
41
+ closeScreen: Routes.ScriptDetail,
42
+ formData: { title: 'title', message: 'message' },
43
+ },
44
+ };
45
+
46
+ const listUser = [
47
+ {
48
+ id: 1,
49
+ avatar: 'https://image.com',
50
+ email: 'user1@eoh.io',
51
+ name: 'User 1',
52
+ phone_number: null,
53
+ },
54
+ {
55
+ id: 2,
56
+ avatar: null,
57
+ email: null,
58
+ name: 'User 2',
59
+ phone_number: '090xxx',
60
+ share_id: 3,
61
+ },
62
+ ];
63
+ beforeEach(() => {
64
+ mockedNavigate.mockClear();
65
+ useRoute.mockImplementation(() => route);
66
+ spyToastSuccess.mockClear();
67
+ spyToastError.mockClear();
68
+ });
69
+
70
+ it('test create script notify success', async () => {
71
+ mock.onGet(API.SHARE.UNITS_MEMBERS(1)).reply(200, listUser);
72
+ mock.onPost(API.AUTOMATE.ADD_SCRIPT_NOTIFY(1)).reply(200);
73
+ await act(async () => {
74
+ tree = await renderer.create(wrapComponent(route));
75
+ });
76
+
77
+ const instance = tree.root;
78
+ let checkboxs = instance.findAllByType(CheckBox);
79
+ expect(checkboxs).toHaveLength(3);
80
+ expect(checkboxs[0].props.value).toBeFalsy(); // Select All
81
+ expect(checkboxs[1].props.disabled).toBeFalsy();
82
+ expect(checkboxs[2].props.disabled).toBeFalsy();
83
+
84
+ const receiverSelect = instance.findByType(ReceiverSelect);
85
+ expect(receiverSelect.props.listUser).toEqual([]);
86
+
87
+ await act(async () => {
88
+ checkboxs[1].props.onValueChange(true);
89
+ });
90
+ expect(checkboxs[0].props.value).toBeFalsy();
91
+ expect(receiverSelect.props.listUser).toEqual([1]);
92
+
93
+ checkboxs = instance.findAllByType(CheckBox);
94
+ await act(async () => {
95
+ checkboxs[2].props.onValueChange(true);
96
+ });
97
+ expect(checkboxs[0].props.value).toBeTruthy();
98
+
99
+ checkboxs = instance.findAllByType(CheckBox);
100
+ await act(async () => {
101
+ checkboxs[2].props.onValueChange(false);
102
+ });
103
+ expect(checkboxs[0].props.value).toBeFalsy();
104
+
105
+ const button = instance.findByType(BottomButtonView);
106
+ await act(async () => {
107
+ button.props.onPressMain();
108
+ });
109
+ expect(mock.history.post).toHaveLength(1);
110
+ expect(mock.history.post[0].url).toEqual(API.AUTOMATE.ADD_SCRIPT_NOTIFY(1));
111
+ expect(mock.history.post[0].data).toEqual(
112
+ JSON.stringify({
113
+ title: 'title',
114
+ message: 'message',
115
+ receiver: [1],
116
+ })
117
+ );
118
+ expect(spyToastSuccess).toHaveBeenCalledWith(
119
+ getTranslate('en', 'text_done')
120
+ );
121
+ });
122
+
123
+ it('test filter select all and deselect all', async () => {
124
+ mock.onGet(API.SHARE.UNITS_MEMBERS(1)).reply(200, listUser);
125
+ await act(async () => {
126
+ tree = await renderer.create(wrapComponent(route));
127
+ });
128
+
129
+ const instance = tree.root;
130
+ let checkboxs = instance.findAllByType(CheckBox);
131
+ expect(checkboxs).toHaveLength(3);
132
+ expect(checkboxs[0].props.value).toBeFalsy(); // Select All
133
+ expect(checkboxs[1].props.disabled).toBeFalsy();
134
+ expect(checkboxs[2].props.disabled).toBeFalsy();
135
+
136
+ const flatList = instance.findByType(FlatList);
137
+ expect(flatList.props.data).toEqual([
138
+ {
139
+ ...listUser[0],
140
+ label: null,
141
+ invalidLabel: getTranslate('en', 'no_phone_number'),
142
+ },
143
+ {
144
+ ...listUser[1],
145
+ label: '090xxx',
146
+ invalidLabel: getTranslate('en', 'no_phone_number'),
147
+ },
148
+ ]);
149
+
150
+ const search = instance.findByType(Search);
151
+ await act(async () => {
152
+ search.props.onSearch('User 1');
153
+ });
154
+ expect(flatList.props.data).toEqual([
155
+ {
156
+ ...listUser[0],
157
+ label: null,
158
+ invalidLabel: getTranslate('en', 'no_phone_number'),
159
+ },
160
+ ]);
161
+
162
+ await act(async () => {
163
+ search.props.onSearch('090');
164
+ });
165
+ expect(flatList.props.data).toEqual([
166
+ {
167
+ ...listUser[1],
168
+ label: '090xxx',
169
+ invalidLabel: getTranslate('en', 'no_phone_number'),
170
+ },
171
+ ]);
172
+
173
+ await act(async () => {
174
+ search.props.onSearch('');
175
+ });
176
+ await act(async () => {
177
+ checkboxs[0].props.onValueChange(true); // Select All
178
+ });
179
+ checkboxs = instance.findAllByType(CheckBox);
180
+ expect(checkboxs[0].props.value).toBeTruthy();
181
+ expect(checkboxs[1].props.value).toBeTruthy();
182
+ expect(checkboxs[2].props.value).toBeTruthy();
183
+
184
+ await act(async () => {
185
+ checkboxs[0].props.onValueChange(false); // Deselect All
186
+ });
187
+ checkboxs = instance.findAllByType(CheckBox);
188
+ expect(checkboxs[0].props.value).toBeFalsy();
189
+ expect(checkboxs[1].props.value).toBeFalsy();
190
+ expect(checkboxs[2].props.value).toBeFalsy();
191
+ });
192
+
193
+ it('test create script notify fail', async () => {
194
+ mock.onGet(API.SHARE.UNITS_MEMBERS(1)).reply(200, listUser);
195
+ mock.onPost(API.AUTOMATE.ADD_SCRIPT_NOTIFY(1)).reply(400);
196
+ await act(async () => {
197
+ tree = await renderer.create(wrapComponent(route));
198
+ });
199
+ const instance = tree.root;
200
+ const checkboxs = instance.findAllByType(CheckBox);
201
+ expect(checkboxs).toHaveLength(3);
202
+ expect(checkboxs[0].props.value).toBeFalsy(); // Select All
203
+ expect(checkboxs[1].props.disabled).toBeFalsy();
204
+ expect(checkboxs[2].props.disabled).toBeFalsy();
205
+ await act(async () => {
206
+ checkboxs[1].props.onValueChange(true);
207
+ });
208
+
209
+ const button = instance.findByType(BottomButtonView);
210
+ await act(async () => {
211
+ button.props.onPressMain();
212
+ });
213
+ expect(spyToastError).toHaveBeenCalledWith(
214
+ getTranslate('en', 'error_please_try_later')
215
+ );
216
+ });
217
+ });
@@ -66,11 +66,12 @@ describe('Test SetupScriptReceiverSms', () => {
66
66
  });
67
67
  const instance = tree.root;
68
68
  const checkboxs = instance.findAllByType(CheckBox);
69
- expect(checkboxs).toHaveLength(2);
70
- expect(checkboxs[0].props.disabled).toBeFalsy();
71
- expect(checkboxs[1].props.disabled).toBeTruthy();
69
+ expect(checkboxs).toHaveLength(3);
70
+ expect(checkboxs[0].props.value).toBeFalsy(); // Select All
71
+ expect(checkboxs[1].props.disabled).toBeFalsy();
72
+ expect(checkboxs[2].props.disabled).toBeTruthy();
72
73
  await act(async () => {
73
- checkboxs[0].props.onValueChange(true);
74
+ checkboxs[1].props.onValueChange(true);
74
75
  });
75
76
 
76
77
  const button = instance.findByType(BottomButtonView);
@@ -89,11 +90,12 @@ describe('Test SetupScriptReceiverSms', () => {
89
90
  });
90
91
  const instance = tree.root;
91
92
  const checkboxs = instance.findAllByType(CheckBox);
92
- expect(checkboxs).toHaveLength(2);
93
- expect(checkboxs[0].props.disabled).toBeFalsy();
94
- expect(checkboxs[1].props.disabled).toBeTruthy();
93
+ expect(checkboxs).toHaveLength(3);
94
+ expect(checkboxs[0].props.value).toBeFalsy(); // Select All
95
+ expect(checkboxs[1].props.disabled).toBeFalsy();
96
+ expect(checkboxs[2].props.disabled).toBeTruthy();
95
97
  await act(async () => {
96
- checkboxs[0].props.onValueChange(true);
98
+ checkboxs[1].props.onValueChange(true);
97
99
  });
98
100
 
99
101
  const button = instance.findByType(BottomButtonView);
@@ -6,6 +6,7 @@ import { API } from '../../../configs';
6
6
  import { AccessibilityLabel } from '../../../configs/Constants';
7
7
  import _TextInput from '../../../commons/Form/TextInput';
8
8
  import styles from './InputNameStyles';
9
+ import useInAppReview from '../../../hooks/Review/useInAppReview';
9
10
  import { useTranslations } from '../../../hooks/Common/useTranslations';
10
11
  import { axiosPost } from '../../../utils/Apis/axios';
11
12
  import NewActionWrapper from '../../Automate/AddNewAction/NewActionWrapper';
@@ -18,6 +19,8 @@ const InputName = ({ title, placeholder }) => {
18
19
  const { navigate } = useNavigation();
19
20
  const [name, setName] = useState(automateName);
20
21
  const [processing, setProcessing] = useState(false);
22
+ const { allowInAppReview } = useInAppReview(false);
23
+
21
24
  const handleContinue = useCallback(async () => {
22
25
  if (processing) {
23
26
  /* istanbul ignore next */
@@ -37,6 +40,7 @@ const InputName = ({ title, placeholder }) => {
37
40
  );
38
41
 
39
42
  if (success) {
43
+ allowInAppReview();
40
44
  navigate({
41
45
  name: Routes.ScriptDetail,
42
46
  merge: true,
@@ -47,7 +51,7 @@ const InputName = ({ title, placeholder }) => {
47
51
  });
48
52
  }
49
53
  setProcessing(false);
50
- }, [processing, automate, name, navigate, closeScreen]);
54
+ }, [processing, automate, name, navigate, closeScreen, allowInAppReview]);
51
55
 
52
56
  return (
53
57
  <NewActionWrapper
@@ -8,7 +8,7 @@ import { useTranslations } from '../../../hooks/Common/useTranslations';
8
8
  import BottomButtonView from '../../../commons/BottomButtonView';
9
9
  import { axiosPut, axiosGet } from '../../../utils/Apis/axios';
10
10
  import { API, Colors } from '../../../configs';
11
- import { ToastBottomHelper } from '../../../utils/Utils';
11
+ import { ToastBottomHelper, shortEmailName } from '../../../utils/Utils';
12
12
  import Routes from '../../../utils/Route';
13
13
  import moment from 'moment';
14
14
  import CheckBox from '@react-native-community/checkbox';
@@ -110,8 +110,9 @@ const UpdateReceiverEmailScript = ({ route }) => {
110
110
  }, [share_id, id]);
111
111
 
112
112
  const firstWordsInName = useMemo(() => {
113
- return name.charAt();
114
- }, [name]);
113
+ const nameTemp = name || shortEmailName(email);
114
+ return nameTemp?.charAt() || '';
115
+ }, [email, name]);
115
116
 
116
117
  const circleColor = arrColor[index % arrColor.length];
117
118
 
@@ -8,7 +8,7 @@ import { useTranslations } from '../../../hooks/Common/useTranslations';
8
8
  import BottomButtonView from '../../../commons/BottomButtonView/index.js';
9
9
  import { axiosPut, axiosGet } from '../../../utils/Apis/axios.js';
10
10
  import { API, Colors } from '../../../configs/index.js';
11
- import { ToastBottomHelper } from '../../../utils/Utils.js';
11
+ import { ToastBottomHelper, shortEmailName } from '../../../utils/Utils.js';
12
12
  import Routes from '../../../utils/Route/index.js';
13
13
  import moment from 'moment';
14
14
  import CheckBox from '@react-native-community/checkbox';
@@ -96,7 +96,7 @@ const UpdateReceiverSmsScript = ({ route }) => {
96
96
  );
97
97
 
98
98
  const RowMember = memo(({ member, index, onValueChange }) => {
99
- const { id, name, avatar, share_id, phone_number } = member;
99
+ const { id, name, email, avatar, share_id, phone_number } = member;
100
100
  const [role, roleColor] = useMemo(() => {
101
101
  if (!share_id) {
102
102
  return [t('owner'), Colors.Primary];
@@ -108,8 +108,9 @@ const UpdateReceiverSmsScript = ({ route }) => {
108
108
  }, [share_id, id]);
109
109
 
110
110
  const firstWordsInName = useMemo(() => {
111
- return name.charAt();
112
- }, [name]);
111
+ const nameTemp = name || shortEmailName(email);
112
+ return nameTemp?.charAt() || '';
113
+ }, [email, name]);
113
114
 
114
115
  const circleColor = arrColor[index % arrColor.length];
115
116
 
@@ -12,6 +12,18 @@ import api from '../../../../utils/Apis/axios';
12
12
  import Routes from '../../../../utils/Route';
13
13
  import AddNewOneTap from '../index';
14
14
 
15
+ const mockAllowInAppReview = jest.fn();
16
+ const mockAskReview = jest.fn();
17
+ jest.mock('../../../../hooks/Review/useInAppReview', () => {
18
+ return {
19
+ __esModule: true,
20
+ default: jest.fn(() => ({
21
+ allowInAppReview: mockAllowInAppReview,
22
+ askReview: mockAskReview,
23
+ })),
24
+ };
25
+ });
26
+
15
27
  const wrapComponent = (route) => {
16
28
  useRoute.mockReturnValue(route);
17
29
  return (
@@ -28,6 +40,8 @@ describe('test OneTap', () => {
28
40
  const mockedNavigate = useNavigation().navigate;
29
41
  beforeEach(() => {
30
42
  mockedNavigate.mockClear();
43
+ mockAllowInAppReview.mockClear();
44
+ mockAskReview.mockClear();
31
45
  });
32
46
 
33
47
  it('create OneTap success', async () => {
@@ -91,6 +105,8 @@ describe('test OneTap', () => {
91
105
  },
92
106
  },
93
107
  });
108
+ expect(mockAllowInAppReview).toHaveBeenCalled();
109
+ expect(mockAskReview).not.toHaveBeenCalled();
94
110
  });
95
111
 
96
112
  it('create OneTap fail', async () => {
@@ -116,6 +132,8 @@ describe('test OneTap', () => {
116
132
  await item[0].props.onPress();
117
133
  });
118
134
  expect(global.mockedNavigate).not.toHaveBeenCalled();
135
+ expect(mockAllowInAppReview).not.toHaveBeenCalled();
136
+ expect(mockAskReview).not.toHaveBeenCalled();
119
137
  });
120
138
 
121
139
  it('test onClose have automateId', async () => {
@@ -53,7 +53,7 @@ export default StyleSheet.create({
53
53
  flexDirection: 'row',
54
54
  alignItems: 'center',
55
55
  justifyContent: 'space-between',
56
- marginBottom: 7,
56
+ marginBottom: 16,
57
57
  },
58
58
  editButton: {
59
59
  height: 40,
@@ -3,7 +3,7 @@ import { BackHandler, Platform } from 'react-native';
3
3
  import { create, act } from 'react-test-renderer';
4
4
  import Toast from 'react-native-toast-message';
5
5
  import { useNavigation } from '@react-navigation/native';
6
- import { Switch, TouchableOpacity } from 'react-native';
6
+ import { TouchableOpacity } from 'react-native';
7
7
  import MockAdapter from 'axios-mock-adapter';
8
8
 
9
9
  import { SCProvider } from '../../../../context';
@@ -92,6 +92,7 @@ describe('Test ScriptDetail', () => {
92
92
  script: {
93
93
  name: 'name',
94
94
  enable: true,
95
+ enabled_notifications: true,
95
96
  },
96
97
  },
97
98
  },
@@ -920,7 +921,9 @@ describe('Test ScriptDetail', () => {
920
921
  tree = await create(wrapComponent(route));
921
922
  });
922
923
  const instance = tree.root;
923
- const switchButton = instance.findByType(Switch);
924
+ const switchButton = instance.findByProps({
925
+ accessibilityLabel: AccessibilityLabel.SWITCH_ENABLE_SCRIPT,
926
+ });
924
927
  await act(async () => {
925
928
  await switchButton.props.onValueChange(false);
926
929
  });
@@ -939,6 +942,116 @@ describe('Test ScriptDetail', () => {
939
942
  el.type === TouchableOpacity
940
943
  );
941
944
  expect(buttonEditScript).toHaveLength(0);
945
+ expect(mock.history.post).toHaveLength(1);
946
+ expect(mock.history.post[0].url).toEqual(API.AUTOMATE.ENABLE_SCRIPT(1));
947
+ });
948
+
949
+ it('test press disable script false', async () => {
950
+ mock.onGet(API.AUTOMATE.SCRIPT_ITEMS(1)).reply(200, data);
951
+ mock.onPost(API.AUTOMATE.ENABLE_SCRIPT(1)).reply(400);
952
+ await act(async () => {
953
+ tree = await create(wrapComponent(route));
954
+ });
955
+ const instance = tree.root;
956
+ const switchButton = instance.findByProps({
957
+ accessibilityLabel: AccessibilityLabel.SWITCH_ENABLE_SCRIPT,
958
+ });
959
+ expect(switchButton.props.value).toBeTruthy();
960
+
961
+ await act(async () => {
962
+ await switchButton.props.onValueChange(false);
963
+ });
964
+ expect(switchButton.props.value).toBeTruthy();
965
+ const buttonAddScript = instance.findAll(
966
+ (el) =>
967
+ el.props.accessibilityLabel ===
968
+ AccessibilityLabel.BUTTON_ADD_SCRIPT_ACTION &&
969
+ el.type === TouchableOpacity
970
+ );
971
+ expect(buttonAddScript).toHaveLength(1);
972
+
973
+ const buttonEditScript = instance.findAll(
974
+ (el) =>
975
+ el.props.accessibilityLabel ===
976
+ AccessibilityLabel.BUTTON_EDIT_SCRIPT_ACTION &&
977
+ el.type === TouchableOpacity
978
+ );
979
+ expect(buttonEditScript).toHaveLength(1);
980
+ expect(mock.history.post).toHaveLength(1);
981
+ expect(mock.history.post[0].url).toEqual(API.AUTOMATE.ENABLE_SCRIPT(1));
982
+ });
983
+
984
+ it('test enable notifications for script', async () => {
985
+ mock.onGet(API.AUTOMATE.SCRIPT_ITEMS(1)).reply(200, data);
986
+ mock.onPost(API.AUTOMATE.ENABLE_NOTIFICATIONS(1)).reply(200);
987
+ await act(async () => {
988
+ tree = await create(wrapComponent(route));
989
+ });
990
+
991
+ const instance = tree.root;
992
+ const enableNotificationsSwitch = instance.findByProps({
993
+ accessibilityLabel: AccessibilityLabel.SWITCH_ENABLE_NOTIFICATIONS_SCRIPT,
994
+ });
995
+ expect(enableNotificationsSwitch.props.value).toBeTruthy();
996
+
997
+ await act(async () => {
998
+ await enableNotificationsSwitch.props.onValueChange(false);
999
+ });
1000
+ expect(enableNotificationsSwitch.props.value).toBeFalsy();
1001
+ expect(mock.history.post).toHaveLength(1);
1002
+ expect(mock.history.post[0].url).toEqual(
1003
+ API.AUTOMATE.ENABLE_NOTIFICATIONS(1)
1004
+ );
1005
+ });
1006
+
1007
+ it('test enable notifications for script fail', async () => {
1008
+ mock.onGet(API.AUTOMATE.SCRIPT_ITEMS(1)).reply(200, data);
1009
+ mock.onPost(API.AUTOMATE.ENABLE_NOTIFICATIONS(1)).reply(400);
1010
+ await act(async () => {
1011
+ tree = await create(wrapComponent(route));
1012
+ });
1013
+
1014
+ const instance = tree.root;
1015
+ const enableNotificationsSwitch = instance.findByProps({
1016
+ accessibilityLabel: AccessibilityLabel.SWITCH_ENABLE_NOTIFICATIONS_SCRIPT,
1017
+ });
1018
+ expect(enableNotificationsSwitch.props.value).toBeTruthy();
1019
+
1020
+ await act(async () => {
1021
+ await enableNotificationsSwitch.props.onValueChange(false);
1022
+ });
1023
+ expect(enableNotificationsSwitch.props.value).toBeTruthy();
1024
+ expect(mock.history.post).toHaveLength(1);
1025
+ expect(mock.history.post[0].url).toEqual(
1026
+ API.AUTOMATE.ENABLE_NOTIFICATIONS(1)
1027
+ );
1028
+ });
1029
+
1030
+ it('test hidden enable notifications switch when disable script', async () => {
1031
+ mock.onGet(API.AUTOMATE.SCRIPT_ITEMS(1)).reply(200, data);
1032
+ mock.onPost(API.AUTOMATE.ENABLE_SCRIPT(1)).reply(200, data);
1033
+ await act(async () => {
1034
+ tree = await create(wrapComponent(route));
1035
+ });
1036
+
1037
+ const instance = tree.root;
1038
+ const enableSwitch = instance.findByProps({
1039
+ accessibilityLabel: AccessibilityLabel.SWITCH_ENABLE_SCRIPT,
1040
+ });
1041
+ let enableNotificationsSwitchs = instance.findAllByProps({
1042
+ accessibilityLabel: AccessibilityLabel.SWITCH_ENABLE_NOTIFICATIONS_SCRIPT,
1043
+ });
1044
+ expect(enableSwitch.props.value).toBeTruthy();
1045
+ expect(enableNotificationsSwitchs).toHaveLength(1);
1046
+
1047
+ await act(async () => {
1048
+ await enableSwitch.props.onValueChange(false);
1049
+ });
1050
+ expect(enableSwitch.props.value).toBeFalsy();
1051
+ enableNotificationsSwitchs = instance.findAllByProps({
1052
+ accessibilityLabel: AccessibilityLabel.SWITCH_ENABLE_NOTIFICATIONS_SCRIPT,
1053
+ });
1054
+ expect(enableNotificationsSwitchs).toHaveLength(0);
942
1055
  });
943
1056
 
944
1057
  it('test press add action reach limit', async () => {
@@ -1068,6 +1181,7 @@ describe('Test ScriptDetail', () => {
1068
1181
  script: {
1069
1182
  name: 'name',
1070
1183
  enable: true,
1184
+ enabled_notifications: true,
1071
1185
  },
1072
1186
  };
1073
1187
  route.params = {
@@ -108,10 +108,14 @@ const ScriptDetail = ({ route }) => {
108
108
  chip_local_control,
109
109
  is_local_control,
110
110
  chip_id_local_control,
111
+ enabled_notifications,
111
112
  } = script || {};
112
113
 
113
114
  const [local_control, setLocalControl] = useState({});
114
115
  const [enableScript, setEnableScript] = useState(enable);
116
+ const [enableNotificationsScript, setEnableNotificationsScript] = useState(
117
+ enabled_notifications
118
+ );
115
119
  const [listMenuItemCondition, setListMenuItemCondition] = useState([]);
116
120
  const [isShowAddCondition, setIsShowAddCondition] = useState(false);
117
121
  const permissions = useBackendPermission();
@@ -289,6 +293,7 @@ const ScriptDetail = ({ route }) => {
289
293
  if (success) {
290
294
  setAutomate(automateData);
291
295
  setEnableScript(automateData.script.enable);
296
+ setEnableNotificationsScript(automateData.script.enabled_notifications);
292
297
  setNeedAllCondition(automateData.is_need_all_conditions);
293
298
  }
294
299
  }, [automateId]);
@@ -330,7 +335,6 @@ const ScriptDetail = ({ route }) => {
330
335
 
331
336
  const onChangeSwitch = useCallback(
332
337
  async (checked) => {
333
- setEnableScript(checked);
334
338
  const { success } = await axiosPost(
335
339
  API.AUTOMATE.ENABLE_SCRIPT(automateId),
336
340
  {
@@ -338,8 +342,24 @@ const ScriptDetail = ({ route }) => {
338
342
  }
339
343
  );
340
344
  if (success) {
341
- ToastBottomHelper.success(t('update_successfully'));
342
345
  setEnableScript(checked);
346
+ ToastBottomHelper.success(t('update_successfully'));
347
+ }
348
+ },
349
+ [automateId, t]
350
+ );
351
+
352
+ const onChangeNotificationsSwitch = useCallback(
353
+ async (checked) => {
354
+ const { success } = await axiosPost(
355
+ API.AUTOMATE.ENABLE_NOTIFICATIONS(automateId),
356
+ {
357
+ enable: checked,
358
+ }
359
+ );
360
+ if (success) {
361
+ setEnableNotificationsScript(checked);
362
+ ToastBottomHelper.success(t('update_successfully'));
343
363
  }
344
364
  },
345
365
  [automateId, t]
@@ -680,7 +700,25 @@ const ScriptDetail = ({ route }) => {
680
700
  <Text type="H3" semibold>
681
701
  {t('enable_this_script')}
682
702
  </Text>
683
- <Switch value={enableScript} onValueChange={onChangeSwitch} />
703
+ <Switch
704
+ value={enableScript}
705
+ onValueChange={onChangeSwitch}
706
+ accessibilityLabel={AccessibilityLabel.SWITCH_ENABLE_SCRIPT}
707
+ />
708
+ </View>
709
+ )}
710
+ {!!enableScript && (
711
+ <View style={styles.row}>
712
+ <Text type="H3" semibold>
713
+ {t('enable_notifications_for_this_script')}
714
+ </Text>
715
+ <Switch
716
+ value={enableNotificationsScript}
717
+ onValueChange={onChangeNotificationsSwitch}
718
+ accessibilityLabel={
719
+ AccessibilityLabel.SWITCH_ENABLE_NOTIFICATIONS_SCRIPT
720
+ }
721
+ />
684
722
  </View>
685
723
  )}
686
724
  {renderLocalControl}
@@ -955,7 +993,7 @@ const Item = ({ item, index, enableScript, t, local_control }) => {
955
993
  </View>
956
994
  );
957
995
  } else if (notify_script) {
958
- const { title, message, unit_name } = notify_script;
996
+ const { title, message, unit_name, str_receivers } = notify_script;
959
997
  return (
960
998
  <View style={styles.wrapItem}>
961
999
  <View style={styles.leftItem}>
@@ -978,7 +1016,7 @@ const Item = ({ item, index, enableScript, t, local_control }) => {
978
1016
  </View>
979
1017
  </View>
980
1018
  <Text numberOfLines={1} type="H4" color={color}>
981
- {unit_name}
1019
+ {`${unit_name}: ${str_receivers}`}
982
1020
  </Text>
983
1021
  </View>
984
1022
  </View>
@@ -1008,7 +1046,7 @@ const Item = ({ item, index, enableScript, t, local_control }) => {
1008
1046
  </View>
1009
1047
  );
1010
1048
  } else if (email_script) {
1011
- const { title, message, str_emails } = email_script;
1049
+ const { title, message, unit_name, str_emails } = email_script;
1012
1050
  return (
1013
1051
  <View style={styles.wrapItem}>
1014
1052
  <View style={styles.leftItem}>
@@ -1031,13 +1069,13 @@ const Item = ({ item, index, enableScript, t, local_control }) => {
1031
1069
  </View>
1032
1070
  </View>
1033
1071
  <Text numberOfLines={1} type="H4" color={color}>
1034
- {str_emails}
1072
+ {`${unit_name}: ${str_emails}`}
1035
1073
  </Text>
1036
1074
  </View>
1037
1075
  </View>
1038
1076
  );
1039
1077
  } else if (sms_script) {
1040
- const { message, str_phone_numbers } = sms_script;
1078
+ const { message, unit_name, str_phone_numbers } = sms_script;
1041
1079
  color = local_control.is_local_control ? color : Colors.Gray7;
1042
1080
  return (
1043
1081
  <View style={styles.wrapItem}>
@@ -1056,7 +1094,7 @@ const Item = ({ item, index, enableScript, t, local_control }) => {
1056
1094
  {message}
1057
1095
  </Text>
1058
1096
  <Text numberOfLines={1} type="H4" color={color}>
1059
- {str_phone_numbers}
1097
+ {`${unit_name}: ${str_phone_numbers}`}
1060
1098
  </Text>
1061
1099
  </View>
1062
1100
  </View>