source: frontend/node_modules/eslint-plugin-jsx-a11y/__tests__/src/rules/no-interactive-element-to-noninteractive-role-test.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 2 weeks ago

Fix frontend appearance

  • Property mode set to 100644
File size: 17.9 KB
Line 
1/**
2 * @fileoverview Disallow inherently interactive elements to be assigned
3 * non-interactive roles.
4 * @author Jesse Beach
5 */
6
7// -----------------------------------------------------------------------------
8// Requirements
9// -----------------------------------------------------------------------------
10
11import { RuleTester } from 'eslint';
12import { configs } from '../../../src/index';
13import parserOptionsMapper from '../../__util__/parserOptionsMapper';
14import parsers from '../../__util__/helpers/parsers';
15import rule from '../../../src/rules/no-interactive-element-to-noninteractive-role';
16import ruleOptionsMapperFactory from '../../__util__/ruleOptionsMapperFactory';
17
18// -----------------------------------------------------------------------------
19// Tests
20// -----------------------------------------------------------------------------
21const ruleTester = new RuleTester();
22
23const errorMessage = 'Interactive elements should not be assigned non-interactive roles.';
24
25const expectedError = {
26 message: errorMessage,
27 type: 'JSXAttribute',
28};
29
30const ruleName = 'jsx-a11y/no-interactive-element-to-noninteractive-role';
31
32const componentsSettings = {
33 'jsx-a11y': {
34 components: {
35 Button: 'button',
36 Link: 'a',
37 },
38 },
39};
40
41const alwaysValid = [
42 { code: '<TestComponent onClick={doFoo} />' },
43 { code: '<Button onClick={doFoo} />' },
44 /* Interactive elements */
45 { code: '<a href="http://x.y.z" role="button" />' },
46 { code: '<a href="http://x.y.z" tabIndex="0" role="button" />' },
47 { code: '<button className="foo" role="button" />' },
48 /* All flavors of input */
49 { code: '<input role="button" />' },
50 { code: '<input type="button" role="button" />' },
51 { code: '<input type="checkbox" role="button" />' },
52 { code: '<input type="color" role="button" />' },
53 { code: '<input type="date" role="button" />' },
54 { code: '<input type="datetime" role="button" />' },
55 { code: '<input type="datetime-local" role="button" />' },
56 { code: '<input type="email" role="button" />' },
57 { code: '<input type="file" role="button" />' },
58 { code: '<input type="image" role="button" />' },
59 { code: '<input type="month" role="button" />' },
60 { code: '<input type="number" role="button" />' },
61 { code: '<input type="password" role="button" />' },
62 { code: '<input type="radio" role="button" />' },
63 { code: '<input type="range" role="button" />' },
64 { code: '<input type="reset" role="button" />' },
65 { code: '<input type="search" role="button" />' },
66 { code: '<input type="submit" role="button" />' },
67 { code: '<input type="tel" role="button" />' },
68 { code: '<input type="text" role="button" />' },
69 { code: '<input type="time" role="button" />' },
70 { code: '<input type="url" role="button" />' },
71 { code: '<input type="week" role="button" />' },
72 { code: '<input type="hidden" role="button" />' },
73 /* End all flavors of input */
74 { code: '<menuitem role="button" />;' },
75 { code: '<option className="foo" role="button" />' },
76 { code: '<select className="foo" role="button" />' },
77 { code: '<textarea className="foo" role="button" />' },
78 { code: '<tr role="button" />;' },
79 /* HTML elements with neither an interactive or non-interactive valence (static) */
80 { code: '<a role="button" />' },
81 { code: '<a role="img" />;' },
82 { code: '<a tabIndex="0" role="button" />' },
83 { code: '<a tabIndex="0" role="img" />' },
84 { code: '<acronym role="button" />;' },
85 { code: '<address role="button" />;' },
86 { code: '<applet role="button" />;' },
87 { code: '<aside role="button" />;' },
88 { code: '<audio role="button" />;' },
89 { code: '<b role="button" />;' },
90 { code: '<base role="button" />;' },
91 { code: '<bdi role="button" />;' },
92 { code: '<bdo role="button" />;' },
93 { code: '<big role="button" />;' },
94 { code: '<blink role="button" />;' },
95 { code: '<blockquote role="button" />;' },
96 { code: '<body role="button" />;' },
97 { code: '<br role="button" />;' },
98 { code: '<canvas role="button" />;' },
99 { code: '<caption role="button" />;' },
100 { code: '<center role="button" />;' },
101 { code: '<cite role="button" />;' },
102 { code: '<code role="button" />;' },
103 { code: '<col role="button" />;' },
104 { code: '<colgroup role="button" />;' },
105 { code: '<content role="button" />;' },
106 { code: '<data role="button" />;' },
107 { code: '<datalist role="button" />;' },
108 { code: '<del role="button" />;' },
109 { code: '<details role="button" />;' },
110 { code: '<dir role="button" />;' },
111 { code: '<div role="button" />;' },
112 { code: '<div className="foo" role="button" />;' },
113 { code: '<div className="foo" {...props} role="button" />;' },
114 { code: '<div aria-hidden role="button" />;' },
115 { code: '<div aria-hidden={true} role="button" />;' },
116 { code: '<div role="button" />;' },
117 { code: '<div role={undefined} role="button" />;' },
118 { code: '<div {...props} role="button" />;' },
119 { code: '<div onKeyUp={() => void 0} aria-hidden={false} role="button" />;' },
120 { code: '<dl role="button" />;' },
121 { code: '<em role="button" />;' },
122 { code: '<embed role="button" />;' },
123 { code: '<figcaption role="button" />;' },
124 { code: '<font role="button" />;' },
125 { code: '<footer role="button" />;' },
126 { code: '<frameset role="button" />;' },
127 { code: '<head role="button" />;' },
128 { code: '<header role="button" />;' },
129 { code: '<hgroup role="button" />;' },
130 { code: '<html role="button" />;' },
131 { code: '<i role="button" />;' },
132 { code: '<iframe role="button" />;' },
133 { code: '<ins role="button" />;' },
134 { code: '<kbd role="button" />;' },
135 { code: '<keygen role="button" />;' },
136 { code: '<label role="button" />;' },
137 { code: '<legend role="button" />;' },
138 { code: '<link role="button" />;' },
139 { code: '<map role="button" />;' },
140 { code: '<mark role="button" />;' },
141 { code: '<marquee role="button" />;' },
142 { code: '<menu role="button" />;' },
143 { code: '<meta role="button" />;' },
144 { code: '<meter role="button" />;' },
145 { code: '<noembed role="button" />;' },
146 { code: '<noscript role="button" />;' },
147 { code: '<object role="button" />;' },
148 { code: '<optgroup role="button" />;' },
149 { code: '<output role="button" />;' },
150 { code: '<p role="button" />;' },
151 { code: '<param role="button" />;' },
152 { code: '<picture role="button" />;' },
153 { code: '<pre role="button" />;' },
154 { code: '<progress role="button" />;' },
155 { code: '<q role="button" />;' },
156 { code: '<rp role="button" />;' },
157 { code: '<rt role="button" />;' },
158 { code: '<rtc role="button" />;' },
159 { code: '<ruby role="button" />;' },
160 { code: '<s role="button" />;' },
161 { code: '<samp role="button" />;' },
162 { code: '<script role="button" />;' },
163 { code: '<section role="button" />;' },
164 { code: '<small role="button" />;' },
165 { code: '<source role="button" />;' },
166 { code: '<spacer role="button" />;' },
167 { code: '<span role="button" />;' },
168 { code: '<strike role="button" />;' },
169 { code: '<strong role="button" />;' },
170 { code: '<style role="button" />;' },
171 { code: '<sub role="button" />;' },
172 { code: '<summary role="button" />;' },
173 { code: '<sup role="button" />;' },
174 { code: '<th role="button" />;' },
175 { code: '<time role="button" />;' },
176 { code: '<title role="button" />;' },
177 { code: '<track role="button" />;' },
178 { code: '<tt role="button" />;' },
179 { code: '<u role="button" />;' },
180 { code: '<var role="button" />;' },
181 { code: '<video role="button" />;' },
182 { code: '<wbr role="button" />;' },
183 { code: '<xmp role="button" />;' },
184 /* HTML elements attributed with an interactive role */
185 { code: '<div role="button" />;' },
186 { code: '<div role="checkbox" />;' },
187 { code: '<div role="columnheader" />;' },
188 { code: '<div role="combobox" />;' },
189 { code: '<div role="grid" />;' },
190 { code: '<div role="gridcell" />;' },
191 { code: '<div role="link" />;' },
192 { code: '<div role="listbox" />;' },
193 { code: '<div role="menu" />;' },
194 { code: '<div role="menubar" />;' },
195 { code: '<div role="menuitem" />;' },
196 { code: '<div role="menuitemcheckbox" />;' },
197 { code: '<div role="menuitemradio" />;' },
198 { code: '<div role="option" />;' },
199 { code: '<div role="progressbar" />;' },
200 { code: '<div role="radio" />;' },
201 { code: '<div role="radiogroup" />;' },
202 { code: '<div role="row" />;' },
203 { code: '<div role="rowheader" />;' },
204 { code: '<div role="searchbox" />;' },
205 { code: '<div role="slider" />;' },
206 { code: '<div role="spinbutton" />;' },
207 { code: '<div role="switch" />;' },
208 { code: '<div role="tab" />;' },
209 { code: '<div role="textbox" />;' },
210 { code: '<div role="treeitem" />;' },
211 /* Presentation is a special case role that indicates intentional static semantics */
212 { code: '<div role="presentation" />;' },
213 /* HTML elements attributed with an abstract role */
214 { code: '<div role="command" />;' },
215 { code: '<div role="composite" />;' },
216 { code: '<div role="input" />;' },
217 { code: '<div role="landmark" />;' },
218 { code: '<div role="range" />;' },
219 { code: '<div role="roletype" />;' },
220 { code: '<div role="section" />;' },
221 { code: '<div role="sectionhead" />;' },
222 { code: '<div role="select" />;' },
223 { code: '<div role="structure" />;' },
224 { code: '<div role="tablist" />;' },
225 { code: '<div role="toolbar" />;' },
226 { code: '<div role="tree" />;' },
227 { code: '<div role="treegrid" />;' },
228 { code: '<div role="widget" />;' },
229 { code: '<div role="window" />;' },
230 /* HTML elements with an inherent, non-interactive role, assigned an
231 * interactive role. */
232 { code: '<main role="button" />;' },
233 { code: '<area role="button" />;' },
234 { code: '<article role="button" />;' },
235 { code: '<article role="button" />;' },
236 { code: '<dd role="button" />;' },
237 { code: '<dfn role="button" />;' },
238 { code: '<dt role="button" />;' },
239 { code: '<fieldset role="button" />;' },
240 { code: '<figure role="button" />;' },
241 { code: '<form role="button" />;' },
242 { code: '<frame role="button" />;' },
243 { code: '<h1 role="button" />;' },
244 { code: '<h2 role="button" />;' },
245 { code: '<h3 role="button" />;' },
246 { code: '<h4 role="button" />;' },
247 { code: '<h5 role="button" />;' },
248 { code: '<h6 role="button" />;' },
249 { code: '<hr role="button" />;' },
250 { code: '<img role="button" />;' },
251 { code: '<li role="button" />;' },
252 { code: '<li role="presentation" />;' },
253 { code: '<nav role="button" />;' },
254 { code: '<ol role="button" />;' },
255 { code: '<table role="button" />;' },
256 { code: '<tbody role="button" />;' },
257 { code: '<td role="button" />;' },
258 { code: '<tfoot role="button" />;' },
259 { code: '<thead role="button" />;' },
260 { code: '<ul role="button" />;' },
261 /* HTML elements attributed with a non-interactive role */
262 { code: '<div role="alert" />;' },
263 { code: '<div role="alertdialog" />;' },
264 { code: '<div role="application" />;' },
265 { code: '<div role="article" />;' },
266 { code: '<div role="banner" />;' },
267 { code: '<div role="cell" />;' },
268 { code: '<div role="complementary" />;' },
269 { code: '<div role="contentinfo" />;' },
270 { code: '<div role="definition" />;' },
271 { code: '<div role="dialog" />;' },
272 { code: '<div role="directory" />;' },
273 { code: '<div role="document" />;' },
274 { code: '<div role="feed" />;' },
275 { code: '<div role="figure" />;' },
276 { code: '<div role="form" />;' },
277 { code: '<div role="group" />;' },
278 { code: '<div role="heading" />;' },
279 { code: '<div role="img" />;' },
280 { code: '<div role="list" />;' },
281 { code: '<div role="listitem" />;' },
282 { code: '<div role="log" />;' },
283 { code: '<div role="main" />;' },
284 { code: '<div role="marquee" />;' },
285 { code: '<div role="math" />;' },
286 { code: '<div role="navigation" />;' },
287 { code: '<div role="note" />;' },
288 { code: '<div role="region" />;' },
289 { code: '<div role="rowgroup" />;' },
290 { code: '<div role="search" />;' },
291 { code: '<div role="separator" />;' },
292 { code: '<div role="scrollbar" />;' },
293 { code: '<div role="status" />;' },
294 { code: '<div role="table" />;' },
295 { code: '<div role="tabpanel" />;' },
296 { code: '<div role="term" />;' },
297 { code: '<div role="timer" />;' },
298 { code: '<div role="tooltip" />;' },
299 /* Namespaced roles are not checked */
300 { code: '<div mynamespace:role="term" />' },
301 { code: '<input mynamespace:role="img" />' },
302 { code: '<Link href="http://x.y.z" role="img" />' },
303 { code: '<Link href="http://x.y.z" />', settings: componentsSettings },
304 { code: '<Button onClick={doFoo} />', settings: componentsSettings },
305];
306
307const neverValid = [
308 /* Interactive elements */
309 { code: '<a href="http://x.y.z" role="img" />', errors: [expectedError] },
310 { code: '<a href="http://x.y.z" tabIndex="0" role="img" />', errors: [expectedError] },
311 /* All flavors of input */
312 { code: '<input role="img" />', errors: [expectedError] },
313 { code: '<input type="img" role="img" />', errors: [expectedError] },
314 { code: '<input type="checkbox" role="img" />', errors: [expectedError] },
315 { code: '<input type="color" role="img" />', errors: [expectedError] },
316 { code: '<input type="date" role="img" />', errors: [expectedError] },
317 { code: '<input type="datetime" role="img" />', errors: [expectedError] },
318 { code: '<input type="datetime-local" role="img" />', errors: [expectedError] },
319 { code: '<input type="email" role="img" />', errors: [expectedError] },
320 { code: '<input type="file" role="img" />', errors: [expectedError] },
321 { code: '<input type="hidden" role="img" />', errors: [expectedError] },
322 { code: '<input type="image" role="img" />', errors: [expectedError] },
323 { code: '<input type="month" role="img" />', errors: [expectedError] },
324 { code: '<input type="number" role="img" />', errors: [expectedError] },
325 { code: '<input type="password" role="img" />', errors: [expectedError] },
326 { code: '<input type="radio" role="img" />', errors: [expectedError] },
327 { code: '<input type="range" role="img" />', errors: [expectedError] },
328 { code: '<input type="reset" role="img" />', errors: [expectedError] },
329 { code: '<input type="search" role="img" />', errors: [expectedError] },
330 { code: '<input type="submit" role="img" />', errors: [expectedError] },
331 { code: '<input type="tel" role="img" />', errors: [expectedError] },
332 { code: '<input type="text" role="img" />', errors: [expectedError] },
333 { code: '<input type="time" role="img" />', errors: [expectedError] },
334 { code: '<input type="url" role="img" />', errors: [expectedError] },
335 { code: '<input type="week" role="img" />', errors: [expectedError] },
336 /* End all flavors of input */
337 { code: '<menuitem role="img" />;', errors: [expectedError] },
338 { code: '<option className="foo" role="img" />', errors: [expectedError] },
339 { code: '<select className="foo" role="img" />', errors: [expectedError] },
340 { code: '<textarea className="foo" role="img" />', errors: [expectedError] },
341 { code: '<tr role="img" />;', errors: [expectedError] },
342 /* Interactive elements */
343 { code: '<a href="http://x.y.z" role="listitem" />', errors: [expectedError] },
344 { code: '<a href="http://x.y.z" tabIndex="0" role="listitem" />', errors: [expectedError] },
345 /* All flavors of input */
346 { code: '<input role="listitem" />', errors: [expectedError] },
347 { code: '<input type="listitem" role="listitem" />', errors: [expectedError] },
348 { code: '<input type="checkbox" role="listitem" />', errors: [expectedError] },
349 { code: '<input type="color" role="listitem" />', errors: [expectedError] },
350 { code: '<input type="date" role="listitem" />', errors: [expectedError] },
351 { code: '<input type="datetime" role="listitem" />', errors: [expectedError] },
352 { code: '<input type="datetime-local" role="listitem" />', errors: [expectedError] },
353 { code: '<input type="email" role="listitem" />', errors: [expectedError] },
354 { code: '<input type="file" role="listitem" />', errors: [expectedError] },
355 { code: '<input type="image" role="listitem" />', errors: [expectedError] },
356 { code: '<input type="month" role="listitem" />', errors: [expectedError] },
357 { code: '<input type="number" role="listitem" />', errors: [expectedError] },
358 { code: '<input type="password" role="listitem" />', errors: [expectedError] },
359 { code: '<input type="radio" role="listitem" />', errors: [expectedError] },
360 { code: '<input type="range" role="listitem" />', errors: [expectedError] },
361 { code: '<input type="reset" role="listitem" />', errors: [expectedError] },
362 { code: '<input type="search" role="listitem" />', errors: [expectedError] },
363 { code: '<input type="submit" role="listitem" />', errors: [expectedError] },
364 { code: '<input type="tel" role="listitem" />', errors: [expectedError] },
365 { code: '<input type="text" role="listitem" />', errors: [expectedError] },
366 { code: '<input type="time" role="listitem" />', errors: [expectedError] },
367 { code: '<input type="url" role="listitem" />', errors: [expectedError] },
368 { code: '<input type="week" role="listitem" />', errors: [expectedError] },
369 /* End all flavors of input */
370 { code: '<menuitem role="listitem" />;', errors: [expectedError] },
371 { code: '<option className="foo" role="listitem" />', errors: [expectedError] },
372 { code: '<select className="foo" role="listitem" />', errors: [expectedError] },
373 { code: '<textarea className="foo" role="listitem" />', errors: [expectedError] },
374 { code: '<tr role="listitem" />;', errors: [expectedError] },
375 /* Custom elements */
376 { code: '<Link href="http://x.y.z" role="img" />', errors: [expectedError], settings: componentsSettings },
377];
378
379const recommendedOptions = (configs.recommended.rules[ruleName][1] || {});
380ruleTester.run(`${ruleName}:recommended`, rule, {
381 valid: parsers.all([].concat(
382 ...alwaysValid,
383 { code: '<tr role="presentation" />;' },
384 { code: '<canvas role="img" />;' },
385 { code: '<Component role="presentation" />;' },
386 ))
387 .map(ruleOptionsMapperFactory(recommendedOptions))
388 .map(parserOptionsMapper),
389 invalid: parsers.all([].concat(
390 ...neverValid,
391 ))
392 .map(ruleOptionsMapperFactory(recommendedOptions))
393 .map(parserOptionsMapper),
394});
395
396ruleTester.run(`${ruleName}:strict`, rule, {
397 valid: parsers.all([].concat(
398 ...alwaysValid,
399 )).map(parserOptionsMapper),
400 invalid: parsers.all([].concat(
401 ...neverValid,
402 { code: '<tr role="presentation" />;', errors: [expectedError] },
403 { code: '<canvas role="img" />;', errors: [expectedError] },
404 )).map(parserOptionsMapper),
405});
Note: See TracBrowser for help on using the repository browser.