From 447b0db5d7eac24cd2621518a6779c75fff2e237 Mon Sep 17 00:00:00 2001 From: j-mendez Date: Sat, 14 Oct 2023 15:28:37 -0400 Subject: [PATCH] feat(rules): add duplicate ID [F77] --- RULES.md | 5 ++- .../src/engine/rules/techniques.rs | 2 +- .../src/engine/rules/wcag_rule_map.rs | 43 ++++++++++++++++++- accessibility-rs/src/i18n/locales.rs | 11 ++++- accessibility-rs/tests/unit/html.rs | 29 +++++++++++++ accessibility-rs/tests/unit/mod.rs | 1 + 6 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 accessibility-rs/tests/unit/html.rs diff --git a/RULES.md b/RULES.md index 8bfa055..a3c818c 100644 --- a/RULES.md +++ b/RULES.md @@ -21,5 +21,8 @@ List of [WCAG2.1 techniques](https://www.w3.org/TR/WCAG21/) and whether or not w | [F40](https://www.w3.org/TR/WCAG20-TECHS/F40.html) | meta redirect used with a time limit | A-AAA | error | 2 | ✅ | | [F41](https://www.w3.org/TR/WCAG20-TECHS/F41.html) | meta refresh used to reload the page | A-AAA | error | 2 | ✅ | | [F47](https://www.w3.org/TR/WCAG20-TECHS/F47.html) | blink element used for attention | A-AAA | error | | ✅ | +| [F77](https://www.w3.org/TR/WCAG20-TECHS/F77.html) | duplicate ID found | A-AAA | error | | ✔️ | -Errors that can be to be tested with automation `13/70`. +Errors that can be to be tested with automation `14/70`. + +Key: ✅ = Complete, ✔️ = Complete with a bit of missing details. diff --git a/accessibility-rs/src/engine/rules/techniques.rs b/accessibility-rs/src/engine/rules/techniques.rs index 774989e..347d0e3 100644 --- a/accessibility-rs/src/engine/rules/techniques.rs +++ b/accessibility-rs/src/engine/rules/techniques.rs @@ -28,7 +28,7 @@ pub enum Techniques { /// F47, /// - F77 + F77, } impl Techniques { diff --git a/accessibility-rs/src/engine/rules/wcag_rule_map.rs b/accessibility-rs/src/engine/rules/wcag_rule_map.rs index aae9f8b..ff44acb 100644 --- a/accessibility-rs/src/engine/rules/wcag_rule_map.rs +++ b/accessibility-rs/src/engine/rules/wcag_rule_map.rs @@ -4,9 +4,11 @@ use crate::engine::rules::utils::nodes::{ get_unique_selector, has_alt, validate_empty_nodes, validate_missing_attr, }; use crate::engine::rules::wcag_base::{Guideline, IssueType, Principle}; -use accessibility_scraper::Selector; +use accessibility_scraper::{ElementRef, Selector}; use selectors::Element; use std::collections::BTreeMap; +use std::collections::HashMap; +use std::ops::Add; // todo: validate each element and add a shape that can prevent repitiion lazy_static! { @@ -42,6 +44,45 @@ lazy_static! { alphabetic && lang.len() < 12 }, "3.XmlLang") }), + Rule::new(Techniques::F77, IssueType::Error, Principle::Robust, Guideline::Compatible, "1", |_rule, nodes| { + let mut id_map: HashMap<&str, u8> = HashMap::new(); + let mut valid = true; + + for item in nodes { + let ele = item.0; + let tree = ele.tree(); + for e in tree.nodes() { + match ElementRef::wrap(e) { + Some(element) => { + match element.value().id() { + Some(s) => { + if id_map.contains_key(s) { + let u = id_map.get(s); + match u { + Some(u) => { + valid = false; + id_map.insert(s, u.add(1)); + } + _ => () + } + } else { + id_map.insert(s, 1); + } + } + _ => () + } + } + _ => (), + } + } + } + + let duplicate_ids = id_map.into_iter().filter_map(|(id, size)| if size >= 1 { Some(id.to_string()) } else { None }).collect(); + + // let message = t!(&crate::i18n::locales::get_message_i18n_str(_rule, "")); + + Validation::new(valid, "", duplicate_ids, "") + }), ])), ("meta", Vec::from([ Rule::new(Techniques::F40, IssueType::Error, Principle::Operable, Guideline::EnoughTime, "1", |_rule, nodes| { diff --git a/accessibility-rs/src/i18n/locales.rs b/accessibility-rs/src/i18n/locales.rs index f417c90..8e0e90d 100644 --- a/accessibility-rs/src/i18n/locales.rs +++ b/accessibility-rs/src/i18n/locales.rs @@ -64,7 +64,7 @@ impl Langs { } /// get message config type -pub fn get_message_i18n(rule: &Rule, section: &str, lang: &str) -> String { +pub fn get_message_i18n_str(rule: &Rule, section: &str) -> String { // todo: add criteria handling fix let base = [rule.guideline.as_index(), rule.success_criteria].join("_") + "_"; let message = if section.is_empty() { @@ -74,5 +74,12 @@ pub fn get_message_i18n(rule: &Rule, section: &str, lang: &str) -> String { }; let message = [base.as_str(), message.as_str()].join("").to_string(); - t!(&message, lang = lang) + message +} + +/// get message config type +pub fn get_message_i18n(rule: &Rule, section: &str, lang: &str) -> String { + let message = get_message_i18n_str(rule, section); + + t!(&message, locale = lang) } diff --git a/accessibility-rs/tests/unit/html.rs b/accessibility-rs/tests/unit/html.rs new file mode 100644 index 0000000..00bcae5 --- /dev/null +++ b/accessibility-rs/tests/unit/html.rs @@ -0,0 +1,29 @@ +//! Test generic html elements +use accessibility_rs::AuditConfig; + +#[test] +/// duplicate html elements +fn _audit_duplicate_element_id() { + let audit = accessibility_rs::audit(AuditConfig::basic( + r###" + + Duplicate ID: Do not Use. + + +
+
+ + "###, + )); + let mut valid = true; + + for x in &audit { + println!("{:?}", x); + if x.code == "WCAGAAA.Principle4.Guideline4_1.F77" { + valid = false; + break; + } + } + + assert_eq!(valid, false) +} diff --git a/accessibility-rs/tests/unit/mod.rs b/accessibility-rs/tests/unit/mod.rs index 99f0c83..5d617db 100644 --- a/accessibility-rs/tests/unit/mod.rs +++ b/accessibility-rs/tests/unit/mod.rs @@ -1,4 +1,5 @@ pub mod anchor; pub mod heading; +pub mod html; pub mod img; pub mod meta;