v3.23.8: the command-grouped TERMINAL RESPONSE result list
The step editor's result select offers the full TS 102 223 8.12.0 list; the
spec's applicability table (Tables 6.1/6.2/6.3, "Proactive commands versus
possible terminal response") now groups it by the selected command: the
command-relevant values first (an <optgroup> "relevant for <command>"), the
rest after ("all other results").
The grouping is ordering-only - nothing is hidden, the manual hex entry
covers everything, and ANY (the drain's respond/first too) stays a flat list,
so a stored result always matches an option and real-card deviations remain
reachable.
TR_RESULT_BY_COMMAND is parsed from the pinned PDF (41 commands) and pinned
by frontend/tests/tr_result_by_command.json; trCommandCode() resolves the
editor's command names to their type codes (the first code per name,
matching testCommandOptions) and testFormSelect gained the optgroup support.
Tests: the map vs the parsed fixture, the grouping and the union (nothing
lost), the optgroup rendering and the call-site pin. 861 frontend + 842
Python; sw cache simple-v421.
This commit is contained in:
@@ -62,8 +62,10 @@ eval(extractFunc(html, 'testFormInput'));
|
||||
eval(extractFunc(html, 'testFormSelect'));
|
||||
// the TERMINAL RESPONSE result tables + the shared result-row helpers
|
||||
eval(extractBlock('const TR_RESULTS = [', 'const TR_RESULT_ADD_INFO = [').replace(/^const /gm, 'var '));
|
||||
eval(extractBlock('const TR_RESULT_ADD_INFO = [', 'const TR_RESULT_ALIASES = {').replace(/^const /gm, 'var '));
|
||||
eval(extractBlock('const TR_RESULT_ALIASES = {', 'function trResultOptions').replace(/^const /gm, 'var '));
|
||||
eval(extractBlock('const TR_RESULT_ADD_INFO = [', '// TS 102 223 v18.3.0 Tables 6.1/6.2').replace(/^const /gm, 'var '));
|
||||
eval(extractBlock('const TR_RESULT_BY_COMMAND = {', 'const TR_RESULT_ALIASES = {').replace(/^const /gm, 'var '));
|
||||
eval(extractBlock('const TR_RESULT_ALIASES = {', 'function trCommandCode').replace(/^const /gm, 'var '));
|
||||
eval(extractFunc(html, 'trCommandCode'));
|
||||
eval(extractFunc(html, 'trResultOptions'));
|
||||
eval(extractFunc(html, 'trResultFormValue'));
|
||||
eval(extractFunc(html, 'trResultValid'));
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"_note": "TS 102 223 v18.3.0 Tables 6.1/6.2/6.3 \"Proactive commands versus possible terminal response\", parsed from the pinned PDF (docs/uicc/, workspace copy) - which result values the spec lists for each proactive command. The PWA's TR_RESULT_BY_COMMAND mirrors this file (frontend/tests/tr_results.test.js asserts it); the list is used for GROUPING only (the command-relevant values first, the rest after - nothing is hidden). Regenerate with a pdftotext -layout parse of the table pages if the spec pin changes.",
|
||||
"commands": {
|
||||
"01": ["00", "01", "02", "03", "04", "08", "20", "30", "31", "32", "33", "36", "3C"],
|
||||
"02": ["00", "01", "02", "20", "30", "31", "32", "33", "36"],
|
||||
"03": ["00", "01", "02", "20", "30", "31", "32", "33", "36"],
|
||||
"04": ["00", "01", "02", "20", "30", "31", "32", "33", "36"],
|
||||
"05": ["00", "01", "02", "20", "30", "31", "32", "33", "36"],
|
||||
"10": ["00", "01", "02", "04", "05", "10", "14", "20", "21", "22", "23", "25", "30", "31", "32", "33", "34", "36", "39", "3C"],
|
||||
"14": ["00", "01", "02", "04", "10", "20", "30", "31", "32", "33", "36", "3C"],
|
||||
"15": ["00", "01", "02", "20", "21", "26", "30", "31", "32", "33", "3C"],
|
||||
"20": ["00", "01", "02", "04", "09", "10", "20", "30", "31", "32", "33", "36", "3C"],
|
||||
"21": ["00", "01", "02", "04", "10", "11", "12", "20", "30", "31", "32", "33", "36", "3C"],
|
||||
"22": ["00", "01", "02", "04", "10", "11", "12", "13", "20", "30", "31", "32", "33", "36", "3C"],
|
||||
"23": ["00", "01", "02", "04", "10", "11", "12", "13", "20", "30", "31", "32", "33", "36", "3C"],
|
||||
"24": ["00", "01", "02", "04", "10", "11", "12", "13", "20", "30", "31", "32", "33", "36", "3C"],
|
||||
"25": ["00", "01", "02", "04", "20", "30", "31", "32", "33", "36"],
|
||||
"26": ["00", "01", "02", "06", "20", "30", "31", "32", "33", "36"],
|
||||
"27": ["00", "01", "02", "20", "24", "30", "31", "32", "33", "36"],
|
||||
"28": ["00", "01", "02", "20", "30", "31", "32", "33", "36", "3C"],
|
||||
"30": ["00", "01", "02", "20", "30", "31", "32", "33", "36", "38"],
|
||||
"31": ["00", "01", "02", "20", "30", "31", "32", "33", "36", "38"],
|
||||
"32": ["00", "01", "02", "20", "30", "31", "32", "33", "36", "38"],
|
||||
"33": ["00", "01", "02", "20", "30", "31", "32", "33", "36", "38"],
|
||||
"34": ["00", "01", "02", "20", "30", "31", "32", "33", "36", "3C"],
|
||||
"35": ["00", "01", "02", "20", "30", "31", "32", "33", "36"],
|
||||
"40": ["00", "01", "02", "04", "07", "10", "20", "21", "22", "25", "28", "30", "31", "32", "33", "36", "3A", "3B", "3C"],
|
||||
"41": ["00", "01", "02", "04", "10", "20", "30", "31", "32", "33", "36", "3A", "3C"],
|
||||
"42": ["00", "01", "02", "04", "10", "20", "30", "31", "32", "33", "36", "3A", "3C"],
|
||||
"43": ["00", "01", "02", "04", "10", "20", "21", "22", "30", "31", "32", "33", "36", "3A", "3C"],
|
||||
"44": ["00", "01", "02", "04", "10", "20", "30", "31", "32", "33", "36"],
|
||||
"45": ["00", "01", "04", "10", "20", "30", "31", "32", "33", "36", "3A", "3B", "3C"],
|
||||
"46": ["00", "01", "04", "10", "20", "30", "31", "32", "33", "36", "3A", "3B", "3C"],
|
||||
"47": ["00", "01", "02", "07", "20", "30", "31", "32", "33", "36", "3A"],
|
||||
"50": ["00", "01", "20", "30", "31", "32", "33", "36", "3C"],
|
||||
"51": ["00", "01", "20", "30", "31", "32", "33", "36", "3C"],
|
||||
"60": ["00", "01", "02", "27", "30", "31", "32", "33", "36", "3D"],
|
||||
"61": ["00", "01", "02", "27", "30", "31", "32", "33", "36", "3D"],
|
||||
"62": ["00", "01", "02", "10", "27", "30", "31", "32", "33", "36", "3D"],
|
||||
"70": ["00", "01", "20", "30", "31", "32", "33", "36"],
|
||||
"71": ["00", "01", "20", "30", "31", "32", "33"],
|
||||
"72": ["00", "01", "20", "30", "31", "32", "33", "36"],
|
||||
"73": ["00", "01", "20", "30", "31", "32", "33", "36"],
|
||||
"79": ["00", "01", "20", "30", "31", "32", "36"]
|
||||
}
|
||||
}
|
||||
@@ -31,9 +31,12 @@ function extractFunc(src, name) {
|
||||
|
||||
// Rewrite top-level const -> var so the tables leak out of sloppy-mode eval.
|
||||
eval(extractBlock('const TR_RESULTS = [', 'const TR_RESULT_ADD_INFO = [').replace(/^const /gm, 'var '));
|
||||
eval(extractBlock('const TR_RESULT_ADD_INFO = [', 'const TR_RESULT_ALIASES = {').replace(/^const /gm, 'var '));
|
||||
eval(extractBlock('const TR_RESULT_ALIASES = {', 'function trResultOptions').replace(/^const /gm, 'var '));
|
||||
eval(extractBlock('const TR_RESULT_ADD_INFO = [', '// TS 102 223 v18.3.0 Tables 6.1/6.2').replace(/^const /gm, 'var '));
|
||||
eval(extractBlock('const TR_RESULT_BY_COMMAND = {', 'const TR_RESULT_ALIASES = {').replace(/^const /gm, 'var '));
|
||||
eval(extractBlock('const TR_RESULT_ALIASES = {', 'function trCommandCode').replace(/^const /gm, 'var '));
|
||||
eval(extractBlock('const CMD_NAMES = {', 'function cmdQualifierShort').replace(/^const /gm, 'var '));
|
||||
eval(extractFunc(html, 'esc'));
|
||||
eval(extractFunc(html, 'trCommandCode'));
|
||||
eval(extractFunc(html, 'trResultOptions'));
|
||||
eval(extractFunc(html, 'trResultFormValue'));
|
||||
eval(extractFunc(html, 'trResultValid'));
|
||||
@@ -110,7 +113,7 @@ test('the step editor offers the full list for all three result fields', () => {
|
||||
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);
|
||||
assert.strictEqual((form.match(/\{list: true/g) || []).length, 2, form);
|
||||
});
|
||||
|
||||
test('trResultRowHtml renders the manual entry and the additional info', () => {
|
||||
@@ -135,3 +138,59 @@ test('trResultRowHtml renders the manual entry and the additional info', () => {
|
||||
+ trResultRowHtml('test-f-pfirst', '', '', { empty: 'same as above' });
|
||||
assert.strictEqual((twoRows.match(/id="tr-addinfo-list"/g) || []).length, 1, twoRows);
|
||||
});
|
||||
|
||||
test('the command/result grouping table matches the parsed spec fixture', () => {
|
||||
const fixture = JSON.parse(fs.readFileSync(path.join(__dirname, 'tr_result_by_command.json'), 'utf8'));
|
||||
assert.deepStrictEqual(TR_RESULT_BY_COMMAND, fixture.commands);
|
||||
// the editor's command names resolve to their type codes (the first code
|
||||
// wins, matching testCommandOptions' de-duplication)
|
||||
assert.strictEqual(trCommandCode('REFRESH'), 0x01);
|
||||
assert.strictEqual(trCommandCode('PLAY TONE'), 0x20);
|
||||
assert.strictEqual(trCommandCode('TIMER MANAGEMENT'), 0x27);
|
||||
assert.strictEqual(trCommandCode('OPEN CHANNEL'), 0x40);
|
||||
assert.ok(Number.isNaN(trCommandCode('ANY')));
|
||||
assert.ok(Number.isNaN(trCommandCode('NONSENSE')));
|
||||
// every mapped code is a known proactive command
|
||||
for (const code of Object.keys(TR_RESULT_BY_COMMAND)) {
|
||||
assert.ok(CMD_NAMES[code], 'the mapped code 0x' + code + ' must be a known command');
|
||||
}
|
||||
});
|
||||
|
||||
test('trResultOptions groups the list by the selected command', () => {
|
||||
const flat = trResultOptions();
|
||||
assert.strictEqual(flat.filter(o => o.group).length, 0, 'no groups without a command');
|
||||
assert.strictEqual(flat.length, TR_RESULTS.length + 1);
|
||||
assert.strictEqual(trResultOptions({ command: 'ANY' }).filter(o => o.group).length, 0,
|
||||
'ANY stays flat');
|
||||
const grouped = trResultOptions({ command: 'REFRESH' });
|
||||
assert.deepStrictEqual(grouped.filter(o => o.group).map(o => o.group),
|
||||
['relevant for REFRESH', 'all other results']);
|
||||
// the first group carries the spec's REFRESH set in the spec order
|
||||
const split = grouped.findIndex(o => o.group === 'all other results');
|
||||
const first = grouped.slice(1, split);
|
||||
assert.deepStrictEqual(first.map(o => o.v),
|
||||
TR_RESULT_BY_COMMAND['01'].map(hex => (TR_RESULTS.find(r => r.v === hex) || {}).alias || hex));
|
||||
// nothing is hidden: both groups together are the full list + the manual
|
||||
// entry, and the stored value always matches an option
|
||||
const values = grouped.filter(o => !o.group).map(o => o.v).sort();
|
||||
const all = TR_RESULTS.map(r => r.alias || r.v).concat(['manual']).sort();
|
||||
assert.deepStrictEqual(values, all);
|
||||
});
|
||||
|
||||
test('testFormSelect renders the optgroups', () => {
|
||||
const out = testFormSelect('x', 'ok', [{ group: 'relevant for REFRESH' }, { v: 'ok', l: '00 - ok' },
|
||||
{ group: 'all other results' }, { v: '1F', l: '1F - Slices status change' }]);
|
||||
assert.ok(out.includes('<optgroup label="relevant for REFRESH">'), out);
|
||||
assert.ok(out.includes('<optgroup label="all other results">'), out);
|
||||
assert.strictEqual((out.match(/<optgroup/g) || []).length, 2, out);
|
||||
assert.strictEqual((out.match(/<\/optgroup>/g) || []).length, 2, out);
|
||||
assert.ok(out.includes('<option value="ok" selected>00 - ok</option>'), out);
|
||||
// the flat form is unchanged
|
||||
assert.ok(!testFormSelect('y', 'ok', [{ v: 'ok', l: '00 - ok' }]).includes('optgroup'));
|
||||
});
|
||||
|
||||
test('the expectation result row passes the selected command', () => {
|
||||
const form = extractFunc(html, 'testStepRender');
|
||||
assert.ok(/trResultRowHtml\('test-f-result'[\s\S]*?command: step\.command \|\| 'ANY'/.test(form),
|
||||
'the expectation result must group by the selected command');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user