fix(a11y,test): deep-review round 2 — e2e helper attr, trigger names, observer test

Second gate round over the full branch surfaced 4 verified findings (1 was a
regression the round-1 role change introduced); a 5th (--sbw load jump) was
dropped as refuted (the header only mounts after auth+data, so --sbw is set
before it ever paints).

- e2e (medium): the round-1 switch of FilterSelect rows from role=option to
  menuitemcheckbox moved their state from aria-selected to aria-checked, but
  the Playwright helper expectContentType still asserted aria-selected, so the
  channel specs would fail whenever the e2e lane runs (not part of the fast
  gate, so it stayed green). Assert aria-checked.
- a11y (low): the FilterSelect and SortControl pill triggers set
  aria-label={label}, which overrides the visible "Label: value" text, so a
  screen reader announced only the group name, never the current selection.
  Carry the value (and, for sort, the direction) in the accessible name,
  matching ViewSwitcher. Updated the unit-test trigger name queries to match.
- tests (low): cover the PageShell font-scale MutationObserver recompute path
  (a post-mount root font-size change → --rail-pinned-w re-computes once, no
  loop) — previously only the pre-render and resize paths were exercised.

Verified live: the triggers now announce e.g. "Sort: Date, Descending" /
"Watch state: All". Gate green: tsc (app+node+e2e), 373 vitest, prettier,
eslint (0 err).
This commit is contained in:
2026-08-22 01:06:22 +02:00
parent 4ee452b854
commit 5e1cc4d981
6 changed files with 31 additions and 11 deletions
@@ -48,7 +48,7 @@ function renderControl(sort: SortState, extra: Record<string, unknown> = {}) {
return { onChange };
}
const openPanel = () => fireEvent.click(screen.getByRole("button", { name: "Sort" }));
const openPanel = () => fireEvent.click(screen.getByRole("button", { name: /^Sort/ }));
afterEach(cleanup);
@@ -71,7 +71,7 @@ describe("SortControl", () => {
it("summarises the trigger with the first option (not blank) for an unavailable key without defaultLabel", () => {
renderControl({ key: "ghost", dir: "asc" });
expect(screen.getByRole("button", { name: "Sort" }).textContent).toContain("Date");
expect(screen.getByRole("button", { name: /^Sort/ }).textContent).toContain("Date");
});
it("picking a new option fires onChange at that option's defaultDir", () => {