Skip to content

Commit

Permalink
chore(rules): add xml:lang case
Browse files Browse the repository at this point in the history
  • Loading branch information
j-mendez committed Oct 13, 2023
1 parent d0e20e2 commit d24abaf
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 41 deletions.
2 changes: 1 addition & 1 deletion accessibility-rs/src/engine/audit/wcag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ impl WCAG3AA {
rule.rule_id.as_str(),
]
.join("."),
rule.criteria.as_str(),
rule.issue_type.as_str(),
validation.elements,
);
issues.push(issue);
Expand Down
11 changes: 7 additions & 4 deletions accessibility-rs/src/engine/rules/rule.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use slotmap::DefaultKey;

use crate::engine::rules::techniques::Techniques;
use crate::engine::rules::wcag_base::{Criteria, Guideline, Principle};
use crate::engine::rules::wcag_base::{IssueType, Guideline, Principle};
use crate::ElementRef;

/// the validation response
Expand Down Expand Up @@ -48,30 +48,33 @@ 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,
pub issue_type: IssueType,
/// validate a test returns (valid, rule, selectors)
pub validate: fn(&str, &Vec<(ElementRef<'_>, Option<DefaultKey>)>) -> Validation,
/// the principle type
pub principle: Principle,
/// the guideline to follow
pub guideline: Guideline,
/// the success criteria
pub success_criteria: u8,
}

impl Rule {
/// a new rule type
pub fn new(
rule_id: Techniques,
criteria: Criteria,
issue_type: IssueType,
principle: Principle,
guideline: Guideline,
validate: fn(&str, &Vec<(ElementRef<'_>, Option<DefaultKey>)>) -> Validation,
) -> Rule {
Rule {
rule_id,
criteria,
issue_type,
guideline,
principle,
validate,
success_criteria: 0
}
}
}
10 changes: 5 additions & 5 deletions accessibility-rs/src/engine/rules/wcag_base.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// the success criteria to use
#[derive(Debug)]
pub enum Criteria {
pub enum IssueType {
/// a hard error that should be fixed
Error,
/// a warning that may be an issue
Expand All @@ -9,13 +9,13 @@ pub enum Criteria {
Notice,
}

impl Criteria {
impl IssueType {
/// get rule id to string
pub fn as_str(&self) -> &'static str {
match self {
Criteria::Error => "error",
Criteria::Warning => "warning",
Criteria::Notice => "notice",
IssueType::Error => "error",
IssueType::Warning => "warning",
IssueType::Notice => "notice",
}
}
}
Expand Down
59 changes: 30 additions & 29 deletions accessibility-rs/src/engine/rules/wcag_rule_map.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::engine::rules::rule::{Rule, Validation};
use crate::engine::rules::techniques::Techniques;
use crate::engine::rules::wcag_base::{Criteria, Guideline, Principle};
use crate::engine::rules::wcag_base::{IssueType, Guideline, Principle};
use crate::ElementRef;
use accessibility_scraper::Selector;
use selectors::Element;
Expand Down Expand Up @@ -41,36 +41,37 @@ lazy_static! {
pub static ref RULES_A: BTreeMap<&'static str, Vec<Rule>> =
vec![
("html", Vec::from([
Rule::new(Techniques::H57, Criteria::Error, Principle::Understandable, Guideline::Readable, |_rule, nodes| {
Validation::new_issue(!nodes[0].0.attr("lang").unwrap_or_default().is_empty(), "2")
Rule::new(Techniques::H57, IssueType::Error, Principle::Understandable, Guideline::Readable, |_rule, nodes| {
let n = nodes[0].0;
Validation::new_issue(!n.attr("lang").unwrap_or_default().is_empty() || !n.attr("xml:lang").unwrap_or_default().is_empty(), "2")
}),
Rule::new(Techniques::H57, Criteria::Error, Principle::Understandable, Guideline::Readable, |_rule, nodes| {
Rule::new(Techniques::H57, IssueType::Error, Principle::Understandable, Guideline::Readable, |_rule, nodes| {
let lang = nodes[0].0.attr("lang").unwrap_or_default();
let alphabetic = lang.chars().all(|x| x.is_alphabetic());
// <https://www.rfc-editor.org/rfc/bcp/bcp47.txt>
Validation::new_issue(if lang.len() > 3 {
let mut c = lang.chars();
let has_underscore = c.nth(3).unwrap_or_default() == '_' || lang.len() >= 4 && c.nth(1).unwrap_or_default() == '_';
let has_underscore = c.nth(2).unwrap_or_default() == '_' || lang.len() >= 4 && c.nth(1).unwrap_or_default() == '_';
alphabetic && has_underscore && lang.len() < 12
} else {
alphabetic && lang.len() < 12
}, "3.Lang")
}),
Rule::new(Techniques::H57, Criteria::Error, Principle::Understandable, Guideline::Readable, |_rule, nodes| {
Rule::new(Techniques::H57, IssueType::Error, Principle::Understandable, Guideline::Readable, |_rule, nodes| {
let lang = nodes[0].0.attr("xml:lang").unwrap_or_default();
let alphabetic = lang.chars().all(|x| x.is_alphabetic());
let alphabetic = lang.chars().all(|x| x == '_' || x.is_alphabetic());
// <https://www.rfc-editor.org/rfc/bcp/bcp47.txt>
Validation::new_issue(if lang.len() > 3 {
let mut c = lang.chars();
let has_underscore = c.nth(3).unwrap_or_default() == '_' || lang.len() >= 4 && c.nth(1).unwrap_or_default() == '_';
alphabetic && has_underscore && lang.len() < 12
let mut c = lang.chars();
let has_underscore = c.nth(2).unwrap_or_default() == '_' || lang.len() >= 4 && c.nth(1).unwrap_or_default() == '_';
alphabetic && has_underscore && lang.len() < 12
} else {
alphabetic && lang.len() < 12
}, "3.XmlLang")
}),
])),
("meta", Vec::from([
Rule::new(Techniques::F40, Criteria::Error, Principle::Operable, Guideline::EnoughTime, |_rule, nodes| {
Rule::new(Techniques::F40, IssueType::Error, Principle::Operable, Guideline::EnoughTime, |_rule, nodes| {
let mut valid = true;

for node in nodes {
Expand All @@ -86,7 +87,7 @@ lazy_static! {

Validation::new_issue(valid, "2")
}),
Rule::new(Techniques::F41, Criteria::Error, Principle::Understandable, Guideline::EnoughTime, |_rule, nodes| {
Rule::new(Techniques::F41, IssueType::Error, Principle::Understandable, Guideline::EnoughTime, |_rule, nodes| {
let mut valid = true;

for node in nodes {
Expand All @@ -104,30 +105,30 @@ lazy_static! {
}),
])),
("title", Vec::from([
Rule::new(Techniques::H25, Criteria::Error, Principle::Operable, Guideline::Navigable, |_rule, nodes| {
Rule::new(Techniques::H25, IssueType::Error, Principle::Operable, Guideline::Navigable, |_rule, nodes| {
Validation::new_issue(!nodes.is_empty(), "1.NoTitleEl")
}),
Rule::new(Techniques::H25, Criteria::Error, Principle::Operable, Guideline::Navigable, |_rule, nodes| {
Rule::new(Techniques::H25, IssueType::Error, Principle::Operable, Guideline::Navigable, |_rule, nodes| {
Validation::new_issue(nodes.is_empty() || nodes[0].0.html().is_empty(), "1.EmptyTitle")
}),
])),
("blink", Vec::from([
Rule::new(Techniques::F47, Criteria::Error, Principle::Operable, Guideline::EnoughTime, |_rule, nodes| {
Rule::new(Techniques::F47, IssueType::Error, Principle::Operable, Guideline::EnoughTime, |_rule, nodes| {
Validation::new_issue(nodes.is_empty(), "")
}),
])),
("iframe", Vec::from([
Rule::new(Techniques::H64, Criteria::Error, Principle::Operable, Guideline::Navigable, |_rule, nodes| {
Rule::new(Techniques::H64, IssueType::Error, Principle::Operable, Guideline::Navigable, |_rule, nodes| {
Validation::new_issue(nodes.iter().all(|e| !e.0.attr("title").unwrap_or_default().is_empty()), "")
}),
])),
("frame", Vec::from([
Rule::new(Techniques::H64, Criteria::Error, Principle::Operable, Guideline::Navigable, |_rule, nodes| {
Rule::new(Techniques::H64, IssueType::Error, Principle::Operable, Guideline::Navigable, |_rule, nodes| {
Validation::new_issue(nodes.iter().all(|e| !e.0.attr("title").unwrap_or_default().is_empty()), "")
}),
])),
("form", Vec::from([
Rule::new(Techniques::H32, Criteria::Error, Principle::Operable, Guideline::Predictable, |_rule, nodes| {
Rule::new(Techniques::H32, IssueType::Error, Principle::Operable, Guideline::Predictable, |_rule, nodes| {
// check the first element for now
let mut valid = false;
let selector = unsafe { Selector::parse("button[type=submit]").unwrap_unchecked() };
Expand All @@ -142,7 +143,7 @@ lazy_static! {

Validation::new_issue(valid, "2")
}),
Rule::new(Techniques::H36, Criteria::Error, Principle::Perceivable, Guideline::TextAlternatives, |_rule, nodes| {
Rule::new(Techniques::H36, IssueType::Error, Principle::Perceivable, Guideline::TextAlternatives, |_rule, nodes| {
let mut valid = false;
let selector = unsafe { Selector::parse("input[type=image][name=submit]").unwrap_unchecked() };

Expand All @@ -159,7 +160,7 @@ lazy_static! {
}),
])),
("a", Vec::from([
Rule::new(Techniques::H30, Criteria::Error, Principle::Perceivable, Guideline::TextAlternatives, |_rule, nodes| {
Rule::new(Techniques::H30, IssueType::Error, Principle::Perceivable, Guideline::TextAlternatives, |_rule, nodes| {
let mut valid = true;
let selector = unsafe { Selector::parse("img").unwrap_unchecked() };
// todo: use tree to see if img exist to skip
Expand All @@ -175,7 +176,7 @@ lazy_static! {

Validation::new_issue(valid, "2")
}),
Rule::new(Techniques::H91, Criteria::Error, Principle::Robust, Guideline::Compatible, |_rule, nodes| {
Rule::new(Techniques::H91, IssueType::Error, Principle::Robust, Guideline::Compatible, |_rule, nodes| {
let mut valid = true;
for ele in nodes {
let ele = ele.0;
Expand All @@ -188,7 +189,7 @@ lazy_static! {
}
Validation::new_issue(valid, "A.NoContent")
}),
Rule::new(Techniques::H91, Criteria::Error, Principle::Robust, Guideline::Compatible, |_rule, nodes| {
Rule::new(Techniques::H91, IssueType::Error, Principle::Robust, Guideline::Compatible, |_rule, nodes| {
let mut valid = true;
for ele in nodes {
let ele = ele.0;
Expand All @@ -198,7 +199,7 @@ lazy_static! {
}),
])),
("img", Vec::from([
Rule::new(Techniques::H37, Criteria::Error, Principle::Perceivable, Guideline::TextAlternatives, |_rule, nodes| {
Rule::new(Techniques::H37, IssueType::Error, Principle::Perceivable, Guideline::TextAlternatives, |_rule, nodes| {
let mut valid = true;

for ele in nodes {
Expand All @@ -210,32 +211,32 @@ lazy_static! {
}),
])),
("h1", Vec::from([
Rule::new(Techniques::H42, Criteria::Error, Principle::Perceivable, Guideline::Adaptable, |_rule, nodes| {
Rule::new(Techniques::H42, IssueType::Error, Principle::Perceivable, Guideline::Adaptable, |_rule, nodes| {
Validation::new_issue(!is_empty(nodes), Techniques::H42.pairs()[0])
}),
])),
("h2", Vec::from([
Rule::new(Techniques::H42, Criteria::Error, Principle::Perceivable, Guideline::Adaptable, |_rule, nodes| {
Rule::new(Techniques::H42, IssueType::Error, Principle::Perceivable, Guideline::Adaptable, |_rule, nodes| {
Validation::new_issue(!is_empty(nodes), Techniques::H42.pairs()[0])
}),
])),
("h3", Vec::from([
Rule::new(Techniques::H42, Criteria::Error, Principle::Perceivable, Guideline::Adaptable, |_rule, nodes| {
Rule::new(Techniques::H42, IssueType::Error, Principle::Perceivable, Guideline::Adaptable, |_rule, nodes| {
Validation::new_issue(!is_empty(nodes), Techniques::H42.pairs()[0])
}),
])),
("h4", Vec::from([
Rule::new(Techniques::H42, Criteria::Error, Principle::Perceivable, Guideline::Adaptable, |_rule, nodes| {
Rule::new(Techniques::H42, IssueType::Error, Principle::Perceivable, Guideline::Adaptable, |_rule, nodes| {
Validation::new_issue(!is_empty(nodes), Techniques::H42.pairs()[0])
}),
])),
("h5", Vec::from([
Rule::new(Techniques::H42, Criteria::Error, Principle::Perceivable, Guideline::Adaptable, |_rule, nodes| {
Rule::new(Techniques::H42, IssueType::Error, Principle::Perceivable, Guideline::Adaptable, |_rule, nodes| {
Validation::new_issue(!is_empty(nodes), Techniques::H42.pairs()[0])
}),
])),
("h6", Vec::from([
Rule::new(Techniques::H42, Criteria::Error, Principle::Perceivable, Guideline::Adaptable, |_rule, nodes| {
Rule::new(Techniques::H42, IssueType::Error, Principle::Perceivable, Guideline::Adaptable, |_rule, nodes| {
Validation::new_issue(!is_empty(nodes), Techniques::H42.pairs()[0])
}),
]))
Expand Down
1 change: 1 addition & 0 deletions accessibility-rs/src/i18n/locales.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ impl Langs {

/// get message config type
pub fn get_message_i18n(rule: &Rule, section: &str, lang: &str) -> String {
// todo: add criteria handling fix
let base = [rule.guideline.as_index(), rule.principle.as_index()].join("_") + "_";
let message = if section.is_empty() {
[rule.rule_id.as_str()].join(".").to_string()
Expand Down
4 changes: 2 additions & 2 deletions accessibility-rs/tests/unit/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,10 @@ fn _iframe_missing_title() {
}

#[test]
/// no blink elements
/// incorrect xml:lang
fn _xml_lang_incorrect_format() {
let audit = accessibility_rs::audit(AuditConfig::basic(
r###"<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-fwdf">
r###"<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en_3">
<head>
<title>Do not use this!</title>
</head>
Expand Down

0 comments on commit d24abaf

Please sign in to comment.