openvibers/openvibe.network Commits

Admin AI tab: x-unit seconds in the owner settings form (#562)

OP OpenVibers committed 8 days ago · 09ca90485a · 2 files, +52 −1

Changes

ddiff --git a/public/js/admin/ai.js b/public/js/admin/ai.js
iindex 3bea6de1..72d66fd8 100644
---- a/public/js/admin/ai.js
++++ b/public/js/admin/ai.js
@@@ -574,10 +574,17 @@ const PAI_ACCESS_LABEL = {
OWN_DEPLOYMENT: 'Own deployment', AUTHORIZED_WHOLESALE: 'Authorized wholesale', CUSTOMER_BYOK: 'Customer BYOK',
RESTRICTED: 'Restricted', UNVERIFIED: 'Unverified',
};
++// An integer's x-unit, as the owner form labels it. A unit this page does not know is left off so the
++// input is a plain number: the page then works with a newer openvibe-shared before its pin moves.
++const PAI_UNIT_LABEL = { ms: 'ms', seconds: 's', bps: 'bps' };
const PAI = {
providers: [], services: [], namespaces: [], warnings: [], vault: {}, serviceState: {}, loading: false, addProviderError: null,
};
++function paiUnitLabel(unit) {
++ return Object.prototype.hasOwnProperty.call(PAI_UNIT_LABEL, unit) ? PAI_UNIT_LABEL[unit] : '';
++}
++
function paiLabel(service) {
return PAI_NAMES[service] || ('OpenVibe.' + String(service || '').replace(/^./, (c) => c.toUpperCase()));
}
@@@ -1093,7 +1100,9 @@ function paiField(n, key, prop, pinned, pinName) {
} else if (prop['x-unit'] === 'nano_usd') {
control = '<span class="pai-money-input"><span class="pai-prefix">$</span><input data-pai-control data-type="nano_usd" data-key="' + esc(key) + '" inputmode="decimal" value="' + esc(paiUsd(value)) + '" aria-label="' + esc(title) + '"' + dis + '></span>';
} else if (type === 'integer') {
-- control = '<span class="pai-unit-input"><input data-pai-control data-type="integer" data-key="' + esc(key) + '"' + (nullable ? ' data-nullable="1"' : '') + ' type="number" step="1" value="' + esc(value == null ? '' : value) + '" aria-label="' + esc(title) + '"' + dis + '><span class="pai-unit">' + esc(prop['x-unit'] || '') + '</span></span>';
++ const unit = paiUnitLabel(prop['x-unit']);
++ const input = '<input data-pai-control data-type="integer" data-key="' + esc(key) + '"' + (nullable ? ' data-nullable="1"' : '') + ' type="number" step="1" value="' + esc(value == null ? '' : value) + '" aria-label="' + esc(title) + '"' + dis + '>';
++ control = unit ? '<span class="pai-unit-input">' + input + '<span class="pai-unit">' + esc(unit) + '</span></span>' : input;
} else if (type === 'number') {
control = '<input data-pai-control data-type="number" data-key="' + esc(key) + '"' + (nullable ? ' data-nullable="1"' : '') + ' type="number" step="any" value="' + esc(value == null ? '' : value) + '" aria-label="' + esc(title) + '"' + dis + '>';
} else {
ddiff --git a/test/admin-ai-units.test.js b/test/admin-ai-units.test.js
nnew file mode 100644
iindex 00000000..73c09305
---- /dev/null
++++ b/test/admin-ai-units.test.js
@@@ -0,0 +1,42 @@
++'use strict';
++// The owner settings form's unit labels (public/js/admin/ai.js, Platform AI). An integer with
++// x-unit "seconds" is shown in seconds with the label "s" (the same treatment as ms); a unit this
++// page does not know is still a plain number, so a newer openvibe-shared schema renders before the
++// pin moves.
++// node test/admin-ai-units.test.js
++const assert = require('assert');
++const fs = require('fs');
++const path = require('path');
++const vm = require('vm');
++
++const src = fs.readFileSync(path.join(__dirname, '..', 'public', 'js', 'admin', 'ai.js'), 'utf8');
++const ctx = {
++ esc: (s) => String(s ?? '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;'),
++ console,
++};
++vm.createContext(ctx);
++vm.runInContext(src + '\n;globalThis.__paiField = paiField;', ctx);
++
++const field = (unit) => ctx.__paiField(
++ { values: { interval: 90 } }, 'interval',
++ { type: 'integer', title: 'Interval', ...(unit ? { 'x-unit': unit } : {}) }, false, null,
++);
++
++{
++ const html = field('seconds');
++ assert.match(html, /data-type="integer"/);
++ assert.match(html, /value="90"/, 'the input is in seconds');
++ assert.match(html, /<span class="pai-unit">s<\/span>/, 'seconds is labelled "s"');
++ assert.ok(!html.includes('>seconds<'), 'the raw unit name is not shown');
++}
++{
++ const ms = field('ms');
++ assert.match(ms, /<span class="pai-unit">ms<\/span>/, 'ms keeps its label');
++ assert.match(ms, /value="90"/);
++}
++{
++ const unknown = field('fortnights');
++ assert.ok(!unknown.includes('pai-unit'), 'an unknown unit renders as a plain number');
++ assert.match(unknown, /value="90"/);
++}
++console.log('admin-ai-units: seconds, ms and an unknown unit render as expected');