facett-table/tests/a11y.rs
//! **FC-4 / FC-5 proof for `facett-table`.** Render a real [`Table`] with rows,
//! select one through the real selection API, render it headless under
//! `egui_kittest`, and assert the *selected row* rides into the AccessKit tree as
//! a queryable, `Toggled::True` node — and that an *unselected* row reports
//! `Toggled::False`.
//!
//! Why the `"row: …"` label prefix: egui_extras already emits an AccessKit node
//! per *cell* (so the cell text "knut" is itself queryable). If the row node used
//! the bare identity it could collide with a cell's text and make `get_by_label`
//! ambiguous. Prefixing with `"row: "` gives the row node an unambiguous label
//! distinct from every cell, so the selection toggle is addressable on its own.
use egui::accesskit::{Role, Toggled};
use egui_kittest::Harness;
use egui_kittest::kittest::{NodeT, Queryable};
use facett_core::Facet;
use facett_table::Table;
/// A 2-column, 2-row table with stable RowIds (FC-5), row 1 pre-selected.
fn selected_table() -> Table {
let mut t = Table::new("repos", vec!["name".into(), "version".into()])
.with_row_ids(vec!["pkg-knut".into(), "pkg-korp".into()]);
t.push_row(vec!["knut".into(), "0.1".into()]);
t.push_row(vec!["korp".into(), "0.2".into()]);
t.select_row(1); // real selection API selects the second row
t
}
#[test]
fn selected_row_reports_toggled_true_in_accesskit_tree() {
let mut table = selected_table();
// Sanity: the real selection state is what we think it is before rendering.
assert_eq!(table.selected_rows(), vec![1]);
let mut harness = Harness::new_ui(move |ui| {
table.ui(ui);
});
harness.run();
// The SELECTED row (RowId "pkg-korp") is queryable by its prefixed label and
// reports its selection as Toggled::True — the FC-4 gap this fix closes.
let selected = harness.get_by_label("row: pkg-korp").accesskit_node();
// egui maps WidgetType::SelectableLabel → Role::Button (a togglable button).
assert_eq!(selected.role(), Role::Button, "selectable-label row maps to a Button role");
assert_eq!(
selected.toggled(),
Some(Toggled::True),
"the selected row must surface Toggled::True so a driver/screen reader can read it"
);
// The UNSELECTED row is also addressable and reports Toggled::False.
let unselected = harness.get_by_label("row: pkg-knut").accesskit_node();
assert_eq!(unselected.toggled(), Some(Toggled::False), "unselected row must report Toggled::False");
// Feed the matrix: ok iff selection rides into the a11y tree (FC-4/FC-5).
#[cfg(feature = "testmatrix")]
facett_core::testmatrix::emit(
"Table",
"fc4_selected_row_toggle",
selected.toggled() == Some(Toggled::True) && unselected.toggled() == Some(Toggled::False),
"selected=Toggled::True, unselected=Toggled::False",
);
}
#[test]
fn row_node_label_does_not_collide_with_cell_text() {
// Without RowIds the identity is the index; the cell text ("knut") still has
// its own node, and the row node ("row: 0") is a distinct, unambiguous label.
let mut table = Table::new("repos", vec!["name".into()]);
table.push_row(vec!["knut".into()]);
table.select_row(0);
let mut harness = Harness::new_ui(move |ui| {
table.ui(ui);
});
harness.run();
// The cell text is queryable on its own (emitted by egui_extras)…
let _cell = harness.get_by_label("knut");
// …and the ROW node is a separate, selected node keyed on the index identity.
let row = harness.get_by_label("row: 0").accesskit_node();
assert_eq!(row.toggled(), Some(Toggled::True));
// Feed the matrix: ok iff the row node label is distinct from cell text (FC-5).
#[cfg(feature = "testmatrix")]
facett_core::testmatrix::emit(
"Table",
"fc5_row_label_no_collision",
row.toggled() == Some(Toggled::True),
"row: 0 node distinct from cell 'knut', Toggled::True",
);
}