Skip to content

Commit

Permalink
Merge branch 'master' into act-unapproved
Browse files Browse the repository at this point in the history
  • Loading branch information
tombrunet authored Feb 4, 2025
2 parents 3eed2b7 + a33fbb9 commit 1768f8c
Show file tree
Hide file tree
Showing 47 changed files with 1,119 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ <h3 id="ruleMessage"></h3>

Visible labels are essential, so every user can know what information to enter:
- People with cognitive, language, and learning disabilities, older adults, and all users will easily learn what information is expected.
- People using voice control will see what to speak. This allows them to easily jump to interactive elements and form fields.
- People using voice control will see what to speak. This allows them to easily jump to interactive elements, buttons, and form fields.

Placeholder labels when used as the only visible label can reduce the accessibility for a wide range of users. Avoid placeholder labels for the following reasons:
- May not be persistent: the placeholder label disappears when the user starts typing in the input field
Expand All @@ -60,7 +60,7 @@ <h3 id="ruleMessage"></h3>

### What to do

The intent of labels, including expected formats and required fields,
The intent of labels, including expected formats, required fields and button text
on interactive elements is not to clutter the page with unnecessary information but to provide important cues that will benefit all users.
Too much information can be just as harmful as too little.

Expand Down Expand Up @@ -98,6 +98,7 @@ <h3 id="ruleMessage"></h3>
### About this requirement

