govuk_publishing_components 21.16.3 → 21.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (230) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/component_guide/accessibility-test.js +0 -6
  3. data/app/views/govuk_publishing_components/components/docs/machine_readable_metadata.yml +7 -0
  4. data/lib/govuk_publishing_components/presenters/machine_readable/dataset_schema.rb +34 -0
  5. data/lib/govuk_publishing_components/presenters/schema_org.rb +3 -0
  6. data/lib/govuk_publishing_components/version.rb +1 -1
  7. data/node_modules/axe-core/CHANGELOG.md +166 -2
  8. data/node_modules/axe-core/CONTRIBUTING.md +5 -5
  9. data/node_modules/axe-core/README.md +4 -4
  10. data/node_modules/axe-core/axe.d.ts +27 -11
  11. data/node_modules/axe-core/axe.js +9597 -2431
  12. data/node_modules/axe-core/axe.min.js +2 -2
  13. data/node_modules/axe-core/bower.json +1 -1
  14. data/node_modules/axe-core/doc/API.md +211 -128
  15. data/node_modules/axe-core/doc/accessibility-supported.md +1 -1
  16. data/node_modules/axe-core/doc/aria-supported.md +4 -13
  17. data/node_modules/axe-core/doc/backwards-compatibility-doc.md +93 -0
  18. data/node_modules/axe-core/doc/code-submission-guidelines.md +4 -4
  19. data/node_modules/axe-core/doc/developer-guide.md +27 -13
  20. data/node_modules/axe-core/doc/examples/chrome-debugging-protocol/package.json +5 -2
  21. data/node_modules/axe-core/doc/examples/jasmine/README.md +3 -5
  22. data/node_modules/axe-core/doc/examples/jasmine/karma.conf.js +29 -0
  23. data/node_modules/axe-core/doc/examples/jasmine/package.json +6 -5
  24. data/node_modules/axe-core/doc/examples/jest_react/README.md +1 -1
  25. data/node_modules/axe-core/doc/examples/jest_react/link.test.js +3 -3
  26. data/node_modules/axe-core/doc/examples/jest_react/package.json +9 -9
  27. data/node_modules/axe-core/doc/examples/jsdom/package.json +15 -0
  28. data/node_modules/axe-core/doc/examples/mocha/README.md +5 -7
  29. data/node_modules/axe-core/doc/examples/mocha/karma.conf.js +29 -0
  30. data/node_modules/axe-core/doc/examples/mocha/package.json +7 -6
  31. data/node_modules/axe-core/doc/examples/phantomjs/README.md +3 -3
  32. data/node_modules/axe-core/doc/examples/phantomjs/axe-phantom.js +4 -2
  33. data/node_modules/axe-core/doc/examples/phantomjs/package.json +3 -3
  34. data/node_modules/axe-core/doc/examples/puppeteer/package.json +5 -2
  35. data/node_modules/axe-core/doc/examples/qunit/README.md +2 -2
  36. data/node_modules/axe-core/doc/examples/qunit/package.json +2 -2
  37. data/node_modules/axe-core/doc/examples/test-examples.js +32 -0
  38. data/node_modules/axe-core/doc/plugins.md +10 -10
  39. data/node_modules/axe-core/doc/projects.md +12 -8
  40. data/node_modules/axe-core/doc/rule-descriptions.md +87 -79
  41. data/node_modules/axe-core/doc/rule-development.md +30 -2
  42. data/node_modules/axe-core/lib/checks/aria/allowed-attr.js +1 -1
  43. data/node_modules/axe-core/lib/checks/aria/aria-roledescription.js +14 -0
  44. data/node_modules/axe-core/lib/checks/aria/aria-roledescription.json +23 -0
  45. data/node_modules/axe-core/lib/checks/aria/no-implicit-explicit-label.js +21 -0
  46. data/node_modules/axe-core/lib/checks/aria/no-implicit-explicit-label.json +11 -0
  47. data/node_modules/axe-core/lib/checks/aria/required-attr.js +32 -7
  48. data/node_modules/axe-core/lib/checks/aria/required-children.js +40 -14
  49. data/node_modules/axe-core/lib/checks/aria/required-children.json +12 -1
  50. data/node_modules/axe-core/lib/checks/aria/required-parent.js +1 -1
  51. data/node_modules/axe-core/lib/checks/aria/unsupportedattr.js +1 -1
  52. data/node_modules/axe-core/lib/checks/aria/valid-attr-value.js +50 -17
  53. data/node_modules/axe-core/lib/checks/aria/valid-attr-value.json +2 -1
  54. data/node_modules/axe-core/lib/checks/aria/valid-attr.js +1 -1
  55. data/node_modules/axe-core/lib/checks/color/color-contrast.js +2 -2
  56. data/node_modules/axe-core/lib/checks/forms/autocomplete-appropriate.js +4 -4
  57. data/node_modules/axe-core/lib/checks/forms/autocomplete-valid.js +1 -1
  58. data/node_modules/axe-core/lib/checks/forms/fieldset.json +1 -0
  59. data/node_modules/axe-core/lib/checks/forms/group-labelledby.json +1 -0
  60. data/node_modules/axe-core/lib/checks/keyboard/focusable-content.js +16 -0
  61. data/node_modules/axe-core/lib/checks/keyboard/focusable-content.json +11 -0
  62. data/node_modules/axe-core/lib/checks/keyboard/focusable-element.js +12 -0
  63. data/node_modules/axe-core/lib/checks/keyboard/focusable-element.json +11 -0
  64. data/node_modules/axe-core/lib/checks/keyboard/tabindex.js +6 -1
  65. data/node_modules/axe-core/lib/checks/label/alt-space-value.js +3 -2
  66. data/node_modules/axe-core/lib/checks/label/duplicate-img-label.js +18 -15
  67. data/node_modules/axe-core/lib/checks/label/label-content-name-mismatch.js +13 -3
  68. data/node_modules/axe-core/lib/checks/label/label-content-name-mismatch.json +4 -0
  69. data/node_modules/axe-core/lib/checks/label/multiple-label.js +22 -12
  70. data/node_modules/axe-core/lib/checks/label/multiple-label.json +1 -1
  71. data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique-after.js +23 -0
  72. data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique.js +7 -0
  73. data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique.json +12 -0
  74. data/node_modules/axe-core/lib/checks/lists/listitem.js +1 -0
  75. data/node_modules/axe-core/lib/checks/lists/listitem.json +1 -1
  76. data/node_modules/axe-core/lib/checks/lists/only-listitems.js +0 -4
  77. data/node_modules/axe-core/lib/checks/mobile/css-orientation-lock.js +8 -6
  78. data/node_modules/axe-core/lib/checks/navigation/region.js +2 -19
  79. data/node_modules/axe-core/lib/checks/shared/avoid-inline-spacing.js +18 -0
  80. data/node_modules/axe-core/lib/checks/shared/avoid-inline-spacing.json +11 -0
  81. data/node_modules/axe-core/lib/checks/shared/exists.js +1 -1
  82. data/node_modules/axe-core/lib/checks/shared/exists.json +1 -1
  83. data/node_modules/axe-core/lib/checks/shared/has-alt.js +6 -4
  84. data/node_modules/axe-core/lib/checks/shared/non-empty-alt.js +1 -1
  85. data/node_modules/axe-core/lib/checks/tables/caption-faked.json +1 -1
  86. data/node_modules/axe-core/lib/checks/tables/td-has-header.js +5 -4
  87. data/node_modules/axe-core/lib/checks/tables/th-has-data-cells.js +19 -29
  88. data/node_modules/axe-core/lib/commons/aria/get-owned-virtual.js +1 -1
  89. data/node_modules/axe-core/lib/commons/aria/index.js +50 -46
  90. data/node_modules/axe-core/lib/commons/aria/is-accessible-ref.js +41 -37
  91. data/node_modules/axe-core/lib/commons/aria/label-virtual.js +2 -2
  92. data/node_modules/axe-core/lib/commons/aria/roles.js +1 -1
  93. data/node_modules/axe-core/lib/commons/aria/validate-attr-value.js +0 -1
  94. data/node_modules/axe-core/lib/commons/color/center-point-of-rect.js +30 -0
  95. data/node_modules/axe-core/lib/commons/color/contrast.js +7 -1
  96. data/node_modules/axe-core/lib/commons/color/element-has-image.js +36 -0
  97. data/node_modules/axe-core/lib/commons/color/get-background-color.js +332 -306
  98. data/node_modules/axe-core/lib/commons/color/get-foreground-color.js +35 -6
  99. data/node_modules/axe-core/lib/commons/color/get-own-background-color.js +22 -0
  100. data/node_modules/axe-core/lib/commons/dom/find-up.js +5 -5
  101. data/node_modules/axe-core/lib/commons/dom/get-scroll-offset.js +0 -1
  102. data/node_modules/axe-core/lib/commons/dom/get-tabbable-elements.js +1 -1
  103. data/node_modules/axe-core/lib/commons/dom/has-content-virtual.js +7 -5
  104. data/node_modules/axe-core/lib/commons/dom/is-focusable.js +8 -5
  105. data/node_modules/axe-core/lib/commons/dom/is-hidden-with-css.js +15 -2
  106. data/node_modules/axe-core/lib/commons/dom/is-in-text-block.js +1 -2
  107. data/node_modules/axe-core/lib/commons/dom/is-skip-link.js +45 -0
  108. data/node_modules/axe-core/lib/commons/dom/is-visible.js +43 -17
  109. data/node_modules/axe-core/lib/commons/dom/is-visual-content.js +0 -1
  110. data/node_modules/axe-core/lib/commons/dom/visually-contains.js +0 -1
  111. data/node_modules/axe-core/lib/commons/dom/visually-overlaps.js +0 -1
  112. data/node_modules/axe-core/lib/commons/forms/index.js +8 -0
  113. data/node_modules/axe-core/lib/commons/forms/is-aria-combobox.js +13 -0
  114. data/node_modules/axe-core/lib/commons/forms/is-aria-listbox.js +13 -0
  115. data/node_modules/axe-core/lib/commons/forms/is-aria-range.js +14 -0
  116. data/node_modules/axe-core/lib/commons/forms/is-aria-textbox.js +13 -0
  117. data/node_modules/axe-core/lib/commons/forms/is-native-select.js +13 -0
  118. data/node_modules/axe-core/lib/commons/forms/is-native-textbox.js +28 -0
  119. data/node_modules/axe-core/lib/commons/table/get-cell-position.js +2 -2
  120. data/node_modules/axe-core/lib/commons/table/get-headers.js +55 -11
  121. data/node_modules/axe-core/lib/commons/table/get-scope.js +1 -1
  122. data/node_modules/axe-core/lib/commons/table/is-data-table.js +0 -1
  123. data/node_modules/axe-core/lib/commons/table/to-grid.js +2 -2
  124. data/node_modules/axe-core/lib/commons/text/accessible-text-virtual.js +5 -5
  125. data/node_modules/axe-core/lib/commons/text/form-control-value.js +18 -30
  126. data/node_modules/axe-core/lib/commons/text/is-icon-ligature.js +210 -0
  127. data/node_modules/axe-core/lib/commons/text/is-valid-autocomplete.js +0 -1
  128. data/node_modules/axe-core/lib/commons/text/label-text.js +1 -1
  129. data/node_modules/axe-core/lib/commons/text/label-virtual.js +1 -1
  130. data/node_modules/axe-core/lib/commons/text/native-text-methods.js +1 -1
  131. data/node_modules/axe-core/lib/commons/text/subtree-text.js +3 -3
  132. data/node_modules/axe-core/lib/commons/text/unicode.js +15 -0
  133. data/node_modules/axe-core/lib/commons/text/visible-text-nodes.js +25 -0
  134. data/node_modules/axe-core/lib/commons/text/visible-virtual.js +1 -1
  135. data/node_modules/axe-core/lib/core/base/audit.js +90 -15
  136. data/node_modules/axe-core/lib/core/base/cache.js +33 -0
  137. data/node_modules/axe-core/lib/core/base/check.js +48 -1
  138. data/node_modules/axe-core/lib/core/base/context.js +15 -14
  139. data/node_modules/axe-core/lib/core/base/rule.js +223 -46
  140. data/node_modules/axe-core/lib/core/base/virtual-node/abstract-virtual-node.js +40 -0
  141. data/node_modules/axe-core/lib/core/base/virtual-node/serial-virtual-node.js +86 -0
  142. data/node_modules/axe-core/lib/core/base/virtual-node/virtual-node.js +85 -0
  143. data/node_modules/axe-core/lib/core/constants.js +10 -2
  144. data/node_modules/axe-core/lib/core/imports/index.js +28 -3
  145. data/node_modules/axe-core/lib/core/index.js +2 -4
  146. data/node_modules/axe-core/lib/core/public/configure.js +28 -1
  147. data/node_modules/axe-core/lib/core/public/run-rules.js +2 -0
  148. data/node_modules/axe-core/lib/core/public/run-virtual-rule.js +50 -0
  149. data/node_modules/axe-core/lib/core/public/run.js +13 -2
  150. data/node_modules/axe-core/lib/core/reporters/helpers/process-aggregate.js +1 -1
  151. data/node_modules/axe-core/lib/core/reporters/na.js +4 -0
  152. data/node_modules/axe-core/lib/core/reporters/raw-env.js +12 -0
  153. data/node_modules/axe-core/lib/core/reporters/raw.js +25 -1
  154. data/node_modules/axe-core/lib/core/utils/are-styles-set.js +4 -7
  155. data/node_modules/axe-core/lib/core/utils/assert.js +12 -0
  156. data/node_modules/axe-core/lib/core/utils/collect-results-from-frames.js +2 -2
  157. data/node_modules/axe-core/lib/core/utils/contains.js +27 -13
  158. data/node_modules/axe-core/lib/core/utils/css-parser.js +2 -0
  159. data/node_modules/axe-core/lib/core/utils/element-matches.js +5 -1
  160. data/node_modules/axe-core/lib/core/utils/escape-selector.js +1 -2
  161. data/node_modules/axe-core/lib/core/utils/flattened-tree.js +47 -61
  162. data/node_modules/axe-core/lib/core/utils/get-check-option.js +0 -1
  163. data/node_modules/axe-core/lib/core/utils/get-friendly-uri-end.js +1 -1
  164. data/node_modules/axe-core/lib/core/utils/get-node-attributes.js +21 -0
  165. data/node_modules/axe-core/lib/core/utils/get-scroll.js +39 -0
  166. data/node_modules/axe-core/lib/core/utils/get-selector.js +9 -6
  167. data/node_modules/axe-core/lib/core/utils/get-stylesheet-factory.js +51 -0
  168. data/node_modules/axe-core/lib/core/utils/get-xpath.js +0 -1
  169. data/node_modules/axe-core/lib/core/utils/is-hidden.js +16 -4
  170. data/node_modules/axe-core/lib/core/utils/is-html-element.js +5 -5
  171. data/node_modules/axe-core/lib/core/utils/is-shadow-root.js +3 -3
  172. data/node_modules/axe-core/lib/core/utils/memoize.js +17 -0
  173. data/node_modules/axe-core/lib/core/utils/parse-crossorigin-stylesheet.js +53 -0
  174. data/node_modules/axe-core/lib/core/utils/parse-sameorigin-stylesheet.js +96 -0
  175. data/node_modules/axe-core/lib/core/utils/parse-stylesheet.js +70 -0
  176. data/node_modules/axe-core/lib/core/utils/performance-timer.js +7 -2
  177. data/node_modules/axe-core/lib/core/utils/preload-cssom.js +77 -281
  178. data/node_modules/axe-core/lib/core/utils/preload.js +49 -23
  179. data/node_modules/axe-core/lib/core/utils/qsa.js +39 -50
  180. data/node_modules/axe-core/lib/core/utils/respondable.js +20 -3
  181. data/node_modules/axe-core/lib/core/utils/rule-should-run.js +0 -1
  182. data/node_modules/axe-core/lib/core/utils/scroll-state.js +12 -25
  183. data/node_modules/axe-core/lib/core/utils/select.js +12 -23
  184. data/node_modules/axe-core/lib/core/utils/uuid.js +1 -2
  185. data/node_modules/axe-core/lib/intro.stub +1 -1
  186. data/node_modules/axe-core/lib/misc/incomplete-fallback.json +1 -1
  187. data/node_modules/axe-core/lib/rules/aria-allowed-attr-matches.js +1 -1
  188. data/node_modules/axe-core/lib/rules/aria-form-field-name-matches.js +50 -0
  189. data/node_modules/axe-core/lib/rules/aria-has-attr-matches.js +1 -1
  190. data/node_modules/axe-core/lib/rules/aria-hidden-focus.json +1 -1
  191. data/node_modules/axe-core/lib/rules/aria-input-field-name.json +13 -0
  192. data/node_modules/axe-core/lib/rules/aria-roledescription.json +12 -0
  193. data/node_modules/axe-core/lib/rules/aria-toggle-field-name.json +18 -0
  194. data/node_modules/axe-core/lib/rules/autocomplete-matches.js +14 -10
  195. data/node_modules/axe-core/lib/rules/avoid-inline-spacing.json +12 -0
  196. data/node_modules/axe-core/lib/rules/button-name.json +3 -5
  197. data/node_modules/axe-core/lib/rules/checkboxgroup.json +2 -1
  198. data/node_modules/axe-core/lib/rules/color-contrast-matches.js +37 -22
  199. data/node_modules/axe-core/lib/rules/duplicate-id-active-matches.js +1 -1
  200. data/node_modules/axe-core/lib/rules/duplicate-id-misc-matches.js +2 -2
  201. data/node_modules/axe-core/lib/rules/form-field-multiple-labels.json +1 -1
  202. data/node_modules/axe-core/lib/rules/html-has-lang.json +1 -0
  203. data/node_modules/axe-core/lib/rules/image-alt.json +1 -1
  204. data/node_modules/axe-core/lib/rules/img-redundant-alt.json +3 -3
  205. data/node_modules/axe-core/lib/rules/input-button-name.json +26 -0
  206. data/node_modules/axe-core/lib/rules/landmark-unique-matches.js +41 -0
  207. data/node_modules/axe-core/lib/rules/landmark-unique.json +13 -0
  208. data/node_modules/axe-core/lib/rules/link-name.json +5 -4
  209. data/node_modules/axe-core/lib/rules/meta-refresh.json +8 -1
  210. data/node_modules/axe-core/lib/rules/radiogroup.json +2 -1
  211. data/node_modules/axe-core/lib/rules/role-img-alt.json +18 -0
  212. data/node_modules/axe-core/lib/rules/scrollable-region-focusable-matches.js +30 -0
  213. data/node_modules/axe-core/lib/rules/scrollable-region-focusable.json +12 -0
  214. data/node_modules/axe-core/lib/rules/skip-link-matches.js +1 -1
  215. data/node_modules/axe-core/lib/rules/skip-link.json +1 -1
  216. data/node_modules/axe-core/lib/rules/video-description.json +3 -1
  217. data/node_modules/axe-core/locales/de.json +1 -5
  218. data/node_modules/axe-core/locales/es.json +773 -0
  219. data/node_modules/axe-core/locales/fr.json +15 -19
  220. data/node_modules/axe-core/locales/ja.json +65 -11
  221. data/node_modules/axe-core/locales/ko.json +777 -0
  222. data/node_modules/axe-core/locales/nl.json +35 -35
  223. data/node_modules/axe-core/locales/pt_BR.json +773 -0
  224. data/node_modules/axe-core/package.json +56 -52
  225. data/node_modules/axe-core/sri-history.json +20 -0
  226. data/node_modules/axe-core/typings/axe-core/axe-core-tests.ts +5 -15
  227. data/node_modules/govuk-frontend/package.json +10 -10
  228. metadata +62 -4
  229. data/node_modules/axe-core/doc/axelogo2018.png +0 -0
  230. data/node_modules/axe-core/lib/rules/role-not-button-matches.js +0 -1
