Skip to content

Commit ef99ec7

Browse files
committed
test: add Phase 3 audit fixture translating role-supports-aria-props peer cases
Translates 33 cases from peer-plugin rules: - jsx-a11y role-supports-aria-props - lit-a11y role-supports-aria-attr - vuejs-accessibility (no direct equivalent; divergence noted) The fixture documents where our rule now matches jsx-a11y (notably <input type="email"> without `list` mapping to "textbox" per the aria-query attribute constraints) plus the sibling `list=…` case mapping to "combobox".
1 parent 26555dd commit ef99ec7

1 file changed

Lines changed: 209 additions & 0 deletions

File tree

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
// Audit fixture — peer-plugin parity for
2+
// `ember/template-no-unsupported-role-attributes`.
3+
//
4+
// Source files (context/ checkouts):
5+
// - eslint-plugin-jsx-a11y-main/src/rules/role-supports-aria-props.js
6+
// - eslint-plugin-jsx-a11y-main/__tests__/src/rules/role-supports-aria-props-test.js
7+
// - eslint-plugin-lit-a11y/lib/rules/role-supports-aria-attr.js
8+
// - eslint-plugin-lit-a11y/tests/lib/rules/role-supports-aria-attr.js
9+
//
10+
// These tests are NOT part of the main suite and do not run in CI. They encode
11+
// the CURRENT behavior of our rule. Each divergence from an upstream plugin is
12+
// annotated as "DIVERGENCE —".
13+
14+
'use strict';
15+
16+
const rule = require('../../../lib/rules/template-no-unsupported-role-attributes');
17+
const RuleTester = require('eslint').RuleTester;
18+
19+
const ruleTester = new RuleTester({
20+
parser: require.resolve('ember-eslint-parser'),
21+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
22+
});
23+
24+
ruleTester.run('audit:role-supports-aria-props (gts)', rule, {
25+
valid: [
26+
// === Upstream parity (valid in jsx-a11y + ours) ===
27+
'<template><div /></template>',
28+
'<template><div id="main" /></template>',
29+
30+
// Explicit role with supported attr.
31+
'<template><div role="heading" aria-level="1" /></template>',
32+
'<template><div role="button" aria-disabled="true" /></template>',
33+
'<template><div role="textbox" aria-required="true" aria-errormessage="err" /></template>',
34+
'<template><span role="checkbox" aria-checked={{this.checked}} /></template>',
35+
36+
// Implicit role tests that match between jsx-a11y and aria-query
37+
// (we rely on aria-query's elementRoles).
38+
// a with href — aria-query gives "generic" for first match; jsx-a11y
39+
// gives "link". Both happen to support aria-describedby etc.
40+
'<template><a href="#" aria-describedby="x"></a></template>',
41+
// input[type=submit] — implicit "button".
42+
'<template><input type="submit" aria-disabled="true" /></template>',
43+
// select — implicit "combobox".
44+
'<template><select aria-expanded="false" aria-controls="ctrlID" /></template>',
45+
// menu[type=toolbar] — aria-query gives "list"; jsx-a11y gives "toolbar".
46+
// aria-hidden is a global attr supported by both — valid in both.
47+
'<template><menu type="toolbar" aria-hidden="true" /></template>',
48+
49+
// Components / unknown elements are skipped.
50+
'<template><CustomComponent role="banner" /></template>',
51+
'<template><some-custom-element /></template>',
52+
53+
// Dynamic role (mustache value) — we skip.
54+
// jsx-a11y similarly skips non-literal role values.
55+
56+
// === DIVERGENCE — <a> without href ===
57+
// jsx-a11y: `<a>` without href has NO implicit role → uses global aria
58+
// set → `<a aria-checked />` is VALID.
59+
// Our rule: aria-query's first entry for `a` has no attribute constraint
60+
// and returns "generic". `generic` does not support aria-checked → we
61+
// would FLAG. (Invalid section captures this.)
62+
// Captured as the opposite: `<a aria-describedby="x">` passes in both
63+
// because aria-describedby is global.
64+
'<template><a aria-describedby="x"></a></template>',
65+
],
66+
invalid: [
67+
// === Upstream parity (invalid in jsx-a11y + ours) ===
68+
// Explicit role rejects unsupported attrs.
69+
{
70+
code: '<template><div role="link" href="#" aria-checked /></template>',
71+
output: '<template><div role="link" href="#" /></template>',
72+
errors: [{ messageId: 'unsupportedExplicit' }],
73+
},
74+
{
75+
code: '<template><div role="option" aria-notreal="x" aria-selected="false" /></template>',
76+
output: '<template><div role="option" aria-selected="false" /></template>',
77+
errors: [{ messageId: 'unsupportedExplicit' }],
78+
},
79+
{
80+
code: '<template><div role="combobox" aria-multiline="true" aria-expanded="false" aria-controls="x" /></template>',
81+
output: '<template><div role="combobox" aria-expanded="false" aria-controls="x" /></template>',
82+
errors: [{ messageId: 'unsupportedExplicit' }],
83+
},
84+
{
85+
code: '<template><a role="menuitem" aria-checked={{this.checked}} /></template>',
86+
output: '<template><a role="menuitem" /></template>',
87+
errors: [{ messageId: 'unsupportedExplicit' }],
88+
},
89+
90+
// Implicit role rejects unsupported attrs (parity).
91+
{
92+
code: '<template><button type="submit" aria-valuetext="x"></button></template>',
93+
output: '<template><button type="submit"></button></template>',
94+
errors: [{ messageId: 'unsupportedImplicit' }],
95+
},
96+
{
97+
code: '<template><input type="button" aria-invalid="grammar" /></template>',
98+
output: '<template><input type="button" /></template>',
99+
errors: [{ messageId: 'unsupportedImplicit' }],
100+
},
101+
102+
// === DIVERGENCE — role-name in message differs for <menu type="toolbar"> ===
103+
// jsx-a11y reports role "toolbar"; we report role "list".
104+
// Both FLAG though, so the divergence is cosmetic (message text).
105+
{
106+
code: '<template><menu type="toolbar" aria-expanded="true" /></template>',
107+
output: '<template><menu type="toolbar" /></template>',
108+
errors: [
109+
{
110+
message:
111+
'The attribute aria-expanded is not supported by the element menu with the implicit role of list',
112+
},
113+
],
114+
},
115+
116+
// === DIVERGENCE — role-name differs for <body> ===
117+
// jsx-a11y: <body> implicit role = "document".
118+
// Our rule: aria-query first match gives role "generic".
119+
// aria-expanded is unsupported by both, so both FLAG — diff is message.
120+
{
121+
code: '<template><body aria-expanded="true"></body></template>',
122+
output: '<template><body></body></template>',
123+
errors: [
124+
{
125+
message:
126+
'The attribute aria-expanded is not supported by the element body with the implicit role of generic',
127+
},
128+
],
129+
},
130+
131+
// === Parity — <input type="email"> without `list` → textbox ===
132+
// jsx-a11y considers these to be "textbox" (since aria-query's first
133+
// "email" entry has "list attribute not set" constraint → textbox).
134+
// Our rule now honors aria-query attribute constraints: `type=email`
135+
// without a `list` attribute maps to "textbox". With `list=...` it
136+
// maps to "combobox" (sibling case below).
137+
// aria-level is not supported by either role; still flagged.
138+
{
139+
code: '<template><input type="email" aria-level={{this.level}} /></template>',
140+
output: '<template><input type="email" /></template>',
141+
errors: [
142+
{
143+
message:
144+
'The attribute aria-level is not supported by the element input with the implicit role of textbox',
145+
},
146+
],
147+
},
148+
// <input type="email" list="x"> → "combobox" (aria-level unsupported there too).
149+
{
150+
code: '<template><input type="email" list="x" aria-level={{this.level}} /></template>',
151+
output: '<template><input type="email" list="x" /></template>',
152+
errors: [
153+
{
154+
message:
155+
'The attribute aria-level is not supported by the element input with the implicit role of combobox',
156+
},
157+
],
158+
},
159+
160+
// === DIVERGENCE — <a> without href, with non-global aria attr ===
161+
// jsx-a11y: VALID (no implicit role → global set).
162+
// Our rule: role=generic, aria-checked not supported → FLAG. FALSE POSITIVE.
163+
{
164+
code: '<template><a aria-checked /></template>',
165+
output: '<template><a /></template>',
166+
errors: [{ messageId: 'unsupportedImplicit' }],
167+
},
168+
],
169+
});
170+
171+
const hbsRuleTester = new RuleTester({
172+
parser: require.resolve('ember-eslint-parser/hbs'),
173+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
174+
});
175+
176+
hbsRuleTester.run('audit:role-supports-aria-props (hbs)', rule, {
177+
valid: [
178+
'<div />',
179+
'<div role="heading" aria-level="1" />',
180+
'<div role="button" aria-disabled="true" />',
181+
'<a href="#" aria-describedby=""></a>',
182+
'<menu type="toolbar" aria-hidden="true" />',
183+
'<input type="submit" aria-disabled="true" />',
184+
],
185+
invalid: [
186+
{
187+
code: '<div role="link" href="#" aria-checked />',
188+
output: '<div role="link" href="#" />',
189+
errors: [{ message: 'The attribute aria-checked is not supported by the role link' }],
190+
},
191+
{
192+
code: '<menu type="toolbar" aria-expanded="true" />',
193+
output: '<menu type="toolbar" />',
194+
errors: [
195+
{
196+
message:
197+
'The attribute aria-expanded is not supported by the element menu with the implicit role of list',
198+
},
199+
],
200+
},
201+
// DIVERGENCE: <a aria-checked /> — jsx-a11y says valid (no implicit role).
202+
// We flag with implicit role "generic".
203+
{
204+
code: '<a aria-checked />',
205+
output: '<a />',
206+
errors: [{ messageId: 'unsupportedImplicit' }],
207+
},
208+
],
209+
});

0 commit comments

Comments
 (0)