@controleonline/ui-common 1.2.70 → 1.2.75

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 (188) hide show
  1. package/.github/actions/workers/manager/action.yml +147 -0
  2. package/.github/actions/workers/qa/action.yml +44 -0
  3. package/.github/actions/workers/security/action.yml +44 -0
  4. package/.github/actions/workers/technical-documenter/action.yml +51 -0
  5. package/.github/workflows/manager-worker.yml +168 -0
  6. package/.github/workflows/publish-npm.yml +45 -0
  7. package/.gitmodules +3 -0
  8. package/AGENTS.md +6 -0
  9. package/README.md +14 -0
  10. package/package.json +32 -28
  11. package/src/api/fetch.js +1 -1
  12. package/src/api/index.js +328 -58
  13. package/src/react/components/AddImportModal.js +95 -111
  14. package/src/react/components/AnimatedModal.js +171 -0
  15. package/src/react/components/AnimatedModal.styles.js +21 -0
  16. package/src/react/components/AppTypeSwitcher.js +164 -0
  17. package/src/react/components/AppTypeSwitcher.styles.js +109 -0
  18. package/src/react/components/BackgroundRuntimeBridge.js +5 -4
  19. package/src/react/components/BottomNavigationBar.config.js +211 -231
  20. package/src/react/components/BottomNavigationBar.js +86 -31
  21. package/src/react/components/BottomNavigationBar.styles.js +118 -110
  22. package/src/react/components/CategoryForm.js +52 -35
  23. package/src/react/components/DefaultProvider.native.js +194 -156
  24. package/src/react/components/DefaultProvider.web.js +188 -112
  25. package/src/react/components/DeliveryPushBridge.native.js +2 -2
  26. package/src/react/components/DeviceAlertSoundService.js +3 -3
  27. package/src/react/components/KioskModeBridge.js +2 -2
  28. package/src/react/components/LauncherModeBridge.js +2 -2
  29. package/src/react/components/ManagerPushBridge.native.js +2 -2
  30. package/src/react/components/NetworkPrinterPreviewModal.js +249 -0
  31. package/src/react/components/NetworkPrinterPreviewModal.styles.js +159 -0
  32. package/src/react/components/RemoteCheckoutService.js +28 -20
  33. package/src/react/components/RuntimeBottomNavigationBar.js +146 -119
  34. package/src/react/components/RuntimeInfoFooter.js +137 -45
  35. package/src/react/components/RuntimeInfoFooter.styles.js +3 -0
  36. package/src/react/components/StateStore.js +258 -0
  37. package/src/react/components/UnifiedPaymentBar.js +7 -4
  38. package/src/react/components/UserAvatar.js +160 -0
  39. package/src/react/components/WebsocketListener.native.js +11 -11
  40. package/src/react/components/integrations/IntegrationHero/index.js +48 -0
  41. package/src/react/components/integrations/IntegrationHero/styles.js +57 -0
  42. package/src/react/components/integrations/IntegrationTabs/index.js +89 -0
  43. package/src/react/components/integrations/IntegrationTabs/styles.js +44 -0
  44. package/src/react/config/deviceConfigBootstrap.js +71 -30
  45. package/src/react/css/orders.js +72 -0
  46. package/src/react/pages/Connections.js +229 -0
  47. package/src/react/pages/Connections.styles.js +222 -0
  48. package/src/react/pages/DeviceDetailPage.js +2814 -0
  49. package/src/react/pages/DeviceDetailPage.styles.js +308 -0
  50. package/src/react/pages/Devices/currentDevice.js +194 -0
  51. package/src/react/pages/Devices/deviceTypes/DeviceGroupCard.js +257 -0
  52. package/src/react/pages/Devices/deviceTypes/all.js +23 -0
  53. package/src/react/pages/Devices/deviceTypes/device.js +25 -0
  54. package/src/react/pages/Devices/deviceTypes/deviceListHelpers.js +178 -0
  55. package/src/react/pages/Devices/deviceTypes/display.js +26 -0
  56. package/src/react/pages/Devices/deviceTypes/index.js +145 -0
  57. package/src/react/pages/Devices/deviceTypes/ipCamera.js +35 -0
  58. package/src/react/pages/Devices/deviceTypes/pdv.js +88 -0
  59. package/src/react/pages/Devices/deviceTypes/printer.js +37 -0
  60. package/src/react/pages/Devices/deviceTypes/shared.js +453 -0
  61. package/src/react/pages/Devices/deviceTypes/useDeviceNetworkConnectivity.js +145 -0
  62. package/src/react/pages/Devices.js +183 -0
  63. package/src/react/pages/Devices.styles.js +353 -0
  64. package/src/react/pages/Food99IntegrationPage/components/Food99CatalogTab.js +219 -0
  65. package/src/react/pages/Food99IntegrationPage/components/Food99OverviewTab.js +134 -0
  66. package/src/react/pages/Food99IntegrationPage/components/Food99PreviewModal.js +173 -0
  67. package/src/react/pages/Food99IntegrationPage/components/Food99ProductCard.js +1 -0
  68. package/src/react/pages/Food99IntegrationPage/components/Food99QuickWalletModal.js +181 -0
  69. package/src/react/pages/Food99IntegrationPage/components/Food99SettingsTab.js +306 -0
  70. package/src/react/pages/Food99IntegrationPage/components/Food99StoreTab.js +417 -0
  71. package/src/react/pages/Food99IntegrationPage/index.js +1530 -0
  72. package/src/react/pages/Food99IntegrationPage/styles.js +792 -0
  73. package/src/react/pages/Food99IntegrationPage/utils.js +155 -0
  74. package/src/react/pages/Food99IntegrationPage.js +1 -0
  75. package/src/react/pages/Food99IntegrationPage.styles.js +1 -0
  76. package/src/react/pages/GenericLogPage.js +42 -167
  77. package/src/react/pages/IFoodIntegrationPage/components/IFoodCatalogTab.js +8 -0
  78. package/src/react/pages/IFoodIntegrationPage/components/IFoodOperationsTab.js +420 -0
  79. package/src/react/pages/IFoodIntegrationPage/components/IFoodOverviewTab.js +118 -0
  80. package/src/react/pages/IFoodIntegrationPage/components/IFoodPreviewModal.js +103 -0
  81. package/src/react/pages/IFoodIntegrationPage/components/IFoodProductCard.js +1 -0
  82. package/src/react/pages/IFoodIntegrationPage/components/IFoodStoreTab.js +150 -0
  83. package/src/react/pages/IFoodIntegrationPage/index.js +1298 -0
  84. package/src/react/pages/IFoodIntegrationPage/styles.js +764 -0
  85. package/src/react/pages/IFoodIntegrationPage/utils.js +91 -0
  86. package/src/react/pages/IFoodIntegrationPage.js +1 -0
  87. package/src/react/pages/IFoodIntegrationPage.styles.js +1 -0
  88. package/src/react/pages/Imports.js +214 -166
  89. package/src/react/pages/IntegrationConfigPage.js +744 -0
  90. package/src/react/pages/IntegrationConfigPage.styles.js +269 -0
  91. package/src/react/pages/Integrations.js +299 -0
  92. package/src/react/pages/Integrations.styles.js +130 -0
  93. package/src/react/pages/ManagerOrderNotificationsPage.js +642 -0
  94. package/src/react/pages/MarketplaceIntegrationPage/components/MarketplaceProductCard.js +205 -0
  95. package/src/react/pages/MarketplaceIntegrationPage/components/RemoteMarketplaceMenuTab.js +335 -0
  96. package/src/react/pages/MarketplaceIntegrationPage/index.js +60 -0
  97. package/src/react/pages/MarketplaceIntegrationPage/utils.js +137 -0
  98. package/src/react/pages/MercadoLivreIntegrationPage.js +538 -0
  99. package/src/react/pages/PrinterDeviceDetailPage.js +1916 -0
  100. package/src/react/pages/PrinterDeviceDetailPage.styles.js +223 -0
  101. package/src/react/pages/PrinterDeviceFormPage.js +521 -0
  102. package/src/react/pages/PrinterDeviceFormPage.styles.js +159 -0
  103. package/src/react/pages/SettingsPage/PaymentTypesByWalletTab.js +484 -454
  104. package/src/react/pages/SettingsPage/index.js +1266 -1167
  105. package/src/react/pages/WhatsAppConnectionPage.js +459 -0
  106. package/src/react/pages/WhatsAppConnectionPage.styles.js +284 -0
  107. package/src/react/pages/integrationsCatalog.js +399 -0
  108. package/src/react/print/providers/local.js +1 -1
  109. package/src/react/router/publicRoutes.js +25 -0
  110. package/src/react/services/Cielo/Checkout.js +39 -0
  111. package/src/react/services/Cielo/Cielo.js +193 -0
  112. package/src/react/services/Cielo/Print.js +7 -0
  113. package/src/react/services/InfinitePay/Checkout.js +33 -0
  114. package/src/react/services/InfinitePay/InfinitePay.js +24 -0
  115. package/src/react/{utils → services}/paymentGatewayExecution.js +91 -91
  116. package/src/react/styles/global.js +115 -0
  117. package/src/react/utils/categoryContexts.js +46 -0
  118. package/src/react/utils/entityDisplay.js +222 -217
  119. package/src/react/utils/fileUrl.js +201 -16
  120. package/src/react/utils/integrationPage.js +16 -0
  121. package/src/react/utils/menuNavigation.js +31 -0
  122. package/src/react/utils/orderIdentity.js +277 -0
  123. package/src/react/utils/remotePayment.js +115 -61
  124. package/src/react/utils/runtimeFooter.js +40 -22
  125. package/src/react/utils/runtimeLanguage.js +50 -9
  126. package/src/react/utils/runtimeMenu.js +190 -98
  127. package/src/react/utils/runtimeZoom.js +43 -0
  128. package/src/react/utils/shopConfig.js +72 -34
  129. package/src/react/utils/shopFranchises.js +182 -23
  130. package/src/react/utils/socketRuntimePipeline.js +14 -14
  131. package/src/react/utils/storeColumns.js +23 -4
  132. package/src/react/utils/userAvatar.js +104 -0
  133. package/src/store/categories/index.js +48 -20
  134. package/src/store/configs/index.js +2 -2
  135. package/src/store/device_config/index.js +62 -21
  136. package/src/store/entity_log/index.js +22 -1
  137. package/src/store/imports/customActions.js +51 -0
  138. package/src/store/imports/index.js +89 -88
  139. package/src/store/status/index.js +11 -11
  140. package/src/store/themes/index.js +22 -0
  141. package/src/store/timezones/index.js +3 -1
  142. package/src/tests/browser/manager/connections.spec.js +143 -0
  143. package/src/tests/browser/manager/devices-current.spec.js +343 -0
  144. package/src/tests/browser/manager/mercado-livre-integration.spec.js +268 -0
  145. package/src/tests/react/api/deviceHeaders.test.js +114 -0
  146. package/src/tests/react/api/loadSmokeIndex.test.js +75 -0
  147. package/src/tests/react/components/AddImportModal.test.js +166 -0
  148. package/src/tests/react/components/BottomNavigationBar.config.test.js +20 -0
  149. package/src/tests/react/components/UserAvatar.test.js +52 -0
  150. package/src/tests/react/config/deviceConfigBootstrap.test.js +47 -0
  151. package/src/tests/react/pages/DevicePickerStyles.test.js +47 -0
  152. package/src/tests/react/pages/Devices/deviceListHelpers.test.js +39 -0
  153. package/src/tests/react/pages/DevicesCurrentDevice.test.js +198 -0
  154. package/src/tests/react/pages/connectionsHelpers.test.js +34 -0
  155. package/src/tests/react/pages/mercadoLivreIntegrationPageContract.test.mjs +16 -0
  156. package/src/tests/react/print/localPrintProvider.test.js +1 -1
  157. package/src/tests/react/services/Cielo.test.js +190 -0
  158. package/src/tests/react/store/categoriesOrdering.test.js +26 -0
  159. package/src/tests/react/utils/appDomain.test.js +62 -0
  160. package/src/tests/react/utils/entityDisplay.test.js +2 -2
  161. package/src/tests/react/utils/fileUrl.test.js +62 -0
  162. package/src/tests/react/utils/formatter.test.mjs +33 -0
  163. package/src/tests/react/utils/importStatus.test.js +2 -2
  164. package/src/tests/react/utils/orderIdentity.test.js +139 -0
  165. package/src/tests/react/utils/remotePayment.test.js +62 -0
  166. package/src/tests/react/utils/runtimeFooter.test.js +32 -9
  167. package/src/tests/react/utils/runtimeLanguage.test.js +80 -10
  168. package/src/tests/react/utils/runtimeMenu.test.js +195 -111
  169. package/src/tests/react/utils/runtimeZoom.test.js +38 -0
  170. package/src/tests/react/utils/shopConfig.test.js +75 -0
  171. package/src/tests/react/utils/shopFranchises.test.js +122 -12
  172. package/src/tests/react/utils/translate.discovery.test.mjs +170 -0
  173. package/src/tests/react/utils/translate.test.helpers.mjs +152 -0
  174. package/src/tests/react/utils/translate.test.mjs +252 -396
  175. package/src/tests/react/utils/userAvatar.test.js +63 -0
  176. package/src/utils/appDomain.js +15 -4
  177. package/src/utils/formatter.js +122 -16
  178. package/src/utils/integrationConfigs.js +1 -0
  179. package/src/utils/translate.js +277 -398
  180. package/src/utils/translateDiscovery.js +96 -0
  181. package/src/utils/translateNormalize.js +33 -0
  182. package/src/utils/translatePending.js +92 -0
  183. package/src/utils/translateStorage.js +142 -0
  184. package/.github/agents/developer.agent.md +0 -30
  185. package/.github/agents/devops.agent.md +0 -30
  186. package/.github/agents/qa.agent.md +0 -30
  187. package/.github/agents/security.agent.md +0 -30
  188. package/.github/workflows/npmjs.yml +0 -35
