Files
otaman/frontend/tests/fs_sort.test.js
T
catarrh 5485d61390 ui: sort the file manager tree by FID or symbolic name
- pysimFsSortChildren() groups DFs above EFs, then sorts each group by
  FID (default) or case-insensitive symbolic name; missing names fall
  back to the FID as the sort key; deterministic tie-break; the input
  array is not mutated
- render uses the sorted copy, so lazily loaded directories and custom
  file injections are sorted too
- FID / Name pills above the tree (pysimFsSetSort) persist the choice in
  localStorage (otaman_fs_sort) and re-render without refetching
- tests for the comparator (DF priority, both keys, fallbacks, custom
  entries, no mutation) and the pill wiring; help/README updated;
  SW cache v108 -> v109.
2026-09-12 19:41:49 +03:00

67 lines
2.8 KiB
JavaScript

const { test } = require('node:test');
const assert = require('node:assert');
const fs = require('node:fs');
const path = require('node:path');
const html = fs.readFileSync(path.join(__dirname, '..', 'index.html'), 'utf8');
function extractFunc(src, name) {
const re = new RegExp('function\\s+' + name + '\\s*\\([^)]*\\)\\s*\\{');
const m = re.exec(src);
if (!m) throw new Error('function ' + name + ' not found');
let i = m.index + m[0].length - 1;
let depth = 0;
for (; i < src.length; i++) {
if (src[i] === '{') depth++;
else if (src[i] === '}') {
depth--;
if (depth === 0) break;
}
}
return src.slice(m.index, i + 1);
}
eval(extractFunc(html, 'pysimFsSortChildren'));
const f = (fid, name) => ({ fid, name, isDir: false });
const d = (fid, name) => ({ fid, name, isDir: true });
test('DFs sort before EFs in both modes', () => {
const children = [f('6F07', 'EF.IMSI'), d('7F20', 'DF.GSM'), f('2FE2', 'EF.ICCID'), d('7F10', 'DF.TELECOM')];
assert.deepStrictEqual(pysimFsSortChildren(children, 'fid').map(x => x.fid), ['7F10', '7F20', '2FE2', '6F07']);
assert.deepStrictEqual(pysimFsSortChildren(children, 'name').map(x => x.name), ['DF.GSM', 'DF.TELECOM', 'EF.ICCID', 'EF.IMSI']);
});
test('FID mode sorts EFs numerically by FID string', () => {
const children = [f('6F3A', 'EF.ADN'), f('2FE2', 'EF.ICCID'), f('6F07', 'EF.IMSI')];
assert.deepStrictEqual(pysimFsSortChildren(children, 'fid').map(x => x.fid), ['2FE2', '6F07', '6F3A']);
});
test('name mode sorts case-insensitively', () => {
const children = [f('6F3A', 'EF.ADN'), f('2FE2', 'ef.iccid'), f('6F07', 'EF.IMSI')];
assert.deepStrictEqual(pysimFsSortChildren(children, 'name').map(x => x.name), ['EF.ADN', 'ef.iccid', 'EF.IMSI']);
});
test('missing name falls back to the FID as the sort key', () => {
const children = [f('2FE2', null), f('6F07', 'EF.IMSI'), f('6F3A', '')];
// keys: '2FE2', 'EF.IMSI', '6F3A' -> '2FE2' < '6F3A' < 'EF.IMSI'
assert.deepStrictEqual(pysimFsSortChildren(children, 'name').map(x => x.fid), ['2FE2', '6F3A', '6F07']);
});
test('custom entries use their isDir flag for the DF priority', () => {
const children = [f('6F3A', 'EF.ADN'), d('7F20', 'DF.GSM')];
assert.strictEqual(pysimFsSortChildren(children, 'fid')[0].name, 'DF.GSM');
});
test('equal keys keep a deterministic tie-break by the other field', () => {
const children = [f('6F07', 'EF.SAME'), f('2FE2', 'EF.SAME')];
assert.deepStrictEqual(pysimFsSortChildren(children, 'name').map(x => x.fid), ['2FE2', '6F07']);
assert.deepStrictEqual(pysimFsSortChildren(children, 'fid').map(x => x.fid), ['2FE2', '6F07']);
});
test('does not mutate the input array', () => {
const children = [f('6F3A', 'B'), f('2FE2', 'A')];
pysimFsSortChildren(children, 'fid');
assert.deepStrictEqual(children.map(x => x.fid), ['6F3A', '2FE2']);
});