diff --git a/Gruntfile.js b/Gruntfile.js index 1e73e90eac..6a5427c13b 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,5 +1,5 @@ /*eslint complexity: ["error",12], max-statements: ["error", 30], - camelcase: ["error", {"properties": "never"}]*/ +camelcase: ["error", {"properties": "never"}]*/ var testConfig = require('./build/test/config'); module.exports = function (grunt) { diff --git a/build/configure.js b/build/configure.js index b22fbc3fc8..b952f735e1 100644 --- a/build/configure.js +++ b/build/configure.js @@ -6,9 +6,9 @@ var clone = require('clone'); var dot = require('dot'); var templates = require('./templates'); var buildManual = require('./build-manual'); -var entities = new (require('html-entities').AllHtmlEntities)(); +var entities = new (require('html-entities').AllHtmlEntities)(); -var descriptionHeaders = '| Rule ID | Description | Tags | Enabled by default |\n| :------- | :------- | :------- | :------- |\n'; +var descriptionHeaders = '| Rule ID | Description | Impact | Tags | Enabled by default |\n| :------- | :------- | :------- | :------- | :------- |\n'; dot.templateSettings.strip = false; @@ -24,10 +24,29 @@ function getLocale(grunt, options) { } function buildRules(grunt, options, commons, callback) { + var axeImpact = Object.freeze(['minor', 'moderate', 'serious', 'critical']); // TODO: require('../axe') does not work if grunt configure is moved after uglify, npm test breaks with undefined. Complicated grunt concurrency issue. var locale = getLocale(grunt, options); options.getFiles = false; buildManual(grunt, options, commons, function (result) { + var metadata = { + rules: {}, + checks: {} + }; + var descriptions = []; + var tags = options.tags ? options.tags.split(/\s*,\s*/) : []; + var rules = result.rules; + var checks = result.checks; + + // Translate checks + if (locale && locale.checks) { + checks.forEach(function (check) { + if (locale.checks[check.id] && check.metadata) { + check.metadata.messages = locale.checks[check.id]; + } + }) + } + function parseMetaData(source, propType) { var data = source.metadata var key = source.id || source.type @@ -64,7 +83,7 @@ function buildRules(grunt, options, commons, callback) { function getIncompleteMsg(summaries) { var result = {}; - summaries.forEach(function(summary) { + summaries.forEach(function (summary) { if (summary.incompleteFallbackMessage) { result = dot.template(summary.incompleteFallbackMessage).toString(); } @@ -72,7 +91,6 @@ function buildRules(grunt, options, commons, callback) { return result; } - function replaceFunctions(string) { return string.replace(/"(evaluate|after|gather|matches|source|commons)":\s*("[^"]+?")/g, function (m, p1, p2) { return m.replace(p2, getSource(p2.replace(/^"|"$/g, ''), p1)); @@ -81,7 +99,6 @@ function buildRules(grunt, options, commons, callback) { }).replace(/"(\(function \(\) {)([\s\S]+?)(}\)\(\))"/g, function (m) { return JSON.parse(m); }); - } function getSource(file, type) { @@ -107,27 +124,6 @@ function buildRules(grunt, options, commons, callback) { return v; } - var metadata = { - rules: {}, - checks: {} - }; - - var descriptions = []; - - var tags = options.tags ? options.tags.split(/\s*,\s*/) : []; - - var rules = result.rules; - var checks = result.checks; - - // Translate checks - if (locale && locale.checks) { - checks.forEach(function (check) { - if (locale.checks[check.id] && check.metadata) { - check.metadata.messages = locale.checks[check.id] - } - }) - } - function parseChecks(collection) { return collection.map(function (check) { var c = {}; @@ -145,19 +141,77 @@ function buildRules(grunt, options, commons, callback) { return c.options === undefined ? id : c; }); + } + + function parseImpactForRule(rule) { + + function capitalize(s) { + return s.charAt(0).toUpperCase() + s.slice(1); + } + + function getUniqueArr(arr) { + return arr.filter(function (value, index, self) { + return self.indexOf(value) === index; + }) + } + + function getImpactScores(checkCollection) { + return checkCollection.reduce(function (out, check) { + var id = typeof check === 'string' ? check : check.id; + var definition = clone(findCheck(checks, id)); + if (!definition) { + grunt.log.error('check ' + id + ' not found'); + } + if (definition && definition.metadata && definition.metadata.impact) { + var impactScore = axeImpact.indexOf(definition.metadata.impact); + out.push(impactScore); + } + return out; + }, []); + } + + function getScore(checkCollection, onlyHighestScore) { + var scores = getImpactScores(checkCollection); + if (scores && scores.length) { + return onlyHighestScore + ? [Math.max.apply(null, scores)] + : getUniqueArr(scores); + } else { + return []; + } + } + + var highestImpactForRuleTypeAny = getScore(rule.any, true); + var allUniqueImpactsForRuleTypeAll = getScore(rule.all, false); + var allUniqueImpactsForRuleTypeNone = getScore(rule.none, false); + var cumulativeImpacts = highestImpactForRuleTypeAny.concat(allUniqueImpactsForRuleTypeAll).concat(allUniqueImpactsForRuleTypeNone); + var cumulativeScores = getUniqueArr(cumulativeImpacts).sort(); //order lowest to highest + + return cumulativeScores.reduce(function (out, cV) { + return out.length + ? out + ', ' + capitalize(axeImpact[cV]) + : capitalize(axeImpact[cV]); + }, ''); } + rules.map(function (rule) { + + var impact = parseImpactForRule(rule); rule.any = parseChecks(rule.any); rule.all = parseChecks(rule.all); rule.none = parseChecks(rule.none); - if (rule.metadata && !metadata.rules[rule.id]) { - // Translate rules - metadata.rules[rule.id] = parseMetaData(rule, 'rules'); + metadata.rules[rule.id] = parseMetaData(rule, 'rules'); // Translate rules } - descriptions.push([rule.id, entities.encode(rule.metadata.description), rule.tags.join(', '), rule.enabled === false ? false : true]); + descriptions.push([ + rule.id, + entities.encode(rule.metadata.description), + impact, + rule.tags.join(', '), + rule.enabled === false ? false : true + ]); if (tags.length) { rule.enabled = !!rule.tags.filter(function (t) { return tags.indexOf(t) !== -1; diff --git a/doc/rule-descriptions.md b/doc/rule-descriptions.md index 68e1a42261..7061cdc46d 100644 --- a/doc/rule-descriptions.md +++ b/doc/rule-descriptions.md @@ -1,69 +1,69 @@ -| Rule ID | Description | Tags | Enabled by default | -| :------- | :------- | :------- | :------- | -| accesskeys | Ensures every accesskey attribute value is unique | wcag2a, wcag211, cat.keyboard | true | -| area-alt | Ensures <area> elements of image maps have alternate text | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a | true | -| aria-allowed-attr | Ensures ARIA attributes are allowed for an element's role | cat.aria, wcag2a, wcag411, wcag412 | true | -| aria-dpub-role-fallback | Ensures unsupported DPUB roles are only used on elements with implicit fallback roles | cat.aria, wcag2a, wcag131 | true | -| aria-hidden-body | Ensures aria-hidden='true' is not present on the document body. | cat.aria, wcag2a, wcag412 | true | -| aria-required-attr | Ensures elements with ARIA roles have all required ARIA attributes | cat.aria, wcag2a, wcag411, wcag412 | true | -| aria-required-children | Ensures elements with an ARIA role that require child roles contain them | cat.aria, wcag2a, wcag131 | true | -| aria-required-parent | Ensures elements with an ARIA role that require parent roles are contained by them | cat.aria, wcag2a, wcag131 | true | -| aria-roles | Ensures all elements with a role attribute use a valid value | cat.aria, wcag2a, wcag131, wcag411, wcag412 | true | -| aria-valid-attr-value | Ensures all ARIA attributes have valid values | cat.aria, wcag2a, wcag131, wcag411, wcag412 | true | -| aria-valid-attr | Ensures attributes that begin with aria- are valid ARIA attributes | cat.aria, wcag2a, wcag411 | true | -| audio-caption | Ensures <audio> elements have captions | cat.time-and-media, wcag2a, wcag121, section508, section508.22.a | true | -| blink | Ensures <blink> elements are not used | cat.time-and-media, wcag2a, wcag222, section508, section508.22.j | true | -| button-name | Ensures buttons have discernible text | cat.name-role-value, wcag2a, wcag412, section508, section508.22.a | true | -| bypass | Ensures each page has at least one mechanism for a user to bypass navigation and jump straight to the content | cat.keyboard, wcag2a, wcag241, section508, section508.22.o | true | -| checkboxgroup | Ensures related <input type="checkbox"> elements have a group and that the group designation is consistent | cat.forms, best-practice | true | -| color-contrast | Ensures the contrast between foreground and background colors meets WCAG 2 AA contrast ratio thresholds | cat.color, wcag2aa, wcag143 | true | -| definition-list | Ensures <dl> elements are structured correctly | cat.structure, wcag2a, wcag131 | true | -| dlitem | Ensures <dt> and <dd> elements are contained by a <dl> | cat.structure, wcag2a, wcag131 | true | -| document-title | Ensures each HTML document contains a non-empty <title> element | cat.text-alternatives, wcag2a, wcag242 | true | -| duplicate-id | Ensures every id attribute value is unique | cat.parsing, wcag2a, wcag411 | true | -| empty-heading | Ensures headings have discernible text | cat.name-role-value, best-practice | true | -| focus-order-semantics | Ensures elements in the focus order have an appropriate role | cat.keyboard, best-practice, experimental | true | -| frame-tested | Ensures <iframe> and <frame> elements contain the axe-core script | cat.structure, review-item | true | -| frame-title-unique | Ensures <iframe> and <frame> elements contain a unique title attribute | cat.text-alternatives, best-practice | true | -| frame-title | Ensures <iframe> and <frame> elements contain a non-empty title attribute | cat.text-alternatives, wcag2a, wcag241, section508, section508.22.i | true | -| heading-order | Ensures the order of headings is semantically correct | cat.semantics, best-practice | true | -| hidden-content | Informs users about hidden content. | cat.structure, experimental, review-item | true | -| html-has-lang | Ensures every HTML document has a lang attribute | cat.language, wcag2a, wcag311 | true | -| html-lang-valid | Ensures the lang attribute of the <html> element has a valid value | cat.language, wcag2a, wcag311 | true | -| image-alt | Ensures <img> elements have alternate text or a role of none or presentation | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a | true | -| image-redundant-alt | Ensure button and link text is not repeated as image alternative | cat.text-alternatives, best-practice | true | -| input-image-alt | Ensures <input type="image"> elements have alternate text | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a | true | -| label-title-only | Ensures that every form element is not solely labeled using the title or aria-describedby attributes | cat.forms, best-practice | true | -| label | Ensures every form element has a label | cat.forms, wcag2a, wcag332, wcag131, section508, section508.22.n | true | -| landmark-banner-is-top-level | The banner landmark should not be contained in another landmark | cat.semantics, best-practice | true | -| landmark-contentinfo-is-top-level | The contentinfo landmark should not be contained in another landmark | cat.semantics, best-practice | true | -| landmark-main-is-top-level | The main landmark should not be contained in another landmark | cat.semantics, best-practice | true | -| landmark-no-duplicate-banner | Ensures the document has no more than one banner landmark | cat.semantics, best-practice | true | -| landmark-no-duplicate-contentinfo | Ensures the document has no more than one contentinfo landmark | cat.semantics, best-practice | true | -| landmark-one-main | Ensures a navigation point to the primary content of the page. If the page contains iframes, each iframe should contain either no main landmarks or just one | cat.semantics, best-practice | true | -| layout-table | Ensures presentational <table> elements do not use <th>, <caption> elements or the summary attribute | cat.semantics, wcag2a, wcag131 | true | -| link-in-text-block | Links can be distinguished without relying on color | cat.color, experimental, wcag2a, wcag141 | true | -| link-name | Ensures links have discernible text | cat.name-role-value, wcag2a, wcag111, wcag412, wcag244, section508, section508.22.a | true | -| list | Ensures that lists are structured correctly | cat.structure, wcag2a, wcag131 | true | -| listitem | Ensures <li> elements are used semantically | cat.structure, wcag2a, wcag131 | true | -| marquee | Ensures <marquee> elements are not used | cat.parsing, wcag2a, wcag222 | true | -| meta-refresh | Ensures <meta http-equiv="refresh"> is not used | cat.time, wcag2a, wcag2aaa, wcag221, wcag224, wcag325 | true | -| meta-viewport-large | Ensures <meta name="viewport"> can scale a significant amount | cat.sensory-and-visual-cues, best-practice | true | -| meta-viewport | Ensures <meta name="viewport"> does not disable text scaling and zooming | cat.sensory-and-visual-cues, wcag2aa, wcag144 | true | -| object-alt | Ensures <object> elements have alternate text | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a | true | -| p-as-heading | Ensure p elements are not used to style headings | cat.semantics, wcag2a, wcag131, experimental | true | -| page-has-heading-one | Ensure that the page, or at least one of its frames contains a level-one heading | cat.semantics, best-practice | true | -| radiogroup | Ensures related <input type="radio"> elements have a group and that the group designation is consistent | cat.forms, best-practice | true | -| region | Ensures all content is contained within a landmark region | cat.keyboard, best-practice | true | -| scope-attr-valid | Ensures the scope attribute is used correctly on tables | cat.tables, best-practice | true | -| server-side-image-map | Ensures that server-side image maps are not used | cat.text-alternatives, wcag2a, wcag211, section508, section508.22.f | true | -| skip-link | Ensure all skip links have a focusable target | cat.keyboard, best-practice | true | -| tabindex | Ensures tabindex attribute values are not greater than 0 | cat.keyboard, best-practice | true | -| table-duplicate-name | Ensure that tables do not have the same summary and caption | cat.tables, best-practice | true | -| table-fake-caption | Ensure that tables with a caption use the <caption> element. | cat.tables, experimental, wcag2a, wcag131, section508, section508.22.g | true | -| td-has-header | Ensure that each non-empty data cell in a large table has one or more table headers | cat.tables, experimental, wcag2a, wcag131, section508, section508.22.g | true | -| td-headers-attr | Ensure that each cell in a table using the headers refers to another cell in that table | cat.tables, wcag2a, wcag131, section508, section508.22.g | true | -| th-has-data-cells | Ensure that each table header in a data table refers to data cells | cat.tables, wcag2a, wcag131, section508, section508.22.g | true | -| valid-lang | Ensures lang attributes have valid values | cat.language, wcag2aa, wcag312 | true | -| video-caption | Ensures <video> elements have captions | cat.text-alternatives, wcag2a, wcag122, section508, section508.22.a | true | -| video-description | Ensures <video> elements have audio descriptions | cat.text-alternatives, wcag2aa, wcag125, section508, section508.22.b | true | \ No newline at end of file +| Rule ID | Description | Impact | Tags | Enabled by default | +| :------- | :------- | :------- | :------- | :------- | +| accesskeys | Ensures every accesskey attribute value is unique | Serious | wcag2a, wcag211, cat.keyboard | true | +| area-alt | Ensures <area> elements of image maps have alternate text | Critical | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a | true | +| aria-allowed-attr | Ensures ARIA attributes are allowed for an element's role | Critical | cat.aria, wcag2a, wcag411, wcag412 | true | +| aria-dpub-role-fallback | Ensures unsupported DPUB roles are only used on elements with implicit fallback roles | Moderate | cat.aria, wcag2a, wcag131 | true | +| aria-hidden-body | Ensures aria-hidden='true' is not present on the document body. | Critical | cat.aria, wcag2a, wcag412 | true | +| aria-required-attr | Ensures elements with ARIA roles have all required ARIA attributes | Critical | cat.aria, wcag2a, wcag411, wcag412 | true | +| aria-required-children | Ensures elements with an ARIA role that require child roles contain them | Critical | cat.aria, wcag2a, wcag131 | true | +| aria-required-parent | Ensures elements with an ARIA role that require parent roles are contained by them | Critical | cat.aria, wcag2a, wcag131 | true | +| aria-roles | Ensures all elements with a role attribute use a valid value | Serious, Critical | cat.aria, wcag2a, wcag131, wcag411, wcag412 | true | +| aria-valid-attr-value | Ensures all ARIA attributes have valid values | Critical | cat.aria, wcag2a, wcag131, wcag411, wcag412 | true | +| aria-valid-attr | Ensures attributes that begin with aria- are valid ARIA attributes | Critical | cat.aria, wcag2a, wcag411 | true | +| audio-caption | Ensures <audio> elements have captions | Critical | cat.time-and-media, wcag2a, wcag121, section508, section508.22.a | true | +| blink | Ensures <blink> elements are not used | Serious | cat.time-and-media, wcag2a, wcag222, section508, section508.22.j | true | +| button-name | Ensures buttons have discernible text | Serious, Critical | cat.name-role-value, wcag2a, wcag412, section508, section508.22.a | true | +| bypass | Ensures each page has at least one mechanism for a user to bypass navigation and jump straight to the content | Serious | cat.keyboard, wcag2a, wcag241, section508, section508.22.o | true | +| checkboxgroup | Ensures related <input type="checkbox"> elements have a group and that the group designation is consistent | Critical | cat.forms, best-practice | true | +| color-contrast | Ensures the contrast between foreground and background colors meets WCAG 2 AA contrast ratio thresholds | Serious | cat.color, wcag2aa, wcag143 | true | +| definition-list | Ensures <dl> elements are structured correctly | Serious | cat.structure, wcag2a, wcag131 | true | +| dlitem | Ensures <dt> and <dd> elements are contained by a <dl> | Serious | cat.structure, wcag2a, wcag131 | true | +| document-title | Ensures each HTML document contains a non-empty <title> element | Serious | cat.text-alternatives, wcag2a, wcag242 | true | +| duplicate-id | Ensures every id attribute value is unique | Moderate | cat.parsing, wcag2a, wcag411 | true | +| empty-heading | Ensures headings have discernible text | Minor | cat.name-role-value, best-practice | true | +| focus-order-semantics | Ensures elements in the focus order have an appropriate role | Minor | cat.keyboard, best-practice, experimental | true | +| frame-tested | Ensures <iframe> and <frame> elements contain the axe-core script | Critical | cat.structure, review-item | true | +| frame-title-unique | Ensures <iframe> and <frame> elements contain a unique title attribute | Serious | cat.text-alternatives, best-practice | true | +| frame-title | Ensures <iframe> and <frame> elements contain a non-empty title attribute | Serious | cat.text-alternatives, wcag2a, wcag241, section508, section508.22.i | true | +| heading-order | Ensures the order of headings is semantically correct | Moderate | cat.semantics, best-practice | true | +| hidden-content | Informs users about hidden content. | Minor | cat.structure, experimental, review-item | true | +| html-has-lang | Ensures every HTML document has a lang attribute | Serious | cat.language, wcag2a, wcag311 | true | +| html-lang-valid | Ensures the lang attribute of the <html> element has a valid value | Serious | cat.language, wcag2a, wcag311 | true | +| image-alt | Ensures <img> elements have alternate text or a role of none or presentation | Critical | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a | true | +| image-redundant-alt | Ensure button and link text is not repeated as image alternative | Minor | cat.text-alternatives, best-practice | true | +| input-image-alt | Ensures <input type="image"> elements have alternate text | Critical | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a | true | +| label-title-only | Ensures that every form element is not solely labeled using the title or aria-describedby attributes | Serious | cat.forms, best-practice | true | +| label | Ensures every form element has a label | Minor, Serious, Critical | cat.forms, wcag2a, wcag332, wcag131, section508, section508.22.n | true | +| landmark-banner-is-top-level | The banner landmark should not be contained in another landmark | Moderate | cat.semantics, best-practice | true | +| landmark-contentinfo-is-top-level | The contentinfo landmark should not be contained in another landmark | Moderate | cat.semantics, best-practice | true | +| landmark-main-is-top-level | The main landmark should not be contained in another landmark | Moderate | cat.semantics, best-practice | true | +| landmark-no-duplicate-banner | Ensures the document has no more than one banner landmark | Moderate | cat.semantics, best-practice | true | +| landmark-no-duplicate-contentinfo | Ensures the document has no more than one contentinfo landmark | Moderate | cat.semantics, best-practice | true | +| landmark-one-main | Ensures a navigation point to the primary content of the page. If the page contains iframes, each iframe should contain either no main landmarks or just one | Moderate | cat.semantics, best-practice | true | +| layout-table | Ensures presentational <table> elements do not use <th>, <caption> elements or the summary attribute | Serious | cat.semantics, wcag2a, wcag131 | true | +| link-in-text-block | Links can be distinguished without relying on color | Serious | cat.color, experimental, wcag2a, wcag141 | true | +| link-name | Ensures links have discernible text | Serious | cat.name-role-value, wcag2a, wcag111, wcag412, wcag244, section508, section508.22.a | true | +| list | Ensures that lists are structured correctly | Serious | cat.structure, wcag2a, wcag131 | true | +| listitem | Ensures <li> elements are used semantically | Serious | cat.structure, wcag2a, wcag131 | true | +| marquee | Ensures <marquee> elements are not used | Serious | cat.parsing, wcag2a, wcag222 | true | +| meta-refresh | Ensures <meta http-equiv="refresh"> is not used | Critical | cat.time, wcag2a, wcag2aaa, wcag221, wcag224, wcag325 | true | +| meta-viewport-large | Ensures <meta name="viewport"> can scale a significant amount | Minor | cat.sensory-and-visual-cues, best-practice | true | +| meta-viewport | Ensures <meta name="viewport"> does not disable text scaling and zooming | Critical | cat.sensory-and-visual-cues, wcag2aa, wcag144 | true | +| object-alt | Ensures <object> elements have alternate text | Serious | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a | true | +| p-as-heading | Ensure p elements are not used to style headings | Serious | cat.semantics, wcag2a, wcag131, experimental | true | +| page-has-heading-one | Ensure that the page, or at least one of its frames contains a level-one heading | Moderate | cat.semantics, best-practice | true | +| radiogroup | Ensures related <input type="radio"> elements have a group and that the group designation is consistent | Critical | cat.forms, best-practice | true | +| region | Ensures all content is contained within a landmark region | Moderate | cat.keyboard, best-practice | true | +| scope-attr-valid | Ensures the scope attribute is used correctly on tables | Moderate, Critical | cat.tables, best-practice | true | +| server-side-image-map | Ensures that server-side image maps are not used | Minor | cat.text-alternatives, wcag2a, wcag211, section508, section508.22.f | true | +| skip-link | Ensure all skip links have a focusable target | Moderate | cat.keyboard, best-practice | true | +| tabindex | Ensures tabindex attribute values are not greater than 0 | Serious | cat.keyboard, best-practice | true | +| table-duplicate-name | Ensure that tables do not have the same summary and caption | Minor | cat.tables, best-practice | true | +| table-fake-caption | Ensure that tables with a caption use the <caption> element. | Serious | cat.tables, experimental, wcag2a, wcag131, section508, section508.22.g | true | +| td-has-header | Ensure that each non-empty data cell in a large table has one or more table headers | Critical | cat.tables, experimental, wcag2a, wcag131, section508, section508.22.g | true | +| td-headers-attr | Ensure that each cell in a table using the headers refers to another cell in that table | Serious | cat.tables, wcag2a, wcag131, section508, section508.22.g | true | +| th-has-data-cells | Ensure that each table header in a data table refers to data cells | Serious | cat.tables, wcag2a, wcag131, section508, section508.22.g | true | +| valid-lang | Ensures lang attributes have valid values | Serious | cat.language, wcag2aa, wcag312 | true | +| video-caption | Ensures <video> elements have captions | Critical | cat.text-alternatives, wcag2a, wcag122, section508, section508.22.a | true | +| video-description | Ensures <video> elements have audio descriptions | Critical | cat.text-alternatives, wcag2aa, wcag125, section508, section508.22.b | true | \ No newline at end of file diff --git a/lib/core/constants.js b/lib/core/constants.js index d49b5a5f1d..8f6169725e 100644 --- a/lib/core/constants.js +++ b/lib/core/constants.js @@ -30,7 +30,6 @@ impact: Object.freeze(['minor', 'moderate', 'serious', 'critical']) }; - definitions.forEach(function (definition) { var name = definition.name; var value = definition.value;