profiler: show raw-data mismatches as aligned monospace fields
FCI and content (incl. record) mismatches are now rendered as two read-only monospace inputs — expected on top, actual directly beneath — sharing a fixed-width right-aligned label column (w-24) so both fields start at exactly the same horizontal position and stretch to fill the row (flex-1/min-w-0, no overlap). A title attribute shows the full value on hover. Non-raw checks (fileSize, recordLen, numRecords, content.records, read failure) keep the inline line. New profilerRawDataCheck helper; 2 new tests. SW cache v59 -> v60.
This commit is contained in:
+21
-1
@@ -7737,6 +7737,14 @@ function profilerNumRanges(nums) {
|
||||
return parts.join(', ');
|
||||
}
|
||||
|
||||
// Checks whose expected/actual are raw data (hex), rendered as aligned fields.
|
||||
function profilerRawDataCheck(c) {
|
||||
if (c.label === 'fci') return true;
|
||||
if (/^content\.rec\d+$/.test(c.label)) return true;
|
||||
if (c.label === 'content') return c.expected !== 'readable';
|
||||
return false;
|
||||
}
|
||||
|
||||
function profilerRenderReport(results) {
|
||||
let html = '';
|
||||
for (const r of results) {
|
||||
@@ -7755,7 +7763,19 @@ function profilerRenderReport(results) {
|
||||
if (r.error) html += '<div class="text-xs text-yellow-600 mt-1">' + esc(r.error) + '</div>';
|
||||
for (const c of r.checks) {
|
||||
if (c.ok) continue;
|
||||
html += '<div class="text-xs text-red-600 mt-1">' + esc(c.label) + ': ' + esc(t('expected')) + ' <b>' + esc(String(c.expected)) + '</b>, ' + esc(t('actual')) + ' <b>' + esc(String(c.actual)) + '</b></div>';
|
||||
if (profilerRawDataCheck(c)) {
|
||||
html += '<div class="mt-1">';
|
||||
html += '<div class="text-xs text-red-600 font-mono pl-2">' + esc(c.label) + '</div>';
|
||||
html += '<div class="flex items-center gap-2 mt-0.5 pl-2">' +
|
||||
'<span class="text-xs text-gray-500 dark:text-slate-400 w-24 text-right shrink-0">' + esc(t('expected')) + '</span>' +
|
||||
'<input readonly title="' + escHtml(String(c.expected)) + '" value="' + escHtml(String(c.expected)) + '" class="flex-1 min-w-0 font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1 bg-gray-100 dark:bg-slate-800"></div>';
|
||||
html += '<div class="flex items-center gap-2 mt-0.5 pl-2">' +
|
||||
'<span class="text-xs text-gray-500 dark:text-slate-400 w-24 text-right shrink-0">' + esc(t('actual')) + '</span>' +
|
||||
'<input readonly title="' + escHtml(String(c.actual)) + '" value="' + escHtml(String(c.actual)) + '" class="flex-1 min-w-0 font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1 bg-gray-100 dark:bg-slate-800"></div>';
|
||||
html += '</div>';
|
||||
} else {
|
||||
html += '<div class="text-xs text-red-600 mt-1">' + esc(c.label) + ': ' + esc(t('expected')) + ' <b>' + esc(String(c.expected)) + '</b>, ' + esc(t('actual')) + ' <b>' + esc(String(c.actual)) + '</b></div>';
|
||||
}
|
||||
}
|
||||
const recFailed = (r.checks || []).some(c => /^content\.rec\d+$/.test(c.label) && !c.ok);
|
||||
if (recFailed && r.recordsMatched && r.recordsMatched.length) {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'otaman-v59';
|
||||
const CACHE = 'otaman-v60';
|
||||
const URLS = [
|
||||
'index.html',
|
||||
'help.html',
|
||||
|
||||
@@ -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', 'profilerRenderReport'];
|
||||
const FNS = ['profilerNormHex', 'profilerNormHexStrict', 'profilerMatch', 'profilerMatchMin', 'profilerMaskPrefix4', 'profilerFileFields', 'profilerContentKindForFileType', 'profilerEmptyRecordContent', 'profilerValidateProfile', 'profilerCustomNameForPath', 'profilerUpdateRulePath', 'profilerResultAspects', 'profilerAspectSummary', 'profilerNumRanges', 'esc', 'escHtml', 'profilerRawDataCheck', 'profilerRenderReport'];
|
||||
let code = '';
|
||||
for (const f of FNS) code += extractFunc(html, f) + '\n';
|
||||
code += extractFunc(html, 'profilerBuildFileRule', true) + '\n';
|
||||
@@ -535,3 +535,36 @@ test('profilerRenderReport includes the checked-aspects summary and matching-rec
|
||||
assert.ok(html.includes('1-2, 5'));
|
||||
delete global.t;
|
||||
});
|
||||
|
||||
test('profilerRawDataCheck identifies raw-data checks', () => {
|
||||
assert.strictEqual(profilerRawDataCheck({ label: 'fci' }), true);
|
||||
assert.strictEqual(profilerRawDataCheck({ label: 'content', expected: 'AABBCC' }), true);
|
||||
assert.strictEqual(profilerRawDataCheck({ label: 'content.rec3' }), true);
|
||||
assert.strictEqual(profilerRawDataCheck({ label: 'content', expected: 'readable' }), false);
|
||||
assert.strictEqual(profilerRawDataCheck({ label: 'content.records' }), false);
|
||||
assert.strictEqual(profilerRawDataCheck({ label: 'fileSize' }), false);
|
||||
});
|
||||
|
||||
test('profilerRenderReport renders raw-data mismatches as aligned readonly fields', () => {
|
||||
global.t = s => s;
|
||||
global.pysimCustomFiles = [];
|
||||
const html = profilerRenderReport([
|
||||
{ path: 'MF/6F07', name: 'EF.IMSI', status: 'fail', checks: [
|
||||
{ label: 'fci', ok: false, expected: '621082024021', actual: '621082024022' },
|
||||
] },
|
||||
{ path: 'MF/7F20/6F3A', name: 'EF.X', status: 'fail', checks: [
|
||||
{ label: 'content.records', ok: false, expected: '30 records', actual: '10 records' },
|
||||
] },
|
||||
]);
|
||||
const fciBlock = html.split('</div>').find(s => s.includes('621082024021'));
|
||||
assert.ok(html.includes('readonly'));
|
||||
assert.ok((html.match(/readonly/g) || []).length >= 2);
|
||||
assert.ok(html.includes('font-mono'));
|
||||
assert.ok(html.includes('value="621082024021"'));
|
||||
assert.ok(html.includes('value="621082024022"'));
|
||||
assert.ok(html.includes('w-24 text-right'));
|
||||
// non-raw check stays inline
|
||||
assert.ok(html.includes('content.records: expected'));
|
||||
assert.ok(!fciBlock.includes(': expected'));
|
||||
delete global.t;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user