diff --git a/frontend/index.html b/frontend/index.html index 5724049..df5a1d9 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -8045,6 +8045,11 @@ function trResultOptions() { // A stored result -> the select's value and the manual hex ('' unless the // value is not in the list). Accepts the aliases, the hex and the 0x form. function trResultFormValue(stored) { + // a numeric stored value (an API-authored script: the engine reads ints as + // the value, not as hex) -> its hex form + if (typeof stored === 'number') { + return trResultFormValue(stored.toString(16).toUpperCase().padStart(2, '0')); + } const raw = String(stored === undefined || stored === null ? '' : stored).trim(); if (raw === '') return {value: '', manual: ''}; if (TR_RESULT_ALIASES[raw.toLowerCase()]) return {value: raw.toLowerCase(), manual: ''}; @@ -8094,9 +8099,13 @@ function trResultRowHtml(selId, stored, addInfo, opts) { html += '
' + '
' + testFormInput(selId + '-addinfo', add, '00', 'list="tr-addinfo-list" maxlength="2"') + '
'; - html += '' - + TR_RESULT_ADD_INFO.map(a => '').join('') - + ''; + // the suggestion list once per form (opts.list) - a form may carry several + // result rows (the drain's respond + first) and duplicate ids are invalid + if (opts && opts.list) { + html += '' + + TR_RESULT_ADD_INFO.map(a => '').join('') + + ''; + } return html; } @@ -8213,7 +8222,8 @@ function testStepRender() { } else if (step.kind === 'proactive-drain') { const pr = p.respond || {}; html += testFormRow('TERMINAL RESPONSE result (each command)', - trResultRowHtml('test-f-presult', pr.result === undefined ? 'ok' : pr.result, pr.additional_info)); + trResultRowHtml('test-f-presult', pr.result === undefined ? 'ok' : pr.result, pr.additional_info, + {list: true})); const pf = p.first || {}; html += testFormRow('First command only (optional)', trResultRowHtml('test-f-pfirst', pf.result === undefined ? '' : pf.result, pf.additional_info, @@ -8261,7 +8271,8 @@ function testStepRender() { + '
'; const r = step.respond || {}; html += testFormRow('TERMINAL RESPONSE result', - trResultRowHtml('test-f-result', r.result === undefined ? 'ok' : r.result, r.additional_info)); + trResultRowHtml('test-f-result', r.result === undefined ? 'ok' : r.result, r.additional_info, + {list: true})); html += testFormRow('Item identifier (SELECT ITEM / SET UP MENU)', testFormInput('test-f-ritem', r.item_id, '1..255')); html += testFormRow('Text (GET INKEY / GET INPUT)', '
' + testFormInput('test-f-rtext', r.text, 'text') diff --git a/frontend/tests/tr_results.test.js b/frontend/tests/tr_results.test.js index 6f360c1..5768b15 100644 --- a/frontend/tests/tr_results.test.js +++ b/frontend/tests/tr_results.test.js @@ -90,6 +90,11 @@ test('trResultFormValue maps the stored alias/hex/manual values', () => { assert.deepStrictEqual(trResultFormValue(''), { value: '', manual: '' }); assert.deepStrictEqual(trResultFormValue(undefined), { value: '', manual: '' }); assert.deepStrictEqual(trResultFormValue('bogus'), { value: 'ok', manual: '' }); + // a numeric stored value is the value, not hex (the engine reads ints) + assert.deepStrictEqual(trResultFormValue(7), { value: 'modified', manual: '' }); + assert.deepStrictEqual(trResultFormValue(16), { value: 'cancel', manual: '' }); + assert.deepStrictEqual(trResultFormValue(10), { value: 'manual', manual: '0A' }); + assert.deepStrictEqual(trResultFormValue(0), { value: 'ok', manual: '' }); }); test('trResultValid accepts the aliases and hex bytes only', () => { @@ -103,10 +108,13 @@ test('the step editor offers the full list for all three result fields', () => { assert.ok(form.includes("trResultRowHtml('test-f-presult'"), 'the drain result'); assert.ok(form.includes("trResultRowHtml('test-f-pfirst'"), 'the drain first'); assert.ok(form.includes("{empty: 'same as above'}"), 'the first keeps its empty option'); + // the suggestion list is rendered by the first result row of each form + // (the drain's respond and the expectation), never twice in one form + assert.strictEqual((form.match(/\{list: true\}/g) || []).length, 2, form); }); test('trResultRowHtml renders the manual entry and the additional info', () => { - const out = trResultRowHtml('test-f-result', '07', '04'); + const out = trResultRowHtml('test-f-result', '07', '04', { list: true }); assert.ok(out.includes('07 - modified'), out); assert.ok(out.includes('value="04"'), out); assert.ok(out.includes('id="tr-addinfo-list"'), out); @@ -119,4 +127,11 @@ test('trResultRowHtml renders the manual entry and the additional info', () => { // a server-normalised (integer) additional info shows as hex const intAdd = trResultRowHtml('test-f-result', '07', 10); assert.ok(intAdd.includes('value="0A"'), intAdd); + // the suggestion list is rendered once per form: only the row with the + // flag carries it (a form may have several result rows) + assert.ok(!manual.includes('id="tr-addinfo-list"'), manual); + assert.ok(!intAdd.includes('id="tr-addinfo-list"'), intAdd); + const twoRows = trResultRowHtml('test-f-presult', 'ok', '', { list: true }) + + trResultRowHtml('test-f-pfirst', '', '', { empty: 'same as above' }); + assert.strictEqual((twoRows.match(/id="tr-addinfo-list"/g) || []).length, 1, twoRows); });