Keep manual review visible in accessibility summaries (#1224)
Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
4541974ef9
commit
51359beaea
|
|
@ -48,6 +48,10 @@ pub fn report_html(report: &Report, file_name: &str, date: &str, lang: &str, wor
|
|||
// `REPORT_WORDS` can be read off this file.
|
||||
h.push_str(&if report.count(Status::Failed) > 0 {
|
||||
esc(&words("The checker found problems which may prevent the document from being fully accessible."))
|
||||
} else if report.count(Status::Manual) > 0 {
|
||||
format!("{}: {}", esc(&words(Status::Manual.label())), report.count(Status::Manual))
|
||||
} else if report.count(Status::Skipped) > 0 {
|
||||
format!("{}: {}", esc(&words(Status::Skipped.label())), report.count(Status::Skipped))
|
||||
} else {
|
||||
esc(&words("The checker found no problems in this document."))
|
||||
});
|
||||
|
|
|
|||
|
|
@ -421,6 +421,13 @@ fn rules_have_ids_and_the_report_lists_them() {
|
|||
let other = report_html(&report, "a<b>.pdf", "2026-10-02", "es", &|w| format!("«{w}»"));
|
||||
assert!(other.contains("<html lang=\"es\">") && other.contains("<h3>«Headings»</h3>") && other.contains("a<b>.pdf"));
|
||||
assert!(!other.contains(">Headings<"), "an English heading is left in the report: {other}");
|
||||
let manual_only = Report { results: vec![RuleResult { rule: Rule::LogicalReadingOrder, status: Status::Manual, findings: Vec::new() }] };
|
||||
let manual_html = report_html(&manual_only, "review.pdf", "2026-10-02", "en", &in_english);
|
||||
assert!(manual_html.contains("<p>Needs manual check: 1</p>"));
|
||||
assert!(!manual_html.contains("no problems"));
|
||||
let all_passed = Report { results: vec![RuleResult { rule: Rule::LogicalReadingOrder, status: Status::Passed, findings: Vec::new() }] };
|
||||
let passed_html = report_html(&all_passed, "review.pdf", "2026-10-02", "en", &in_english);
|
||||
assert!(passed_html.contains("<p>The checker found no problems in this document.</p>"), "{passed_html}");
|
||||
}
|
||||
|
||||
fn figures_doc() -> Document {
|
||||
|
|
|
|||
|
|
@ -1736,6 +1736,10 @@ fn accessibility_check_report_and_fixes_through_tools() {
|
|||
assert_eq!(r["failed"].as_u64(), Some(3), "tagging, tagged content and (undone) title");
|
||||
let html = std::fs::read_to_string(dir.join("report.html")).unwrap();
|
||||
assert!(html.contains("Accessibility Report") && html.contains("a.pdf"));
|
||||
ok(&mut a, "accessibility_report", json!({ "doc": doc, "path": "manual-report.html", "rules": ["scripts"] }));
|
||||
let manual_html = std::fs::read_to_string(dir.join("manual-report.html")).unwrap();
|
||||
assert!(manual_html.contains("<p>Needs manual check: 1</p>"));
|
||||
assert!(!manual_html.contains("no problems"));
|
||||
// Figures: list, describe, mark decorative.
|
||||
std::fs::write(
|
||||
dir.join("figures.pdf"),
|
||||
|
|
|
|||
|
|
@ -176,14 +176,20 @@ pub(crate) fn panel(ui: &mut egui::Ui, t: &Tokens, state: &mut A11yState, doc: D
|
|||
return action;
|
||||
};
|
||||
let failed = report.count(Status::Failed);
|
||||
let manual = report.count(Status::Manual);
|
||||
let skipped = report.count(Status::Skipped);
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(
|
||||
egui::RichText::new(if failed == 0 {
|
||||
egui::RichText::new(if failed == 0 && manual == 0 && skipped == 0 {
|
||||
tl!("No issues found").to_string()
|
||||
} else if failed == 1 {
|
||||
tl!("1 issue").to_string()
|
||||
} else {
|
||||
} else if failed > 1 {
|
||||
crate::i18n::fmt(tl!("{n} issues"), &[("n", &failed.to_string())])
|
||||
} else if manual > 0 {
|
||||
format!("{}: {manual}", tl!("Needs manual check"))
|
||||
} else {
|
||||
format!("{}: {skipped}", tl!("Skipped"))
|
||||
})
|
||||
.color(t.text_muted),
|
||||
);
|
||||
|
|
@ -196,6 +202,9 @@ pub(crate) fn panel(ui: &mut egui::Ui, t: &Tokens, state: &mut A11yState, doc: D
|
|||
}
|
||||
});
|
||||
});
|
||||
if failed > 0 && manual > 0 {
|
||||
ui.label(egui::RichText::new(format!("{}: {manual}", tl!("Needs manual check"))).small().color(t.text_muted));
|
||||
}
|
||||
if let Some((_, previous)) = state.previous.as_ref().filter(|(d, _)| *d == doc) {
|
||||
egui::CollapsingHeader::new(tl!("Compare")).default_open(true).show(ui, |ui| {
|
||||
for status in [Status::Passed, Status::Failed, Status::Manual, Status::Skipped] {
|
||||
|
|
@ -360,20 +369,26 @@ impl PdfCraftApp {
|
|||
r.findings.clear();
|
||||
}
|
||||
let failed = report.count(Status::Failed);
|
||||
let manual = report.count(Status::Manual);
|
||||
let skipped = report.count(Status::Skipped);
|
||||
self.a11y.previous =
|
||||
if self.a11y.options.as_ref() == Some(&options) { self.a11y.report.take().filter(|(prior_id, _)| *prior_id == id) } else { None };
|
||||
self.a11y.options = Some(options);
|
||||
self.a11y.report = Some((id, report));
|
||||
self.right = Some(RightPanel::Accessibility);
|
||||
self.notify(if failed == 0 {
|
||||
self.notify(if failed == 0 && manual == 0 && skipped == 0 {
|
||||
tl!("The checker found no problems in this document.").to_string()
|
||||
} else if failed == 1 {
|
||||
tl!("The checker found 1 problem which may prevent the document from being fully accessible.").to_string()
|
||||
} else {
|
||||
} else if failed > 1 {
|
||||
crate::i18n::fmt(
|
||||
tl!("The checker found {n} problems which may prevent the document from being fully accessible."),
|
||||
&[("n", &failed.to_string())],
|
||||
)
|
||||
} else if manual > 0 {
|
||||
format!("{}: {manual}", tl!("Needs manual check"))
|
||||
} else {
|
||||
format!("{}: {skipped}", tl!("Skipped"))
|
||||
});
|
||||
if self.a11y_options.create_report {
|
||||
self.show_accessibility_report();
|
||||
|
|
|
|||
|
|
@ -92,6 +92,31 @@ fn checking_fixing_skipping_and_reporting() {
|
|||
assert!(html.contains("Accessibility Report") && html.contains("Skipped"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checker_summary_does_not_hide_manual_review() {
|
||||
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(move |_cc| {
|
||||
let mut app = PdfCraftApp::new();
|
||||
app.set_option("language", "en").unwrap();
|
||||
app.open_bytes("notes.pdf", None, FIXTURE.to_vec()).unwrap();
|
||||
app
|
||||
});
|
||||
h.run_steps(3);
|
||||
h.state_mut().run_accessibility_check();
|
||||
let report = &mut h.state_mut().a11y.report.as_mut().unwrap().1;
|
||||
for result in &mut report.results {
|
||||
result.status = Status::Passed;
|
||||
result.findings.clear();
|
||||
}
|
||||
report.results[0].status = Status::Manual;
|
||||
h.run_steps(2);
|
||||
h.get_by_label("Needs manual check: 1");
|
||||
assert!(h.query_by_label("No issues found").is_none());
|
||||
// Every rule passing keeps the plain success wording.
|
||||
h.state_mut().a11y.report.as_mut().unwrap().1.results[0].status = Status::Passed;
|
||||
h.run_steps(2);
|
||||
h.get_by_label("No issues found");
|
||||
}
|
||||
|
||||
/// Two tagged figures: an image without alternate text and a bar (role-mapped Photo) with.
|
||||
const FIGURES: &[u8] = b"%PDF-1.7
|
||||
1 0 obj << /Type /Catalog /Pages 2 0 R /MarkInfo << /Marked true >> /StructTreeRoot 5 0 R >> endobj
|
||||
|
|
|
|||
|
|
@ -8390,8 +8390,8 @@ milestone = "M12"
|
|||
status = "shipped"
|
||||
commands = ["a11y.check"]
|
||||
tools = ["accessibility_check"]
|
||||
evidence = ["crates/a11y/src/tests.rs::an_untagged_document_fails_the_document_rules", "crates/a11y/src/tests.rs::a_well_tagged_document_passes", "crates/a11y/src/tests.rs::structure_findings_identify_indirect_elements_without_guessing_inline_objects", "crates/a11y/src/tests.rs::figures_are_listed_with_their_alt_text_and_place", "crates/a11y/src/tests.rs::annotation_findings_only_locate_valid_rectangles", "crates/automation/tests/automation.rs::accessibility_check_locates_field_description_on_page", "crates/ui-egui/tests/accessibility.rs::checking_fixing_skipping_and_reporting", "crates/ui-egui/tests/accessibility.rs::checker_shows_a_figure_finding_on_its_page", "crates/automation/tests/automation.rs::accessibility_check_report_and_fixes_through_tools"]
|
||||
notes = "full check: options dialog (report, page range, 32 rules by category, 31 on by default), results panel by category with Passed / Failed / Needs manual check / Skipped and findings that go to their page. Locatable figure, annotation and form-widget findings flash their bounds on the page; accessibility_check exposes those bounds in PDF user space. Other findings navigate to their page. Structure findings expose the indirect tag object number and generation through accessibility_check, when available, for precise follow-up in the Tags Panel."
|
||||
evidence = ["crates/a11y/src/tests.rs::an_untagged_document_fails_the_document_rules", "crates/a11y/src/tests.rs::a_well_tagged_document_passes", "crates/a11y/src/tests.rs::structure_findings_identify_indirect_elements_without_guessing_inline_objects", "crates/a11y/src/tests.rs::figures_are_listed_with_their_alt_text_and_place", "crates/a11y/src/tests.rs::annotation_findings_only_locate_valid_rectangles", "crates/automation/tests/automation.rs::accessibility_check_locates_field_description_on_page", "crates/ui-egui/tests/accessibility.rs::checking_fixing_skipping_and_reporting", "crates/ui-egui/tests/accessibility.rs::checker_shows_a_figure_finding_on_its_page", "crates/ui-egui/tests/accessibility.rs::checker_summary_does_not_hide_manual_review", "crates/automation/tests/automation.rs::accessibility_check_report_and_fixes_through_tools"]
|
||||
notes = "full check: options dialog (report, page range, 32 rules by category, 31 on by default), results panel by category with Passed / Failed / Needs manual check / Skipped and findings that go to their page. A zero-failure check reports manual or skipped rules rather than claiming no issues. Locatable figure, annotation and form-widget findings flash their bounds on the page; accessibility_check exposes those bounds in PDF user space. Other findings navigate to their page. Structure findings expose the indirect tag object number and generation through accessibility_check, when available, for precise follow-up in the Tags Panel."
|
||||
|
||||
[[feature]]
|
||||
id = "a11y.checker-report"
|
||||
|
|
@ -8403,7 +8403,7 @@ status = "shipped"
|
|||
commands = ["a11y.report"]
|
||||
tools = ["accessibility_report", "accessibility_review_record", "accessibility_review_html"]
|
||||
evidence = ["crates/a11y/src/tests.rs::rules_have_ids_and_the_report_lists_them", "crates/ui-egui/tests/accessibility.rs::checking_fixing_skipping_and_reporting", "crates/automation/tests/automation.rs::accessibility_check_report_and_fixes_through_tools", "crates/automation/tests/automation.rs::accessibility_review_record_compares_two_versions_without_changing_them", "crates/automation/src/a11y.rs::document_names_are_escaped_in_review_html"]
|
||||
notes = "HTML checker report: summary counts and a table per category with descriptions and findings. Headless comparison of two open versions exports the same before-and-after evidence as JSON or accessible, self-contained HTML with hashes, rule changes, and remaining review items."
|
||||
notes = "HTML checker report: summary counts and a table per category with descriptions and findings. The headline shows manual or skipped rules when there are no automated failures. Headless comparison of two open versions exports the same before-and-after evidence as JSON or accessible, self-contained HTML with hashes, rule changes, and remaining review items."
|
||||
|
||||
[[feature]]
|
||||
id = "a11y.checker-fix"
|
||||
|
|
|
|||
Loading…
Reference in New Issue