@markuplint/ml-spec 2.0.1 → 3.0.0-alpha.82

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 (123) hide show
  1. package/lib/dom-traverse/accname-computation.d.ts +1 -0
  2. package/lib/dom-traverse/accname-computation.js +8 -0
  3. package/lib/dom-traverse/focusability-computation.d.ts +1 -0
  4. package/lib/dom-traverse/focusability-computation.js +8 -0
  5. package/lib/dom-traverse/get-aria.d.ts +9 -0
  6. package/lib/dom-traverse/get-aria.js +54 -0
  7. package/lib/dom-traverse/get-attr-specs.d.ts +2 -0
  8. package/lib/dom-traverse/get-attr-specs.js +8 -0
  9. package/lib/dom-traverse/get-computed-role.d.ts +2 -0
  10. package/lib/dom-traverse/get-computed-role.js +182 -0
  11. package/lib/dom-traverse/get-explicit-role.d.ts +2 -0
  12. package/lib/dom-traverse/get-explicit-role.js +102 -0
  13. package/lib/dom-traverse/get-implicit-role.d.ts +3 -0
  14. package/lib/dom-traverse/get-implicit-role.js +37 -0
  15. package/lib/dom-traverse/get-non-presentational-ancestor.d.ts +5 -0
  16. package/lib/dom-traverse/get-non-presentational-ancestor.js +21 -0
  17. package/lib/dom-traverse/get-permitted-roles.d.ts +5 -0
  18. package/lib/dom-traverse/get-permitted-roles.js +8 -0
  19. package/lib/dom-traverse/get-spec.d.ts +2 -0
  20. package/lib/dom-traverse/get-spec.js +8 -0
  21. package/lib/dom-traverse/has-required-owned-elements.d.ts +3 -0
  22. package/lib/dom-traverse/has-required-owned-elements.js +69 -0
  23. package/lib/index.d.ts +16 -6
  24. package/lib/index.js +17 -7
  25. package/lib/specs/aria-spec.d.ts +5 -0
  26. package/lib/specs/aria-spec.js +12 -0
  27. package/lib/specs/aria-specs.d.ts +6 -0
  28. package/lib/specs/aria-specs.js +8 -0
  29. package/lib/specs/content-model-category-to-tag-names.d.ts +3 -0
  30. package/lib/specs/content-model-category-to-tag-names.js +15 -0
  31. package/lib/specs/get-aria.d.ts +3 -0
  32. package/lib/specs/get-aria.js +71 -0
  33. package/lib/specs/get-attr-specs.d.ts +8 -0
  34. package/lib/{get-attr-specs.js → specs/get-attr-specs.js} +8 -6
  35. package/lib/specs/get-implicit-role.d.ts +2 -0
  36. package/lib/specs/get-implicit-role.js +12 -0
  37. package/lib/specs/get-permitted-roles.d.ts +12 -0
  38. package/lib/specs/get-permitted-roles.js +61 -0
  39. package/lib/specs/get-role-spec.d.ts +5 -0
  40. package/lib/specs/get-role-spec.js +52 -0
  41. package/lib/specs/get-selectors-by-content-model-category.d.ts +3 -0
  42. package/lib/specs/get-selectors-by-content-model-category.js +8 -0
  43. package/lib/specs/get-spec-by-tag-name.d.ts +2 -0
  44. package/lib/specs/get-spec-by-tag-name.js +16 -0
  45. package/lib/{get-spec.d.ts → specs/get-spec.d.ts} +4 -4
  46. package/lib/{get-spec.js → specs/get-spec.js} +1 -1
  47. package/lib/specs/html-spec.d.ts +6 -0
  48. package/lib/specs/html-spec.js +16 -0
  49. package/lib/specs/is-presentational.d.ts +1 -0
  50. package/lib/specs/is-presentational.js +10 -0
  51. package/lib/specs/resolve-namespace.d.ts +9 -0
  52. package/lib/specs/resolve-namespace.js +26 -0
  53. package/lib/specs/resolve-version.d.ts +3 -0
  54. package/lib/specs/resolve-version.js +21 -0
  55. package/lib/specs/schema-to-spec.d.ts +13 -0
  56. package/lib/specs/schema-to-spec.js +93 -0
  57. package/lib/types/aria.d.ts +130 -0
  58. package/lib/{attributes.js → types/aria.js} +0 -0
  59. package/lib/{attributes.d.ts → types/attributes.d.ts} +5 -12
  60. package/lib/{permitted-structres.js → types/attributes.js} +0 -0
  61. package/lib/{types.d.ts → types/index.d.ts} +50 -65
  62. package/lib/{types.js → types/index.js} +0 -0
  63. package/lib/{permitted-structres.d.ts → types/permitted-structres.d.ts} +14 -23
  64. package/lib/types/permitted-structres.js +8 -0
  65. package/lib/utils/get-ns.d.ts +1 -0
  66. package/lib/{get-ns.js → utils/get-ns.js} +0 -0
  67. package/lib/utils/merge-array.d.ts +5 -0
  68. package/lib/utils/merge-array.js +36 -0
  69. package/lib/utils/resolve-namespace.d.ts +9 -0
  70. package/lib/utils/resolve-namespace.js +26 -0
  71. package/lib/utils.d.ts +6 -3
  72. package/lib/utils.js +15 -1
  73. package/package.json +10 -6
  74. package/schemas/aria.schema.json +258 -0
  75. package/schemas/attributes.schema.json +5 -23
  76. package/schemas/{permitted-structures.schema.json → content-models.schema.json} +52 -81
  77. package/schemas/element.schema.json +11 -0
  78. package/schemas/global-attributes.schema.json +6 -2
  79. package/src/dom-traverse/accname-computation.spec.ts +69 -0
  80. package/src/dom-traverse/accname-computation.ts +5 -0
  81. package/src/dom-traverse/get-attr-specs.ts +8 -0
  82. package/src/dom-traverse/get-computed-role.spec.ts +134 -0
  83. package/src/dom-traverse/get-computed-role.ts +193 -0
  84. package/src/dom-traverse/get-explicit-role.ts +112 -0
  85. package/src/dom-traverse/get-implicit-role.ts +36 -0
  86. package/src/dom-traverse/get-non-presentational-ancestor.ts +20 -0
  87. package/src/dom-traverse/get-permitted-roles.ts +7 -0
  88. package/src/dom-traverse/get-spec.ts +7 -0
  89. package/src/dom-traverse/has-required-owned-elements.spec.ts +36 -0
  90. package/src/dom-traverse/has-required-owned-elements.ts +72 -0
  91. package/src/index.ts +16 -6
  92. package/src/specs/aria-specs.ts +6 -0
  93. package/src/specs/content-model-category-to-tag-names.ts +15 -0
  94. package/src/specs/get-aria.ts +85 -0
  95. package/src/{get-attr-specs.spec.ts → specs/get-attr-specs.spec.ts} +9 -6
  96. package/src/{get-attr-specs.ts → specs/get-attr-specs.ts} +12 -7
  97. package/src/specs/get-implicit-role.spec.ts +81 -0
  98. package/src/specs/get-implicit-role.ts +17 -0
  99. package/src/specs/get-permitted-roles.spec.ts +420 -0
  100. package/src/specs/get-permitted-roles.ts +87 -0
  101. package/src/specs/get-role-spec.spec.ts +67 -0
  102. package/src/specs/get-role-spec.ts +72 -0
  103. package/src/specs/get-selectors-by-content-model-category.ts +10 -0
  104. package/src/specs/get-spec-by-tag-name.spec.ts +68 -0
  105. package/src/specs/get-spec-by-tag-name.ts +16 -0
  106. package/src/specs/is-presentational.ts +6 -0
  107. package/src/specs/resolve-version.ts +20 -0
  108. package/src/{get-spec.spec.ts → specs/schema-to-spec.spec.ts} +2 -2
  109. package/src/{get-spec.ts → specs/schema-to-spec.ts} +22 -8
  110. package/src/types/aria.ts +147 -0
  111. package/src/{attributes.ts → types/attributes.ts} +5 -12
  112. package/src/{types.ts → types/index.ts} +70 -69
  113. package/src/{permitted-structres.ts → types/permitted-structres.ts} +15 -25
  114. package/src/{get-ns.ts → utils/get-ns.ts} +1 -1
  115. package/src/{utils.ts → utils/merge-array.ts} +0 -0
  116. package/src/utils/resolve-namespace.ts +36 -0
  117. package/tsconfig.tsbuildinfo +1 -1
  118. package/lib/get-attr-specs.d.ts +0 -7
  119. package/lib/get-ns.d.ts +0 -1
  120. package/lib/get-spec-by-tag-name.d.ts +0 -2
  121. package/lib/get-spec-by-tag-name.js +0 -31
  122. package/schemas/wai-aria.schema.json +0 -87
  123. package/src/get-spec-by-tag-name.ts +0 -32
