From f3096d195b3627cb7f48069965f58c3eaa3b1db9 Mon Sep 17 00:00:00 2001 From: PJ Date: Sat, 15 Aug 2026 12:46:13 +0530 Subject: [PATCH] test(browser): tap a role-based control end to end every control on the page is an
  • , the shape the replay ui gives its step rows, and the spec carries no action of its own: the property firing is the evidence the default enumeration offered a tap on one. --- test/browser/browser_test.go | 12 +++++++++ test/browser/testdata/aria-roles/index.html | 29 +++++++++++++++++++++ test/browser/testdata/aria-roles/spec.ts | 16 ++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 test/browser/testdata/aria-roles/index.html create mode 100644 test/browser/testdata/aria-roles/spec.ts diff --git a/test/browser/browser_test.go b/test/browser/browser_test.go index efc62d9..3cae9cd 100644 --- a/test/browser/browser_test.go +++ b/test/browser/browser_test.go @@ -65,6 +65,18 @@ func TestBrowserShadowDOMIsReachable(t *testing.T) { } } +// TestBrowserAriaRoleIsTappable drives a page whose only controls are +//
  • rows, the shape the replay UI gives its step list. The +// tappable set covered role="button" and nothing else, so a spec had to +// hand-write an action to reach a row; with the standard interactive roles +// covered, the default tap enumeration finds them and the counter moves. +func TestBrowserAriaRoleIsTappable(t *testing.T) { + violations := runFixture(t, "aria-roles") + if !slices.Contains(violations, "noRowWasEverSelected") { + t.Fatalf("no role=\"option\" row was ever tapped; violations=%v", violations) + } +} + // runFixture serves the named testdata case over an in-process file server, // drives it through headless Chrome with a fixed seed and a bounded step count, // and returns every property name that was ever reported violated. diff --git a/test/browser/testdata/aria-roles/index.html b/test/browser/testdata/aria-roles/index.html new file mode 100644 index 0000000..40d9696 --- /dev/null +++ b/test/browser/testdata/aria-roles/index.html @@ -0,0 +1,29 @@ + + + + + aria roles + + + +
      +
    1. step 1
    2. +
    3. step 2
    4. +
    5. step 3
    6. +
    +
    0
    + + + diff --git a/test/browser/testdata/aria-roles/spec.ts b/test/browser/testdata/aria-roles/spec.ts new file mode 100644 index 0000000..066edd6 --- /dev/null +++ b/test/browser/testdata/aria-roles/spec.ts @@ -0,0 +1,16 @@ +import { always, extract, taps } from "@sanderling/spec"; + +const selections = extract((s) => { + const el = s.ax.find({ id: "selected" }); + return el ? parseInt(el.text, 10) || 0 : 0; +}).named("selections"); + +// Every control on the page is an
  • , the shape the replay UI +// gives its own step rows. Nothing else can move this counter, so the spec +// carries no action of its own: this property firing IS the evidence that the +// default enumeration offered a tap on a role-based control. +const noRowWasEverSelected = always(() => selections.current === 0); + +export const properties = { noRowWasEverSelected }; + +export const actionsRoot = taps;