Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(engine): Generate a spreadsheet of rule mappings as an artifact for PRs #2071

Merged
merged 3 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/test.yml
philljenkins marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ jobs:
with:
name: Rule listing
path: accessibility-checker-engine/dist/help/rules.html
- name: Upload rule mapping spreadsheet
uses: actions/upload-artifact@v4
with:
name: Rule mapping spreadsheet
path: accessibility-checker-engine/dist/help/rules.csv


act-results:
Expand Down
75 changes: 73 additions & 2 deletions accessibility-checker-engine/src/genHelp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ width="16px" height="16px" viewBox="0 0 16 16" style="enable-background:new 0 0
return `<span class="issueLevel">${icon}&nbsp;${val}</span>`;
}

function processRules() {
function processRules(includePass?: boolean) {
let retVal = [];
for (const ruleset of a11yRulesets as Guideline[]) {
if (ruleset.type === "extension") continue;
Expand Down Expand Up @@ -183,7 +183,7 @@ function processRules() {
if (msgCode === "group") return;
let re = new RegExp(`\\.Rule([^()) ]+)[ ()]+["']${msgCode}["']`);
let reMatch = re.exec(rule.run.toString());
if (reMatch && reMatch[1] !== "Pass") {
if (includePass || (reMatch && reMatch[1] !== "Pass")) {
ruleInfo.reasons.push({
id: msgCode,
message: rule.messages["en-US"][msgCode],
Expand All @@ -200,6 +200,8 @@ function processRules() {
if (b.level === "Potential") return 1;
if (a.level === "Manual") return -1;
if (b.level === "Manual") return 1;
if (a.level === "Pass") return -1;
if (b.level === "Pass") return 1;
return 0;
})
cpInfo.rules.push(ruleInfo);
Expand All @@ -214,6 +216,9 @@ function processRules() {
if (retVal === 0) {
retVal = b.reasons.filter(reasonInfo => (reasonInfo.type === "Manual")).length - a.reasons.filter(reasonInfo => (reasonInfo.type === "Manual")).length;
}
if (retVal === 0) {
retVal = b.reasons.filter(reasonInfo => (reasonInfo.type === "Pass")).length - a.reasons.filter(reasonInfo => (reasonInfo.type === "Pass")).length;
}
return retVal;
}
if (a.level === "VIOLATION") return -1;
Expand Down Expand Up @@ -304,7 +309,73 @@ ${cpSections}
writeFileSync(path.join(__dirname, '..', 'dist', "help", "rules.html"), rulesHTML);
}

function buildRuleMapping() {
const vMap = {
"VIOLATION_Fail": "Violation",
"VIOLATION_Potential": "Violation Potential",
"VIOLATION_Manual": "Violation Manual",
"VIOLATION_Pass": "Pass",
"RECOMMENDATION_Fail": "Rec",
"RECOMMENDATION_Potential": "Rec Potential",
"RECOMMENDATION_Manual": "Rec Manual",
"RECOMMENDATION_Pass": "Pass"
}
const actMap = {
"Pass": "pass",
"Fail": "fail",
"Potential": "cantTell",
"Manual": "cantTell"
}
const csv = (str : string) => {
if (str === null) {
return '"null"';
} else if (!str || str.length == 0) {
return '""';
} else {
str = str.replace(/"/g, '""');
return `"${str}"`;
}
}
let rsInfo = processRules(true);
let csvStr = `Rule ID, Reason Code, Rule message, Reason message, Violation Level, Toolkit Level, WCAG Requirements, ACT mapping\n`;
let ruleset = rsInfo.filter(rs => rs.id === "IBM_Accessibility")[0];
for (const checkpoint of ruleset.checkpoints) {
for (const rule of checkpoint.rules) {
for (const reason of rule.reasons) {
let vLevelStrings = [];
let tkLevelStrings = [];
let rsStrings = [];
let actStrings = [];
for (const rsInfo of rule.rule.rulesets) {
tkLevelStrings.push(rsInfo.toolkitLevel);
vLevelStrings.push(vMap[`${rsInfo.level}_${reason.type}`]);
rsStrings.push(rsInfo.num);
}
if (typeof rule.rule.act === typeof "") {
actStrings.push(rule.rule.act+":"+actMap[reason.type]);
} else if (rule.rule.act.length) {
for (const actInfo of rule.rule.act) {
if (typeof actInfo === typeof "") {
actStrings.push(actInfo+":"+actMap[reason.type]);
} else {
for (const key in actInfo) {
if (reason.id in actInfo[key]) {
actStrings.push(key+":"+actInfo[key][reason.id])
}
}
}
}
}
let csvStrLine = `${csv(rule.rule.id)},${csv(reason.id)},${csv(rule.rule.messages['en-US'].group)},${csv(reason.message)},${csv(vLevelStrings.join(" | "))},${csv(tkLevelStrings.join(" | "))},${csv(rsStrings.join(" | "))},${csv(actStrings.join(" | "))}\n`;
csvStr += csvStrLine;
}
}
}
writeFileSync(path.join(__dirname, '..', 'dist', "help", "rules.csv"), csvStr);
}

(async () => {
await buildV4();
await buildRuleViewer();
await buildRuleMapping();
})();
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,17 @@ export let aria_activedescendant_valid: Rule = {
"Pass_0": "Pass_0",
"Fail_1": "Fail_1",
"Fail_2": "Fail_2",
"Fail_3": "Fail_3",
"Fail_4": "Fail_4"}
"Fail_3": "Fail_3"
// "Fail_4": "Fail_4"
}
},
help: {
"en-US": {
"Pass_0": "aria_activedescendant_valid.html",
"Fail_1": "aria_activedescendant_valid.html",
"Fail_2": "aria_activedescendant_valid.html",
"Fail_3": "aria_activedescendant_valid.html",
"Fail_4": "aria_activedescendant_valid.html",
// "Fail_4": "aria_activedescendant_valid.html",
"group": "aria_activedescendant_valid.html"
}
},
Expand All @@ -45,7 +46,7 @@ export let aria_activedescendant_valid: Rule = {
"Fail_1": "The 'aria-activedescendant' property is empty",
"Fail_2": "The 'aria-activedescendant' property references a hidden node",
"Fail_3": "Element is not a combobox, and the referenced active-descendant element is not a valid descendant",
"Fail_4": "Element is a combobox, and the referenced active-descendant element is not controlled by this component",
// "Fail_4": "Element is a combobox, and the referenced active-descendant element is not controlled by this component",
"group": "The 'aria-activedescendant' property must reference the 'id' of a non-empty, non-hidden active child element"
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ export let aria_attribute_conflict: Rule = {
+ ", dom:*[aria-rowspan]",
help: {
"en-US": {
"pass": "aria_attribute_conflict.html",
// "pass": "aria_attribute_conflict.html",
"fail_conflict": "aria_attribute_conflict.html",
"group": "aria_attribute_conflict.html"
}
},
messages: {
"en-US": {
"pass": "The ARIA attribute is not conflict with the corresponding HTML attribute",
// "pass": "The ARIA attribute is not conflict with the corresponding HTML attribute",
"fail_conflict": "The ARIA attribute \"{0}\" is in conflict with the corresponding HTML attribute \"{1}\"",
"group": "An ARIA attribute must not conflict with the corresponding HTML attribute"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export let aria_attribute_deprecated: Rule = {
context: "dom:*",
help: {
"en-US": {
"pass": "aria_attribute_deprecated.html",
// "pass": "aria_attribute_deprecated.html",
"fail_aria_role": "aria_attribute_deprecated.html",
"fail_aria_attr": "aria_attribute_deprecated.html",
"fail_role_attr": "aria_attribute_deprecated.html",
Expand All @@ -29,7 +29,7 @@ export let aria_attribute_deprecated: Rule = {
},
messages: {
"en-US": {
"pass": "The ARIA roles and attribute are used per specification",
// "pass": "The ARIA roles and attribute are used per specification",
"fail_aria_role": "The ARIA role \"{0}\" is deprecated in the ARIA specification",
"fail_aria_attr": "The ARIA attributes \"{0}\" are deprecated in the ARIA specification",
"fail_role_attr": "The ARIA attributes \"{0}\" are deprecated for the role \"{1}\" in the ARIA specification",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,23 @@ export let aria_region_labelled: Rule = {
refactor: {
"Rpt_Aria_RegionLabel_Implicit": {
"Pass_0": "Pass_0",
"Fail_1": "Fail_1",
"Fail_2": "Fail_2"}
"Fail_1": "Fail_1"
// "Fail_2": "Fail_2"
}
},
help: {
"en-US": {
"Pass_0": "aria_region_labelled.html",
"Fail_1": "aria_region_labelled.html",
"Fail_2": "aria_region_labelled.html",
// "Fail_2": "aria_region_labelled.html",
"group": "aria_region_labelled.html"
}
},
messages: {
"en-US": {
"Pass_0": "Rule Passed",
"Fail_1": "Element with \"region\" role does not have a label",
"Fail_2": "Element with \"region\" role is not labeled with 'aria-label' or 'aria-labelledby'",
// "Fail_2": "Element with \"region\" role is not labeled with 'aria-label' or 'aria-labelledby'",
"group": "Each element with \"region\" role must have a label that describes its purpose"
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,18 @@ export let blink_elem_deprecated: Rule = {
context: "dom:blink",
refactor: {
"WCAG20_Blink_AlwaysTrigger": {
"Pass_0": "Pass_0",
"Fail_1": "Fail_1"
}
},
help: {
"en-US": {
"group": `blink_elem_deprecated.html`,
"Pass_0": `blink_elem_deprecated.html`,
"Fail_1": `blink_elem_deprecated.html`
}
},
messages: {
"en-US": {
"group": "Content that blinks persistently must not be used",
"Pass_0": "Rule Passed",
"Fail_1": "Content found that blinks persistently"
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export let canvas_content_described: Rule = {
//skip the rule
if (VisUtil.isNodeHiddenFromAT(ruleContext)) return null;
let passed = ruleContext.innerHTML.trim().length > 0;
if (passed) return RulePass(1);
if (passed) return RulePass("Pass_0");
if (!passed) return RuleManual("Manual_1");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,17 @@ export let combobox_popup_reference: Rule = {
}

if (pattern === "1.0") {
return RulePass(expanded ? "Pass_1.0_expanded" : "Pass_1.0_collapsed");
if (expanded) {
return RulePass("Pass_1.0_expanded");
} else {
return RulePass("Pass_1.0_collapsed");
}
} else {
return RulePass(expanded ? "Pass_1.2_expanded" : "Pass_1.2_collapsed");
if (expanded) {
return RulePass("Pass_1.2_expanded");
} else {
return RulePass("Pass_1.2_collapsed");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,16 @@ export let download_keyboard_controllable: Rule = {
context: "dom:a[href],dom:area[href]",
refactor: {
"HAAC_Media_DocumentTrigger2": {
"Pass_0": "Pass_0",
"Manual_1": "Manual_1"}
},
help: {
"en-US": {
"Pass_0": "download_keyboard_controllable.html",
"Manual_1": "download_keyboard_controllable.html",
"group": "download_keyboard_controllable.html"
}
},
messages: {
"en-US": {
"Pass_0": "Rule Passed",
"Manual_1": "Verify that the file download mechanism does not cause a keyboard trap",
"group": "File download mechanisms should be keyboard-operable and preserve page focus location"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ export let draggable_alternative_exists: Rule = {
help: {
"en-US": {
"group": "draggable_alternative_exists.html",
"pass_alternative": "draggable_alternative_exists.html",
// "pass_alternative": "draggable_alternative_exists.html",
"pass_undraggable": "draggable_alternative_exists.html",
"potential_alternative": "draggable_alternative_exists.html"
}
},
messages: {
"en-US": {
"group": "A draggable element must have a \"single pointer\" alternative",
"pass_alternative": "The draggable element \"{0}\" has a \"single pointer\" alternative",
// "pass_alternative": "The draggable element \"{0}\" has a \"single pointer\" alternative",
"pass_undraggable": "The element \"{0}\" is not draggable",
"potential_alternative": "Ensure the draggable element \"{0}\" has a \"single pointer\" alternative"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ export let input_haspopup_conflict: Rule = {
context: "dom:input[list][aria-haspopup]",
refactor: {
"input_haspopup_invalid": {
"Pass": "pass",
// "Pass": "pass",
"Potential_1": "potential_type_misuse",
"Potential_2": "potential_misuse"}
},
help: {
"en-US": {
"group": "input_haspopup_conflict.html",
"pass": "input_haspopup_conflict.html",
// "pass": "input_haspopup_conflict.html",
"potential_type_misuse": "input_haspopup_conflict.html",
"potential_misuse": "input_haspopup_conflict.html",
"potential_list_notexist": "input_haspopup_conflict.html",
Expand All @@ -39,7 +39,7 @@ export let input_haspopup_conflict: Rule = {
messages: {
"en-US": {
"group": "<input> element with a 'list' attribute should not use an explicit 'aria-haspopup' attribute",
"pass": "The <input> element with a 'list' attribute does not use an explicit 'aria-haspopup' attribute",
// "pass": "The <input> element with a 'list' attribute does not use an explicit 'aria-haspopup' attribute",
"potential_type_misuse": "The <input> element with type \"{0}\" and 'list' attribute uses an explicit 'aria-haspopup' attribute",
"potential_misuse": "The <input> element with a missing or invalid type and 'list' attribute uses an explicit 'aria-haspopup' attribute",
"potential_list_notexist": "The list attribute for the <input> element is invalid",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,20 @@ export let input_onchange_review: Rule = {
context: "dom:input[onchange], dom:textarea[onchange], dom:select[onchange]",
refactor: {
"WCAG20_Input_HasOnchange": {
"Pass_0": "pass",
// "Pass_0": "pass",
"Potential_1": "potential_warning"}
},
help: {
"en-US": {
"pass": "input_onchange_review.html",
// "pass": "input_onchange_review.html",
"potential_warning": "input_onchange_review.html",
"group": "input_onchange_review.html"
}
},
messages: {
"en-US": {
"group": "Users must be advised if, due to a change of element value, a form automatically submits, a new window opens, or a change in focus occurs",
"pass": "The user is advised of the automatic form submission, new window opening, or focus change",
// "pass": "The user is advised of the automatic form submission, new window opening, or focus change",
"potential_warning": "Confirm that the user is advised if, due to a change of element value, a form automatically submits, a new window opens, or a change in focus occurs"
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,19 @@ export let list_markup_review: Rule = {
context: "dom:*",
refactor: {
"RPT_List_UseMarkup": {
"Pass_0": "Pass_0",
// "Pass_0": "Pass_0",
"Potential_1": "Potential_1"}
},
help: {
"en-US": {
"pass": "list_markup_review.html",
// "pass": "list_markup_review.html",
"potential_list": "list_markup_review.html",
"group": "list_markup_review.html"
}
},
messages: {
"en-US": {
"pass": "Proper HTML elements are used to create a list",
// "pass": "Proper HTML elements are used to create a list",
"potential_list": "Verify this is a list and if so, modify to use proper HTML elements for the list",
"group": "Proper HTML elements should be used to create a list"
}
Expand Down
Loading
Loading