* [IBM 3.3.2 Label or Instruction](https://www.ibm.com/able/requirements/requirements/#3_3_2)
* [IBM 2.5.3 Label in Name](https://www.ibm.com/able/requirements/requirements/#2_5_3)
* [H44: Associate text labels with form controls](https://www.w3.org/WAI/WCAG22/Techniques/html/H44)
* [ARIA1: Use aria-describedby to label a user interface control](https://www.w3.org/WAI/WCAG22/Techniques/aria/ARIA1)
* [ARIA9: Use aria-labelledby to concatenate several text nodes](https://www.w3.org/WAI/WCAG22/Techniques/aria/ARIA9)
Expand Down
6 changes: 3 additions & 3 deletions accessibility-checker-engine/karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@
//{ pattern: 'test/v2/checker/accessibility/rules/label_name_visible_ruleunit/label_offscreen.html', watched: true },
//{ pattern: 'test/v2/checker/accessibility/rules/aria_role_valid_ruleunit/td_attribute_invalid_copy.html', watched: true },
//{ pattern: 'test/v2/checker/accessibility/rules/text_block_heading_ruleunit/Headings-noneUsedEmphasizedText.html', watched: true },
{ pattern: 'test/v2/checker/accessibility/rules/aria_landmark_name_unique_ruleunit/*.html', watched: true },
//{ pattern: 'test/v2/checker/accessibility/rules/aria_landmark_name_unique_ruleunit/*.html', watched: true },
// { pattern: 'test/v2/checker/accessibility/rules/aria_parent_required_ruleunit/webComponentPass2.html', watched: true },


// { pattern: 'test/**/*_ruleunit/*.html', watched: true },
// { pattern: 'test/**/*_ruleunit/*.htm', watched: true },
{ pattern: 'test/**/*_ruleunit/*.html', watched: true },
{ pattern: 'test/**/*_ruleunit/*.htm', watched: true },
// all files ending in "_test"
// { pattern: 'test/*_test.js', watched: true },
{ pattern: 'test/**/*_test.js', watched: true }
Expand Down
41 changes: 39 additions & 2 deletions accessibility-checker-engine/src/v2/aria/ARIAMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,45 @@ export class ARIAMapper extends CommonMapper {
for (let iId=0; iId < ownIds.length; ++iId) {
const owned = doc.getElementById(ownIds[iId]);
//ignore if the aria-owns point to the element itself
if (owned && !DOMUtil.sameNode(owner, owned)) {
CacheUtil.setCache(owned, "aria-owned", owner);
//if (owned && !DOMUtil.sameNode(owner, owned)) {
// CacheUtil.setCache(owned, "aria-owned", owner);
//}
/**
* circular hierarchy check:
* (1) the owned element is neither the same element with the owner nor any ascendant of the owner
* (2) any child with aria-owns cannot point to the owner or any ascendant of the owner
*/
if (owned && !DOMUtil.sameNode(owner, owned)) {
// check if the owned with aria-owns that points to another element
let ownedNodes = [];
const sub_owners = owned.querySelectorAll("[aria-owns]");
for (let i = 0; i < sub_owners.length; ++i) {
const sub_owner = sub_owners[i];
const sub_ownIds = sub_owner.getAttribute("aria-owns").split(/ +/g);
for (let j=0; j < sub_ownIds.length; ++j) {
const ownedNode = doc.getElementById(sub_ownIds[j]);
if (ownedNode)
ownedNodes.push(ownedNode);
}
}
if (ownedNodes.length === 0) {
CacheUtil.setCache(owned, "aria-owned", owner);
continue;
}
// check if any aria-owns points to the element itself or any of it's parent
let parent : Element = owner;
let circular = false;
while (parent !== null) {
const found = ownedNodes.some(item => DOMUtil.sameNode(parent, item));
if (!found)
parent = DOMWalker.parentElement(parent);
else {
circular = true;
break;
}
}
if (!circular)
CacheUtil.setCache(owned, "aria-owned", owner);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
limitations under the License.
*****************************************************************************/

import { Rule, RuleResult, RuleFail, RuleContext, RulePotential, RuleManual, RulePass, RuleContextHierarchy } from "../api/IRule";
import { Rule, RuleResult, RuleFail, RuleContext, RulePass, RuleContextHierarchy } from "../api/IRule";
import { eRulePolicy, eToolkitLevel } from "../api/IRule";
import { AriaUtil } from "../util/AriaUtil";
import { CommonUtil } from "../util/CommonUtil";
Expand Down Expand Up @@ -39,11 +39,19 @@ export const aria_search_label_unique: Rule = {
"group": "Each element with \"search\" role must have a unique label that describes its purpose"
}
},
rulesets: [{
/**
* deprecated the rule on 01/27/2025
* rulesets: [{
"id": ["IBM_Accessibility", "IBM_Accessibility_next", "WCAG_2_1", "WCAG_2_0", "WCAG_2_2"],
"num": ["2.4.1"],
"level": eRulePolicy.VIOLATION,
"toolkitLevel": eToolkitLevel.LEVEL_THREE
}],*/
rulesets: [{
"id": [],
"num": ["2.4.1"],
"level": eRulePolicy.VIOLATION,
"toolkitLevel": eToolkitLevel.LEVEL_THREE
}],
act: [],
run: (context: RuleContext, options?: {}, contextHierarchies?: RuleContextHierarchy): RuleResult | RuleResult[] => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { FragmentUtil } from "../../v2/checker/accessibility/util/fragment";
import { VisUtil } from "../util/VisUtil";
import { CSSUtil } from "../util/CSSUtil";
import { DOMWalker } from "../../v2/dom/DOMWalker";
import { AccNameUtil } from "../util/AccNameUtil";

export const label_name_visible: Rule = {
id: "label_name_visible",
Expand Down
49 changes: 30 additions & 19 deletions accessibility-checker-engine/src/v4/rules/meta_redirect_optional.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
limitations under the License.
*****************************************************************************/

import { FragmentUtil } from "../../v2/checker/accessibility/util/fragment";
import { CommonUtil } from "../util/CommonUtil";
import { Rule, RuleResult, RuleFail, RuleContext, RulePass, RuleContextHierarchy } from "../api/IRule";
import { eRulePolicy, eToolkitLevel } from "../api/IRule";
import { CacheUtil } from "../util/CacheUtil";

export const meta_redirect_optional: Rule = {
id: "meta_redirect_optional",
Expand Down Expand Up @@ -49,39 +48,51 @@ export const meta_redirect_optional: Rule = {
"toolkitLevel": eToolkitLevel.LEVEL_THREE
}],
// Removed ACT bisz58 AAA
act: [{
/**act: [{
"bc659a" : {
"pass": "pass",
"fail": "fail",
"fail_longrefresh": "pass"
}
}],
}],*/
act: [ "bisz58"], // fail even if a page is redirected after more than 20 hours (7200)
run: (context: RuleContext, options?: {}, contextHierarchies?: RuleContextHierarchy): RuleResult | RuleResult[] => {
const ruleContext = context["dom"].node as Element;
// JCH - NO OUT OF SCOPE hidden in context

if (ruleContext.getAttribute("http-equiv").toLowerCase() !== 'refresh') {
return null;
}

let doc = ruleContext.ownerDocument;
if (!doc) return;

// check if the rule already passed or failed: only the first one tridders if multiple
if (CacheUtil.getCache(doc, "meta_redirect_optional_done", false))
return null;

let content = ruleContext.getAttribute("content").toLowerCase();
// Invalid content field
if (!content.match(/^\d+$/) && !content.match(/^\d+;/)) {
if (!content || content.trim().length ===0)
return null;

let time:number = -1;
if (content.match(/^\d+$/))
time = parseInt(content);
else if (content.match(/^\d+;/)) {
let pos = content.indexOf(";");
time = parseInt(content.substring(0, pos));
}
// Only check the first one since it takes priority
if (CommonUtil.triggerOnce(FragmentUtil.getOwnerFragment(ruleContext), "meta_redirect_optional", false)) {
// Invalid content field
if (time === -1) {
return null;
}
let timeMatch = content.match(/^(\d+); +[^ ]/);
if (!timeMatch || parseInt(timeMatch[1]) === 0) {

CacheUtil.setCache(doc, "meta_redirect_optional_done", true);
if (time === 0)
return RulePass("pass");
} else {
let time = parseInt(timeMatch[1]);
if (time < 72001) {
return RuleFail("fail");
} else {
return RuleFail("fail_longrefresh");
}
}
else if (time < 72001)
return RuleFail("fail");

return RuleFail("fail_longrefresh");

}
}
42 changes: 32 additions & 10 deletions accessibility-checker-engine/src/v4/rules/meta_refresh_delay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,29 @@

import { Rule, RuleResult, RuleContext, RulePotential, RulePass, RuleContextHierarchy } from "../api/IRule";
import { eRulePolicy, eToolkitLevel } from "../api/IRule";
import { CacheUtil } from "../util/CacheUtil";

export const meta_refresh_delay: Rule = {
id: "meta_refresh_delay",
context: "dom:meta[http-equiv][content]",
refactor: {
"RPT_Meta_Refresh": {
"Pass_0": "Pass_0",
"Potential_1": "Potential_1"
"Pass_0": "pass",
"Potential_1": "potential_refresh"
}
},
help: {
"en-US": {
"group": "meta_refresh_delay.html",
"Pass_0": "meta_refresh_delay.html",
"Potential_1": "meta_refresh_delay.html"
"pass": "meta_refresh_delay.html",
"potential_refresh": "meta_refresh_delay.html"
}
},
messages: {
"en-US": {
"group": "Pages should not refresh automatically",
"Pass_0": "Rule Passed",
"Potential_1": "Verify page is not being caused to refresh automatically",
"pass": "Pages do not refresh automatically",
"potential_refresh": "Verify page is not being caused to refresh automatically",
}
},
rulesets: [{
Expand All @@ -43,18 +44,39 @@ export const meta_refresh_delay: Rule = {
"level": eRulePolicy.VIOLATION,
"toolkitLevel": eToolkitLevel.LEVEL_THREE
}],
act: [ "bisz58", "bc659a" ],
//act: [ "bisz58", "bc659a" ],
act: [ "bc659a" ], // pass if a page is redirected after more than 20 hours (7200)
run: (context: RuleContext, options?: {}, contextHierarchies?: RuleContextHierarchy): RuleResult | RuleResult[] => {
const ruleContext = context["dom"].node as Element;
if (ruleContext.getAttribute("http-equiv").toLowerCase() !== 'refresh')
return null;

let doc = ruleContext.ownerDocument;
if (!doc) return;

// check if the rule already passed: the first one takes priority
if (CacheUtil.getCache(doc, "meta_refresh_delay_done", false))
return null;

let content = ruleContext.getAttribute("content").toLowerCase();
if (!content || content.trim().length ===0)
return null;

let time:number = -1;
if (content.match(/^\d+$/))
time = parseInt(content);
else if (content.match(/^\d+;/)) {
let pos = content.indexOf(";");
time = parseInt(content.substring(0, pos));
}
// Invalid content field
if (!content.match(/^\d+$/) && !content.match(/^\d+;/)) {
if (time === -1) {
return null;
}
let fail = !content.match(/^\d+; +[^ ]/);
return !fail ? RulePass("Pass_0") : RulePotential("Potential_1");

CacheUtil.setCache(doc, "meta_refresh_delay_done", true);
if (time === 0)
return RulePass("pass");
return RulePotential("potential_refresh");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ export const meta_viewport_zoomable: Rule = {
}],
act: [{
"b4f0c3": {
"Pass_0": "pass",
"Potential_1": "fail"
"pass": "pass",
"potential_zoomable": "fail"
}
}],
run: (context: RuleContext, options?: {}, contextHierarchies?: RuleContextHierarchy): RuleResult | RuleResult[] => {
Expand Down
8 changes: 4 additions & 4 deletions accessibility-checker-engine/src/v4/util/CommonUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1207,15 +1207,15 @@ export class CommonUtil {
if (nw.bEndTag) continue;
if ((nw.node.nodeType === 1 && (VisUtil.hiddenByDefaultElements.includes(nw.node.nodeName.toLowerCase())) || !VisUtil.isNodeVisible(nw.node) || VisUtil.isElementOffscreen(nw.node as HTMLElement))) {
if (nw.node.nextSibling) {
if (nw.node.nextSibling.nodeType === 3 && nw.node.nextSibling.nodeValue !== null)
text += nw.node.nextSibling.nodeValue;
if (nw.node.nextSibling.nodeType === 3 && nw.node.nextSibling.nodeValue && nw.node.nextSibling.nodeValue.trim() !== '')
text += ' ' + nw.node.nextSibling.nodeValue.trim();
nw.node = nw.node.nextSibling;
continue;
} else
break;
}
if (nw.node.nodeType === 3 && nw.node.nodeValue !== null) {
text += nw.node.nodeValue.trim();
if (nw.node.nodeType === 3 && nw.node.nodeValue && nw.node.nodeValue.trim() !== '') {
text += ' ' + nw.node.nodeValue.trim();
}
}
return text.trim();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en-US">

<head>
<title>Sandbox</title>
<meta charset="UTF-8" />
</head>

<body>
<div class="mm-panels">
<div id="mm-1" class="mm-panel mm-panel_opened">
<ul class="mm-listview">
<li class="mm-listitem"><a href="#mm-2" aria-owns="mm-2">Open submenu</a>
<li class="mm-listitem"><a href="#mm-3" aria-owns="mm-3">Open Another submenu</a>
</li>
</ul>
</div>
<div id="mm-3">
<div class="mm-navbar"><a href="#mm-1" aria-owns="mm-1">Close submenu</a>
</div>
</div>
</div>
</body>
<script>
UnitTest = {
ruleIds: ["aria_descendant_valid"],
results: [

]
}
</script>
</body>

</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test case</title>
</head>
<body>
<button
aria-expanded="false"
aria-controls="id-:re:"
aria-label="AI Text goes here"
type="button">
<span class="cds--ai-label__text">AI</span>
<span class="cds--ai-label__additional-text">Text goes here</span>
</button>

<script>
UnitTest = {
ruleIds: ["label_name_visible"],
results: [
{
"ruleId": "label_name_visible",
"value": [
"INFORMATION",
"PASS"
],
"path": {
"dom": "/html[1]/body[1]/button[1]",
"aria": "/document[1]/button[1]"
},
"reasonId": "Pass_0",
"message": "Accessible name matches or contains the visible label text",
"messageArgs": [],
"apiArgs": [],
"category": "Accessibility"
}
]
};
</script>
</body>
</html>
Loading

0 comments on commit 1768f8c

Please sign in to comment.