v3.25.6: the PLI dictionary save feedback + the timer wording

The v3.25.5 report's follow-ups: a field the server could not use was
silently dropped, and the PWA also filtered non-hex fields client-side
(never sending them).  The POST now collects them and names them in the
response - `invalid` (a malformed key/value: not hex, an odd number of
digits, a non-string value) and `unknown` (a well-formed code outside the
qualifier list), both omitted when everything was applied - and the PWA's
Save sends every field, composes "Not saved: 0A (invalid value), 18
(unknown qualifier)" (`pysimPliRejectMsg`) into a new #pli-save-msg line
under the button and shows "Not saved" briefly.

The guide's timer exception now says "card-initiated automatic action"
(the timer retries every 5 s; polling/menu are suspended, not retrying).

Docs: api.md's POST contract, help EN/RU - which also said "22 PLI
qualifiers" while the list has 25.  Tests: the rejection lists per case
(odd length, non-hex value/key, non-string, unknown code) and the
valid-save absence, the PWA message helper and the wiring pins.
887 frontend + 875 Python; sw cache simple-v430.
This commit is contained in:
2026-10-11 16:51:30 +03:00
parent f2046d7f0a
commit 9fd8238051
10 changed files with 152 additions and 36 deletions
+40
View File
@@ -87,3 +87,43 @@ test('the server PLI defaults decode through their codecs', () => {
assert.deepStrictEqual(PLI_CODECS['09'].decode('650101'), { mode: '1' });
assert.deepStrictEqual(PLI_CODECS['0A'].decode('630104'), { charge: '4' });
});
// the same extraction the profiler tests use
function extractFunc(src, name) {
const re = new RegExp('function\\s+' + name + '\\s*\\([^)]*\\)\\s*\\{');
const m = re.exec(src);
if (!m) throw new Error('function ' + name + ' not found');
let i = m.index + m[0].length - 1;
let depth = 0;
for (; i < src.length; i++) {
if (src[i] === '{') depth++;
else if (src[i] === '}') {
depth--;
if (depth === 0) break;
}
}
return src.slice(m.index, i + 1);
}
test('a rejected PLI save is reported, not silently dropped', () => {
// the server names the fields it could not use (invalid/unknown) and the
// PWA shows them under the Save button (v3.25.6)
global.t = s => s;
eval(extractFunc(html, 'pysimPliRejectMsg'));
assert.strictEqual(pysimPliRejectMsg({}), '');
assert.strictEqual(pysimPliRejectMsg({ '0A': '630104' }), '');
assert.strictEqual(pysimPliRejectMsg({ invalid: ['0A'] }),
'Not saved: 0A (invalid value)');
assert.strictEqual(pysimPliRejectMsg({ unknown: ['18'] }),
'Not saved: 18 (unknown qualifier)');
assert.strictEqual(pysimPliRejectMsg({ invalid: ['0A'], unknown: ['18'] }),
'Not saved: 0A (invalid value), 18 (unknown qualifier)');
// the save posts every field (no silent client-side skip) and wires the
// message element + the reject branch
assert.ok(html.includes('if (el) data[q.code] = el.value.trim()'),
'every field is sent to the server for validation');
assert.ok(html.includes('id="pli-save-msg"'), 'the message element exists');
assert.ok(html.includes('const reject = pysimPliRejectMsg(resp);'),
'the save reports the rejection');
delete global.t;
});