fix: late-PoR handler on every path, one poll per packet, LOAD-step components (v3.6.37)
_send_envelope defined its SEND SHORT MESSAGE capture handler inside the 91xx branch, but the 'STATUS poll (PoR not captured)' path used it: a packet whose ENVELOPE answered 9000 and whose late PoR arrived on the STATUS poll raised UnboundLocalError, returned 500 and aborted the RAM operation mid-packet with the card's command left pending (live 2026-09-29, 120-byte LOAD blocks). The handler is now defined for every path, and the late-PoR poll runs only after a packet's last segment (poll_status from _send_secured_packet) - a concatenated download makes no spurious mid-packet STATUS exchanges. The PWA annotates every LOAD step with the CAP components its block covers (ramStepComponents: 'completes Import' / 'inside Method' / 'Header...Import') and ramInstallFailHint names the component the failing block completed - the live import-verification rejection lands in the block completing Import (6438/6985 at both 240 B and 64 B blocks). sw cache simple-v310; tests: 509 Python / 672 Node.
This commit is contained in:
@@ -321,7 +321,7 @@
|
||||
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">Операция</th><th class="text-left py-1 px-2">Описание</th></tr></thead>
|
||||
<tbody>
|
||||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Обзор карты (все данные GP)</td><td class="py-1 px-2">Запрос GET STATUS для ISD, приложений, ELF и модулей ELF, а также GET DATA FF21 (расширенные ресурсы карты: число установленных приложений и свободная энергонезависимая/энергозависимая память). Если карта отвергает компактный формат листинга (<code class="font-mono text-sm">P2.b2=0</code>, устаревший по GP), запросы повторяются в расширенном формате <code class="font-mono text-sm">E3</code>; флажок <strong>Запрашивать расширенные данные реестра (E3)</strong> дополнительно запрашивает их и объединяет расширенные поля (привилегии, связи ELF/SD, списки модулей). Результаты отображаются в обзоре с кнопками <strong>Удалить</strong> для каждого элемента и определённым форматом команд RAM.</td></tr>
|
||||
<tr><td class="py-1 px-2">Установка пакета (.cap файл)</td><td class="py-1 px-2">Отправка <code class="font-mono text-sm">.cap</code> файла на карту через сервер: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. Load-файл делится на LOAD APDU размера <strong>размер блока LOAD</strong> (1–240 байт полезной нагрузки, по умолчанию 240); каждый защищённый пакет доставляется одним SMS или, если он больше, конкатенированной SMS-PP загрузкой до 5 сегментов, так что большой <code>.cap</code> просто занимает несколько SMS. Сразу после выбора файл проверяется и в форме показывается <strong>оценка требований к памяти</strong>: <strong>требование NVRAM</strong> (образ кода + постоянные данные, сумма в стиле C6+C8 из GP Card Spec) и оценка RAM. Там же показаны <strong>библиотеки, с которыми слинкован CAP</strong> (его компонент Import: имя библиотеки, AID и минимальная версия <code class="font-mono text-sm">≥ x.y</code> — имена стандартных библиотек и семейства подставляются, а где известно, даётся подсказка о версии Java Card), идентификаторы пакета и апплетов и разбивка по компонентам (размеры и доли в порядке load-файла) — всё сгруппировано по разделам <strong>Package</strong>, <strong>Requires</strong>, <strong>Memory</strong> и <strong>Components</strong>. Строка <strong>Platform</strong> показывает минимальный уровень Java Card, который должна предоставить карта (выводится из импорта <code class="font-mono text-sm">javacard.framework</code>; флаг <code class="font-mono text-sm">int</code> в заголовке дополнительно требует поддержки int — Java Card 2.2+): JCRE проверяет каждый импорт по своим пакетам при верификации load-файла (JC VM spec 4.5.2), поэтому карта со старой платформой отклоняет LOAD сразу после завершения компонента <strong>Import</strong> (в строке шага виден remote SW карты) — меньший размер блока LOAD помогает только если блок превышает буфер карты. Повреждённый файл не проходит анализ, и <strong>Execute</strong> остаётся недоступной до успешного анализа. Оценка носит справочный характер и никогда не используется при установке: NVRAM — это образ кода плюс постоянные данные (сумма в стиле C6+C8 по GP Table 11-48; управление памятью карты, округление и запись в реестре не учитываются, а образ статических полей входит в обе части); рекомендуемый <strong>C6</strong> следует за load-файлом, а рекомендуемые квоты — это десятичные значения в байтах (шестнадцатеричное значение в скобках — это кодирование параметров установки C7/C8: C6 — энергонезависимый код, C7 — энергозависимая RAM, C8 — энергонезависимые данные).</td></tr>
|
||||
<tr><td class="py-1 px-2">Установка пакета (.cap файл)</td><td class="py-1 px-2">Отправка <code class="font-mono text-sm">.cap</code> файла на карту через сервер: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. Load-файл делится на LOAD APDU размера <strong>размер блока LOAD</strong> (1–240 байт полезной нагрузки, по умолчанию 240); каждый защищённый пакет доставляется одним SMS или, если он больше, конкатенированной SMS-PP загрузкой до 5 сегментов, так что большой <code>.cap</code> просто занимает несколько SMS. Сразу после выбора файл проверяется и в форме показывается <strong>оценка требований к памяти</strong>: <strong>требование NVRAM</strong> (образ кода + постоянные данные, сумма в стиле C6+C8 из GP Card Spec) и оценка RAM. Там же показаны <strong>библиотеки, с которыми слинкован CAP</strong> (его компонент Import: имя библиотеки, AID и минимальная версия <code class="font-mono text-sm">≥ x.y</code> — имена стандартных библиотек и семейства подставляются, а где известно, даётся подсказка о версии Java Card), идентификаторы пакета и апплетов и разбивка по компонентам (размеры и доли в порядке load-файла) — всё сгруппировано по разделам <strong>Package</strong>, <strong>Requires</strong>, <strong>Memory</strong> и <strong>Components</strong>. Строка <strong>Platform</strong> показывает минимальный уровень Java Card, который должна предоставить карта (выводится из импорта <code class="font-mono text-sm">javacard.framework</code>; флаг <code class="font-mono text-sm">int</code> в заголовке дополнительно требует поддержки int — Java Card 2.2+): JCRE проверяет каждый импорт по своим пакетам при верификации load-файла (JC VM spec 4.5.2), поэтому карта со старой платформой отклоняет LOAD сразу после завершения компонента <strong>Import</strong> (в строке шага виден remote SW карты и компонент, который завершил блок, например <em>завершает Import</em>) — меньший размер блока LOAD помогает только если блок превышает буфер карты. Повреждённый файл не проходит анализ, и <strong>Execute</strong> остаётся недоступной до успешного анализа. Оценка носит справочный характер и никогда не используется при установке: NVRAM — это образ кода плюс постоянные данные (сумма в стиле C6+C8 по GP Table 11-48; управление памятью карты, округление и запись в реестре не учитываются, а образ статических полей входит в обе части); рекомендуемый <strong>C6</strong> следует за load-файлом, а рекомендуемые квоты — это десятичные значения в байтах (шестнадцатеричное значение в скобках — это кодирование параметров установки C7/C8: C6 — энергонезависимый код, C7 — энергозависимая RAM, C8 — энергонезависимые данные).</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
+1
-1
@@ -320,7 +320,7 @@
|
||||
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">Operation</th><th class="text-left py-1 px-2">Description</th></tr></thead>
|
||||
<tbody>
|
||||
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Explore Card (all GP data)</td><td class="py-1 px-2">Queries GET STATUS for ISD, Applications, ELFs, and ELF Modules, plus GET DATA FF21 (Extended Card Resources: the number of installed applications and the free non-volatile/volatile memory). If a card rejects the compact listing (<code class="font-mono text-sm">P2.b2=0</code>, deprecated by GP) the queries are retried in the expanded <code class="font-mono text-sm">E3</code> form; the <strong>Query expanded registry details (E3)</strong> checkbox also requests it as an extra pass and merges the expanded-only fields (privileges, ELF/SD associations, module lists). Results appear in an explorer view with per-item <strong>Delete</strong> buttons and the detected RAM command format.</td></tr>
|
||||
<tr><td class="py-1 px-2">Install Package (.cap file)</td><td class="py-1 px-2">Sends a <code class="font-mono text-sm">.cap</code> file to the card via the server: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. The load file is split into LOAD APDUs of the <strong>LOAD block size</strong> (1–240 bytes of payload, 240 by default); each secured packet is delivered as a single SMS or, when larger, as a concatenated SMS-PP download of up to 5 segments, so a large <code class="font-mono text-sm">.cap</code> simply takes several SMS. As soon as the file is selected it is validated and its <strong>memory requirements are estimated</strong> in the form: the <strong>NVRAM requirement</strong> (the code image + persistent data, the GlobalPlatform Card Spec C6+C8 style total) and the RAM estimate. The same box reports the <strong>libraries the CAP is linked against</strong> (its Import component: library name, AID and the minimum version <code class="font-mono text-sm">≥ x.y</code>, with standard names and families resolved and a Java Card SDK hint where known), the package/applet identity and the component breakdown (sizes and shares in load-file order) — grouped under the <strong>Package</strong>, <strong>Requires</strong>, <strong>Memory</strong> and <strong>Components</strong> headings. The <strong>Platform</strong> line names the minimum Java Card level the card must provide (derived from the <code class="font-mono text-sm">javacard.framework</code> import; the header <code class="font-mono text-sm">int</code> flag additionally requires int support, Java Card 2.2+): the JCRE checks every import against its own packages when it verifies the load file (JC VM spec 4.5.2), so a card whose platform is older rejects the LOAD as soon as the <strong>Import</strong> component is complete (the step line carries the card's remote SW) — a smaller LOAD block only helps when the block exceeds the card's buffer. A corrupt or wrong-format file fails the analysis, and <strong>Execute</strong> stays disabled until a successful analysis. The estimate is informational only and is never used for installation: NVRAM is the code image plus the persistent data (the GlobalPlatform C6+C8 style total, Table 11-48 — card memory management, rounding and the registry entry are not included, and the static field image is counted in both parts); the suggested <strong>C6</strong> follows the load file, and the suggested quotas are decimal byte counts (the hex in parentheses is how the C7/C8 install parameters encode them: C6 = non-volatile code, C7 = volatile RAM, C8 = non-volatile data).</td></tr>
|
||||
<tr><td class="py-1 px-2">Install Package (.cap file)</td><td class="py-1 px-2">Sends a <code class="font-mono text-sm">.cap</code> file to the card via the server: INSTALL[for load] → LOAD ×N → INSTALL[for install (+make selectable)]. The load file is split into LOAD APDUs of the <strong>LOAD block size</strong> (1–240 bytes of payload, 240 by default); each secured packet is delivered as a single SMS or, when larger, as a concatenated SMS-PP download of up to 5 segments, so a large <code class="font-mono text-sm">.cap</code> simply takes several SMS. As soon as the file is selected it is validated and its <strong>memory requirements are estimated</strong> in the form: the <strong>NVRAM requirement</strong> (the code image + persistent data, the GlobalPlatform Card Spec C6+C8 style total) and the RAM estimate. The same box reports the <strong>libraries the CAP is linked against</strong> (its Import component: library name, AID and the minimum version <code class="font-mono text-sm">≥ x.y</code>, with standard names and families resolved and a Java Card SDK hint where known), the package/applet identity and the component breakdown (sizes and shares in load-file order) — grouped under the <strong>Package</strong>, <strong>Requires</strong>, <strong>Memory</strong> and <strong>Components</strong> headings. The <strong>Platform</strong> line names the minimum Java Card level the card must provide (derived from the <code class="font-mono text-sm">javacard.framework</code> import; the header <code class="font-mono text-sm">int</code> flag additionally requires int support, Java Card 2.2+): the JCRE checks every import against its own packages when it verifies the load file (JC VM spec 4.5.2), so a card whose platform is older rejects the LOAD as soon as the <strong>Import</strong> component is complete (the step line carries the card's remote SW and names the component the block completed, e.g. <em>completes Import</em>) — a smaller LOAD block only helps when the block exceeds the card's buffer. A corrupt or wrong-format file fails the analysis, and <strong>Execute</strong> stays disabled until a successful analysis. The estimate is informational only and is never used for installation: NVRAM is the code image plus the persistent data (the GlobalPlatform C6+C8 style total, Table 11-48 — card memory management, rounding and the registry entry are not included, and the static field image is counted in both parts); the suggested <strong>C6</strong> follows the load file, and the suggested quotas are decimal byte counts (the hex in parentheses is how the C7/C8 install parameters encode them: C6 = non-volatile code, C7 = volatile RAM, C8 = non-volatile data).</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
+48
-5
@@ -1732,7 +1732,7 @@
|
||||
// ===== Version =====
|
||||
// Single source of truth for the PWA version: shown in the header and used
|
||||
// by the server version check in pysimConnect().
|
||||
const SIMPLE_VERSION = '3.6.36';
|
||||
const SIMPLE_VERSION = '3.6.37';
|
||||
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
||||
|
||||
// ===== Tab switching =====
|
||||
@@ -9358,12 +9358,42 @@ async function ramDeleteFromExplorer(aid, withCascade) {
|
||||
// consumed was saved above.
|
||||
}
|
||||
|
||||
// Which CAP components a LOAD step's payload covers (pure, diagnostic). The
|
||||
// server sends the component list in load-file order with sizes that include
|
||||
// each component's 3-byte header (capmem), so the ranges accumulate directly.
|
||||
// `completed` lists the components whose last byte falls inside the block -
|
||||
// the card has just seen them whole, which is where a verifier rejection
|
||||
// lands (a CAP whose Import list the card cannot satisfy is refused exactly
|
||||
// in the block completing Import).
|
||||
function ramStepComponents(name, components, blockSize) {
|
||||
const m = /^LOAD \((\d+)\/(\d+)\)$/.exec(String(name || '').trim());
|
||||
const comps = components || [];
|
||||
const size = Number(blockSize) || 0;
|
||||
if (!m || !comps.length || size <= 0) return null;
|
||||
const start = (Number(m[1]) - 1) * size;
|
||||
const end = Number(m[1]) * size;
|
||||
let off = 0;
|
||||
const names = [];
|
||||
const completed = [];
|
||||
for (const c of comps) {
|
||||
const cstart = off, cend = off + (Number(c.size) || 0);
|
||||
off = cend;
|
||||
if (cend <= start) continue; // entirely before this block
|
||||
if (cstart >= end) break; // entirely after it
|
||||
names.push(c.name);
|
||||
if (cend > start && cend <= end) completed.push(c.name);
|
||||
}
|
||||
if (!names.length) return null;
|
||||
return { names: names, completed: completed };
|
||||
}
|
||||
|
||||
// One RAM install step line: the PoR verdict plus the remote command's
|
||||
// status word (9000/61xx/62xx/63xx/CAFE are success; anything else is the
|
||||
// card's error) - the details that used to be lost behind a bare
|
||||
// "por_error_" label. The envelope/GET RESPONSE SW is only shown when it is
|
||||
// not a plain 9000.
|
||||
function ramStepLine(s, idx) {
|
||||
// not a plain 9000. With the CAP analysis at hand the line also names the
|
||||
// component the block completed (see ramStepComponents).
|
||||
function ramStepLine(s, idx, comps, blockSize) {
|
||||
const ok = !s.por_error && (s.por_status === 'por_ok' || s.por_status === 'no_por');
|
||||
let line = (ok ? '\u2705' : '\u274c') + ' ' + t('Step') + ' ' + (idx + 1) + ': ' +
|
||||
s.name + ' \u2014 ';
|
||||
@@ -9382,6 +9412,12 @@ function ramStepLine(s, idx) {
|
||||
if (s.bytes && s.segments) {
|
||||
line += ' \u00b7 ' + s.bytes + ' ' + t('bytes') + ' / ' + s.segments + ' SMS';
|
||||
}
|
||||
const sc = ramStepComponents(s.name, comps, blockSize);
|
||||
if (sc) {
|
||||
if (sc.completed.length) line += ' \u00b7 ' + t('completes') + ' ' + sc.completed[sc.completed.length - 1];
|
||||
else if (sc.names.length === 1) line += ' \u00b7 ' + t('inside') + ' ' + sc.names[0];
|
||||
else line += ' \u00b7 ' + sc.names[0] + '\u2026' + sc.names[sc.names.length - 1];
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
@@ -9398,7 +9434,10 @@ function ramInstallFailHint(data, mem) {
|
||||
const step = steps[(Number(data.failed_step) || 1) - 1] || null;
|
||||
const name = step ? String(step.name || '') : '';
|
||||
if (!/^(LOAD |INSTALL \[for load\])/.test(name)) return '';
|
||||
let hint = t('a LOAD rejected here means the card refused the CAP: every import version under Requires must be present on the card');
|
||||
let hint = '';
|
||||
const sc = ramStepComponents(name, mem && mem.components, data.load_block_size);
|
||||
if (sc && sc.completed.length) hint += t('the failed step completes') + ' ' + sc.completed[sc.completed.length - 1] + '. ';
|
||||
hint += t('a LOAD rejected here means the card refused the CAP: every import version under Requires must be present on the card');
|
||||
if (mem && mem.requires_java_card) hint += ' (Java Card \u2265 ' + mem.requires_java_card + ')';
|
||||
hint += t('. If the CAP needs only versions this card has, retry with a smaller LOAD block (e.g. 64 B)');
|
||||
return hint;
|
||||
@@ -9507,8 +9546,9 @@ function ramShowInstallResult(data, okText, capMem) {
|
||||
const resultEl = document.getElementById('ram-result');
|
||||
const stepsEl = document.getElementById('ram-steps');
|
||||
stepsEl.classList.remove('hidden');
|
||||
const comps = capMem && capMem.components;
|
||||
let txt = '';
|
||||
(data.steps || []).forEach((s, idx) => { txt += ramStepLine(s, idx) + '\n'; });
|
||||
(data.steps || []).forEach((s, idx) => { txt += ramStepLine(s, idx, comps, data.load_block_size) + '\n'; });
|
||||
stepsEl.textContent = txt;
|
||||
// Persist the counter the card actually consumed (accepted packets only):
|
||||
// even a failed install leaves the accepted steps behind, and replaying
|
||||
@@ -17176,6 +17216,9 @@ const LANG_RU = {
|
||||
'Install OK': 'Установка OK',
|
||||
'Install FAILED at step:': 'Установка не удалась на шаге:',
|
||||
'Hint:': 'Подсказка:',
|
||||
'completes': 'завершает',
|
||||
'inside': 'внутри',
|
||||
'the failed step completes': 'неудавшийся шаг завершает',
|
||||
'a LOAD rejected here means the card refused the CAP: every import version under Requires must be present on the card': 'отклонение LOAD здесь означает, что карта отклонила CAP: все версии импортов из «Требуются» должны присутствовать на карте',
|
||||
'. If the CAP needs only versions this card has, retry with a smaller LOAD block (e.g. 64 B)': '. Если CAP требует только доступные этой карте версии, повторите с меньшим размером блока LOAD (например, 64 Б)',
|
||||
'Profile from snapshot': 'Профиль из снимка',
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'simple-v309';
|
||||
const CACHE = 'simple-v310';
|
||||
const URLS = [
|
||||
'index.html',
|
||||
'help.html',
|
||||
|
||||
@@ -22,7 +22,7 @@ function extractFunc(src, name) {
|
||||
}
|
||||
|
||||
// Extract chain builder functions and dependencies
|
||||
const FNS = ['berLenStr', 'buildApdu', 'escHtml', 'esc', 'chainInit', 'chainRamBuildRowHex', 'ramFmtLifecycle', 'ramFmtPrivileges', 'ramRenderExploreHtml', 'ramStepLine', 'ramInstallFailHint', 'ramGetStatusApdu', 'ramDeleteApdu',
|
||||
const FNS = ['berLenStr', 'buildApdu', 'escHtml', 'esc', 'chainInit', 'chainRamBuildRowHex', 'ramFmtLifecycle', 'ramFmtPrivileges', 'ramRenderExploreHtml', 'ramStepLine', 'ramStepComponents', 'ramInstallFailHint', 'ramGetStatusApdu', 'ramDeleteApdu',
|
||||
'stkParamsBuild', 'ramRemoteSwOk', 'spPorAccepted', 'ramIncrementCntr', 'ramDeleteFromExplorer', 'ramListingSpi2', 'ramRemoveFromExplorer', 'ramHasInstance', 'ramExpandedQueryApdu',
|
||||
'_parseRawElfEntry', '_parseRawAppEntry', 'ramParseElfStatus', 'ramParseAppStatus', 'parseTLV', '_parseE3Entry',
|
||||
'ramCardIdxAfterRemove', 'ramClearResults', 'ramHideProgress', 'ramOpChanged', 'ramRender', 'ramApplyCard', 'ramExecute', 'decodePrivileges', 'ramActionBtn', 'ramCapToolkitMode', 'ramOpProgressText',
|
||||
@@ -390,6 +390,46 @@ test('ramStepLine shows the PoR verdict and the remote status word', () => {
|
||||
assert.ok(noPor.startsWith('\u2705') && noPor.includes('no PoR'), noPor);
|
||||
});
|
||||
|
||||
test('ramStepComponents maps a LOAD step to the CAP components it covers', () => {
|
||||
// RemMobileID-like layout: sizes include each component's 3-byte header
|
||||
const comps = [
|
||||
{ name: 'Header', size: 20 }, { name: 'Directory', size: 34 },
|
||||
{ name: 'Applet', size: 15 }, { name: 'Import', size: 101 },
|
||||
{ name: 'ConstantPool', size: 1121 }, { name: 'Method', size: 8506 },
|
||||
];
|
||||
// 240 B block 1 (0..239) completes the Import component (its end is 170)
|
||||
let sc = ramStepComponents('LOAD (1/64)', comps, 240);
|
||||
assert.deepStrictEqual(sc.completed, ['Header', 'Directory', 'Applet', 'Import']);
|
||||
assert.deepStrictEqual(sc.names.slice(0, 2), ['Header', 'Directory']);
|
||||
// 64 B blocks: 1 completes Header/Directory, 3 completes Import
|
||||
assert.deepStrictEqual(ramStepComponents('LOAD (1/240)', comps, 64).completed, ['Header', 'Directory']);
|
||||
sc = ramStepComponents('LOAD (2/240)', comps, 64);
|
||||
assert.deepStrictEqual(sc.names, ['Applet', 'Import']);
|
||||
assert.deepStrictEqual(sc.completed, ['Applet']);
|
||||
sc = ramStepComponents('LOAD (3/240)', comps, 64);
|
||||
assert.deepStrictEqual(sc.names, ['Import', 'ConstantPool']);
|
||||
assert.deepStrictEqual(sc.completed, ['Import']);
|
||||
// other step names and missing data produce nothing
|
||||
assert.strictEqual(ramStepComponents('INSTALL [for load]', comps, 64), null);
|
||||
assert.strictEqual(ramStepComponents('LOAD (1/64)', comps, 0), null);
|
||||
assert.strictEqual(ramStepComponents('LOAD (1/64)', null, 64), null);
|
||||
});
|
||||
|
||||
test('ramStepLine annotates a LOAD step with the completing component', () => {
|
||||
globalThis.t = s => s;
|
||||
globalThis.lookupSw = () => '';
|
||||
const comps = [{ name: 'Header', size: 20 }, { name: 'Import', size: 101 },
|
||||
{ name: 'Method', size: 8506 }];
|
||||
const line = ramStepLine({ name: 'LOAD (1/64)', por_status: 'por_ok', por_sw: '9000' }, 0, comps, 240);
|
||||
assert.ok(line.includes('completes Import'), line);
|
||||
// block 2 of 40 B blocks (40..79) lies strictly inside Import
|
||||
const inside = ramStepLine({ name: 'LOAD (2/64)', por_status: 'por_ok', por_sw: '9000' }, 1, comps, 40);
|
||||
assert.ok(inside.includes('inside Import'), inside);
|
||||
// without a component list the line is unchanged
|
||||
const plain = ramStepLine({ name: 'LOAD (1/64)', por_status: 'por_ok', por_sw: '9000' }, 0);
|
||||
assert.ok(!plain.includes('completes') && !plain.includes('inside'), plain);
|
||||
});
|
||||
|
||||
test('ramInstallFailHint names the CAP import requirement for a rejected LOAD', () => {
|
||||
globalThis.t = s => s;
|
||||
const failedLoad = {
|
||||
@@ -408,6 +448,14 @@ test('ramInstallFailHint names the CAP import requirement for a rejected LOAD',
|
||||
assert.strictEqual(ramInstallFailHint(
|
||||
{ success: false, failed_step: 2, steps: [{ name: 'INSTALL [for load]' }, { name: 'INSTALL [for install]' }] }, null), '');
|
||||
assert.strictEqual(ramInstallFailHint({ success: true, failed_step: 1, steps: [] }, null), '');
|
||||
// with the CAP analysis the completing component of the failing block is
|
||||
// named (the card check lands on the component boundary)
|
||||
const comps = [{ name: 'Header', size: 20 }, { name: 'Import', size: 101 }];
|
||||
const withBoundary = ramInstallFailHint(
|
||||
{ success: false, failed_step: 2, load_block_size: 64,
|
||||
steps: [{ name: 'INSTALL [for load]' }, { name: 'LOAD (2/240)', por_sw: '6438' }] },
|
||||
{ requires_java_card: '2.2.2', components: comps });
|
||||
assert.ok(withBoundary.startsWith('the failed step completes Import. '), withBoundary);
|
||||
});
|
||||
|
||||
test('ramOpChanged clears the executed status only on a real op change', () => {
|
||||
|
||||
Reference in New Issue
Block a user