@@ -1,7 +1,6 @@
1
1
  /*global RuleResult, createExecutionContext, SupportError */
2
2
 
3
3
  function Rule(spec, parentAudit) {
4
- /*eslint complexity: ["error", 11] */
5
4
  'use strict';
6
5
 
7
6
  this._audit = parentAudit;
@@ -90,16 +89,48 @@ Rule.prototype.matches = function() {
90
89
  /**
91
90
  * Selects `HTMLElement`s based on configured selector
92
91
  * @param {Context} context The resolved Context object
92
+ * @param {Mixed} options Options specific to this rule
93
93
  * @return {Array} All matching `HTMLElement`s
94
94
  */
95
- Rule.prototype.gather = function(context) {
96
- 'use strict';
95
+ Rule.prototype.gather = function(context, options = {}) {
96
+ const markStart = 'mark_gather_start_' + this.id;
97
+ const markEnd = 'mark_gather_end_' + this.id;
98
+ const markHiddenStart = 'mark_isHidden_start_' + this.id;
99
+ const markHiddenEnd = 'mark_isHidden_end_' + this.id;
100
+
101
+ if (options.performanceTimer) {
102
+ axe.utils.performanceTimer.mark(markStart);
103
+ }
104
+
97
105
  var elements = axe.utils.select(this.selector, context);
98
106
  if (this.excludeHidden) {
99
- return elements.filter(function(element) {
107
+ if (options.performanceTimer) {
108
+ axe.utils.performanceTimer.mark(markHiddenStart);
109
+ }
110
+
111
+ elements = elements.filter(function(element) {
100
112
  return !axe.utils.isHidden(element.actualNode);
101
113
  });
114
+
115
+ if (options.performanceTimer) {
116
+ axe.utils.performanceTimer.mark(markHiddenEnd);
117
+ axe.utils.performanceTimer.measure(
118
+ 'rule_' + this.id + '#gather_axe.utils.isHidden',
119
+ markHiddenStart,
120
+ markHiddenEnd
121
+ );
122
+ }
123
+ }
124
+
125
+ if (options.performanceTimer) {
126
+ axe.utils.performanceTimer.mark(markEnd);
127
+ axe.utils.performanceTimer.measure(
128
+ 'rule_' + this.id + '#gather',
129
+ markStart,
130
+ markEnd
131
+ );
102
132
  }
133
+
103
134
  return elements;
104
135
  };
105
136
 
@@ -135,29 +166,46 @@ Rule.prototype.runChecks = function(
135
166
  .catch(reject);
136
167
  };
137
168
 
169
+ /**
170
+ * Run a check for a rule synchronously.
171
+ */
172
+ Rule.prototype.runChecksSync = function(type, node, options, context) {
173
+ 'use strict';
174
+
175
+ const self = this;
176
+ let results = [];
177
+
178
+ this[type].forEach(function(c) {
179
+ const check = self._audit.checks[c.id || c];
180
+ const option = axe.utils.getCheckOption(check, self.id, options);
181
+ results.push(check.runSync(node, option, context));
182
+ });
183
+
184
+ results = results.filter(function(check) {
185
+ return check;
186
+ });
187
+
188
+ return { type: type, results: results };
189
+ };
190
+
138
191
  /**
139
192
  * Runs the Rule's `evaluate` function
140
193
  * @param {Context} context The resolved Context object
141
194
  * @param {Mixed} options Options specific to this rule
142
195
  * @param {Function} callback Function to call when evaluate is complete; receives a RuleResult instance
143
196
  */
144
- Rule.prototype.run = function(context, options, resolve, reject) {
145
- /*eslint max-statements: ["error",17] */
197
+ Rule.prototype.run = function(context, options = {}, resolve, reject) {
198
+ if (options.performanceTimer) {
199
+ this._trackPerformance();
200
+ }
146
201
 
147
202
  const q = axe.utils.queue();
148
203
  const ruleResult = new RuleResult(this);
149
- const markStart = 'mark_rule_start_' + this.id;
150
- const markEnd = 'mark_rule_end_' + this.id;
151
- const markChecksStart = 'mark_runchecks_start_' + this.id;
152
- const markChecksEnd = 'mark_runchecks_end_' + this.id;
153
-
154
204
  let nodes;
155
205
 
156
206
  try {
157
207
  // Matches throws an error when it lacks support for document methods
158
- nodes = this.gather(context).filter(node =>
159
- this.matches(node.actualNode, node, context)
160
- );
208
+ nodes = this.gatherAndMatchNodes(context, options);
161
209
  } catch (error) {
162
210
  // Exit the rule execution if matches fails
163
211
  reject(new SupportError({ cause: error, ruleId: this.id }));
@@ -165,13 +213,7 @@ Rule.prototype.run = function(context, options, resolve, reject) {
165
213
  }
166
214
 
167
215
  if (options.performanceTimer) {
168
- axe.log(
169
- 'gather (',
170
- nodes.length,
171
- '):',
172
- axe.utils.performanceTimer.timeElapsed() + 'ms'
173
- );
174
- axe.utils.performanceTimer.mark(markChecksStart);
216
+ this._logGatherPerformance(nodes);
175
217
  }
176
218
 
177
219
  nodes.forEach(node => {
@@ -186,22 +228,10 @@ Rule.prototype.run = function(context, options, resolve, reject) {
186
228
 
187
229
  checkQueue
188
230
  .then(function(results) {
189
- if (results.length) {
190
- var hasResults = false,
191
- result = {};
192
- results.forEach(function(r) {
193
- var res = r.results.filter(function(result) {
194
- return result;
195
- });
196
- result[r.type] = res;
197
- if (res.length) {
198
- hasResults = true;
199
- }
200
- });
201
- if (hasResults) {
202
- result.node = new axe.utils.DqElement(node.actualNode, options);
203
- ruleResult.nodes.push(result);
204
- }
231
+ const result = getResult(results);
232
+ if (result) {
233
+ result.node = new axe.utils.DqElement(node.actualNode, options);
234
+ ruleResult.nodes.push(result);
205
235
  }
206
236
  resolveNode();
207
237
  })
@@ -214,18 +244,165 @@ Rule.prototype.run = function(context, options, resolve, reject) {
214
244
  q.defer(resolve => setTimeout(resolve, 0));
215
245
 
216
246
  if (options.performanceTimer) {
217
- axe.utils.performanceTimer.mark(markChecksEnd);
218
- axe.utils.performanceTimer.mark(markEnd);
247
+ this._logRulePerformance();
248
+ }
249
+
250
+ q.then(() => resolve(ruleResult)).catch(error => reject(error));
251
+ };
252
+
253
+ /**
254
+ * Runs the Rule's `evaluate` function synchronously
255
+ * @param {Context} context The resolved Context object
256
+ * @param {Mixed} options Options specific to this rule
257
+ */
258
+ Rule.prototype.runSync = function(context, options = {}) {
259
+ if (options.performanceTimer) {
260
+ this._trackPerformance();
261
+ }
262
+
263
+ const ruleResult = new RuleResult(this);
264
+ let nodes;
265
+
266
+ try {
267
+ nodes = this.gatherAndMatchNodes(context, options);
268
+ } catch (error) {
269
+ // Exit the rule execution if matches fails
270
+ throw new SupportError({ cause: error, ruleId: this.id });
271
+ }
272
+
273
+ if (options.performanceTimer) {
274
+ this._logGatherPerformance(nodes);
275
+ }
276
+
277
+ nodes.forEach(node => {
278
+ let results = [];
279
+ ['any', 'all', 'none'].forEach(type => {
280
+ results.push(this.runChecksSync(type, node, options, context));
281
+ });
282
+
283
+ const result = getResult(results);
284
+ if (result) {
285
+ result.node = node.actualNode
286
+ ? new axe.utils.DqElement(node.actualNode, options)
287
+ : null;
288
+ ruleResult.nodes.push(result);
289
+ }
290
+ });
291
+
292
+ if (options.performanceTimer) {
293
+ this._logRulePerformance();
294
+ }
295
+
296
+ return ruleResult;
297
+ };
298
+
299
+ /**
300
+ * Add performance tracking properties to the rule
301
+ * @private
302
+ */
303
+ Rule.prototype._trackPerformance = function() {
304
+ this._markStart = 'mark_rule_start_' + this.id;
305
+ this._markEnd = 'mark_rule_end_' + this.id;
306
+ this._markChecksStart = 'mark_runchecks_start_' + this.id;
307
+ this._markChecksEnd = 'mark_runchecks_end_' + this.id;
308
+ };
309
+
310
+ /**
311
+ * Log performance of rule.gather
312
+ * @private
313
+ * @param {Rule} rule The rule to log
314
+ * @param {Array} nodes Result of rule.gather
315
+ */
316
+ Rule.prototype._logGatherPerformance = function(nodes) {
317
+ axe.log(
318
+ 'gather (',
319
+ nodes.length,
320
+ '):',
321
+ axe.utils.performanceTimer.timeElapsed() + 'ms'
322
+ );
323
+ axe.utils.performanceTimer.mark(this._markChecksStart);
324
+ };
325
+
326
+ /**
327
+ * Log performance of the rule
328
+ * @private
329
+ * @param {Rule} rule The rule to log
330
+ */
331
+ Rule.prototype._logRulePerformance = function() {
332
+ axe.utils.performanceTimer.mark(this._markChecksEnd);
333
+ axe.utils.performanceTimer.mark(this._markEnd);
334
+ axe.utils.performanceTimer.measure(
335
+ 'runchecks_' + this.id,
336
+ this._markChecksStart,
337
+ this._markChecksEnd
338
+ );
339
+
340
+ axe.utils.performanceTimer.measure(
341
+ 'rule_' + this.id,
342
+ this._markStart,
343
+ this._markEnd
344
+ );
345
+ };
346
+
347
+ /**
348
+ * Process the results of each check and return the result if a check
349
+ * has a result
350
+ * @private
351
+ * @param {Array} results Array of each check result
352
+ * @returns {Object|null}
353
+ */
354
+ function getResult(results) {
355
+ if (results.length) {
356
+ let hasResults = false,
357
+ result = {};
358
+ results.forEach(function(r) {
359
+ const res = r.results.filter(function(result) {
360
+ return result;
361
+ });
362
+ result[r.type] = res;
363
+ if (res.length) {
364
+ hasResults = true;
365
+ }
366
+ });
367
+
368
+ if (hasResults) {
369
+ return result;
370
+ }
371
+
372
+ return null;
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Selects `HTMLElement`s based on configured selector and filters them based on
378
+ * the rules matches function
379
+ * @param {Rule} rule The rule to check for after checks
380
+ * @param {Context} context The resolved Context object
381
+ * @param {Mixed} options Options specific to this rule
382
+ * @return {Array} All matching `HTMLElement`s
383
+ */
384
+ Rule.prototype.gatherAndMatchNodes = function(context, options) {
385
+ const markMatchesStart = 'mark_matches_start_' + this.id;
386
+ const markMatchesEnd = 'mark_matches_end_' + this.id;
387
+
388
+ let nodes = this.gather(context, options);
389
+
390
+ if (options.performanceTimer) {
391
+ axe.utils.performanceTimer.mark(markMatchesStart);
392
+ }
393
+
394
+ nodes = nodes.filter(node => this.matches(node.actualNode, node, context));
395
+
396
+ if (options.performanceTimer) {
397
+ axe.utils.performanceTimer.mark(markMatchesEnd);
219
398
  axe.utils.performanceTimer.measure(
220
- 'runchecks_' + this.id,
221
- markChecksStart,
222
- markChecksEnd
399
+ 'rule_' + this.id + '#matches',
400
+ markMatchesStart,
401
+ markMatchesEnd
223
402
  );
224
-
225
- axe.utils.performanceTimer.measure('rule_' + this.id, markStart, markEnd);
226
403
  }
227
404
 
228
- q.then(() => resolve(ruleResult)).catch(error => reject(error));
405
+ return nodes;
229
406
  };
230
407
 
231
408
  /**
@@ -336,7 +513,7 @@ Rule.prototype.after = function(result, options) {
336
513
  * @param {Object} spec - the attributes to be reconfigured
337
514
  */
338
515
  Rule.prototype.configure = function(spec) {
339
- /*eslint complexity:["error",14], max-statements:["error",22], no-eval:0 */
516
+ /*eslint no-eval:0 */
340
517
  'use strict';
341
518
 
342
519
  if (spec.hasOwnProperty('selector')) {
@@ -0,0 +1,40 @@
1
+ const whitespaceRegex = /[\t\r\n\f]/g;
2
+
3
+ class AbstractVirtualNode {
4
+ constructor() {
5
+ this.children = [];
6
+ this.parent = null;
7
+ }
8
+
9
+ get props() {
10
+ throw new Error(
11
+ 'VirtualNode class must have a "props" object consisting ' +
12
+ 'of "nodeType" and "nodeName" properties'
13
+ );
14
+ }
15
+
16
+ attr() {
17
+ throw new Error('VirtualNode class must have a "attr" function');
18
+ }
19
+
20
+ hasAttr() {
21
+ throw new Error('VirtualNode class must have a "hasAttr" function');
22
+ }
23
+
24
+ hasClass(className) {
25
+ // get the value of the class attribute as svgs return a SVGAnimatedString
26
+ // if you access the className property
27
+ let classAttr = this.attr('class');
28
+ if (!classAttr) {
29
+ return false;
30
+ }
31
+
32
+ let selector = ' ' + className + ' ';
33
+ return (
34
+ (' ' + classAttr + ' ').replace(whitespaceRegex, ' ').indexOf(selector) >=
35
+ 0
36
+ );
37
+ }
38
+ }
39
+
40
+ axe.AbstractVirtualNode = AbstractVirtualNode;
@@ -0,0 +1,86 @@
1
+ // eslint-disable-next-line no-unused-vars
2
+ class SerialVirtualNode extends axe.AbstractVirtualNode {
3
+ /**
4
+ * Convert a serialised node into a VirtualNode object
5
+ * @param {Object} node Serialised node
6
+ */
7
+ constructor(serialNode) {
8
+ super();
9
+ this._props = normaliseProps(serialNode);
10
+ this._attrs = normaliseAttrs(serialNode);
11
+ }
12
+
13
+ // Accessof for DOM node properties
14
+ get props() {
15
+ return this._props;
16
+ }
17
+
18
+ /**
19
+ * Get the value of the given attribute name.
20
+ * @param {String} attrName The name of the attribute.
21
+ * @return {String|null} The value of the attribute or null if the attribute does not exist
22
+ */
23
+ attr(attrName) {
24
+ return this._attrs[attrName] || null;
25
+ }
26
+
27
+ /**
28
+ * Determine if the element has the given attribute.
29
+ * @param {String} attrName The name of the attribute
30
+ * @return {Boolean} True if the element has the attribute, false otherwise.
31
+ */
32
+ hasAttr(attrName) {
33
+ return this._attrs[attrName] !== undefined;
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Convert between serialised props and DOM-like properties
39
+ * @param {Object} serialNode
40
+ * @return {Object} normalProperties
41
+ */
42
+ function normaliseProps(serialNode) {
43
+ let { nodeName, nodeType = 1 } = serialNode;
44
+ axe.utils.assert(
45
+ nodeType === 1,
46
+ `nodeType has to be undefined or 1, got '${nodeType}'`
47
+ );
48
+ axe.utils.assert(
49
+ typeof nodeName === 'string',
50
+ `nodeName has to be a string, got '${nodeName}'`
51
+ );
52
+
53
+ const props = {
54
+ ...serialNode,
55
+ nodeType,
56
+ nodeName: nodeName.toLowerCase()
57
+ };
58
+ delete props.attributes;
59
+ return Object.freeze(props);
60
+ }
61
+
62
+ /**
63
+ * Convert between serialised attributes and DOM-like attributes
64
+ * @param {Object} serialNode
65
+ * @return {Object} normalAttributes
66
+ */
67
+ function normaliseAttrs({ attributes = {} }) {
68
+ const attrMap = {
69
+ htmlFor: 'for',
70
+ className: 'class'
71
+ };
72
+
73
+ return Object.keys(attributes).reduce((attrs, attrName) => {
74
+ const value = attributes[attrName];
75
+ axe.utils.assert(
76
+ typeof value !== 'object' || value === null,
77
+ `expects attributes not to be an object, '${attrName}' was`
78
+ );
79
+
80
+ if (value !== undefined) {
81
+ const mappedName = attrMap[attrName] || attrName;
82
+ attrs[mappedName] = value !== null ? String(value) : null;
83
+ }
84
+ return attrs;
85
+ }, {});
86
+ }