AX: Audit tab should have built-in accessibility tests.
authoraaron_chu@apple.com <aaron_chu@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 16 Feb 2019 03:51:41 +0000 (03:51 +0000)
committeraaron_chu@apple.com <aaron_chu@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 16 Feb 2019 03:51:41 +0000 (03:51 +0000)
https://bugs.webkit.org/show_bug.cgi?id=194005
<rdar://problem/47657503>

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
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Controllers/AuditManager.js

index 7c250e8..7218b85 100644 (file)
@@ -1,3 +1,19 @@
+2019-02-15  Aaron Chu  <aaron_chu@apple.com>
+
+        AX: Audit tab should have built-in accessibility tests.
+        https://bugs.webkit.org/show_bug.cgi?id=194005
+        <rdar://problem/47657503>
+
+        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  <nvasilyev@apple.com>
 
         Web Inspector: Dark Mode: commas in CSS selectors are too dim
index 651be14..6fd8ea7 100644 (file)
@@ -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 <area> elements have alternate text."] = "Ensure <area> elements have alternate text.";
-localizedStrings["Ensure <blink> is not used."] = "Ensure <blink> is not used.";
-localizedStrings["Ensure <dt> and <dd> elements are contained by a <dl>."] = "Ensure <dt> and <dd> elements are contained by a <dl>.";
-localizedStrings["Ensure <form>s have at least one input."] = "Ensure <form>s have at least one input.";
-localizedStrings["Ensure <frame> elements have a title."] = "Ensure <frame> elements have a title.";
-localizedStrings["Ensure <img> elements have alternate text."] = "Ensure <img> elements have alternate text.";
-localizedStrings["Ensure <marquee> is not used."] = "Ensure <marquee> is not used.";
-localizedStrings["Ensure <meta http-equiv=refresh> is not used."] = "Ensure <meta http-equiv=refresh> is not used.";
-localizedStrings["Ensure exactly one <legend> exists per <form>."] = "Ensure exactly one <legend> exists per <form>.";
-localizedStrings["Ensure hidden=true is not present on the <body>."] = "Ensure hidden=true is not present on the <body>.";
-localizedStrings["Ensure tabindex is a number."] = "Ensure tabindex is a number.";
-localizedStrings["Ensure that the <legend> is the first child in the <form>."] = "Ensure that the <legend> is the first child in the <form>.";
+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";
index 815212d..f808b27 100644 (file)
@@ -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 <img> 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 <area> 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 <frame> 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 <body>.")}),
-                    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 <meta http-equiv=refresh> 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 <blink> 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 <marquee> 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 <dt> and <dd> elements are contained by a <dl>.")}),
-                ], {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 <legend> exists per <form>.")}),
-                    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 <legend> is the first child in the <form>.")}),
-                    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 <form>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) => {