@@ -0,0 +1,85 @@
1
+ import type { ARIAVersion, Matches, MLMLSpec } from '../types';
2
+ import type { ARIA, PermittedRoles } from '../types/aria';
3
+
4
+ import { getSpecByTagName } from './get-spec-by-tag-name';
5
+ import { resolveVersion } from './resolve-version';
6
+
7
+ const cache = new Map<string, Omit<ARIA, ARIAVersion> | null>();
8
+
9
+ export function getARIA(
10
+ specs: Readonly<MLMLSpec>,
11
+ localName: string,
12
+ namespace: string | null,
13
+ version: ARIAVersion,
14
+ matches: Matches,
15
+ ): Omit<ARIA, ARIAVersion | 'conditions'> | null {
16
+ const aria = getVersionResolvedARIA(specs, localName, namespace, version);
17
+ if (!aria) {
18
+ return null;
19
+ }
20
+ const conditions = aria.conditions;
21
+ if (!conditions) {
22
+ return aria;
23
+ }
24
+ const conds = Object.keys(conditions);
25
+ let { implicitRole, permittedRoles, implicitProperties, properties, namingProhibited } = aria;
26
+ for (const cond of conds) {
27
+ if (!matches(cond)) {
28
+ continue;
29
+ }
30
+ const condARIA = conditions[cond];
31
+ implicitRole = condARIA.implicitRole ?? implicitRole;
32
+ permittedRoles = condARIA.permittedRoles ?? permittedRoles;
33
+ implicitProperties = condARIA.implicitProperties ?? implicitProperties;
34
+ properties = condARIA.properties ?? properties;
35
+ namingProhibited = condARIA.namingProhibited ?? namingProhibited;
36
+ }
37
+ return {
38
+ implicitRole,
39
+ permittedRoles,
40
+ implicitProperties,
41
+ properties,
42
+ namingProhibited,
43
+ };
44
+ }
45
+
46
+ function getVersionResolvedARIA(
47
+ specs: Readonly<MLMLSpec>,
48
+ localName: string,
49
+ namespace: string | null,
50
+ version: ARIAVersion,
51
+ ) {
52
+ const key = localName + namespace + version;
53
+ let aria = cache.get(key);
54
+ if (aria !== undefined) {
55
+ return aria;
56
+ }
57
+ const spec = getSpecByTagName(specs, localName, namespace)?.aria;
58
+ if (!spec) {
59
+ cache.set(key, null);
60
+ return null;
61
+ }
62
+ aria = resolveVersion(spec, version);
63
+ if (aria.permittedRoles) {
64
+ aria.permittedRoles = optimizePermittedRoles(aria.permittedRoles);
65
+ }
66
+ cache.set(key, aria);
67
+ return aria;
68
+ }
69
+
70
+ function optimizePermittedRoles(permittedRoles: PermittedRoles) {
71
+ if (!Array.isArray(permittedRoles)) {
72
+ return permittedRoles;
73
+ }
74
+ const unique = new Set(permittedRoles);
75
+
76
+ // https://www.w3.org/TR/wai-aria-1.2/#note-regarding-the-aria-1-1-none-role
77
+ if (unique.has('presentation')) {
78
+ unique.add('none');
79
+ }
80
+ if (unique.has('none')) {
81
+ unique.add('presentation');
82
+ }
83
+
84
+ return Array.from(unique).sort();
85
+ }
@@ -4,10 +4,9 @@ import { getAttrSpecs } from './get-attr-specs';
4
4
 
