diff --git a/frontend/index.html b/frontend/index.html
index 9755398..132f3f8 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -5940,14 +5940,10 @@ async function pysimFsRefresh() {
async function pysimFsLoadChildren(node) {
if (node.children) return;
try {
- let body = { name: node.name, fid: node.fid };
+ const body = { name: node.name, fid: node.fid };
const parentSel = getParentSel(node);
if (parentSel) body.parent_sel = parentSel;
- let data = await pysimFetch('/api/tree', body);
- if (!data || data.exists === false) {
- body = { name: node.name, fid: node.fid };
- data = await pysimFetch('/api/tree', body);
- }
+ const data = await pysimFetch('/api/tree', body);
if (!data || data.exists === false || data.success === false || data.error) {
throw new Error((data && data.error) || 'File not found');
}
@@ -5965,7 +5961,6 @@ async function pysimFsLoadChildren(node) {
pysimCustomInject(node);
} catch (e) {
node.exists = false;
- node.children = [];
pysimFsRenderTree();
}
}
@@ -6007,7 +6002,7 @@ async function pysimFsProbeAll() {
if (probe.stop) return;
if (!child.isDir) continue;
await pysimFsLoadChildren(child);
- const exists = child.exists !== false;
+ const exists = child.exists === true;
probe.dirs++;
count(exists);
pysimFsRenderTree();
@@ -6019,7 +6014,7 @@ async function pysimFsProbeAll() {
const files = [];
const collect = node => {
for (const child of (node.children || [])) {
- if (child.isDir) { if (child.exists !== false) collect(child); }
+ if (child.isDir) { if (child.exists === true) collect(child); }
else files.push(child);
}
};
@@ -6031,7 +6026,6 @@ async function pysimFsProbeAll() {
const data = await pysimFetch('/api/select', pysimFsSelectBody(file));
const exists = !!(data && data.exists !== false && !data.error);
file.exists = exists;
- if (!exists) file.children = [];
count(exists);
probe.done++;
pysimFsProbeUi(t('Probing:') + ' ' + probe.done + '/' + probe.total, true);
diff --git a/frontend/sw.js b/frontend/sw.js
index aa2a56f..3939048 100644
--- a/frontend/sw.js
+++ b/frontend/sw.js
@@ -1,4 +1,4 @@
-const CACHE = 'otaman-v116';
+const CACHE = 'otaman-v117';
const URLS = [
'index.html',
'help.html',
diff --git a/frontend/tests/fs_load.test.js b/frontend/tests/fs_load.test.js
index 408f5a1..9ead305 100644
--- a/frontend/tests/fs_load.test.js
+++ b/frontend/tests/fs_load.test.js
@@ -54,9 +54,9 @@ test('tree error payload marks the directory as absent', async () => {
];
await pysimFsLoadChildren(node);
assert.strictEqual(node.exists, false);
- assert.deepStrictEqual(node.children, []);
+ assert.strictEqual(node.children, null);
assert.strictEqual(renders, 1);
- assert.strictEqual(calls.length, 2);
+ assert.strictEqual(calls.length, 1);
assert.strictEqual(calls[0].body.parent_sel, 'MF');
});
@@ -68,23 +68,28 @@ test('error payload without exists is not treated as an empty listing', async ()
];
await pysimFsLoadChildren(node);
assert.strictEqual(node.exists, false);
- assert.deepStrictEqual(node.children, []);
+ assert.strictEqual(node.children, null);
assert.strictEqual(renders, 1);
assert.strictEqual(calls.length, 1);
});
-test('retry without parent_sel succeeds and maps children', async () => {
+test('a 200 exists:false response marks the directory absent without retrying', async () => {
const node = setup();
- responses = [
- { exists: false },
- { exists: true, children: [{ name: 'EF.IMSI', fid: '6f07', isDir: false }] },
- ];
+ responses = [{ exists: false }];
+ await pysimFsLoadChildren(node);
+ assert.strictEqual(node.exists, false);
+ assert.strictEqual(node.children, null);
+ assert.strictEqual(calls.length, 1);
+});
+
+test('a node with loaded children is not fetched again', async () => {
+ const node = setup();
+ node.exists = true;
+ node.children = [{ name: 'EF.UPLMNWLAN', fid: '4f42', isDir: false, exists: true }];
await pysimFsLoadChildren(node);
assert.strictEqual(node.exists, true);
assert.strictEqual(node.children.length, 1);
- assert.strictEqual(node.children[0].parent, node);
- assert.strictEqual(node.children[0].exists, true);
- assert.strictEqual(calls[1].body.parent_sel, undefined);
+ assert.strictEqual(calls.length, 0);
});
test('empty successful listing keeps the directory present', async () => {
diff --git a/frontend/tests/fs_probe.test.js b/frontend/tests/fs_probe.test.js
index a44ff86..1cd100a 100644
--- a/frontend/tests/fs_probe.test.js
+++ b/frontend/tests/fs_probe.test.js
@@ -104,7 +104,7 @@ test('an absent directory is marked and its subtree is never fetched', async ()
await pysimFsProbeAll();
assert.strictEqual(pysimFsTreeRoot.children[0].exists, false);
assert.deepStrictEqual(selectNames(), ['EF.ROOT']);
- assert.strictEqual(calls.filter(c => c.path === '/api/tree' && c.body.name === 'DF.B').length, 2);
+ assert.strictEqual(calls.filter(c => c.path === '/api/tree' && c.body.name === 'DF.B').length, 1);
const status = els['pysim-fs-probe-status'].textContent;
assert.match(status, /2\/2 files/);
assert.match(status, /0 present/);
@@ -122,3 +122,22 @@ test('stop halts the walk and still reports a summary', async () => {
assert.ok(status.startsWith('Stopped —'), status);
assert.strictEqual(els['pysim-fs-probe-btn'].textContent, 'Probe all files');
});
+
+test('children of an absent directory are neither fetched nor selected', async () => {
+ const stale = Object.assign(df('DF.C', '5f03'), {
+ exists: false,
+ children: [{ name: 'EF.STALE', fid: '6f0e', isDir: false, exists: true, children: null }],
+ });
+ root([stale, ef('EF.ROOT', '2f01')]);
+ setup([
+ { path: '/api/select', name: 'EF.ROOT', reply: { exists: true } },
+ ]);
+ await pysimFsProbeAll();
+ assert.deepStrictEqual(selectNames(), ['EF.ROOT']);
+ assert.strictEqual(stale.exists, false);
+ assert.strictEqual(calls.filter(c => c.path === '/api/tree').length, 0);
+ const status = els['pysim-fs-probe-status'].textContent;
+ assert.match(status, /2\/2 files/);
+ assert.match(status, /1 present/);
+ assert.match(status, /1 absent/);
+});
diff --git a/frontend/tests/fs_render.test.js b/frontend/tests/fs_render.test.js
index e938c23..464a9be 100644
--- a/frontend/tests/fs_render.test.js
+++ b/frontend/tests/fs_render.test.js
@@ -56,3 +56,12 @@ test('collapsed directory hides its children', () => {
const out = pysimFsRenderNode(node, 0);
assert.ok(!out.includes('EF.IMSI'), out);
});
+
+test('non-existing directory hides previously loaded children', () => {
+ const node = df('DF.WLAN', '5f40', { exists: false, expanded: true, children: [ef('EF.UPLMNWLAN', '4f42')] });
+ const out = pysimFsRenderNode(node, 0);
+ assert.ok(out.includes('✗'), out);
+ assert.ok(!out.includes('EF.UPLMNWLAN'), out);
+ assert.ok(!out.includes('(empty)'), out);
+ assert.ok(!out.includes('pysimFsToggleDir'), out);
+});