From 3667da09d3158b1242d1ec0d24b8b6325dc9323f Mon Sep 17 00:00:00 2001 From: "aaron_chu@apple.com" Date: Sat, 16 Feb 2019 03:51:41 +0000 Subject: [PATCH] AX: Audit tab should have built-in accessibility tests. https://bugs.webkit.org/show_bug.cgi?id=194005 Updated built-in accessibility audits test suite. Reviewed by Devin Rousso. * Localizations/en.lproj/localizedStrings.js: * UserInterface/Controllers/AuditManager.js: (WI.AuditManager.prototype.addDefaultTestsIfNeeded): (WI.AuditManager): (WI.AuditManager.prototype.addDefaultTestsIfNeeded.): Deleted. git-svn-id: https://svn.webkit.org/repository/webkit/trunk@241639 268f45cc-cd09-0410-ab3c-d52691b4dbfc --- Source/WebInspectorUI/ChangeLog | 16 + .../Localizations/en.lproj/localizedStrings.js | 30 +- .../UserInterface/Controllers/AuditManager.js | 579 ++++++++++++++++++++- 3 files changed, 590 insertions(+), 35 deletions(-) diff --git a/Source/WebInspectorUI/ChangeLog b/Source/WebInspectorUI/ChangeLog index 7c250e8..7218b85 100644 --- a/Source/WebInspectorUI/ChangeLog +++ b/Source/WebInspectorUI/ChangeLog @@ -1,3 +1,19 @@ +2019-02-15 Aaron Chu + + AX: Audit tab should have built-in accessibility tests. + https://bugs.webkit.org/show_bug.cgi?id=194005 + + + Updated built-in accessibility audits test suite. + + Reviewed by Devin Rousso. + + * Localizations/en.lproj/localizedStrings.js: + * UserInterface/Controllers/AuditManager.js: + (WI.AuditManager.prototype.addDefaultTestsIfNeeded): + (WI.AuditManager): + (WI.AuditManager.prototype.addDefaultTestsIfNeeded.): Deleted. + 2019-02-15 Nikita Vasilyev Web Inspector: Dark Mode: commas in CSS selectors are too dim diff --git a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js index 651be14..6fd8ea7 100644 --- a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js +++ b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js @@ -304,6 +304,7 @@ localizedStrings["Detach into separate window"] = "Detach into separate window"; localizedStrings["Detached"] = "Detached"; localizedStrings["Details"] = "Details"; localizedStrings["Device Settings"] = "Device Settings"; +localizedStrings["Diagnoses common accessibility problems affecting screen readers and other assistive technology."] = "Diagnoses common accessibility problems affecting screen readers and other assistive technology."; localizedStrings["Dimensions"] = "Dimensions"; localizedStrings["Disable Breakpoint"] = "Disable Breakpoint"; localizedStrings["Disable Breakpoints"] = "Disable Breakpoints"; @@ -384,18 +385,19 @@ localizedStrings["Enable source maps"] = "Enable source maps"; localizedStrings["Enabled"] = "Enabled"; localizedStrings["Encoded"] = "Encoded"; localizedStrings["Encoding"] = "Encoding"; -localizedStrings["Ensure elements have alternate text."] = "Ensure elements have alternate text."; -localizedStrings["Ensure is not used."] = "Ensure is not used."; -localizedStrings["Ensure
and
elements are contained by a
."] = "Ensure
and
elements are contained by a
."; -localizedStrings["Ensure
s have at least one input."] = "Ensure s have at least one input."; -localizedStrings["Ensure elements have a title."] = "Ensure elements have a title."; -localizedStrings["Ensure elements have alternate text."] = "Ensure elements have alternate text."; -localizedStrings["Ensure is not used."] = "Ensure is not used."; -localizedStrings["Ensure is not used."] = "Ensure is not used."; -localizedStrings["Ensure exactly one exists per ."] = "Ensure exactly one exists per ."; -localizedStrings["Ensure hidden=true is not present on the ."] = "Ensure hidden=true is not present on the ."; -localizedStrings["Ensure tabindex is a number."] = "Ensure tabindex is a number."; -localizedStrings["Ensure that the is the first child in the ."] = "Ensure that the is the first child in the ."; +localizedStrings["Ensure aria-hidden=\u0022%s\u0022 is not used."] = "Ensure aria-hidden=\u0022%s\u0022 is not used."; +localizedStrings["Ensure that \u0022%s\u0022 is spelled correctly."] = "Ensure that \u0022%s\u0022 is spelled correctly."; +localizedStrings["Ensure that buttons have accessible labels for assistive technology."] = "Ensure that buttons have accessible labels for assistive technology."; +localizedStrings["Ensure that dialogs have accessible labels for assistive technology."] = "Ensure that dialogs have accessible labels for assistive technology."; +localizedStrings["Ensure that element of role \u0022%s\u0022 and \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA."] = "Ensure that element of role \u0022%s\u0022 and \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA."; +localizedStrings["Ensure that element of role \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA."] = "Ensure that element of role \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA."; +localizedStrings["Ensure that elements of role \u0022%s\u0022 have accessible labels for assistive technology."] = "Ensure that elements of role \u0022%s\u0022 have accessible labels for assistive technology."; +localizedStrings["Ensure that elements of role \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA."] = "Ensure that elements of role \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA."; +localizedStrings["Ensure that links have accessible labels for assistive technology."] = "Ensure that links have accessible labels for assistive technology."; +localizedStrings["Ensure that only one banner is used on the page."] = "Ensure that only one banner is used on the page."; +localizedStrings["Ensure that only one live region is used on the page."] = "Ensure that only one live region is used on the page."; +localizedStrings["Ensure that only one main content section is used on the page."] = "Ensure that only one main content section is used on the page."; +localizedStrings["Ensure that values for \u0022%s\u0022 are valid."] = "Ensure that values for \u0022%s\u0022 are valid."; localizedStrings["Entered Full-Screen Mode"] = "Entered Full-Screen Mode"; localizedStrings["Entered Low-Power Mode"] = "Entered Low-Power Mode"; localizedStrings["Entire Recording"] = "Entire Recording"; @@ -954,10 +956,6 @@ localizedStrings["Tabs"] = "Tabs"; localizedStrings["Tag"] = "Tag"; localizedStrings["Take snapshot"] = "Take snapshot"; localizedStrings["Template Content"] = "Template Content"; -localizedStrings["Tests for element accessibility issues."] = "Tests for element accessibility issues."; -localizedStrings["Tests for element attribute accessibility issues."] = "Tests for element attribute accessibility issues."; -localizedStrings["Tests for ways to improve accessibility."] = "Tests for ways to improve accessibility."; -localizedStrings["Tests the accessibility of form elements."] = "Tests the accessibility of form elements."; localizedStrings["Text"] = "Text"; localizedStrings["Text Frame"] = "Text Frame"; localizedStrings["Text Node"] = "Text Node"; diff --git a/Source/WebInspectorUI/UserInterface/Controllers/AuditManager.js b/Source/WebInspectorUI/UserInterface/Controllers/AuditManager.js index 815212d..f808b27 100644 --- a/Source/WebInspectorUI/UserInterface/Controllers/AuditManager.js +++ b/Source/WebInspectorUI/UserInterface/Controllers/AuditManager.js @@ -305,6 +305,543 @@ WI.AuditManager = class AuditManager extends WI.Object if (this._tests.length) return; + const testMenuRoleForRequiredChidren = function() { + const relationships = { + menu: ["menuitem", "menuitemcheckbox", "menuitemradio"], + menubar: ["menuitem", "menuitemcheckbox", "menuitemradio"], + }; + let domNodes = []; + let visitedParents = new Set; + function hasChildWithRole(node, expectedRoles) { + let childNode = node; + if (!childNode) + return false; + + if (childNode.parentNode) + visitedParents.add(childNode.parentNode); + + while (childNode) { + let properties = WebInspectorAudit.Accessibility.getComputedProperties(childNode); + if (childNode.nodeType === Node.ELEMENT_NODE && properties) { + if (expectedRoles.includes(properties.role)) + return true; + + if (childNode.hasChildNodes() && hasChildWithRole(childNode.firstChild, expectedRoles)) + return true; + } + childNode = childNode.nextSibling; + } + return false; + } + for (let role in relationships) { + for (let parentNode of WebInspectorAudit.Accessibility.getElementsByComputedRole(role)) { + if (visitedParents.has(parentNode)) + continue; + + if (!hasChildWithRole(parentNode.firstChild, relationships[role])) + domNodes.push(parentNode); + } + } + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["role"]}; + }; + + const testGridRoleForRequiredChidren = function() { + const relationships = { + grid: ["row", "rowgroup"], + }; + let domNodes = []; + let visitedParents = new Set; + function hasChildWithRole(node, expectedRoles) { + let childNode = node; + if (!childNode) + return false; + + if (childNode.parentNode) + visitedParents.add(childNode.parentNode); + + while (childNode) { + let properties = WebInspectorAudit.Accessibility.getComputedProperties(childNode); + if (childNode.nodeType === Node.ELEMENT_NODE && properties) { + if (expectedRoles.includes(properties.role)) + return true; + + if (childNode.hasChildNodes() && hasChildWithRole(childNode.firstChild, expectedRoles)) + return true; + } + childNode = childNode.nextSibling; + } + return false; + } + for (let role in relationships) { + for (let parentNode of WebInspectorAudit.Accessibility.getElementsByComputedRole(role)) { + if (visitedParents.has(parentNode)) + continue; + + if (!hasChildWithRole(parentNode.firstChild, relationships[role])) + domNodes.push(parentNode); + } + } + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["role"]}; + }; + + const testForAriaLabelledBySpelling = function() { + let domNodes = Array.from(document.querySelectorAll("[aria-labeledby]")); + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["aria-labeledby"]}; + }; + + const testForMultipleBanners = function() { + let domNodes = []; + let banners = WebInspectorAudit.Accessibility.getElementsByComputedRole("banner"); + if (banners.length > 1) + domNodes = banners; + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["role"]}; + }; + + const testForLinkLabels = function() { + let links = WebInspectorAudit.Accessibility.getElementsByComputedRole("link"); + let domNodes = links.filter((link) => !WebInspectorAudit.Accessibility.getComputedProperties(link).label); + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["aria-label", "aria-labelledby", "title"]}; + }; + + const testRowGroupRoleForRequiredChidren = function() { + const relationships = { + rowgroup: ["row"], + }; + let domNodes = []; + let visitedParents = new Set; + function hasChildWithRole(node, expectedRoles) { + let childNode = node; + if (!childNode) + return false; + + if (childNode.parentNode) + visitedParents.add(childNode.parentNode); + + while (childNode) { + let properties = WebInspectorAudit.Accessibility.getComputedProperties(childNode); + if (childNode.nodeType === Node.ELEMENT_NODE && properties) { + if (expectedRoles.includes(properties.role)) + return true; + + if (childNode.hasChildNodes() && hasChildWithRole(childNode.firstChild, expectedRoles)) + return true; + } + childNode = childNode.nextSibling; + } + return false; + } + for (let role in relationships) { + for (let parentNode of WebInspectorAudit.Accessibility.getElementsByComputedRole(role)) { + if (visitedParents.has(parentNode)) + continue; + + if (!hasChildWithRole(parentNode.firstChild, relationships[role])) + domNodes.push(parentNode); + } + } + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["role"]}; + }; + + const testTableRoleForRequiredChidren = function() { + const relationships = { + table: ["row", "rowgroup"], + }; + let domNodes = []; + let visitedParents = new Set; + function hasChildWithRole(node, expectedRoles) { + let childNode = node; + if (!childNode) + return false; + + if (childNode.parentNode) + visitedParents.add(childNode.parentNode); + + while (childNode) { + let properties = WebInspectorAudit.Accessibility.getComputedProperties(childNode); + if (childNode.nodeType === Node.ELEMENT_NODE && properties) { + if (expectedRoles.includes(properties.role)) + return true; + + if (childNode.hasChildNodes() && hasChildWithRole(childNode.firstChild, expectedRoles)) + return true; + } + childNode = childNode.nextSibling; + } + return false; + } + for (let role in relationships) { + for (let parentNode of WebInspectorAudit.Accessibility.getElementsByComputedRole(role)) { + if (visitedParents.has(parentNode)) + continue; + + if (!hasChildWithRole(parentNode.firstChild, relationships[role])) + domNodes.push(parentNode); + } + } + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["role"]}; + }; + + const testForMultipleLiveRegions = function() { + const liveRegionRoles = ["alert", "log", "status", "marquee", "timer"]; + let domNodes = []; + let liveRegions = liveRegionRoles.reduce((a, b) => { + return a.concat(WebInspectorAudit.Accessibility.getElementsByComputedRole(b)); + }, []); + liveRegions = liveRegions.concat(Array.from(document.querySelectorAll(`[aria-live="polite"], [aria-live="assertive"]`))); + if (liveRegions.length > 1) + domNodes = liveRegions; + return {level: domNodes.length ? "warn" : "pass", domNodes, domAttributes: ["aria-live"]}; + }; + + const testListBoxRoleForRequiredChidren = function() { + const relationships = { + listbox: ["option"], + }; + let domNodes = []; + let visitedParents = new Set; + function hasChildWithRole(node, expectedRoles) { + let childNode = node; + if (!childNode) + return false; + + if (childNode.parentNode) + visitedParents.add(childNode.parentNode); + + while (childNode) { + let properties = WebInspectorAudit.Accessibility.getComputedProperties(childNode); + if (childNode.nodeType === Node.ELEMENT_NODE && properties) { + if (expectedRoles.includes(properties.role)) + return true; + + if (childNode.hasChildNodes() && hasChildWithRole(childNode.firstChild, expectedRoles)) + return true; + } + childNode = childNode.nextSibling; + } + return false; + } + for (let role in relationships) { + for (let parentNode of WebInspectorAudit.Accessibility.getElementsByComputedRole(role)) { + if (visitedParents.has(parentNode)) + continue; + + if (!hasChildWithRole(parentNode.firstChild, relationships[role])) + domNodes.push(parentNode); + } + } + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["role"]}; + }; + + const testImageLabels = function() { + let images = WebInspectorAudit.Accessibility.getElementsByComputedRole("img"); + let domNodes = images.filter((image) => !WebInspectorAudit.Accessibility.getComputedProperties(image).label); + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["aria-label", "aria-labelledby", "title", "alt"]}; + }; + + const testForAriaHiddenFalse = function() { + let domNodes = Array.from(document.querySelectorAll(`[aria-hidden="false"]`)); + return {level: domNodes.length ? "warn" : "pass", domNodes, domAttributes: ["aria-hidden"]}; + }; + + const testTreeRoleForRequiredChidren = function() { + const relationships = { + tree: ["treeitem", "group"], + }; + let domNodes = []; + let visitedParents = new Set; + function hasChildWithRole(node, expectedRoles) { + let childNode = node; + if (!childNode) + return false; + + if (childNode.parentNode) + visitedParents.add(childNode.parentNode); + + while (childNode) { + let properties = WebInspectorAudit.Accessibility.getComputedProperties(childNode); + if (childNode.nodeType === Node.ELEMENT_NODE && properties) { + if (expectedRoles.includes(properties.role)) + return true; + + if (childNode.hasChildNodes() && hasChildWithRole(childNode.firstChild, expectedRoles)) + return true; + } + childNode = childNode.nextSibling; + } + return false; + } + for (let role in relationships) { + for (let parentNode of WebInspectorAudit.Accessibility.getElementsByComputedRole(role)) { + if (visitedParents.has(parentNode)) + continue; + + if (!hasChildWithRole(parentNode.firstChild, relationships[role])) + domNodes.push(parentNode); + } + } + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["role"]}; + }; + + const testRadioGroupRoleForRequiredChidren = function() { + const relationships = { + radiogroup: ["radio"], + }; + let domNodes = []; + let visitedParents = new Set; + function hasChildWithRole(node, expectedRoles) { + let childNode = node; + if (!childNode) + return false; + + if (childNode.parentNode) + visitedParents.add(childNode.parentNode); + + while (childNode) { + let properties = WebInspectorAudit.Accessibility.getComputedProperties(childNode); + if (childNode.nodeType === Node.ELEMENT_NODE && properties) { + if (expectedRoles.includes(properties.role)) + return true; + + if (childNode.hasChildNodes() && hasChildWithRole(childNode.firstChild, expectedRoles)) + return true; + } + childNode = childNode.nextSibling; + } + return false; + } + for (let role in relationships) { + for (let parentNode of WebInspectorAudit.Accessibility.getElementsByComputedRole(role)) { + if (visitedParents.has(parentNode)) + continue; + + if (!hasChildWithRole(parentNode.firstChild, relationships[role])) + domNodes.push(parentNode); + } + } + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["role"]}; + }; + + const testFeedRoleForRequiredChidren = function() { + const relationships = { + feed: ["article"], + }; + let domNodes = []; + let visitedParents = new Set; + function hasChildWithRole(node, expectedRoles) { + let childNode = node; + if (!childNode) + return false; + + if (childNode.parentNode) + visitedParents.add(childNode.parentNode); + + while (childNode) { + let properties = WebInspectorAudit.Accessibility.getComputedProperties(childNode); + if (childNode.nodeType === Node.ELEMENT_NODE && properties) { + if (expectedRoles.includes(properties.role)) + return true; + + if (childNode.hasChildNodes() && hasChildWithRole(childNode.firstChild, expectedRoles)) + return true; + } + childNode = childNode.nextSibling; + } + return false; + } + for (let role in relationships) { + for (let parentNode of WebInspectorAudit.Accessibility.getElementsByComputedRole(role)) { + if (visitedParents.has(parentNode)) + continue; + + if (!hasChildWithRole(parentNode.firstChild, relationships[role])) + domNodes.push(parentNode); + } + } + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["role"]}; + }; + + const testTabListRoleForRequiredChidren = function() { + const relationships = { + tablist: ["tab"], + }; + let domNodes = []; + let visitedParents = new Set; + function hasChildWithRole(node, expectedRoles) { + let childNode = node; + if (!childNode) + return false; + + if (childNode.parentNode) + visitedParents.add(childNode.parentNode); + + while (childNode) { + let properties = WebInspectorAudit.Accessibility.getComputedProperties(childNode); + if (childNode.nodeType === Node.ELEMENT_NODE && properties) { + if (expectedRoles.includes(properties.role)) + return true; + + if (childNode.hasChildNodes() && hasChildWithRole(childNode.firstChild, expectedRoles)) + return true; + } + childNode = childNode.nextSibling; + } + return false; + } + for (let role in relationships) { + for (let parentNode of WebInspectorAudit.Accessibility.getElementsByComputedRole(role)) { + if (visitedParents.has(parentNode)) + continue; + + if (!hasChildWithRole(parentNode.firstChild, relationships[role])) + domNodes.push(parentNode); + } + } + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["role"]}; + }; + + const testButtonLabels = function() { + let buttons = WebInspectorAudit.Accessibility.getElementsByComputedRole("button"); + let domNodes = buttons.filter((button) => !WebInspectorAudit.Accessibility.getComputedProperties(button).label); + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["aria-label", "aria-labelledby", "title"]}; + }; + + const testCellRoleForRequiredChidren = function() { + const relationships = { + cell: ["row"], + }; + let domNodes = []; + let visitedParents = new Set; + function hasChildWithRole(node, expectedRoles) { + let childNode = node; + if (!childNode) + return false; + + if (childNode.parentNode) + visitedParents.add(childNode.parentNode); + + while (childNode) { + let properties = WebInspectorAudit.Accessibility.getComputedProperties(childNode); + if (childNode.nodeType === Node.ELEMENT_NODE && properties) { + if (expectedRoles.includes(properties.role)) + return true; + + if (childNode.hasChildNodes() && hasChildWithRole(childNode.firstChild, expectedRoles)) + return true; + } + childNode = childNode.nextSibling; + } + return false; + } + for (let role in relationships) { + for (let parentNode of WebInspectorAudit.Accessibility.getElementsByComputedRole(role)) { + if (visitedParents.has(parentNode)) + continue; + + if (!hasChildWithRole(parentNode.firstChild, relationships[role])) + domNodes.push(parentNode); + } + } + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["role"]}; + }; + + const testListRoleForRequiredChidren = function() { + const relationships = { + list: ["listitem", "group"], + }; + let domNodes = []; + let visitedParents = new Set; + function hasChildWithRole(node, expectedRoles) { + let childNode = node; + if (!childNode) + return false; + + if (childNode.parentNode) + visitedParents.add(childNode.parentNode); + + while (childNode) { + let properties = WebInspectorAudit.Accessibility.getComputedProperties(childNode); + if (childNode.nodeType === Node.ELEMENT_NODE && properties) { + if (expectedRoles.includes(properties.role)) + return true; + + if (childNode.hasChildNodes() && hasChildWithRole(childNode.firstChild, expectedRoles)) + return true; + } + childNode = childNode.nextSibling; + } + return false; + } + for (let role in relationships) { + for (let parentNode of WebInspectorAudit.Accessibility.getElementsByComputedRole(role)) { + if (visitedParents.has(parentNode)) + continue; + + if (!hasChildWithRole(parentNode.firstChild, relationships[role])) + domNodes.push(parentNode); + } + } + return {level: domNodes.length ? "warn" : "pass", domNodes, domAttributes: ["role"]}; + }; + + const testComboBoxRoleForRequiredChidren = function() { + const relationships = { + combobox: ["textbox", "listbox", "tree", "grid", "dialog"], + }; + let domNodes = []; + let visitedParents = new Set; + function hasChildWithRole(node, expectedRoles) { + let childNode = node; + if (!childNode) + return false; + + if (childNode.parentNode) + visitedParents.add(childNode.parentNode); + + while (childNode) { + let properties = WebInspectorAudit.Accessibility.getComputedProperties(childNode); + if (childNode.nodeType === Node.ELEMENT_NODE && properties) { + if (expectedRoles.includes(properties.role)) + return true; + + if (childNode.hasChildNodes() && hasChildWithRole(childNode.firstChild, expectedRoles)) + return true; + } + childNode = childNode.nextSibling; + } + return false; + } + for (let role in relationships) { + for (let parentNode of WebInspectorAudit.Accessibility.getElementsByComputedRole(role)) { + if (visitedParents.has(parentNode)) + continue; + + if (!hasChildWithRole(parentNode.firstChild, relationships[role])) + domNodes.push(parentNode); + } + } + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["role"]}; + }; + + const testForMultipleMainContentSections = function() { + let domNodes = []; + let mainContentElements = WebInspectorAudit.Accessibility.getElementsByComputedRole("main"); + if (mainContentElements.length > 1) { + domNodes = mainContentElements; + } + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["role"]}; + }; + + const testDialogsForLabels = function() { + let dialogs = WebInspectorAudit.Accessibility.getElementsByComputedRole("dialog"); + let domNodes = dialogs.filter((dialog) => !WebInspectorAudit.Accessibility.getComputedProperties(dialog).label); + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["aria-label", "aria-labelledby", "title"]}; + }; + + const testForInvalidAriaHiddenValue = function() { + let domNodes = Array.from(document.querySelectorAll(`[aria-hidden]:not([aria-hidden="true"], [aria-hidden="false"])`)); + return {level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["aria-hidden"]}; + }; + const defaultTests = [ new WI.AuditTestGroup(WI.UIString("Demo Audit"), [ new WI.AuditTestGroup(WI.UIString("Result Levels"), [ @@ -321,25 +858,29 @@ WI.AuditManager = class AuditManager extends WI.Object ], {description: WI.UIString("These are all of the different types of data that can be returned with the test result.")}), ], {description: WI.UIString("These tests serve as a demonstration of the functionality and structure of audits.")}), new WI.AuditTestGroup(WI.UIString("Accessibility"), [ - new WI.AuditTestGroup(WI.UIString("Attributes"), [ - new WI.AuditTestCase(`img-alt`, `function() { let domNodes = Array.from(document.getElementsByTagName("img")).filter((img) => !img.alt || !img.alt.length); return { level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["alt"] }; }`, {description: WI.UIString("Ensure elements have alternate text.")}), - new WI.AuditTestCase(`area-alt`, `function() { let domNodes = Array.from(document.getElementsByTagName("area")).filter((area) => !area.alt || !area.alt.length); return { level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["alt"] }; }`, {description: WI.UIString("Ensure elements have alternate text.")}), - new WI.AuditTestCase(`valid-tabindex`, `function() { let domNodes = Array.from(document.querySelectorAll("*[tabindex]")) .filter((node) => { let tabindex = node.getAttribute("tabindex"); if (!tabindex) return false; tabindex = parseInt(tabindex); return isNaN(tabindex) || (tabindex !== 0 && tabindex !== -1); }); return { level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["tabindex"] }; }`, {description: WI.UIString("Ensure tabindex is a number.")}), - new WI.AuditTestCase(`frame-title`, `function() { let domNodes = Array.from(document.querySelectorAll("iframe, frame")) .filter((node) => { let title = node.getAttribute("title"); return !title || !title.trim().length; }); return { level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["title"] }; }`, {description: WI.UIString("Ensure elements have a title.")}), - new WI.AuditTestCase(`hidden-body`, `function() { let domNodes = Array.from(document.querySelectorAll("body[hidden]")).filter((body) => body.hidden); return { level: domNodes.length ? "fail" : "pass", domNodes, domAttributes: ["hidden"] }; }`, {description: WI.UIString("Ensure hidden=true is not present on the .")}), - new WI.AuditTestCase(`meta-refresh`, `function() { let domNodes = Array.from(document.querySelectorAll("meta[http-equiv=refresh]")); return { level: domNodes.length ? "warn" : "pass", domNodes, domAttributes: ["http-equiv"] }; }`, {description: WI.UIString("Ensure is not used.")}), - ], {description: WI.UIString("Tests for element attribute accessibility issues.")}), - new WI.AuditTestGroup(WI.UIString("Elements"), [ - new WI.AuditTestCase(`blink`, `function() { let domNodes = Array.from(document.getElementsByTagName("blink")); return { level: domNodes.length ? "warn" : "pass", domNodes }; }`, {description: WI.UIString("Ensure is not used.")}), - new WI.AuditTestCase(`marquee`, `function() { let domNodes = Array.from(document.getElementsByTagName("marquee")); return { level: domNodes.length ? "warn" : "pass", domNodes }; }`, {description: WI.UIString("Ensure is not used.")}), - new WI.AuditTestCase(`dlitem`, `function() { function check(node) { if (!node) { return false; } if (node.nodeName === "DD") { return true; } return check(node.parentNode); } let domNodes = Array.from(document.querySelectorAll("dt, dd")).filter(check); return { level: domNodes.length ? "warn" : "pass", domNodes }; }`, {description: WI.UIString("Ensure
and
elements are contained by a
.")}), - ], {description: WI.UIString("Tests for element accessibility issues.")}), - new WI.AuditTestGroup(WI.UIString("Forms"), [ - new WI.AuditTestCase(`one-legend`, `function() { let formLegendsMap = Array.from(document.querySelectorAll("form legend")).reduce((accumulator, node) => { let existing = accumulator.get(node.form); if (!existing) { existing = []; accumulator.set(node.form, existing); } existing.push(node); return accumulator; }, new Map); let domNodes = Array.from(formLegendsMap.values()).reduce((accumulator, legends) => accumulator.concat(legends), []); return { level: domNodes.length ? "warn" : "pass", domNodes }; }`, {description: WI.UIString("Ensure exactly one exists per .")}), - new WI.AuditTestCase(`legend-first-child`, `function() { let domNodes = Array.from(document.querySelectorAll("form > legend:not(:first-child)")); return { level: domNodes.length ? "warn" : "pass", domNodes }; }`, {description: WI.UIString("Ensure that the is the first child in the .")}), - new WI.AuditTestCase(`form-input`, `function() { let domNodes = Array.from(document.getElementsByTagName("form")) .filter(node => !node.elements.length); return { level: domNodes.length ? "warn" : "pass", domNodes }; }`, {description: WI.UIString("Ensure s have at least one input.")}), - ], {description: WI.UIString("Tests the accessibility of form elements.")}), - ], {description: WI.UIString("Tests for ways to improve accessibility.")}), + new WI.AuditTestCase(`testMenuRoleForRequiredChidren`, testMenuRoleForRequiredChidren.toString(), {description: WI.UIString("Ensure that element of role \u0022%s\u0022 and \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA.").format(WI.unlocalizedString("menu"), WI.unlocalizedString("menubar")), supports: 1}), + new WI.AuditTestCase(`testGridRoleForRequiredChidren`, testGridRoleForRequiredChidren.toString(), {description: WI.UIString("Ensure that elements of role \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA.").format(WI.unlocalizedString("grid")), supports: 1}), + new WI.AuditTestCase(`testForAriaLabelledBySpelling`, testForAriaLabelledBySpelling.toString(), {description: WI.UIString("Ensure that \u0022%s\u0022 is spelled correctly.").format(WI.unlocalizedString("aria-labelledby")), supports: 1}), + new WI.AuditTestCase(`testForMultipleBanners`, testForMultipleBanners.toString(), {description: WI.UIString("Ensure that only one banner is used on the page."), supports: 1}), + new WI.AuditTestCase(`testForLinkLabels`, testForLinkLabels.toString(), {description: WI.UIString("Ensure that links have accessible labels for assistive technology."), supports: 1}), + new WI.AuditTestCase(`testRowGroupRoleForRequiredChidren`, testRowGroupRoleForRequiredChidren.toString(), {description: WI.UIString("Ensure that element of role \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA.").format(WI.unlocalizedString("rowgroup")), supports: 1}), + new WI.AuditTestCase(`testTableRoleForRequiredChidren`, testTableRoleForRequiredChidren.toString(), {description: WI.UIString("Ensure that elements of role \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA.").format(WI.unlocalizedString("table")), supports: 1}), + new WI.AuditTestCase(`testForMultipleLiveRegions`, testForMultipleLiveRegions.toString(), {description: WI.UIString("Ensure that only one live region is used on the page."), supports: 1}), + new WI.AuditTestCase(`testListBoxRoleForRequiredChidren`, testListBoxRoleForRequiredChidren.toString(), {description: WI.UIString("Ensure that elements of role \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA.").format(WI.unlocalizedString("listbox")), supports: 1}), + new WI.AuditTestCase(`testImageLabels`, testImageLabels.toString(), {description: WI.UIString("Ensure that elements of role \u0022%s\u0022 have accessible labels for assistive technology.").format(WI.unlocalizedString("img")), supports: 1}), + new WI.AuditTestCase(`testForAriaHiddenFalse`, testForAriaHiddenFalse.toString(), {description: WI.UIString("Ensure aria-hidden=\u0022%s\u0022 is not used.").format(WI.unlocalizedString("false")), supports: 1}), + new WI.AuditTestCase(`testTreeRoleForRequiredChidren`, testTreeRoleForRequiredChidren.toString(), {description: WI.UIString("Ensure that element of role \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA.").format(WI.unlocalizedString("tree")), supports: 1}), + new WI.AuditTestCase(`testRadioGroupRoleForRequiredChidren`, testRadioGroupRoleForRequiredChidren.toString(), {description: WI.UIString("Ensure that element of role \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA.").format(WI.unlocalizedString("radiogroup")), supports: 1}), + new WI.AuditTestCase(`testFeedRoleForRequiredChidren`, testFeedRoleForRequiredChidren.toString(), {description: WI.UIString("Ensure that elements of role \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA.").format(WI.unlocalizedString("feed")), supports: 1}), + new WI.AuditTestCase(`testTabListRoleForRequiredChidren`, testTabListRoleForRequiredChidren.toString(), {description: WI.UIString("Ensure that element of role \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA.").format(WI.unlocalizedString("tablist")), supports: 1}), + new WI.AuditTestCase(`testButtonLabels`, testButtonLabels.toString(), {description: WI.UIString("Ensure that buttons have accessible labels for assistive technology."), supports: 1}), + new WI.AuditTestCase(`testCellRoleForRequiredChidren`, testCellRoleForRequiredChidren.toString(), {description: WI.UIString("Ensure that elements of role \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA.").format(WI.unlocalizedString("cell")), supports: 1}), + new WI.AuditTestCase(`testListRoleForRequiredChidren`, testListRoleForRequiredChidren.toString(), {description: WI.UIString("Ensure that elements of role \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA.").format(WI.unlocalizedString("list")), supports: 1}), + new WI.AuditTestCase(`testComboBoxRoleForRequiredChidren`, testComboBoxRoleForRequiredChidren.toString(), {description: WI.UIString("Ensure that elements of role \u0022%s\u0022 have required owned elements in accordance with WAI-ARIA.").format(WI.unlocalizedString("combobox")), supports: 1}), + new WI.AuditTestCase(`testForMultipleMainContentSections`, testForMultipleMainContentSections.toString(), {description: WI.UIString("Ensure that only one main content section is used on the page."), supports: 1}), + new WI.AuditTestCase(`testDialogsForLabels`, testDialogsForLabels.toString(), {description: WI.UIString("Ensure that dialogs have accessible labels for assistive technology."), supports: 1}), + new WI.AuditTestCase(`testForInvalidAriaHiddenValue`, testForInvalidAriaHiddenValue.toString(), {description: WI.UIString("Ensure that values for \u0022%s\u0022 are valid.").format(WI.unlocalizedString("aria-hidden")), supports: 1}) + ], {description: WI.UIString("Diagnoses common accessibility problems affecting screen readers and other assistive technology.")}), ]; let checkDisabledDefaultTest = (test) => { -- 1.8.3.1