diff --git a/__tests__/hyperlinks.js b/__tests__/hyperlinks.js new file mode 100644 index 00000000000..b4362d3d7fd --- /dev/null +++ b/__tests__/hyperlinks.js @@ -0,0 +1,53 @@ +/** + * Note: + * This is a top level test suite, which runs across all the markdown pages under `_rules`, `pages` etc., + * which is why this is not scoped under `__tests__` within any of those sections. + */ +const markdownLinkExtractor = require('markdown-link-extractor') +const isUrl = require('is-url') + +const describeRule = require('../test-utils/describe-rule') +const describePage = require('../test-utils/describe-page') + +const badLinksAndRecommendations = { + '://www.w3.org/TR/WCAG20/': 'Use WCAG 2.1 reference- https://www.w3.org/WAI/WCAG21/', + '://www.w3.org/TR/UNDERSTANDING-WCAG20/': 'Use WCAG 2.1 reference - https://www.w3.org/WAI/WCAG21/Understanding/', + '://www.w3.org/TR/WCAG20-TECHS/': 'Use WCAG 2.1 reference - https://www.w3.org/WAI/WCAG21/Techniques/', + '://www.w3.org/TR/wai-aria-1.0/': 'Use ARIA 1.1 reference - https://www.w3.org/TR/wai-aria-1.1/', + '://www.w3.org/TR/dom41/': 'Use http://dom.spec.whatwg.org', + '://www.w3.org/TR/html/': 'Use http://html.spec.whatwg.org' +}; + +/** + * Find first matching bad link for a given link + * @param {String} link link url + * @returns {String} + */ +const getBadLink = link => { + return Object.keys(badLinksAndRecommendations) + .find(badLink => link.includes(badLink)) +} + +/** + * Test markdown body for outdated links + * @param {String} param markdown body + */ +const validateMarkdownBody = ({ body }) => { + const hyperlinks = markdownLinkExtractor(body).filter(link => isUrl(link)) + if (!hyperlinks || !hyperlinks.length) { + return + } + test.each(hyperlinks)('%s', link => { + const badLink = getBadLink(link) + const recommendation = badLinksAndRecommendations[badLink] + expect(!!badLink, recommendation).toBe(false) + }) +} + +/** + * Validate `Rules` and `Pages` markdown files + */ +describe('Validate hyperlinks are not outdated', () => { + describeRule('hyperlinks', ruleData => validateMarkdownBody(ruleData)) + describePage('hyperlinks', pageData => validateMarkdownBody(pageData)) +}) diff --git a/_rules/__tests__/hyperlinks.js b/_rules/__tests__/hyperlinks.js deleted file mode 100644 index eb52a14cbe0..00000000000 --- a/_rules/__tests__/hyperlinks.js +++ /dev/null @@ -1,33 +0,0 @@ -const describeRule = require('../../test-utils/describe-rule') -const markdownLinkExtractor = require('markdown-link-extractor') -const isUrl = require('is-url') - -const blacklistLinksAndRecommendations = { - '://www.w3.org/TR/WCAG20/': 'Use WCAG 2.1 reference- https://www.w3.org/WAI/WCAG21/', - '://www.w3.org/TR/UNDERSTANDING-WCAG20/': 'Use WCAG 2.1 reference - https://www.w3.org/WAI/WCAG21/Understanding/', - '://www.w3.org/TR/WCAG20-TECHS/': 'Use WCAG 2.1 reference - https://www.w3.org/WAI/WCAG21/Techniques/', - '://www.w3.org/TR/wai-aria-1.0/': 'Use ARIA 1.1 reference - https://www.w3.org/TR/wai-aria-1.1/', - '://www.w3.org/TR/dom41/': 'Use http://dom.spec.whatwg.org', - '://www.w3.org/TR/html/': 'Use http://html.spec.whatwg.org' -}; - -describeRule('hyperlinks', ruleData => { - const { body } = ruleData - const hyperlinks = markdownLinkExtractor(body).filter(link => isUrl(link)) - - /** - * Check that none of links point to WCAG20 - */ - test.each(hyperlinks)('hyperlink does not resolve to outdated content - `%s`', link => { - const badLinks = Object.keys(blacklistLinksAndRecommendations) - const hasBadLink = badLinks.some(badlink => { - if(link.includes(badlink)) { - const recommendation = blacklistLinksAndRecommendations[badlink] - console.log(recommendation) - return true - } - return false - }) - expect(hasBadLink).toBe(false) - }) -}) diff --git a/package-lock.json b/package-lock.json index 66546b047a7..f8a98470f2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11854,14 +11854,6 @@ "is-unc-path": "^1.0.0" } }, - "is-relative-url": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-relative-url/-/is-relative-url-2.0.0.tgz", - "integrity": "sha1-cpAtf+BLPUeS59sV+duEtyBMnO8=", - "requires": { - "is-absolute-url": "^2.0.0" - } - }, "is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", @@ -12687,6 +12679,11 @@ } } }, + "jest-expect-message": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/jest-expect-message/-/jest-expect-message-1.0.2.tgz", + "integrity": "sha512-WFiXMgwS2lOqQZt1iJMI/hOXpUm32X+ApsuzYcQpW5m16Pv6/Gd9kgC+Q+Q1YVNU04kYcAOv9NXMnjg6kKUy6Q==" + }, "jest-extended": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-0.11.2.tgz", diff --git a/package.json b/package.json index 3202ee2c4cc..d571479cac1 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,7 @@ "gfm-code-blocks": "^1.0.0", "globby": "^10.0.1", "is-url": "^1.2.4", + "jest-expect-message": "^1.0.2", "jsonld": "^1.6.2", "make-dir": "^3.0.0", "markdown-link-extractor": "^1.2.1", @@ -202,10 +203,10 @@ } }, "jest": { - "verbose": true, - "bail": true, + "verbose": false, "setupFilesAfterEnv": [ - "jest-extended" + "jest-extended", + "jest-expect-message" ], "testPathIgnorePatterns": [ "/test-utils/", diff --git a/pages/design/definition-of-done.md b/pages/design/definition-of-done.md index 95ffa8f9055..75f71e207f6 100644 --- a/pages/design/definition-of-done.md +++ b/pages/design/definition-of-done.md @@ -15,14 +15,14 @@ The Definition of "Done" is a living document, and might change as the rule writ ## The Definition of "Done" - The relation between the rule and its accessibility requirements is as described in the [ACT Rules Format](https://www.w3.org/TR/act-rules-format/) under [Accessibility Requirements](https://www.w3.org/TR/act-rules-format/#structure-accessibility-requirements). - - Remember to also check that the rule is in line with supporting documentation, e.g. [Understanding WCAG 2.0](https://www.w3.org/TR/UNDERSTANDING-WCAG20/) and [Techniques for WCAG 2.0](https://www.w3.org/TR/WCAG-TECHS/) + - Remember to also check that the rule is in line with supporting documentation, e.g. [Understanding WCAG 2.1](https://www.w3.org/WAI/WCAG21/Understanding/) and [Techniques for WCAG 2.1](https://www.w3.org/WAI/WCAG21/Techniques/) - Applicability (for [atomic](https://www.w3.org/TR/act-rules-format/#test-applicability) / [composed rules](https://www.w3.org/TR/act-rules-format/#aggregation-applicability)) and Expectations (for [atomic](https://www.w3.org/TR/act-rules-format/#test-expectations) / [composed rules](https://www.w3.org/TR/act-rules-format/#aggregation-expectations)) live up to the requirements for these sections in the [ACT Rules Format](https://www.w3.org/TR/act-rules-format/) - Requirements for use of atomic and composed rules are followed, see [Rule Types](https://www.w3.org/TR/act-rules-format/#rule-types) in the [ACT Rules Format](https://www.w3.org/TR/act-rules-format/) - The rule follows the WCAG-ACT-RULES-CG [rule template](/design/rule-template.html), especially in relation to headings, styling, test case descriptions, etc. - The rule is using WCAG-ACT-RULES-CG [Glossary terms]({{ site.url }}/pages/glossary) whenever possible. Be particularly aware of the following much-used algorithms: - For the Applicability, consider if the definitions [included in the accessibility tree](#included-in-the-accessibility-tree) and [visible on the page](#visible-on-the-page) should be used to narrow down the scope of the rule. - For the Applicability and Expectations, consider if the definition for [semantic role](#semantic-role) (including specifics of explicit and implicit semantic role) could be used to describe the targets of the rule. -- The rule links to any relevant documentation, e.g. [Understanding WCAG 2.0](https://www.w3.org/TR/UNDERSTANDING-WCAG20/) and [Techniques for WCAG 2.0](https://www.w3.org/TR/WCAG-TECHS/), specifications used, etc. +- The rule links to any relevant documentation, e.g. [Understanding WCAG 2.1](https://www.w3.org/WAI/WCAG21/Understanding/) and [Techniques for WCAG 2.1](https://www.w3.org/WAI/WCAG21/Techniques/), specifications used, etc. - The name of the rule is written in plain language, with capitalized initial letter - The file name of the rule follows the WCAG-ACT-RULES-CG naming convention - The rule has been spellchecked diff --git a/pages/design/rule-design.md b/pages/design/rule-design.md index 782713dfd10..bd047beba5c 100644 --- a/pages/design/rule-design.md +++ b/pages/design/rule-design.md @@ -51,7 +51,7 @@ Many accessibility evaluations (especially automated tools) make assumptions abo While most assumptions relate to the rule itself, there are some assumptions that apply at other stages of the evaluation: -- It is assumed that the tested web page is the one that has to conform to WCAG 2.0 and that there is no [conforming alternative version](http://www.w3.org/TR/WCAG20/#conforming-alternate-versiondef). +- It is assumed that the tested web page is the one that has to conform to WCAG 2.0 and that there is no [conforming alternative version](https://www.w3.org/TR/WCAG21/#dfn-conforming-alternate-version). - It is assumed that the following technologies are accessibility supported: HTML, CSS, WAI-ARIA, ... (See also WCAG-ACT-RULES-CG's [explanation on Accessibility Support](accessibility-support.html)). diff --git a/pages/glossary/focusable.md b/pages/glossary/focusable.md index e805ff30199..c8d3d6d1438 100644 --- a/pages/glossary/focusable.md +++ b/pages/glossary/focusable.md @@ -3,4 +3,4 @@ title: Focusable key: focusable --- -Elements that can become the target of keyboard input as described in the [HTML](https://www.w3.org/TR/html) specification of [focusable](https://www.w3.org/TR/html/editing.html#focusable) and [can be focused](https://www.w3.org/TR/html/editing.html#can-be-focused). +Elements that can become the target of keyboard input as described in the [HTML](https://www.w3.org/TR/html) specification of [focusable](https://html.spec.whatwg.org/#focusable-area) and [can be focused](https://html.spec.whatwg.org/#specially-focusable). diff --git a/pages/glossary/implicit-role.md b/pages/glossary/implicit-role.md index e1c464b07bb..479f2d3f653 100644 --- a/pages/glossary/implicit-role.md +++ b/pages/glossary/implicit-role.md @@ -3,6 +3,6 @@ title: Implicit Semantic Role key: implicit-role --- -The Implicit Semantic Role is the [semantic role](#semantic-role) of each element that is used when no valid [explicit semantic role](#explicit-role) is specified. Elements with no [role attribute](https://www.w3.org/TR/html/dom.html#aria-role-attribute), or with a [role attribute](https://www.w3.org/TR/role-attribute/) containing no valid token, are mapped to their implicit role. +The Implicit Semantic Role is the [semantic role](#semantic-role) of each element that is used when no valid [explicit semantic role](#explicit-role) is specified. Elements with no [role attribute](https://html.spec.whatwg.org/#attr-aria-role), or with a [role attribute](https://www.w3.org/TR/role-attribute/) containing no valid token, are mapped to their implicit role. Implicit roles for HTML and SVG, are documented in the [HTML accessibility API mappings](https://www.w3.org/TR/html-aam/) (work in progress), the [ARIA in HTML](https://www.w3.org/TR/html-aria/) (work in progress) documentation, and the [SVG accessibility API mappings](https://www.w3.org/TR/svg-aam/) (work in progress). diff --git a/pages/glossary/matching-characters.md b/pages/glossary/matching-characters.md index 1874e1e8a68..87f578ebcfa 100644 --- a/pages/glossary/matching-characters.md +++ b/pages/glossary/matching-characters.md @@ -3,4 +3,4 @@ title: Matching characters key: matching-characters --- -A sequence of [characters](#character) is considered to match another if, after removing leading and trailing [space characters](https://www.w3.org/TR/html/infrastructure.html#space-characters) and replacing remaining occurrences of one or more space characters with a single space, the two sequences of characters are equal character-by-character, ignoring any differences in letter casing. +A sequence of [characters](#character) is considered to match another if, after removing leading and trailing [space characters](https://html.spec.whatwg.org/#white_space) and replacing remaining occurrences of one or more space characters with a single space, the two sequences of characters are equal character-by-character, ignoring any differences in letter casing. diff --git a/pages/glossary/visible-text-content.md b/pages/glossary/visible-text-content.md index c5a2338055d..a0e454a7e9b 100644 --- a/pages/glossary/visible-text-content.md +++ b/pages/glossary/visible-text-content.md @@ -3,6 +3,6 @@ title: Visible Text Content key: visible-text-content --- -Elements that are [visible](#visible), that are of type text, excluding text content in `title` or `alt` attributes, and are not categorised as [non text content](https://www.w3.org/TR/UNDERSTANDING-WCAG20/text-equiv-all.html). +Elements that are [visible](#visible), that are of type text, excluding text content in `title` or `alt` attributes, and are not categorised as [non text content](https://www.w3.org/WAI/WCAG21/Understanding/non-text-content). **Note**: These elements should also be ensured to meet the color contrast and visibility requirements. diff --git a/test-utils/describe-page.js b/test-utils/describe-page.js new file mode 100644 index 00000000000..34e7083a701 --- /dev/null +++ b/test-utils/describe-page.js @@ -0,0 +1,21 @@ +const getPagesMarkdownData = require('../utils/get-pages-markdown-data') + +/** + * describe page helper + * @param {String} groupName name of the `describe` block + * @param {Function} runTests function callback of `describle` block, which executes per page + */ +const describePage = (groupName, runTests) => { + const pages = getPagesMarkdownData() + + pages.forEach(pageData => { + const { filename } = pageData + describe(filename, () => { + describe(groupName, () => { + runTests(pageData) + }) + }) + }) +} + +module.exports = describePage \ No newline at end of file diff --git a/utils/get-pages-markdown-data.js b/utils/get-pages-markdown-data.js new file mode 100644 index 00000000000..f27bb6bb72f --- /dev/null +++ b/utils/get-pages-markdown-data.js @@ -0,0 +1,10 @@ +const globby = require('globby') +const getMarkdownData = require('./get-markdown-data') + +/** + * Read all pages & parse the markdown + */ +const getPagesMarkdownData = () => globby.sync([`./pages/**/*.md`]) + .map(path => getMarkdownData(path)) + +module.exports = getPagesMarkdownData \ No newline at end of file