diff --git a/frontend/index.html b/frontend/index.html index 7fb5d3e..f25ae6e 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -7293,7 +7293,7 @@ function profilerRenderSnapshotList() { html += '' + esc(s.name) + ''; if (s.iccid) html += '' + esc(s.iccid) + ''; html += '' + esc(new Date(s.created).toLocaleString()) + ''; - html += '(' + s.files.length + ' ' + esc(t('files')) + ')'; + html += '(' + esc(profilerSnapshotCountLabel(s)) + ')'; html += ''; html += ''; html += ''; @@ -7623,6 +7623,14 @@ function profilerFormatMs(ms) { return Math.round(ms) + ' ms'; } +function profilerSnapshotCountLabel(s) { + let label = (s.files ? s.files.length : 0) + ' ' + t('files'); + if (s.timing && s.timing.total_ms !== undefined) { + label += ', ' + t('scanned in') + ' ' + (s.timing.total_ms / 1000).toFixed(2) + ' ' + t('sec'); + } + return label; +} + async function profilerBuildSnapshotFile(path, c, timing) { let sel; try { @@ -8951,6 +8959,8 @@ const LANG_RU = { 'Files': 'Файлов', 'Records': 'Записей', 'Scan time': 'Время сканирования', + 'scanned in': 'за', + 'sec': 'сек', 'No timing data': 'Нет данных о времени', 'min': 'мин', 'avg': 'сред', diff --git a/frontend/sw.js b/frontend/sw.js index cf53041..1ab3f96 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'otaman-v100'; +const CACHE = 'otaman-v101'; const URLS = [ 'index.html', 'help.html', diff --git a/frontend/tests/profiler.test.js b/frontend/tests/profiler.test.js index 43590ad..c0d7fde 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', 'profilerScanNameKeydown', 'profilerTimingStats', 'profilerTimingAccumulator', 'profilerFormatMs', 'profilerRenderSnapshotSummary']; +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', 'profilerTimingStats', 'profilerTimingAccumulator', 'profilerFormatMs', 'profilerRenderSnapshotSummary', 'profilerSnapshotCountLabel']; let code = ''; for (const f of FNS) code += extractFunc(html, f) + '\n'; code += extractFunc(html, 'profilerBuildFileRule', true) + '\n'; @@ -1216,3 +1216,14 @@ test('profilerRenderSnapshotSummary notes missing timing data', () => { assert.ok(html.includes('No timing data'), html); delete global.t; }); + +test('profilerSnapshotCountLabel appends the scan time when available', () => { + global.t = s => (s === 'scanned in' ? 'scanned in' : s); + const withTime = profilerSnapshotCountLabel({ files: new Array(95).fill({}), timing: { total_ms: 21320 } }); + assert.strictEqual(withTime, '95 files, scanned in 21.32 sec'); + const noTime = profilerSnapshotCountLabel({ files: new Array(3).fill({}) }); + assert.strictEqual(noTime, '3 files'); + const empty = profilerSnapshotCountLabel({ files: [], timing: {} }); + assert.strictEqual(empty, '0 files'); + delete global.t; +});