@controleonline/ui-common 1.2.71 → 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 (150) 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 +3 -2
  11. package/src/api/fetch.js +1 -1
  12. package/src/api/index.js +92 -22
  13. package/src/react/components/AddImportModal.js +94 -110
  14. package/src/react/components/BottomNavigationBar.config.js +211 -231
  15. package/src/react/components/CategoryForm.js +52 -35
  16. package/src/react/components/DefaultProvider.native.js +131 -95
  17. package/src/react/components/DefaultProvider.web.js +148 -74
  18. package/src/react/components/NetworkPrinterPreviewModal.js +249 -0
  19. package/src/react/components/NetworkPrinterPreviewModal.styles.js +159 -0
  20. package/src/react/components/RuntimeInfoFooter.js +100 -36
  21. package/src/react/components/RuntimeInfoFooter.styles.js +3 -0
  22. package/src/react/components/UnifiedPaymentBar.js +7 -4
  23. package/src/react/components/UserAvatar.js +160 -0
  24. package/src/react/components/integrations/IntegrationHero/index.js +48 -0
  25. package/src/react/components/integrations/IntegrationHero/styles.js +57 -0
  26. package/src/react/components/integrations/IntegrationTabs/index.js +89 -0
  27. package/src/react/components/integrations/IntegrationTabs/styles.js +44 -0
  28. package/src/react/config/deviceConfigBootstrap.js +5 -3
  29. package/src/react/pages/Connections.js +229 -0
  30. package/src/react/pages/Connections.styles.js +222 -0
  31. package/src/react/pages/DeviceDetailPage.js +2814 -0
  32. package/src/react/pages/DeviceDetailPage.styles.js +308 -0
  33. package/src/react/pages/Devices/currentDevice.js +194 -0
  34. package/src/react/pages/Devices/deviceTypes/DeviceGroupCard.js +257 -0
  35. package/src/react/pages/Devices/deviceTypes/all.js +23 -0
  36. package/src/react/pages/Devices/deviceTypes/device.js +25 -0
  37. package/src/react/pages/Devices/deviceTypes/deviceListHelpers.js +178 -0
  38. package/src/react/pages/Devices/deviceTypes/display.js +26 -0
  39. package/src/react/pages/Devices/deviceTypes/index.js +145 -0
  40. package/src/react/pages/Devices/deviceTypes/ipCamera.js +35 -0
  41. package/src/react/pages/Devices/deviceTypes/pdv.js +88 -0
  42. package/src/react/pages/Devices/deviceTypes/printer.js +37 -0
  43. package/src/react/pages/Devices/deviceTypes/shared.js +453 -0
  44. package/src/react/pages/Devices/deviceTypes/useDeviceNetworkConnectivity.js +145 -0
  45. package/src/react/pages/Devices.js +183 -0
  46. package/src/react/pages/Devices.styles.js +353 -0
  47. package/src/react/pages/Food99IntegrationPage/components/Food99CatalogTab.js +219 -0
  48. package/src/react/pages/Food99IntegrationPage/components/Food99OverviewTab.js +134 -0
  49. package/src/react/pages/Food99IntegrationPage/components/Food99PreviewModal.js +173 -0
  50. package/src/react/pages/Food99IntegrationPage/components/Food99ProductCard.js +1 -0
  51. package/src/react/pages/Food99IntegrationPage/components/Food99QuickWalletModal.js +181 -0
  52. package/src/react/pages/Food99IntegrationPage/components/Food99SettingsTab.js +306 -0
  53. package/src/react/pages/Food99IntegrationPage/components/Food99StoreTab.js +417 -0
  54. package/src/react/pages/Food99IntegrationPage/index.js +1530 -0
  55. package/src/react/pages/Food99IntegrationPage/styles.js +792 -0
  56. package/src/react/pages/Food99IntegrationPage/utils.js +155 -0
  57. package/src/react/pages/Food99IntegrationPage.js +1 -0
  58. package/src/react/pages/Food99IntegrationPage.styles.js +1 -0
  59. package/src/react/pages/GenericLogPage.js +42 -167
  60. package/src/react/pages/IFoodIntegrationPage/components/IFoodCatalogTab.js +8 -0
  61. package/src/react/pages/IFoodIntegrationPage/components/IFoodOperationsTab.js +420 -0
  62. package/src/react/pages/IFoodIntegrationPage/components/IFoodOverviewTab.js +118 -0
  63. package/src/react/pages/IFoodIntegrationPage/components/IFoodPreviewModal.js +103 -0
  64. package/src/react/pages/IFoodIntegrationPage/components/IFoodProductCard.js +1 -0
  65. package/src/react/pages/IFoodIntegrationPage/components/IFoodStoreTab.js +150 -0
  66. package/src/react/pages/IFoodIntegrationPage/index.js +1298 -0
  67. package/src/react/pages/IFoodIntegrationPage/styles.js +764 -0
  68. package/src/react/pages/IFoodIntegrationPage/utils.js +91 -0
  69. package/src/react/pages/IFoodIntegrationPage.js +1 -0
  70. package/src/react/pages/IFoodIntegrationPage.styles.js +1 -0
  71. package/src/react/pages/Imports.js +214 -166
  72. package/src/react/pages/IntegrationConfigPage.js +744 -0
  73. package/src/react/pages/IntegrationConfigPage.styles.js +269 -0
  74. package/src/react/pages/Integrations.js +299 -0
  75. package/src/react/pages/Integrations.styles.js +130 -0
  76. package/src/react/pages/ManagerOrderNotificationsPage.js +642 -0
  77. package/src/react/pages/MarketplaceIntegrationPage/components/MarketplaceProductCard.js +205 -0
  78. package/src/react/pages/MarketplaceIntegrationPage/components/RemoteMarketplaceMenuTab.js +335 -0
  79. package/src/react/pages/MarketplaceIntegrationPage/index.js +60 -0
  80. package/src/react/pages/MarketplaceIntegrationPage/utils.js +137 -0
  81. package/src/react/pages/MercadoLivreIntegrationPage.js +538 -0
  82. package/src/react/pages/PrinterDeviceDetailPage.js +1916 -0
  83. package/src/react/pages/PrinterDeviceDetailPage.styles.js +223 -0
  84. package/src/react/pages/PrinterDeviceFormPage.js +521 -0
  85. package/src/react/pages/PrinterDeviceFormPage.styles.js +159 -0
  86. package/src/react/pages/WhatsAppConnectionPage.js +459 -0
  87. package/src/react/pages/WhatsAppConnectionPage.styles.js +284 -0
  88. package/src/react/pages/integrationsCatalog.js +399 -0
  89. package/src/react/services/Cielo/Cielo.js +193 -193
  90. package/src/react/utils/categoryContexts.js +46 -0
  91. package/src/react/utils/entityDisplay.js +222 -217
  92. package/src/react/utils/fileUrl.js +39 -20
  93. package/src/react/utils/integrationPage.js +16 -0
  94. package/src/react/utils/runtimeFooter.js +40 -22
  95. package/src/react/utils/runtimeLanguage.js +50 -9
  96. package/src/react/utils/runtimeMenu.js +191 -131
  97. package/src/react/utils/runtimeZoom.js +43 -0
  98. package/src/react/utils/shopConfig.js +27 -15
  99. package/src/react/utils/shopFranchises.js +126 -1
  100. package/src/react/utils/storeColumns.js +23 -4
  101. package/src/react/utils/userAvatar.js +104 -0
  102. package/src/store/categories/index.js +48 -20
  103. package/src/store/device_config/index.js +62 -21
  104. package/src/store/entity_log/index.js +22 -1
  105. package/src/store/imports/customActions.js +51 -0
  106. package/src/store/imports/index.js +89 -88
  107. package/src/store/status/index.js +11 -11
  108. package/src/store/themes/index.js +22 -0
  109. package/src/store/timezones/index.js +3 -1
  110. package/src/tests/browser/manager/connections.spec.js +143 -0
  111. package/src/tests/browser/manager/devices-current.spec.js +343 -0
  112. package/src/tests/browser/manager/mercado-livre-integration.spec.js +268 -0
  113. package/src/tests/react/api/deviceHeaders.test.js +114 -0
  114. package/src/tests/react/components/AddImportModal.test.js +166 -0
  115. package/src/tests/react/components/BottomNavigationBar.config.test.js +20 -0
  116. package/src/tests/react/components/UserAvatar.test.js +52 -0
  117. package/src/tests/react/pages/DevicePickerStyles.test.js +47 -0
  118. package/src/tests/react/pages/Devices/deviceListHelpers.test.js +39 -0
  119. package/src/tests/react/pages/DevicesCurrentDevice.test.js +198 -0
  120. package/src/tests/react/pages/connectionsHelpers.test.js +34 -0
  121. package/src/tests/react/pages/mercadoLivreIntegrationPageContract.test.mjs +16 -0
  122. package/src/tests/react/services/Cielo.test.js +190 -190
  123. package/src/tests/react/store/categoriesOrdering.test.js +26 -0
  124. package/src/tests/react/utils/appDomain.test.js +62 -0
  125. package/src/tests/react/utils/entityDisplay.test.js +2 -2
  126. package/src/tests/react/utils/fileUrl.test.js +3 -3
  127. package/src/tests/react/utils/formatter.test.mjs +33 -0
  128. package/src/tests/react/utils/runtimeFooter.test.js +29 -4
  129. package/src/tests/react/utils/runtimeLanguage.test.js +79 -9
  130. package/src/tests/react/utils/runtimeMenu.test.js +198 -134
  131. package/src/tests/react/utils/runtimeZoom.test.js +38 -0
  132. package/src/tests/react/utils/shopConfig.test.js +10 -9
  133. package/src/tests/react/utils/shopFranchises.test.js +43 -0
  134. package/src/tests/react/utils/translate.discovery.test.mjs +170 -0
  135. package/src/tests/react/utils/translate.test.helpers.mjs +152 -0
  136. package/src/tests/react/utils/translate.test.mjs +141 -349
  137. package/src/tests/react/utils/userAvatar.test.js +63 -0
  138. package/src/utils/appDomain.js +15 -4
  139. package/src/utils/formatter.js +104 -16
  140. package/src/utils/integrationConfigs.js +80 -80
  141. package/src/utils/translate.js +222 -438
  142. package/src/utils/translateDiscovery.js +96 -0
  143. package/src/utils/translateNormalize.js +33 -0
  144. package/src/utils/translatePending.js +92 -0
  145. package/src/utils/translateStorage.js +142 -0
  146. package/.github/agents/developer.agent.md +0 -30
  147. package/.github/agents/devops.agent.md +0 -30
  148. package/.github/agents/qa.agent.md +0 -30
  149. package/.github/agents/security.agent.md +0 -30
  150. package/.github/workflows/npmjs.yml +0 -35
