v3.17.0: test scripts for the applet handoff - menu by text, spec-order TRs, codings
The applet-testing batch agreed after v3.15/3.16:
- menu-select by text: the step names the item by `text` (+ `mode`
exact/contains, `case_sensitive`) instead of the install-dependent id
(both or neither is refused); the runner resolves it against the card's
cached menu and raises a step error naming the available items when nothing
(or several items) matches. The cache is refreshed on every SET UP MENU
fetch - the proactive chain and the expect step - so an applet install (new
items/ids after its REFRESH) no longer leaves a stale menu behind. The
step editor offers the id or the text + mode and the client-side pre-check
bounds them.
- TERMINAL RESPONSE object order: both builders emitted the Item identifier
before the Result (and the scripted builder its Text string before it too);
TS 102 223 6.8.0 mandates Command details, Device identities, Result,
Duration, Text string, Item identifier. `testscript.build_tr` and
`_menu_send_response` now follow it; `respond.raw` is documented as
appended after the standard objects, and the exact-byte tests pin the order.
- Text codings: the scripted response codes the GSM default alphabet via the
GSM 03.38 codec ('00' packed, '04' 8-bit) instead of latin-1, keeps '08'
UCS2 and sends the spec's null text string for an empty answer; the packing
helper is shared with the interactive path (`testscript.pack_gsm7`). A
UCS2 GET INPUT answer is documented and tested.
- docs/test-scripts.md gained the handoff brief for the applet agent: the
value-collection table, fill-in worksheets for the two applet scenarios, a
menu-by-text example, the validation checklist and a format stamp; help
EN/RU and docs/api.md updated.
Tests: 809 frontend + 705 Python; sw cache simple-v380.
This commit is contained in:
@@ -31,6 +31,7 @@ function extractFunc(src, name, asyncFn) {
|
||||
eval(extractBlock('const CMD_NAMES = {', 'function cmdQualifierShort').replace(/^const /gm, 'var '));
|
||||
eval(extractBlock('const TEST_ACTION_KINDS = [', 'let _testScripts').replace(/^const /gm, 'var '));
|
||||
eval(extractFunc(html, 'testTemplate'));
|
||||
eval(extractFunc(html, 'testMenuSelectProblem'));
|
||||
eval(extractFunc(html, 'testScriptProblem'));
|
||||
eval(extractFunc(html, 'testCheckSummary'));
|
||||
eval(extractFunc(html, 'testStepSummary'));
|
||||
@@ -85,6 +86,13 @@ test('testScriptProblem accepts good scripts and names bad ones', () => {
|
||||
steps: [{ type: 'action', kind: 'scp80', params: { apdu: '' } }] }), /SCP80/);
|
||||
assert.match(testScriptProblem({
|
||||
steps: [{ type: 'action', kind: 'menu-select', params: { item_id: 0 } }] }), /item id/);
|
||||
// the item can be selected by its text instead of the install-dependent id
|
||||
assert.strictEqual(testScriptProblem({ steps: [{ type: 'action', kind: 'menu-select',
|
||||
params: { text: 'My menu', mode: 'contains' } }] }), '');
|
||||
assert.match(testScriptProblem({ steps: [{ type: 'action', kind: 'menu-select',
|
||||
params: {} }] }), /item id or text/);
|
||||
assert.match(testScriptProblem({ steps: [{ type: 'action', kind: 'menu-select',
|
||||
params: { item_id: 1, text: 'My menu' } }] }), /not both/);
|
||||
assert.match(testScriptProblem({
|
||||
steps: [{ type: 'action', kind: 'file-read', params: {} }] }), /file path/);
|
||||
assert.match(testScriptProblem({ steps: [{ type: 'expect' }] }), /command is required/);
|
||||
@@ -102,6 +110,10 @@ test('testStepSummary renders actions', () => {
|
||||
testStepSummary({ type: 'action', kind: 'menu-select', params: { item_id: 3 },
|
||||
check: { sw: { mode: 'mask', value: '91??' } } }),
|
||||
'ENVELOPE(Menu Selection) item=3 · SW ~91??');
|
||||
assert.strictEqual(
|
||||
testStepSummary({ type: 'action', kind: 'menu-select',
|
||||
params: { text: 'My menu', mode: 'exact' } }),
|
||||
'ENVELOPE(Menu Selection) text ="My menu"');
|
||||
assert.strictEqual(
|
||||
testStepSummary({ type: 'action', kind: 'apdu', params: { apdu: '00A4' } }),
|
||||
'APDU 00A4');
|
||||
@@ -172,6 +184,23 @@ test('the SCP80 source switch sticks and preserves the other value', () => {
|
||||
assert.strictEqual(_testEditStep.params.sp, 'AABBCC');
|
||||
});
|
||||
|
||||
test('the menu-select editor collects the item id or the text', () => {
|
||||
// by text (the id varies with the applet's install parameters)
|
||||
_testEditStep = { type: 'action', kind: 'menu-select', params: {} };
|
||||
_testEditChecks = [];
|
||||
_testEditStepIndex = 0;
|
||||
fakeForm({ 'test-step-kind': 'menu-select', 'test-f-item': '', 'test-f-itemtext': 'My menu',
|
||||
'test-f-itemmode': 'contains', 'test-f-sw': '', 'test-f-cdata': '', 'test-f-fail': 'error' });
|
||||
testStepCollect();
|
||||
assert.deepStrictEqual(_testEditStep.params, { text: 'My menu', mode: 'contains' });
|
||||
// by id when no text is given
|
||||
_testEditStep = { type: 'action', kind: 'menu-select', params: {} };
|
||||
fakeForm({ 'test-step-kind': 'menu-select', 'test-f-item': '3', 'test-f-itemtext': '',
|
||||
'test-f-itemmode': 'exact', 'test-f-sw': '', 'test-f-cdata': '', 'test-f-fail': 'error' });
|
||||
testStepCollect();
|
||||
assert.deepStrictEqual(_testEditStep.params, { item_id: 3 });
|
||||
});
|
||||
|
||||
test('the STATUS step leaves the SW check to the server default', () => {
|
||||
_testEditStep = { type: 'action', kind: 'status', params: { attempts: 5, interval_ms: 200 } };
|
||||
fakeForm({ 'test-step-kind': 'status', 'test-f-attempts': '5', 'test-f-interval': '200',
|
||||
|
||||
Reference in New Issue
Block a user