v3.28.4: the Custom-mode FCI editor + the shared ADM pattern
The profile rule editor rendered the same "FCI hex (raw SELECT response)" field for Exact and Custom, although the custom check never reads `fciHex` (it compares the data objects against the card's live FCI) - so the field looked like an expectation it was not. - The raw-FCI field and its decoded preview are exact-mode-only now; the Custom mode shows just its data-object editor. - Switching a rule to Custom seeds the data objects from its captured FCI when the list is empty (the same all-'value' seeding a captured profile gets); the "load from the captured FCI" link refills an emptied list, and a rule without a captured FCI still starts empty for manual rows. From the v3.28.3 review: the ADM's byte-pair rule now lives in one place, presets.ADM_PATTERN, used by /api/verify-adm and the outfile import - two literals could drift and an imported ADM could silently become unverifiable. The RU help's ADM wording is "4-16 hex-цифр (чётное число)". Docs: help EN/RU, the repo AGENTS. Tests: the mode switch (seed / keep / no FCI / switch-away), the field-gating source pin, the pattern's shape and the both-users pin. 917 frontend + 927 Python; sw cache simple-v442.
This commit is contained in:
@@ -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',
|
||||
'fcpArrRef', 'fcpSeidName', 'fcpArrRules', 'fcpArrRefFromFci', 'fcpWithoutArrRef', 'fcpArrSplitTop', 'fcpArrKeyRefFromName', 'fcpArrKeyRefOptions', 'fcpArrScParse', 'fcpArrScEncode', 'fcpArrRuleParse', 'fcpArrRuleEncode', 'fcpArrRuleOps', 'fcpArrRuleOpCovered', 'fcpArrRuleSatisfied', 'fcpArrRuleToggleBit', 'fcpArrRuleSetKind', 'fcpArrRuleSetSc', 'fcpArrRuleSetUq', 'fcpArrRuleAddSc', 'fcpArrRuleRemoveSc', 'profilerRenderAccessRulesEditor', 'profilerArrRuleHtml', 'profilerArrScSelect', 'profilerArrUqSelect', 'profilerArrToggleOpen', 'profilerArrShiftOpen', 'profilerAccessRulesDiffHtml', 'fcpDosFromFci', 'fcpDoName', 'profilerRenderFciDosEditor', 'profilerFciDoRowHtml', 'profilerFciDosDiffHtml', 'profilerSnapshotArr', 'efBytes', 'efHex', 'efTlv', 'efDecArr', 'efArrAmLabel', 'efArrSc', 'efArrAmDo', 'efFidFromPath',
|
||||
'fcpArrRef', 'fcpSeidName', 'fcpArrRules', 'fcpArrRefFromFci', 'fcpWithoutArrRef', 'fcpArrSplitTop', 'fcpArrKeyRefFromName', 'fcpArrKeyRefOptions', 'fcpArrScParse', 'fcpArrScEncode', 'fcpArrRuleParse', 'fcpArrRuleEncode', 'fcpArrRuleOps', 'fcpArrRuleOpCovered', 'fcpArrRuleSatisfied', 'fcpArrRuleToggleBit', 'fcpArrRuleSetKind', 'fcpArrRuleSetSc', 'fcpArrRuleSetUq', 'fcpArrRuleAddSc', 'fcpArrRuleRemoveSc', 'profilerRenderAccessRulesEditor', 'profilerArrRuleHtml', 'profilerArrScSelect', 'profilerArrUqSelect', 'profilerArrToggleOpen', 'profilerArrShiftOpen', 'profilerAccessRulesDiffHtml', 'fcpDosFromFci', 'fcpDoName', 'profilerRenderFciDosEditor', 'profilerFciDoRowHtml', 'profilerFciDosDiffHtml', 'profilerUpdateRuleFciMode', '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'];
|
||||
let code = '';
|
||||
for (const f of FNS) code += extractFunc(html, f) + '\n';
|
||||
@@ -1437,6 +1437,45 @@ test('the custom-FCI editor renders the data object rows', () => {
|
||||
delete global.t;
|
||||
});
|
||||
|
||||
test('switching a rule to Custom seeds the data objects from its FCI (v3.28.4)', () => {
|
||||
const FCI = '621A82054221000F0583026F4F8A01058B032F06028802004B8801B0';
|
||||
global.profilerRenderEditor = () => {};
|
||||
// a captured FCI with an empty list: the objects are seeded
|
||||
global.profilerDraft = { rules: [{ fciMode: 'exact', fciHex: FCI, fciDos: undefined }] };
|
||||
profilerUpdateRuleFciMode(0, 'custom');
|
||||
assert.strictEqual(global.profilerDraft.rules[0].fciMode, 'custom');
|
||||
const seeded = global.profilerDraft.rules[0].fciDos;
|
||||
assert.ok(Array.isArray(seeded) && seeded.length >= 5, JSON.stringify(seeded));
|
||||
assert.deepStrictEqual(seeded[0], { key: '82', mode: 'value', value: '4221000F05' });
|
||||
// an existing list is kept as it is
|
||||
const existing = [{ key: '82', mode: 'present' }];
|
||||
global.profilerDraft = { rules: [{ fciMode: 'exact', fciHex: FCI, fciDos: existing }] };
|
||||
profilerUpdateRuleFciMode(0, 'custom');
|
||||
assert.strictEqual(global.profilerDraft.rules[0].fciDos, existing);
|
||||
// no captured FCI: the list stays empty (rows are added by hand)
|
||||
global.profilerDraft = { rules: [{ fciMode: 'exact', fciHex: null, fciDos: undefined }] };
|
||||
profilerUpdateRuleFciMode(0, 'custom');
|
||||
assert.ok(!(global.profilerDraft.rules[0].fciDos || []).length);
|
||||
// switching away does not touch the list
|
||||
global.profilerDraft = { rules: [{ fciMode: 'custom', fciHex: FCI, fciDos: undefined }] };
|
||||
profilerUpdateRuleFciMode(0, 'exact');
|
||||
assert.ok(!(global.profilerDraft.rules[0].fciDos || []).length);
|
||||
delete global.profilerRenderEditor;
|
||||
delete global.profilerDraft;
|
||||
});
|
||||
|
||||
test('the raw-FCI field is exact-mode-only (v3.28.4)', () => {
|
||||
// the custom mode shows its own editor: the raw FCI is only the source for
|
||||
// the data objects, never the expectation
|
||||
assert.ok(/if \(fciMode === 'exact'\) \{\n\t\thtml \+= '<div class="mb-2 flex gap-3 items-start">'/.test(html),
|
||||
'the FCI hex field must be gated to the exact mode');
|
||||
assert.ok(!/fciMode === 'exact' \|\| fciMode === 'custom'/.test(html),
|
||||
'the custom mode must not render the exact-mode FCI field');
|
||||
// the auto-seed lives in the mode updater
|
||||
assert.ok(/if \(value === 'custom' && !\(r\.fciDos \|\| \[\]\)\.length && r\.fciHex\)/.test(html),
|
||||
'the Custom switch must seed the data objects');
|
||||
});
|
||||
|
||||
test('the custom FCI report block marks the expectations', () => {
|
||||
global.t = s => s;
|
||||
const out = profilerFciDosDiffHtml({ label: 'fci', mode: 'custom', ok: false, parseError: '',
|
||||
|
||||
Reference in New Issue
Block a user