govuk_publishing_components 21.16.3 → 21.17.0

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 (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
+ }