v3.25.0 review: the collapsible access-rules block
The access-rules form is large for every file rule, so the block now collapses to its header (a toggle carrying the rule count) and hides entirely when None is selected - the header keeps the title and the None/Check radios, so re-enabling is one click (Check also reopens the block). The expanded state is transient editor UI state (a module map keyed by the rule index), kept across the editor's re-renders; a rule without accessRules starts hidden and Check creates the empty block to build rules in. Tests: collapsed by default (header + count, no rows), expanded rows, None and no-accessRules hidden, the enabled-but-empty hint, and the radio driving the visibility. 882 frontend + 858 Python; sw cache simple-v424.
This commit is contained in:
@@ -466,7 +466,7 @@
|
|||||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||||
<li><strong>Путь</strong> — начинается с <code class="font-mono text-sm">MF</code> (например, <code class="font-mono text-sm">MF/7F10/6F3A</code>) или с AID ADF (например, <code class="font-mono text-sm">A0000000871002/6F07</code>).</li>
|
<li><strong>Путь</strong> — начинается с <code class="font-mono text-sm">MF</code> (например, <code class="font-mono text-sm">MF/7F10/6F3A</code>) или с AID ADF (например, <code class="font-mono text-sm">A0000000871002/6F07</code>).</li>
|
||||||
<li><strong>Проверка FCP/FCI</strong> — какая часть информации об управлении файлом проверяется: <strong>Только тип файла (FCP)</strong> (существование + тип файла), <strong>Тип файла + размер (FCP)</strong> (добавляются размер файла либо длина/число записей для record-файлов) или <strong>Полный FCI</strong> (добавляется побайтовое сравнение сырого ответа SELECT — шаблона FCP <code class="font-mono text-sm">'62'</code> — выявляет изменения FID/AID, жизненного цикла, атрибутов безопасности и проприетарных параметров).</li>
|
<li><strong>Проверка FCP/FCI</strong> — какая часть информации об управлении файлом проверяется: <strong>Только тип файла (FCP)</strong> (существование + тип файла), <strong>Тип файла + размер (FCP)</strong> (добавляются размер файла либо длина/число записей для record-файлов) или <strong>Полный FCI</strong> (добавляется побайтовое сравнение сырого ответа SELECT — шаблона FCP <code class="font-mono text-sm">'62'</code> — выявляет изменения FID/AID, жизненного цикла, атрибутов безопасности и проприетарных параметров).</li>
|
||||||
<li><strong>Правила доступа</strong> — проверяет условия доступа файла как <em>декодированные правила</em>, независимо от того, на какую запись <code class="font-mono text-sm">EF.ARR</code> указывает ссылка атрибута безопасности (<code class="font-mono text-sm">'8B'</code>) в FCI: правила указанной записи разрешаются из <code class="font-mono text-sm">EF.ARR</code> карты (или снимка) и сравниваются как множество с захваченными строками. Захватываются автоматически при создании профиля с карты или из снимка для каждого файла, чей FCI содержит ссылку (собственное содержимое EF.ARR по умолчанию пропускается — значимая проверка это правила); в редакторе захваченные правила показываются структурированными строками — операция в виде флажков битов (или командной формы RESIZE FILE / INCREASE), а каждое условие безопасности — селектором (always, never или ссылка на ключ с её использованием), с предпросмотром канонической декодированной строки, которую сравнивает проверка; «+ правило» создаёт новое (READ always), а шаблонные формы вне этого набора остаются нередактируемыми строками с возможностью удаления. Переключатель «Нет/Проверять» отключает проверку, не теряя правила. Файл, чей FCI потерял ссылку, не проходит проверку; ссылка, которую не удалось разрешить (нет кэша <code class="font-mono text-sm">EF.ARR</code>, запись вне диапазона), помечается как непроверяемая. При <strong>Полном FCI</strong> необязательный флажок <em>«Игнорировать ссылку на EF.ARR в точном FCI»</em> сравнивает FCI без значения <code class="font-mono text-sm">'8B'</code>: другой номер записи больше не приводит к несовпадению, а все остальные DO по-прежнему сравниваются побайтово (смысл ссылки проверяет эта проверка).</li>
|
<li><strong>Правила доступа</strong> — проверяет условия доступа файла как <em>декодированные правила</em>, независимо от того, на какую запись <code class="font-mono text-sm">EF.ARR</code> указывает ссылка атрибута безопасности (<code class="font-mono text-sm">'8B'</code>) в FCI: правила указанной записи разрешаются из <code class="font-mono text-sm">EF.ARR</code> карты (или снимка) и сравниваются как множество с захваченными строками. Захватываются автоматически при создании профиля с карты или из снимка для каждого файла, чей FCI содержит ссылку (собственное содержимое EF.ARR по умолчанию пропускается — значимая проверка это правила); в редакторе захваченные правила показываются структурированными строками — операция в виде флажков битов (или командной формы RESIZE FILE / INCREASE), а каждое условие безопасности — селектором (always, never или ссылка на ключ с её использованием), с предпросмотром канонической декодированной строки, которую сравнивает проверка; «+ правило» создаёт новое (READ always), а шаблонные формы вне этого набора остаются нередактируемыми строками с возможностью удаления. Переключатель «Нет/Проверять» отключает проверку, не теряя правила; блок сворачивается в заголовок со счётчиком правил, а «Нет» скрывает его полностью. Файл, чей FCI потерял ссылку, не проходит проверку; ссылка, которую не удалось разрешить (нет кэша <code class="font-mono text-sm">EF.ARR</code>, запись вне диапазона), помечается как непроверяемая. При <strong>Полном FCI</strong> необязательный флажок <em>«Игнорировать ссылку на EF.ARR в точном FCI»</em> сравнивает FCI без значения <code class="font-mono text-sm">'8B'</code>: другой номер записи больше не приводит к несовпадению, а все остальные DO по-прежнему сравниваются побайтово (смысл ссылки проверяет эта проверка).</li>
|
||||||
<li><strong>Атрибуты файла</strong> — тип файла, размер, длина записи и число записей из FCP-шаблона (любой можно оставить незаданным).</li>
|
<li><strong>Атрибуты файла</strong> — тип файла, размер, длина записи и число записей из FCP-шаблона (любой можно оставить незаданным).</li>
|
||||||
<li><strong>Проверить содержимое</strong> (опционально) — <strong>Точное</strong> (точное совпадение hex) или <strong>Маска</strong>, где <code class="font-mono text-sm">?</code> — шаблон на один полубайт (маска без <code class="font-mono text-sm">?</code> — совпадение префикса, например <code class="font-mono text-sm">0891</code> для MCC/MNC из IMSI). Для record-файлов хранится список по записям.</li>
|
<li><strong>Проверить содержимое</strong> (опционально) — <strong>Точное</strong> (точное совпадение hex) или <strong>Маска</strong>, где <code class="font-mono text-sm">?</code> — шаблон на один полубайт (маска без <code class="font-mono text-sm">?</code> — совпадение префикса, например <code class="font-mono text-sm">0891</code> для MCC/MNC из IMSI). Для record-файлов хранится список по записям.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
+1
-1
@@ -465,7 +465,7 @@
|
|||||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||||
<li><strong>Path</strong> — starts with <code class="font-mono text-sm">MF</code> (e.g. <code class="font-mono text-sm">MF/7F10/6F3A</code>) or an ADF AID (e.g. <code class="font-mono text-sm">A0000000871002/6F07</code>).</li>
|
<li><strong>Path</strong> — starts with <code class="font-mono text-sm">MF</code> (e.g. <code class="font-mono text-sm">MF/7F10/6F3A</code>) or an ADF AID (e.g. <code class="font-mono text-sm">A0000000871002/6F07</code>).</li>
|
||||||
<li><strong>FCP/FCI check</strong> — how much of the file control information to verify: <strong>Filetype only (FCP)</strong> (existence + file type), <strong>Filetype + size (FCP)</strong> (adds file size, or record length/count for record files), or <strong>Exact FCI</strong> (adds a byte-for-byte comparison of the raw SELECT response — the FCP template <code class="font-mono text-sm">'62'</code> — catching FID/AID, life-cycle status, security-attribute and proprietary-parameter changes).</li>
|
<li><strong>FCP/FCI check</strong> — how much of the file control information to verify: <strong>Filetype only (FCP)</strong> (existence + file type), <strong>Filetype + size (FCP)</strong> (adds file size, or record length/count for record files), or <strong>Exact FCI</strong> (adds a byte-for-byte comparison of the raw SELECT response — the FCP template <code class="font-mono text-sm">'62'</code> — catching FID/AID, life-cycle status, security-attribute and proprietary-parameter changes).</li>
|
||||||
<li><strong>Access rules</strong> — verifies a file’s access conditions as <em>decoded rules</em>, independent of which <code class="font-mono text-sm">EF.ARR</code> record the FCI’s security-attribute reference (<code class="font-mono text-sm">'8B'</code>) points at: the referenced record’s rules are resolved from the card’s (or the snapshot’s) <code class="font-mono text-sm">EF.ARR</code> and compared as a set with the captured lines. Captured automatically when a profile is created from a card or a snapshot for every file whose FCI carries a reference (EF.ARR’s own contents are skipped by default — the rules are the meaningful check); the editor shows the captured rules as structured rows — the operation as bit checkboxes (or the RESIZE FILE / INCREASE command form) and each security condition as a select (always, never, or a key reference with its usage), with a preview line of the canonical decoded string the check compares; <em>+ rule</em> builds a new one (READ always), and the template forms outside this subset stay raw and removable. The None/Check toggle disables the check without losing the rules. A file whose FCI lost the reference fails; a reference that cannot be resolved (no cached <code class="font-mono text-sm">EF.ARR</code>, record out of range) is reported as unverifiable. With <strong>Exact FCI</strong>, the optional <em>Ignore the EF.ARR reference in the exact FCI</em> checkbox compares the FCI without the <code class="font-mono text-sm">'8B'</code> value: a different record number no longer fails while every other DO is still compared byte-for-byte (the reference’s meaning is verified by this check).</li>
|
<li><strong>Access rules</strong> — verifies a file’s access conditions as <em>decoded rules</em>, independent of which <code class="font-mono text-sm">EF.ARR</code> record the FCI’s security-attribute reference (<code class="font-mono text-sm">'8B'</code>) points at: the referenced record’s rules are resolved from the card’s (or the snapshot’s) <code class="font-mono text-sm">EF.ARR</code> and compared as a set with the captured lines. Captured automatically when a profile is created from a card or a snapshot for every file whose FCI carries a reference (EF.ARR’s own contents are skipped by default — the rules are the meaningful check); the editor shows the captured rules as structured rows — the operation as bit checkboxes (or the RESIZE FILE / INCREASE command form) and each security condition as a select (always, never, or a key reference with its usage), with a preview line of the canonical decoded string the check compares; <em>+ rule</em> builds a new one (READ always), and the template forms outside this subset stay raw and removable. The None/Check toggle disables the check without losing the rules; the block collapses to a header with the rule count and None hides it entirely. A file whose FCI lost the reference fails; a reference that cannot be resolved (no cached <code class="font-mono text-sm">EF.ARR</code>, record out of range) is reported as unverifiable. With <strong>Exact FCI</strong>, the optional <em>Ignore the EF.ARR reference in the exact FCI</em> checkbox compares the FCI without the <code class="font-mono text-sm">'8B'</code> value: a different record number no longer fails while every other DO is still compared byte-for-byte (the reference’s meaning is verified by this check).</li>
|
||||||
<li><strong>File attributes</strong> — file type, size, record length and record count, taken from the FCP template (any may be left unset).</li>
|
<li><strong>File attributes</strong> — file type, size, record length and record count, taken from the FCP template (any may be left unset).</li>
|
||||||
<li><strong>Check contents</strong> (optional) — <strong>Exact</strong> hex equality, or <strong>Mask</strong> where <code class="font-mono text-sm">?</code> is a per-nibble wildcard (a mask with no <code class="font-mono text-sm">?</code> is a prefix match, e.g. <code class="font-mono text-sm">0891</code> for the IMSI MCC/MNC). Record files store a per-record list.</li>
|
<li><strong>Check contents</strong> (optional) — <strong>Exact</strong> hex equality, or <strong>Mask</strong> where <code class="font-mono text-sm">?</code> is a per-nibble wildcard (a mask with no <code class="font-mono text-sm">?</code> is a prefix match, e.g. <code class="font-mono text-sm">0891</code> for the IMSI MCC/MNC). Record files store a per-record list.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
+22
-3
@@ -19812,19 +19812,27 @@ function profilerRenderRule(i, r) {
|
|||||||
// built the same way; every change re-encodes the canonical decoded line the
|
// built the same way; every change re-encodes the canonical decoded line the
|
||||||
// check compares. Forms outside the supported subset stay raw (read-only,
|
// check compares. Forms outside the supported subset stay raw (read-only,
|
||||||
// removable). Capture fills the lines when a profile is created from a card
|
// removable). Capture fills the lines when a profile is created from a card
|
||||||
// or a snapshot; the radio toggles the check.
|
// or a snapshot; the radio toggles the check, None hides the whole block and
|
||||||
|
// the block collapses to its header (the rules are kept either way).
|
||||||
function profilerRenderAccessRulesEditor(i, r) {
|
function profilerRenderAccessRulesEditor(i, r) {
|
||||||
const ar = r.accessRules || null;
|
const ar = r.accessRules || null;
|
||||||
const lines = (ar && ar.expected) || [];
|
const lines = (ar && ar.expected) || [];
|
||||||
const on = !!(ar && ar.enabled !== false);
|
const on = !!(ar && ar.enabled !== false);
|
||||||
|
const open = on && !!_profilerArrOpen[i];
|
||||||
let html = '<div class="flex items-center justify-between mb-1 mt-1">';
|
let html = '<div class="flex items-center justify-between mb-1 mt-1">';
|
||||||
html += '<span class="text-xs text-gray-600 dark:text-slate-400">' + esc(t('Check access rules')) + '</span>';
|
if (on) {
|
||||||
|
html += '<button onclick="profilerArrToggleOpen(' + i + ')" class="text-xs text-gray-600 dark:text-slate-400 hover:text-gray-800 dark:hover:text-slate-200">' +
|
||||||
|
esc((open ? '\u25be ' : '\u25b8 ') + t('Check access rules') + (lines.length ? ' (' + lines.length + ')' : '')) + '</button>';
|
||||||
|
} else {
|
||||||
|
html += '<span class="text-xs text-gray-600 dark:text-slate-400">' + esc(t('Check access rules')) + '</span>';
|
||||||
|
}
|
||||||
html += '<div class="flex items-center gap-3">';
|
html += '<div class="flex items-center gap-3">';
|
||||||
html += '<label class="text-xs flex items-center gap-1"><input type="radio" name="profiler-ar-' + i + '" value="none"' + (!on ? ' checked' : '') + ' onchange="profilerSetAccessRules(' + i + ',false)"> ' + esc(t('None')) + '</label>';
|
html += '<label class="text-xs flex items-center gap-1"><input type="radio" name="profiler-ar-' + i + '" value="none"' + (!on ? ' checked' : '') + ' onchange="profilerSetAccessRules(' + i + ',false)"> ' + esc(t('None')) + '</label>';
|
||||||
html += '<label class="text-xs flex items-center gap-1"><input type="radio" name="profiler-ar-' + i + '" value="check"' + (on ? ' checked' : '') + ' onchange="profilerSetAccessRules(' + i + ',true)"> ' + esc(t('Check')) + '</label>';
|
html += '<label class="text-xs flex items-center gap-1"><input type="radio" name="profiler-ar-' + i + '" value="check"' + (on ? ' checked' : '') + ' onchange="profilerSetAccessRules(' + i + ',true)"> ' + esc(t('Check')) + '</label>';
|
||||||
html += '</div></div>';
|
html += '</div></div>';
|
||||||
|
if (!open) return html;
|
||||||
if (r.fciMode === 'exact') {
|
if (r.fciMode === 'exact') {
|
||||||
html += '<label class="text-xs flex items-center gap-1 mb-1' + (on ? '' : ' opacity-50') + '"><input type="checkbox"' + (ar && ar.ignoreFciRef ? ' checked' : '') + (on ? '' : ' disabled') + ' onchange="profilerSetAccessRulesIgnore(' + i + ',this.checked)"> ' + esc(t('Ignore the EF.ARR reference in the exact FCI')) + '</label>';
|
html += '<label class="text-xs flex items-center gap-1 mb-1"><input type="checkbox"' + (ar && ar.ignoreFciRef ? ' checked' : '') + ' onchange="profilerSetAccessRulesIgnore(' + i + ',this.checked)"> ' + esc(t('Ignore the EF.ARR reference in the exact FCI')) + '</label>';
|
||||||
}
|
}
|
||||||
for (let ri = 0; ri < lines.length; ri++) {
|
for (let ri = 0; ri < lines.length; ri++) {
|
||||||
html += profilerArrRuleHtml(i, ri, lines[ri]);
|
html += profilerArrRuleHtml(i, ri, lines[ri]);
|
||||||
@@ -19836,6 +19844,15 @@ function profilerRenderAccessRulesEditor(i, r) {
|
|||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The expanded access-rule blocks of the profile editor (transient UI state,
|
||||||
|
// kept across the editor re-renders; None hides the block entirely).
|
||||||
|
let _profilerArrOpen = {};
|
||||||
|
|
||||||
|
function profilerArrToggleOpen(i) {
|
||||||
|
_profilerArrOpen[i] = !_profilerArrOpen[i];
|
||||||
|
profilerRenderEditor();
|
||||||
|
}
|
||||||
|
|
||||||
// One expected rule row: the operation form + bit checkboxes (or the RESIZE /
|
// One expected rule row: the operation form + bit checkboxes (or the RESIZE /
|
||||||
// INCREASE command), the condition rows and the canonical preview line.
|
// INCREASE command), the condition rows and the canonical preview line.
|
||||||
function profilerArrRuleHtml(i, ri, line) {
|
function profilerArrRuleHtml(i, ri, line) {
|
||||||
@@ -19972,6 +19989,8 @@ function profilerSetAccessRules(i, on) {
|
|||||||
const r = profilerDraft.rules[i];
|
const r = profilerDraft.rules[i];
|
||||||
if (!r.accessRules) r.accessRules = { expected: [] };
|
if (!r.accessRules) r.accessRules = { expected: [] };
|
||||||
r.accessRules.enabled = on;
|
r.accessRules.enabled = on;
|
||||||
|
// Check opens the block, None hides it (the rules are kept either way)
|
||||||
|
_profilerArrOpen[i] = on;
|
||||||
profilerRenderEditor();
|
profilerRenderEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ function extractFunc(src, name, asyncFn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const FNS = ['profilerNormHex', 'profilerNormHexStrict', 'profilerMatch', 'profilerMatchMin', 'profilerMaskPrefix4', 'profilerFileFields', 'profilerContentKindForFileType', 'profilerEmptyRecordContent', 'profilerValidateProfile', 'profilerCustomNameForPath', 'pysimCustomNormPath', 'profilerUpdateRulePath', 'profilerResultAspects', 'profilerAspectSummary', 'profilerNumRanges', 'profilerMatchedRecordsText', 'profilerCloneName', 'profilerClone', 'profilerNewId', 'esc', 'escHtml', 'profilerRawDataCheck', 'profilerRenderReport', 'parseBerLen', 'parseTlvList', 'fcpInt', 'fcpParseTlvs', 'fcpFileDescriptor', 'fcpLifeCycle', 'fcpSfi', 'fcpDo', 'fcpDecode',
|
const FNS = ['profilerNormHex', 'profilerNormHexStrict', 'profilerMatch', 'profilerMatchMin', 'profilerMaskPrefix4', 'profilerFileFields', 'profilerContentKindForFileType', 'profilerEmptyRecordContent', 'profilerValidateProfile', 'profilerCustomNameForPath', 'pysimCustomNormPath', 'profilerUpdateRulePath', 'profilerResultAspects', 'profilerAspectSummary', 'profilerNumRanges', 'profilerMatchedRecordsText', 'profilerCloneName', 'profilerClone', 'profilerNewId', 'esc', 'escHtml', 'profilerRawDataCheck', 'profilerRenderReport', 'parseBerLen', 'parseTlvList', 'fcpInt', 'fcpParseTlvs', 'fcpFileDescriptor', 'fcpLifeCycle', 'fcpSfi', 'fcpDo', 'fcpDecode',
|
||||||
'fcpArrRef', 'fcpSeidName', 'fcpArrRules', 'fcpArrRefFromFci', 'fcpWithoutArrRef', 'fcpArrSplitTop', 'fcpArrKeyRefFromName', 'fcpArrKeyRefOptions', 'fcpArrScParse', 'fcpArrScEncode', 'fcpArrRuleParse', 'fcpArrRuleEncode', 'fcpArrRuleToggleBit', 'fcpArrRuleSetKind', 'fcpArrRuleSetSc', 'fcpArrRuleSetUq', 'fcpArrRuleAddSc', 'fcpArrRuleRemoveSc', 'profilerRenderAccessRulesEditor', 'profilerArrRuleHtml', 'profilerArrScSelect', 'profilerArrUqSelect', 'profilerAccessRulesDiffHtml', 'profilerSnapshotArr', 'efBytes', 'efHex', 'efTlv', 'efDecArr', 'efArrAmLabel', 'efArrSc', 'efArrAmDo', 'efFidFromPath',
|
'fcpArrRef', 'fcpSeidName', 'fcpArrRules', 'fcpArrRefFromFci', 'fcpWithoutArrRef', 'fcpArrSplitTop', 'fcpArrKeyRefFromName', 'fcpArrKeyRefOptions', 'fcpArrScParse', 'fcpArrScEncode', 'fcpArrRuleParse', 'fcpArrRuleEncode', 'fcpArrRuleToggleBit', 'fcpArrRuleSetKind', 'fcpArrRuleSetSc', 'fcpArrRuleSetUq', 'fcpArrRuleAddSc', 'fcpArrRuleRemoveSc', 'profilerRenderAccessRulesEditor', 'profilerArrRuleHtml', 'profilerArrScSelect', 'profilerArrUqSelect', 'profilerArrToggleOpen', 'profilerAccessRulesDiffHtml', 'profilerSnapshotArr', 'efBytes', 'efHex', 'efTlv', 'efDecArr', 'efArrAmLabel', 'efArrSc', 'efArrAmDo', 'efFidFromPath',
|
||||||
'pinKeyRefName', 'fcpPsTemplate', 'fcpDiffHtml', 'profilerFciPreviewItems', 'profilerUpdateFciPreview', 'profilerUpdateRule', 'profilerFciInput', 'profilerScanToggleAll', 'profilerScanIgnoreAllState', 'swapNibbles', 'decIccid', 'profilerSnapshotIccid', 'profilerValidateSnapshot', 'profilerListSwitch', 'profilerScanRefreshOptions', 'profilerLiveSource', 'profilerSnapshotSource', 'profilerVisibleResults', 'profilerRulesFromSnapshot', 'profilerExtraFileResults', 'profilerScanNameKeydown', 'profilerTimingStats', 'profilerTimingAccumulator', 'profilerFormatMs', 'profilerRenderSnapshotSummary', 'profilerSnapshotCountLabel', 'pysimFsInfoHtml', 'profilerLabelText', 'profilerResultsHeaderText', 'profilerRenderResultsView', 'profilerBuildFileRuleFromSnapshot', 'profilerSnapshotPickListHtml', 'profilerScanSetTarget'];
|
'pinKeyRefName', 'fcpPsTemplate', 'fcpDiffHtml', 'profilerFciPreviewItems', 'profilerUpdateFciPreview', 'profilerUpdateRule', 'profilerFciInput', 'profilerScanToggleAll', 'profilerScanIgnoreAllState', 'swapNibbles', 'decIccid', 'profilerSnapshotIccid', 'profilerValidateSnapshot', 'profilerListSwitch', 'profilerScanRefreshOptions', 'profilerLiveSource', 'profilerSnapshotSource', 'profilerVisibleResults', 'profilerRulesFromSnapshot', 'profilerExtraFileResults', 'profilerScanNameKeydown', 'profilerTimingStats', 'profilerTimingAccumulator', 'profilerFormatMs', 'profilerRenderSnapshotSummary', 'profilerSnapshotCountLabel', 'pysimFsInfoHtml', 'profilerLabelText', 'profilerResultsHeaderText', 'profilerRenderResultsView', 'profilerBuildFileRuleFromSnapshot', 'profilerSnapshotPickListHtml', 'profilerScanSetTarget'];
|
||||||
let code = '';
|
let code = '';
|
||||||
for (const f of FNS) code += extractFunc(html, f) + '\n';
|
for (const f of FNS) code += extractFunc(html, f) + '\n';
|
||||||
@@ -37,6 +37,7 @@ code += extractFunc(html, 'profilerScanSnapshot', true) + '\n';
|
|||||||
code += extractFunc(html, 'pysimArrEnsure', true) + '\n';
|
code += extractFunc(html, 'pysimArrEnsure', true) + '\n';
|
||||||
code += "var _scanTarget = 'profile';\nvar profilerResults = null;\nvar profilerResultsHeader = null;\nvar profilerMismatchOnly = false;\n";
|
code += "var _scanTarget = 'profile';\nvar profilerResults = null;\nvar profilerResultsHeader = null;\nvar profilerMismatchOnly = false;\n";
|
||||||
code += "var _pysimArrCache = null;\nvar _pysimCardSession = null;\n";
|
code += "var _pysimArrCache = null;\nvar _pysimCardSession = null;\n";
|
||||||
|
code += "var _profilerArrOpen = {};\n";
|
||||||
// the view-history hooks (the router lives in the page; this pure test stubs it)
|
// the view-history hooks (the router lives in the page; this pure test stubs it)
|
||||||
code += 'globalThis.navRecord = function() {};\nglobalThis.navReplace = function() {};\n';
|
code += 'globalThis.navRecord = function() {};\nglobalThis.navReplace = function() {};\n';
|
||||||
code += html.match(/const PROFILER_MASK_PREFIX4_FIDS = \{[\s\S]*?\n\};/)[0] + '\n';
|
code += html.match(/const PROFILER_MASK_PREFIX4_FIDS = \{[\s\S]*?\n\};/)[0] + '\n';
|
||||||
@@ -1114,23 +1115,45 @@ test('the access-rules editor renders structured rows', () => {
|
|||||||
'RESIZE FILE (AM_DO 0x84, INS 0xD4): never',
|
'RESIZE FILE (AM_DO 0x84, INS 0xD4): never',
|
||||||
'AM 0x02 (UPDATE/ERASE (EF) / CREATE FILE EF (DF)): (PIN app1 (verify) OR PIN app2 (verify))',
|
'AM 0x02 (UPDATE/ERASE (EF) / CREATE FILE EF (DF)): (PIN app1 (verify) OR PIN app2 (verify))',
|
||||||
] } };
|
] } };
|
||||||
const html = profilerRenderAccessRulesEditor(3, r);
|
// collapsed by default: the header + toggle carry the rule count, no rows
|
||||||
|
_profilerArrOpen = {};
|
||||||
|
const closed = profilerRenderAccessRulesEditor(3, r);
|
||||||
|
assert.ok(closed.includes('profilerArrToggleOpen(3)'), closed);
|
||||||
|
assert.ok(closed.includes('Check access rules (3)'), closed);
|
||||||
|
assert.ok(!closed.includes('profilerArrToggleBit(3,0,1'), closed);
|
||||||
|
assert.ok(!closed.includes('profilerArrAddRule(3)'), closed);
|
||||||
|
// expanded: structured rows
|
||||||
|
_profilerArrOpen = { 3: true };
|
||||||
|
const out = profilerRenderAccessRulesEditor(3, r);
|
||||||
// the READ bit is checked; the condition select shows always
|
// the READ bit is checked; the condition select shows always
|
||||||
assert.ok(html.includes('type="checkbox" checked onchange="profilerArrToggleBit(3,0,1,this.checked)"'), html);
|
assert.ok(out.includes('type="checkbox" checked onchange="profilerArrToggleBit(3,0,1,this.checked)"'), out);
|
||||||
assert.ok(html.includes('<option value="always" selected>always</option>'), html);
|
assert.ok(out.includes('<option value="always" selected>always</option>'), out);
|
||||||
assert.ok(html.includes('<option value="key:10">ADM1</option>'), html);
|
assert.ok(out.includes('<option value="key:10">ADM1</option>'), out);
|
||||||
// the RESIZE row uses the command form, not bits
|
// the RESIZE row uses the command form, not bits
|
||||||
assert.ok(html.includes('<option value="resize" selected>RESIZE FILE</option>'), html);
|
assert.ok(out.includes('<option value="resize" selected>RESIZE FILE</option>'), out);
|
||||||
// the template rule is raw and not editable
|
// the template rule is raw and not editable
|
||||||
assert.ok(html.includes('raw: AM 0x02'), html);
|
assert.ok(out.includes('raw: AM 0x02'), out);
|
||||||
assert.ok(!html.includes('profilerArrToggleBit(3,2'), html);
|
assert.ok(!out.includes('profilerArrToggleBit(3,2'), out);
|
||||||
// the preview lines and the + rule button are present
|
// the preview lines and the + rule button are present
|
||||||
assert.ok(html.includes('RESIZE FILE (AM_DO 0x84, INS 0xD4): never'), html);
|
assert.ok(out.includes('RESIZE FILE (AM_DO 0x84, INS 0xD4): never'), out);
|
||||||
assert.ok(html.includes('profilerArrAddRule(3)'), html);
|
assert.ok(out.includes('profilerArrAddRule(3)'), out);
|
||||||
// an empty rule shows the capture hint with no disabled controls
|
// None hides the whole block (no toggle, no body)
|
||||||
const empty = profilerRenderAccessRulesEditor(4, { fciMode: 'type_size', accessRules: null });
|
const none = profilerRenderAccessRulesEditor(3, { ...r, accessRules: { ...r.accessRules, enabled: false } });
|
||||||
|
assert.ok(none.includes('Check access rules'), none);
|
||||||
|
assert.ok(!none.includes('profilerArrToggleOpen'), none);
|
||||||
|
assert.ok(!none.includes('profilerArrAddRule'), none);
|
||||||
|
// a rule without accessRules starts hidden too (Check enables it)
|
||||||
|
const absent = profilerRenderAccessRulesEditor(4, { fciMode: 'type_size', accessRules: null });
|
||||||
|
assert.ok(!absent.includes('profilerArrToggleOpen'), absent);
|
||||||
|
assert.ok(!absent.includes('profilerArrAddRule'), absent);
|
||||||
|
// an enabled but empty block shows the capture hint and + rule
|
||||||
|
_profilerArrOpen = { 4: true };
|
||||||
|
const empty = profilerRenderAccessRulesEditor(4, { fciMode: 'type_size', accessRules: { expected: [] } });
|
||||||
assert.ok(empty.includes('profilerArrAddRule(4)'), empty);
|
assert.ok(empty.includes('profilerArrAddRule(4)'), empty);
|
||||||
assert.ok(!empty.includes(' disabled'), empty);
|
assert.ok(!empty.includes(' disabled'), empty);
|
||||||
|
_profilerArrOpen = {};
|
||||||
|
// Check opens the block, None hides it
|
||||||
|
assert.ok(html.includes('_profilerArrOpen[i] = on;'), 'the radio drives the visibility');
|
||||||
delete global.t;
|
delete global.t;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user