@@ -0,0 +1,114 @@
1
+ const {afterAll, afterEach, beforeEach, describe, expect, it} = global;
2
+ const {jest} = require('@jest/globals');
3
+
4
+ jest.mock('react-native', () => ({
5
+ Platform: {
6
+ OS: 'web',
7
+ select: options => options.web || options.default || options.ios || options.android,
8
+ },
9
+ }));
10
+
11
+ const originalLocalStorage = global.localStorage;
12
+
13
+ global.localStorage = {
14
+ clear: jest.fn(),
15
+ getItem: jest.fn(() => '{}'),
16
+ removeItem: jest.fn(),
17
+ setItem: jest.fn(),
18
+ };
19
+
20
+ const {api} = require('@controleonline/ui-common/src/api');
21
+
22
+ describe('device request headers', () => {
23
+ const originalFetch = global.fetch;
24
+ const originalGetToken = api.getToken;
25
+
26
+ beforeEach(() => {
27
+ global.fetch = jest.fn().mockResolvedValue({
28
+ ok: true,
29
+ status: 200,
30
+ statusText: 'OK',
31
+ text: async () => JSON.stringify({'hydra:member': []}),
32
+ });
33
+ api.getToken = jest.fn().mockResolvedValue('session-token');
34
+ });
35
+
36
+ afterEach(() => {
37
+ global.fetch = originalFetch;
38
+ api.getToken = originalGetToken;
39
+ jest.restoreAllMocks();
40
+ });
41
+
42
+ afterAll(() => {
43
+ global.localStorage = originalLocalStorage;
44
+ });
45
+
46
+ it('sends the operational type from the same runtime device as the identifier', async () => {
47
+ global.localStorage.getItem.mockImplementation(key => {
48
+ if (key === 'device') {
49
+ return JSON.stringify({id: 'web-31477', type: 'PDV'});
50
+ }
51
+
52
+ return '{}';
53
+ });
54
+
55
+ await api.fetch('/product-showcases/catalog', {
56
+ params: {type: ['service', 'product']},
57
+ });
58
+
59
+ const [, options] = global.fetch.mock.calls[0];
60
+ expect(options.headers.get('DEVICE')).toBe('web-31477');
61
+ expect(options.headers.get('DEVICE-TYPE')).toBe('PDV');
62
+ });
63
+
64
+ it('keeps master device identifier and type paired when it is selected', async () => {
65
+ global.localStorage.getItem.mockImplementation(key => {
66
+ if (key === 'device') {
67
+ return JSON.stringify({id: 'web-31477', type: 'MANAGER'});
68
+ }
69
+
70
+ if (key === 'master-device') {
71
+ return JSON.stringify({id: 'terminal-cielo-01', type: 'PDV'});
72
+ }
73
+
74
+ return '{}';
75
+ });
76
+
77
+ await api.fetch('/product-showcases/catalog');
78
+
79
+ const [, options] = global.fetch.mock.calls[0];
80
+ expect(options.headers.get('DEVICE')).toBe('terminal-cielo-01');
81
+ expect(options.headers.get('DEVICE-TYPE')).toBe('PDV');
82
+ });
83
+
84
+ it('does not guess a device type when the selected device has none', async () => {
85
+ global.localStorage.getItem.mockImplementation(key =>
86
+ key === 'device' ? JSON.stringify({id: 'legacy-device'}) : '{}',
87
+ );
88
+
89
+ await api.fetch('/product-showcases/catalog');
90
+
91
+ const [, options] = global.fetch.mock.calls[0];
92
+ expect(options.headers.get('DEVICE')).toBe('legacy-device');
93
+ expect(options.headers.get('DEVICE-TYPE')).toBeNull();
94
+ });
95
+
96
+ it('keeps the app domain in the header and removes it from query params', async () => {
97
+ await api.fetch('/people/company/default?app-domain=loja.exemplo.com.br&existing=1', {
98
+ params: {
99
+ 'app-domain': 'loja.exemplo.com.br',
100
+ AppDomain: 'loja.exemplo.com.br',
101
+ q: 'shop',
102
+ },
103
+ });
104
+
105
+ const [url, options] = global.fetch.mock.calls[0];
106
+ const requestUrl = new URL(String(url));
107
+
108
+ expect(options.headers.get('App-Domain')).toBeTruthy();
109
+ expect(requestUrl.searchParams.has('app-domain')).toBe(false);
110
+ expect(requestUrl.searchParams.has('AppDomain')).toBe(false);
111
+ expect(requestUrl.searchParams.get('existing')).toBe('1');
112
+ expect(requestUrl.searchParams.get('q')).toBe('shop');
113
+ });
114
+ });
@@ -0,0 +1,75 @@
1
+ const {afterAll, afterEach, beforeEach, describe, expect, it} = global;
2
+ const {jest} = require('@jest/globals');
3
+
4
+ jest.mock('react-native', () => ({
5
+ Platform: {
6
+ OS: 'web',
7
+ select: options => options.web || options.default || options.ios || options.android,
8
+ },
9
+ }));
10
+
11
+ const originalLocalStorage = global.localStorage;
12
+
13
+ global.localStorage = {
14
+ clear: jest.fn(),
15
+ getItem: jest.fn(() => '{}'),
16
+ removeItem: jest.fn(),
17
+ setItem: jest.fn(),
18
+ };
19
+
20
+ const {api} = require('@controleonline/ui-common/src/api');
21
+
22
+ describe('smoke api helper', () => {
23
+ const originalFetch = global.fetch;
24
+ const originalGetToken = api.getToken;
25
+
26
+ beforeEach(() => {
27
+ global.fetch = jest.fn();
28
+ api.getToken = jest.fn().mockResolvedValue('session-token');
29
+ });
30
+
31
+ afterEach(() => {
32
+ global.fetch = originalFetch;
33
+ api.getToken = originalGetToken;
34
+ jest.restoreAllMocks();
35
+ });
36
+
37
+ afterAll(() => {
38
+ global.localStorage = originalLocalStorage;
39
+ });
40
+
41
+ it('loads the smoke index from the canonical /tests endpoint and sends the app-domain', async () => {
42
+ global.fetch.mockResolvedValue({
43
+ ok: true,
44
+ status: 200,
45
+ statusText: 'OK',
46
+ text: async () =>
47
+ JSON.stringify({
48
+ status: 'idle',
49
+ summary: {
50
+ types: {total: 0, passed: 0, failed: 0},
51
+ suites: {total: 0, passed: 0, failed: 0},
52
+ tests: {total: 0, passed: 0, failed: 0},
53
+ },
54
+ types: [],
55
+ suites: [],
56
+ }),
57
+ });
58
+
59
+ const response = await api.loadSmokeIndex({
60
+ apiBaseUrl: 'https://smoke.example.test',
61
+ domain: 'https://admin.controleonline.com/tests-playground',
62
+ });
63
+
64
+ expect(global.fetch).toHaveBeenCalledTimes(1);
65
+
66
+ const [url, options] = global.fetch.mock.calls[0];
67
+ expect(url).toBe('https://smoke.example.test/tests');
68
+ expect(options.headers.get('App-Domain')).toBe('admin.controleonline.com');
69
+ expect(options.headers.get('API-TOKEN')).toBe('session-token');
70
+ expect(options.headers.get('X-API-KEY')).toBeNull();
71
+ expect(options.headers.get('Accept')).toBe('application/ld+json');
72
+ expect(options.headers.get('Content-Type')).toBe('application/ld+json');
73
+ expect(response.status).toBe('idle');
74
+ });
75
+ });
@@ -0,0 +1,166 @@
1
+ const React = require('react');
2
+ const renderer = require('react-test-renderer');
3
+ const {jest} = require('@jest/globals');
4
+
5
+ const {describe, expect, it, beforeEach} = global;
6
+
7
+ global.IS_REACT_ACT_ENVIRONMENT = true;
8
+
9
+ const importActions = {
10
+ uploadImportFile: jest.fn(),
11
+ };
12
+
13
+ jest.mock('@store', () => ({
14
+ useStore: jest.fn(name => {
15
+ if (name === 'people') {
16
+ return {
17
+ getters: {
18
+ currentCompany: {id: 7},
19
+ },
20
+ actions: {},
21
+ };
22
+ }
23
+
24
+ if (name === 'imports') {
25
+ return {
26
+ getters: {},
27
+ actions: importActions,
28
+ };
29
+ }
30
+
31
+ return {
32
+ getters: {},
33
+ actions: {},
34
+ };
35
+ }),
36
+ }));
37
+
38
+ jest.mock('@controleonline/ui-common/src/react/components/AnimatedModal', () =>
39
+ props => (props.visible ? React.createElement('modal', props, props.children) : null),
40
+ );
41
+
42
+ jest.mock('@controleonline/ui-common/src/react/components/MessageService', () => ({
43
+ useMessage: () => ({
44
+ showError: jest.fn(),
45
+ showSuccess: jest.fn(),
46
+ }),
47
+ }));
48
+
49
+ jest.mock('react-native-vector-icons/MaterialIcons', () => props =>
50
+ React.createElement('icon', props, props.children),
51
+ {virtual: true},
52
+ );
53
+
54
+ jest.mock('react-native-vector-icons/Feather', () => props =>
55
+ React.createElement('icon', props, props.children),
56
+ {virtual: true},
57
+ );
58
+
59
+ jest.mock('@controleonline/ui-default/src/react/components/upload/DefaultUpload', () => {
60
+ const React = require('react');
61
+
62
+ return props => {
63
+ const trigger =
64
+ typeof props.renderTrigger === 'function'
65
+ ? props.renderTrigger({
66
+ openManager: jest.fn(),
67
+ uploading: false,
68
+ })
69
+ : null;
70
+
71
+ return React.createElement(
72
+ 'defaultupload',
73
+ props,
74
+ trigger,
75
+ React.createElement(
76
+ 'touchableopacity',
77
+ {
78
+ onPress: () =>
79
+ props.onUploadFile({
80
+ file: {
81
+ name: 'import.csv',
82
+ uri: 'file:///import.csv',
83
+ mimeType: 'text/csv',
84
+ },
85
+ }),
86
+ },
87
+ React.createElement('text', null, 'Enviar via gerenciador'),
88
+ ),
89
+ );
90
+ };
91
+ });
92
+
93
+ jest.mock('react-native', () => ({
94
+ ActivityIndicator: props => React.createElement('activityindicator', props, props.children),
95
+ Platform: {OS: 'web'},
96
+ StyleSheet: {
97
+ create: value => value,
98
+ },
99
+ ScrollView: props => React.createElement('scrollview', props, props.children),
100
+ Text: props => React.createElement('text', props, props.children),
101
+ TouchableOpacity: props => React.createElement('touchableopacity', props, props.children),
102
+ View: props => React.createElement('view', props, props.children),
103
+ }));
104
+
105
+ const AddImportModal = require('@controleonline/ui-common/src/react/components/AddImportModal').default;
106
+
107
+ const collectText = node =>
108
+ node
109
+ .findAllByType('text')
110
+ .map(textNode => {
111
+ const children = textNode.props.children;
112
+ if (Array.isArray(children)) {
113
+ return children.flat(Infinity).join('');
114
+ }
115
+ return children || '';
116
+ })
117
+ .join('');
118
+
119
+ const findButtonByLabel = (root, label) =>
120
+ root.findAllByType('touchableopacity').find(button => collectText(button).includes(label));
121
+
122
+ const findButtonByExactLabel = (root, label) =>
123
+ root.findAllByType('touchableopacity').find(button => collectText(button) === label);
124
+
125
+ describe('AddImportModal', () => {
126
+ beforeEach(() => {
127
+ jest.clearAllMocks();
128
+ global.t = {
129
+ t: (_scope, _kind, key) => key,
130
+ };
131
+
132
+ global.document = undefined;
133
+ });
134
+
135
+ it('usa a action do store imports para enviar o arquivo', async () => {
136
+ importActions.uploadImportFile.mockResolvedValue({});
137
+ let tree;
138
+
139
+ await renderer.act(async () => {
140
+ tree = renderer.create(
141
+ React.createElement(AddImportModal, {
142
+ visible: true,
143
+ onClose: jest.fn(),
144
+ onSuccess: jest.fn(),
145
+ context: {context: 'product'},
146
+ }),
147
+ );
148
+ });
149
+
150
+ const selectButton = findButtonByLabel(tree.root, 'select_file');
151
+ expect(selectButton).toBeTruthy();
152
+
153
+ const importButton = findButtonByExactLabel(tree.root, 'Enviar via gerenciador');
154
+ expect(importButton).toBeTruthy();
155
+
156
+ await renderer.act(async () => {
157
+ await importButton.props.onPress();
158
+ });
159
+
160
+ expect(importActions.uploadImportFile).toHaveBeenCalledWith({
161
+ file: expect.objectContaining({name: 'import.csv'}),
162
+ importType: 'product',
163
+ peopleId: 7,
164
+ });
165
+ });
166
+ });
@@ -0,0 +1,20 @@
1
+ import {
2
+ resolveBottomNavigationItems,
3
+ BOTTOM_NAVIGATION_PRESETS,
4
+ } from '../../../react/components/BottomNavigationBar.config';
5
+
6
+ describe('BottomNavigationBar.config', () => {
7
+ it('uses Portuguese fallbacks when translate is missing', () => {
8
+ const items = resolveBottomNavigationItems(
9
+ BOTTOM_NAVIGATION_PRESETS.crmToolbar.items,
10
+ null,
11
+ );
12
+ const labels = items.map(i => i.label);
13
+ expect(labels).toContain('Início');
14
+ expect(labels).toContain('Clientes');
15
+ expect(labels).toContain('Perfil');
16
+ expect(labels).not.toContain('Home');
17
+ expect(labels).not.toContain('Clients');
18
+ expect(labels).not.toContain('Profile');
19
+ });
20
+ });
@@ -0,0 +1,52 @@
1
+ const React = require('react');
2
+ const renderer = require('react-test-renderer');
3
+ const {jest} = require('@jest/globals');
4
+
5
+ jest.mock('react-native', () => ({
6
+ Image: props => React.createElement('Image', props),
7
+ StyleSheet: {
8
+ create: styles => styles,
9
+ },
10
+ Text: props => React.createElement('Text', props, props.children),
11
+ View: props => React.createElement('View', props, props.children),
12
+ }));
13
+
14
+ const UserAvatar = require('../../../react/components/UserAvatar').default;
15
+
16
+ const {describe, expect, it} = global;
17
+
18
+ describe('UserAvatar', () => {
19
+ it('renders initials without requesting gravatar when disabled', () => {
20
+ let tree;
21
+
22
+ renderer.act(() => {
23
+ tree = renderer.create(
24
+ React.createElement(UserAvatar, {
25
+ email: 'client@example.com',
26
+ name: 'Client Test',
27
+ useGravatar: false,
28
+ }),
29
+ );
30
+ });
31
+
32
+ expect(tree.root.findAllByType('Image')).toHaveLength(0);
33
+ expect(tree.root.findByType('Text').props.children).toBe('CT');
34
+ });
35
+
36
+ it('uses gravatar as fallback when enabled', () => {
37
+ let tree;
38
+
39
+ renderer.act(() => {
40
+ tree = renderer.create(
41
+ React.createElement(UserAvatar, {
42
+ email: 'client@example.com',
43
+ name: 'Client Test',
44
+ }),
45
+ );
46
+ });
47
+
48
+ const image = tree.root.findByType('Image');
49
+
50
+ expect(image.props.source.uri).toContain('gravatar.com/avatar/');
51
+ });
52
+ });
@@ -18,6 +18,8 @@ const {
18
18
  POS_CASH_MANAGEMENT_MODE_CONFIG_KEY,
19
19
  POS_DELIVERY_ENABLED_CONFIG_KEY,
20
20
  POS_OPERATION_MODE_CONFIG_KEY,
21
+ POS_CHECK_ORDER_TYPE_CONFIG_KEY,
22
+ POS_CHECK_ORDER_TYPE_STAMP,
21
23
  isPosSingleItemMode,
22
24
  isPosAutoPrintEnabled,
23
25
  isPosCashRegisterClosed,
@@ -30,9 +32,11 @@ const {
30
32
  isDisplaySideBreakEnabled,
31
33
  normalizeDisplaySize,
32
34
  resolveDisplaySize,
35
+ resolvePosCheckOrderTypeForShop,
33
36
  resolvePosCashManagementMode,
34
37
  resolvePosOperationMode,
35
38
  resolvePosPrintMode,
39
+ resolvePosCheckOrderType,
36
40
  shouldEnableAndroidKioskMode,
37
41
  shouldEnableAndroidLauncherMode,
38
42
  shouldUsePosCashRegisterLifecycle,
@@ -179,6 +183,49 @@ describe('deviceConfigBootstrap POS operation helpers', () => {
179
183
  ).toBe(false)
180
184
  })
181
185
 
186
+ it('normalizes stamp linked order types for the POS config', () => {
187
+ expect(
188
+ resolvePosCheckOrderType({
189
+ [POS_CHECK_ORDER_TYPE_CONFIG_KEY]: 'stamp',
190
+ }),
191
+ ).toBe(POS_CHECK_ORDER_TYPE_STAMP)
192
+ expect(
193
+ resolvePosCheckOrderType({
194
+ [POS_CHECK_ORDER_TYPE_CONFIG_KEY]: 'carimbo',
195
+ }),
196
+ ).toBe(POS_CHECK_ORDER_TYPE_STAMP)
197
+ })
198
+
199
+ it('disables stamp when shop loyalty coupons are off', () => {
200
+ expect(
201
+ resolvePosCheckOrderTypeForShop(
202
+ {
203
+ [POS_CHECK_ORDER_TYPE_CONFIG_KEY]: 'stamp',
204
+ },
205
+ {
206
+ 'shop-loyalty-coupons-enabled': '0',
207
+ },
208
+ ),
209
+ ).toBe('none')
210
+
211
+ expect(
212
+ resolvePosCheckOrderTypeForShop(
213
+ {
214
+ [POS_CHECK_ORDER_TYPE_CONFIG_KEY]: 'stamp',
215
+ },
216
+ {
217
+ 'shop-loyalty-coupons-enabled': '1',
218
+ },
219
+ ),
220
+ ).toBe(POS_CHECK_ORDER_TYPE_STAMP)
221
+
222
+ expect(
223
+ resolvePosCheckOrderTypeForShop({
224
+ [POS_CHECK_ORDER_TYPE_CONFIG_KEY]: 'stamp',
225
+ }),
226
+ ).toBe(POS_CHECK_ORDER_TYPE_STAMP)
227
+ })
228
+
182
229
  it('defaults display size to 5 and clamps the configured range to 1..10', () => {
183
230
  expect(resolveDisplaySize({})).toBe(DISPLAY_SIZE_DEFAULT)
184
231
  expect(
@@ -0,0 +1,47 @@
1
+ const assert = require('node:assert/strict');
2
+ const fs = require('node:fs');
3
+ const path = require('node:path');
4
+ const {test} = require('@jest/globals');
5
+
6
+ const readFixture = relativePath =>
7
+ fs.readFileSync(path.resolve(__dirname, relativePath), 'utf8');
8
+
9
+ test('device detail display pickers keep explicit native styling hooks', () => {
10
+ const source = readFixture('../../../react/pages/DeviceDetailPage.js');
11
+ const styles = readFixture('../../../react/pages/DeviceDetailPage.styles.js');
12
+
13
+ assert.match(
14
+ source,
15
+ /selectedValue=\{linkedDisplayId \|\| ''\}[\s\S]*style=\{styles\.picker\}[\s\S]*dropdownIconColor="#64748B"/,
16
+ );
17
+ assert.match(
18
+ source,
19
+ /selectedValue=\{displayPrinterId \|\| ''\}[\s\S]*style=\{styles\.picker\}[\s\S]*dropdownIconColor="#64748B"/,
20
+ );
21
+ assert.match(
22
+ source,
23
+ /selectedValue=\{devicePaymentTarget \|\| ''\}[\s\S]*style=\{styles\.picker\}[\s\S]*dropdownIconColor="#64748B"/,
24
+ );
25
+ assert.match(
26
+ styles,
27
+ /picker:\s*\{[\s\S]*minHeight:\s*44,[\s\S]*color:\s*'#0F172A'[\s\S]*backgroundColor:\s*'#F8FAFC'/,
28
+ );
29
+ });
30
+
31
+ test('printer device detail pickers keep explicit native styling hooks', () => {
32
+ const source = readFixture('../../../react/pages/PrinterDeviceDetailPage.js');
33
+ const styles = readFixture('../../../react/pages/PrinterDeviceDetailPage.styles.js');
34
+
35
+ assert.match(
36
+ source,
37
+ /selectedValue=\{protocol \|\| DEFAULT_NETWORK_CAMERA_PROTOCOL\}[\s\S]*style=\{styles\.picker\}[\s\S]*dropdownIconColor="#64748B"/,
38
+ );
39
+ assert.match(
40
+ source,
41
+ /selectedValue=\{managerDeviceId \|\| ''\}[\s\S]*style=\{styles\.picker\}[\s\S]*dropdownIconColor="#64748B"/,
42
+ );
43
+ assert.match(
44
+ styles,
45
+ /picker:\s*\{[\s\S]*minHeight:\s*44,[\s\S]*color:\s*'#0F172A'[\s\S]*backgroundColor:\s*'#F8FAFC'/,
46
+ );
47
+ });
@@ -0,0 +1,39 @@
1
+ /* global describe, expect, it */
2
+
3
+ const {
4
+ mergeDeviceConfigs,
5
+ buildDeviceListParams,
6
+ PAGE_SIZE,
7
+ } = require('../../../../react/pages/Devices/deviceTypes/deviceListHelpers');
8
+
9
+ describe('deviceListHelpers', () => {
10
+ it('mergeDeviceConfigs dedupes by id', () => {
11
+ const merged = mergeDeviceConfigs(
12
+ [{id: 1, type: 'PDV'}, {id: 2, type: 'PRINTER'}],
13
+ [{id: 2, type: 'PRINTER'}, {id: 3, type: 'DISPLAY'}],
14
+ );
15
+ expect(merged.map(item => item.id)).toEqual([1, 2, 3]);
16
+ });
17
+
18
+ it('buildDeviceListParams sets people and single type', () => {
19
+ const params = buildDeviceListParams({
20
+ companyId: 9,
21
+ page: 1,
22
+ pageSize: PAGE_SIZE,
23
+ queryTypes: ['PDV'],
24
+ });
25
+ expect(params.people).toBe('/people/9');
26
+ expect(params.type).toBe('PDV');
27
+ expect(params.itemsPerPage).toBe(PAGE_SIZE);
28
+ });
29
+
30
+ it('buildDeviceListParams accepts multiple types', () => {
31
+ const params = buildDeviceListParams({
32
+ companyId: 1,
33
+ page: 2,
34
+ queryTypes: ['PDV', 'PRINTER'],
35
+ });
36
+ expect(params.type).toEqual(['PDV', 'PRINTER']);
37
+ expect(params.page).toBe(2);
38
+ });
39
+ });