@eohjsc/react-native-smart-city 0.4.98 → 0.5.0

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 (33) hide show
  1. package/package.json +1 -1
  2. package/src/commons/ActionGroup/OnOffTemplate/index.js +3 -31
  3. package/src/commons/ActionGroup/SliderRangeTemplate.js +2 -2
  4. package/src/commons/ActionGroup/TerminalBoxTemplate.js +182 -0
  5. package/src/commons/ActionGroup/TerminalBoxTemplateStyle.js +57 -0
  6. package/src/commons/ActionGroup/TextBoxTemplate.js +29 -24
  7. package/src/commons/ActionGroup/TextBoxTemplateStyle.js +3 -0
  8. package/src/commons/ActionGroup/__test__/TerminalBoxTemplate.test.js +232 -0
  9. package/src/commons/ActionGroup/__test__/TextBoxTemplate.test.js +1 -1
  10. package/src/commons/ActionGroup/index.js +3 -1
  11. package/src/commons/Device/ConnectedViewHeader.js +1 -2
  12. package/src/commons/Device/PMSensor/PMSensorIndicator.js +3 -5
  13. package/src/commons/HeaderAni/index.js +1 -1
  14. package/src/commons/OneTapTemplate/TerminalBoxActionTemplate.js +87 -0
  15. package/src/commons/OneTapTemplate/TextBoxActionTemplate.js +2 -2
  16. package/src/commons/OneTapTemplate/__test__/TerminalBoxActionTemplate.test.js +118 -0
  17. package/src/commons/Sharing/WrapHeaderScrollable.js +1 -1
  18. package/src/commons/SubUnit/__test__/ShortDetail.test.js +2 -0
  19. package/src/commons/UnitSummary/ConfigHistoryChart/index.js +15 -16
  20. package/src/configs/API.js +1 -0
  21. package/src/configs/AccessibilityLabel.js +2 -0
  22. package/src/configs/Colors.js +1 -0
  23. package/src/hooks/IoT/__test__/useWatchConfigs.test.js +1 -0
  24. package/src/hooks/IoT/useWatchConfigs.js +3 -1
  25. package/src/iot/mqtt.js +23 -9
  26. package/src/screens/Automate/AddNewAction/ChooseAction.js +10 -0
  27. package/src/screens/Automate/ScriptDetail/index.js +2 -1
  28. package/src/screens/Device/__test__/mqttDetail.test.js +33 -20
  29. package/src/screens/Device/components/SensorDisplayItem.js +1 -0
  30. package/src/screens/Device/detail.js +25 -19
  31. package/src/screens/Device/hooks/useDeviceWatchConfigControl.js +4 -10
  32. package/src/utils/I18n/translations/en.js +1 -1
  33. package/src/utils/I18n/translations/vi.js +1 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@eohjsc/react-native-smart-city",
3
3
  "title": "React Native Smart Home",
4
- "version": "0.4.98",
4
+ "version": "0.5.00",
5
5
  "description": "TODO",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -9,8 +9,6 @@ import OnOffButtonTemplate from './OnOffButtonTemplate';
9
9
  import OnOffSimpleTemplate from './OnOffSimpleTemplate';
10
10
  import styles from './styles';
11
11
 
