diff --git a/README.md b/README.md index d7e6752..b402bee 100644 --- a/README.md +++ b/README.md @@ -514,7 +514,7 @@ A filesystem rule is defined by: - **File attributes** — file type, size, record length and record count, taken from the FCP template (any may be left unset). - **Check contents** (optional) — **Exact** hex equality, or **Mask** where `?` is a per-nibble wildcard (a mask with no `?` is a prefix match, e.g. `0891` for the IMSI MCC/MNC). Record files store a per-record list. -The check report marks each verified aspect (e.g. *filetype ✓, size ✗, contents ✓*), lists mismatches as read-only monospace expected/actual fields aligned in one column, and shows a decoded per-parameter FCI comparison for FCI mismatches. Corrupt FCI data shows whatever decoded before the faulty part plus an explicit decode-failure note; record mismatches list the *matching records*. In the report the mismatch fields and FCI comparison columns are labelled `expected (profile name)` and `actual (card ICCID)` for a live check, or `actual (snapshot name)` for a snapshot check; the results title shows `profile — ICCID` / `profile — snapshot`. **Only mismatches** in the results header hides all passing files and keeps failures and errors only. +The check report marks each verified aspect (e.g. *filetype ✓, size ✗, contents ✓*), lists mismatches as read-only monospace expected/actual fields aligned in one column, and shows a decoded per-parameter FCI comparison for FCI mismatches. Corrupt FCI data shows whatever decoded before the faulty part plus an explicit decode-failure note; record mismatches list the *matching records*. In the report the mismatch fields and FCI comparison columns are labelled `expected (profile name)` and `actual (card ICCID)` for a live check, or `actual (snapshot name)` for a snapshot check; the results header reads `Profile verification results for: ` (or `… → `; snapshot comparison: `Snapshot comparison results: `). **Only mismatches** in the results header hides all passing files and keeps failures and errors only. #### “Profile from card” scan options diff --git a/README_RUS.md b/README_RUS.md index beccf83..d660cc1 100644 --- a/README_RUS.md +++ b/README_RUS.md @@ -488,7 +488,7 @@ Delivery PoR (SPI2 `01`) проще — карта возвращает PoR на - **Атрибуты файла** — тип, размер, длина и число записей из шаблона FCP (любое можно не задавать). - **Проверка содержимого** (опционально) — **Exact** (точное равенство hex) или **Mask**, где `?` — пониббловый джокер (маска без `?` — префиксное совпадение, напр. `0891` для MCC/MNC IMSI). Для record-файлов хранится список записей. -Отчёт проверки помечает каждый аспект (напр. *тип файла ✓, размер ✗, содержимое ✓*), показывает расхождения как поля только для чтения (ожидаемое/фактическое в одной колонке) и декодированное сравнение параметров FCI для расхождений FCI. Повреждённые FCI показывают всё, что удалось декодировать, плюс явное сообщение об ошибке; для записей указываются *совпадающие записи*. В отчёте поля расхождений и колонки сравнения FCI подписаны `ожидалось (имя профиля)` и `фактически (ICCID карты)` для проверки карты либо `фактически (имя снимка)` для проверки снимка; в заголовке отчёта — `профиль — ICCID` / `профиль — снимок`. Опция **«Только расхождения»** скрывает все совпавшие файлы, оставляя несовпадения и ошибки. +Отчёт проверки помечает каждый аспект (напр. *тип файла ✓, размер ✗, содержимое ✓*), показывает расхождения как поля только для чтения (ожидаемое/фактическое в одной колонке) и декодированное сравнение параметров FCI для расхождений FCI. Повреждённые FCI показывают всё, что удалось декодировать, плюс явное сообщение об ошибке; для записей указываются *совпадающие записи*. В отчёте поля расхождений и колонки сравнения FCI подписаны `ожидалось (имя профиля)` и `фактически (ICCID карты)` для проверки карты либо `фактически (имя снимка)` для проверки снимка; в заголовке отчёта — `Результаты проверки профиля: <профиль> → ` (или `… → <имя снимка>`; для сравнения снимков — `Результаты сравнения снимков: <эталон> → <проверяемый>`). Опция **«Только расхождения»** скрывает все совпавшие файлы, оставляя несовпадения и ошибки. #### Опции сканирования «Профиль с карты» diff --git a/frontend/help-ru.html b/frontend/help-ru.html index 3e717d1..02a3471 100644 --- a/frontend/help-ru.html +++ b/frontend/help-ru.html @@ -387,7 +387,7 @@
  • Атрибуты файла — тип файла, размер, длина записи и число записей из FCP-шаблона (любой можно оставить незаданным).
  • Проверить содержимое (опционально) — Точное (точное совпадение hex) или Маска, где ? — шаблон на один полубайт (маска без ? — совпадение префикса, например 0891 для MCC/MNC из IMSI). Для record-файлов хранится список по записям.
  • -

    Проверить карту выполняет каждое правило на подключённой карте и показывает строку прогресса и отчёт прохождения. Рядом с путём файла указывается, что именно проверялось (например, тип файла и размер, содержимое или полный FCI); если часть проверок прошла, а часть нет — каждый аспект помечается (тип файла ✓, размер ✗, содержимое ✓), а расхождения расписываются ниже. Несовпавшие сырые данные (FCI, содержимое, данные записей) показываются как поля только для чтения с моноширинным шрифтом — ожидаемое над фактическим, в одной и той же колонке — для удобного сравнения; для расхождений FCI дополнительно показывается декодированное сравнение по параметрам (размер файла, дескриптор/структура, жизненный цикл, FID, SFI, проприетарные параметры…). Декодированный просмотр FCI также отображается рядом с полем FCI hex при редактировании правила. Если данные FCI повреждены, показывается всё, что удалось декодировать до места ошибки, вместе с явным сообщением об ошибке декодирования. Для record-файлов при расхождении содержимого добавляется пометка совпадающие записи: 1-5, 7-10 со списком записей, которые совпали. В отчёте поля расхождений и колонки сравнения FCI подписаны ожидалось (имя профиля) и фактически (ICCID карты), а в заголовке отчёта показываются имя профиля и ICCID карты. Опция «Только расхождения» в заголовке отчёта скрывает все совпавшие файлы и оставляет только несовпадения и ошибки.

    +

    Проверить карту выполняет каждое правило на подключённой карте и показывает строку прогресса и отчёт прохождения. Рядом с путём файла указывается, что именно проверялось (например, тип файла и размер, содержимое или полный FCI); если часть проверок прошла, а часть нет — каждый аспект помечается (тип файла ✓, размер ✗, содержимое ✓), а расхождения расписываются ниже. Несовпавшие сырые данные (FCI, содержимое, данные записей) показываются как поля только для чтения с моноширинным шрифтом — ожидаемое над фактическим, в одной и той же колонке — для удобного сравнения; для расхождений FCI дополнительно показывается декодированное сравнение по параметрам (размер файла, дескриптор/структура, жизненный цикл, FID, SFI, проприетарные параметры…). Декодированный просмотр FCI также отображается рядом с полем FCI hex при редактировании правила. Если данные FCI повреждены, показывается всё, что удалось декодировать до места ошибки, вместе с явным сообщением об ошибке декодирования. Для record-файлов при расхождении содержимого добавляется пометка совпадающие записи: 1-5, 7-10 со списком записей, которые совпали. В отчёте поля расхождений и колонки сравнения FCI подписаны ожидалось (имя профиля) и фактически (ICCID карты), а в заголовке отчёта выводится Результаты проверки профиля: <профиль> → <ICCID карты>. Опция «Только расхождения» в заголовке отчёта скрывает все совпавшие файлы и оставляет только несовпадения и ошибки.

    Опции сканирования «Профиль с карты»

    Диалог сканирования запрашивает имя профиля и предлагает селектор «Проверка FCP/FCI» (те же три режима, по умолчанию Тип файла + размер (FCP)), применяемый ко всем создаваемым правилам, а также список «Игнорировать содержимое файлов» (все отмечены по умолчанию, кроме EF.ARR; флажок в заголовке отмечает или снимает весь список) часто перезаписываемых файлов, содержимое которых пропускается: EF.LOCI, EF.PSLOCI, EF.EPSLOCI, EF.5GS3GPPLOCI, EF.Keys, EF.KeysPS, EF.SMS, EF.Kc, EF.KcGPRS, EF.LOCIGPRS, EF.CBMID, EF.SMSS, EF.ACC, EF.EPSNSC, EF.START-HFN, EF.ARR. Ещё две отмеченные по умолчанию опции «Сравнивать первые 4 байта для» EF.IMSI и EF.ICCID захватывают содержимое этих файлов как маску только первых 4 байт (снимите для точного сравнения). Строка прогресса показывает N / всего файлов с текущим путём файла во время сканирования; при сканировании опции скрываются, а кнопки блокируются. Правила создаются только для файлов, которые реально существуют на карте (возвращён FCP-шаблон); отсутствующие файлы пропускаются. Пользовательские файлы из подвкладки «Пользовательские файлы» включаются с той же проверкой существования.

    @@ -397,8 +397,8 @@
  • Новый снимок — запрашивает имя и сканирует карту, затем возвращает к списку.
  • Импорт снимка — загружает снимок из JSON-файла.
  • В каждой строке снимка — Открыть, Экспорт и Удалить. Открыть показывает все захваченные данные только для чтения (сырой FCI с декодированным FCI, содержимое); редактируется только имя снимка.
  • -
  • Проверить снимок карты в строке профиля выполняет правила профиля на выбранном из списка снимке, без картридера. Отчёт такой же, как при проверке карты, но фактическая сторона подписана именем снимка (фактически (имя снимка)); файлы, содержимое которых не было захвачено при сканировании, помечаются как непроверяемые ошибки.
  • -
  • Сравнить снимки сравнивает два снимка без картридера так же, как проверка профиля: выберите эталонный снимок и снимок для проверки, при необходимости включите маску первых 4 байт EF.IMSI/EF.ICCID (включена по умолчанию) и получите такой же отчёт; в этом отчёте поля расхождений и колонки сравнения FCI подписаны именами эталонного и проверяемого снимков вместо expected/actual. Файлы, которые есть только в проверяемом снимке, помечаются как лишние. «К списку» возвращает на вкладку «Снимки карт».
  • +
  • Проверить снимок карты в строке профиля выполняет правила профиля на выбранном из списка снимке, без картридера. Отчёт такой же, как при проверке карты: фактическая сторона подписана именем снимка (фактически (имя снимка)), а в заголовке — Результаты проверки профиля: <профиль> → <имя снимка>; файлы, содержимое которых не было захвачено при сканировании, помечаются как непроверяемые ошибки.
  • +
  • Сравнить снимки сравнивает два снимка без картридера так же, как проверка профиля: выберите эталонный снимок и снимок для проверки, при необходимости включите маску первых 4 байт EF.IMSI/EF.ICCID (включена по умолчанию) и получите такой же отчёт; в этом отчёте заголовок — Результаты сравнения снимков: <эталон> → <проверяемый>, а поля расхождений и колонки сравнения FCI подписаны именами эталонного и проверяемого снимков вместо expected/actual. Файлы, которые есть только в проверяемом снимке, помечаются как лишние. «К списку» возвращает на вкладку «Снимки карт».
  • diff --git a/frontend/help.html b/frontend/help.html index f119f8e..383134d 100644 --- a/frontend/help.html +++ b/frontend/help.html @@ -387,7 +387,7 @@
  • File attributes — file type, size, record length and record count, taken from the FCP template (any may be left unset).
  • Check contents (optional) — Exact hex equality, or Mask where ? is a per-nibble wildcard (a mask with no ? is a prefix match, e.g. 0891 for the IMSI MCC/MNC). Record files store a per-record list.
  • -

    Check card runs every rule against the equipped card and shows a live progress line plus a pass/fail report. Each row states exactly what was verified next to the file path (e.g. filetype and size, contents or exact FCI); when some checks pass and others fail, each aspect is marked (filetype ✓, size ✗, contents ✓) with the mismatches detailed below. Mismatched raw data (FCI, contents, record data) is shown as read-only monospace fields — expected above actual, aligned in the same column — for easy comparison; FCI mismatches additionally show a decoded per-parameter comparison (file size, file descriptor/structure, life cycle, FID, SFI, proprietary parameters…). A decoded FCI preview is also shown beside the FCI hex field while editing a rule. If the FCI data is corrupt, whatever was decoded before the faulty part is shown together with an explicit decode-failure note. For record files with a contents mismatch, a matching records: 1-5, 7-10 note lists the records that did match. In the report the mismatch fields and FCI comparison columns are labelled expected (profile name) and actual (card ICCID), and the results title shows the profile name and the card ICCID. An Only mismatches option in the results header hides all passing files and keeps only failures and errors.

    +

    Check card runs every rule against the equipped card and shows a live progress line plus a pass/fail report. Each row states exactly what was verified next to the file path (e.g. filetype and size, contents or exact FCI); when some checks pass and others fail, each aspect is marked (filetype ✓, size ✗, contents ✓) with the mismatches detailed below. Mismatched raw data (FCI, contents, record data) is shown as read-only monospace fields — expected above actual, aligned in the same column — for easy comparison; FCI mismatches additionally show a decoded per-parameter comparison (file size, file descriptor/structure, life cycle, FID, SFI, proprietary parameters…). A decoded FCI preview is also shown beside the FCI hex field while editing a rule. If the FCI data is corrupt, whatever was decoded before the faulty part is shown together with an explicit decode-failure note. For record files with a contents mismatch, a matching records: 1-5, 7-10 note lists the records that did match. In the report the mismatch fields and FCI comparison columns are labelled expected (profile name) and actual (card ICCID), and the results header reads Profile verification results for: <profile> → <card ICCID>. An Only mismatches option in the results header hides all passing files and keeps only failures and errors.

    “Profile from card” scan options

    The scan dialog asks for a profile name and offers a “FCP/FCI check” selector (the same three modes above, default Filetype + size) applied to every generated rule, plus an “Ignore contents of files” checklist (all checked by default except EF.ARR; the header checkbox checks or unchecks the whole list) of frequently-overwritten files whose contents are skipped: EF.LOCI, EF.PSLOCI, EF.EPSLOCI, EF.5GS3GPPLOCI, EF.Keys, EF.KeysPS, EF.SMS, EF.Kc, EF.KcGPRS, EF.LOCIGPRS, EF.CBMID, EF.SMSS, EF.ACC, EF.EPSNSC, EF.START-HFN, EF.ARR. Two further checked-by-default options “Match first 4 bytes for” EF.IMSI and EF.ICCID capture those files’ contents as a mask of only the first 4 bytes (uncheck for exact matching). A progress line shows N / total files with the current file path while scanning; during the scan the options are hidden and the buttons are locked. Rules are created only for files that actually exist on the card (a FCP template is returned); missing files are skipped. Custom files from the Custom files sub-tab are included under the same existence check.

    @@ -397,8 +397,8 @@
  • New snapshot — asks for a name and scans the card, then returns to the list.
  • Import snapshot — loads a snapshot from a JSON file.
  • Each snapshot row has Open, Export, and Delete. Open shows all captured data read-only (raw FCI with the decoded FCI, contents); only the snapshot name is editable.
  • -
  • Check card snapshot on a profile row runs the profile rules against a snapshot you pick from the list, without a card reader. The report is the same as a live check, but the actual side is labelled with the snapshot name (actual (snapshot name)); files whose contents were not captured during the scan are reported as unverifiable errors.
  • -
  • Compare snapshots compares two snapshots offline, exactly like a profile check: pick the master snapshot and the snapshot to check, optionally masking the first 4 bytes of EF.IMSI/EF.ICCID (on by default), and get the same pass/fail report; in that report the mismatch fields and the FCI comparison columns are labeled with the master and checked snapshot names instead of expected/actual. Files present only in the checked snapshot are reported as extra files. Back to list returns to the Card snapshots tab.
  • +
  • Check card snapshot on a profile row runs the profile rules against a snapshot you pick from the list, without a card reader. The report is the same as a live check: the actual side is labelled with the snapshot name (actual (snapshot name)) and the header reads Profile verification results for: <profile> → <snapshot name>; files whose contents were not captured during the scan are reported as unverifiable errors.
  • +
  • Compare snapshots compares two snapshots offline, exactly like a profile check: pick the master snapshot and the snapshot to check, optionally masking the first 4 bytes of EF.IMSI/EF.ICCID (on by default), and get the same pass/fail report; in that report the header reads Snapshot comparison results: <master> → <checked> and the mismatch fields and the FCI comparison columns are labeled with the master and checked snapshot names instead of expected/actual. Files present only in the checked snapshot are reported as extra files. Back to list returns to the Card snapshots tab.
  • diff --git a/frontend/index.html b/frontend/index.html index d222e70..2b442f7 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -7404,6 +7404,7 @@ let profilerEditId = null; let profilerDraft = null; let profilerResults = null; let profilerResultsLabels = null; +let profilerResultsHeader = null; let profilerMismatchOnly = false; let snapshots = []; let snapshotViewId = null; @@ -8440,13 +8441,13 @@ async function profilerCardIccid() { async function profilerCheck(i) { const p = profiles[i]; const iccid = await profilerCardIccid(); - await profilerRunProfile(p, null, iccid ? p.name + ' — ' + iccid : p.name, null, + await profilerRunProfile(p, null, { kind: 'profile', from: p.name, to: iccid }, null, { expected: p.name, actual: iccid, prefix: true }); } -async function profilerRunProfile(p, source, title, extraResults, labels) { +async function profilerRunProfile(p, source, header, extraResults, labels) { profilerSetView('results'); - document.getElementById('profiler-results-title').textContent = title; + profilerResultsHeader = header || null; const prog = document.getElementById('profiler-progress'); document.getElementById('profiler-summary').innerHTML = ''; document.getElementById('profiler-report').innerHTML = ''; @@ -8483,7 +8484,7 @@ async function profilerCheckSnapshot(i, si) { document.getElementById('snapshot-pick-modal').classList.add('hidden'); const p = profiles[i]; const s = snapshots[si]; - await profilerRunProfile(p, profilerSnapshotSource(s), p.name + ' — ' + s.name, null, + await profilerRunProfile(p, profilerSnapshotSource(s), { kind: 'profile', from: p.name, to: s.name }, null, { expected: p.name, actual: s.name, prefix: true }); } @@ -8571,7 +8572,7 @@ async function snapshotCompareRun() { }); document.getElementById('snapshot-compare-modal').classList.add('hidden'); const pseudo = { name: master.name + ' → ' + check.name, rules: profilerRulesFromSnapshot(master, maskFids) }; - await profilerRunProfile(pseudo, profilerSnapshotSource(check), pseudo.name, profilerExtraFileResults(master, check), + await profilerRunProfile(pseudo, profilerSnapshotSource(check), { kind: 'snapshot', from: master.name, to: check.name }, profilerExtraFileResults(master, check), { expected: master.name, actual: check.name }); } @@ -8584,8 +8585,15 @@ function profilerVisibleResults(results, mismatchOnly) { return mismatchOnly ? results.filter(r => r.status !== 'pass') : results; } +function profilerResultsHeaderText(header) { + if (!header || !header.from) return ''; + const base = header.kind === 'snapshot' ? t('Snapshot comparison results:') : t('Profile verification results for:'); + return base + ' ' + header.from + (header.to ? ' \u2192 ' + header.to : ''); +} + function profilerRenderResultsView() { if (!profilerResults) return; + document.getElementById('profiler-results-title').textContent = profilerResultsHeaderText(profilerResultsHeader); let passed = 0, failed = 0, errors = 0; for (const res of profilerResults) { if (res.status === 'pass') passed++; @@ -9276,6 +9284,8 @@ const LANG_RU = { 'Step': 'Шаг', 'Install OK': 'Установка OK', 'Install FAILED at step:': 'Установка не удалась на шаге:', + 'Profile verification results for:': 'Результаты проверки профиля:', + 'Snapshot comparison results:': 'Результаты сравнения снимков:', 'RAM operations perform atomic GlobalPlatform commands over SCP80. Card keys are taken from the saved preset.': 'RAM-операции выполняют атомарные команды GlobalPlatform через SCP80. Ключи карты берутся из сохранённого пресета.', 'No cards defined.': 'Карты не заданы.', 'Card presets': 'Выбор карты', diff --git a/frontend/sw.js b/frontend/sw.js index 8392a6a..1928775 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'otaman-v129'; +const CACHE = 'otaman-v130'; const URLS = [ 'index.html', 'help.html', diff --git a/frontend/tests/profiler.test.js b/frontend/tests/profiler.test.js index 3a9a8e7..bf6f1ee 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', 'profilerSnapshotCountLabel', 'pysimFsInfoHtml', 'profilerLabelText']; +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', 'pysimFsInfoHtml', 'profilerLabelText', 'profilerResultsHeaderText', 'profilerRenderResultsView']; let code = ''; for (const f of FNS) code += extractFunc(html, f) + '\n'; code += extractFunc(html, 'profilerBuildFileRule', true) + '\n'; @@ -31,7 +31,7 @@ code += extractFunc(html, 'profilerBuildSnapshotFile', true) + '\n'; code += extractFunc(html, 'profilerCardIccid', true) + '\n'; code += extractFunc(html, 'profilerCheck', true) + '\n'; code += extractFunc(html, 'profilerCheckSnapshot', true) + '\n'; -code += "var _scanTarget = 'profile';\n"; +code += "var _scanTarget = 'profile';\nvar profilerResults = null;\nvar profilerResultsHeader = null;\nvar profilerMismatchOnly = false;\n"; code += html.match(/const PROFILER_MASK_PREFIX4_FIDS = \{[\s\S]*?\n\};/)[0] + '\n'; eval(code); @@ -1359,7 +1359,7 @@ test('profilerCheck titles with the card ICCID and passes prefixed labels', asyn let captured = null; global.profilerRunProfile = async (...args) => { captured = args; }; await profilerCheck(0); - assert.strictEqual(captured[2], 'MyProfile — 89012345678901234567'); + assert.deepStrictEqual(captured[2], { kind: 'profile', from: 'MyProfile', to: '89012345678901234567' }); assert.deepStrictEqual(captured[4], { expected: 'MyProfile', actual: '89012345678901234567', prefix: true }); delete global.profiles; delete global.pysimFetch; delete global.profilerRunProfile; delete global.t; }); @@ -1372,7 +1372,33 @@ test('profilerCheckSnapshot passes the snapshot name as the actual label', async let captured = null; global.profilerRunProfile = async (...args) => { captured = args; }; await profilerCheckSnapshot(0, 0); - assert.strictEqual(captured[2], 'Prof — SnapX'); + assert.deepStrictEqual(captured[2], { kind: 'profile', from: 'Prof', to: 'SnapX' }); assert.deepStrictEqual(captured[4], { expected: 'Prof', actual: 'SnapX', prefix: true }); delete global.profiles; delete global.snapshots; delete global.profilerRunProfile; delete global.t; }); + +test('profilerResultsHeaderText builds profile and snapshot headers', () => { + global.t = s => 't:' + s; + assert.strictEqual(profilerResultsHeaderText({ kind: 'profile', from: 'Prof', to: '8901' }), 't:Profile verification results for: Prof \u2192 8901'); + assert.strictEqual(profilerResultsHeaderText({ kind: 'snapshot', from: 'Snap1', to: 'Snap2' }), 't:Snapshot comparison results: Snap1 \u2192 Snap2'); + assert.strictEqual(profilerResultsHeaderText({ kind: 'profile', from: 'Prof', to: null }), 't:Profile verification results for: Prof'); + assert.strictEqual(profilerResultsHeaderText(null), ''); + delete global.t; +}); + +test('profilerRenderResultsView writes the built header into the title', () => { + global.t = s => s; + profilerResults = []; + profilerResultsHeader = { kind: 'profile', from: 'Prof', to: 'Snap' }; + const els = {}; + for (const id of ['profiler-summary', 'profiler-report', 'profiler-results-title']) els[id] = { innerHTML: '', textContent: '' }; + global.document = { getElementById: id => els[id] || null }; + profilerRenderResultsView(); + assert.strictEqual(els['profiler-results-title'].textContent, 'Profile verification results for: Prof \u2192 Snap'); + profilerResultsHeader = { kind: 'snapshot', from: 'A', to: 'B' }; + profilerRenderResultsView(); + assert.strictEqual(els['profiler-results-title'].textContent, 'Snapshot comparison results: A \u2192 B'); + profilerResults = null; + profilerResultsHeader = null; + delete global.t; +});