@@ -1,190 +1,190 @@
1
- const {jest} = require('@jest/globals')
2
-
3
- const mockPayment = jest.fn()
4
- const mockFetch = jest.fn()
5
- const mockGetAllStores = jest.fn()
6
-
7
- jest.mock('@controleonline-rn/react-native-cielo-payment', () => ({
8
- payment: (...args) => mockPayment(...args),
9
- }))
10
-
11
- jest.mock('@controleonline/ui-common/src/api', () => ({
12
- api: {
13
- fetch: (...args) => mockFetch(...args),
14
- },
15
- }))
16
-
17
- jest.mock('@store', () => ({
18
- getAllStores: (...args) => mockGetAllStores(...args),
19
- }))
20
-
21
- jest.mock('@env', () => ({
22
- env: {
23
- CIELO: {
24
- ACCESS_TOKEN: '',
25
- CLIENT_ID: '',
26
- EMAIL: '',
27
- MERCHANT_CODE: '',
28
- },
29
- },
30
- }))
31
-
32
- const {describe, expect, it, beforeEach} = global
33
-
34
- const loadService = () => {
35
- const serviceModule = require('../../../react/services/Cielo/Cielo')
36
- return serviceModule.default || serviceModule
37
- }
38
-
39
- const runtimeStores = merchantCode => ({
40
- people: {
41
- getters: {
42
- currentCompany: {
43
- id: 99,
44
- configs: {
45
- CIELO: JSON.stringify({
46
- ACCESS_TOKEN: 'runtime-token',
47
- CLIENT_ID: 'runtime-client',
48
- EMAIL: 'runtime@empresa.com',
49
- ...(merchantCode ? {MERCHANT_CODE: merchantCode} : {}),
50
- }),
51
- },
52
- },
53
- defaultCompany: {id: 99},
54
- },
55
- },
56
- configs: {
57
- getters: {
58
- items: {
59
- CIELO: JSON.stringify({
60
- ACCESS_TOKEN: 'runtime-token',
61
- CLIENT_ID: 'runtime-client',
62
- EMAIL: 'runtime@empresa.com',
63
- ...(merchantCode ? {MERCHANT_CODE: merchantCode} : {}),
64
- }),
65
- },
66
- },
67
- },
68
- })
69
-
70
- describe('CieloService', () => {
71
- beforeEach(() => {
72
- jest.resetModules()
73
- mockPayment.mockReset()
74
- mockFetch.mockReset()
75
- mockGetAllStores.mockReset()
76
-
77
- mockPayment.mockResolvedValue({
78
- success: true,
79
- code: '0',
80
- result: JSON.stringify({
81
- status: 'ok',
82
- payments: [{merchantCode: '0000000000000001'}],
83
- }),
84
- })
85
- })
86
-
87
- it('falls back to the main company private CIELO config', async () => {
88
- mockGetAllStores.mockReturnValue({
89
- people: {
90
- getters: {
91
- currentCompany: {id: 21, configs: {}},
92
- defaultCompany: {id: 99},
93
- },
94
- },
95
- configs: {getters: {items: {}}},
96
- })
97
- mockFetch.mockResolvedValue({
98
- member: [
99
- {
100
- configKey: 'CIELO',
101
- configValue: JSON.stringify({
102
- ACCESS_TOKEN: 'private-token',
103
- CLIENT_ID: 'private-client',
104
- EMAIL: 'cielo@principal.com',
105
- MERCHANT_CODE: '0000000000000001',
106
- }),
107
- },
108
- ],
109
- })
110
-
111
- const CieloService = loadService()
112
- await new CieloService().payment('credit', [{name: 'Produto'}], 1250)
113
-
114
- expect(mockFetch).toHaveBeenCalledWith('/configs', {
115
- params: {
116
- configKey: 'CIELO',
117
- people: '/people/99',
118
- visibility: 'private',
119
- },
120
- })
121
- expect(JSON.parse(mockPayment.mock.calls[0][0])).toMatchObject({
122
- accessToken: 'private-token',
123
- clientID: 'private-client',
124
- email: 'cielo@principal.com',
125
- merchantCode: '0000000000000001',
126
- paymentCode: 'credit',
127
- value: 1250,
128
- })
129
- })
130
-
131
- it('sends the configured merchantCode to Cielo', async () => {
132
- mockGetAllStores.mockReturnValue(runtimeStores('0000000000000001'))
133
-
134
- const CieloService = loadService()
135
- await new CieloService().payment('debit', [], 500)
136
-
137
- expect(mockFetch).not.toHaveBeenCalled()
138
- expect(JSON.parse(mockPayment.mock.calls[0][0])).toMatchObject({
139
- merchantCode: '0000000000000001',
140
- paymentCode: 'debit',
141
- value: 500,
142
- })
143
- })
144
-
145
- it('keeps the legacy request when merchantCode is not configured', async () => {
146
- mockGetAllStores.mockReturnValue(runtimeStores(''))
147
-
148
- const CieloService = loadService()
149
- await new CieloService().payment('debit', [], 500)
150
-
151
- expect(JSON.parse(mockPayment.mock.calls[0][0])).not.toHaveProperty(
152
- 'merchantCode',
153
- )
154
- })
155
-
156
- it('rejects a successful response without merchantCode', async () => {
157
- mockGetAllStores.mockReturnValue(runtimeStores('0000000000000001'))
158
- mockPayment.mockResolvedValue({
159
- success: true,
160
- code: '0',
161
- result: JSON.stringify({status: 'ok', payments: [{}]}),
162
- })
163
-
164
- const CieloService = loadService()
165
- const response = await new CieloService().payment('debit', [], 500)
166
-
167
- expect(response.success).toBe(false)
168
- })
169
-
170
- it('rejects a successful response from a different merchantCode', async () => {
171
- mockGetAllStores.mockReturnValue(runtimeStores('0000000000000001'))
172
- mockPayment.mockResolvedValue({
173
- success: true,
174
- code: '0',
175
- result: JSON.stringify({
176
- status: 'ok',
177
- payments: [{merchantCode: '0000000000000002'}],
178
- }),
179
- })
180
-
181
- const CieloService = loadService()
182
- const response = await new CieloService().payment('debit', [], 500)
183
-
184
- expect(response).toMatchObject({
185
- success: false,
186
- result:
187
- 'Pagamento processado em estabelecimento Cielo diferente do configurado.',
188
- })
189
- })
190
- })
1
+ const {jest} = require('@jest/globals')
2
+
3
+ const mockPayment = jest.fn()
4
+ const mockFetch = jest.fn()
5
+ const mockGetAllStores = jest.fn()
6
+
7
+ jest.mock('@controleonline-rn/react-native-cielo-payment', () => ({
8
+ payment: (...args) => mockPayment(...args),
9
+ }))
10
+
11
+ jest.mock('@controleonline/ui-common/src/api', () => ({
12
+ api: {
13
+ fetch: (...args) => mockFetch(...args),
14
+ },
15
+ }))
16
+
17
+ jest.mock('@store', () => ({
18
+ getAllStores: (...args) => mockGetAllStores(...args),
19
+ }))
20
+
21
+ jest.mock('@env', () => ({
22
+ env: {
23
+ CIELO: {
24
+ ACCESS_TOKEN: '',
25
+ CLIENT_ID: '',
26
+ EMAIL: '',
27
+ MERCHANT_CODE: '',
28
+ },
29
+ },
30
+ }))
31
+
32
+ const {describe, expect, it, beforeEach} = global
33
+
34
+ const loadService = () => {
35
+ const serviceModule = require('../../../react/services/Cielo/Cielo')
36
+ return serviceModule.default || serviceModule
37
+ }
38
+
39
+ const runtimeStores = merchantCode => ({
40
+ people: {
41
+ getters: {
42
+ currentCompany: {
43
+ id: 99,
44
+ configs: {
45
+ CIELO: JSON.stringify({
46
+ ACCESS_TOKEN: 'runtime-token',
47
+ CLIENT_ID: 'runtime-client',
48
+ EMAIL: 'runtime@empresa.com',
49
+ ...(merchantCode ? {MERCHANT_CODE: merchantCode} : {}),
50
+ }),
51
+ },
52
+ },
53
+ defaultCompany: {id: 99},
54
+ },
55
+ },
56
+ configs: {
57
+ getters: {
58
+ items: {
59
+ CIELO: JSON.stringify({
60
+ ACCESS_TOKEN: 'runtime-token',
61
+ CLIENT_ID: 'runtime-client',
62
+ EMAIL: 'runtime@empresa.com',
63
+ ...(merchantCode ? {MERCHANT_CODE: merchantCode} : {}),
64
+ }),
65
+ },
66
+ },
67
+ },
68
+ })
69
+
70
+ describe('CieloService', () => {
71
+ beforeEach(() => {
72
+ jest.resetModules()
73
+ mockPayment.mockReset()
74
+ mockFetch.mockReset()
75
+ mockGetAllStores.mockReset()
76
+
77
+ mockPayment.mockResolvedValue({
78
+ success: true,
79
+ code: '0',
80
+ result: JSON.stringify({
81
+ status: 'ok',
82
+ payments: [{merchantCode: '0000000000000001'}],
83
+ }),
84
+ })
85
+ })
86
+
87
+ it('falls back to the main company private CIELO config', async () => {
88
+ mockGetAllStores.mockReturnValue({
89
+ people: {
90
+ getters: {
91
+ currentCompany: {id: 21, configs: {}},
92
+ defaultCompany: {id: 99},
93
+ },
94
+ },
95
+ configs: {getters: {items: {}}},
96
+ })
97
+ mockFetch.mockResolvedValue({
98
+ member: [
99
+ {
100
+ configKey: 'CIELO',
101
+ configValue: JSON.stringify({
102
+ ACCESS_TOKEN: 'private-token',
103
+ CLIENT_ID: 'private-client',
104
+ EMAIL: 'cielo@principal.com',
105
+ MERCHANT_CODE: '0000000000000001',
106
+ }),
107
+ },
108
+ ],
109
+ })
110
+
111
+ const CieloService = loadService()
112
+ await new CieloService().payment('credit', [{name: 'Produto'}], 1250)
113
+
114
+ expect(mockFetch).toHaveBeenCalledWith('/configs', {
115
+ params: {
116
+ configKey: 'CIELO',
117
+ people: '/people/99',
118
+ visibility: 'private',
119
+ },
120
+ })
121
+ expect(JSON.parse(mockPayment.mock.calls[0][0])).toMatchObject({
122
+ accessToken: 'private-token',
123
+ clientID: 'private-client',
124
+ email: 'cielo@principal.com',
125
+ merchantCode: '0000000000000001',
126
+ paymentCode: 'credit',
127
+ value: 1250,
128
+ })
129
+ })
130
+
131
+ it('sends the configured merchantCode to Cielo', async () => {
132
+ mockGetAllStores.mockReturnValue(runtimeStores('0000000000000001'))
133
+
134
+ const CieloService = loadService()
135
+ await new CieloService().payment('debit', [], 500)
136
+
137
+ expect(mockFetch).not.toHaveBeenCalled()
138
+ expect(JSON.parse(mockPayment.mock.calls[0][0])).toMatchObject({
139
+ merchantCode: '0000000000000001',
140
+ paymentCode: 'debit',
141
+ value: 500,
142
+ })
143
+ })
144
+
145
+ it('keeps the legacy request when merchantCode is not configured', async () => {
146
+ mockGetAllStores.mockReturnValue(runtimeStores(''))
147
+
148
+ const CieloService = loadService()
149
+ await new CieloService().payment('debit', [], 500)
150
+
151
+ expect(JSON.parse(mockPayment.mock.calls[0][0])).not.toHaveProperty(
152
+ 'merchantCode',
153
+ )
154
+ })
155
+
156
+ it('rejects a successful response without merchantCode', async () => {
157
+ mockGetAllStores.mockReturnValue(runtimeStores('0000000000000001'))
158
+ mockPayment.mockResolvedValue({
159
+ success: true,
160
+ code: '0',
161
+ result: JSON.stringify({status: 'ok', payments: [{}]}),
162
+ })
163
+
164
+ const CieloService = loadService()
165
+ const response = await new CieloService().payment('debit', [], 500)
166
+
167
+ expect(response.success).toBe(false)
168
+ })
169
+
170
+ it('rejects a successful response from a different merchantCode', async () => {
171
+ mockGetAllStores.mockReturnValue(runtimeStores('0000000000000001'))
172
+ mockPayment.mockResolvedValue({
173
+ success: true,
174
+ code: '0',
175
+ result: JSON.stringify({
176
+ status: 'ok',
177
+ payments: [{merchantCode: '0000000000000002'}],
178
+ }),
179
+ })
180
+
181
+ const CieloService = loadService()
182
+ const response = await new CieloService().payment('debit', [], 500)
183
+
184
+ expect(response).toMatchObject({
185
+ success: false,
186
+ result:
187
+ 'Pagamento processado em estabelecimento Cielo diferente do configurado.',
188
+ })
189
+ })
190
+ })
@@ -0,0 +1,26 @@
1
+ /* global jest */
2
+ jest.mock('@controleonline/ui-default/src/store/default/actions', () => ({}))
3
+ jest.mock('@controleonline/ui-default/src/store/default/getters', () => ({}))
4
+ jest.mock('@controleonline/ui-default/src/store/default/mutations', () => ({
5
+ __esModule: true,
6
+ default: {},
7
+ }))
8
+
9
+ const categoriesStore = require('../../../store/categories').default
10
+
11
+ const {describe, expect, it} = global
12
+
13
+ describe('categories store ordering', () => {
14
+ it('exposes the canonical order as the editable default table sort', () => {
15
+ const column = categoriesStore.state.columns.find(item => item.name === 'sortOrder')
16
+
17
+ expect(column).toMatchObject({
18
+ defaultSort: 'ASC',
19
+ editable: true,
20
+ inputType: 'number',
21
+ sortable: true,
22
+ })
23
+ expect(column.saveFormat(0)).toBe(0)
24
+ expect(column.saveFormat('')).toBeNull()
25
+ })
26
+ })
@@ -0,0 +1,62 @@
1
+ const {describe, expect, it} = global
2
+
3
+ const {resolveAppDomain} = require('../../../utils/appDomain')
4
+
5
+ describe('resolveAppDomain', () => {
6
+ const withLocation = (host, fn) => {
7
+ const previousLocation = Object.getOwnPropertyDescriptor(global, 'location')
8
+ try {
9
+ Object.defineProperty(global, 'location', {
10
+ configurable: true,
11
+ value: {host},
12
+ })
13
+ fn()
14
+ } finally {
15
+ if (previousLocation) {
16
+ Object.defineProperty(global, 'location', previousLocation)
17
+ } else {
18
+ delete global.location
19
+ }
20
+ }
21
+ }
22
+
23
+ it('ignores invalid configured domain values and uses the runtime host', () => {
24
+ withLocation('erp.exemplo.com.br', () => {
25
+ expect(resolveAppDomain('undefined')).toBe('erp.exemplo.com.br')
26
+ expect(resolveAppDomain('null')).toBe('erp.exemplo.com.br')
27
+ expect(resolveAppDomain('false')).toBe('erp.exemplo.com.br')
28
+ })
29
+ })
30
+
31
+ it('prefers window.location.host over configured DOMAIN (multi-tenant web)', () => {
32
+ withLocation('erpjaguncos.com.br', () => {
33
+ expect(resolveAppDomain('https://app.controleonline.com')).toBe(
34
+ 'erpjaguncos.com.br',
35
+ )
36
+ expect(resolveAppDomain('app.controleonline.com')).toBe(
37
+ 'erpjaguncos.com.br',
38
+ )
39
+ })
40
+ })
41
+
42
+ it('uses configured domain when there is no runtime location (native)', () => {
43
+ const previousLocation = Object.getOwnPropertyDescriptor(global, 'location')
44
+ try {
45
+ Object.defineProperty(global, 'location', {
46
+ configurable: true,
47
+ value: undefined,
48
+ })
49
+ // resolveRuntimeHost returns '' when location is undefined
50
+ expect(resolveAppDomain('https://app.controleonline.com')).toBe(
51
+ 'app.controleonline.com',
52
+ )
53
+ expect(resolveAppDomain('app.controleonline.com')).toBe(
54
+ 'app.controleonline.com',
55
+ )
56
+ } finally {
57
+ if (previousLocation) {
58
+ Object.defineProperty(global, 'location', previousLocation)
59
+ }
60
+ }
61
+ })
62
+ })
@@ -12,7 +12,7 @@ describe('entityDisplay helpers', () => {
12
12
  });
