@eohjsc/react-native-smart-city 0.3.93 → 0.3.95

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 (48) hide show
  1. package/package.json +1 -1
  2. package/src/commons/ActionGroup/OnOffTemplate/index.js +20 -17
  3. package/src/commons/ActionGroup/__test__/OnOffButtonTemplate.test.js +1 -1
  4. package/src/commons/ActionGroup/__test__/OnOffTemplate.test.js +5 -24
  5. package/src/commons/SubUnit/OneTap/ItemOneTap.js +83 -85
  6. package/src/commons/SubUnit/OneTap/__test__/SubUnitAutomate.test.js +68 -24
  7. package/src/commons/SubUnit/OneTap/index.js +21 -1
  8. package/src/configs/API.js +1 -0
  9. package/src/screens/ActivityLog/__test__/index.test.js +60 -2
  10. package/src/screens/ActivityLog/hooks/__test__/index.test.js +5 -0
  11. package/src/screens/ActivityLog/hooks/index.js +11 -0
  12. package/src/screens/AddLocationMaps/index.js +1 -1
  13. package/src/screens/AddNewGateway/ConnectingDevice.js +7 -0
  14. package/src/screens/AddNewGateway/ConnectingWifiDevice.js +6 -4
  15. package/src/screens/AddNewGateway/ConnectingWifiGuide.js +0 -1
  16. package/src/screens/AddNewGateway/RenameNewDevices.js +2 -2
  17. package/src/screens/AddNewGateway/ScanWifiDeviceQR.js +1 -6
  18. package/src/screens/AddNewGateway/SelectDeviceType.js +67 -9
  19. package/src/screens/AddNewGateway/ShareWifiPassword.js +20 -3
  20. package/src/screens/AddNewGateway/__test__/ConnectingZigbeeDevice.test.js +35 -0
  21. package/src/screens/AddNewGateway/__test__/ScanWifiDeviceQR.test.js +0 -10
  22. package/src/screens/AddNewGateway/__test__/SelectDeviceType.test.js +183 -29
  23. package/src/screens/AllGateway/GatewayInfo/index.js +3 -3
  24. package/src/screens/Automate/AddNewAction/SetupConfigCondition.js +0 -1
  25. package/src/screens/Automate/MultiUnits.js +32 -18
  26. package/src/screens/Automate/ScriptDetail/__test__/index.test.js +36 -2
  27. package/src/screens/Automate/ScriptDetail/index.js +17 -3
  28. package/src/screens/Automate/Styles/MultiUnitsStyles.js +1 -1
  29. package/src/screens/Automate/__test__/MultiUnits.test.js +125 -8
  30. package/src/screens/Automate/__test__/index.test.js +95 -11
  31. package/src/screens/Automate/index.js +62 -38
  32. package/src/screens/Device/hooks/__test__/useEvaluateValue.test.js +24 -1
  33. package/src/screens/Sharing/InfoMemberUnit.js +2 -2
  34. package/src/screens/Sharing/MemberList.js +28 -7
  35. package/src/screens/Sharing/__test__/InfoMemberUnit.test.js +32 -18
  36. package/src/screens/Sharing/__test__/MemberList.test.js +37 -4
  37. package/src/screens/SubUnit/AddSubUnit.js +1 -1
  38. package/src/screens/SubUnit/__test__/AddSubUnit.test.js +16 -3
  39. package/src/screens/Unit/AddMenu.js +42 -19
  40. package/src/screens/Unit/__test__/AddMenu.test.js +68 -15
  41. package/src/screens/Unit/components/AutomateScript/index.js +1 -1
  42. package/src/utils/I18n/translations/en.js +1409 -0
  43. package/src/utils/I18n/translations/vi.js +1414 -0
  44. package/src/utils/I18n/translations.ts +2 -2
  45. package/src/utils/Permission/backend.js +7 -0
  46. package/src/screens/Sharing/__test__/MemberList2.test.js +0 -74
  47. package/src/utils/I18n/translations/en.json +0 -1142
  48. package/src/utils/I18n/translations/vi.json +0 -1139
