@controleonline/ui-common 1.2.80 → 1.2.82
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/AGENTS.md +8 -0
- package/package.json +9 -3
- package/src/api/fetch.js +7 -7
- package/src/react/components/AddImportModal.js +6 -2
- package/src/react/components/BackgroundRuntimeBridge.js +201 -215
- package/src/react/components/ConfirmModal.js +44 -9
- package/src/react/components/ConfirmModal.styles.js +55 -41
- package/src/react/components/CopyDeviceConfigModal.js +193 -0
- package/src/react/components/CopyDeviceConfigModal.styles.js +123 -0
- package/src/react/components/DefaultProvider.native.js +22 -0
- package/src/react/components/DefaultProvider.web.js +25 -1
- package/src/react/components/EntityLogBranch.js +482 -0
- package/src/react/components/EntityLogCards.js +369 -0
- package/src/react/components/EntityLogContent.js +113 -794
- package/src/react/components/LinkedOrderProductsTab.js +3 -3
- package/src/react/components/PrintService.js +130 -409
- package/src/react/components/RemoteCheckoutService.js +2 -2
- package/src/react/components/RuntimeBottomNavigationBar.js +141 -146
- package/src/react/components/RuntimeFooterMarqueeText.js +137 -0
- package/src/react/components/RuntimeInfoFooter.js +12 -15
- package/src/react/components/SystemErrorToast.js +49 -29
- package/src/react/components/SystemErrorToast.styles.js +43 -38
- package/src/react/components/UserAvatar.js +120 -28
- package/src/react/config/deviceConfigBootstrap.js +330 -304
- package/src/react/hooks/usePrintSpoolEffects.js +161 -0
- package/src/react/pages/DeviceDetailPage.js +6 -2882
- package/src/react/pages/Devices/detail/DeviceDetailAlertsCommandsSection.js +233 -0
- package/src/react/pages/Devices/detail/DeviceDetailHeader.js +138 -0
- package/src/react/pages/Devices/detail/DeviceDetailMovementSections.js +167 -0
- package/src/react/pages/Devices/detail/DeviceDetailOrdersPrintSection.js +216 -0
- package/src/react/pages/Devices/detail/DeviceDetailPaymentSection.js +44 -0
- package/src/react/pages/Devices/detail/DeviceDetailPdvConfigSection.js +390 -0
- package/src/react/pages/Devices/detail/DeviceDetailRenderers.js +135 -0
- package/src/react/pages/Devices/detail/DeviceDetailScreen.js +495 -0
- package/src/react/pages/Devices/detail/OptionButtonChip.js +65 -0
- package/src/react/pages/Devices/detail/deviceDetailConstants.js +36 -0
- package/src/react/pages/Devices/detail/deviceDetailHelpers.js +70 -0
- package/src/react/pages/Devices/detail/useDeviceDetailActions.js +483 -0
- package/src/react/pages/Devices/detail/useDeviceDetailCopyConfig.js +87 -0
- package/src/react/pages/Devices/detail/useDeviceDetailLoaders.js +327 -0
- package/src/react/pages/Devices/detail/useDeviceDetailSaves.js +429 -0
- package/src/react/pages/Devices/detail/useDeviceDetailStateA.js +489 -0
- package/src/react/pages/Devices/detail/useDeviceDetailStateB.js +98 -0
- package/src/react/pages/Devices/deviceTypes/index.js +2 -0
- package/src/react/pages/Devices/deviceTypes/pdv.js +11 -0
- package/src/react/pages/Imports.js +3 -2
- package/src/react/pages/IntegrationConfigFields.js +126 -0
- package/src/react/pages/IntegrationConfigPage.js +122 -591
- package/src/react/pages/IntegrationConfigPage.utils.js +95 -0
- package/src/react/print/usePrintButtonController.js +9 -1
- package/src/react/services/Getnet/Checkout.js +81 -0
- package/src/react/services/Getnet/Getnet.js +77 -0
- package/src/react/services/paymentGatewayExecution.js +20 -0
- package/src/react/utils/bottomNavigationToolbar.js +161 -0
- package/src/react/utils/commercialDocumentOrders.js +18 -6
- package/src/react/utils/copyDeviceConfigs.js +198 -0
- package/src/react/utils/fileUrl.js +43 -0
- package/src/react/utils/importStatus.js +19 -10
- package/src/react/utils/logSettings.js +7 -7
- package/src/react/utils/maintenanceSettings.js +16 -0
- package/src/react/utils/paymentDevices.js +6 -0
- package/src/react/utils/printManagedPrinter.js +45 -0
- package/src/react/utils/printRouting.js +63 -0
- package/src/react/utils/printSpoolUtils.js +119 -0
- package/src/react/utils/printerDevices.js +368 -368
- package/src/react/utils/runtimeFooter.js +10 -19
- package/src/react/utils/shopFranchises.js +186 -65
- package/src/react/utils/systemErrorMessage.js +71 -67
- package/src/store/device_config/customActions.js +119 -30
- package/src/tests/browser/manager/bottom-navigation-role-toolbar.spec.js +353 -0
- package/src/tests/browser/manager/device-detail-alias-save.spec.js +345 -0
- package/src/tests/browser/manager/device-detail-copy-config.spec.js +365 -0
- package/src/tests/browser/manager/runtime-footer-marquee.spec.js +262 -0
- package/src/tests/browser/manager/theme-confirm-toast.spec.js +236 -0
- package/src/tests/react/components/RuntimeFooterMarqueeText.test.js +171 -0
- package/src/tests/react/components/UserAvatar.test.js +6 -2
- package/src/tests/react/components/themeAwareModals.test.mjs +60 -0
- package/src/tests/react/config/deviceConfigBootstrap.test.js +104 -66
- package/src/tests/react/pages/deviceDetailDelete.test.js +78 -0
- package/src/tests/react/pages/receitaFederalIntegrationContract.test.mjs +48 -0
- package/src/tests/react/utils/bottomNavigationToolbar.test.js +222 -0
- package/src/tests/react/utils/copyDeviceConfigs.test.mjs +127 -0
- package/src/tests/react/utils/fileUrl.test.js +38 -0
- package/src/tests/react/utils/importStatus.test.js +26 -9
- package/src/tests/react/utils/passwordPolicy.test.js +27 -0
- package/src/tests/react/utils/printRouting.test.mjs +86 -0
- package/src/tests/react/utils/printSpoolUtils.test.mjs +52 -0
- package/src/tests/react/utils/runtimeFooter.test.js +7 -0
- package/src/tests/react/utils/shopFranchises.test.js +121 -115
- package/src/tests/react/utils/systemErrorMessage.test.js +32 -59
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import {
|
|
4
|
+
buildCopyPayloads,
|
|
5
|
+
buildSourceDeviceOptions,
|
|
6
|
+
executeCopyDeviceConfigs,
|
|
7
|
+
listCopyableConfigKeys,
|
|
8
|
+
} from '../../../react/utils/copyDeviceConfigs.js';
|
|
9
|
+
|
|
10
|
+
const companyId = '42';
|
|
11
|
+
const peopleIri = '/people/42';
|
|
12
|
+
|
|
13
|
+
const sourceRows = [
|
|
14
|
+
{
|
|
15
|
+
id: 1,
|
|
16
|
+
type: 'PDV',
|
|
17
|
+
people: {id: 42},
|
|
18
|
+
device: {id: 10, device: 'src-hash-1', alias: 'Caixa 1'},
|
|
19
|
+
configs: {
|
|
20
|
+
'pos-operation-mode': 'cashier',
|
|
21
|
+
'pos-gateway': 'cielo',
|
|
22
|
+
'printer-enabled': '1',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 2,
|
|
27
|
+
type: 'PRINT',
|
|
28
|
+
people: {id: 42},
|
|
29
|
+
device: {id: 10, device: 'src-hash-1', alias: 'Caixa 1'},
|
|
30
|
+
configs: {'print-network-port': '9100'},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 3,
|
|
34
|
+
type: 'PDV',
|
|
35
|
+
people: {id: 99},
|
|
36
|
+
device: {id: 20, device: 'other-co', alias: 'Outra empresa'},
|
|
37
|
+
configs: {'pos-gateway': 'infinite-pay'},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 4,
|
|
41
|
+
type: 'PDV',
|
|
42
|
+
people: {id: 42},
|
|
43
|
+
device: {id: 30, device: 'dest-hash', alias: 'Destino'},
|
|
44
|
+
configs: {'pos-operation-mode': 'waiter'},
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
test('listCopyableConfigKeys sorts keys from object or JSON string', () => {
|
|
49
|
+
assert.deepEqual(listCopyableConfigKeys({'b-key': '1', 'a-key': '2'}), [
|
|
50
|
+
'a-key',
|
|
51
|
+
'b-key',
|
|
52
|
+
]);
|
|
53
|
+
assert.deepEqual(
|
|
54
|
+
listCopyableConfigKeys(JSON.stringify({'pos-gateway': 'cielo'})),
|
|
55
|
+
['pos-gateway'],
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('buildSourceDeviceOptions excludes destination and other tenants', () => {
|
|
60
|
+
const options = buildSourceDeviceOptions({
|
|
61
|
+
companyDeviceConfigs: sourceRows,
|
|
62
|
+
companyId,
|
|
63
|
+
destinationDeviceString: 'dest-hash',
|
|
64
|
+
});
|
|
65
|
+
assert.equal(options.length, 1);
|
|
66
|
+
assert.equal(options[0].deviceString, 'src-hash-1');
|
|
67
|
+
assert.equal(options[0].configs.length, 2);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('buildCopyPayloads keeps destination identity and source config keys', () => {
|
|
71
|
+
const payloads = buildCopyPayloads({
|
|
72
|
+
sourceConfigs: sourceRows.filter(r => r.device.device === 'src-hash-1'),
|
|
73
|
+
destinationDeviceString: 'dest-hash',
|
|
74
|
+
peopleIri,
|
|
75
|
+
});
|
|
76
|
+
assert.equal(payloads.length, 2);
|
|
77
|
+
const pdv = payloads.find(p => p.type === 'PDV');
|
|
78
|
+
const print = payloads.find(p => p.type === 'PRINT');
|
|
79
|
+
assert.equal(pdv.device, 'dest-hash');
|
|
80
|
+
assert.equal(pdv.people, peopleIri);
|
|
81
|
+
assert.equal(JSON.parse(pdv.configs)['pos-gateway'], 'cielo');
|
|
82
|
+
assert.deepEqual(pdv.keys, [
|
|
83
|
+
'pos-gateway',
|
|
84
|
+
'pos-operation-mode',
|
|
85
|
+
'printer-enabled',
|
|
86
|
+
]);
|
|
87
|
+
assert.deepEqual(print.keys, ['print-network-port']);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('executeCopyDeviceConfigs posts one payload per type', async () => {
|
|
91
|
+
const calls = [];
|
|
92
|
+
const addDeviceConfigs = async params => {
|
|
93
|
+
calls.push(params);
|
|
94
|
+
return {ok: true, type: params.type};
|
|
95
|
+
};
|
|
96
|
+
const summary = await executeCopyDeviceConfigs({
|
|
97
|
+
addDeviceConfigs,
|
|
98
|
+
sourceConfigs: sourceRows.filter(r => r.device.device === 'src-hash-1'),
|
|
99
|
+
destinationDeviceString: 'dest-hash',
|
|
100
|
+
peopleIri,
|
|
101
|
+
});
|
|
102
|
+
assert.equal(calls.length, 2);
|
|
103
|
+
assert.ok(calls.every(c => c.device === 'dest-hash'));
|
|
104
|
+
assert.ok(calls.every(c => c.people === peopleIri));
|
|
105
|
+
assert.deepEqual(summary.types.sort(), ['PDV', 'PRINT']);
|
|
106
|
+
assert.ok(summary.keys.includes('pos-gateway'));
|
|
107
|
+
assert.ok(summary.keys.includes('print-network-port'));
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('buildCopyPayloads returns empty without dest or people', () => {
|
|
111
|
+
assert.deepEqual(
|
|
112
|
+
buildCopyPayloads({
|
|
113
|
+
sourceConfigs: sourceRows,
|
|
114
|
+
destinationDeviceString: '',
|
|
115
|
+
peopleIri,
|
|
116
|
+
}),
|
|
117
|
+
[],
|
|
118
|
+
);
|
|
119
|
+
assert.deepEqual(
|
|
120
|
+
buildCopyPayloads({
|
|
121
|
+
sourceConfigs: sourceRows,
|
|
122
|
+
destinationDeviceString: 'dest-hash',
|
|
123
|
+
peopleIri: '',
|
|
124
|
+
}),
|
|
125
|
+
[],
|
|
126
|
+
);
|
|
127
|
+
});
|
|
@@ -59,4 +59,42 @@ describe('fileUrl helpers', () => {
|
|
|
59
59
|
headers: {},
|
|
60
60
|
});
|
|
61
61
|
});
|
|
62
|
+
|
|
63
|
+
it('does not path-inject environment hosts like staging (app-community#432)', () => {
|
|
64
|
+
const source = resolveDefaultFileSource(
|
|
65
|
+
{
|
|
66
|
+
id: 8444,
|
|
67
|
+
url: '/files/8444/download',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
company: {
|
|
71
|
+
domain: 'staging.controleonline.com',
|
|
72
|
+
},
|
|
73
|
+
headers: {
|
|
74
|
+
Authorization: 'Bearer token',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(source).toEqual({
|
|
80
|
+
uri: 'https://api.controleonline.com/files/8444/download',
|
|
81
|
+
headers: {
|
|
82
|
+
Authorization: 'Bearer token',
|
|
83
|
+
'app-domain': 'staging.controleonline.com',
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
expect(source.uri).not.toContain('staging.controleonline.com/files');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('does not path-inject when fallback DOMAIN is manager platform host', () => {
|
|
90
|
+
// DOMAIN mock is manager.controleonline.com; no company → environment-like
|
|
91
|
+
const source = resolveDefaultFileSource(
|
|
92
|
+
{id: 9, url: '/files/9/download'},
|
|
93
|
+
{headers: {Authorization: 'Bearer t'}},
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
expect(source.uri).toBe('https://api.controleonline.com/files/9/download');
|
|
97
|
+
expect(source.headers['app-domain']).toBe('manager.controleonline.com');
|
|
98
|
+
expect(source.uri).not.toMatch(/manager\.controleonline\.com\/files/);
|
|
99
|
+
});
|
|
62
100
|
});
|
|
@@ -4,10 +4,11 @@ const {test} = require('node:test')
|
|
|
4
4
|
const {
|
|
5
5
|
getImportErrorDetail,
|
|
6
6
|
getImportStatusMeta,
|
|
7
|
-
normalizeImportStatus,
|
|
8
|
-
resolveImportLabels,
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
normalizeImportStatus,
|
|
8
|
+
resolveImportLabels,
|
|
9
|
+
resolveImportTranslator,
|
|
10
|
+
resolveImportStatusLabel,
|
|
11
|
+
} = require('@controleonline/ui-common/src/react/utils/importStatus')
|
|
11
12
|
|
|
12
13
|
test('normalizeImportStatus trims and lowercases the raw value', () => {
|
|
13
14
|
assert.equal(normalizeImportStatus(' Processing '), 'processing')
|
|
@@ -50,7 +51,7 @@ test('getImportErrorDetail returns the first non-empty message candidate', () =>
|
|
|
50
51
|
)
|
|
51
52
|
})
|
|
52
53
|
|
|
53
|
-
test('resolveImportLabels only reads translation keys without hardcoded fallbacks', () => {
|
|
54
|
+
test('resolveImportLabels only reads translation keys without hardcoded fallbacks', () => {
|
|
54
55
|
const calls = []
|
|
55
56
|
const labels = resolveImportLabels((...args) => {
|
|
56
57
|
calls.push(args)
|
|
@@ -66,10 +67,26 @@ test('resolveImportLabels only reads translation keys without hardcoded fallback
|
|
|
66
67
|
['imports', 'message', 'processing_after_upload'],
|
|
67
68
|
])
|
|
68
69
|
assert.equal(labels.refreshLabel, 'imports.button.refresh')
|
|
69
|
-
assert.equal(labels.processingHelpLabel, 'imports.message.processing_after_upload')
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
test('
|
|
70
|
+
assert.equal(labels.processingHelpLabel, 'imports.message.processing_after_upload')
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('resolveImportTranslator preserves translator object context', () => {
|
|
74
|
+
const translator = {
|
|
75
|
+
prefix: 'translated',
|
|
76
|
+
t(type, scope, key) {
|
|
77
|
+
return `${this.prefix}.${type}.${scope}.${key}`
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const translate = resolveImportTranslator(translator)
|
|
82
|
+
|
|
83
|
+
assert.equal(
|
|
84
|
+
translate('imports', 'button', 'refresh'),
|
|
85
|
+
'translated.imports.button.refresh',
|
|
86
|
+
)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test('resolveImportStatusLabel falls back to the raw backend status before the generic empty label', () => {
|
|
73
90
|
assert.equal(
|
|
74
91
|
resolveImportStatusLabel(
|
|
75
92
|
{
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PASSWORD_MIN_LENGTH,
|
|
3
|
+
PASSWORD_MSG_COMPROMISED,
|
|
4
|
+
PASSWORD_MSG_MIN_LENGTH,
|
|
5
|
+
mapPasswordErrorMessage,
|
|
6
|
+
validatePasswordClient,
|
|
7
|
+
} from '../../../react/utils/passwordPolicy';
|
|
8
|
+
|
|
9
|
+
describe('passwordPolicy', () => {
|
|
10
|
+
it('rejects short password with explicit min length', () => {
|
|
11
|
+
expect(validatePasswordClient('123')).toBe(PASSWORD_MSG_MIN_LENGTH);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('accepts password meeting min length', () => {
|
|
15
|
+
expect(validatePasswordClient('abcdef')).toBeNull();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('maps english breach message to portuguese', () => {
|
|
19
|
+
const raw =
|
|
20
|
+
'This password has been leaked in a data breach, it must not be used. Please use another password.';
|
|
21
|
+
expect(mapPasswordErrorMessage(raw)).toBe(PASSWORD_MSG_COMPROMISED);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('exposes policy min length constant', () => {
|
|
25
|
+
expect(PASSWORD_MIN_LENGTH).toBe(6);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {describe, it} from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {
|
|
4
|
+
PRINT_ROUTE_MISSING_DESTINATION,
|
|
5
|
+
resolvePrintRoutingError,
|
|
6
|
+
resolveSpoolDeviceIdsForRuntime,
|
|
7
|
+
} from '../../../react/utils/printRouting.js';
|
|
8
|
+
|
|
9
|
+
const PRINT_DEVICE_TYPE = 'PRINT';
|
|
10
|
+
const PDV_DEVICE_TYPE = 'PDV';
|
|
11
|
+
const DISPLAY_DEVICE_TYPE = 'DISPLAY';
|
|
12
|
+
|
|
13
|
+
describe('resolveSpoolDeviceIdsForRuntime (print routing #628)', () => {
|
|
14
|
+
const managedPrinters = [
|
|
15
|
+
{device: 'printer-a', type: PRINT_DEVICE_TYPE},
|
|
16
|
+
{device: 'printer-b', type: PRINT_DEVICE_TYPE},
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
it('PDV includes local + managed .device ids', () => {
|
|
20
|
+
const ids = resolveSpoolDeviceIdsForRuntime({
|
|
21
|
+
runtimeDeviceId: 'pdv-1',
|
|
22
|
+
runtimeDeviceType: PDV_DEVICE_TYPE,
|
|
23
|
+
managedPrinters,
|
|
24
|
+
includeLocalDevice: true,
|
|
25
|
+
});
|
|
26
|
+
assert.deepEqual(ids.sort(), ['pdv-1', 'printer-a', 'printer-b'].sort());
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('accepts deviceId fallback and prefers device', () => {
|
|
30
|
+
const idsWithOnlyDeviceId = resolveSpoolDeviceIdsForRuntime({
|
|
31
|
+
runtimeDeviceId: 'pdv-1',
|
|
32
|
+
runtimeDeviceType: PDV_DEVICE_TYPE,
|
|
33
|
+
managedPrinters: [{deviceId: 'should-not-be-used', type: PRINT_DEVICE_TYPE}],
|
|
34
|
+
includeLocalDevice: false,
|
|
35
|
+
});
|
|
36
|
+
const preferred = resolveSpoolDeviceIdsForRuntime({
|
|
37
|
+
runtimeDeviceId: 'pdv-1',
|
|
38
|
+
runtimeDeviceType: PDV_DEVICE_TYPE,
|
|
39
|
+
managedPrinters: [{device: 'printer-a'}],
|
|
40
|
+
includeLocalDevice: false,
|
|
41
|
+
});
|
|
42
|
+
assert.deepEqual(preferred, ['printer-a']);
|
|
43
|
+
assert.deepEqual(idsWithOnlyDeviceId, ['should-not-be-used']);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('DISPLAY only polls managed printers', () => {
|
|
47
|
+
const ids = resolveSpoolDeviceIdsForRuntime({
|
|
48
|
+
runtimeDeviceId: 'display-1',
|
|
49
|
+
runtimeDeviceType: DISPLAY_DEVICE_TYPE,
|
|
50
|
+
managedPrinters,
|
|
51
|
+
});
|
|
52
|
+
assert.deepEqual(ids.sort(), ['printer-a', 'printer-b'].sort());
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('PRINT runtime does not receive jobs (no spool poll)', () => {
|
|
56
|
+
const ids = resolveSpoolDeviceIdsForRuntime({
|
|
57
|
+
runtimeDeviceId: 'printer-a',
|
|
58
|
+
runtimeDeviceType: PRINT_DEVICE_TYPE,
|
|
59
|
+
managedPrinters,
|
|
60
|
+
});
|
|
61
|
+
assert.deepEqual(ids, []);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('empty managed list + no local => no destinations', () => {
|
|
65
|
+
const ids = resolveSpoolDeviceIdsForRuntime({
|
|
66
|
+
runtimeDeviceId: 'pdv-1',
|
|
67
|
+
runtimeDeviceType: PDV_DEVICE_TYPE,
|
|
68
|
+
managedPrinters: [],
|
|
69
|
+
includeLocalDevice: false,
|
|
70
|
+
});
|
|
71
|
+
assert.deepEqual(ids, []);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('resolvePrintRoutingError', () => {
|
|
76
|
+
it('returns a visible message when routing has no destination', () => {
|
|
77
|
+
assert.equal(resolvePrintRoutingError({}), PRINT_ROUTE_MISSING_DESTINATION);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('is silent when a printer device is selected', () => {
|
|
81
|
+
assert.equal(
|
|
82
|
+
resolvePrintRoutingError({selectedPrinter: {device: 'printer-a'}}),
|
|
83
|
+
'',
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import {describe, it} from 'node:test';
|
|
3
|
+
import {
|
|
4
|
+
resolveSpoolId,
|
|
5
|
+
extractCollectionMembers,
|
|
6
|
+
mergeOpenSpoolCollections,
|
|
7
|
+
resolveRequestedTargetDeviceId,
|
|
8
|
+
resolveRequestedTargetDeviceTypeValue,
|
|
9
|
+
} from '../../../react/utils/printSpoolUtils.js';
|
|
10
|
+
|
|
11
|
+
describe('printSpoolUtils (#628 modularization)', () => {
|
|
12
|
+
it('resolveSpoolId handles number, string, object and @id', () => {
|
|
13
|
+
assert.equal(resolveSpoolId(12), 12);
|
|
14
|
+
assert.equal(resolveSpoolId('spool/34'), 34);
|
|
15
|
+
assert.equal(resolveSpoolId({id: 7}), 7);
|
|
16
|
+
assert.equal(resolveSpoolId({'@id': '/spools/9'}), 9);
|
|
17
|
+
assert.equal(resolveSpoolId(null), null);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('extractCollectionMembers supports array, member, hydra:member', () => {
|
|
21
|
+
assert.deepEqual(extractCollectionMembers([1]), [1]);
|
|
22
|
+
assert.deepEqual(extractCollectionMembers({member: [2]}), [2]);
|
|
23
|
+
assert.deepEqual(extractCollectionMembers({'hydra:member': [3]}), [3]);
|
|
24
|
+
assert.deepEqual(extractCollectionMembers({}), []);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('mergeOpenSpoolCollections dedupes and sorts by id', () => {
|
|
28
|
+
const merged = mergeOpenSpoolCollections([
|
|
29
|
+
[{id: 3}, {id: 1}],
|
|
30
|
+
[{id: 1}, {id: 2}],
|
|
31
|
+
]);
|
|
32
|
+
assert.deepEqual(
|
|
33
|
+
merged.map(s => s.id),
|
|
34
|
+
[1, 2, 3],
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('resolveRequestedTargetDeviceId prefers explicit fields', () => {
|
|
39
|
+
assert.equal(
|
|
40
|
+
resolveRequestedTargetDeviceId({targetDeviceId: 5}, 9),
|
|
41
|
+
'5',
|
|
42
|
+
);
|
|
43
|
+
assert.equal(resolveRequestedTargetDeviceId({}, 9), '9');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('resolveRequestedTargetDeviceTypeValue normalizes type', () => {
|
|
47
|
+
assert.equal(
|
|
48
|
+
resolveRequestedTargetDeviceTypeValue({targetDeviceType: 'pdv'}),
|
|
49
|
+
'PDV',
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -11,6 +11,7 @@ const {
|
|
|
11
11
|
getRuntimeFooterWebIdentifierCandidates,
|
|
12
12
|
getRuntimeFooterWebHost,
|
|
13
13
|
normalizeRuntimeFooterText,
|
|
14
|
+
shouldShowRuntimeFooter,
|
|
14
15
|
} = require('../../../react/utils/runtimeFooter')
|
|
15
16
|
|
|
16
17
|
const originalLocationDescriptor = Object.getOwnPropertyDescriptor(globalThis, 'location')
|
|
@@ -445,3 +446,9 @@ test('rotates each footer line before the runtime version entry', () => {
|
|
|
445
446
|
['www.site.com', '(11) 99999-9999', 'web (203.0.113.42) / v1.3.6'],
|
|
446
447
|
)
|
|
447
448
|
})
|
|
449
|
+
|
|
450
|
+
test('hides runtime device information on the customer paylist only', () => {
|
|
451
|
+
assert.equal(shouldShowRuntimeFooter('PaylistPage'), false)
|
|
452
|
+
assert.equal(shouldShowRuntimeFooter('LoginPage'), true)
|
|
453
|
+
assert.equal(shouldShowRuntimeFooter('WalletsPage'), true)
|
|
454
|
+
})
|
|
@@ -1,152 +1,158 @@
|
|
|
1
|
-
jest.mock('@controleonline/ui-common/src/api', () => ({
|
|
2
|
-
api: {
|
|
3
|
-
fetch: jest.fn(),
|
|
4
|
-
},
|
|
5
|
-
}));
|
|
6
|
-
|
|
7
|
-
const {api} = require('@controleonline/ui-common/src/api');
|
|
1
|
+
jest.mock('@controleonline/ui-common/src/api', () => ({
|
|
2
|
+
api: {
|
|
3
|
+
fetch: jest.fn(),
|
|
4
|
+
},
|
|
5
|
+
}));
|
|
6
|
+
|
|
7
|
+
const {api} = require('@controleonline/ui-common/src/api');
|
|
8
8
|
const {
|
|
9
9
|
extractAddressCategoryIds,
|
|
10
10
|
filterShopFranchiseDirectory,
|
|
11
11
|
fetchAllShopFranchiseDirectory,
|
|
12
12
|
fetchShopFranchiseDirectory,
|
|
13
13
|
} = require('../../../react/utils/shopFranchises');
|
|
14
|
-
|
|
15
|
-
const {beforeEach, describe, expect, it} = global;
|
|
16
|
-
|
|
17
|
-
describe('shopFranchises', () => {
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
api.fetch.mockReset();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('uses the public shop franchises endpoint and preserves shop addresses', async () => {
|
|
23
|
-
api.fetch.mockResolvedValueOnce({
|
|
24
|
-
member: [
|
|
25
|
-
{
|
|
26
|
-
id: 21,
|
|
27
|
-
alias: 'Centro',
|
|
28
|
-
shopAddresses: [{id: 501, nickname: 'Loja Centro'}],
|
|
29
|
-
},
|
|
30
|
-
],
|
|
31
|
-
'hydra:member': [
|
|
32
|
-
{
|
|
33
|
-
id: 21,
|
|
34
|
-
alias: 'Centro',
|
|
35
|
-
shopAddresses: [{id: 501, nickname: 'Loja Centro'}],
|
|
36
|
-
},
|
|
37
|
-
],
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
const directory = await fetchShopFranchiseDirectory({
|
|
41
|
-
companyId: 10,
|
|
42
|
-
publicDirectory: true,
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
expect(api.fetch).toHaveBeenCalledWith('/shop/franchises', {
|
|
46
|
-
params: {
|
|
47
|
-
itemsPerPage: 50,
|
|
48
|
-
page: 1,
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
expect(directory).toEqual([
|
|
52
|
-
{
|
|
53
|
-
id: 21,
|
|
54
|
-
alias: 'Centro',
|
|
55
|
-
shopAddresses: [{id: 501, nickname: 'Loja Centro'}],
|
|
56
|
-
},
|
|
57
|
-
]);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('keeps authenticated management lookup scoped by company', async () => {
|
|
14
|
+
|
|
15
|
+
const {beforeEach, describe, expect, it} = global;
|
|
16
|
+
|
|
17
|
+
describe('shopFranchises', () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
api.fetch.mockReset();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('uses the public shop franchises endpoint and preserves shop addresses', async () => {
|
|
61
23
|
api.fetch.mockResolvedValueOnce({
|
|
62
24
|
member: [
|
|
63
25
|
{
|
|
64
|
-
id:
|
|
65
|
-
alias: '
|
|
66
|
-
|
|
67
|
-
},
|
|
68
|
-
],
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
26
|
+
id: 21,
|
|
27
|
+
alias: 'Centro',
|
|
28
|
+
shopAddresses: [{id: 501, nickname: 'Loja Centro'}],
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
'hydra:member': [
|
|
32
|
+
{
|
|
33
|
+
id: 21,
|
|
34
|
+
alias: 'Centro',
|
|
35
|
+
shopAddresses: [{id: 501, nickname: 'Loja Centro'}],
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const directory = await fetchShopFranchiseDirectory({
|
|
41
|
+
companyId: 10,
|
|
42
|
+
publicDirectory: true,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
expect(api.fetch).toHaveBeenCalledWith('/shop/franchises', {
|
|
46
|
+
params: {
|
|
47
|
+
itemsPerPage: 50,
|
|
48
|
+
page: 1,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
expect(directory).toEqual([
|
|
52
|
+
{
|
|
53
|
+
id: 21,
|
|
54
|
+
alias: 'Centro',
|
|
55
|
+
shopAddresses: [{id: 501, nickname: 'Loja Centro'}],
|
|
56
|
+
},
|
|
57
|
+
]);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('loads franchises from people_links on both company and people sides', async () => {
|
|
61
|
+
api.fetch
|
|
62
|
+
.mockResolvedValueOnce({
|
|
63
|
+
member: [
|
|
64
|
+
{
|
|
65
|
+
id: 51,
|
|
66
|
+
linkType: 'franchisee',
|
|
67
|
+
company: {id: 10, alias: 'Franchisor'},
|
|
68
|
+
people: {
|
|
69
|
+
id: 22,
|
|
70
|
+
alias: 'Norte',
|
|
71
|
+
address: [{id: 601, nickname: 'Loja Norte'}],
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
})
|
|
76
|
+
.mockResolvedValueOnce({
|
|
77
|
+
member: [],
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const directory = await fetchShopFranchiseDirectory({companyId: 10});
|
|
81
|
+
|
|
82
|
+
expect(api.fetch).toHaveBeenCalledWith('people_links', {
|
|
83
|
+
params: {
|
|
84
|
+
company: '/people/10',
|
|
85
|
+
linkType: 'franchisee',
|
|
86
|
+
itemsPerPage: 50,
|
|
87
|
+
page: 1,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
expect(api.fetch).toHaveBeenCalledWith('people_links', {
|
|
91
|
+
params: {
|
|
92
|
+
people: '/people/10',
|
|
93
|
+
linkType: 'franchisee',
|
|
94
|
+
itemsPerPage: 50,
|
|
95
|
+
page: 1,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
expect(directory[0].id).toBe(22);
|
|
99
|
+
expect(directory[0].alias).toBe('Norte');
|
|
81
100
|
expect(directory[0].shopAddresses).toEqual([
|
|
82
101
|
{id: 601, nickname: 'Loja Norte'},
|
|
83
102
|
]);
|
|
84
103
|
});
|
|
85
104
|
|
|
86
|
-
|
|
105
|
+
it('loads every page when building the full franchise directory', async () => {
|
|
106
|
+
// Dual-side people_links: company side page1 (full), page2 empty break;
|
|
107
|
+
// people side empty.
|
|
87
108
|
api.fetch
|
|
88
109
|
.mockResolvedValueOnce({
|
|
89
110
|
member: [
|
|
90
111
|
{
|
|
91
|
-
id:
|
|
92
|
-
|
|
93
|
-
|
|
112
|
+
id: 1,
|
|
113
|
+
linkType: 'franchisee',
|
|
114
|
+
company: {id: 10},
|
|
115
|
+
people: {
|
|
116
|
+
id: 21,
|
|
117
|
+
alias: 'Centro',
|
|
118
|
+
shopAddresses: [{id: 501, nickname: 'Loja Centro'}],
|
|
119
|
+
},
|
|
94
120
|
},
|
|
95
121
|
{
|
|
96
|
-
id:
|
|
97
|
-
|
|
98
|
-
|
|
122
|
+
id: 2,
|
|
123
|
+
linkType: 'franchisee',
|
|
124
|
+
company: {id: 10},
|
|
125
|
+
people: {
|
|
126
|
+
id: 22,
|
|
127
|
+
alias: 'Norte',
|
|
128
|
+
shopAddresses: [{id: 601, nickname: 'Loja Norte'}],
|
|
129
|
+
},
|
|
99
130
|
},
|
|
100
131
|
],
|
|
101
132
|
})
|
|
102
133
|
.mockResolvedValueOnce({
|
|
103
134
|
member: [
|
|
104
135
|
{
|
|
105
|
-
id:
|
|
106
|
-
|
|
107
|
-
|
|
136
|
+
id: 3,
|
|
137
|
+
linkType: 'franchisee',
|
|
138
|
+
company: {id: 10},
|
|
139
|
+
people: {
|
|
140
|
+
id: 23,
|
|
141
|
+
alias: 'Sul',
|
|
142
|
+
shopAddresses: [{id: 701, nickname: 'Loja Sul'}],
|
|
143
|
+
},
|
|
108
144
|
},
|
|
109
145
|
],
|
|
110
|
-
})
|
|
146
|
+
})
|
|
147
|
+
.mockResolvedValueOnce({member: []});
|
|
111
148
|
|
|
112
149
|
const directory = await fetchAllShopFranchiseDirectory({
|
|
113
150
|
companyId: 10,
|
|
114
151
|
itemsPerPage: 2,
|
|
115
152
|
});
|
|
116
153
|
|
|
117
|
-
expect(api.fetch).
|
|
118
|
-
|
|
119
|
-
'link.company': '/people/10',
|
|
120
|
-
'link.linkType': 'franchisee',
|
|
121
|
-
itemsPerPage: 2,
|
|
122
|
-
page: 1,
|
|
123
|
-
},
|
|
124
|
-
});
|
|
125
|
-
expect(api.fetch).toHaveBeenNthCalledWith(2, 'people', {
|
|
126
|
-
params: {
|
|
127
|
-
'link.company': '/people/10',
|
|
128
|
-
'link.linkType': 'franchisee',
|
|
129
|
-
itemsPerPage: 2,
|
|
130
|
-
page: 2,
|
|
131
|
-
},
|
|
132
|
-
});
|
|
133
|
-
expect(directory).toEqual([
|
|
134
|
-
{
|
|
135
|
-
id: 21,
|
|
136
|
-
alias: 'Centro',
|
|
137
|
-
shopAddresses: [{id: 501, nickname: 'Loja Centro'}],
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
id: 22,
|
|
141
|
-
alias: 'Norte',
|
|
142
|
-
shopAddresses: [{id: 601, nickname: 'Loja Norte'}],
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
id: 23,
|
|
146
|
-
alias: 'Sul',
|
|
147
|
-
shopAddresses: [{id: 701, nickname: 'Loja Sul'}],
|
|
148
|
-
},
|
|
149
|
-
]);
|
|
154
|
+
expect(api.fetch.mock.calls[0][0]).toBe('people_links');
|
|
155
|
+
expect(directory.map(item => item.id).sort()).toEqual([21, 22, 23]);
|
|
150
156
|
});
|
|
151
157
|
|
|
152
158
|
it('extracts category ids from address category payload variants', () => {
|