From bb225779444cada82674fcd2f8409f608515b5ba Mon Sep 17 00:00:00 2001 From: j-mendez Date: Mon, 9 Oct 2023 11:14:20 -0400 Subject: [PATCH] chore(rule): add html lang validation --- Cargo.lock | 61 ++++++++++++++----- RULES.md | 1 + accessibility-rs/Cargo.toml | 4 +- accessibility-rs/src/engine/issue.rs | 4 ++ accessibility-rs/src/engine/rules/ids.rs | 12 ++-- .../src/engine/rules/wcag_base.rs | 3 + .../src/engine/rules/wcag_rule_map.rs | 9 +++ accessibility-rs/src/i18n/locales.rs | 5 +- 8 files changed, 78 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 881b47f..8c26c61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "accessibility-rs" -version = "0.0.5" +version = "0.0.6" dependencies = [ "accessibility-scraper", "accessibility-tree", @@ -17,6 +17,8 @@ dependencies = [ "serde", "slotmap", "smallvec 1.11.1", + "strum", + "strum_macros", "taffy", "url", "wasm-bindgen-test", @@ -189,7 +191,7 @@ dependencies = [ "itoa", "matches", "phf 0.7.24", - "proc-macro2 1.0.68", + "proc-macro2 1.0.69", "procedural-masquerade", "quote 1.0.33", "smallvec 0.6.14", @@ -203,7 +205,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bb1c84e87c717666564ec056105052331431803d606bd45529b28547b611eef" dependencies = [ "phf_codegen 0.7.24", - "proc-macro2 1.0.68", + "proc-macro2 1.0.69", "procedural-masquerade", "quote 1.0.33", "syn 1.0.109", @@ -337,6 +339,12 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "html5ever" version = "0.26.0" @@ -346,7 +354,7 @@ dependencies = [ "log", "mac", "markup5ever", - "proc-macro2 1.0.68", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 1.0.109", ] @@ -625,9 +633,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1106fec09662ec6dd98ccac0f81cef56984d0b49f75c92d8cbad76e20c005c" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -653,7 +661,7 @@ version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ - "proc-macro2 1.0.68", + "proc-macro2 1.0.69", ] [[package]] @@ -840,6 +848,12 @@ dependencies = [ "bitflags", ] +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "scoped-tls" version = "1.0.1" @@ -892,7 +906,7 @@ version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ - "proc-macro2 1.0.68", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 2.0.38", ] @@ -977,8 +991,27 @@ checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" dependencies = [ "phf_generator 0.10.0", "phf_shared 0.10.0", - "proc-macro2 1.0.68", + "proc-macro2 1.0.69", + "quote 1.0.33", +] + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + +[[package]] +name = "strum_macros" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +dependencies = [ + "heck", + "proc-macro2 1.0.69", "quote 1.0.33", + "rustversion", + "syn 2.0.38", ] [[package]] @@ -998,7 +1031,7 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.68", + "proc-macro2 1.0.69", "quote 1.0.33", "unicode-ident", ] @@ -1009,7 +1042,7 @@ version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ - "proc-macro2 1.0.68", + "proc-macro2 1.0.69", "quote 1.0.33", "unicode-ident", ] @@ -1148,7 +1181,7 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.68", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 2.0.38", "wasm-bindgen-shared", @@ -1182,7 +1215,7 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ - "proc-macro2 1.0.68", + "proc-macro2 1.0.69", "quote 1.0.33", "syn 2.0.38", "wasm-bindgen-backend", @@ -1215,7 +1248,7 @@ version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" dependencies = [ - "proc-macro2 1.0.68", + "proc-macro2 1.0.69", "quote 1.0.33", ] diff --git a/RULES.md b/RULES.md index a889071..b774301 100644 --- a/RULES.md +++ b/RULES.md @@ -10,6 +10,7 @@ List of techniques we want to have and whether we have it handled or not for WCA | -------------------------------------------------- | -------------------------- | -------- | | [H25](https://www.w3.org/TR/WCAG20-TECHS/H25.html) | empty titles | ✅ | | [H32](https://www.w3.org/TR/WCAG20-TECHS/H32.html) | missing form submit button | ✅ | +| [H57](https://www.w3.org/TR/WCAG20-TECHS/H57.html) | html contains valid lang | ✅ | ### WCAGAA diff --git a/accessibility-rs/Cargo.toml b/accessibility-rs/Cargo.toml index 9e39e67..69dd252 100644 --- a/accessibility-rs/Cargo.toml +++ b/accessibility-rs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "accessibility-rs" -version = "0.0.5" +version = "0.0.6" authors = ["The A11yWatch Project Developers", "Jeff Mendez "] edition = "2021" license = "MIT OR Apache-2.0" @@ -26,6 +26,8 @@ accessibility-tree = { version = "0.0.1", path = "../accessibility-tree/victor" markup5ever = "0.11.0" cssparser = { workspace = true } slotmap = "1.0.6" +strum = "0.25" +strum_macros = "0.25" [dev-dependencies] wasm-bindgen-test = "0.3.37" diff --git a/accessibility-rs/src/engine/issue.rs b/accessibility-rs/src/engine/issue.rs index 2cb21c6..592f419 100644 --- a/accessibility-rs/src/engine/issue.rs +++ b/accessibility-rs/src/engine/issue.rs @@ -18,6 +18,10 @@ pub struct Clip { pub struct RunnerExtras { /// the url to get more information on the issue pub help_url: &'static str, + /// a detailed description of the issue + pub description: &'static str, + /// the impact level criteria + pub impact: &'static str, } /// issue details diff --git a/accessibility-rs/src/engine/rules/ids.rs b/accessibility-rs/src/engine/rules/ids.rs index 81d1f6a..0c2a976 100644 --- a/accessibility-rs/src/engine/rules/ids.rs +++ b/accessibility-rs/src/engine/rules/ids.rs @@ -1,27 +1,29 @@ use std::vec; +use strum_macros::IntoStaticStr; -#[derive(PartialOrd, Ord, std::cmp::Eq, PartialEq, Hash, Debug)] +#[derive(PartialOrd, Ord, std::cmp::Eq, PartialEq, Hash, Debug, IntoStaticStr)] /// techniques for WCAG pub enum Techniques { /// H25, /// H32, + /// + H57 } impl Techniques { /// get rule id to string pub fn as_str(&self) -> &'static str { - match self { - Techniques::H25 => "H25", - Techniques::H32 => "H32", - } + // todo: make macro + self.into() } /// get pairs for a rule pub fn pairs(&self) -> Vec<&'static str> { match self { Techniques::H25 => vec!["H25.1.NoTitleEl", "H25.1.EmptyTitle"], Techniques::H32 => vec!["H32.2"], + Techniques::H57 => vec!["H57.2", "H57.3.Lang", "H57.3.XmlLang"], } } } diff --git a/accessibility-rs/src/engine/rules/wcag_base.rs b/accessibility-rs/src/engine/rules/wcag_base.rs index acd26e4..0027444 100644 --- a/accessibility-rs/src/engine/rules/wcag_base.rs +++ b/accessibility-rs/src/engine/rules/wcag_base.rs @@ -47,6 +47,8 @@ impl Principle { pub enum Guideline { /// Provide ways to help users navigate, find content, and determine where they are. Navigable, + /// Make text content readable and understandable. + Readable, /// Make Web pages appear and operate in predictable ways. Predictable, } @@ -55,6 +57,7 @@ impl Guideline { pub fn as_str(&self) -> &'static str { match self { Guideline::Navigable => "Guideline2_4", + Guideline::Readable => "Guideline3_1", Guideline::Predictable => "Guideline3_2", } } diff --git a/accessibility-rs/src/engine/rules/wcag_rule_map.rs b/accessibility-rs/src/engine/rules/wcag_rule_map.rs index 4f2184a..49d6ec4 100644 --- a/accessibility-rs/src/engine/rules/wcag_rule_map.rs +++ b/accessibility-rs/src/engine/rules/wcag_rule_map.rs @@ -9,6 +9,15 @@ lazy_static! { /// a list of rules that should be applied for WCAG1 pub static ref RULES_A: BTreeMap<&'static str, Vec> = vec![ + ("html", Vec::from([ + Rule::new(Techniques::H57, Criteria::Error, Principle::Understandable, Guideline::Readable, |_rule, nodes| { + (!nodes[0].0.attr("lang").unwrap_or_default().is_empty(), "2", Default::default()) + }), + Rule::new(Techniques::H57, Criteria::Error, Principle::Understandable, Guideline::Readable, |_rule, nodes| { + let lang = nodes[0].0.attr("lang").unwrap_or_default(); + (lang.chars().all(|x| x.is_alphanumeric()) && !lang.contains("_"), "3.Lang", Default::default()) + }), + ])), // empty titles ("title", Vec::from([ Rule::new(Techniques::H25, Criteria::Error, Principle::Operable, Guideline::Navigable, |_rule, nodes| { diff --git a/accessibility-rs/src/i18n/locales.rs b/accessibility-rs/src/i18n/locales.rs index 44699ff..f2ea21b 100644 --- a/accessibility-rs/src/i18n/locales.rs +++ b/accessibility-rs/src/i18n/locales.rs @@ -101,7 +101,10 @@ lazy_static! { BTreeMap::from([ (Techniques::H25.pairs()[0], Messages::new(&"A title should be provided for the document, using a non-empty title element in the head section.", "", "")), (Techniques::H25.pairs()[1], Messages::new(&"The title element in the head section should be non-empty.", "", "")), - (Techniques::H32.pairs()[0], Messages::new(&r###"Form does not contain a submit button (input type="submit", input type="image", or button type="submit")."###, "", "")) + (Techniques::H32.pairs()[0], Messages::new(&r###"Form does not contain a submit button (input type="submit", input type="image", or button type="submit")."###, "", "")), + (Techniques::H57.pairs()[0], Messages::new(&"The html element should have a lang or xml:lang attribute which describes the language of the document.", "", "")), + (Techniques::H57.pairs()[1], Messages::new(&"The language specified in the lang attribute of the document element does not appear to be well-formed.", "", "")), + (Techniques::H57.pairs()[2], Messages::new(&"The language specified in the xml:lang attribute of the document element does not appear to be well-formed.", "", "")), ]) }; }