@arcaauth/eslint-plugin-jsx-a11y 6.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/.babelrc +17 -0
  2. package/.eslintrc +44 -0
  3. package/CHANGELOG.md +774 -0
  4. package/LICENSE.md +8 -0
  5. package/README.md +423 -0
  6. package/__mocks__/IdentifierMock.js +15 -0
  7. package/__mocks__/JSXAttributeMock.js +39 -0
  8. package/__mocks__/JSXElementMock.js +37 -0
  9. package/__mocks__/JSXExpressionContainerMock.js +15 -0
  10. package/__mocks__/JSXSpreadAttributeMock.js +18 -0
  11. package/__mocks__/JSXTextMock.js +17 -0
  12. package/__mocks__/LiteralMock.js +17 -0
  13. package/__mocks__/genInteractives.js +218 -0
  14. package/__tests__/__util__/axeMapping.js +6 -0
  15. package/__tests__/__util__/helpers/getESLintCoreRule.js +9 -0
  16. package/__tests__/__util__/helpers/parsers.js +186 -0
  17. package/__tests__/__util__/parserOptionsMapper.js +53 -0
  18. package/__tests__/__util__/ruleOptionsMapperFactory.js +33 -0
  19. package/__tests__/index-test.js +40 -0
  20. package/__tests__/src/rules/accessible-emoji-test.js +66 -0
  21. package/__tests__/src/rules/alt-text-test.js +291 -0
  22. package/__tests__/src/rules/anchor-ambiguous-text-test.js +117 -0
  23. package/__tests__/src/rules/anchor-has-content-test.js +54 -0
  24. package/__tests__/src/rules/anchor-is-valid-test.js +532 -0
  25. package/__tests__/src/rules/aria-activedescendant-has-tabindex-test.js +95 -0
  26. package/__tests__/src/rules/aria-props-test.js +69 -0
  27. package/__tests__/src/rules/aria-proptypes-test.js +311 -0
  28. package/__tests__/src/rules/aria-role-test.js +118 -0
  29. package/__tests__/src/rules/aria-unsupported-elements-test.js +75 -0
  30. package/__tests__/src/rules/autocomplete-valid-test.js +77 -0
  31. package/__tests__/src/rules/click-events-have-key-events-test.js +77 -0
  32. package/__tests__/src/rules/control-has-associated-label-test.js +327 -0
  33. package/__tests__/src/rules/heading-has-content-test.js +85 -0
  34. package/__tests__/src/rules/html-has-lang-test.js +42 -0
  35. package/__tests__/src/rules/iframe-has-title-test.js +55 -0
  36. package/__tests__/src/rules/img-redundant-alt-test.js +137 -0
  37. package/__tests__/src/rules/interactive-supports-focus-test.js +267 -0
  38. package/__tests__/src/rules/label-has-associated-control-test.js +243 -0
  39. package/__tests__/src/rules/label-has-for-test.js +235 -0
  40. package/__tests__/src/rules/lang-test.js +59 -0
  41. package/__tests__/src/rules/media-has-caption-test.js +220 -0
  42. package/__tests__/src/rules/mouse-events-have-key-events-test.js +154 -0
  43. package/__tests__/src/rules/no-access-key-test.js +48 -0
  44. package/__tests__/src/rules/no-aria-hidden-on-focusable-test.js +44 -0
  45. package/__tests__/src/rules/no-autofocus-test.js +68 -0
  46. package/__tests__/src/rules/no-distracting-elements-test.js +51 -0
  47. package/__tests__/src/rules/no-interactive-element-to-noninteractive-role-test.js +405 -0
  48. package/__tests__/src/rules/no-noninteractive-element-interactions-test.js +502 -0
  49. package/__tests__/src/rules/no-noninteractive-element-to-interactive-role-test.js +500 -0
  50. package/__tests__/src/rules/no-noninteractive-tabindex-test.js +123 -0
  51. package/__tests__/src/rules/no-onchange-test.js +57 -0
  52. package/__tests__/src/rules/no-redundant-roles-test.js +98 -0
  53. package/__tests__/src/rules/no-static-element-interactions-test.js +501 -0
  54. package/__tests__/src/rules/prefer-tag-over-role-test.js +63 -0
  55. package/__tests__/src/rules/role-has-required-aria-props-test.js +134 -0
  56. package/__tests__/src/rules/role-supports-aria-props-test.js +570 -0
  57. package/__tests__/src/rules/scope-test.js +50 -0
  58. package/__tests__/src/rules/tabindex-no-positive-test.js +55 -0
  59. package/__tests__/src/util/attributesComparator-test.js +91 -0
  60. package/__tests__/src/util/getAccessibleChildText-test.js +174 -0
  61. package/__tests__/src/util/getComputedRole-test.js +71 -0
  62. package/__tests__/src/util/getElementType-test.js +154 -0
  63. package/__tests__/src/util/getExplicitRole-test.js +35 -0
  64. package/__tests__/src/util/getImplicitRole-test.js +25 -0
  65. package/__tests__/src/util/getSuggestion-test.js +33 -0
  66. package/__tests__/src/util/getTabIndex-test.js +85 -0
  67. package/__tests__/src/util/hasAccessibleChild-test.js +157 -0
  68. package/__tests__/src/util/implicitRoles/input-test.js +87 -0
  69. package/__tests__/src/util/implicitRoles/menu-test.js +20 -0
  70. package/__tests__/src/util/implicitRoles/menuitem-test.js +38 -0
  71. package/__tests__/src/util/isAbstractRole-test.js +51 -0
  72. package/__tests__/src/util/isContentEditable-test.js +52 -0
  73. package/__tests__/src/util/isDOMElement-test.js +30 -0
  74. package/__tests__/src/util/isDisabledElement-test.js +88 -0
  75. package/__tests__/src/util/isFocusable-test.js +111 -0
  76. package/__tests__/src/util/isInteractiveElement-test.js +104 -0
  77. package/__tests__/src/util/isInteractiveRole-test.js +59 -0
  78. package/__tests__/src/util/isNonInteractiveElement-test.js +97 -0
  79. package/__tests__/src/util/isNonInteractiveRole-test.js +59 -0
  80. package/__tests__/src/util/isNonLiteralProperty-test.js +52 -0
  81. package/__tests__/src/util/isSemanticRoleElement-test.js +72 -0
  82. package/__tests__/src/util/mayContainChildComponent-test.js +219 -0
  83. package/__tests__/src/util/mayHaveAccessibleLabel-test.js +256 -0
  84. package/__tests__/src/util/parserOptionsMapper-test.js +93 -0
  85. package/__tests__/src/util/schemas-test.js +35 -0
  86. package/docs/rules/accessible-emoji.md +30 -0
  87. package/docs/rules/alt-text.md +168 -0
  88. package/docs/rules/anchor-ambiguous-text.md +91 -0
  89. package/docs/rules/anchor-has-content.md +64 -0
  90. package/docs/rules/anchor-is-valid.md +270 -0
  91. package/docs/rules/aria-activedescendant-has-tabindex.md +52 -0
  92. package/docs/rules/aria-props.md +29 -0
  93. package/docs/rules/aria-proptypes.md +30 -0
  94. package/docs/rules/aria-role.md +51 -0
  95. package/docs/rules/aria-unsupported-elements.md +30 -0
  96. package/docs/rules/autocomplete-valid.md +49 -0
  97. package/docs/rules/click-events-have-key-events.md +28 -0
  98. package/docs/rules/control-has-associated-label.md +113 -0
  99. package/docs/rules/heading-has-content.md +67 -0
  100. package/docs/rules/html-has-lang.md +31 -0
  101. package/docs/rules/iframe-has-title.md +37 -0
  102. package/docs/rules/img-redundant-alt.md +48 -0
  103. package/docs/rules/interactive-supports-focus.md +156 -0
  104. package/docs/rules/label-has-associated-control.md +152 -0
  105. package/docs/rules/label-has-for.md +130 -0
  106. package/docs/rules/lang.md +31 -0
  107. package/docs/rules/media-has-caption.md +48 -0
  108. package/docs/rules/mouse-events-have-key-events.md +58 -0
  109. package/docs/rules/no-access-key.md +30 -0
  110. package/docs/rules/no-aria-hidden-on-focusable.md +37 -0
  111. package/docs/rules/no-autofocus.md +43 -0
  112. package/docs/rules/no-distracting-elements.md +41 -0
  113. package/docs/rules/no-interactive-element-to-noninteractive-role.md +73 -0
  114. package/docs/rules/no-noninteractive-element-interactions.md +145 -0
  115. package/docs/rules/no-noninteractive-element-to-interactive-role.md +76 -0
  116. package/docs/rules/no-noninteractive-tabindex.md +115 -0
  117. package/docs/rules/no-onchange.md +36 -0
  118. package/docs/rules/no-redundant-roles.md +46 -0
  119. package/docs/rules/no-static-element-interactions.md +114 -0
  120. package/docs/rules/prefer-tag-over-role.md +32 -0
  121. package/docs/rules/role-has-required-aria-props.md +31 -0
  122. package/docs/rules/role-supports-aria-props.md +39 -0
  123. package/docs/rules/scope.md +30 -0
  124. package/docs/rules/tabindex-no-positive.md +32 -0
  125. package/lib/configs/flat-config-base.js +11 -0
  126. package/lib/configs/legacy-config-base.js +9 -0
  127. package/lib/index.js +209 -0
  128. package/lib/rules/accessible-emoji.js +63 -0
  129. package/lib/rules/alt-text.js +218 -0
  130. package/lib/rules/anchor-ambiguous-text.js +64 -0
  131. package/lib/rules/anchor-has-content.js +60 -0
  132. package/lib/rules/anchor-is-valid.js +122 -0
  133. package/lib/rules/aria-activedescendant-has-tabindex.js +66 -0
  134. package/lib/rules/aria-props.js +59 -0
  135. package/lib/rules/aria-proptypes.js +114 -0
  136. package/lib/rules/aria-role.js +89 -0
  137. package/lib/rules/aria-unsupported-elements.js +64 -0
  138. package/lib/rules/autocomplete-valid.js +67 -0
  139. package/lib/rules/click-events-have-key-events.js +68 -0
  140. package/lib/rules/control-has-associated-label.js +103 -0
  141. package/lib/rules/heading-has-content.js +61 -0
  142. package/lib/rules/html-has-lang.js +50 -0
  143. package/lib/rules/iframe-has-title.js +50 -0
  144. package/lib/rules/img-redundant-alt.js +88 -0
  145. package/lib/rules/interactive-supports-focus.js +87 -0
  146. package/lib/rules/label-has-associated-control.js +127 -0
  147. package/lib/rules/label-has-for.js +150 -0
  148. package/lib/rules/lang.js +68 -0
  149. package/lib/rules/media-has-caption.js +96 -0
  150. package/lib/rules/mouse-events-have-key-events.js +94 -0
  151. package/lib/rules/no-access-key.js +43 -0
  152. package/lib/rules/no-aria-hidden-on-focusable.js +47 -0
  153. package/lib/rules/no-autofocus.js +62 -0
  154. package/lib/rules/no-distracting-elements.js +54 -0
  155. package/lib/rules/no-interactive-element-to-noninteractive-role.js +81 -0
  156. package/lib/rules/no-noninteractive-element-interactions.js +95 -0
  157. package/lib/rules/no-noninteractive-element-to-interactive-role.js +80 -0
  158. package/lib/rules/no-noninteractive-tabindex.js +109 -0
  159. package/lib/rules/no-onchange.js +52 -0
  160. package/lib/rules/no-redundant-roles.js +86 -0
  161. package/lib/rules/no-static-element-interactions.js +102 -0
  162. package/lib/rules/prefer-tag-over-role.js +75 -0
  163. package/lib/rules/role-has-required-aria-props.js +88 -0
  164. package/lib/rules/role-supports-aria-props.js +78 -0
  165. package/lib/rules/scope.js +58 -0
  166. package/lib/rules/tabindex-no-positive.js +53 -0
  167. package/lib/util/attributesComparator.js +34 -0
  168. package/lib/util/getAccessibleChildText.js +55 -0
  169. package/lib/util/getComputedRole.js +19 -0
  170. package/lib/util/getElementType.js +30 -0
  171. package/lib/util/getExplicitRole.js +27 -0
  172. package/lib/util/getImplicitRole.js +24 -0
  173. package/lib/util/getSuggestion.js +32 -0
  174. package/lib/util/getTabIndex.js +34 -0
  175. package/lib/util/hasAccessibleChild.js +30 -0
  176. package/lib/util/implicitRoles/a.js +17 -0
  177. package/lib/util/implicitRoles/area.js +17 -0
  178. package/lib/util/implicitRoles/article.js +13 -0
  179. package/lib/util/implicitRoles/aside.js +13 -0
  180. package/lib/util/implicitRoles/body.js +13 -0
  181. package/lib/util/implicitRoles/button.js +13 -0
  182. package/lib/util/implicitRoles/datalist.js +13 -0
  183. package/lib/util/implicitRoles/details.js +13 -0
  184. package/lib/util/implicitRoles/dialog.js +13 -0
  185. package/lib/util/implicitRoles/form.js +13 -0
  186. package/lib/util/implicitRoles/h1.js +13 -0
  187. package/lib/util/implicitRoles/h2.js +13 -0
  188. package/lib/util/implicitRoles/h3.js +13 -0
  189. package/lib/util/implicitRoles/h4.js +13 -0
  190. package/lib/util/implicitRoles/h5.js +13 -0
  191. package/lib/util/implicitRoles/h6.js +13 -0
  192. package/lib/util/implicitRoles/hr.js +13 -0
  193. package/lib/util/implicitRoles/img.js +31 -0
  194. package/lib/util/implicitRoles/index.js +82 -0
  195. package/lib/util/implicitRoles/input.js +38 -0
  196. package/lib/util/implicitRoles/li.js +13 -0
  197. package/lib/util/implicitRoles/link.js +17 -0
  198. package/lib/util/implicitRoles/menu.js +19 -0
  199. package/lib/util/implicitRoles/menuitem.js +28 -0
  200. package/lib/util/implicitRoles/meter.js +13 -0
  201. package/lib/util/implicitRoles/nav.js +13 -0
  202. package/lib/util/implicitRoles/ol.js +13 -0
  203. package/lib/util/implicitRoles/option.js +13 -0
  204. package/lib/util/implicitRoles/output.js +13 -0
  205. package/lib/util/implicitRoles/progress.js +13 -0
  206. package/lib/util/implicitRoles/section.js +13 -0
  207. package/lib/util/implicitRoles/select.js +13 -0
  208. package/lib/util/implicitRoles/tbody.js +13 -0
  209. package/lib/util/implicitRoles/textarea.js +13 -0
  210. package/lib/util/implicitRoles/tfoot.js +13 -0
  211. package/lib/util/implicitRoles/thead.js +13 -0
  212. package/lib/util/implicitRoles/ul.js +13 -0
  213. package/lib/util/isAbstractRole.js +23 -0
  214. package/lib/util/isContentEditable.js +13 -0
  215. package/lib/util/isDOMElement.js +15 -0
  216. package/lib/util/isDisabledElement.js +23 -0
  217. package/lib/util/isFocusable.js +23 -0
  218. package/lib/util/isHiddenFromScreenReader.js +26 -0
  219. package/lib/util/isInteractiveElement.js +116 -0
  220. package/lib/util/isInteractiveRole.js +54 -0
  221. package/lib/util/isNonInteractiveElement.js +131 -0
  222. package/lib/util/isNonInteractiveRole.js +55 -0
  223. package/lib/util/isNonLiteralProperty.js +29 -0
  224. package/lib/util/isPresentationRole.js +13 -0
  225. package/lib/util/isSemanticRoleElement.js +54 -0
  226. package/lib/util/mayContainChildComponent.js +50 -0
  227. package/lib/util/mayHaveAccessibleLabel.js +95 -0
  228. package/lib/util/schemas.js +52 -0
  229. package/package.json +120 -0
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @fileoverview Enforce tabIndex value is not greater than zero.
3
+ * @author Ethan Cohen
4
+ */
5
+
6
+ // -----------------------------------------------------------------------------
7
+ // Requirements
8
+ // -----------------------------------------------------------------------------
9
+
10
+ import { RuleTester } from 'eslint';
11
+ import parserOptionsMapper from '../../__util__/parserOptionsMapper';
12
+ import parsers from '../../__util__/helpers/parsers';
13
+ import rule from '../../../src/rules/tabindex-no-positive';
14
+
15
+ // -----------------------------------------------------------------------------
16
+ // Tests
17
+ // -----------------------------------------------------------------------------
18
+
19
+ const ruleTester = new RuleTester();
20
+
21
+ const expectedError = {
22
+ message: 'Avoid positive integer values for tabIndex.',
23
+ type: 'JSXAttribute',
24
+ };
25
+
26
+ ruleTester.run('tabindex-no-positive', rule, {
27
+ valid: parsers.all([].concat(
28
+ { code: '<div />;' },
29
+ { code: '<div {...props} />' },
30
+ { code: '<div id="main" />' },
31
+ { code: '<div tabIndex={undefined} />' },
32
+ { code: '<div tabIndex={`${undefined}`} />' },
33
+ { code: '<div tabIndex={`${undefined}${undefined}`} />' },
34
+ { code: '<div tabIndex={0} />' },
35
+ { code: '<div tabIndex={-1} />' },
36
+ { code: '<div tabIndex={null} />' },
37
+ { code: '<div tabIndex={bar()} />' },
38
+ { code: '<div tabIndex={bar} />' },
39
+ { code: '<div tabIndex={"foobar"} />' },
40
+ { code: '<div tabIndex="0" />' },
41
+ { code: '<div tabIndex="-1" />' },
42
+ { code: '<div tabIndex="-5" />' },
43
+ { code: '<div tabIndex="-5.5" />' },
44
+ { code: '<div tabIndex={-5.5} />' },
45
+ { code: '<div tabIndex={-5} />' },
46
+ )).map(parserOptionsMapper),
47
+
48
+ invalid: parsers.all([].concat(
49
+ { code: '<div tabIndex="1" />', errors: [expectedError] },
50
+ { code: '<div tabIndex={1} />', errors: [expectedError] },
51
+ { code: '<div tabIndex={"1"} />', errors: [expectedError] },
52
+ { code: '<div tabIndex={`1`} />', errors: [expectedError] },
53
+ { code: '<div tabIndex={1.589} />', errors: [expectedError] },
54
+ )).map(parserOptionsMapper),
55
+ });
@@ -0,0 +1,91 @@
1
+ import test from 'tape';
2
+
3
+ import attributesComparator from '../../../src/util/attributesComparator';
4
+ import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
5
+ import JSXElementMock from '../../../__mocks__/JSXElementMock';
6
+
7
+ test('attributesComparator', (t) => {
8
+ t.equal(
9
+ attributesComparator(),
10
+ true,
11
+ 'baseAttributes are undefined and attributes are undefined -> true',
12
+ );
13
+
14
+ t.equal(
15
+ attributesComparator([], []),
16
+ true,
17
+ 'baseAttributes are empty and attributes are empty -> true',
18
+ );
19
+
20
+ t.equal(
21
+ attributesComparator([], [
22
+ JSXAttributeMock('foo', 0),
23
+ JSXAttributeMock('bar', 'baz'),
24
+ ]),
25
+ true,
26
+ 'baseAttributes are empty and attributes have values -> true',
27
+ );
28
+
29
+ const baseAttributes = [
30
+ {
31
+ name: 'biz',
32
+ value: 1,
33
+ }, {
34
+ name: 'fizz',
35
+ value: 'pop',
36
+ }, {
37
+ name: 'fuzz',
38
+ value: 'lolz',
39
+ },
40
+ ];
41
+
42
+ t.equal(
43
+ attributesComparator(baseAttributes, []),
44
+ false,
45
+ 'baseAttributes have values and attributes are empty -> false',
46
+ );
47
+
48
+ t.equal(
49
+ attributesComparator(baseAttributes, [
50
+ JSXElementMock(),
51
+ JSXAttributeMock('biz', 2),
52
+ JSXAttributeMock('ziff', 'opo'),
53
+ JSXAttributeMock('far', 'lolz'),
54
+ ]),
55
+ false,
56
+ 'baseAttributes have values and attributes have values, and the values are different -> false',
57
+ );
58
+
59
+ t.equal(
60
+ attributesComparator(baseAttributes, [
61
+ JSXAttributeMock('biz', 1),
62
+ JSXAttributeMock('fizz', 'pop'),
63
+ JSXAttributeMock('goo', 'gazz'),
64
+ ]),
65
+ false,
66
+ 'baseAttributes have values and attributes have values, and the values are a subset -> false',
67
+ );
68
+
69
+ t.equal(
70
+ attributesComparator(baseAttributes, [
71
+ JSXAttributeMock('biz', 1),
72
+ JSXAttributeMock('fizz', 'pop'),
73
+ JSXAttributeMock('fuzz', 'lolz'),
74
+ ]),
75
+ true,
76
+ 'baseAttributes have values and attributes have values, and the values are the same -> true',
77
+ );
78
+
79
+ t.equal(
80
+ attributesComparator(baseAttributes, [
81
+ JSXAttributeMock('biz', 1),
82
+ JSXAttributeMock('fizz', 'pop'),
83
+ JSXAttributeMock('fuzz', 'lolz'),
84
+ JSXAttributeMock('dar', 'tee'),
85
+ ]),
86
+ true,
87
+ 'baseAttributes have values and attributes have values, and the values are a superset -> true',
88
+ );
89
+
90
+ t.end();
91
+ });
@@ -0,0 +1,174 @@
1
+ import test from 'tape';
2
+ import { elementType } from 'jsx-ast-utils';
3
+
4
+ import getAccessibleChildText from '../../../src/util/getAccessibleChildText';
5
+ import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
6
+ import JSXElementMock from '../../../__mocks__/JSXElementMock';
7
+
8
+ test('getAccessibleChildText', (t) => {
9
+ t.equal(
10
+ getAccessibleChildText(JSXElementMock(
11
+ 'a',
12
+ [JSXAttributeMock('aria-label', 'foo')],
13
+ ), elementType),
14
+ 'foo',
15
+ 'returns the aria-label when present',
16
+ );
17
+
18
+ t.equal(
19
+ getAccessibleChildText(JSXElementMock(
20
+ 'a',
21
+ [JSXAttributeMock('aria-label', 'foo')],
22
+ [{ type: 'JSXText', value: 'bar' }],
23
+ ), elementType),
24
+ 'foo',
25
+ 'returns the aria-label instead of children',
26
+ );
27
+
28
+ t.equal(
29
+ getAccessibleChildText(JSXElementMock(
30
+ 'a',
31
+ [JSXAttributeMock('aria-hidden', 'true')],
32
+ ), elementType),
33
+ '',
34
+ 'skips elements with aria-hidden=true',
35
+ );
36
+
37
+ t.equal(
38
+ getAccessibleChildText(JSXElementMock(
39
+ 'a',
40
+ [],
41
+ [{ type: 'JSXText', value: 'bar' }],
42
+ ), elementType),
43
+ 'bar',
44
+ 'returns literal value for JSXText child',
45
+ );
46
+
47
+ t.equal(
48
+ getAccessibleChildText(JSXElementMock(
49
+ 'a',
50
+ [],
51
+ [JSXElementMock('img', [
52
+ JSXAttributeMock('src', 'some/path'),
53
+ JSXAttributeMock('alt', 'a sensible label'),
54
+ ])],
55
+ ), elementType),
56
+ 'a sensible label',
57
+ 'returns alt text for img child',
58
+ );
59
+
60
+ t.equal(
61
+ getAccessibleChildText(JSXElementMock(
62
+ 'a',
63
+ [],
64
+ [JSXElementMock('span', [
65
+ JSXAttributeMock('alt', 'a sensible label'),
66
+ ])],
67
+ ), elementType),
68
+ '',
69
+ 'returns blank when alt tag is used on arbitrary element',
70
+ );
71
+
72
+ t.equal(
73
+ getAccessibleChildText(JSXElementMock(
74
+ 'a',
75
+ [],
76
+ [{ type: 'Literal', value: 'bar' }],
77
+ ), elementType),
78
+ 'bar',
79
+ 'returns literal value for JSXText child',
80
+ );
81
+
82
+ t.equal(
83
+ getAccessibleChildText(JSXElementMock(
84
+ 'a',
85
+ [],
86
+ [{ type: 'Literal', value: ' bar ' }],
87
+ ), elementType),
88
+ 'bar',
89
+ 'returns trimmed literal value for JSXText child',
90
+ );
91
+
92
+ t.equal(
93
+ getAccessibleChildText(JSXElementMock(
94
+ 'a',
95
+ [],
96
+ [{ type: 'Literal', value: 'foo bar' }],
97
+ ), elementType),
98
+ 'foo bar',
99
+ 'returns space-collapsed literal value for JSXText child',
100
+ );
101
+
102
+ t.equal(
103
+ getAccessibleChildText(JSXElementMock(
104
+ 'a',
105
+ [],
106
+ [{ type: 'Literal', value: 'foo, bar. baz? foo; bar:' }],
107
+ ), elementType),
108
+ 'foo bar baz foo bar',
109
+ 'returns punctuation-stripped literal value for JSXText child',
110
+ );
111
+
112
+ t.equal(
113
+ getAccessibleChildText(JSXElementMock(
114
+ 'a',
115
+ [],
116
+ [JSXElementMock(
117
+ 'span',
118
+ [],
119
+ [{ type: 'Literal', value: 'bar' }],
120
+ )],
121
+ ), elementType),
122
+ 'bar',
123
+ 'returns recursive value for JSXElement child',
124
+ );
125
+
126
+ t.equal(
127
+ getAccessibleChildText(JSXElementMock(
128
+ 'a',
129
+ [],
130
+ [JSXElementMock(
131
+ 'span',
132
+ [],
133
+ [JSXElementMock(
134
+ 'span',
135
+ [JSXAttributeMock('aria-hidden', 'true')],
136
+ )],
137
+ )],
138
+ ), elementType),
139
+ '',
140
+ 'skips children with aria-hidden-true',
141
+ );
142
+
143
+ t.equal(
144
+ getAccessibleChildText(JSXElementMock(
145
+ 'a',
146
+ [],
147
+ [{ type: 'Literal', value: 'foo' }, { type: 'Literal', value: 'bar' }],
148
+ ), elementType),
149
+ 'foo bar',
150
+ 'joins multiple children properly - no spacing',
151
+ );
152
+
153
+ t.equal(
154
+ getAccessibleChildText(JSXElementMock(
155
+ 'a',
156
+ [],
157
+ [{ type: 'Literal', value: ' foo ' }, { type: 'Literal', value: ' bar ' }],
158
+ ), elementType),
159
+ 'foo bar',
160
+ 'joins multiple children properly - with spacing',
161
+ );
162
+
163
+ t.equal(
164
+ getAccessibleChildText(JSXElementMock(
165
+ 'a',
166
+ [],
167
+ [{ type: 'Literal', value: 'foo' }, { type: 'Unknown' }, { type: 'Literal', value: 'bar' }],
168
+ ), elementType),
169
+ 'foo bar',
170
+ 'skips unknown elements',
171
+ );
172
+
173
+ t.end();
174
+ });
@@ -0,0 +1,71 @@
1
+ import test from 'tape';
2
+
3
+ import getComputedRole from '../../../src/util/getComputedRole';
4
+ import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
5
+
6
+ test('getComputedRole', (t) => {
7
+ t.equal(
8
+ getComputedRole(
9
+ 'div',
10
+ [JSXAttributeMock('role', 'button')],
11
+ ),
12
+ 'button',
13
+ 'explicit role + valid role -> returns the role',
14
+ );
15
+
16
+ t.equal(
17
+ getComputedRole(
18
+ 'li',
19
+ [JSXAttributeMock('role', 'beeswax')],
20
+ ),
21
+ 'listitem',
22
+ 'explicit role + invalid role + has implicit -> returns the implicit role',
23
+ );
24
+
25
+ t.equal(
26
+ getComputedRole(
27
+ 'div',
28
+ [JSXAttributeMock('role', 'beeswax')],
29
+ ),
30
+ null,
31
+ 'explicit role + invalid role + lacks implicit -> returns null',
32
+ );
33
+
34
+ t.equal(
35
+ getComputedRole(
36
+ 'li',
37
+ [],
38
+ ),
39
+ 'listitem',
40
+ 'explicit role + no role + has implicit -> returns the implicit role',
41
+ );
42
+
43
+ t.equal(
44
+ getComputedRole(
45
+ 'div',
46
+ [],
47
+ ),
48
+ null,
49
+ 'explicit role + no role + lacks implicit -> returns null',
50
+ );
51
+
52
+ t.equal(
53
+ getComputedRole(
54
+ 'li',
55
+ [JSXAttributeMock('role', 'beeswax')],
56
+ ),
57
+ 'listitem',
58
+ 'implicit role + has implicit -> returns the implicit role',
59
+ );
60
+
61
+ t.equal(
62
+ getComputedRole(
63
+ 'div',
64
+ [],
65
+ ),
66
+ null,
67
+ 'implicit role + lacks implicit -> returns null',
68
+ );
69
+
70
+ t.end();
71
+ });
@@ -0,0 +1,154 @@
1
+ import test from 'tape';
2
+
3
+ import getElementType from '../../../src/util/getElementType';
4
+ import JSXElementMock from '../../../__mocks__/JSXElementMock';
5
+ import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
6
+
7
+ test('getElementType', (t) => {
8
+ t.test('no settings in context', (st) => {
9
+ const elementType = getElementType({ settings: {} });
10
+
11
+ st.equal(
12
+ elementType(JSXElementMock('input').openingElement),
13
+ 'input',
14
+ 'returns the exact tag name for a DOM element',
15
+ );
16
+
17
+ st.equal(
18
+ elementType(JSXElementMock('CustomInput').openingElement),
19
+ 'CustomInput',
20
+ 'returns the exact tag name for a custom element',
21
+ );
22
+
23
+ st.equal(
24
+ elementType(JSXElementMock('toString').openingElement),
25
+ 'toString',
26
+ 'returns the exact tag name for names that are in Object.prototype',
27
+ );
28
+
29
+ st.equal(
30
+ elementType(JSXElementMock('span', [JSXAttributeMock('as', 'h1')]).openingElement),
31
+ 'span',
32
+ 'returns the default tag name provided',
33
+ );
34
+
35
+ st.end();
36
+ });
37
+
38
+ t.test('components settings in context', (st) => {
39
+ const elementType = getElementType({
40
+ settings: {
41
+ 'jsx-a11y': {
42
+ components: {
43
+ CustomInput: 'input',
44
+ },
45
+ },
46
+ },
47
+ });
48
+
49
+ st.equal(
50
+ elementType(JSXElementMock('input').openingElement),
51
+ 'input',
52
+ 'returns the exact tag name for a DOM element',
53
+ );
54
+
55
+ st.equal(
56
+ elementType(JSXElementMock('CustomInput').openingElement),
57
+ 'input',
58
+ 'returns the mapped tag name for a custom element',
59
+ );
60
+
61
+ st.equal(
62
+ elementType(JSXElementMock('CityInput').openingElement),
63
+ 'CityInput',
64
+ 'returns the exact tag name for a custom element not in the components map',
65
+ );
66
+
67
+ st.equal(
68
+ elementType(JSXElementMock('span', [JSXAttributeMock('as', 'h1')]).openingElement),
69
+ 'span',
70
+ 'return the default tag name since not polymorphicPropName was provided',
71
+ );
72
+
73
+ st.end();
74
+ });
75
+
76
+ t.test('polymorphicPropName settings in context', (st) => {
77
+ const elementType = getElementType({
78
+ settings: {
79
+ 'jsx-a11y': {
80
+ polymorphicPropName: 'asChild',
81
+ components: {
82
+ CustomButton: 'button',
83
+ },
84
+ },
85
+ },
86
+ });
87
+
88
+ st.equal(
89
+ elementType(JSXElementMock('span', [JSXAttributeMock('asChild', 'h1')]).openingElement),
90
+ 'h1',
91
+ 'returns the tag name provided by the polymorphic prop, "asChild", defined in the settings',
92
+ );
93
+
94
+ st.equal(
95
+ elementType(JSXElementMock('CustomButton', [JSXAttributeMock('asChild', 'a')]).openingElement),
96
+ 'a',
97
+ 'returns the tag name provided by the polymorphic prop, "asChild", defined in the settings instead of the component mapping tag',
98
+ );
99
+
100
+ st.equal(
101
+ elementType(JSXElementMock('CustomButton', [JSXAttributeMock('as', 'a')]).openingElement),
102
+ 'button',
103
+ 'returns the tag name provided by the componnet mapping if the polymorphic prop, "asChild", defined in the settings is not set',
104
+ );
105
+
106
+ st.end();
107
+ });
108
+
109
+ t.test('polymorphicPropName settings and explicitly defined polymorphicAllowList in context', (st) => {
110
+ const elementType = getElementType({
111
+ settings: {
112
+ 'jsx-a11y': {
113
+ polymorphicPropName: 'asChild',
114
+ polymorphicAllowList: [
115
+ 'Box',
116
+ 'Icon',
117
+ ],
118
+ components: {
119
+ Box: 'div',
120
+ Icon: 'svg',
121
+ },
122
+ },
123
+ },
124
+ });
125
+
126
+ st.equal(
127
+ elementType(JSXElementMock('Spinner', [JSXAttributeMock('asChild', 'img')]).openingElement),
128
+ 'Spinner',
129
+ 'does not use the polymorphic prop if polymorphicAllowList is defined, but element is not part of polymorphicAllowList',
130
+ );
131
+
132
+ st.equal(
133
+ elementType(JSXElementMock('Icon', [JSXAttributeMock('asChild', 'img')]).openingElement),
134
+ 'img',
135
+ 'uses the polymorphic prop if it is in explicitly defined polymorphicAllowList',
136
+ );
137
+
138
+ st.equal(
139
+ elementType(JSXElementMock('Box', [JSXAttributeMock('asChild', 'span')]).openingElement),
140
+ 'span',
141
+ 'returns the tag name provided by the polymorphic prop, "asChild", defined in the settings instead of the component mapping tag',
142
+ );
143
+
144
+ st.equal(
145
+ elementType(JSXElementMock('Box', [JSXAttributeMock('as', 'a')]).openingElement),
146
+ 'div',
147
+ 'returns the tag name provided by the component mapping if the polymorphic prop, "asChild", defined in the settings is not set',
148
+ );
149
+
150
+ st.end();
151
+ });
152
+
153
+ t.end();
154
+ });
@@ -0,0 +1,35 @@
1
+ import test from 'tape';
2
+
3
+ import getExplicitRole from '../../../src/util/getExplicitRole';
4
+ import JSXAttributeMock from '../../../__mocks__/JSXAttributeMock';
5
+
6
+ test('getExplicitRole', (t) => {
7
+ t.equal(
8
+ getExplicitRole(
9
+ 'div',
10
+ [JSXAttributeMock('role', 'button')],
11
+ ),
12
+ 'button',
13
+ 'valid role returns the role',
14
+ );
15
+
16
+ t.equal(
17
+ getExplicitRole(
18
+ 'div',
19
+ [JSXAttributeMock('role', 'beeswax')],
20
+ ),
21
+ null,
22
+ 'invalid role returns null',
23
+ );
24
+
25
+ t.equal(
26
+ getExplicitRole(
27
+ 'div',
28
+ [],
29
+ ),
30
+ null,
31
+ 'no role returns null',
32
+ );
33
+
34
+ t.end();
35
+ });
@@ -0,0 +1,25 @@
1
+ import test from 'tape';
2
+
3
+ import getImplicitRole from '../../../src/util/getImplicitRole';
4
+
5
+ test('getImplicitRole', (t) => {
6
+ t.equal(
7
+ getImplicitRole(
8
+ 'li',
9
+ [],
10
+ ),
11
+ 'listitem',
12
+ 'has implicit, returns implicit role',
13
+ );
14
+
15
+ t.equal(
16
+ getImplicitRole(
17
+ 'div',
18
+ [],
19
+ ),
20
+ null,
21
+ 'lacks implicit, returns null',
22
+ );
23
+
24
+ t.end();
25
+ });
@@ -0,0 +1,33 @@
1
+ import test from 'tape';
2
+
3
+ import getSuggestion from '../../../src/util/getSuggestion';
4
+
5
+ test('spell check suggestion API', (t) => {
6
+ t.deepEqual([], getSuggestion('foo'), 'returns no suggestions given empty word and no dictionary');
7
+
8
+ t.deepEqual(
9
+ getSuggestion('foo'),
10
+ [],
11
+ 'returns no suggestions given real word and no dictionary',
12
+ );
13
+
14
+ t.deepEqual(
15
+ getSuggestion('fo', ['foo', 'bar', 'baz']),
16
+ ['foo'],
17
+ 'returns correct suggestion given real word and a dictionary',
18
+ );
19
+
20
+ t.deepEqual(
21
+ getSuggestion('theer', ['there', 'their', 'foo', 'bar']),
22
+ ['there', 'their'],
23
+ 'returns multiple correct suggestions given real word and a dictionary',
24
+ );
25
+
26
+ t.deepEqual(
27
+ getSuggestion('theer', ['there', 'their', 'foo', 'bar'], 1),
28
+ ['there'],
29
+ 'returns correct # of suggestions given the limit argument',
30
+ );
31
+
32
+ t.end();
33
+ });