use chromiumoxide::Page;
use futures::future::BoxFuture;
use crate::cli::WcagLevel;
use crate::wcag::Violation;
use super::{
check_abbreviations_with_page, check_aria_hidden_focus, check_aria_prohibited_attr_with_page,
check_background_audio_with_page, check_content_on_hover_with_page,
check_focus_visible_css_with_page, check_frame_title_with_page,
check_identify_purpose_with_page, check_label_in_name_with_page, check_location_with_page,
check_motion_actuation_with_page, check_no_interruptions_with_page, check_no_timing_with_page,
check_orientation_with_page, check_parsing_with_page, check_pointer_cancellation_with_page,
check_pointer_gestures_with_page, check_re_authenticate_with_page,
check_reduced_motion_with_page, check_target_size_enhanced_with_page, check_timeouts_with_page,
check_timing_with_page, check_use_of_color_with_page, check_visual_presentation_with_page,
};
use crate::wcag::engine::check_click_handlers_with_page;
pub struct PageRuleEntry {
pub rule_id: &'static str,
pub name: &'static str,
pub min_level: WcagLevel,
pub check_fn: for<'a> fn(&'a Page) -> BoxFuture<'a, Vec<Violation>>,
}
pub const PAGE_RULES: &[PageRuleEntry] = &[
PageRuleEntry {
rule_id: "4.1.2/aria-hidden-focus",
name: "aria-hidden-focus",
min_level: WcagLevel::A,
check_fn: |p| Box::pin(check_aria_hidden_focus(p)),
},
PageRuleEntry {
rule_id: "4.1.2/aria-prohibited-attr",
name: "aria-prohibited-attr",
min_level: WcagLevel::A,
check_fn: |p| Box::pin(check_aria_prohibited_attr_with_page(p)),
},
PageRuleEntry {
rule_id: "4.1.2/frame-title",
name: "frame-title",
min_level: WcagLevel::A,
check_fn: |p| Box::pin(check_frame_title_with_page(p)),
},
PageRuleEntry {
rule_id: "2.1.1/click-handler",
name: "inline click-handler",
min_level: WcagLevel::A,
check_fn: |p| Box::pin(check_click_handlers_with_page(p)),
},
PageRuleEntry {
rule_id: "2.2.1/meta-refresh",
name: "meta-refresh",
min_level: WcagLevel::A,
check_fn: |p| Box::pin(check_timing_with_page(p)),
},
PageRuleEntry {
rule_id: "1.4.1/use-of-color",
name: "use-of-color",
min_level: WcagLevel::A,
check_fn: |p| Box::pin(check_use_of_color_with_page(p)),
},
PageRuleEntry {
rule_id: "1.3.4/orientation",
name: "orientation",
min_level: WcagLevel::AA,
check_fn: |p| Box::pin(check_orientation_with_page(p)),
},
PageRuleEntry {
rule_id: "2.4.7/focus-visible-css",
name: "CSS focus-suppression",
min_level: WcagLevel::AA,
check_fn: |p| Box::pin(check_focus_visible_css_with_page(p)),
},
PageRuleEntry {
rule_id: "2.3.3/reduced-motion",
name: "reduced-motion",
min_level: WcagLevel::AA,
check_fn: |p| Box::pin(check_reduced_motion_with_page(p)),
},
PageRuleEntry {
rule_id: "1.4.13/content-on-hover",
name: "content-on-hover",
min_level: WcagLevel::AA,
check_fn: |p| Box::pin(check_content_on_hover_with_page(p)),
},
PageRuleEntry {
rule_id: "1.3.6/identify-purpose",
name: "identify-purpose",
min_level: WcagLevel::AAA,
check_fn: |p| Box::pin(check_identify_purpose_with_page(p)),
},
PageRuleEntry {
rule_id: "1.4.7/background-audio",
name: "background-audio",
min_level: WcagLevel::AAA,
check_fn: |p| Box::pin(check_background_audio_with_page(p)),
},
PageRuleEntry {
rule_id: "1.4.8/visual-presentation",
name: "visual-presentation",
min_level: WcagLevel::AAA,
check_fn: |p| Box::pin(check_visual_presentation_with_page(p)),
},
PageRuleEntry {
rule_id: "2.2.3/no-timing",
name: "no-timing",
min_level: WcagLevel::AAA,
check_fn: |p| Box::pin(check_no_timing_with_page(p)),
},
PageRuleEntry {
rule_id: "2.2.4/no-interruptions",
name: "no-interruptions",
min_level: WcagLevel::AAA,
check_fn: |p| Box::pin(check_no_interruptions_with_page(p)),
},
PageRuleEntry {
rule_id: "2.2.5/re-authenticate",
name: "re-authenticate",
min_level: WcagLevel::AAA,
check_fn: |p| Box::pin(check_re_authenticate_with_page(p)),
},
PageRuleEntry {
rule_id: "2.2.6/timeouts",
name: "timeouts",
min_level: WcagLevel::AAA,
check_fn: |p| Box::pin(check_timeouts_with_page(p)),
},
PageRuleEntry {
rule_id: "2.4.8/location",
name: "location",
min_level: WcagLevel::AAA,
check_fn: |p| Box::pin(check_location_with_page(p)),
},
PageRuleEntry {
rule_id: "2.5.1/pointer-gestures",
name: "pointer-gestures",
min_level: WcagLevel::AAA,
check_fn: |p| Box::pin(check_pointer_gestures_with_page(p)),
},
PageRuleEntry {
rule_id: "2.5.2/pointer-cancellation",
name: "pointer-cancellation",
min_level: WcagLevel::AAA,
check_fn: |p| Box::pin(check_pointer_cancellation_with_page(p)),
},
PageRuleEntry {
rule_id: "2.5.3/label-in-name",
name: "label-in-name",
min_level: WcagLevel::AAA,
check_fn: |p| Box::pin(check_label_in_name_with_page(p)),
},
PageRuleEntry {
rule_id: "2.5.4/motion-actuation",
name: "motion-actuation",
min_level: WcagLevel::AAA,
check_fn: |p| Box::pin(check_motion_actuation_with_page(p)),
},
PageRuleEntry {
rule_id: "2.5.5/target-size-enhanced",
name: "target-size-enhanced",
min_level: WcagLevel::AAA,
check_fn: |p| Box::pin(check_target_size_enhanced_with_page(p)),
},
PageRuleEntry {
rule_id: "3.1.4/abbreviations",
name: "abbreviations",
min_level: WcagLevel::AAA,
check_fn: |p| Box::pin(check_abbreviations_with_page(p)),
},
PageRuleEntry {
rule_id: "4.1.1/parsing",
name: "parsing",
min_level: WcagLevel::AAA,
check_fn: |p| Box::pin(check_parsing_with_page(p)),
},
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn page_rules_table_is_non_empty() {
assert!(!PAGE_RULES.is_empty());
}
#[test]
fn page_rules_have_unique_rule_ids() {
let mut ids: Vec<&'static str> = PAGE_RULES.iter().map(|r| r.rule_id).collect();
ids.sort_unstable();
let before = ids.len();
ids.dedup();
assert_eq!(before, ids.len(), "duplicate rule_id in PAGE_RULES");
}
#[test]
fn level_a_filter_includes_only_level_a_rules() {
let count = PAGE_RULES
.iter()
.filter(|r| WcagLevel::A >= r.min_level)
.count();
assert_eq!(count, 6);
}
#[test]
fn level_aa_filter_includes_aa_plus_a_rules() {
let count = PAGE_RULES
.iter()
.filter(|r| WcagLevel::AA >= r.min_level)
.count();
assert_eq!(count, 10);
}
#[test]
fn level_aaa_filter_includes_all_rules() {
let count = PAGE_RULES
.iter()
.filter(|r| WcagLevel::AAA >= r.min_level)
.count();
assert_eq!(count, PAGE_RULES.len());
}
}