@@ -1,8 +1,12 @@
1
1
  import React from 'react';
2
2
  import { renderHook } from '@testing-library/react-hooks';
3
- import { useEvaluateValue } from '../useEvaluateValue';
3
+ import { useEvaluateValue, useGetEvaluateValue } from '../useEvaluateValue';
4
4
  import { SCProvider } from '../../../../context';
5
5
  import { mockSCStore } from '../../../../context/mockStore';
6
+ import { API } from '../../../../configs';
7
+ import MockAdapter from 'axios-mock-adapter';
8
+ import api from '../../../../utils/Apis/axios';
9
+ import { flushPromises } from '../../../AllGateway/test-utils';
6
10
 
7
11
  const wrapper =
8
12
  (valueEvaluations) =>
@@ -99,4 +103,23 @@ describe('Test useEvaluateValue', () => {
99
103
  expect(evaluateValue.current(2, 1).text).toBe('On');
100
104
  expect(evaluateValue.current(2, 2).text).toBe(2);
101
105
  });
106
+
107
+ it('test evaluate fetch from server if not exists', async () => {
108
+ const mock = new MockAdapter(api.axiosInstance);
109
+ mock.onGet(API.FETCH_VALUE_EVALUATION(111)).reply(200, {
110
+ unit_id: 1,
111
+ configs: [111],
112
+ });
113
+
114
+ const { result: evaluateValue } = renderHook(
115
+ () => useGetEvaluateValue(111),
116
+ {
117
+ wrapper: wrapper(valueEvaluations),
118
+ }
119
+ );
120
+ expect(evaluateValue.current).toBe(undefined);
121
+ await flushPromises();
122
+
123
+ expect(mock.history.get.length).toBe(1);
124
+ });
102
125
  });
@@ -148,9 +148,10 @@ const InfoMemberUnit = memo(({ route }) => {
148
148
  stateAlertAction?.is_change,
149
149
  stateAlertAction?.message,
150
150
  ]);
