feat: answer GET INKEY / GET INPUT interactively (v3.6.38)
The menu chain paused only for DISPLAY TEXT / SELECT ITEM / SET UP MENU, so a GET INPUT was auto-answered with a bare success TR and no Text string - the live RemMobileID PIN prompt got an empty answer. Server: _parse_get_inkey/_parse_get_input read the prompt, qualifier flags (digits-only, UCS2, Yes/No, immediate, hidden, packed, help), Response length 91 and Default Text 9D; the fetch handler pauses with them; _menu_send_response(..., text=) validates the answer (digits set, min/max, one char for GET INKEY) and builds the Text string TLV 8D in the requested coding (SMS unpacked 04 / UCS2 08 / packed 00 via _pack_gsm7), Yes/No as 01/00, empty input as the null text string, help as result 13 and the variable-timeout Duration echoed (6.8.4). /api/menu-respond takes text; /api/stk-status carries pending_data so a card-initiated prompt opens the panel; _decode_cmd decodes both commands. PWA: stkInputHtml/stkInputHint/stkInputValidate render the constrained panel (password field for hidden entry, numeric keyboard, default text, Yes/No buttons, help, immediate-digit auto-submit); OK submits through stkMenuOk; the poll opens the panel for card-initiated prompts. sw cache simple-v311; tests: 517 Python / 674 Node.
This commit is contained in:
@@ -26,6 +26,9 @@ code += extractFunc(html, 'stkNoPendingError') + '\n';
|
||||
code += extractFunc(html, 'stkMenuPanelReset') + '\n';
|
||||
code += extractFunc(html, 'stkMenuNaiSuffix') + '\n';
|
||||
code += extractFunc(html, 'stkMenuItemsHtml') + '\n';
|
||||
code += extractFunc(html, 'stkInputHtml') + '\n';
|
||||
code += extractFunc(html, 'stkInputHint') + '\n';
|
||||
code += extractFunc(html, 'stkInputValidate') + '\n';
|
||||
code += 'globalThis.esc = s => s;\n';
|
||||
eval(code);
|
||||
|
||||
@@ -132,3 +135,49 @@ test('an already-answered respond does not reach the dialog handler', async () =
|
||||
assert.strictEqual(calls.handled, null);
|
||||
assert.strictEqual(calls.rendered, 0);
|
||||
});
|
||||
|
||||
test('stkInputHtml builds the card-driven input field', () => {
|
||||
globalThis.t = s => s;
|
||||
const out = stkInputHtml({ type: 'get_input', text: 'PIN', min: 4, max: 16,
|
||||
digits_only: true, hidden: true });
|
||||
assert.ok(out.includes('PIN'), out);
|
||||
assert.ok(out.includes('type="password"'), out); // hidden entry
|
||||
assert.ok(out.includes('inputmode="numeric"'), out); // digits only
|
||||
assert.ok(out.includes('maxlength="16"'), out);
|
||||
assert.ok(out.includes('4\u201316 characters'), out);
|
||||
assert.ok(out.includes('hidden entry'), out);
|
||||
// UCS2 alphabet entry: a plain text field without the numeric keyboard
|
||||
const ucs2 = stkInputHtml({ type: 'get_input', text: 'Name', min: 0, max: 10, ucs2: true });
|
||||
assert.ok(ucs2.includes('type="text"'), ucs2);
|
||||
assert.ok(!ucs2.includes('inputmode'), ucs2);
|
||||
assert.ok(ucs2.includes('up to 10 characters'), ucs2);
|
||||
// 0xFF = no maximum (TS 102 223 8.11): no maxlength and no range hint
|
||||
const noMax = stkInputHtml({ type: 'get_input', text: 'x', min: 0, max: 255 });
|
||||
assert.ok(!noMax.includes('maxlength'), noMax);
|
||||
assert.ok(!noMax.includes('characters'), noMax);
|
||||
// Yes/No: buttons instead of a field
|
||||
const yesNo = stkInputHtml({ type: 'get_inkey', text: 'Continue?', yes_no: true });
|
||||
assert.ok(yesNo.includes("stkInputSubmit('01')"), yesNo);
|
||||
assert.ok(yesNo.includes("stkInputSubmit('00')"), yesNo);
|
||||
assert.ok(!yesNo.includes('stk-input-field'), yesNo);
|
||||
// the card's default text is prefilled; help adds the help action
|
||||
const def = stkInputHtml({ type: 'get_input', text: 'x', default: '1234', help: true });
|
||||
assert.ok(def.includes('value="1234"'), def);
|
||||
assert.ok(def.includes("stkMenuRespond('help')"), def);
|
||||
});
|
||||
|
||||
test('stkInputValidate mirrors the server input rules', () => {
|
||||
globalThis.t = s => s;
|
||||
const pd = { type: 'get_input', min: 4, max: 16, digits_only: true, hidden: true };
|
||||
assert.strictEqual(stkInputValidate(pd, '1234').ok, true);
|
||||
assert.strictEqual(stkInputValidate(pd, '12').ok, false);
|
||||
assert.strictEqual(stkInputValidate(pd, '12345678901234567').ok, false);
|
||||
assert.strictEqual(stkInputValidate(pd, 'abcd').ok, false);
|
||||
// hidden entry allows only the digits set even without digits_only
|
||||
assert.strictEqual(stkInputValidate({ type: 'get_input', hidden: true }, 'ab').ok, false);
|
||||
// GET INKEY: exactly one character, digits-only honours * # +
|
||||
assert.strictEqual(stkInputValidate({ type: 'get_inkey' }, 'a').ok, true);
|
||||
assert.strictEqual(stkInputValidate({ type: 'get_inkey' }, 'ab').ok, false);
|
||||
assert.strictEqual(stkInputValidate({ type: 'get_inkey', digits_only: true }, '*').ok, true);
|
||||
assert.strictEqual(stkInputValidate({ type: 'get_inkey', digits_only: true }, 'a').ok, false);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user