Skip to content

Commit 621ae67

Browse files
k4cper-gclaude
andcommitted
Map non-schema ARIA roles to CUP equivalents in web adapter
The web adapter's CUP_ROLES set contained 16 ARIA roles not in the CUP schema enum (article, definition, directory, feed, figure, gridcell, listbox, math, meter, note, pane, presentation, radiogroup, rowgroup, term, treegrid). These would pass through as-is and fail schema validation. Fix: restrict CUP_ROLES to the 59 schema roles and add a NON_SCHEMA_ROLE_MAP fallback that maps each extra role to its closest CUP equivalent. Also remove listbox from the Role type union. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 39e4c20 commit 621ae67

2 files changed

Lines changed: 41 additions & 17 deletions

File tree

src/platforms/web.ts

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -162,39 +162,60 @@ const CDP_ROLE_MAP: Record<string, string> = {
162162
radioButton: "radio",
163163
scrollBar: "scrollbar",
164164
Summary: "button",
165-
Meter: "progressbar",
165+
Meter: "progressbar", // meter → progressbar (both show value in range)
166166
Output: "status",
167-
Figure: "figure",
167+
Figure: "group",
168168
Canvas: "img",
169169
Video: "generic",
170170
Audio: "generic",
171171
Section: "generic",
172172
};
173173

174+
// Canonical CUP roles (59) — matches the schema enum exactly.
175+
// Non-schema ARIA roles are mapped to CUP equivalents below.
174176
const CUP_ROLES = new Set([
175-
"alert", "alertdialog", "application", "article", "banner", "button",
177+
"alert", "alertdialog", "application", "banner", "button",
176178
"cell", "checkbox", "columnheader", "combobox", "complementary",
177-
"contentinfo", "definition", "dialog", "directory", "document", "feed",
178-
"figure", "form", "generic", "grid", "gridcell", "group", "heading",
179-
"img", "link", "list", "listbox", "listitem", "log", "main", "marquee",
180-
"math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio",
181-
"meter", "navigation", "none", "note", "option", "pane", "presentation",
182-
"progressbar", "radio", "radiogroup", "region", "row", "rowgroup",
183-
"rowheader", "scrollbar", "search", "searchbox", "separator", "slider",
184-
"spinbutton", "status", "switch", "tab", "table", "tablist", "tabpanel",
185-
"term", "text", "textbox", "timer", "toolbar", "tooltip", "tree",
186-
"treegrid", "treeitem", "window",
179+
"contentinfo", "dialog", "document", "form", "generic", "grid",
180+
"group", "heading", "img", "link", "list", "listitem", "log",
181+
"main", "marquee", "menu", "menubar", "menuitem", "menuitemcheckbox",
182+
"menuitemradio", "navigation", "none", "option", "progressbar", "radio",
183+
"region", "row", "rowheader", "scrollbar", "search", "searchbox",
184+
"separator", "slider", "spinbutton", "status", "switch", "tab", "table",
185+
"tablist", "tabpanel", "text", "textbox", "timer", "titlebar", "toolbar",
186+
"tooltip", "tree", "treeitem", "window",
187187
]);
188188

189+
// Non-schema ARIA roles → closest CUP equivalent.
190+
// CDP sometimes returns these as role names directly (lowercase).
191+
const NON_SCHEMA_ROLE_MAP: Record<string, string> = {
192+
article: "region",
193+
definition: "text",
194+
directory: "list",
195+
feed: "list",
196+
figure: "group",
197+
gridcell: "cell",
198+
listbox: "list",
199+
math: "generic",
200+
meter: "progressbar",
201+
note: "region",
202+
pane: "generic",
203+
presentation: "none",
204+
radiogroup: "group",
205+
rowgroup: "group",
206+
term: "text",
207+
treegrid: "grid",
208+
};
209+
189210
const TEXT_INPUT_ROLES = new Set(["textbox", "searchbox", "combobox", "spinbutton"]);
190211
const CLICKABLE_ROLES = new Set([
191212
"button", "link", "menuitem", "menuitemcheckbox", "menuitemradio", "option", "tab",
192213
]);
193214
const SELECTABLE_ROLES = new Set([
194-
"option", "tab", "treeitem", "listitem", "row", "cell", "gridcell",
215+
"option", "tab", "treeitem", "listitem", "row", "cell",
195216
]);
196217
const TOGGLE_ROLES = new Set(["checkbox", "switch", "menuitemcheckbox"]);
197-
const RANGE_ROLES = new Set(["slider", "spinbutton", "progressbar", "scrollbar", "meter"]);
218+
const RANGE_ROLES = new Set(["slider", "spinbutton", "progressbar", "scrollbar"]);
198219

199220
function mapCdpRole(cdpRole: string, name: string): string | null {
200221
if (SKIP_ROLES.has(cdpRole)) return null;
@@ -208,6 +229,10 @@ function mapCdpRole(cdpRole: string, name: string): string | null {
208229
const lower = cdpRole.toLowerCase();
209230
if (CUP_ROLES.has(lower)) return lower;
210231

232+
// Map non-schema ARIA roles to CUP equivalents
233+
const mapped = NON_SCHEMA_ROLE_MAP[lower];
234+
if (mapped) return mapped;
235+
211236
return "generic";
212237
}
213238

@@ -349,7 +374,7 @@ function axValue(field: unknown): unknown {
349374

350375
const VALUE_NODE_ROLES = new Set([
351376
"textbox", "searchbox", "combobox", "spinbutton", "slider",
352-
"progressbar", "meter", "document",
377+
"progressbar", "document",
353378
]);
354379

355380
function buildCupNode(

src/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ export type Role =
7070
| "img"
7171
| "link"
7272
| "list"
73-
| "listbox"
7473
| "listitem"
7574
| "log"
7675
| "main"

0 commit comments

Comments
 (0)