13
13
 
14
14
  it('uppercases pt-BR strings with cedilla correctly', () => {
15
- expect(formatDisplayUppercase('Jagunços')).toBe('JAGUNÇOS');
16
- expect(formatDisplayUppercase('Empório Jagunços Ltda')).toBe('EMPÓRIO JAGUNÇOS LTDA');
15
+ expect(formatDisplayUppercase('Empresa Exemplo')).toBe('EMPRESA EXEMPLO');
16
+ expect(formatDisplayUppercase('Empresa Exemplo Ltda')).toBe('EMPRESA EXEMPLO LTDA');
17
17
  });
18
18
  });
@@ -14,7 +14,7 @@ const {
14
14
  } = require('../../../react/utils/fileUrl');
15
15
 
16
16
  describe('fileUrl helpers', () => {
17
- it('adds app-domain to backend download urls and keeps headers', () => {
17
+ it('uses path app-domain for backend download urls and keeps headers', () => {
18
18
  const company = {
19
19
  domain: 'maincompany.controleonline.com',
20
20
  };
@@ -33,14 +33,14 @@ describe('fileUrl helpers', () => {
33
33
  );
34
34
 
35
35
  expect(source).toEqual({
36
- uri: 'https://api.controleonline.com/files/3/download?app-domain=maincompany.controleonline.com',
36
+ uri: 'https://api.controleonline.com/maincompany.controleonline.com/files/3/download',
37
37
  headers: {
38
38
  Authorization: 'Bearer token',
39
39
  'app-domain': 'maincompany.controleonline.com',
40
40
  },
41
41
  });
42
42
  expect(resolveFileImageUrl(3, {company})).toBe(
43
- 'https://api.controleonline.com/files/3/download?app-domain=maincompany.controleonline.com',
43
+ 'https://api.controleonline.com/maincompany.controleonline.com/files/3/download',
44
44
  );
45
45
  });
46
46
 
@@ -0,0 +1,33 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+
4
+ import Formatter from '../../../utils/formatter.js';
5
+
6
+ test('formats API datetimes using the numeric offset returned by the backend', () => {
7
+ assert.equal(
8
+ Formatter.formatDateYmdTodmY('2026-07-22T00:00:00-02:00'),
9
+ '22/07/2026',
10
+ );
11
+
12
+ assert.equal(
13
+ Formatter.formatDateYmdTodmY('2026-07-22T22:34:22-02:00', true),
14
+ '22/07/2026, 22:34:22',
15
+ );
16
+
17
+ assert.equal(
18
+ Formatter.formatDateYmdTodmY('2026-07-22T21:34:22-03:00', true),
19
+ '22/07/2026, 21:34:22',
20
+ );
21
+ });
22
+
23
+ test('repairs mojibake from API text payloads', () => {
24
+ assert.equal(
25
+ Formatter.repairMojibake('Não, saída, cobrança, garçom, conferência'),
26
+ 'Não, saída, cobrança, garçom, conferência',
27
+ );
28
+
29
+ assert.equal(
30
+ Formatter.repairMojibake('Produção entrada → trabalhando → pronto'),
31
+ 'Produção entrada → trabalhando → pronto',
32
+ );
33
+ });
@@ -10,9 +10,12 @@ const {
10
10
  getRuntimeFooterDebugInfo,
11
11
  getRuntimeFooterNativeIdentifierCandidates,
12
12
  getRuntimeFooterPrimaryText,
13
- getRuntimeFooterStoredVersion,
14
- getRuntimeFooterWebIdentifierCandidates,
15
- getRuntimeFooterWebHost,
13
+ getRuntimeFooterRotationEntries,
14
+ getRuntimeFooterStoredVersion,
15
+ getRuntimeFooterTextLines,
16
+ getRuntimeFooterWebIdentifierCandidates,
17
+ getRuntimeFooterWebHost,
18
+ normalizeRuntimeFooterText,
16
19
  } = require('../../../react/utils/runtimeFooter')
17
20
 
18
21
  const originalLocationDescriptor = Object.getOwnPropertyDescriptor(globalThis, 'location')
@@ -421,7 +424,29 @@ test('falls back to the local device id when the backend identifier is unavailab
421
424
  device: {},
422
425
  },
423
426
  })
424
-
427
+
425
428
  assert.equal(candidates[0]?.value, 'local-device-99')
426
429
  assert.equal(candidates[0]?.source, 'device.id')
427
430
  })
431
+
432
+ test('preserves free-text line breaks while normalizing each footer line', () => {
433
+ assert.equal(
434
+ normalizeRuntimeFooterText(' www.site.com \n\n (11) 99999-9999 \n suporte '),
435
+ 'www.site.com\n(11) 99999-9999\nsuporte',
436
+ )
437
+
438
+ assert.deepEqual(
439
+ getRuntimeFooterTextLines('www.site.com\n(11) 99999-9999\nsuporte'),
440
+ ['www.site.com', '(11) 99999-9999', 'suporte'],
441
+ )
442
+ })
443
+
444
+ test('rotates each footer line before the runtime version entry', () => {
445
+ assert.deepEqual(
446
+ getRuntimeFooterRotationEntries({
447
+ companyFooterText: 'www.site.com\n(11) 99999-9999',
448
+ primaryText: 'web (203.0.113.42) / v1.3.6',
449
+ }),
450
+ ['www.site.com', '(11) 99999-9999', 'web (203.0.113.42) / v1.3.6'],
451
+ )
452
+ })