diff --git a/Cargo.lock b/Cargo.lock index eb00433..de08234 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "accessibility-rs" -version = "0.0.19" +version = "0.0.20" dependencies = [ "accessibility-scraper", "accessibility-tree", diff --git a/accessibility-rs/Cargo.toml b/accessibility-rs/Cargo.toml index b445a9e..f8ba794 100644 --- a/accessibility-rs/Cargo.toml +++ b/accessibility-rs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "accessibility-rs" -version = "0.0.19" +version = "0.0.20" authors = ["The A11yWatch Project Developers", "Jeff Mendez "] edition = "2021" license = "MIT OR Apache-2.0" diff --git a/accessibility-rs/src/engine/issue.rs b/accessibility-rs/src/engine/issue.rs index dfed8ce..94ace6b 100644 --- a/accessibility-rs/src/engine/issue.rs +++ b/accessibility-rs/src/engine/issue.rs @@ -30,7 +30,7 @@ pub struct Issue { /// the context of the issue or raw html pub context: String, /// the selector to identify the issue with css, xpath, or raw path - pub selectors: Vec<&'static str>, + pub selectors: Vec, /// the type of code for the issue pub code: String, /// the type of issue @@ -56,7 +56,7 @@ impl Issue { context: &str, code: &str, issue_type: &'static str, - selectors: Vec<&'static str>, + selectors: Vec, ) -> Issue { Issue { message, diff --git a/accessibility-rs/src/engine/rules/rule.rs b/accessibility-rs/src/engine/rules/rule.rs index 445c86f..92b7645 100644 --- a/accessibility-rs/src/engine/rules/rule.rs +++ b/accessibility-rs/src/engine/rules/rule.rs @@ -12,7 +12,7 @@ pub struct Validation { /// the sub-technique pub id: &'static str, /// elements that match the issue - pub elements: Vec<&'static str>, + pub elements: Vec, /// the message of the error pub message: &'static str, } @@ -22,7 +22,7 @@ impl Validation { pub fn new( valid: bool, id: &'static str, - elements: Vec<&'static str>, + elements: Vec, message: &'static str, ) -> Self { Self { diff --git a/accessibility-rs/src/engine/rules/wcag_rule_map.rs b/accessibility-rs/src/engine/rules/wcag_rule_map.rs index 3ad2455..874d846 100644 --- a/accessibility-rs/src/engine/rules/wcag_rule_map.rs +++ b/accessibility-rs/src/engine/rules/wcag_rule_map.rs @@ -1,11 +1,13 @@ use crate::engine::rules::rule::{Rule, Validation}; use crate::engine::rules::techniques::Techniques; use crate::engine::rules::wcag_base::{Guideline, IssueType, Principle}; +use crate::ElementRef; use accessibility_scraper::Selector; use selectors::Element; use slotmap::DefaultKey; use std::collections::BTreeMap; -use crate::ElementRef; + +type ElementNodes<'a> = Vec<(ElementRef<'a>, Option)>; /// a valid alt attribute for image fn has_alt(ele: ElementRef<'_>) -> bool { @@ -26,7 +28,7 @@ fn has_alt(ele: ElementRef<'_>) -> bool { } /// elements empty -fn is_empty(nodes: &Vec<(ElementRef<'_>, Option)>) -> bool { +fn is_empty(nodes: &ElementNodes) -> bool { let mut empty = false; for ele in nodes { let ele = ele.0; @@ -35,6 +37,41 @@ fn is_empty(nodes: &Vec<(ElementRef<'_>, Option)>) -> bool { empty } +/// get the unique selector for an element +fn get_unique_selector(ele: &ElementRef<'_>) -> String { + let mut selector = String::new(); + + if ele.has_attribute("id") { + selector = ele.attr("id").unwrap_or_default().to_string() + } + + if selector.is_empty() && ele.has_attribute("class") { + selector = ele.local_name().to_string(); + } + + if selector.is_empty() { + // TODO: get parent selector for node if any + selector = ele.value().name().to_string(); + } + + selector +} + +/// validate missing title +fn validate_missing_title(nodes: &ElementNodes, id: &'static str) -> Validation { + let mut elements = Vec::new(); + let mut valid = true; + + nodes.iter().for_each(|e| { + if e.0.attr("title").unwrap_or_default().is_empty() { + valid = false; + elements.push(get_unique_selector(&e.0)) + } + }); + + Validation::new(valid, id, elements, "") +} + // todo: validate each element and add a shape that can prevent repitiion lazy_static! { /// a list of rules that should be applied for WCAG1 @@ -119,12 +156,12 @@ lazy_static! { ])), ("iframe", Vec::from([ Rule::new(Techniques::H64, IssueType::Error, Principle::Operable, Guideline::Navigable, "1", |_rule, nodes| { - Validation::new_issue(nodes.iter().all(|e| !e.0.attr("title").unwrap_or_default().is_empty()), "") + validate_missing_title(nodes, "1") }), ])), ("frame", Vec::from([ Rule::new(Techniques::H64, IssueType::Error, Principle::Operable, Guideline::Navigable, "1", |_rule, nodes| { - Validation::new_issue(nodes.iter().all(|e| !e.0.attr("title").unwrap_or_default().is_empty()), "") + validate_missing_title(nodes, "1") }), ])), ("form", Vec::from([ diff --git a/accessibility-rs/tests/unit/meta.rs b/accessibility-rs/tests/unit/meta.rs index ca98690..1cca6ce 100644 --- a/accessibility-rs/tests/unit/meta.rs +++ b/accessibility-rs/tests/unit/meta.rs @@ -128,7 +128,9 @@ fn _iframe_missing_title() { } } + // this should be valid assert_eq!(valid, true); + let config = AuditConfig::new( r###" @@ -136,7 +138,7 @@ fn _iframe_missing_title() { - + <body> <a href="lib.html" title="Library link">Select to