5
5
  describe('getSpec', () => {
6
6
  test('svg:image[x]', () => {
7
- const specs = getAttrSpecs('svg:image', htmlSpec);
7
+ const specs = getAttrSpecs('image', 'http://www.w3.org/2000/svg', htmlSpec);
8
8
  const x = specs?.find(spec => spec.name === 'x');
9
9
  expect(x).toStrictEqual({
10
- ref: 'https://svgwg.org/svg2-draft/geometry.html#XProperty',
11
10
  defaultValue: '0',
12
11
  name: 'x',
13
12
  type: ['<svg-length>', '<percentage>'],
@@ -16,10 +15,9 @@ describe('getSpec', () => {
16
15
  });
17
16
 
18
17
  test('svg:foreignObject[x]', () => {
19
- const specs = getAttrSpecs('svg:foreignObject', htmlSpec);
18
+ const specs = getAttrSpecs('foreignObject', 'http://www.w3.org/2000/svg', htmlSpec);
20
19
  const x = specs?.find(spec => spec.name === 'x');
21
20
  expect(x).toStrictEqual({
22
- ref: 'https://svgwg.org/svg2-draft/geometry.html#XProperty',
23
21
  defaultValue: '0',
24
22
  description:
25
23
  'The x coordinate of the foreignObject. Value type: <length>|<percentage> ; Default value: 0; Animatable: yes',
@@ -30,10 +28,9 @@ describe('getSpec', () => {
30
28
  });
31
29
 
32
30
  test('svg:linearGradient[xlink:href]', () => {
33
- const specs = getAttrSpecs('svg:linearGradient', htmlSpec);
31
+ const specs = getAttrSpecs('linearGradient', 'http://www.w3.org/2000/svg', htmlSpec);
34
32
  const x = specs?.find(spec => spec.name === 'xlink:href');
35
33
  expect(x).toStrictEqual({
36
- ref: 'https://www.w3.org/TR/xlink11/#link-locators',
37
34
  description:
38
35
  'Deprecated: This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.An <IRI> reference to another <linearGradient> element that will be used as a template. Value type: <IRI> ; Default value: none; Animatable: yes',
39
36
  name: 'xlink:href',
@@ -41,4 +38,10 @@ describe('getSpec', () => {
41
38
  deprecated: true,
42
39
  });
43
40
  });
41
+
42
+ test('img[fetchpriority] (experimental spec)', () => {
43
+ const attrs = getAttrSpecs('img', null, htmlSpec);
44
+ const fetchpriority = attrs?.find(attr => attr.name === 'fetchpriority');
45
+ expect(fetchpriority?.experimental).toBe(true);
46
+ });
44
47
  });
@@ -1,14 +1,19 @@
1
- import type { MLMLSpec, Attribute } from './types';
1
+ import type { MLMLSpec, Attribute } from '../types';
2
+ import type { NamespaceURI } from '@markuplint/ml-ast';
3
+
4
+ import { resolveNamespace } from '../utils/resolve-namespace';
2
5
 
3
6
  const cacheMap = new Map<string, Attribute[] | null>();
4
7
  const schemaCache = new WeakSet<MLMLSpec>();
5
8
 
6
- export function getAttrSpecs(nameWithNS: string, schema: MLMLSpec) {
9
+ export function getAttrSpecs(localName: string, namespace: NamespaceURI | null, schema: MLMLSpec) {
7
10
  if (!schemaCache.has(schema)) {
8
11
  cacheMap.clear();
9
12
  }
10
13
 
11
- const cache = cacheMap.get(nameWithNS);
14
+ const { localNameWithNS } = resolveNamespace(localName, namespace || undefined);
15
+
16
+ const cache = cacheMap.get(localNameWithNS);
12
17
 
13
18
  if (cache !== undefined) {
14
19
  return cache;
@@ -16,9 +21,9 @@ export function getAttrSpecs(nameWithNS: string, schema: MLMLSpec) {
16
21
 
17
22
  schemaCache.add(schema);
18
23
 
19
- const elSpec = schema.specs.find(spec => spec.name === nameWithNS);
24
+ const elSpec = schema.specs.find(spec => spec.name === localNameWithNS);
20
25
  if (!elSpec) {
21
- cacheMap.set(nameWithNS, null);
26
+ cacheMap.set(localNameWithNS, null);
22
27
  return null;
23
28
  }
24
29
 
@@ -78,7 +83,7 @@ export function getAttrSpecs(nameWithNS: string, schema: MLMLSpec) {
78
83
  for (const attr of attrList) {
79
84
  if (!attr.type) {
80
85
  throw new Error(
81
- `The type is empty in the ${attr.name} attribute of the ${nameWithNS} element,
86
+ `The type is empty in the ${attr.name} attribute of the ${localName} element,
82
87
  'packages',
83
88
  '@markuplint',
84
89
  'html-spec',
@@ -92,7 +97,7 @@ export function getAttrSpecs(nameWithNS: string, schema: MLMLSpec) {
92
97
 
93
98
  attrList.sort(nameCompare);
94
99
 
95
- cacheMap.set(nameWithNS, attrList);
100
+ cacheMap.set(localNameWithNS, attrList);
96
101
  return attrList;
97
102
  }
98
103
 
@@ -0,0 +1,81 @@
1
+ import type { ARIAVersion } from '../types';
2
+
3
+ import specs from '@markuplint/html-spec';
4
+ import { createSelector } from '@markuplint/selector';
5
+ import { createJSDOMElement } from '@markuplint/test-tools';
6
+
7
+ import { getImplicitRole } from './get-implicit-role';
8
+
9
+ function c(html: string, version: ARIAVersion) {
10
+ const el = createJSDOMElement(html);
11
+ return getImplicitRole(
12
+ specs,
13
+ el.localName,
14
+ el.namespaceURI,
15
+ version,
16
+ selector => !!createSelector(selector, specs).match(el),
17
+ );
18
+ }
19
+
20
+ describe('getImplicitRole', () => {
21
+ test('the a element', () => {
22
+ expect(c('<a></a>', '1.2')).toBe(false);
23
+ expect(c('<a name="foo"></a>', '1.2')).toBe(false);
24
+ expect(c('<a href></a>', '1.2')).toBe('link');
25
+ expect(c('<a href=""></a>', '1.2')).toBe('link');
26
+ expect(c('<a href="path/to"></a>', '1.2')).toBe('link');
27
+ });
28
+
29
+ test('the area element', () => {
30
+ expect(c('<area />', '1.2')).toBe(false);
31
+ expect(c('<area shape="rect" />', '1.2')).toBe(false);
32
+ expect(c('<area href />', '1.2')).toBe('link');
33
+ expect(c('<area href="" />', '1.2')).toBe('link');
34
+ expect(c('<area href="path/to" />', '1.2')).toBe('link');
35
+ });
36
+
37
+ test('the article element', () => {
38
+ expect(c('<article></article>', '1.2')).toBe('article');
39
+ });
40
+
41
+ test('the aside element', () => {
42
+ expect(c('<aside></aside>', '1.2')).toBe('complementary');
43
+ });
44
+
45
+ test('the audio element', () => {
46
+ expect(c('<audio></audio>', '1.2')).toBe(false);
47
+ });
48
+
49
+ test('the h1 element', () => {
50
+ expect(c('<h1></h1>', '1.2')).toBe('heading');
51
+ });
52
+
53
+ test('the header element', () => {
54
+ expect(c('<header></header>', '1.2')).toBe('banner');
55
+ expect(c('<header><article></article></header>', '1.2')).toBe('generic');
56
+ expect(c('<header><article></article></header>', '1.1')).toBe(false);
57
+ expect(c('<header><div></div></header>', '1.2')).toBe('banner');
58
+ expect(c('<header><div role="article"></div></header>', '1.2')).toBe('generic');
59
+ expect(c('<header><div role="article"></div></header>', '1.1')).toBe(false);
60
+ });
61
+
62
+ test('the img element', () => {
63
+ expect(c('<img />', '1.2')).toBe('img');
64
+ expect(c('<img alt />', '1.2')).toBe('presentation');
65
+ expect(c('<img alt="" />', '1.2')).toBe('presentation');
66
+ expect(c('<img alt="foo" />', '1.2')).toBe('img');
67
+ expect(c('<img aria-label="foo" />', '1.2')).toBe('img');
68
+ });
69
+
70
+ test('the form element', () => {
71
+ expect(c('<form></form>', '1.2')).toBe('form');
72
+ expect(c('<form></form>', '1.1')).toBe(false);
73
+ expect(c('<form aria-label="foo"></form>', '1.2')).toBe('form');
74
+ expect(c('<form aria-label="foo"></form>', '1.1')).toBe('form');
75
+ });
76
+
77
+ test('the section element', () => {
78
+ expect(c('<section></section>', '1.2')).toBe(false);
79
+ expect(c('<section aria-label="foo"></section>', '1.2')).toBe('region');
80
+ });
81
+ });
@@ -0,0 +1,17 @@
1
+ import type { ARIAVersion, Matches, MLMLSpec } from '../types';
2
+
3
+ import { getARIA } from './get-aria';
4
+
5
+ export function getImplicitRole(
6
+ specs: Readonly<MLMLSpec>,
7
+ localName: string,
8
+ namespace: string | null,
9
+ version: ARIAVersion,
10
+ matches: Matches,
11
+ ) {
12
+ const aria = getARIA(specs, localName, namespace, version, matches);
13
+ if (!aria) {
14
+ return false;
15
+ }
16
+ return aria.implicitRole;
17
+ }
@@ -0,0 +1,420 @@
1
+ import type { ARIAVersion } from '../types';
2
+
3
+ import specs from '@markuplint/html-spec';
4
+ import { createSelector } from '@markuplint/selector';
5
+ import { createJSDOMElement } from '@markuplint/test-tools';
6
+
7
+ import { getPermittedRoles } from './get-permitted-roles';
8
+
9
+ function c(html: string, version: ARIAVersion, selector?: string) {
10
+ const el = createJSDOMElement(html, selector);
11
+ const roles = getPermittedRoles(
12
+ specs,
13
+ el.localName,
14
+ el.namespaceURI,
15
+ version,
16
+ selector => !!createSelector(selector, specs).match(el),
17
+ );
18
+ if (Array.isArray(roles)) {
19
+ return roles.map(r => r.name);
20
+ }
21
+ return roles;
22
+ }
23
+
24
+ const coreRoles = [
25
+ 'alert',
26
+ 'alertdialog',
27
+ 'application',
28
+ 'article',
29
+ 'banner',
30
+ 'blockquote',
31
+ 'button',
32
+ 'caption',
33
+ 'cell',
34
+ 'checkbox',
35
+ 'code',
36
+ 'columnheader',
37
+ 'combobox',
38
+ 'complementary',
39
+ 'contentinfo',
40
+ 'definition',
41
+ 'deletion',
42
+ 'dialog',
43
+ 'directory',
44
+ 'document',
45
+ 'emphasis',
46
+ 'feed',
47
+ 'figure',
48
+ 'form',
49
+ 'generic',
50
+ 'grid',
51
+ 'gridcell',
52
+ 'group',
53
+ 'heading',
54
+ 'img',
55
+ 'insertion',
56
+ 'link',
57
+ 'list',
58
+ 'listbox',
59
+ 'listitem',
60
+ 'log',
61
+ 'main',
62
+ 'marquee',
63
+ 'math',
64
+ 'menu',
65
+ 'menubar',
66
+ 'menuitem',
67
+ 'menuitemcheckbox',
68
+ 'menuitemradio',
69
+ 'meter',
70
+ 'navigation',
71
+ 'none',
72
+ 'note',
73
+ 'option',
74
+ 'paragraph',
75
+ 'presentation',
76
+ 'progressbar',
77
+ 'radio',
78
+ 'radiogroup',
79
+ 'region',
80
+ 'row',
81
+ 'rowgroup',
82
+ 'rowheader',
83
+ 'scrollbar',
84
+ 'search',
85
+ 'searchbox',
86
+ 'separator',
87
+ 'slider',
88
+ 'spinbutton',
89
+ 'status',
90
+ 'strong',
91
+ 'subscript',
92
+ 'superscript',
93
+ 'switch',
94
+ 'tab',
95
+ 'table',
96
+ 'tablist',
97
+ 'tabpanel',
98
+ 'term',
99
+ 'textbox',
100
+ 'time',
101
+ 'timer',
102
+ 'toolbar',
103
+ 'tooltip',
104
+ 'tree',
105
+ 'treegrid',
106
+ 'treeitem',
107
+ ];
108
+
109
+ describe('getPermittedRoles', () => {
110
+ test('the a element', () => {
111
+ expect(c('<a></a>', '1.2')).toStrictEqual(coreRoles);
112
+ expect(c('<a href="path/to"></a>', '1.2')).toStrictEqual([
113
+ 'link',
114
+ 'button',
115
+ 'checkbox',
116
+ 'menuitem',
117
+ 'menuitemcheckbox',
118
+ 'menuitemradio',
119
+ 'option',
120
+ 'radio',
121
+ 'switch',
122
+ 'tab',
123
+ 'treeitem',
124
+ ]);
125
+ });
126
+
127
+ test('the area element', () => {
128
+ expect(c('<area></area>', '1.2')).toStrictEqual(['button', 'link']);
129
+ expect(c('<area></area>', '1.1')).toStrictEqual([]);
130
+ expect(c('<area href="path/to"></area>', '1.2')).toStrictEqual(['link']);
131
+ });
132
+
133
+ test('the figure element', () => {
134
+ expect(c('<figure></figure>', '1.2')).toStrictEqual(coreRoles);
135
+ expect(c('<figure><figcaption></figcaption></figure>', '1.2')).toStrictEqual(['figure']);
136
+ });
137
+
138
+ test('the img element', () => {
139
+ expect(c('<img>', '1.2')).toStrictEqual(['img']);
140
+ expect(c('<img alt="">', '1.2')).toStrictEqual(['none', 'presentation']);
141
+ expect(c('<img alt="photo: something">', '1.2')).toStrictEqual([
142
+ 'img',
143
+ 'button',
144
+ 'checkbox',
145
+ 'link',
146
+ 'menuitem',
147
+ 'menuitemcheckbox',
148
+ 'menuitemradio',
149
+ 'option',
150
+ 'progressbar',
151
+ 'radio',
152
+ 'scrollbar',
153
+ 'separator',
154
+ 'slider',
155
+ 'switch',
156
+ 'tab',
157
+ 'treeitem',
158
+ ]);
159
+ expect(c('<img alt="photo: something">', '1.1')).toStrictEqual([
160
+ 'img',
161
+ 'button',
162
+ 'checkbox',
163
+ 'link',
164
+ 'menuitem',
165
+ 'menuitemcheckbox',
166
+ 'menuitemradio',
167
+ 'option',
168
+ 'progressbar',
169
+ 'scrollbar',
170
+ 'separator',
171
+ 'slider',
172
+ 'switch',
173
+ 'tab',
174
+ 'treeitem',
175
+ ]);
176
+ });
177
+
178
+ test('the input element', () => {
179
+ expect(c('<input>', '1.2')).toStrictEqual(['textbox', 'combobox', 'searchbox', 'spinbutton']);
180
+ expect(c('<input>', '1.1')).toStrictEqual(['textbox', 'combobox', 'searchbox', 'spinbutton']);
181
+ expect(c('<input type="button">', '1.2')).toStrictEqual([
182
+ 'button',
183
+ 'checkbox',
184
+ 'combobox',
185
+ 'link',
186
+ 'menuitem',
187
+ 'menuitemcheckbox',
188
+ 'menuitemradio',
189
+ 'option',
190
+ 'radio',
191
+ 'switch',
192
+ 'tab',
193
+ ]);
194
+ expect(c('<input type="button">', '1.1')).toStrictEqual([
195
+ 'button',
196
+ 'link',
197
+ 'menuitem',
198
+ 'menuitemcheckbox',
199
+ 'menuitemradio',
200
+ 'option',
201
+ 'radio',
202
+ 'switch',
203
+ 'tab',
204
+ ]);
205
+ expect(c('<input type="checkbox" aria-pressed="true">', '1.2')).toStrictEqual(['checkbox', 'button']);
206
+ });
207
+
208
+ test('the img element', () => {
209
+ const imgPermitted = [
210
+ 'img',
211
+ 'button',
212
+ 'checkbox',
213
+ 'link',
214
+ 'menuitem',
215
+ 'menuitemcheckbox',
216
+ 'menuitemradio',
217
+ 'option',
218
+ 'progressbar',
219
+ 'radio',
220
+ 'scrollbar',
221
+ 'separator',
222
+ 'slider',
223
+ 'switch',
224
+ 'tab',
225
+ 'treeitem',
226
+ ];
227
+ expect(c('<img />', '1.2')).toStrictEqual(['img']);
228
+ expect(c('<img alt />', '1.2')).toStrictEqual(['none', 'presentation']);
229
+ expect(c('<img alt="" />', '1.2')).toStrictEqual(['none', 'presentation']);
230
+ expect(c('<img alt="foo" />', '1.2')).toStrictEqual(imgPermitted);
231
+ expect(c('<img aria-label="foo" />', '1.2')).toStrictEqual(imgPermitted);
232
+ });
233
+
234
+ test('the form element', () => {
235
+ expect(c('<form></form>', '1.2')).toStrictEqual(['form', 'none', 'presentation', 'search']);
236
+ expect(c('<form></form>', '1.1')).toStrictEqual(['none', 'presentation', 'search']);
237
+ expect(c('<form aria-label="foo"></form>', '1.2')).toStrictEqual(['form', 'none', 'presentation', 'search']);
238
+ expect(c('<form aria-label="foo"></form>', '1.1')).toStrictEqual(['form', 'none', 'presentation', 'search']);
239
+ });
240
+
241
+ test('the svg element', () => {
242
+ expect(c('<svg></svg>', '1.2')).toStrictEqual([
243
+ 'alert',
244
+ 'alertdialog',
245
+ 'application',
246
+ 'article',
247
+ 'banner',
248
+ 'blockquote',
249
+ 'button',
250
+ 'caption',
251
+ 'cell',
252
+ 'checkbox',
253
+ 'code',
254
+ 'columnheader',
255
+ 'combobox',
256
+ 'complementary',
257
+ 'contentinfo',
258
+ 'definition',
259
+ 'deletion',
260
+ 'dialog',
261
+ 'directory',
262
+ 'document',
263
+ 'emphasis',
264
+ 'feed',
265
+ 'figure',
266
+ 'form',
267
+ 'generic',
268
+ 'grid',
269
+ 'gridcell',
270
+ 'group',
271
+ 'heading',
272
+ 'img',
273
+ 'insertion',
274
+ 'link',
275
+ 'list',
276
+ 'listbox',
277
+ 'listitem',
278
+ 'log',
279
+ 'main',
280
+ 'marquee',
281
+ 'math',
282
+ 'menu',
283
+ 'menubar',
284
+ 'menuitem',
285
+ 'menuitemcheckbox',
286
+ 'menuitemradio',
287
+ 'meter',
288
+ 'navigation',
289
+ 'none',
290
+ 'note',
291
+ 'option',
292
+ 'paragraph',
293
+ 'presentation',
294
+ 'progressbar',
295
+ 'radio',
296
+ 'radiogroup',
297
+ 'region',
298
+ 'row',
299
+ 'rowgroup',
300
+ 'rowheader',
301
+ 'scrollbar',
302
+ 'search',
303
+ 'searchbox',
304
+ 'separator',
305
+ 'slider',
306
+ 'spinbutton',
307
+ 'status',
308
+ 'strong',
309
+ 'subscript',
310
+ 'superscript',
311
+ 'switch',
312
+ 'tab',
313
+ 'table',
314
+ 'tablist',
315
+ 'tabpanel',
316
+ 'term',
317
+ 'textbox',
318
+ 'time',
319
+ 'timer',
320
+ 'toolbar',
321
+ 'tooltip',
322
+ 'tree',
323
+ 'treegrid',
324
+ 'treeitem',
325
+ 'graphics-document',
326
+ 'graphics-object',
327
+ 'graphics-symbol',
328
+ ]);
329
+ });
330
+
331
+ test('the rect element', () => {
332
+ expect(c('<svg><rect></rect></svg>', '1.2')).toStrictEqual([
333
+ 'alert',
334
+ 'alertdialog',
335
+ 'application',
336
+ 'article',
337
+ 'banner',
338
+ 'blockquote',
339
+ 'button',
340
+ 'caption',
341
+ 'cell',
342
+ 'checkbox',
343
+ 'code',
344
+ 'columnheader',
345
+ 'combobox',
346
+ 'complementary',
347
+ 'contentinfo',
348
+ 'definition',
349
+ 'deletion',
350
+ 'dialog',
351
+ 'directory',
352
+ 'document',
353
+ 'emphasis',
354
+ 'feed',
355
+ 'figure',
356
+ 'form',
357
+ 'generic',
358
+ 'grid',
359
+ 'gridcell',
360
+ 'group',
361
+ 'heading',
362
+ 'img',
363
+ 'insertion',
364
+ 'link',
365
+ 'list',
366
+ 'listbox',
367
+ 'listitem',
368
+ 'log',
369
+ 'main',
370
+ 'marquee',
371
+ 'math',
372
+ 'menu',
373
+ 'menubar',
374
+ 'menuitem',
375
+ 'menuitemcheckbox',
376
+ 'menuitemradio',
377
+ 'meter',
378
+ 'navigation',
379
+ 'none',
380
+ 'note',
381
+ 'option',
382
+ 'paragraph',
383
+ 'presentation',
384
+ 'progressbar',
385
+ 'radio',
386
+ 'radiogroup',
387
+ 'region',
388
+ 'row',
389
+ 'rowgroup',
390
+ 'rowheader',
391
+ 'scrollbar',
392
+ 'search',
393
+ 'searchbox',
394
+ 'separator',
395
+ 'slider',
396
+ 'spinbutton',
397
+ 'status',
398
+ 'strong',
399
+ 'subscript',
400
+ 'superscript',
401
+ 'switch',
402
+ 'tab',
403
+ 'table',
404
+ 'tablist',
405
+ 'tabpanel',
406
+ 'term',
407
+ 'textbox',
408
+ 'time',
409
+ 'timer',
410
+ 'toolbar',
411
+ 'tooltip',
412
+ 'tree',
413
+ 'treegrid',
414
+ 'treeitem',
415
+ 'graphics-document',
416
+ 'graphics-object',
417
+ 'graphics-symbol',
418
+ ]);
419
+ });
420
+ });