@controleonline/ui-common 1.2.70
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.
- package/.github/agents/developer.agent.md +30 -0
- package/.github/agents/devops.agent.md +30 -0
- package/.github/agents/qa.agent.md +30 -0
- package/.github/agents/security.agent.md +30 -0
- package/.github/workflows/npmjs.yml +35 -0
- package/.github/workflows/pull-request-checks.yml +19 -0
- package/.scrutinizer.yml +24 -0
- package/FUNDING.yml +1 -0
- package/package.json +28 -0
- package/src/api/fetch.js +74 -0
- package/src/api/index.js +155 -0
- package/src/api/localDB.js +131 -0
- package/src/api/queue.js +66 -0
- package/src/react/assets/tap.mp3 +0 -0
- package/src/react/components/AddImportModal.js +157 -0
- package/src/react/components/AddImportModal.styles.js +81 -0
- package/src/react/components/BackgroundRuntimeBridge.js +219 -0
- package/src/react/components/BottomNavigationBar.config.js +238 -0
- package/src/react/components/BottomNavigationBar.js +120 -0
- package/src/react/components/BottomNavigationBar.styles.js +110 -0
- package/src/react/components/CategoryForm.js +572 -0
- package/src/react/components/CategoryForm.styles.js +334 -0
- package/src/react/components/ConfirmModal.js +24 -0
- package/src/react/components/ConfirmModal.styles.js +43 -0
- package/src/react/components/ContextHelpButton.js +69 -0
- package/src/react/components/ContextHelpButton.styles.js +62 -0
- package/src/react/components/DefaultProvider.native.js +848 -0
- package/src/react/components/DefaultProvider.styles.js +21 -0
- package/src/react/components/DefaultProvider.web.js +909 -0
- package/src/react/components/DeliveryPushBridge.native.js +314 -0
- package/src/react/components/DeliveryPushBridge.web.js +5 -0
- package/src/react/components/DeviceAlertSoundService.js +499 -0
- package/src/react/components/EntityLogContent.js +909 -0
- package/src/react/components/EntityLogContent.styles.js +400 -0
- package/src/react/components/EventBus.js +12 -0
- package/src/react/components/KioskModeBridge.js +65 -0
- package/src/react/components/LauncherModeBridge.js +47 -0
- package/src/react/components/LinkedOrderProductsTab.js +388 -0
- package/src/react/components/LinkedOrderProductsTab.styles.js +180 -0
- package/src/react/components/ManagerPushBridge.native.js +345 -0
- package/src/react/components/MessageService.js +314 -0
- package/src/react/components/Paywall/Google.js +89 -0
- package/src/react/components/PrintService.js +778 -0
- package/src/react/components/ProductCatalogCacheService.js +45 -0
- package/src/react/components/RemoteCheckoutService.js +292 -0
- package/src/react/components/RuntimeBottomNavigationBar.js +151 -0
- package/src/react/components/RuntimeInfoFooter.js +214 -0
- package/src/react/components/RuntimeInfoFooter.styles.js +56 -0
- package/src/react/components/SystemErrorToast.js +29 -0
- package/src/react/components/SystemErrorToast.styles.js +38 -0
- package/src/react/components/TouchFeedbackProvider.js +65 -0
- package/src/react/components/UnifiedPaymentBar.js +155 -0
- package/src/react/components/UnifiedPaymentBar.styles.js +174 -0
- package/src/react/components/WebsocketListener.native.js +487 -0
- package/src/react/components/WebsocketListener.web.js +102 -0
- package/src/react/components/inputs/IdInput.js +49 -0
- package/src/react/components/inputs/IdInput.styles.js +26 -0
- package/src/react/components/inputs/InvoiceDueDateFilterInput.js +74 -0
- package/src/react/components/inputs/InvoiceDueDateFilterInput.styles.js +34 -0
- package/src/react/components/inputs/InvoiceDueDateRangeFilterInput.js +115 -0
- package/src/react/components/inputs/InvoiceDueDateRangeFilterInput.styles.js +39 -0
- package/src/react/components/lists/CategoriesList.js +56 -0
- package/src/react/components/lists/PaymentTypeList.js +55 -0
- package/src/react/components/lists/ReceiverList.js +56 -0
- package/src/react/components/lists/SelectList.styles.js +18 -0
- package/src/react/components/lists/StatusList.js +55 -0
- package/src/react/components/lists/WalletList.js +54 -0
- package/src/react/components/radio/Spotify.js +144 -0
- package/src/react/components/radio/Spotify.styles.js +32 -0
- package/src/react/components/radio/YouTube.js +82 -0
- package/src/react/components/radio/YouTube.styles.js +16 -0
- package/src/react/components/toastConfig.js +16 -0
- package/src/react/config/deviceConfigBootstrap.js +571 -0
- package/src/react/index.js +4 -0
- package/src/react/localStorage.js +30 -0
- package/src/react/pages/EntityLogPage.js +124 -0
- package/src/react/pages/EntityLogPage.styles.js +17 -0
- package/src/react/pages/GenericLogPage.js +760 -0
- package/src/react/pages/GenericLogPage.styles.js +401 -0
- package/src/react/pages/Imports.js +361 -0
- package/src/react/pages/Imports.styles.js +223 -0
- package/src/react/pages/SettingsPage/PaymentTypesByWalletTab.js +462 -0
- package/src/react/pages/SettingsPage/index.js +1171 -0
- package/src/react/pages/SettingsPage/index.styles.js +122 -0
- package/src/react/print/jobs.js +147 -0
- package/src/react/print/providers/local.js +53 -0
- package/src/react/print/providers/remote.js +81 -0
- package/src/react/print/selection.js +296 -0
- package/src/react/print/usePrintButtonController.js +474 -0
- package/src/react/router/routes.js +42 -0
- package/src/react/services/NetworkPrinterService.native.js +391 -0
- package/src/react/services/NetworkPrinterService.web.js +15 -0
- package/src/react/stores/index.js +128 -0
- package/src/react/stores/storeErrorBridge.js +36 -0
- package/src/react/utils/backgroundRuntimeRegistration.js +69 -0
- package/src/react/utils/cashPayment.js +87 -0
- package/src/react/utils/commercialDocumentOrders.js +269 -0
- package/src/react/utils/dateRangeFilter.js +264 -0
- package/src/react/utils/deviceRuntime.js +193 -0
- package/src/react/utils/entityDisplay.js +231 -0
- package/src/react/utils/entityLog.js +649 -0
- package/src/react/utils/fileUrl.js +102 -0
- package/src/react/utils/frontendDebugLog.js +45 -0
- package/src/react/utils/googleMapsConfig.js +49 -0
- package/src/react/utils/importStatus.js +100 -0
- package/src/react/utils/invoiceDueDateMessages.js +14 -0
- package/src/react/utils/invoicePresentation.js +46 -0
- package/src/react/utils/logSettings.js +143 -0
- package/src/react/utils/maintenanceSettings.js +53 -0
- package/src/react/utils/managerOrderNotifications.js +523 -0
- package/src/react/utils/mapNavigation.js +76 -0
- package/src/react/utils/menuCatalogConfig.js +8 -0
- package/src/react/utils/menuCatalogDownload.js +157 -0
- package/src/react/utils/networkCameraDevices.js +175 -0
- package/src/react/utils/networkDeviceProfiles.js +135 -0
- package/src/react/utils/normalizedCatalogDownload.js +157 -0
- package/src/react/utils/notificationNavigation.js +94 -0
- package/src/react/utils/notificationSound.js +42 -0
- package/src/react/utils/paymentDevices.js +397 -0
- package/src/react/utils/paymentGatewayExecution.js +91 -0
- package/src/react/utils/paymentOptions.js +88 -0
- package/src/react/utils/peopleDisplay.js +39 -0
- package/src/react/utils/printerDevices.js +465 -0
- package/src/react/utils/remotePayment.js +61 -0
- package/src/react/utils/runtimeFooter.js +475 -0
- package/src/react/utils/runtimeLanguage.js +50 -0
- package/src/react/utils/runtimeMenu.js +224 -0
- package/src/react/utils/screenMetrics.js +30 -0
- package/src/react/utils/shopConfig.js +294 -0
- package/src/react/utils/shopFranchises.js +114 -0
- package/src/react/utils/socketRuntimePipeline.js +454 -0
- package/src/react/utils/spoolAckTracker.js +77 -0
- package/src/react/utils/storeColumns.js +427 -0
- package/src/react/utils/systemErrorChannel.js +109 -0
- package/src/react/utils/systemErrorMessage.js +67 -0
- package/src/react/utils/toastPresentation.js +53 -0
- package/src/react/utils/websocketSharedRuntime.js +826 -0
- package/src/store/acl/getters.js +4 -0
- package/src/store/acl/index.js +19 -0
- package/src/store/acl/mutation_types.js +4 -0
- package/src/store/acl/mutations.js +17 -0
- package/src/store/address/city.js +22 -0
- package/src/store/categories/category_file/index.js +25 -0
- package/src/store/categories/index.js +93 -0
- package/src/store/configs/customActions.js +131 -0
- package/src/store/configs/index.js +25 -0
- package/src/store/connections/customActions.js +26 -0
- package/src/store/connections/index.js +124 -0
- package/src/store/device/index.js +22 -0
- package/src/store/device_config/customActions.js +190 -0
- package/src/store/device_config/index.js +28 -0
- package/src/store/entity_log/customActions.js +63 -0
- package/src/store/entity_log/index.js +25 -0
- package/src/store/extra/data.js +85 -0
- package/src/store/extra/fields.js +88 -0
- package/src/store/file/actions.js +27 -0
- package/src/store/file/index.js +24 -0
- package/src/store/imports/index.js +88 -0
- package/src/store/print/customActions.js +251 -0
- package/src/store/print/index.js +28 -0
- package/src/store/printer/customActions.js +54 -0
- package/src/store/printer/index.js +26 -0
- package/src/store/runtime_debug/customActions.js +71 -0
- package/src/store/runtime_debug/index.js +30 -0
- package/src/store/status/index.js +65 -0
- package/src/store/timezones/index.js +43 -0
- package/src/store/websocket/customActions.js +24 -0
- package/src/store/websocket/index.js +28 -0
- package/src/tests/react/components/LauncherModeBridge.test.js +93 -0
- package/src/tests/react/config/deviceConfigBootstrap.test.js +337 -0
- package/src/tests/react/print/localPrintProvider.test.js +45 -0
- package/src/tests/react/utils/backgroundRuntimeRegistration.test.js +123 -0
- package/src/tests/react/utils/cashPayment.test.js +41 -0
- package/src/tests/react/utils/dateRangeFilter.test.mjs +29 -0
- package/src/tests/react/utils/entityDisplay.test.js +18 -0
- package/src/tests/react/utils/googleMapsConfig.test.js +32 -0
- package/src/tests/react/utils/importStatus.test.js +85 -0
- package/src/tests/react/utils/invoicePresentation.test.mjs +40 -0
- package/src/tests/react/utils/managerOrderNotifications.test.js +127 -0
- package/src/tests/react/utils/mapNavigation.test.js +58 -0
- package/src/tests/react/utils/notificationNavigation.test.js +39 -0
- package/src/tests/react/utils/notificationSound.test.js +27 -0
- package/src/tests/react/utils/paymentDevices.test.js +88 -0
- package/src/tests/react/utils/runtimeFooter.test.js +429 -0
- package/src/tests/react/utils/runtimeLanguage.test.js +80 -0
- package/src/tests/react/utils/runtimeMenu.test.js +174 -0
- package/src/tests/react/utils/shopFranchises.test.js +82 -0
- package/src/tests/react/utils/socketRuntimePipeline.test.js +124 -0
- package/src/tests/react/utils/spoolAckTracker.test.js +40 -0
- package/src/tests/react/utils/storeErrorBridge.test.js +66 -0
- package/src/tests/react/utils/systemErrorChannel.test.js +58 -0
- package/src/tests/react/utils/systemErrorMessage.test.js +59 -0
- package/src/tests/react/utils/toastPresentation.test.js +75 -0
- package/src/tests/react/utils/translate.test.mjs +396 -0
- package/src/tests/react/utils/websocketSharedRuntime.test.js +245 -0
- package/src/utils/apiEntryPoint.js +31 -0
- package/src/utils/appDomain.js +50 -0
- package/src/utils/config.js +14 -0
- package/src/utils/formatter.js +419 -0
- package/src/utils/integrationConfigs.js +79 -0
- package/src/utils/methods.js +23 -0
- package/src/utils/oauth.js +42 -0
- package/src/utils/translate.js +620 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const {
|
|
2
|
+
GOOGLE_MAPS_ANDROID_API_KEY_CONFIG_KEY,
|
|
3
|
+
GOOGLE_MAPS_WEB_API_KEY_CONFIG_KEY,
|
|
4
|
+
resolveGoogleMapsSettings,
|
|
5
|
+
} = require('../../../react/utils/googleMapsConfig');
|
|
6
|
+
|
|
7
|
+
const {describe, expect, it} = global;
|
|
8
|
+
|
|
9
|
+
describe('googleMapsConfig', () => {
|
|
10
|
+
it('ignores legacy map keys and keeps the dedicated keys empty', () => {
|
|
11
|
+
expect(
|
|
12
|
+
resolveGoogleMapsSettings({
|
|
13
|
+
'shop-google-maps-api-key': '"legacy-key"',
|
|
14
|
+
}),
|
|
15
|
+
).toEqual({
|
|
16
|
+
webGoogleMapsApiKey: '',
|
|
17
|
+
androidGoogleMapsApiKey: '',
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('reads the dedicated web and android keys', () => {
|
|
22
|
+
expect(
|
|
23
|
+
resolveGoogleMapsSettings({
|
|
24
|
+
[GOOGLE_MAPS_WEB_API_KEY_CONFIG_KEY]: '"web-key"',
|
|
25
|
+
[GOOGLE_MAPS_ANDROID_API_KEY_CONFIG_KEY]: '"android-key"',
|
|
26
|
+
}),
|
|
27
|
+
).toEqual({
|
|
28
|
+
webGoogleMapsApiKey: 'web-key',
|
|
29
|
+
androidGoogleMapsApiKey: 'android-key',
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const assert = require('node:assert/strict')
|
|
2
|
+
const test = require('node:test')
|
|
3
|
+
|
|
4
|
+
const {
|
|
5
|
+
getImportErrorDetail,
|
|
6
|
+
getImportStatusMeta,
|
|
7
|
+
normalizeImportStatus,
|
|
8
|
+
resolveImportLabels,
|
|
9
|
+
resolveImportStatusLabel,
|
|
10
|
+
} = require('@controleonline/ui-common/src/react/utils/importStatus')
|
|
11
|
+
|
|
12
|
+
test('normalizeImportStatus trims and lowercases the raw value', () => {
|
|
13
|
+
assert.equal(normalizeImportStatus(' Processing '), 'processing')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test('getImportStatusMeta keeps queued imports flagged as processing', () => {
|
|
17
|
+
const result = getImportStatusMeta({
|
|
18
|
+
status: {
|
|
19
|
+
status: 'queued',
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
assert.equal(result.isProcessing, true)
|
|
24
|
+
assert.equal(result.isError, false)
|
|
25
|
+
assert.equal(result.labelKey, 'processing')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('getImportStatusMeta treats invalid imports as errors', () => {
|
|
29
|
+
const result = getImportStatusMeta({
|
|
30
|
+
status: {
|
|
31
|
+
realStatus: 'invalid',
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
assert.equal(result.isProcessing, false)
|
|
36
|
+
assert.equal(result.isError, true)
|
|
37
|
+
assert.equal(result.labelKey, 'error')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('getImportErrorDetail returns the first non-empty message candidate', () => {
|
|
41
|
+
assert.equal(
|
|
42
|
+
getImportErrorDetail({
|
|
43
|
+
error: ' ',
|
|
44
|
+
status: {
|
|
45
|
+
description: 'Arquivo fora do padrão',
|
|
46
|
+
},
|
|
47
|
+
message: 'Mensagem menos específica',
|
|
48
|
+
}),
|
|
49
|
+
'Arquivo fora do padrão',
|
|
50
|
+
)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('resolveImportLabels only reads translation keys without hardcoded fallbacks', () => {
|
|
54
|
+
const calls = []
|
|
55
|
+
const labels = resolveImportLabels((...args) => {
|
|
56
|
+
calls.push(args)
|
|
57
|
+
return args.join('.')
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
assert.deepEqual(calls, [
|
|
61
|
+
['imports', 'button', 'refresh'],
|
|
62
|
+
['imports', 'status', 'processing'],
|
|
63
|
+
['imports', 'status', 'error'],
|
|
64
|
+
['imports', 'status', 'done'],
|
|
65
|
+
['imports', 'status', 'no_status'],
|
|
66
|
+
['imports', 'message', 'processing_after_upload'],
|
|
67
|
+
])
|
|
68
|
+
assert.equal(labels.refreshLabel, 'imports.button.refresh')
|
|
69
|
+
assert.equal(labels.processingHelpLabel, 'imports.message.processing_after_upload')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test('resolveImportStatusLabel falls back to the raw backend status before the generic empty label', () => {
|
|
73
|
+
assert.equal(
|
|
74
|
+
resolveImportStatusLabel(
|
|
75
|
+
{
|
|
76
|
+
labelKey: 'fallback',
|
|
77
|
+
rawStatus: 'sincronizando',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
noStatusLabel: 'sem status',
|
|
81
|
+
},
|
|
82
|
+
),
|
|
83
|
+
'sincronizando',
|
|
84
|
+
)
|
|
85
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
|
|
4
|
+
import invoicePresentation from '../../../react/utils/invoicePresentation.js';
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
formatInvoiceTypeLabel,
|
|
8
|
+
getInvoicePaymentTypeLabel,
|
|
9
|
+
getInvoicePartyLabel,
|
|
10
|
+
normalizeInvoiceType,
|
|
11
|
+
} = invoicePresentation;
|
|
12
|
+
|
|
13
|
+
test('normalizes invoice type and falls back to invoice', () => {
|
|
14
|
+
assert.equal(normalizeInvoiceType(' PAYMENT '), 'payment');
|
|
15
|
+
assert.equal(normalizeInvoiceType(''), 'invoice');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('formats canonical invoice types with stable labels', () => {
|
|
19
|
+
assert.equal(formatInvoiceTypeLabel('discount'), 'Discount');
|
|
20
|
+
assert.equal(formatInvoiceTypeLabel('service_fee'), 'Service Fee');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('reads payment type and party labels from invoice payloads', () => {
|
|
24
|
+
assert.equal(
|
|
25
|
+
getInvoicePaymentTypeLabel({
|
|
26
|
+
paymentType: {
|
|
27
|
+
paymentType: 'Credito',
|
|
28
|
+
},
|
|
29
|
+
}),
|
|
30
|
+
'Credito',
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
assert.equal(
|
|
34
|
+
getInvoicePartyLabel({
|
|
35
|
+
alias: 'BRUNA',
|
|
36
|
+
name: 'Bruna Silva',
|
|
37
|
+
}),
|
|
38
|
+
'BRUNA',
|
|
39
|
+
);
|
|
40
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
const {jest} = require('@jest/globals');
|
|
2
|
+
|
|
3
|
+
jest.mock('react-native', () => ({
|
|
4
|
+
Platform: {
|
|
5
|
+
OS: 'web',
|
|
6
|
+
},
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
applyManagerOrderNotificationPreferences,
|
|
11
|
+
buildManagerOrderNotificationContent,
|
|
12
|
+
ensureManagerOrderNotificationPermission,
|
|
13
|
+
hasStoredManagerOrderNotificationPreferences,
|
|
14
|
+
isManagerAppType,
|
|
15
|
+
resolveManagerOrderNotificationPreferences,
|
|
16
|
+
} = require('../../../react/utils/managerOrderNotifications');
|
|
17
|
+
|
|
18
|
+
const {describe, expect, it} = global;
|
|
19
|
+
|
|
20
|
+
describe('managerOrderNotifications helpers', () => {
|
|
21
|
+
it('defaults push to enabled and sound to disabled', () => {
|
|
22
|
+
expect(resolveManagerOrderNotificationPreferences(null)).toEqual({
|
|
23
|
+
pushEnabled: true,
|
|
24
|
+
soundEnabled: false,
|
|
25
|
+
soundUrl: '',
|
|
26
|
+
});
|
|
27
|
+
expect(hasStoredManagerOrderNotificationPreferences(null)).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('stores preferences under localPreferences without losing existing fields', () => {
|
|
31
|
+
const user = {
|
|
32
|
+
id: 15,
|
|
33
|
+
name: 'Gestor',
|
|
34
|
+
localPreferences: {
|
|
35
|
+
theme: 'light',
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
expect(
|
|
40
|
+
applyManagerOrderNotificationPreferences(user, {
|
|
41
|
+
pushEnabled: false,
|
|
42
|
+
soundEnabled: true,
|
|
43
|
+
soundUrl: 'https://cdn.example.com/alerta.mp3',
|
|
44
|
+
}),
|
|
45
|
+
).toMatchObject({
|
|
46
|
+
id: 15,
|
|
47
|
+
localPreferences: {
|
|
48
|
+
theme: 'light',
|
|
49
|
+
managerOrderNotifications: {
|
|
50
|
+
pushEnabled: false,
|
|
51
|
+
soundEnabled: true,
|
|
52
|
+
soundUrl: 'https://cdn.example.com/alerta.mp3',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('builds a focused title for one order and a grouped summary for many', () => {
|
|
59
|
+
expect(
|
|
60
|
+
buildManagerOrderNotificationContent(
|
|
61
|
+
[
|
|
62
|
+
{
|
|
63
|
+
order: '/orders/77',
|
|
64
|
+
notificationHeader: 'Pedido #77',
|
|
65
|
+
notificationSubheader: 'Ref. interna #77',
|
|
66
|
+
notificationCustomerName: 'Maria Oliveira',
|
|
67
|
+
notificationPriceLabel: 'R$ 42,90',
|
|
68
|
+
notificationStatusLabel: 'Fila',
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
{alias: 'Loja Centro'},
|
|
72
|
+
),
|
|
73
|
+
).toEqual({
|
|
74
|
+
title: 'Pedido #77',
|
|
75
|
+
body:
|
|
76
|
+
'Ref. interna #77 | Cliente: Maria Oliveira | Valor: R$ 42,90 | Loja Centro | Status: Fila',
|
|
77
|
+
orderIds: ['77'],
|
|
78
|
+
statusLabel: 'Fila',
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
expect(
|
|
82
|
+
buildManagerOrderNotificationContent(
|
|
83
|
+
[{order: 10}, {order: '/orders/12'}, {order: '12'}],
|
|
84
|
+
{name: 'Operacao Norte'},
|
|
85
|
+
),
|
|
86
|
+
).toEqual({
|
|
87
|
+
title: '2 novos pedidos',
|
|
88
|
+
body: 'Operacao Norte recebeu 2 novos pedidos. Status: Fila.',
|
|
89
|
+
orderIds: ['10', '12'],
|
|
90
|
+
statusLabel: 'Fila',
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('recognizes the manager app type safely', () => {
|
|
95
|
+
expect(isManagerAppType('MANAGER')).toBe(true);
|
|
96
|
+
expect(isManagerAppType('manager')).toBe(true);
|
|
97
|
+
expect(isManagerAppType('POS')).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('requests notification permission only when the current status is default', async () => {
|
|
101
|
+
const getPermissionStatus = jest.fn(() => Promise.resolve('default'));
|
|
102
|
+
const requestPermission = jest.fn(() => Promise.resolve('granted'));
|
|
103
|
+
|
|
104
|
+
const status = await ensureManagerOrderNotificationPermission({
|
|
105
|
+
getPermissionStatus,
|
|
106
|
+
requestPermission,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
expect(getPermissionStatus).toHaveBeenCalledTimes(1);
|
|
110
|
+
expect(requestPermission).toHaveBeenCalledTimes(1);
|
|
111
|
+
expect(status).toBe('granted');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('keeps the current permission status when it is already resolved', async () => {
|
|
115
|
+
const getPermissionStatus = jest.fn(() => Promise.resolve('granted'));
|
|
116
|
+
const requestPermission = jest.fn(() => Promise.resolve('default'));
|
|
117
|
+
|
|
118
|
+
const status = await ensureManagerOrderNotificationPermission({
|
|
119
|
+
getPermissionStatus,
|
|
120
|
+
requestPermission,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
expect(getPermissionStatus).toHaveBeenCalledTimes(1);
|
|
124
|
+
expect(requestPermission).not.toHaveBeenCalled();
|
|
125
|
+
expect(status).toBe('granted');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const {
|
|
2
|
+
buildGoogleMapsNavigationUrl,
|
|
3
|
+
buildNavigationMapQuery,
|
|
4
|
+
buildWazeNavigationUrl,
|
|
5
|
+
} = require('../../../react/utils/mapNavigation');
|
|
6
|
+
|
|
7
|
+
const {describe, expect, it} = global;
|
|
8
|
+
|
|
9
|
+
describe('mapNavigation', () => {
|
|
10
|
+
it('joins only valid address parts when building the map query', () => {
|
|
11
|
+
expect(
|
|
12
|
+
buildNavigationMapQuery([
|
|
13
|
+
'Rua Emilio Bosco, 5675',
|
|
14
|
+
'',
|
|
15
|
+
'Jardim Sao Geronimo',
|
|
16
|
+
null,
|
|
17
|
+
'Sumare - SP',
|
|
18
|
+
]),
|
|
19
|
+
).toBe('Rua Emilio Bosco, 5675, Jardim Sao Geronimo, Sumare - SP');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('builds a Google Maps directions url when coordinates are available', () => {
|
|
23
|
+
expect(
|
|
24
|
+
buildGoogleMapsNavigationUrl({
|
|
25
|
+
coordinates: {latitude: -22.8218, longitude: -47.2669},
|
|
26
|
+
origin: {latitude: -22.9, longitude: -47.05},
|
|
27
|
+
}),
|
|
28
|
+
).toBe(
|
|
29
|
+
'https://www.google.com/maps/dir/?api=1&destination=-22.8218%2C-47.2669&origin=-22.9%2C-47.05&travelmode=driving',
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('falls back to a Google Maps search url when only the query is available', () => {
|
|
34
|
+
expect(
|
|
35
|
+
buildGoogleMapsNavigationUrl({
|
|
36
|
+
mapQuery: 'Rua Emilio Bosco, 5675, Sumare - SP',
|
|
37
|
+
}),
|
|
38
|
+
).toBe(
|
|
39
|
+
'https://www.google.com/maps/search/?api=1&query=Rua%20Emilio%20Bosco%2C%205675%2C%20Sumare%20-%20SP',
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('builds a Waze url for either coordinates or a free-form query', () => {
|
|
44
|
+
expect(
|
|
45
|
+
buildWazeNavigationUrl({
|
|
46
|
+
coordinates: {latitude: -22.8218, longitude: -47.2669},
|
|
47
|
+
}),
|
|
48
|
+
).toBe('https://waze.com/ul?ll=-22.8218,-47.2669&navigate=yes');
|
|
49
|
+
|
|
50
|
+
expect(
|
|
51
|
+
buildWazeNavigationUrl({
|
|
52
|
+
mapQuery: 'Rua Emilio Bosco, 5675, Sumare - SP',
|
|
53
|
+
}),
|
|
54
|
+
).toBe(
|
|
55
|
+
'https://waze.com/ul?q=Rua%20Emilio%20Bosco%2C%205675%2C%20Sumare%20-%20SP&navigate=yes',
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const {jest} = require('@jest/globals');
|
|
2
|
+
|
|
3
|
+
const {describe, expect, it, beforeEach} = global;
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
clearNotificationNavigationQueue,
|
|
7
|
+
flushNotificationNavigationQueue,
|
|
8
|
+
queueNotificationNavigation,
|
|
9
|
+
setNotificationNavigationHandler,
|
|
10
|
+
} = require('../../../react/utils/notificationNavigation');
|
|
11
|
+
|
|
12
|
+
describe('notificationNavigation helpers', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
clearNotificationNavigationQueue();
|
|
15
|
+
setNotificationNavigationHandler(null);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('queues notification navigation until the handler is ready', () => {
|
|
19
|
+
const handler = jest.fn(() => true);
|
|
20
|
+
|
|
21
|
+
expect(queueNotificationNavigation('OrderDetails', {id: '71137'})).toBe(true);
|
|
22
|
+
expect(handler).not.toHaveBeenCalled();
|
|
23
|
+
|
|
24
|
+
setNotificationNavigationHandler(handler);
|
|
25
|
+
expect(flushNotificationNavigationQueue()).toBe(true);
|
|
26
|
+
expect(handler).toHaveBeenCalledWith('OrderDetails', {id: '71137'});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('deduplicates the same cold-start notification request', () => {
|
|
30
|
+
const handler = jest.fn(() => true);
|
|
31
|
+
|
|
32
|
+
expect(queueNotificationNavigation('OrderDetails', {id: '71137'})).toBe(true);
|
|
33
|
+
expect(queueNotificationNavigation('OrderDetails', {id: '71137'})).toBe(false);
|
|
34
|
+
|
|
35
|
+
setNotificationNavigationHandler(handler);
|
|
36
|
+
flushNotificationNavigationQueue();
|
|
37
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const {describe, expect, it} = global;
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
DEFAULT_NOTIFICATION_SOUND_FILE,
|
|
5
|
+
resolveNotificationSoundSource,
|
|
6
|
+
} = require('../../../react/utils/notificationSound');
|
|
7
|
+
|
|
8
|
+
describe('notificationSound helpers', () => {
|
|
9
|
+
it('keeps the custom sound url when it is provided', () => {
|
|
10
|
+
expect(
|
|
11
|
+
resolveNotificationSoundSource(
|
|
12
|
+
'https://cdn.example.com/alerta.mp3',
|
|
13
|
+
'fallback-source',
|
|
14
|
+
),
|
|
15
|
+
).toBe('https://cdn.example.com/alerta.mp3');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('falls back to the bundled source when the url is blank', () => {
|
|
19
|
+
expect(resolveNotificationSoundSource(' ', 'fallback-source')).toBe(
|
|
20
|
+
'fallback-source',
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('exposes the bundled sound file name', () => {
|
|
25
|
+
expect(DEFAULT_NOTIFICATION_SOUND_FILE).toBe('caixa.m4a');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const {jest} = require('@jest/globals')
|
|
2
|
+
|
|
3
|
+
jest.mock('react-native', () => ({
|
|
4
|
+
Platform: {
|
|
5
|
+
OS: 'android',
|
|
6
|
+
select: options => options?.android,
|
|
7
|
+
},
|
|
8
|
+
}))
|
|
9
|
+
|
|
10
|
+
const {describe, expect, it} = global
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
PAYMENT_TYPE_IDS_CONFIG_KEY,
|
|
14
|
+
filterWalletPaymentTypesByAllowedIds,
|
|
15
|
+
isLocalCieloPrintCapableDeviceConfig,
|
|
16
|
+
resolveDevicePaymentTypeIds,
|
|
17
|
+
} = require('../../../react/utils/paymentDevices')
|
|
18
|
+
|
|
19
|
+
describe('paymentDevices local Cielo print support', () => {
|
|
20
|
+
it('resolves the payment type allowlist from the new config key', () => {
|
|
21
|
+
expect(
|
|
22
|
+
resolveDevicePaymentTypeIds({
|
|
23
|
+
configs: {
|
|
24
|
+
[PAYMENT_TYPE_IDS_CONFIG_KEY]: [1, '2', '/payment_types/3'],
|
|
25
|
+
},
|
|
26
|
+
}),
|
|
27
|
+
).toEqual(['1', '2', '3'])
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('returns an empty list when no payment types are configured', () => {
|
|
31
|
+
expect(resolveDevicePaymentTypeIds({})).toEqual([])
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('filters wallet payment types by association id instead of payment type id', () => {
|
|
35
|
+
const paymentTypes = [
|
|
36
|
+
{
|
|
37
|
+
id: 11,
|
|
38
|
+
wallet: {id: 1, wallet: 'Cielo'},
|
|
39
|
+
paymentType: {id: 5, paymentType: 'Débito'},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: 12,
|
|
43
|
+
wallet: {id: 2, wallet: 'Infinite Pay'},
|
|
44
|
+
paymentType: {id: 5, paymentType: 'Débito'},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 13,
|
|
48
|
+
wallet: {id: 2, wallet: 'Infinite Pay'},
|
|
49
|
+
paymentType: {id: 6, paymentType: 'PIX'},
|
|
50
|
+
},
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
expect(
|
|
54
|
+
filterWalletPaymentTypesByAllowedIds(paymentTypes, [11, '13']),
|
|
55
|
+
).toEqual([paymentTypes[0], paymentTypes[2]])
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('allows local Cielo print only for PDV devices with Cielo gateway', () => {
|
|
59
|
+
expect(
|
|
60
|
+
isLocalCieloPrintCapableDeviceConfig({
|
|
61
|
+
type: 'PDV',
|
|
62
|
+
configs: {
|
|
63
|
+
'pos-gateway': 'cielo',
|
|
64
|
+
},
|
|
65
|
+
}),
|
|
66
|
+
).toBe(true)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('blocks local Cielo print for non-Cielo and non-PDV devices', () => {
|
|
70
|
+
expect(
|
|
71
|
+
isLocalCieloPrintCapableDeviceConfig({
|
|
72
|
+
type: 'PDV',
|
|
73
|
+
configs: {
|
|
74
|
+
'pos-gateway': 'infinite-pay',
|
|
75
|
+
},
|
|
76
|
+
}),
|
|
77
|
+
).toBe(false)
|
|
78
|
+
|
|
79
|
+
expect(
|
|
80
|
+
isLocalCieloPrintCapableDeviceConfig({
|
|
81
|
+
type: 'PRINT',
|
|
82
|
+
configs: {
|
|
83
|
+
'pos-gateway': 'cielo',
|
|
84
|
+
},
|
|
85
|
+
}),
|
|
86
|
+
).toBe(false)
|
|
87
|
+
})
|
|
88
|
+
})
|