Skip to content

Commit bf5ed00

Browse files
committed
test: add Phase 3 audit fixture translating no-redundant-roles peer cases
Translates 35 cases from peer-plugin rules: - jsx-a11y no-redundant-roles - vuejs-accessibility no-redundant-roles - lit-a11y no-redundant-role Fixture documents parity after this fix: - Case-insensitive role comparison (body role="DOCUMENT" now flagged). - Default <select role="combobox"> flagged; <select multiple>/size>1 still valid (implicit listbox, not combobox). Remaining divergences (ul/ol role="list", <a role="link"> without href) are annotated inline. Not wired into the default vitest run.
1 parent 802c738 commit bf5ed00

1 file changed

Lines changed: 210 additions & 0 deletions

File tree

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
// Audit fixture — peer-plugin parity for `ember/template-no-redundant-role`.
2+
//
3+
// Source files (context/ checkouts):
4+
// - eslint-plugin-jsx-a11y-main/src/rules/no-redundant-roles.js
5+
// - eslint-plugin-jsx-a11y-main/__tests__/src/rules/no-redundant-roles-test.js
6+
// - eslint-plugin-vuejs-accessibility-main/src/rules/no-redundant-roles.ts
7+
// - eslint-plugin-lit-a11y/lib/rules/no-redundant-role.js
8+
//
9+
// These tests are NOT part of the main suite and do not run in CI. They encode
10+
// the CURRENT behavior of our rule. Each divergence from an upstream plugin is
11+
// annotated as "DIVERGENCE —".
12+
13+
'use strict';
14+
15+
const rule = require('../../../lib/rules/template-no-redundant-role');
16+
const RuleTester = require('eslint').RuleTester;
17+
18+
const ruleTester = new RuleTester({
19+
parser: require.resolve('ember-eslint-parser'),
20+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
21+
});
22+
23+
ruleTester.run('audit:no-redundant-roles (gts)', rule, {
24+
valid: [
25+
// === Upstream parity (valid in all plugins + ours) ===
26+
// No role attribute.
27+
'<template><div></div></template>',
28+
// Role differs from implicit.
29+
'<template><button role="link"></button></template>',
30+
'<template><button role="main"></button></template>',
31+
// jsx-a11y/lit-a11y default exception: nav[role="navigation"] is allowed.
32+
// Our ALLOWED_ELEMENT_ROLES also permits this.
33+
'<template><nav role="navigation"></nav></template>',
34+
// form[role="search"] — different from implicit "form" role.
35+
'<template><form role="search"></form></template>',
36+
// Dynamic role — we skip.
37+
'<template><footer role={{this.foo}}></footer></template>',
38+
39+
// === DIVERGENCE — <ul role="list"> / <ol role="list"> ===
40+
// jsx-a11y: INVALID — implicit role of ul/ol is "list".
41+
// Our rule: VALID — ALLOWED_ELEMENT_ROLES explicitly permits these.
42+
// Rationale (ember-template-lint carry-over): `role="list"` on ul/ol is
43+
// a deliberate workaround for Safari/VoiceOver stripping list semantics
44+
// when `list-style: none` is applied. This is a well-known pattern.
45+
'<template><ul role="list"></ul></template>',
46+
'<template><ol role="list"></ol></template>',
47+
48+
// === DIVERGENCE — <a role="link"> ===
49+
// jsx-a11y: INVALID only if <a> has href (implicit role "link" requires href).
50+
// Without href, <a> has no implicit role — so <a role="link"> is VALID.
51+
// Our rule: VALID regardless — ALLOWED_ELEMENT_ROLES includes {a, link}.
52+
// The user's existing test treats this as valid, so we encode that.
53+
'<template><a role="link" aria-disabled="true">valid</a></template>',
54+
55+
// === DIVERGENCE — <input role="combobox"> ===
56+
// jsx-a11y: implicit role depends on `type`. Default <input> (type=text)
57+
// has implicit "textbox". So <input role="combobox"> would be VALID.
58+
// Our rule: VALID — ALLOWED_ELEMENT_ROLES includes {input, combobox}.
59+
// Parity by coincidence.
60+
'<template><input role="combobox" /></template>',
61+
62+
// === Parity — <select role="combobox"> gated on multiple/size ===
63+
// jsx-a11y: INVALID — default <select> has implicit role "combobox".
64+
// Our rule: now INVALID for a default <select> (captured in invalid
65+
// section below). Stays VALID when `multiple` or `size > 1` is set —
66+
// those cases give <select> an implicit role of "listbox", so an
67+
// explicit "combobox" is a genuine role override, not a redundancy.
68+
'<template><select role="combobox" multiple><option>1</option></select></template>',
69+
'<template><select role="combobox" size="5"><option>1</option></select></template>',
70+
],
71+
72+
invalid: [
73+
// === Upstream parity (invalid in jsx-a11y + ours) ===
74+
{
75+
code: '<template><dialog role="dialog" /></template>',
76+
output: '<template><dialog /></template>',
77+
errors: [{ message: 'Use of redundant or invalid role: dialog on <dialog> detected.' }],
78+
},
79+
{
80+
code: '<template><button role="button"></button></template>',
81+
output: '<template><button></button></template>',
82+
errors: [{ message: 'Use of redundant or invalid role: button on <button> detected.' }],
83+
},
84+
{
85+
code: '<template><img role="img" /></template>',
86+
output: '<template><img /></template>',
87+
errors: [{ message: 'Use of redundant or invalid role: img on <img> detected.' }],
88+
},
89+
{
90+
code: '<template><body role="document"></body></template>',
91+
output: '<template><body></body></template>',
92+
errors: [{ message: 'Use of redundant or invalid role: document on <body> detected.' }],
93+
},
94+
// Landmark elements — error message is the "landmark" variant for us.
95+
{
96+
code: '<template><header role="banner"></header></template>',
97+
output: '<template><header></header></template>',
98+
errors: [
99+
{
100+
message:
101+
'Use of redundant or invalid role: banner on <header> detected. If a landmark element is used, any role provided will either be redundant or incorrect.',
102+
},
103+
],
104+
},
105+
{
106+
code: '<template><main role="main"></main></template>',
107+
output: '<template><main></main></template>',
108+
errors: [
109+
{
110+
message:
111+
'Use of redundant or invalid role: main on <main> detected. If a landmark element is used, any role provided will either be redundant or incorrect.',
112+
},
113+
],
114+
},
115+
{
116+
code: '<template><aside role="complementary"></aside></template>',
117+
output: '<template><aside></aside></template>',
118+
errors: [
119+
{
120+
message:
121+
'Use of redundant or invalid role: complementary on <aside> detected. If a landmark element is used, any role provided will either be redundant or incorrect.',
122+
},
123+
],
124+
},
125+
{
126+
code: '<template><footer role="contentinfo"></footer></template>',
127+
output: '<template><footer></footer></template>',
128+
errors: [
129+
{
130+
message:
131+
'Use of redundant or invalid role: contentinfo on <footer> detected. If a landmark element is used, any role provided will either be redundant or incorrect.',
132+
},
133+
],
134+
},
135+
136+
// === Parity — case-insensitive role comparison ===
137+
// jsx-a11y (via getExplicitRole), vuejs-accessibility via aria-query,
138+
// and now our rule all lowercase the role value before lookup.
139+
// `<body role="DOCUMENT">` is flagged.
140+
{
141+
code: '<template><body role="DOCUMENT"></body></template>',
142+
output: '<template><body></body></template>',
143+
errors: [{ message: 'Use of redundant or invalid role: document on <body> detected.' }],
144+
},
145+
146+
// === Parity — <select role="combobox"> (default <select>) ===
147+
// Default <select> (no multiple/size) has implicit role "combobox"
148+
// per HTML-AAM; explicit `role="combobox"` is redundant.
149+
{
150+
code: '<template><select role="combobox"><option>1</option></select></template>',
151+
output: '<template><select><option>1</option></select></template>',
152+
errors: [{ message: 'Use of redundant or invalid role: combobox on <select> detected.' }],
153+
},
154+
],
155+
});
156+
157+
const hbsRuleTester = new RuleTester({
158+
parser: require.resolve('ember-eslint-parser/hbs'),
159+
parserOptions: { ecmaVersion: 2022, sourceType: 'module' },
160+
});
161+
162+
hbsRuleTester.run('audit:no-redundant-roles (hbs)', rule, {
163+
valid: [
164+
'<div></div>',
165+
'<button role="main"></button>',
166+
'<nav role="navigation"></nav>',
167+
'<form role="search"></form>',
168+
// DIVERGENCE — ul/ol list kept valid by design (see gts section).
169+
'<ul role="list"></ul>',
170+
'<ol role="list"></ol>',
171+
// DIVERGENCE — <a role="link"> kept valid by design.
172+
'<a role="link">x</a>',
173+
// Parity — <select role="combobox" multiple> stays valid (implicit listbox).
174+
'<select role="combobox" multiple><option>1</option></select>',
175+
],
176+
invalid: [
177+
{
178+
code: '<button role="button"></button>',
179+
output: '<button></button>',
180+
errors: [{ message: 'Use of redundant or invalid role: button on <button> detected.' }],
181+
},
182+
{
183+
code: '<img role="img" />',
184+
output: '<img />',
185+
errors: [{ message: 'Use of redundant or invalid role: img on <img> detected.' }],
186+
},
187+
{
188+
code: '<main role="main"></main>',
189+
output: '<main></main>',
190+
errors: [
191+
{
192+
message:
193+
'Use of redundant or invalid role: main on <main> detected. If a landmark element is used, any role provided will either be redundant or incorrect.',
194+
},
195+
],
196+
},
197+
// Parity — case-insensitive comparison (jsx-a11y also flags).
198+
{
199+
code: '<body role="DOCUMENT"></body>',
200+
output: '<body></body>',
201+
errors: [{ message: 'Use of redundant or invalid role: document on <body> detected.' }],
202+
},
203+
// Parity — default <select> implicit role is combobox.
204+
{
205+
code: '<select role="combobox"><option>1</option></select>',
206+
output: '<select><option>1</option></select>',
207+
errors: [{ message: 'Use of redundant or invalid role: combobox on <select> detected.' }],
208+
},
209+
],
210+
});

0 commit comments

Comments
 (0)