fix: the TAR probe view auto-selects the preset; the keyset selectors refresh (v3.8.0)

Two selector bugs in the new TAR-probe view and one stale-list bug in the RAM
view, all from the lazy population of the view-specific preset selects:

- cardsAutoSelectByIccid() set sp-card-sel and ram-card-sel only, so the TAR
  probe view never got the equipped card's preset (and tarExecute() then ran
  with preset_id: '', i.e. without persisting the counters it consumed);
- tarRender()'s keep-fallback was -1 instead of the remembered _ramCardIdx, so
  the select stayed on "— Select card —" even when the auto-selection had
  remembered the preset; the TAR pill switch now also calls tarRender();
- the keyset selectors showed a stale "N — not defined in the preset":
  spKeysetSync() runs during the auto-selection, when ram/tar-card-sel have no
  options yet, so those lists were built with no preset; ramRender() now
  rebuilds them (spKeysetSync) once its preset is known - tarRender() already
  did.

Tests: the auto-select covers tar-card-sel; a DOM-driven regression test
(ramRender with a remembered preset and a two-keyset preset must show
value="2" selected and no "not defined" entry); source guards for ramRender's
sync, tarRender's keep and the TAR pill switch.  sw cache simple-v339.
This commit is contained in:
2026-09-30 22:41:09 +03:00
parent 18e705a8c3
commit 31715ba58a
5 changed files with 64 additions and 6 deletions
+11 -3
View File
@@ -1831,7 +1831,7 @@ function scp80SwitchSubtab(name) {
document.getElementById('scp80-sub-ram').classList.toggle('hidden', name !== 'ram');
document.getElementById('scp80-sub-tar').classList.toggle('hidden', name !== 'tar');
if (name === 'ram') ramRender();
if (name === 'tar') tarProbeRender();
if (name === 'tar') { tarRender(); tarProbeRender(); }
scp80HelpAnchor = { sp: 'secured-packet', ram: 'ram', tar: 'tar-probe' }[name] || 'secured-packet';
setHelpAnchor(scp80HelpAnchor);
}
@@ -8402,6 +8402,10 @@ function ramRender() {
sel.appendChild(opt);
});
if (keep !== null && keep !== undefined && cards[keep]) sel.value = String(keep);
// the keyset selector is built from this view's preset: rebuild it now that
// the preset is known (the auto-selection runs before the select has its
// options, which used to leave a stale "not defined in the preset" entry)
spKeysetSync();
ramOpChanged();
ramExpandedDetailsInit();
}
@@ -10695,7 +10699,9 @@ function tarRender() {
const sel = document.getElementById('tar-card-sel');
if (!sel) return;
const prev = parseInt(sel.value, 10);
const keep = (!isNaN(prev) && cards[prev]) ? prev : -1;
// keep the current/remembered selection across rebuilds (the auto-selection
// sets _ramCardIdx before this select has its options)
const keep = (!isNaN(prev) && cards[prev]) ? prev : _ramCardIdx;
sel.innerHTML = '<option value="" data-l10n="— Select card —">— Select card —</option>';
cards.forEach((c, i) => {
const opt = document.createElement('option');
@@ -10703,7 +10709,7 @@ function tarRender() {
opt.textContent = c.name || ('Card ' + i);
sel.appendChild(opt);
});
if (keep >= 0) sel.value = String(keep);
if (keep !== null && keep !== undefined && cards[keep]) sel.value = String(keep);
spKeysetSync();
}
@@ -11849,6 +11855,8 @@ function cardsAutoSelectByIccid(iccid) {
if (sp) sp.value = String(idx);
const ram = document.getElementById('ram-card-sel');
if (ram) ram.value = String(idx);
const tar = document.getElementById('tar-card-sel');
if (tar) tar.value = String(idx);
_ramCardIdx = idx;
cardsApply(String(idx));
return idx;
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simple-v338';
const CACHE = 'simple-v339';
const URLS = [
'index.html',
'help.html',
+6 -1
View File
@@ -34,7 +34,8 @@ const RAW_HEX = '980711090000640090F8';
const DIGITS = '8970119000004600098';
function fakeDoc() {
const els = { 'sp-card-sel': { value: '' }, 'ram-card-sel': { value: '' }, 'cards-iccid': { value: '' } };
const els = { 'sp-card-sel': { value: '' }, 'ram-card-sel': { value: '' },
'tar-card-sel': { value: '' }, 'cards-iccid': { value: '' } };
globalThis.document = { getElementById: id => els[id] || null };
return els;
}
@@ -78,6 +79,8 @@ test('cardsAutoSelectByIccid selects the preset in both SCP80 views', () => {
assert.strictEqual(idx, 0);
assert.strictEqual(els['sp-card-sel'].value, '0');
assert.strictEqual(els['ram-card-sel'].value, '0');
assert.strictEqual(els['tar-card-sel'].value, '0',
'the TAR probe view gets the equipped card\'s preset too');
assert.deepStrictEqual(applied, ['0']);
assert.strictEqual(globalThis._ramCardIdx, 0);
});
@@ -162,5 +165,7 @@ test('the From card button and the duplicate refusal are wired into the form', (
test('the card-state update wires the ICCID into the preset selection', () => {
assert.ok(html.includes('cardsAutoSelectByIccid(status.iccid);'));
assert.ok(html.includes('_cardsAutoIccid = null;'));
assert.ok(html.includes("getElementById('tar-card-sel')"),
'the auto-selection must cover the TAR probe view');
assert.ok(html.includes("(data.iccid ? ' | ICCID: <b>' + esc(data.iccid) + '</b>' : '')"));
});
+45 -1
View File
@@ -23,7 +23,8 @@ function extractFunc(src, name) {
let code = '';
for (const fn of ['spKeysetKvnOf', 'spKeysetList', 'spKeysetFor', 'spKeysetCheck',
'spKeysetOptionsHtml', 'spCounterTracked', 'esc', 'escHtml']) {
'spKeysetOptionsHtml', 'spCounterTracked', 'spPresetIdx', 'ramPresetIdx',
'tarPresetIdx', 'spKeysetSync', 'ramRender', 'esc', 'escHtml']) {
code += extractFunc(html, fn) + '\n';
}
code += 'function t(s){return s;}\n';
@@ -131,3 +132,46 @@ test('spCounterTracked follows SPI1 b5b4 (TS 102 225 5.1.1)', () => {
}
assert.strictEqual(spCounterTracked('zz'), false);
});
test('ramRender rebuilds the keyset selector from the remembered preset', () => {
// regression: the auto-selection runs before the RAM select has options, so
// its keyset list was built with no preset and stayed on "not defined"
const els = {
'ram-card-sel': { value: '', innerHTML: '', appendChild() {} },
'ram-keyset-sel': { value: '', innerHTML: '' },
'sp-keyset-sel': { value: '', innerHTML: '' },
'tar-keyset-sel': { value: '', innerHTML: '' },
'sp-kic-hex': { value: '25' },
'sp-kid-hex': { value: '25' },
};
globalThis.document = {
getElementById: id => els[id] || null,
createElement: () => ({ value: '', textContent: '' }),
};
globalThis.cards = [{ name: 'EP', keysets: [
{ kic: '15', kid: '15', kicKey: 'AA', kidKey: 'BB', cntr: '0000000001' },
{ kic: '25', kid: '25', kicKey: 'CC', kidKey: 'DD', cntr: '0000000184' }] }];
globalThis._ramCardIdx = 0;
globalThis.ramOpChanged = () => {};
globalThis.ramExpandedDetailsInit = () => {};
ramRender();
assert.strictEqual(els['ram-card-sel'].value, '0',
'the preset select keeps the remembered card');
assert.match(els['ram-keyset-sel'].innerHTML, /value="2" selected/);
assert.ok(!els['ram-keyset-sel'].innerHTML.includes('not defined'),
'the keyset selector must not show a stale "not defined" entry');
});
test('the RAM and TAR views rebuild their keyset lists when their preset is known', () => {
// ramRender() syncs the keyset selectors once the preset is set ...
const ram = html.slice(html.indexOf('function ramRender()'),
html.indexOf('function ramRender()') + 1400);
assert.ok(ram.includes('spKeysetSync();'), 'ramRender must rebuild the keyset lists');
// ... tarRender() already does, and keeps the remembered selection
const tar = html.slice(html.indexOf('function tarRender()'),
html.indexOf('function tarRender()') + 1200);
assert.ok(tar.includes('spKeysetSync();'));
assert.ok(tar.includes(': _ramCardIdx;'), 'tarRender keeps the remembered preset');
// opening the TAR pill populates both its selectors
assert.ok(html.includes("if (name === 'tar') { tarRender(); tarProbeRender(); }"));
});
+1
View File
@@ -28,6 +28,7 @@ const FNS = ['berLenStr', 'buildApdu', 'escHtml', 'esc', 'chainInit', 'chainRamB
'ramExpandedReport', 'ramExpandedTags', 'ramExpandedGroups', 'ramExpandedElfForm', 'ramElfVersionHint', 'ramChainGetResponse', 'ramDeriveElfVersions', 'ramElfAppletCandidate',
'spCntrLow', 'ramCntrLowHtml', 'ramCntrLowPresetIdx', 'ramShowCntrLow',
'spKeysetKvnOf', 'spKeysetList', 'spKeysetFor', 'spKeysetCheck',
'spKeysetOptionsHtml', 'spPresetIdx', 'tarPresetIdx',
'cardsKeysetsFromForm', 'spKeysetSync', 'spKeysetApply', 'spKeysetChanged',
'ramKeysetChanged', 'ramPresetIdx', 'spKeysetGuard', 'ramKeysetGuard',
'ramCardIdxAfterRemove', 'ramClearResults', 'ramHideProgress', 'ramOpChanged', 'ramRender', 'ramApplyCard', 'ramExecute', 'decodePrivileges', 'ramActionBtn', 'ramCapToolkitMode', 'ramOpProgressText',