From a261675aad2f0fb76a009af506d6bd4887aa5991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD=20=D0=A2=D1=80=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Sat, 12 Sep 2026 14:41:35 +0300 Subject: [PATCH] ui: focus scan name input and start scanning on Enter 'Profile from card' and 'New snapshot' now focus the name field when the dialog opens, and Enter in that field starts the scan (profilerScanNameKeydown, ignored while the Scan button is disabled). Tests for the key handler and the input wiring; SW cache v98 -> v99. --- frontend/index.html | 11 ++++++++++- frontend/sw.js | 2 +- frontend/tests/html.test.js | 4 ++++ frontend/tests/profiler.test.js | 20 +++++++++++++++++++- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 6f9c478..bd31c2a 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -956,7 +956,7 @@

Profile from card

- +
Ignore contents of files: @@ -7376,6 +7376,7 @@ function profilerFromCard() { document.getElementById('profiler-scan-btn').disabled = false; document.getElementById('profiler-scan-btn').textContent = t('Scan'); document.getElementById('profiler-scan-modal').classList.remove('hidden'); + document.getElementById('profiler-scan-name').focus(); } function profilerScanCancel() { @@ -7394,6 +7395,14 @@ function snapshotNew() { document.getElementById('profiler-scan-btn').disabled = false; document.getElementById('profiler-scan-btn').textContent = t('Scan'); document.getElementById('profiler-scan-modal').classList.remove('hidden'); + nameEl.focus(); +} + +function profilerScanNameKeydown(e) { + if (e.key !== 'Enter') return; + if (document.getElementById('profiler-scan-btn').disabled) return; + e.preventDefault(); + profilerScanStart(); } async function profilerScanStart() { diff --git a/frontend/sw.js b/frontend/sw.js index cf808d1..95220b3 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'otaman-v98'; +const CACHE = 'otaman-v99'; const URLS = [ 'index.html', 'help.html', diff --git a/frontend/tests/html.test.js b/frontend/tests/html.test.js index ec908f6..70a2143 100644 --- a/frontend/tests/html.test.js +++ b/frontend/tests/html.test.js @@ -33,3 +33,7 @@ test('phone simulator has Phone / TR Config pills', () => { assert.ok(html.includes('id="phone-sub-phone"')); assert.ok(html.includes('id="phone-sub-tr"')); }); + +test('scan name input starts scanning on Enter', () => { + assert.match(html, /id="profiler-scan-name"[^>]*onkeydown="profilerScanNameKeydown\(event\)"/); +}); diff --git a/frontend/tests/profiler.test.js b/frontend/tests/profiler.test.js index cde6bbc..3857ccb 100644 --- a/frontend/tests/profiler.test.js +++ b/frontend/tests/profiler.test.js @@ -21,7 +21,7 @@ function extractFunc(src, name, asyncFn) { return (asyncFn ? 'async ' : '') + src.slice(m.index, i + 1); } -const FNS = ['profilerNormHex', 'profilerNormHexStrict', 'profilerMatch', 'profilerMatchMin', 'profilerMaskPrefix4', 'profilerFileFields', 'profilerContentKindForFileType', 'profilerEmptyRecordContent', 'profilerValidateProfile', 'profilerCustomNameForPath', 'profilerUpdateRulePath', 'profilerResultAspects', 'profilerAspectSummary', 'profilerNumRanges', 'esc', 'escHtml', 'profilerRawDataCheck', 'profilerRenderReport', 'parseBerLen', 'parseTlvList', 'fcpInt', 'fcpParseTlvs', 'fcpFileDescriptor', 'fcpLifeCycle', 'fcpSfi', 'fcpDo', 'fcpDecode', 'fcpDiffHtml', 'profilerFciPreviewItems', 'profilerUpdateFciPreview', 'profilerUpdateRule', 'profilerFciInput', 'profilerScanToggleAll', 'profilerScanIgnoreAllState', 'swapNibbles', 'decIccid', 'profilerSnapshotIccid', 'profilerValidateSnapshot', 'profilerListSwitch', 'profilerScanRefreshOptions', 'profilerLiveSource', 'profilerSnapshotSource', 'profilerVisibleResults', 'profilerMaskFidForFile', 'profilerRulesFromSnapshot', 'profilerExtraFileResults']; +const FNS = ['profilerNormHex', 'profilerNormHexStrict', 'profilerMatch', 'profilerMatchMin', 'profilerMaskPrefix4', 'profilerFileFields', 'profilerContentKindForFileType', 'profilerEmptyRecordContent', 'profilerValidateProfile', 'profilerCustomNameForPath', 'profilerUpdateRulePath', 'profilerResultAspects', 'profilerAspectSummary', 'profilerNumRanges', 'esc', 'escHtml', 'profilerRawDataCheck', 'profilerRenderReport', 'parseBerLen', 'parseTlvList', 'fcpInt', 'fcpParseTlvs', 'fcpFileDescriptor', 'fcpLifeCycle', 'fcpSfi', 'fcpDo', 'fcpDecode', 'fcpDiffHtml', 'profilerFciPreviewItems', 'profilerUpdateFciPreview', 'profilerUpdateRule', 'profilerFciInput', 'profilerScanToggleAll', 'profilerScanIgnoreAllState', 'swapNibbles', 'decIccid', 'profilerSnapshotIccid', 'profilerValidateSnapshot', 'profilerListSwitch', 'profilerScanRefreshOptions', 'profilerLiveSource', 'profilerSnapshotSource', 'profilerVisibleResults', 'profilerMaskFidForFile', 'profilerRulesFromSnapshot', 'profilerExtraFileResults', 'profilerScanNameKeydown']; let code = ''; for (const f of FNS) code += extractFunc(html, f) + '\n'; code += extractFunc(html, 'profilerBuildFileRule', true) + '\n'; @@ -1112,3 +1112,21 @@ test('profilerExtraFileResults reports files missing from the master', () => { assert.strictEqual(extras[0].status, 'fail'); assert.deepStrictEqual(extras[0].checks, [{ label: 'extra file', expected: 'absent', actual: 'present', ok: false }]); }); + +test('profilerScanNameKeydown starts the scan on Enter only', () => { + const btn = { disabled: false }; + global.document = { getElementById: () => btn }; + let started = 0; + global.profilerScanStart = () => { started++; }; + let prevented = 0; + profilerScanNameKeydown({ key: 'Enter', preventDefault: () => prevented++ }); + profilerScanNameKeydown({ key: 'a', preventDefault: () => prevented++ }); + assert.strictEqual(started, 1); + assert.strictEqual(prevented, 1); + btn.disabled = true; + profilerScanNameKeydown({ key: 'Enter', preventDefault: () => prevented++ }); + assert.strictEqual(started, 1); + assert.strictEqual(prevented, 1); + delete global.document; + delete global.profilerScanStart; +});