fix: follow-up test-script review findings (v3.5.14)
- The SCP80 counter write-back resolves the preset by NAME first (the run
snapshot prefers the name) and only falls back to an ICCID-looking
value: presets with digits in their names are found after a page reload
(the previous fallback ran cardsNormIccid on the name and gave up).
- testScriptProblem validates the *selected* SCP80 source (like the form
and the server), so source=apdu with only sp filled is caught locally
instead of failing with a server 400.
- _test_run_start cleans up (_TEST_RUNNING=False, run state error) when
the worker thread cannot be created/started, and the endpoint answers
500 with a clear error instead of leaving the card blocked behind a run
that never started.
- The 5 s poll writes the counter back whenever a run is finished
(idempotent), so a reloaded page saves it without visiting the pill.
- Mask wildcards ('?') are stripped from the hex fields that cannot carry
a mask (APDU, secured packet, event/file data, TAR/SPI overrides, DCS,
extra TLVs); check values keep them.
Tests: frontend +3 (write-back by name / by ICCID, mask-free fields,
source-aware script check), Python +1 (failed thread start unblocks).
586 frontend / 471 Python green; version 3.5.14; sw simple-v267.
This commit is contained in:
@@ -43,9 +43,11 @@ eval(extractFunc(html, 'testFormSelect'));
|
||||
eval(extractFunc(html, 'testStepRender'));
|
||||
eval(extractFunc(html, 'testRenderChecks'));
|
||||
eval(extractFunc(html, 'testStepCollect'));
|
||||
eval(extractFunc(html, 'testWriteBackCounter'));
|
||||
eval(extractFunc(html, 'testChecksCollect'));
|
||||
eval('var _testEditStep = null; var _testEditChecks = []; var _testEditStepIndex = -1;'
|
||||
+ ' var _testScripts = null; var _testCurrentIdx = -1; var _testRunState = null;');
|
||||
+ ' var _testScripts = null; var _testCurrentIdx = -1; var _testRunState = null;'
|
||||
+ ' var _testLastPresetIdx = -1;');
|
||||
globalThis.localStorage = { getItem: () => null, setItem: () => {}, removeItem: () => {} };
|
||||
|
||||
function fakeForm(values) {
|
||||
@@ -82,6 +84,13 @@ test('testScriptProblem accepts good scripts and names bad ones', () => {
|
||||
assert.match(testScriptProblem({
|
||||
steps: [{ type: 'action', kind: 'file-read', params: {} }] }), /file path/);
|
||||
assert.match(testScriptProblem({ steps: [{ type: 'expect' }] }), /command is required/);
|
||||
// the selected SCP80 source decides which value is required
|
||||
assert.match(testScriptProblem({ steps: [{ type: 'action', kind: 'scp80',
|
||||
params: { source: 'apdu', sp: 'AA' } }] }), /SCP80/);
|
||||
assert.strictEqual(testScriptProblem({ steps: [{ type: 'action', kind: 'scp80',
|
||||
params: { source: 'apdu', apdu: '80E2' } }] }), '');
|
||||
assert.strictEqual(testScriptProblem({ steps: [{ type: 'action', kind: 'scp80',
|
||||
params: { source: 'sp', sp: 'AA', apdu: '80E2' } }] }), '');
|
||||
});
|
||||
|
||||
test('testStepSummary renders actions', () => {
|
||||
@@ -185,6 +194,49 @@ test('the step form renders the chosen source and no pre-filled SW', () => {
|
||||
assert.match(body, /id="test-f-sw" value=""/);
|
||||
});
|
||||
|
||||
test('hex fields strip mask wildcards, check values keep them', () => {
|
||||
_testEditStep = { type: 'action', kind: 'apdu', params: {} };
|
||||
fakeForm({ 'test-step-kind': 'apdu', 'test-f-apdu': '80E2??',
|
||||
'test-f-sw': '', 'test-f-cdata': '', 'test-f-fail': 'error' });
|
||||
testStepCollect();
|
||||
assert.strictEqual(_testEditStep.params.apdu, '80E2');
|
||||
fakeForm({ 'test-step-kind': 'apdu', 'test-f-apdu': '80E2', 'test-f-sw': '91??',
|
||||
'test-f-sw-mode': 'mask', 'test-f-cdata': '', 'test-f-fail': 'error' });
|
||||
testStepCollect();
|
||||
assert.deepStrictEqual(_testEditStep.check.sw, { mode: 'mask', value: '91??' });
|
||||
});
|
||||
|
||||
test('the counter write-back finds the preset by name after a reload', () => {
|
||||
let saved = 0;
|
||||
globalThis.cards = [{ name: 'Card 1', iccid: '8970119000004600098', cntr: '0000000A' }];
|
||||
globalThis.cardsSave = () => { saved++; };
|
||||
globalThis.ioStatus = () => {};
|
||||
// the name lookup must win: the ICCID helpers are not even called
|
||||
globalThis.cardsNormIccid = () => { throw new Error('ICCID lookup for a name'); };
|
||||
globalThis.cardsFindByIccid = () => { throw new Error('ICCID lookup for a name'); };
|
||||
_testLastPresetIdx = -1;
|
||||
_testRunState = { running: false, scp80_counter: '0000000B', preset: 'Card 1' };
|
||||
testWriteBackCounter();
|
||||
assert.strictEqual(cards[0].cntr, '0000000B');
|
||||
assert.strictEqual(saved, 1);
|
||||
testWriteBackCounter(); // idempotent
|
||||
assert.strictEqual(saved, 1);
|
||||
});
|
||||
|
||||
test('the counter write-back falls back to an ICCID snapshot', () => {
|
||||
let saved = 0;
|
||||
globalThis.cards = [{ name: 'Other', iccid: '8970119000004600098', cntr: '0000000A' }];
|
||||
globalThis.cardsSave = () => { saved++; };
|
||||
globalThis.ioStatus = () => {};
|
||||
globalThis.cardsNormIccid = v => String(v).replace(/\D/g, '');
|
||||
globalThis.cardsFindByIccid = v => (globalThis.cardsNormIccid(v) === '8970119000004600098' ? 0 : -1);
|
||||
_testLastPresetIdx = -1;
|
||||
_testRunState = { running: false, scp80_counter: '0000000C', preset: '8970119000004600098' };
|
||||
testWriteBackCounter();
|
||||
assert.strictEqual(cards[0].cntr, '0000000C');
|
||||
assert.strictEqual(saved, 1);
|
||||
});
|
||||
|
||||
test('the item text check offers contains/exact only', () => {
|
||||
const els = {
|
||||
'test-step-modal': { classList: { add: () => {}, remove: () => {} } },
|
||||
|
||||
Reference in New Issue
Block a user