151
+
151
152
  const handleShareDevice = useCallback(() => {
152
153
  if (isOwner && memberInfo?.identity === 'member') {
153
- return navigate(Routes.AddMemberStack, {
154
+ navigate(Routes.AddMemberStack, {
154
155
  screen: Routes.SharingSelectPermission,
155
156
  params: {
156
157
  unit: { id: unit?.id },
@@ -159,7 +160,6 @@ const InfoMemberUnit = memo(({ route }) => {
159
160
  },
160
161
  });
161
162
  }
162
- return <></>;
163
163
  }, [isOwner, navigate, unit?.id, memberInfo]);
164
164
 
165
165
  const footerWarning = useMemo(() => {
@@ -14,6 +14,8 @@ import AlertAction from '../../commons/AlertAction';
14
14
  import { useDataMember, useStateAlertAction } from './hooks';
15
15
  import { AccessibilityLabel } from '../../configs/Constants';
16
16
  import { useSCContextSelector } from '../../context';
17
+ import { useBackendPermission } from '../../utils/Permission/backend';
18
+ import { ToastBottomHelper } from '../../utils/Utils';
17
19
 
18
20
  const MemberList = ({ route }) => {
19
21
  const t = useTranslations();
@@ -24,20 +26,39 @@ const MemberList = ({ route }) => {
24
26
  const { dataMembers, isRefresh, onRefresh, leaveUnit, loading } =
25
27
  useDataMember(unitId, unit?.user_id);
26
28
  const { isOwner } = useIsOwnerOfUnit(unit?.user_id);
29
+ const permissions = useBackendPermission();
27
30
 
28
31
  const { stateAlertSharingMenu, hideStateAlertSharingMenu, stateLeaveUnit } =
29
32
  useStateAlertAction();
30
33
 
31
34
  const onPressRightHeader = useCallback(() => {
32
- if (isOwner) {
33
- navigate(Routes.AddMemberStack, {
34
- screen: Routes.SharingSelectPermission,
35
- params: { unit: { id: unitId } },
36
- });
37
- } else {
35
+ if (!isOwner) {
38
36
  stateLeaveUnit(); //change state stateAlertAction
37
+ return;
38
+ }
39
+
40
+ if (permissions?.max_members_per_unit <= dataMembers.length) {
41
+ ToastBottomHelper.error(
42
+ t('reach_max_members_per_unit', {
43
+ max: permissions?.max_members_per_unit,
44
+ })
45
+ );
46
+ return;
39
47
  }
40
- }, [isOwner, navigate, stateLeaveUnit, unitId]);
48
+
49
+ navigate(Routes.AddMemberStack, {
50
+ screen: Routes.SharingSelectPermission,
51
+ params: { unit: { id: unitId } },
52
+ });
53
+ }, [
54
+ dataMembers.length,
55
+ isOwner,
56
+ navigate,
57
+ permissions?.max_members_per_unit,
58
+ stateLeaveUnit,
59
+ t,
60
+ unitId,
61
+ ]);
41
62
 
42
63
  const handleLeave = useCallback(() => {
43
64
  if (!isOwner) {
@@ -14,6 +14,7 @@ import api from '../../../utils/Apis/axios';
14
14
  import ItemChangeRole from '../Components/ItemChangeRole';
15
15
  import ModalPopupCT from '../../../commons/ModalPopupCT';
16
16
  import Routes from '../../../utils/Route';
17
+ import { useNavigation } from '@react-navigation/native';
17
18
 
18
19
  const mock = new MockAdapter(api.axiosInstance);
19
20
 
@@ -22,22 +23,6 @@ jest.mock('../../../hooks/Common', () => {
22
23
  useIsOwnerOfUnit: () => ({ isOwner: true }),
23
24
  };
24
25
  });
25
- const mockUseIsFocused = jest.fn();
26
- const mockedNavigate = jest.fn();
27
-
28
- const mockGoBack = jest.fn();
29
-
30
- jest.mock('@react-navigation/native', () => {
31
- return {
32
- ...jest.requireActual('@react-navigation/native'),
33
- useRoute: jest.fn(),
34
- useNavigation: () => ({
35
- goBack: mockGoBack,
36
- navigate: mockedNavigate,
37
- }),
38
- useIsFocused: () => mockUseIsFocused,
39
- };
40
- });
41
26
 
42
27
  const wrapComponent = (route) => (
43
28
  <SCProvider initState={mockSCStore({})}>
@@ -48,9 +33,13 @@ const wrapComponent = (route) => (
48
33
  describe('Test InfoMemberUnit', () => {
49
34
  let tree;
50
35
  let route;
36
+ const mockedNavigate = useNavigation().navigate;
37
+ const mockGoBack = useNavigation().goBack;
51
38
 
52
39
  beforeEach(() => {
53
40
  mockedNavigate.mockClear();
41
+ mockGoBack.mockClear();
42
+ mock.reset();
54
43
  route = {
55
44
  params: {
56
45
  unit: {
@@ -112,7 +101,7 @@ describe('Test InfoMemberUnit', () => {
112
101
  it('render InfoMemberUnit delete member', async () => {
113
102
  mock.onGet(API.SHARE.UNIT_MEMBER_INFO(1, 1)).reply(200, {
114
103
  id: 2,
115
- identity: 'owner',
104
+ identity: 'member',
116
105
  phone_number: '88888',
117
106
  name: 'user',
118
107
  email: 'abc@gmail.com',
@@ -130,10 +119,15 @@ describe('Test InfoMemberUnit', () => {
130
119
  expect(header).toHaveLength(1);
131
120
  expect(buttonRemove).toHaveLength(1);
132
121
  mock.onDelete(API.SHARE.UNITS_MEMBER_DETAIL(1)).reply(200);
122
+
123
+ const alert = instance.findByType(AlertAction);
124
+ expect(alert.props.visible).toBeFalsy();
125
+
133
126
  await act(async () => {
134
127
  buttonRemove[0].props.onPress();
135
128
  });
136
- expect(mockGoBack).toBeCalled();
129
+
130
+ expect(alert.props.visible).toBeTruthy();
137
131
  });
138
132
 
139
133
  it('test change owner', async () => {
@@ -191,4 +185,24 @@ describe('Test InfoMemberUnit', () => {
191
185
  type: 'infoMemberUnit',
192
186
  });
193
187
  });
188
+
189
+ it('test share device', async () => {
190
+ mock.onGet(API.SHARE.UNIT_MEMBER_INFO(1, 1)).reply(200, {
191
+ id: 2,
192
+ identity: 'member',
193
+ phone_number: '88888',
194
+ name: 'user',
195
+ email: 'abc@gmail.com',
196
+ });
197
+ await act(async () => {
198
+ tree = await create(wrapComponent(route));
199
+ });
200
+ const instance = tree.root;
201
+ const rows = instance.findAllByType(TouchableOpacity);
202
+
203
+ await act(async () => {
204
+ await rows[rows.length - 1].props.onPress();
205
+ });
206
+ expect(mockedNavigate).toBeCalled();
207
+ });
194
208
  });
@@ -10,6 +10,8 @@ import Routes from '../../../utils/Route';
10
10
  import MockAdapter from 'axios-mock-adapter';
11
11
  import api from '../../../utils/Apis/axios';
12
12
  import { useNavigation } from '@react-navigation/native';
13
+ import { ToastBottomHelper } from '../../../utils/Utils';
14
+ import { getTranslate } from '../../../utils/I18n';
13
15
 
14
16
  new MockAdapter(api.axiosInstance);
15
17
 
@@ -19,8 +21,8 @@ jest.mock('../../../hooks/Common', () => {
19
21
  };
20
22
  });
21
23
 
22
- const wrapComponent = (route, state) => (
23
- <SCProvider initState={mockSCStore({})}>
24
+ const wrapComponent = (route, state = {}) => (
25
+ <SCProvider initState={mockSCStore(state)}>
24
26
  <MemberList route={route} />
25
27
  </SCProvider>
26
28
  );
@@ -64,11 +66,42 @@ describe('test MemberList', () => {
64
66
  await act(async () => {
65
67
  alertAction.props.rightButtonClick();
66
68
  });
67
- const MemberListButtons = instance.findAllByType(TouchableOpacity);
68
- await MemberListButtons[1].props.onPress();
69
+ const memberListButtons = instance.findAllByType(TouchableOpacity);
70
+ await act(async () => {
71
+ await memberListButtons[1].props.onPress();
72
+ });
69
73
  expect(mockedNavigate).toBeCalledWith(Routes.AddMemberStack, {
70
74
  screen: Routes.SharingSelectPermission,
71
75
  params: { unit: { id: 1 } },
72
76
  });
73
77
  });
78
+
79
+ it('add new member but reach limit', async () => {
80
+ let tree;
81
+ await act(async () => {
82
+ tree = await create(
83
+ wrapComponent(route, {
84
+ auth: {
85
+ account: {
86
+ user: {
87
+ permissions: {
88
+ max_members_per_unit: 0,
89
+ },
90
+ },
91
+ },
92
+ },
93
+ })
94
+ );
95
+ });
96
+ const instance = tree.root;
97
+ const spyToastError = jest.spyOn(ToastBottomHelper, 'error');
98
+ const memberListButtons = instance.findAllByType(TouchableOpacity);
99
+ await act(async () => {
100
+ await memberListButtons[1].props.onPress();
101
+ });
102
+ expect(mockedNavigate).not.toBeCalled();
103
+ expect(spyToastError).toBeCalledWith(
104
+ getTranslate('en', 'reach_max_members_per_unit')
105
+ );
106
+ });
74
107
  });
@@ -163,7 +163,7 @@ const AddSubUnit = ({ route }) => {
163
163
 
164
164
  const validateData = useMemo(() => {
165
165
  if (isAddUnit) {
166
- return roomName === '' || wallpaper === '' || location.description === '';
166
+ return roomName === '' || wallpaper === '' || !location.description;
167
167
  } else {
168
168
  return roomName === '' || wallpaper === '';
169
169
  }
@@ -62,6 +62,9 @@ describe('Test AddSubUnit', () => {
62
62
  unit: {
63
63
  id: 1,
64
64
  name: 'Unit name',
65
+ location: {
66
+ description: 'location',
67
+ },
65
68
  },
66
69
  },
67
70
  };
@@ -187,7 +190,9 @@ describe('Test AddSubUnit', () => {
187
190
  it('test create Unit', async () => {
188
191
  route.params = {
189
192
  ...route.params,
190
- location: 'Unit address',
193
+ location: {
194
+ description: 'Unit address',
195
+ },
191
196
  isAddUnit: true,
192
197
  };
193
198
  mock.onPost(API.UNIT.CREATE_UNIT()).reply(200, {
@@ -231,7 +236,9 @@ describe('Test AddSubUnit', () => {
231
236
  it('test create Unit Fail', async () => {
232
237
  route.params = {
233
238
  ...route.params,
234
- location: 'Unit address',
239
+ location: {
240
+ description: 'Unit address',
241
+ },
235
242
  isAddUnit: true,
236
243
  };
237
244
  mock.onPost(API.UNIT.CREATE_UNIT()).reply(400);
@@ -270,7 +277,13 @@ describe('Test AddSubUnit', () => {
270
277
  isAddSubUnit: true,
271
278
  routeName: 'DashboardStack',
272
279
  stationId: undefined,
273
- unitData: { id: 1, name: 'Unit name' },
280
+ unitData: {
281
+ id: 1,
282
+ name: 'Unit name',
283
+ location: {
284
+ description: 'location',
285
+ },
286
+ },
274
287
  unitId: 1,
275
288
  },
276
289
  });
@@ -7,61 +7,84 @@ import AddDeviceIcon from '../../../assets/images/Popover/Dashboard/AddDevice.sv
7
7
  import SmartAccount from '../../../assets/images/Popover/Dashboard/SmartAccount.svg';
8
8
  import AddMemberIcon from '../../../assets/images/Popover/Dashboard/AddMember.svg';
9
9
  import { useNavigation } from '@react-navigation/native';
10
+ import { ToastBottomHelper } from '../../utils/Utils';
11
+ import { useBackendPermission } from '../../utils/Permission/backend';
10
12
 
11
13
  const AddMenu = memo(({ unit, afterItemClick, showAdd, setHideAdd }) => {
12
14
  const t = useTranslations();
13
15
  const navigation = useNavigation();
16
+ const permissions = useBackendPermission();
14
17
 
15
18
  const onItemClick = useCallback(
16
- ({ route: routeName, data }) => {
19
+ (item) => {
17
20
  setHideAdd(true);
18
21
  afterItemClick();
19
- routeName && navigation.navigate(routeName, data);
22
+ item.onClick && item.onClick();
20
23
  },
21
- [setHideAdd, afterItemClick, navigation]
24
+ [setHideAdd, afterItemClick]
22
25
  );
23
26
 
24
27
  const listItem = useMemo(() => {
25
28
  return [
26
29
  {
27
30
  id: 'add_sub_unit',
28
- route: Routes.AddSubUnitStack,
29
31
  text: t('sub_unit'),
30
32
  image: <AddSubUnitIcon width={43} height={43} />,
31
- data: {
32
- screen: Routes.AddSubUnit,
33
- params: { unit, isInsideUnit: true },
33
+ onClick: () => {
34
+ // 2 is station favorite and smart
35
+ if (unit.stations.length - 2 >= permissions.max_stations_per_unit) {
36
+ ToastBottomHelper.error(t('reach_max_stations_per_unit'));
37
+ return;
38
+ }
39
+ navigation.navigate(Routes.AddSubUnitStack, {
40
+ screen: Routes.AddSubUnit,
41
+ params: { unit, isInsideUnit: true },
42
+ });
34
43
  },
35
44
  },
36
45
  {
37
46
  id: 'add_member',
38
- route: Routes.AddMemberStack,
39
47
  text: t('member'),
40
48
  image: <AddMemberIcon width={43} height={43} />,
41
- data: { screen: Routes.SharingSelectPermission, params: { unit } },
49
+ onClick: () =>
50
+ navigation.navigate(Routes.AddMemberStack, {
51
+ screen: Routes.SharingSelectPermission,
52
+ params: { unit },
53
+ }),
42
54
  },
43
55
  {
44
56
  id: 'add_device',
45
- route: Routes.AddGatewayStack,
46
57
  text: t('device'),
47
58
  image: <AddDeviceIcon width={43} height={43} />,
48
- data: {
49
- screen: Routes.SelectDeviceType,
50
- params: { unit },
59
+ onClick: () => {
60
+ let hasPermission = false;
61
+ Object.keys(permissions).forEach((key) => {
62
+ if (key.includes('plug_and_play') && permissions[key]) {
63
+ hasPermission = true;
64
+ }
65
+ });
66
+ if (!hasPermission) {
67
+ ToastBottomHelper.error(t('not_support_plug_and_play'));
68
+ return;
69
+ }
70
+ navigation.navigate(Routes.AddGatewayStack, {
71
+ screen: Routes.SelectDeviceType,
72
+ params: { unit },
73
+ });
51
74
  },
52
75
  },
53
76
  {
54
77
  id: 'add_smart_account',
55
- route: Routes.SmartAccountStack,
56
78
  text: t('name_smart_account'),
57
79
  image: <SmartAccount width={43} height={43} />,
58
- data: {
59
- screen: Routes.SmartAccount,
60
- params: { unitId: unit?.id, unitName: unit?.name },
61
- },
80
+ onClick: () =>
81
+ navigation.navigate(Routes.SmartAccountStack, {
82
+ screen: Routes.SmartAccount,
83
+ params: { unitId: unit?.id, unitName: unit?.name },
84
+ }),
62
85
  },
63
86
  ];
64
- }, [t, unit]);
87
+ }, [t, unit, navigation, permissions]);
65
88
 
66
89
  const hideAddModal = useCallback(() => {
67
90
  setHideAdd(false);
@@ -5,13 +5,15 @@ import MenuActionAddnew from '../../../commons/MenuActionAddnew';
5
5
  import { SCProvider } from '../../../context';
6
6
  import { mockSCStore } from '../../../context/mockStore';
7
7
  import AddMenu from '../AddMenu';
8
+ import { useNavigation } from '@react-navigation/native';
9
+ import { ToastBottomHelper } from '../../../utils/Utils';
10
+ import { getTranslate } from '../../../utils/I18n';
8
11
 
9
- const mockedNavigate = jest.fn();
10
12
  const mockedAfterItemClick = jest.fn();
11
13
  const mockedHideAddModal = jest.fn();
12
14
 
13
- const wrapComponent = (unit) => (
14
- <SCProvider initState={mockSCStore({})}>
15
+ const wrapComponent = (unit, storeData = {}) => (
16
+ <SCProvider initState={mockSCStore(storeData)}>
15
17
  <AddMenu
16
18
  unit={unit}
17
19
  afterItemClick={mockedAfterItemClick}
@@ -21,17 +23,15 @@ const wrapComponent = (unit) => (
21
23
  </SCProvider>
22
24
  );
23
25
 
24
- jest.mock('@react-navigation/native', () => {
25
- return {
26
- ...jest.requireActual('@react-navigation/native'),
27
- useNavigation: () => ({
28
- navigate: mockedNavigate,
29
- }),
30
- useIsFocused: jest.fn(),
31
- };
32
- });
33
-
34
26
  describe('Test AddMenu Unit', () => {
27
+ const mockedNavigate = useNavigation().navigate;
28
+ const spyToastError = jest.spyOn(ToastBottomHelper, 'error');
29
+
30
+ beforeEach(() => {
31
+ mockedNavigate.mockClear();
32
+ spyToastError.mockClear();
33
+ });
34
+
35
35
  let tree;
36
36
  it('render AddMenu without route', async () => {
37
37
  let unit = { id: 1, name: 'Unit 1' };
@@ -68,8 +68,9 @@ describe('Test AddMenu Unit', () => {
68
68
  const menuActionAddnew = instance.findByType(MenuActionAddnew);
69
69
  await act(async () => {
70
70
  menuActionAddnew.props.onItemClick({
71
- route: 'route test',
72
- data: 'data test',
71
+ onClick: () => {
72
+ mockedNavigate('route test', 'data test');
73
+ },
73
74
  });
74
75
  });
75
76
 
@@ -77,4 +78,56 @@ describe('Test AddMenu Unit', () => {
77
78
  expect(mockedAfterItemClick).toHaveBeenCalled();
78
79
  expect(mockedNavigate).toHaveBeenCalledWith('route test', 'data test');
79
80
  });
81
+
82
+ it('add new device but have no permission', async () => {
83
+ let unit = { id: 1, name: 'Unit 1' };
84
+ await act(async () => {
85
+ tree = await create(
86
+ wrapComponent(unit, {
87
+ auth: {
88
+ account: {
89
+ user: {
90
+ permissions: {},
91
+ },
92
+ },
93
+ },
94
+ })
95
+ );
96
+ });
97
+
98
+ const instance = tree.root;
99
+ const menuActionAddnew = instance.findByType(MenuActionAddnew);
100
+ await act(async () => {
101
+ menuActionAddnew.props.onItemClick(menuActionAddnew.props.dataActions[2]);
102
+ });
103
+ expect(spyToastError).toBeCalledWith(
104
+ getTranslate('en', 'not_support_plug_and_play')
105
+ );
106
+ });
107
+
108
+ it('add new device has permission', async () => {
109
+ let unit = { id: 1, name: 'Unit 1' };
110
+ await act(async () => {
111
+ tree = await create(
112
+ wrapComponent(unit, {
113
+ auth: {
114
+ account: {
115
+ user: {
116
+ permissions: {
117
+ plug_and_play_zigbee: true,
118
+ },
119
+ },
120
+ },
121
+ },
122
+ })
123
+ );
124
+ });
125
+
126
+ const instance = tree.root;
127
+ const menuActionAddnew = instance.findByType(MenuActionAddnew);
128
+ await act(async () => {
129
+ menuActionAddnew.props.onItemClick(menuActionAddnew.props.dataActions[2]);
130
+ });
131
+ expect(spyToastError).not.toBeCalled();
132
+ });
80
133
  });
@@ -59,7 +59,7 @@ const AutomateScript = ({ automate, onPress, isSelected }) => {
59
59
  </Text>
60
60
  <View style={styles.descriptionContainer}>
61
61
  <Text numberOfLines={1} type={'Label'} color={Colors.Gray7}>
62
- {`${t('create_by')} ${author}`}
62
+ {t('created_by', { name: author })}
63
63
  </Text>
64
64
  <IconOutline name="right" size={12} />
65
65
  </View>