diff --git a/public/js/settingsAutosave.js b/public/js/settingsAutosave.js index 61bde58a..81993b42 100644 --- a/public/js/settingsAutosave.js +++ b/public/js/settingsAutosave.js @@ -60,7 +60,16 @@ try { var snap = JSON.parse(snapshot || '{}'); if (el.classList && el.classList.contains('toggle')) el.classList.toggle('on', !!snap[el.id]); - else if (el.type === 'checkbox' || el.type === 'radio') el.checked = !!snap[el.id]; + else if (el.type === 'radio') { + // Radio groups are keyed in valuesById by the group name (not per-element id, + // which grouped radios often lack). Restore the whole group to the snapshot value. + var want = snap[el.name]; + if (want != null) { + var group = el.name ? document.getElementsByName(el.name) : [el]; + for (var i = 0; i < group.length; i++) group[i].checked = (group[i].value === String(want)); + } + } + else if (el.type === 'checkbox') el.checked = !!snap[el.id]; else if (el.tagName === 'SELECT' && snap[el.id] != null) el.value = snap[el.id]; } catch (e) {} } diff --git a/tests/settings_autosave_smoke.test.js b/tests/settings_autosave_smoke.test.js index 5f001a8e..d9a0085a 100644 --- a/tests/settings_autosave_smoke.test.js +++ b/tests/settings_autosave_smoke.test.js @@ -112,6 +112,13 @@ test('resync mechanism: controller exposes resync and keeps a binds registry', a assert.match(ctrl.text, /function resync\(\)/); }); +test('rollbackField restores radio groups by name (not by missing element id)', async () => { + const ctrl = await supertest(app).get('/js/settingsAutosave.js').expect(200); + // grouped radios (e.g. au-mode) lack element ids; rollback must use the group name + assert.match(ctrl.text, /getElementsByName/); + assert.match(ctrl.text, /snap\[el\.name\]/); +}); + test('settings.js calls SettingsAutosave.resync for all async-populated clusters', async () => { const js = await supertest(app).get('/js/settings.js').expect(200); const clusters = ['smtp', 'security', 'data', 'monitoring', 'alerts', 'autobackup', 'metrics', 'dns', 'auto-update', 'split-tunnel', 'pihole', 'portal', 'route-block'];