diff --git a/kayle_innate/src/engine/rules/mod.rs b/kayle_innate/src/engine/rules/mod.rs index 9ac1f0f..88de458 100644 --- a/kayle_innate/src/engine/rules/mod.rs +++ b/kayle_innate/src/engine/rules/mod.rs @@ -1,3 +1,10 @@ -// accessibility rules todo: +/// rules or techniques pub mod ids; +/// the rule to follow +pub mod rule; +/// wcag audit pub mod wcag; +/// the base of the wcag set +pub mod wcag_base; +/// rules to map to +pub mod wcag_rule_map; diff --git a/kayle_innate/src/engine/rules/rule.rs b/kayle_innate/src/engine/rules/rule.rs new file mode 100644 index 0000000..991de8b --- /dev/null +++ b/kayle_innate/src/engine/rules/rule.rs @@ -0,0 +1,44 @@ +use crate::engine::rules::ids::Techniques; +use crate::engine::rules::wcag_base::{Criteria, Guideline, Principle}; +use crate::ElementRef; + +/// the rule validation method that should be performed. +pub struct Rule { + /// the message id of the rule to point to the locale + pub rule_id: Techniques, + /// the type of rule + pub criteria: Criteria, + /// validate a test returns (valid, rule, selectors) + pub validate: fn( + &String, + &Vec>, + css: &cssparser::Parser<'_, '_>, + ) -> (bool, &'static str, Vec<&'static str>), + /// the principle type + pub principle: Principle, + /// the guideline to follow + pub guideline: Guideline, +} + +impl Rule { + /// a new rule type + pub fn new( + rule_id: Techniques, + criteria: Criteria, + principle: Principle, + guideline: Guideline, + validate: fn( + &String, + &Vec>, + &cssparser::Parser<'_, '_>, + ) -> (bool, &'static str, Vec<&'static str>), + ) -> Rule { + Rule { + rule_id, + criteria, + guideline, + principle, + validate, + } + } +} diff --git a/kayle_innate/src/engine/rules/wcag.rs b/kayle_innate/src/engine/rules/wcag.rs index b85ec53..10c1d89 100644 --- a/kayle_innate/src/engine/rules/wcag.rs +++ b/kayle_innate/src/engine/rules/wcag.rs @@ -1,163 +1,21 @@ -use crate::engine::rules::ids::Techniques; +use super::wcag_rule_map::RULES_A; use crate::i18n::locales::{get_message, Langs}; use crate::{console_log, engine::issue::Issue}; -use scraper::Selector; -use scraper::{ElementRef, Html}; -use std::collections::BTreeMap; - -/// the success criteria to use -#[derive(Debug)] -enum Criteria { - /// a hard error that should be fixed - Error, - /// a warning that may be an issue - Warning, - /// a generic notice to help accessibility needs - Notice, -} - -impl Criteria { - /// get rule id to string - pub fn as_str(&self) -> &'static str { - match self { - Criteria::Error => "error", - Criteria::Warning => "warning", - Criteria::Notice => "notice", - } - } -} - -/// wcag principle to follow -enum Principle { - Perceivable, - Operable, - Understandable, - Robust, -} - -impl Principle { - pub fn as_str(&self) -> &'static str { - match self { - Principle::Perceivable => "Principle1", - Principle::Operable => "Principle2", - Principle::Understandable => "Principle3", - Principle::Robust => "Principle4", - } - } -} - -/// wcag principle to follow -enum Guideline { - /// Provide ways to help users navigate, find content, and determine where they are. - Navigable, - /// Make Web pages appear and operate in predictable ways. - Predictable, -} - -impl Guideline { - pub fn as_str(&self) -> &'static str { - match self { - Guideline::Navigable => "Guideline2_4", - Guideline::Predictable => "Guideline3_2", - } - } -} - -/// the rule validation method that should be performed. -struct Rule { - /// the message id of the rule to point to the locale - pub rule_id: Techniques, - /// the type of rule - pub criteria: Criteria, - /// validate a test returns (valid, rule, selectors) - pub validate: fn( - &String, - &Vec>, - css: &cssparser::Parser<'_, '_>, - ) -> (bool, &'static str, Vec<&'static str>), - /// the principle type - pub principle: Principle, - /// the guideline to follow - pub guideline: Guideline, -} - -impl Rule { - /// a new rule type - pub fn new( - rule_id: Techniques, - criteria: Criteria, - principle: Principle, - guideline: Guideline, - validate: fn( - &String, - &Vec>, - &cssparser::Parser<'_, '_>, - ) -> (bool, &'static str, Vec<&'static str>), - ) -> Rule { - Rule { - rule_id, - criteria, - guideline, - principle, - validate, - } - } -} - -// todo: validate each element and add a shape that can prevent repitiion -lazy_static! { - /// a list of rules that should be applied for WCAG1 - static ref RULES_A: BTreeMap<&'static str, Vec> = - vec![ - // empty titles - ("title", Vec::from([ - Rule::new(Techniques::H25, Criteria::Error, Principle::Operable, Guideline::Navigable, |_rule, nodes, _css_parser| { - (!nodes.is_empty(), "1.NoTitleEl", Default::default()) - }), - Rule::new(Techniques::H25, Criteria::Error, Principle::Understandable, Guideline::Predictable, |_rule, nodes, _css_parser| { - (nodes.is_empty() || nodes[0].html().is_empty(), "2", Default::default()) - }), - ])), - // missing label - ("form", Vec::from([ - Rule::new(Techniques::H32, Criteria::Error, Principle::Operable, Guideline::Predictable, |_rule, nodes, _css_parser| { - // check the first element for now - let mut valid = false; - - for ele in nodes { - let selector = unsafe { Selector::parse("button[type=submit]").unwrap_unchecked() }; - valid = match ele.select(&selector).next() { - Some(_) => true, - _ => false - }; - } - - (valid, "2", Default::default()) - }), - ])) - ] - .into_iter() - .collect(); -} +use scraper::ElementRef; /// baseline for all rules #[derive(Default)] -pub struct WCAG3AA {} +pub struct WCAG3AA; /// wcag rules to test for impl WCAG3AA { /// init the rules pub fn audit( // allow tree mutation until threads or setup the tree with initial elements. - mut tree: std::collections::BTreeMap>>, + tree: std::collections::BTreeMap>>, _css: cssparser::Parser<'_, '_>, // todo: get configs like viewport ) -> Vec { - // pre populate must have keys - if !tree.contains_key("title") { - tree.insert("title".into(), Default::default()); - } - let mut issues: Vec = Vec::new(); // go through nodes and map to validation rules diff --git a/kayle_innate/src/engine/rules/wcag_base.rs b/kayle_innate/src/engine/rules/wcag_base.rs new file mode 100644 index 0000000..acd26e4 --- /dev/null +++ b/kayle_innate/src/engine/rules/wcag_base.rs @@ -0,0 +1,61 @@ +/// the success criteria to use +#[derive(Debug)] +pub enum Criteria { + /// a hard error that should be fixed + Error, + /// a warning that may be an issue + Warning, + /// a generic notice to help accessibility needs + Notice, +} + +impl Criteria { + /// get rule id to string + pub fn as_str(&self) -> &'static str { + match self { + Criteria::Error => "error", + Criteria::Warning => "warning", + Criteria::Notice => "notice", + } + } +} + +/// wcag principle to follow +pub enum Principle { + /// Provide text alternatives for any non-text content so that it can be changed into other forms people need, such as large print, braille, speech, symbols or simpler language. + Perceivable, + /// Make all functionality available from a keyboard. + Operable, + /// Make text content readable and understandable. + Understandable, + /// Maximize compatibility with current and future user agents, including assistive technologies. + Robust, +} + +impl Principle { + pub fn as_str(&self) -> &'static str { + match self { + Principle::Perceivable => "Principle1", + Principle::Operable => "Principle2", + Principle::Understandable => "Principle3", + Principle::Robust => "Principle4", + } + } +} + +/// wcag principle to follow +pub enum Guideline { + /// Provide ways to help users navigate, find content, and determine where they are. + Navigable, + /// Make Web pages appear and operate in predictable ways. + Predictable, +} + +impl Guideline { + pub fn as_str(&self) -> &'static str { + match self { + Guideline::Navigable => "Guideline2_4", + Guideline::Predictable => "Guideline3_2", + } + } +} diff --git a/kayle_innate/src/engine/rules/wcag_rule_map.rs b/kayle_innate/src/engine/rules/wcag_rule_map.rs new file mode 100644 index 0000000..908478e --- /dev/null +++ b/kayle_innate/src/engine/rules/wcag_rule_map.rs @@ -0,0 +1,42 @@ +use crate::engine::rules::ids::Techniques; +use crate::engine::rules::rule::Rule; +use crate::engine::rules::wcag_base::{Criteria, Guideline, Principle}; +use scraper::Selector; +use std::collections::BTreeMap; + +// todo: validate each element and add a shape that can prevent repitiion +lazy_static! { + /// a list of rules that should be applied for WCAG1 + pub static ref RULES_A: BTreeMap<&'static str, Vec> = + vec![ + // empty titles + ("title", Vec::from([ + Rule::new(Techniques::H25, Criteria::Error, Principle::Operable, Guideline::Navigable, |_rule, nodes, _css_parser| { + (!nodes.is_empty(), "1.NoTitleEl", Default::default()) + }), + Rule::new(Techniques::H25, Criteria::Error, Principle::Understandable, Guideline::Predictable, |_rule, nodes, _css_parser| { + (nodes.is_empty() || nodes[0].html().is_empty(), "2", Default::default()) + }), + ])), + // missing label + ("form", Vec::from([ + Rule::new(Techniques::H32, Criteria::Error, Principle::Operable, Guideline::Predictable, |_rule, nodes, _css_parser| { + // check the first element for now + let mut valid = false; + + for ele in nodes { + // todo: static selectors + let selector = unsafe { Selector::parse("button[type=submit]").unwrap_unchecked() }; + valid = match ele.select(&selector).next() { + Some(_) => true, + _ => false + }; + } + + (valid, "2", Default::default()) + }), + ])) + ] + .into_iter() + .collect(); +} diff --git a/kayle_innate/src/lib.rs b/kayle_innate/src/lib.rs index dd6b5c1..ff590f3 100644 --- a/kayle_innate/src/lib.rs +++ b/kayle_innate/src/lib.rs @@ -204,12 +204,11 @@ pub fn parse_accessibility_tree( let t = now(); // parse doc will start from html downwards // accessibility tree for ordered element mappings - let mut accessibility_tree: BTreeMap>> = BTreeMap::new(); + let mut accessibility_tree: BTreeMap>> = + BTreeMap::from([("title".into(), Default::default())]); for node in html.tree.nodes() { - let h = scraper::element_ref::ElementRef::wrap(node); - - match h { + match scraper::element_ref::ElementRef::wrap(node) { Some(element) => { let element_name = element.value().name(); @@ -234,13 +233,12 @@ pub fn parse_accessibility_tree( /// audit a web page passing the html and css rules. pub fn _audit_not_ready(html: &str, _css_rules: &str) -> Result { set_panic_hook(); - // parse document and get lifetimes for nodes - let h = Box::new(scraper::Html::parse_document(html)); + let html = Box::new(scraper::Html::parse_document(html)); // TODO: if the css rules are empty extract the css from the HTML let css_rules = &mut cssparser::ParserInput::new(&_css_rules); // TODO: build the rules to css blocks that selectors can be used to find the element of the style. let mut _css_parser = cssparser::Parser::new(css_rules); - let _tree = parse_accessibility_tree(&h); + let _tree = parse_accessibility_tree(&html); let _audit = engine::rules::wcag::WCAG3AA::audit(_tree, _css_parser); // todo: map to JsValues instead of serde