#![forbid(unsafe_code)]
mod extra;
mod finding;
mod outcome;
mod report;
pub use extra::Extra;
pub use finding::{Evidence, Finding, Location};
pub use outcome::{Outcome, Severity, WcagLevel};
pub use report::{NotRun, Report, RuleRun, Summary};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zaehlt_zustaende_und_schweregrade_getrennt() {
let mut r = Report::new();
r.push(Finding::fail("a", "x").with_severity(Severity::Critical));
r.push(Finding::review("b", "x").with_severity(Severity::Low));
r.push(Finding::pass("c", "x").with_severity(Severity::Critical));
r.push(Finding::untested("d", "x").with_severity(Severity::High));
let r = r.finish();
assert_eq!(
(
r.summary.fail,
r.summary.review,
r.summary.pass,
r.summary.untested
),
(1, 1, 1, 1)
);
assert_eq!(
(r.summary.critical, r.summary.high, r.summary.low),
(1, 0, 1)
);
assert_eq!(r.summary.problems(), 2);
}
#[test]
fn pass_wird_standardmaessig_nicht_angezeigt() {
let mut r = Report::new();
r.push(Finding::fail("a", "x"));
r.push(Finding::pass("b", "x"));
r.push(Finding::untested("c", "x"));
assert_eq!(r.visible().count(), 2);
assert_eq!(r.by_outcome(Outcome::Pass).count(), 1);
}
#[test]
fn untested_ist_kein_problem() {
assert!(Outcome::Fail.is_problem());
assert!(Outcome::Review.is_problem());
assert!(!Outcome::Pass.is_problem());
assert!(!Outcome::Untested.is_problem());
}
#[test]
fn json_bleibt_schlank_wenn_nichts_gesetzt_ist() {
let f = Finding::fail("images/alt-missing", "kein alt");
let j: serde_json::Value = serde_json::to_value(&f).unwrap();
let obj = j.as_object().unwrap();
assert_eq!(
obj.len(),
4,
"unerwartete Felder: {:?}",
obj.keys().collect::<Vec<_>>()
);
assert_eq!(obj["outcome"], "fail");
assert_eq!(obj["severity"], "medium");
}
#[test]
fn json_rundlauf_erhaelt_alles() {
let f = Finding::review("aria/role-invalid", "unbekannte Rolle")
.with_severity(Severity::High)
.with_wcag(["4.1.2"])
.with_wcag_level(WcagLevel::A)
.with_tags(["best-practice"])
.with_help("Nur Rollen aus der ARIA-Spezifikation verwenden.")
.with_help_url("https://www.w3.org/TR/wai-aria-1.2/#roles")
.with_suggestion("role=\"buton\" ist vermutlich role=\"button\".")
.with_suggested_code("<div role=\"button\">")
.with_snippet("<div role=\"buton\">")
.at(Location::node("42").with_selector("#menu > div"))
.add_evidence(Evidence::dom_attribute("role", Some("buton".into())));
let json = serde_json::to_string(&f).unwrap();
let back: Finding = serde_json::from_str(&json).unwrap();
assert_eq!(f, back);
}
#[test]
fn wcag_stufen_schliessen_ein() {
assert!(WcagLevel::A.included_in(WcagLevel::AA));
assert!(WcagLevel::AA.included_in(WcagLevel::AA));
assert!(!WcagLevel::AAA.included_in(WcagLevel::AA));
assert!(WcagLevel::AAA.included_in(WcagLevel::AAA));
}
#[test]
fn severity_ordnet_sich_aufsteigend() {
let mut v = vec![
Severity::High,
Severity::Low,
Severity::Critical,
Severity::Medium,
];
v.sort();
assert_eq!(
v,
vec![
Severity::Low,
Severity::Medium,
Severity::High,
Severity::Critical
]
);
}
#[test]
fn axe_impact_bleibt_kompatibel() {
assert_eq!(Severity::Low.as_axe_impact(), "minor");
assert_eq!(Severity::Medium.as_axe_impact(), "moderate");
assert_eq!(Severity::High.as_axe_impact(), "serious");
assert_eq!(Severity::Critical.as_axe_impact(), "critical");
}
#[test]
fn nicht_gelaufene_regeln_werden_gezaehlt() {
let mut r = Report::new();
r.record(RuleRun::ran("images/alt-missing", 3));
r.record(RuleRun::not_run("contrast/text", NotRun::CapabilityMissing));
r.record(RuleRun::not_run("forms/label", NotRun::NotApplicable));
let r = r.finish();
assert_eq!(r.summary.rules_not_run, 2);
assert!(r.rule_runs[0].did_run());
assert!(!r.rule_runs[1].did_run());
}
#[derive(Debug)]
struct Screenshot(Vec<u8>);
#[test]
fn beigabe_kommt_typisiert_zurueck() {
let f =
Finding::fail("images/alt-missing", "kein alt").with_extra(Screenshot(vec![1, 2, 3]));
assert_eq!(f.extra.get::<Screenshot>().map(|s| s.0.len()), Some(3));
assert!(f.extra.get::<String>().is_none());
}
#[test]
fn beigabe_landet_nicht_im_json() {
let f = Finding::fail("a", "x").with_extra(Screenshot(vec![0; 4096]));
let j = serde_json::to_string(&f).unwrap();
assert!(
!j.contains("extra"),
"Beigabe darf nicht serialisiert werden: {j}"
);
assert!(j.len() < 200, "{} Bytes", j.len());
}
#[test]
fn beigabe_zaehlt_nicht_zur_identitaet() {
let ohne = Finding::fail("a", "x");
let mit = Finding::fail("a", "x").with_extra(Screenshot(vec![9]));
assert_eq!(ohne, mit);
}
#[test]
fn beigabe_ueberlebt_das_klonen() {
let f = Finding::fail("a", "x").with_extra(Screenshot(vec![7, 7]));
let k = f.clone();
assert_eq!(
k.extra.get::<Screenshot>().map(|s| s.0.clone()),
Some(vec![7, 7])
);
}
#[test]
fn element_und_regelname_gehen_durch_das_json() {
let f = Finding::fail("buttons/name-missing", "kein Name")
.with_rule_name("Button braucht einen zugänglichen Namen")
.with_element(Some("button".into()), None);
let j: serde_json::Value = serde_json::to_value(&f).unwrap();
assert_eq!(j["role"], "button");
assert_eq!(j["rule_name"], "Button braucht einen zugänglichen Namen");
assert!(j.get("name").is_none());
let zurueck: Finding = serde_json::from_value(j).unwrap();
assert_eq!(zurueck, f);
}
#[test]
fn der_schlanke_fall_bleibt_schlank() {
let f = Finding::fail("images/alt-missing", "kein alt");
let j: serde_json::Value = serde_json::to_value(&f).unwrap();
assert_eq!(
j.as_object().unwrap().len(),
4,
"unerwartete Felder: {:?}",
j.as_object().unwrap().keys().collect::<Vec<_>>()
);
}
#[test]
fn derselbe_regelvermerk_kann_je_durchgang_vorkommen() {
let mut r = Report::new();
r.record(RuleRun::ran("target-size/minimum", 0).in_viewport("desktop"));
r.record(RuleRun::ran("target-size/minimum", 3).in_viewport("mobile"));
let r = r.finish();
assert_eq!(r.rule_runs.len(), 2);
let mobil = r
.rule_runs
.iter()
.find(|x| x.viewport.as_deref() == Some("mobile"))
.unwrap();
assert_eq!(mobil.findings, 3);
}
#[test]
fn ein_nicht_gelaufener_vermerk_nennt_das_ungeprueffte_kriterium() {
let mut r = Report::new();
r.record(
RuleRun::not_run("contrast/text", NotRun::CapabilityMissing)
.with_wcag(["1.4.3"])
.with_reason("statische Analyse liefert keine Rendering-Werte"),
);
let r = r.finish();
assert_eq!(r.summary.rules_not_run, 1);
assert_eq!(r.rule_runs[0].wcag, vec!["1.4.3"]);
}
}