Tests: broaden dialog accessibility audit + tab-order cycle check
Local checkpoint - NOT for public release. - The accessibility audit now covers 8 dialogs (was 3): Recording settings, Preferences, Service profile, About, Add EQ band, Rename peer, Keyboard shortcut import, Profile selection. Each is constructed and checked for accessible names on every control + unique Alt mnemonics per group. - Added a tab-order sanity check to the audit: the GetNextControl walk must terminate (no cycle) for every audited form - guards a keyboard/screen-reader user from a Tab trap, and the wrapper-TabIndex trap we've hit before. Deferred (documented): auditing the MAIN WINDOW's tabs needs a headless construction seam - MainForm's ctor opens audio backends, registers global hotkeys, binds sockets and shows a tray icon, interleaved through the ctor. A `headless` flag that skips those is safe in principle (real path unchanged) but invasive on the critical startup path and best added with Ed able to test it. The dialog surface (most of the app's controls) is now covered. Gate 21/21. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b258bec9a2
commit
47e4159cf9
@@ -879,6 +879,12 @@ internal static class SelfTest
|
||||
() => (default(RouterMappingStatus), (IPEndPoint?)null, ""),
|
||||
_ => { }, _ => { })),
|
||||
("Service profile", () => new ServiceProfileDialog(RemSound.Core.Profile.NewBlank(), false)),
|
||||
("About", () => new AboutDialog()),
|
||||
("Add EQ band", () => new AddBandDialog()),
|
||||
("Rename peer", () => new RenamePeerDialog("TestMachine", null)),
|
||||
("Keyboard shortcut import", () => new KeyboardShortcutImportDialog(Array.Empty<string>())),
|
||||
("Profile selection", () => new ProfileSelectionDialog(new ProfileStore(
|
||||
Path.Combine(Path.GetTempPath(), "remsound-selftest-picker-" + Guid.NewGuid().ToString("N"))))),
|
||||
};
|
||||
|
||||
var audited = new List<string>();
|
||||
@@ -927,6 +933,17 @@ internal static class SelfTest
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
violations.Add($"{formName}: a {c.GetType().Name} has no accessible name or text");
|
||||
}
|
||||
|
||||
// Tab-order sanity: the GetNextControl walk must TERMINATE — a cycle would trap a keyboard /
|
||||
// screen-reader user pressing Tab forever. Guards against a malformed tab order.
|
||||
var guard = 0;
|
||||
var seen = new HashSet<Control>();
|
||||
for (Control? cur = form.GetNextControl(form, true); cur is not null && guard < 10000; cur = form.GetNextControl(cur, true))
|
||||
{
|
||||
guard++;
|
||||
if (!seen.Add(cur)) { violations.Add($"{formName}: tab order forms a cycle at {cur.GetType().Name}"); break; }
|
||||
}
|
||||
if (guard >= 10000) violations.Add($"{formName}: tab-order walk did not terminate");
|
||||
}
|
||||
|
||||
/// <summary>Extract the Alt mnemonic letter from a WinForms caption ('&X' marks X; '&&'
|
||||
|
||||
Reference in New Issue
Block a user