12
- let temp;
13
-
14
12
  const getComponent = (template) => {
15
13
  switch (template) {
16
14
  case 'on_off_button_action_template': // todo refactor later with backend
@@ -52,18 +50,9 @@ const OnOffTemplate = memo(({ item = {}, doAction, sensor = {} }) => {
52
50
  }, [config, configValues, is_on_value]);
53
51
 
54
52
  const [isOn, setIsOn] = useState(false);
55
- const [tempIsOn, setTempIsOn] = useState(getIsOnValue());
56
- const [timeoutId, setTimeoutId] = useState();
57
-
58
- const updateStatusFromPusher = useCallback(() => {
59
- const timeout = setTimeout(() => {
60
- setTempIsOn(temp);
61
- }, 7000);
62
- setTimeoutId(timeout);
63
- }, []);
64
53
 
65
54
  const triggerAction = useCallback(async () => {
66
- const action_data = tempIsOn ? action_off_data : action_on_data;
55
+ const action_data = isOn ? action_off_data : action_on_data;
67
56
  if (!action_data) {
68
57
  return;
69
58
  }
@@ -78,11 +67,7 @@ const OnOffTemplate = memo(({ item = {}, doAction, sensor = {} }) => {
78
67
  config_value: isOn ? 0 : 1,
79
68
  };
80
69
  }
81
- clearTimeout(timeoutId);
82
70
  await doAction(action_data, data);
83
- setTempIsOn((prev) => !prev);
84
- updateStatusFromPusher(); // todo Bang read about this magic
85
-
86
71
  if (
87
72
  is_managed_by_backend &&
88
73
  config &&
@@ -91,17 +76,14 @@ const OnOffTemplate = memo(({ item = {}, doAction, sensor = {} }) => {
91
76
  watchMultiConfigs([config]);
92
77
  }
93
78
  }, [
94
- tempIsOn,
79
+ isOn,
95
80
  action_off_data,
96
81
  action_on_data,
97
82
  allow_config_store_value,
98
83
  config,
99
84
  device_type,
100
- timeoutId,
101
85
  doAction,
102
- updateStatusFromPusher,
103
86
  is_managed_by_backend,
104
- isOn,
105
87
  ]);
106
88
 
107
89
  useUnwatchLGDeviceConfigControl(sensor, [config]);
@@ -110,16 +92,6 @@ const OnOffTemplate = memo(({ item = {}, doAction, sensor = {} }) => {
110
92
  setIsOn(getIsOnValue());
111
93
  }, [getIsOnValue]);
112
94
 
113
- useEffect(() => {
114
- temp = isOn;
115
- }, [isOn]);
116
-
117
- useEffect(() => {
118
- if (device_type !== DEVICE_TYPE.LG_THINQ) {
119
- setTempIsOn(getIsOnValue());
120
- }
121
- }, [getIsOnValue, device_type]);
122
-
123
95
  const Component = useMemo(() => {
124
96
  return getComponent(item.template);
125
97
  }, [item.template]);
@@ -127,7 +99,7 @@ const OnOffTemplate = memo(({ item = {}, doAction, sensor = {} }) => {
127
99
  return (
128
100
  <View style={styles.wrap}>
129
101
  <Component
130
- isOn={tempIsOn}
102
+ isOn={isOn}
131
103
  triggerAction={triggerAction}
132
104
  actionGroup={configuration}
133
105
  disabled={!action_on_data && !action_off_data}
@@ -20,7 +20,7 @@ import _TextInput from '../Form/TextInput';
20
20
  const SliderRangeTemplate = memo(
21
21
  ({ item = {}, doAction, sensor, isWidgetOrder }) => {
22
22
  const t = useTranslations();
23
- const { configuration = {} } = item;
23
+ const { configuration = {}, title, label } = item;
24
24
  const [configValues] = useConfigGlobalState('configValues');
25
25
  const {
26
26
  config = undefined,
@@ -91,7 +91,7 @@ const SliderRangeTemplate = memo(
91
91
  <View
92
92
  style={(isWidgetOrder && styles.wrapOrderItem) || styles.viewBrightness}
93
93
  >
94
- <Text type="H4">{item?.title || item?.label || t('brightness')}</Text>
94
+ <Text type="H4">{title || label || t('brightness')}</Text>
95
95
  <View style={styles.wrap}>
96
96
  <Slider
97
97
  step={1}
@@ -0,0 +1,182 @@
1
+ import React, {
2
+ useCallback,
3
+ useEffect,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ } from 'react';
8
+ import { View, TouchableOpacity } from 'react-native';
9
+ import { IconOutline } from '@ant-design/icons-react-native';
10
+ import Text from '../../commons/Text';
11
+ import { useConfigGlobalState } from '../../iot/states';
12
+ import styles from './TerminalBoxTemplateStyle';
13
+ import _TextInput from '../Form/TextInput';
14
+ import AccessibilityLabel from '../../configs/AccessibilityLabel';
15
+ import moment from 'moment';
16
+ import { useFetchConfigHistory } from '../UnitSummary/ConfigHistoryChart';
17
+ import { ScrollView } from 'react-native';
18
+ import { API } from '../../configs';
19
+
20
+ const useNewMessage = (
21
+ newValue,
22
+ latestUpdatedFromMessage,
23
+ setAllMessages,
24
+ setLatestUpdatedFromMessage,
25
+ type
26
+ ) => {
27
+ useEffect(() => {
28
+ if (
29
+ !newValue ||
30
+ !newValue.last_updated ||
31
+ moment(newValue.last_updated).isSameOrBefore(latestUpdatedFromMessage)
32
+ ) {
33
+ return;
34
+ }
35
+ setAllMessages((prev) => [
36
+ ...prev,
37
+ { x: moment(), y: newValue.value, type },
38
+ ]);
39
+ setLatestUpdatedFromMessage(moment(newValue.last_updated));
40
+ }, [
41
+ newValue,
42
+ latestUpdatedFromMessage,
43
+ setAllMessages,
44
+ setLatestUpdatedFromMessage,
45
+ type,
46
+ ]);
47
+ };
48
+
49
+ const TerminalBoxTemplate = ({ item, doAction, isWidgetOrder }) => {
50
+ const { label, configuration } = item;
51
+ const { action_data, from_config, to_config } = configuration;
52
+ const [configValues] = useConfigGlobalState('configValues');
53
+ const [value, setValue] = useState();
54
+ const [allMessages, setAllMessages] = useState([]);
55
+ const [latestUpdatedFromMessage, setLatestUpdatedFromMessage] = useState(
56
+ moment()
57
+ );
58
+ const [latestUpdatedToMessage, setLatestUpdatedToMessage] = useState(
59
+ moment()
60
+ );
61
+ const scrollViewRef = useRef();
62
+ const sendCommand = useCallback(() => {
63
+ doAction(action_data, JSON.stringify({ value: value }));
64
+ setValue('');
65
+ }, [doAction, action_data, value]);
66
+
67
+ const onInputChange = (e) => {
68
+ setValue(e);
69
+ };
70
+
71
+ const toValue = useMemo(() => {
72
+ return configValues[to_config?.id];
73
+ }, [configValues, to_config]);
74
+
75
+ const fromValue = useMemo(() => {
76
+ return configValues[from_config?.id];
77
+ }, [configValues, from_config]);
78
+
79
+ const configs = useMemo(() => {
80
+ if (!from_config || !to_config) {
81
+ return [];
82
+ }
83
+ return [from_config, to_config];
84
+ }, [from_config, to_config]);
85
+
86
+ const scrollToBottom = () => {
87
+ setTimeout(() => {
88
+ scrollViewRef.current.scrollToEnd({ animated: true });
89
+ }, 100);
90
+ };
91
+
92
+ const setMessages = useCallback(
93
+ (configMessages) => {
94
+ let messages = [];
95
+ configMessages.map((configMessage) => {
96
+ const type = configMessage.id === from_config.id ? 'from' : 'to';
97
+ messages = [
98
+ ...messages,
99
+ ...configMessage.data.map((item) => {
100
+ return { x: item.x, y: item.y, type };
101
+ }),
102
+ ];
103
+ });
104
+ messages = messages.sort((a, b) => {
105
+ return moment(a.x).diff(moment(b.x));
106
+ });
107
+ setAllMessages(messages);
108
+ scrollToBottom();
109
+ },
110
+ [from_config]
111
+ );
112
+
113
+ const fetchDataDisplayHistory = useFetchConfigHistory(
114
+ configs,
115
+ setMessages,
116
+ API.CONFIG.DISPLAY_HISTORY_V4()
117
+ );
118
+
119
+ useEffect(() => {
120
+ fetchDataDisplayHistory(moment().subtract(7, 'days'), moment());
121
+ }, [fetchDataDisplayHistory]);
122
+
123
+ useNewMessage(
124
+ fromValue,
125
+ latestUpdatedFromMessage,
126
+ setAllMessages,
127
+ setLatestUpdatedFromMessage,
128
+ 'from'
129
+ );
130
+
131
+ useNewMessage(
132
+ toValue,
133
+ latestUpdatedToMessage,
134
+ setAllMessages,
135
+ setLatestUpdatedToMessage,
136
+ 'to'
137
+ );
138
+ return (
139
+ <View style={(isWidgetOrder && styles.wrapOrderItem) || styles.wrap}>
140
+ <View>
141
+ <Text type="H4">{label}</Text>
142
+ </View>
143
+ <ScrollView
144
+ ref={scrollViewRef}
145
+ onContentSizeChange={() =>
146
+ scrollViewRef.current.scrollToEnd({ animated: true })
147
+ }
148
+ style={styles.scrollView}
149
+ >
150
+ {allMessages.map((item, index) => {
151
+ return (
152
+ <View
153
+ key={index}
154
+ style={item.type === 'to' ? styles.to : styles.from}
155
+ >
156
+ <Text>{item.y}</Text>
157
+ <Text>{moment(item.x).format('DD/MM/YYYY HH:mm:ss')}</Text>
158
+ </View>
159
+ );
160
+ })}
161
+ </ScrollView>
162
+ <View style={styles.iconAndText}>
163
+ <_TextInput
164
+ wrapStyle={styles.wrapInputStyle}
165
+ value={value}
166
+ onChange={onInputChange}
167
+ maxLength={255}
168
+ />
169
+ <TouchableOpacity
170
+ style={styles.iconAndTextOption}
171
+ onPress={sendCommand}
172
+ accessibilityLabel={AccessibilityLabel.TERMINAL_BOX_BUTTON_SEND}
173
+ disabled={!value}
174
+ >
175
+ <IconOutline name="send" size={25} />
176
+ </TouchableOpacity>
177
+ </View>
178
+ </View>
179
+ );
180
+ };
181
+
182
+ export default TerminalBoxTemplate;
@@ -0,0 +1,57 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import { Colors } from '../../configs';
3
+
4
+ export default StyleSheet.create({
5
+ wrapOrderItem: {
6
+ flex: 1,
7
+ flexDirection: 'row',
8
+ justifyContent: 'space-between',
9
+ padding: 16,
10
+ },
11
+ wrap: {
12
+ padding: 16,
13
+ marginHorizontal: 16,
14
+ marginBottom: 16,
15
+ borderWidth: 1,
16
+ borderColor: Colors.Gray4,
17
+ borderRadius: 10,
18
+ flex: 1,
19
+ flexDirection: 'column',
20
+ },
21
+ iconAndText: {
22
+ flexDirection: 'row',
23
+ alignItems: 'center',
24
+ width: '100%',
25
+ },
26
+ iconAndTextOption: {
27
+ justifyContent: 'center',
28
+ marginTop: 25,
29
+ width: 40,
30
+ height: 40,
31
+ },
32
+ wrapInputStyle: {
33
+ marginRight: 14,
34
+ width: '90%',
35
+ },
36
+ scrollView: {
37
+ marginTop: 13,
38
+ height: 250,
39
+ },
40
+ to: {
41
+ padding: 5,
42
+ borderWidth: 1,
43
+ borderColor: Colors.Gray4,
44
+ borderRadius: 10,
45
+ marginBottom: 5,
46
+ },
47
+ from: {
48
+ alignItems: 'flex-end',
49
+ marginRight: 0,
50
+ backgroundColor: Colors.Blue18,
51
+ padding: 5,
52
+ borderWidth: 1,
53
+ borderColor: Colors.Gray4,
54
+ borderRadius: 10,
55
+ marginBottom: 5,
56
+ },
57
+ });
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useState } from 'react';
1
+ import React, { useCallback, useMemo, useState } from 'react';
2
2
  import { View, TouchableOpacity } from 'react-native';
3
3
  import { IconOutline } from '@ant-design/icons-react-native';
4
4
  import Text from '../../commons/Text';
@@ -19,7 +19,6 @@ const TextBoxTemplate = ({ item, doAction, isWidgetOrder }) => {
19
19
  const { action_data, config } = configuration;
20
20
  const [configValues] = useConfigGlobalState('configValues');
21
21
  const [value, setValue] = useState();
22
-
23
22
  const { stateAlert, hideAlertAction, onShowAlert } = useDropdownAction();
24
23
 
25
24
  const onDone = useCallback(() => {
@@ -27,9 +26,13 @@ const TextBoxTemplate = ({ item, doAction, isWidgetOrder }) => {
27
26
  hideAlertAction();
28
27
  }, [doAction, action_data, value, hideAlertAction]);
29
28
 
30
- const onInputChange = (value) => {
31
- setValue(value);
29
+ const onInputChange = (e) => {
30
+ setValue(e);
32
31
  };
32
+ const valueText = useMemo(() => {
33
+ return configValues[config.id]?.value || '';
34
+ }, [config.id, configValues]);
35
+
33
36
  return (
34
37
  <View style={(isWidgetOrder && styles.wrapOrderItem) || styles.wrap}>
35
38
  <View>
@@ -37,33 +40,35 @@ const TextBoxTemplate = ({ item, doAction, isWidgetOrder }) => {
37
40
  </View>
38
41
 
39
42
  <View style={styles.iconAndText}>
40
- <Text type="H4">{configValues[config.id]?.value}</Text>
43
+ <Text style={styles.textValue} type="H4">
44
+ {valueText}
45
+ </Text>
41
46
  <TouchableOpacity
42
47
  style={styles.iconAndTextOption}
43
48
  onPress={onShowAlert}
44
49
  accessibilityLabel={AccessibilityLabel.TEXT_BOX_BUTTON_EDIT}
45
50
  >
46
- <IconOutline name="edit" size={20} />
51
+ <IconOutline name="edit" size={25} />
47
52
  </TouchableOpacity>
48
- <AlertAction
49
- visible={stateAlert.visible}
50
- hideModal={hideAlertAction}
51
- title={t('enter_parameters')}
52
- message={stateAlert.message}
53
- leftButtonTitle={stateAlert.leftButton}
54
- leftButtonClick={hideAlertAction}
55
- rightButtonTitle={stateAlert.rightButton}
56
- rightButtonClick={onDone}
57
- rightDisabled={!value}
58
- transY={transY}
59
- >
60
- <_TextInput
61
- wrapStyle={styles.wrapInputStyle}
62
- value={value}
63
- onChange={onInputChange}
64
- />
65
- </AlertAction>
66
53
  </View>
54
+ <AlertAction
55
+ visible={stateAlert.visible}
56
+ hideModal={hideAlertAction}
57
+ title={t('enter_parameters')}
58
+ message={stateAlert.message}
59
+ leftButtonTitle={stateAlert.leftButton}
60
+ leftButtonClick={hideAlertAction}
61
+ rightButtonTitle={stateAlert.rightButton}
62
+ rightButtonClick={onDone}
63
+ rightDisabled={!value}
64
+ transY={transY}
65
+ >
66
+ <_TextInput
67
+ wrapStyle={styles.wrapInputStyle}
68
+ value={value}
69
+ onChange={onInputChange}
70
+ />
71
+ </AlertAction>
67
72
  </View>
68
73
  );
69
74
  };
@@ -24,6 +24,9 @@ export default StyleSheet.create({
24
24
  flexDirection: 'row',
25
25
  alignItems: 'center',
26
26
  },
27
+ textValue: {
28
+ width: '80%',
29
+ },
27
30
  iconAndTextOption: {
28
31
  flex: 1,
29
32
  flexDirection: 'row',
@@ -0,0 +1,232 @@
1
+ import React from 'react';
2
+ import { TouchableOpacity, View } from 'react-native';
3
+ import { act, create } from 'react-test-renderer';
4
+ import { watchMultiConfigs } from '../../../iot/Monitor';
5
+ import { AccessibilityLabel } from '../../../configs/Constants';
6
+ import { SCProvider } from '../../../context';
7
+ import { mockSCStore } from '../../../context/mockStore';
8
+
9
+ import _TextInput from '../../Form/TextInput';
10
+ import { API, Colors } from '../../../configs';
11
+ import TerminalBoxTemplate from '../TerminalBoxTemplate';
12
+ import MockAdapter from 'axios-mock-adapter';
13
+ import api from '../../../utils/Apis/axios';
14
+ import { ScrollView } from 'react-native';
15
+
16
+ const mock = new MockAdapter(api.axiosInstance);
17
+ const wrapComponent = (item, mockDoAction, isWidgetOrder = false) => (
18
+ <SCProvider initState={mockSCStore({})}>
19
+ <TerminalBoxTemplate
20
+ item={item}
21
+ doAction={mockDoAction}
22
+ isWidgetOrder={isWidgetOrder}
23
+ />
24
+ </SCProvider>
25
+ );
26
+
27
+ jest.mock('../../../iot/Monitor');
28
+
29
+ jest.mock('../../../iot/states', () => ({
30
+ useConfigGlobalState: () => [{ 5: { value: 2 } }, null],
31
+ }));
32
+
33
+ jest.mock('@react-navigation/native', () => {
34
+ return {
35
+ ...jest.requireActual('@react-navigation/native'),
36
+ useFocusEffect: jest.fn(),
37
+ };
38
+ });
39
+
40
+ describe('Test TerminalBoxTemplate', () => {
41
+ let displayItem;
42
+ let sensor;
43
+ let wrapper;
44
+ let data;
45
+
46
+ beforeEach(() => {
47
+ watchMultiConfigs.mockClear();
48
+ jest.useFakeTimers();
49
+ displayItem = {
50
+ id: 33909,
51
+ configuration_error: null,
52
+ is_configuration_ready: true,
53
+ configuration: {
54
+ from_config: {
55
+ id: 35490,
56
+ sensor_id: 18310,
57
+ chip_id: 4129,
58
+ color: '#000000',
59
+ standard: '',
60
+ measure: '',
61
+ unit: '',
62
+ data: [
63
+ {
64
+ x: '2024-02-28T07:53:57.828537Z',
65
+ y: null,
66
+ },
67
+ ],
68
+ },
69
+ to_config: {
70
+ id: 35491,
71
+ sensor_id: 18310,
72
+ chip_id: 4129,
73
+ color: '#000000',
74
+ standard: '',
75
+ measure: '',
76
+ unit: '',
77
+ data: [
78
+ {
79
+ x: '2024-02-28T07:53:57.828537Z',
80
+ y: null,
81
+ },
82
+ ],
83
+ },
84
+ action: 'e8d1de74-7609-4e04-b63d-e6a19389d608',
85
+ action_data: {
86
+ id: 33283,
87
+ name: 'String V4',
88
+ key: 'e8d1de74-7609-4e04-b63d-e6a19389d608',
89
+ icon: '',
90
+ icon_kit: null,
91
+ color: '#00979D',
92
+ home_assistant_action: null,
93
+ lg_action: null,
94
+ arduino_action: {
95
+ id: 8025,
96
+ pin: null,
97
+ value: 0,
98
+ },
99
+ modbus_action: null,
100
+ zigbee_action: null,
101
+ command_prefer_over_internet: true,
102
+ command_prefer_over_bluetooth: false,
103
+ command_prefer_over_googlehome: false,
104
+ allow_config_store_value_id: null,
105
+ end_device: {
106
+ id: 10161,
107
+ name: 'Terminal Box',
108
+ device_type: '',
109
+ },
110
+ sensor_id: 18310,
111
+ chip_id: 4129,
112
+ is_only_bluetooth: false,
113
+ googlehome_action: null,
114
+ },
115
+ },
116
+ template: 'TerminalBoxTemplate',
117
+ order: 0,
118
+ label: 'String Box',
119
+ };
120
+ sensor = {
121
+ name: 'Sensor name',
122
+ is_managed_by_backend: false,
123
+ };
124
+ data = {
125
+ configs: [
126
+ {
127
+ id: 35490,
128
+ head: [
129
+ {
130
+ x: '2024-02-28T07:53:57.787788Z',
131
+ y: null,
132
+ },
133
+ ],
134
+ tail: [
135
+ {
136
+ x: '2024-02-29T07:30:01.940166Z',
137
+ y: null,
138
+ },
139
+ ],
140
+ middle: {
141
+ ready: [],
142
+ not_ready: [],
143
+ channel: 'cache-4e37a226-c61c-4708-a00c-c22bd192b845',
144
+ },
145
+ },
146
+ {
147
+ id: 35491,
148
+ head: [
149
+ {
150
+ x: '2024-02-28T07:53:57.828537Z',
151
+ y: null,
152
+ },
153
+ ],
154
+ tail: [
155
+ {
156
+ x: '2024-02-29T01:42:14.488233Z',
157
+ y: null,
158
+ },
159
+ ],
160
+ middle: {
161
+ ready: [],
162
+ not_ready: [],
163
+ channel: 'cache-2d0806d7-5c15-4af0-8443-6ec6b54ba9cf',
164
+ },
165
+ },
166
+ ],
167
+ };
168
+ });
169
+
170
+ it('render template', async () => {
171
+ const mockDoAction = jest.fn();
172
+ mock.onGet(API.CONFIG.DISPLAY_HISTORY_V4()).reply(200, data);
173
+ await act(async () => {
174
+ wrapper = await create(wrapComponent(displayItem, mockDoAction));
175
+ });
176
+ jest.runAllTimers();
177
+
178
+ const instance = wrapper.root;
179
+ const views = instance.findAllByType(View);
180
+
181
+ expect(views[0].props.style).toEqual({
182
+ padding: 16,
183
+ marginHorizontal: 16,
184
+ marginBottom: 16,
185
+ borderWidth: 1,
186
+ borderColor: Colors.Gray4,
187
+ borderRadius: 10,
188
+ flex: 1,
189
+ flexDirection: 'column',
190
+ });
191
+ const scroll = instance.findByType(ScrollView);
192
+ await act(async () => {
193
+ await scroll.props.onContentSizeChange();
194
+ });
195
+ const textInput = instance.findByType(_TextInput);
196
+ await act(async () => {
197
+ await textInput.props.onChange('123');
198
+ });
199
+ const buttonSend = instance.find(
200
+ (el) =>
201
+ el.props.accessibilityLabel ===
202
+ AccessibilityLabel.TERMINAL_BOX_BUTTON_SEND &&
203
+ el.type === TouchableOpacity
204
+ );
205
+ await act(async () => {
206
+ await buttonSend.props.onPress();
207
+ });
208
+
209
+ expect(mockDoAction).toHaveBeenCalledWith(
210
+ displayItem.configuration.action_data,
211
+ JSON.stringify({ value: '123' })
212
+ );
213
+ });
214
+ it('render template isWidgetOrder change position', async () => {
215
+ const mockDoAction = jest.fn();
216
+ sensor.is_managed_by_backend = true;
217
+ displayItem.configuration.from_config = null;
218
+ mock.onGet(API.CONFIG.DISPLAY_HISTORY_V4()).reply(200, data);
219
+ await act(async () => {
220
+ wrapper = await create(wrapComponent(displayItem, mockDoAction, true));
221
+ });
222
+ const instance = wrapper.root;
223
+ const views = instance.findAllByType(View);
224
+
225
+ expect(views[0].props.style).toEqual({
226
+ flex: 1,
227
+ flexDirection: 'row',
228
+ justifyContent: 'space-between',
229
+ padding: 16,
230
+ });
231
+ });
232
+ });
@@ -34,7 +34,7 @@ jest.mock('@react-navigation/native', () => {
34
34
  };
35
35
  });
36
36
 
37
- describe('Test OptionsDropdownActionTemplate', () => {
37
+ describe('Test TextBoxTemplate', () => {
38
38
  const actionData = {
39
39
  color: '#00979D',
40
40
  command_prefer_over_bluetooth: true,