#[cfg(test)]
mod tests {
use crate::integ as h;
use xa11y::*;
#[test]
#[ignore]
fn check_permissions_granted() {
let _app = h::app_root();
}
#[test]
#[ignore]
fn apps_returns_nonempty() {
let apps = App::list().unwrap();
assert!(!apps.is_empty(), "should find at least one application");
let has_test_app = apps.iter().any(|a| a.name.contains("xa11y"));
assert!(
has_test_app,
"apps should include the test app. Apps: {:?}",
apps.iter().map(|a| &a.name).collect::<Vec<_>>()
);
}
#[test]
#[ignore]
fn tree_has_root_node() {
let app = h::app_root();
assert!(
app.data.role == Role::Application || app.data.role == Role::Window,
"Root role: {:?}",
app.data.role
);
}
#[test]
#[ignore]
fn tree_has_window() {
let app = h::app_root();
if app.data.role == Role::Window {
return; }
let windows = app.locator("window").elements().unwrap();
assert!(!windows.is_empty(), "No windows found. App: {}", app);
}
#[test]
#[ignore]
fn tree_has_buttons() {
let app = h::app_root();
let buttons = app.locator("button").elements().unwrap();
assert!(
buttons.len() >= 2,
"Expected >=2 buttons, found {}. App: {}",
buttons.len(),
app
);
}
#[test]
#[ignore]
fn tree_has_submit_button() {
let app = h::app_root();
let submit = h::named(&app, "Submit");
assert_eq!(submit.role, Role::Button);
}
#[test]
#[ignore]
fn tree_has_cancel_button_disabled() {
let app = h::app_root();
let cancel = h::named(&app, "Cancel");
assert_eq!(cancel.role, Role::Button);
let _ = cancel.states.enabled;
}
#[test]
#[ignore]
fn tree_has_checkbox_unchecked() {
let app = h::app_root();
let cb = h::named(&app, "I agree to terms");
assert_eq!(cb.role, Role::CheckBox);
assert!(
cb.states.checked.is_some(),
"Checkbox should have checked state"
);
}
#[test]
#[ignore]
fn tree_has_text_entry_with_value() {
let app = h::app_root();
let text_elements = app
.locator(r#"[role="text_field"]"#)
.elements()
.unwrap_or_default();
let textarea_elements = app
.locator(r#"[role="text_area"]"#)
.elements()
.unwrap_or_default();
let has_text = text_elements
.iter()
.chain(textarea_elements.iter())
.any(|n| n.value.is_some() || n.name.as_deref() == Some("Name"));
assert!(has_text, "Text entry not found. App: {}", app);
}
#[test]
#[ignore]
fn tree_has_welcome_label() {
let app = h::app_root();
let welcome = app.locator(r#"[name*="Welcome"]"#).elements().unwrap();
if welcome.is_empty() {
let labels = app.locator("static_text").elements().unwrap();
assert!(
!labels.is_empty(),
"No StaticText/label nodes found. App: {}",
app
);
} else {
assert!(
welcome[0].role == Role::StaticText || welcome[0].role == Role::Group,
"Welcome node role: {:?}",
welcome[0].role
);
}
}
#[test]
#[ignore]
fn tree_has_slider_at_50() {
let app = h::app_root();
let sliders = app.locator("slider").elements().unwrap();
assert!(!sliders.is_empty(), "No sliders found. App: {}", app);
assert!(sliders[0].value.is_some(), "Slider should have a value");
let val: f64 = sliders[0].value.as_deref().unwrap().parse().unwrap_or(0.0);
assert!(
(0.0..=100.0).contains(&val),
"Slider value should be in [0,100], got {}",
val
);
}
#[test]
#[ignore]
fn tree_has_progress_bar() {
let app = h::app_root();
let progress = app.locator("progress_bar").elements().unwrap();
assert!(!progress.is_empty(), "No progress bars found. App: {}", app);
}
#[test]
#[ignore]
fn tree_has_radio_buttons() {
let app = h::app_root();
let radios = app.locator("radio_button").elements().unwrap();
assert!(
radios.len() >= 2,
"Expected >=2 radio buttons, found {}. App: {}",
radios.len(),
app
);
}
#[test]
#[ignore]
fn tree_has_combo_box() {
let app = h::app_root();
let combos = app.locator("combo_box").elements().unwrap();
assert!(!combos.is_empty(), "ComboBox not found. App: {}", app);
}
#[test]
#[ignore]
fn tree_has_list_with_items() {
let app = h::app_root();
let lists = app.locator("list").elements().unwrap();
let items = app.locator("list_item").elements().unwrap();
assert!(
!lists.is_empty() || !items.is_empty(),
"Neither List nor ListItem found. App: {}",
app
);
}
#[test]
#[ignore]
fn tree_has_table_with_cells() {
let app = h::app_root();
let tables = app.locator("table").elements().unwrap();
let cells = app.locator("table_cell").elements().unwrap();
assert!(
!tables.is_empty() || !cells.is_empty(),
"Neither Table nor TableCell found. App: {}",
app
);
}
#[test]
#[ignore]
fn role_menu_bar() {
let app = h::app_root();
let nodes = app.locator("menu_bar").elements().unwrap();
assert!(!nodes.is_empty(), "MenuBar not found. App: {}", app);
}
#[test]
#[ignore]
fn role_menu_item() {
let app = h::app_root();
let nodes = app.locator("menu_item").elements().unwrap();
assert!(!nodes.is_empty(), "MenuItem not found. App: {}", app);
let has_file = nodes.iter().any(|n| n.name.as_deref() == Some("File"));
assert!(has_file, "File menu item not found");
}
#[test]
#[ignore]
fn role_toolbar() {
let app = h::app_root();
let nodes = app.locator("toolbar").elements().unwrap();
assert!(!nodes.is_empty(), "Toolbar not found. App: {}", app);
}
#[test]
#[ignore]
fn role_tab_and_tab_group() {
let app = h::app_root();
let tab_groups = app.locator("tab_group").elements().unwrap();
let tabs = app.locator("tab").elements().unwrap();
assert!(
!tab_groups.is_empty() || !tabs.is_empty(),
"Neither TabGroup nor Tab found. App: {}",
app
);
}
#[test]
#[ignore]
fn role_separator() {
let app = h::app_root();
let seps = app.locator("separator").elements().unwrap();
assert!(!seps.is_empty(), "Separator not found. App: {}", app);
}
#[test]
#[ignore]
fn role_image() {
let app = h::app_root();
let images = app.locator("image").elements().unwrap();
assert!(!images.is_empty(), "Image not found. App: {}", app);
}
#[test]
#[ignore]
fn role_link() {
let app = h::app_root();
let links = app.locator("link").elements().unwrap();
assert!(!links.is_empty(), "Link not found. App: {}", app);
}
#[test]
#[ignore]
fn role_tree_item() {
let app = h::app_root();
let items = app.locator("tree_item").elements().unwrap();
assert!(!items.is_empty(), "TreeItem not found. App: {}", app);
}
#[test]
#[ignore]
fn role_dialog() {
let app = h::app_root();
let dialogs = app.locator("dialog").elements().unwrap();
assert!(!dialogs.is_empty(), "Dialog not found. App: {}", app);
}
#[test]
#[ignore]
fn role_alert() {
let app = h::app_root();
let alerts = app.locator("alert").elements().unwrap();
assert!(!alerts.is_empty(), "Alert not found. App: {}", app);
}
#[test]
#[ignore]
fn role_heading() {
let app = h::app_root();
let headings = app.locator("heading").elements().unwrap();
assert!(!headings.is_empty(), "Heading not found. App: {}", app);
}
#[test]
#[ignore]
fn role_scroll_bar() {
let app = h::app_root();
let scrollbars = app.locator("scroll_bar").elements().unwrap();
assert!(!scrollbars.is_empty(), "ScrollBar not found. App: {}", app);
}
#[test]
#[ignore]
fn role_split_group() {
let app = h::app_root();
let node = app.locator(r#"[name*="SplitGroup"]"#).elements().unwrap();
assert!(!node.is_empty(), "SplitGroup node not found. App: {}", app);
}
#[test]
#[ignore]
fn role_static_text() {
let app = h::app_root();
let labels = app.locator("static_text").elements().unwrap();
assert!(!labels.is_empty(), "StaticText not found. App: {}", app);
}
#[test]
#[ignore]
fn tree_children_of_root() {
let app = h::app_root();
let children = app.children().unwrap();
assert!(!children.is_empty(), "Root should have children");
if !children.is_empty() {
let grandchildren = children[0].children().unwrap();
for gc in &grandchildren {
let parent = gc.parent().unwrap();
assert!(parent.is_some(), "Grandchild should have parent");
}
}
}
#[test]
#[ignore]
fn tree_children_of_leaf() {
let app = h::app_root();
let buttons = app.locator("button").elements().unwrap();
for btn in &buttons {
let children = btn.children().unwrap();
if children.is_empty() {
assert!(btn.children().unwrap().is_empty());
return;
}
}
}
#[test]
#[ignore]
fn tree_is_not_empty() {
let app = h::app_root();
let children = app.children().unwrap();
assert!(!children.is_empty(), "Root should have at least one child");
}
#[test]
#[ignore]
fn tree_display_readable() {
let app = h::app_root();
let display = app.to_string();
assert!(!display.is_empty());
assert!(
display.contains(&app.name),
"Display should include app name: {}",
display
);
}
#[test]
#[ignore]
fn tree_locator_finds_elements() {
let app = h::app_root();
let buttons = app.locator("button").elements().unwrap();
assert!(buttons.len() >= 2, "Expected >=2 buttons via locator");
let count = app.locator("button").count().unwrap();
assert_eq!(count, buttons.len());
}
#[test]
#[ignore]
fn element_description_on_image() {
let app = h::app_root();
let images = app.locator("image").elements().unwrap();
if !images.is_empty() {
let img = images.iter().find(|n| {
n.name.as_deref() == Some("Info Icon")
|| n.description.as_deref() == Some("An informational icon")
});
if let Some(img) = img {
assert!(img.description.is_some(), "Image should have description");
assert_eq!(img.description.as_deref(), Some("An informational icon"));
}
}
}
#[test]
#[ignore]
fn element_bounds_present() {
let app = h::app_root();
let submit = h::named(&app, "Submit");
assert!(submit.bounds.is_some(), "Submit should have bounds");
let b = submit.bounds.unwrap();
assert!(b.width > 0, "width > 0");
assert!(b.height > 0, "height > 0");
}
#[test]
#[ignore]
fn element_bounds_none_for_non_component_elements() {
let app = h::app_root();
#[cfg(not(target_os = "windows"))]
assert!(
app.data.bounds.is_none(),
"Application root should not have bounds (no Component interface)"
);
let submit = h::named(&app, "Submit");
assert!(submit.bounds.is_some(), "Submit button should have bounds");
}
#[test]
#[ignore]
fn element_actions_list_on_button() {
let app = h::app_root();
let submit = h::named(&app, "Submit");
assert!(!submit.actions.is_empty());
assert!(
submit.actions.iter().any(|a| a == "press"),
"Submit should support press, got: {:?}",
submit.actions
);
}
#[test]
#[ignore]
fn element_children_ids_valid() {
let app = h::app_root();
let children = app.children().unwrap();
assert!(!children.is_empty());
for child in &children {
let _ = child.role;
}
}
#[test]
#[ignore]
fn element_parent_field() {
let app = h::app_root();
let children = app.children().unwrap();
if !children.is_empty() {
let grandchildren = children[0].children().unwrap();
if !grandchildren.is_empty() {
let parent = grandchildren[0].parent().unwrap();
assert!(parent.is_some(), "Grandchild should have parent");
}
}
}
#[test]
#[ignore]
fn element_handle_nonzero() {
let app = h::app_root();
assert!(app.data.handle != 0, "Root handle should be nonzero");
}
#[test]
#[ignore]
fn state_enabled_default() {
let app = h::app_root();
let submit = h::named(&app, "Submit");
assert!(submit.states.enabled, "Submit should be enabled");
}
#[test]
#[ignore]
fn state_disabled_on_cancel() {
let app = h::app_root();
let cancel = h::named(&app, "Cancel");
#[cfg(not(target_os = "linux"))]
assert!(!cancel.states.enabled, "Cancel should be disabled");
#[cfg(target_os = "linux")]
{
assert_eq!(cancel.role, Role::Button);
let _ = cancel.states.enabled; }
}
#[test]
#[ignore]
fn state_visible_on_shown_widget() {
let app = h::app_root();
let submit = h::named(&app, "Submit");
assert!(submit.states.visible, "Submit should be visible");
}
#[test]
#[ignore]
fn state_focused_after_focus_action() {
let app = h::app_root();
let submit = h::named(&app, "Submit");
let result = h::try_act(&submit, "focus");
if result.is_ok() {
std::thread::sleep(std::time::Duration::from_millis(100));
let app2 = h::app_root();
let submit2 = h::named(&app2, "Submit");
if !submit2.states.focused {
println!("Focus action succeeded but focused state not reflected (AT-SPI adapter limitation)");
}
} else {
println!("Focus action not supported: {:?}", result.err());
}
}
#[test]
#[ignore]
fn state_checked_off_on_checkbox() {
let app = h::app_root();
let cb = h::named(&app, "I agree to terms");
assert_eq!(cb.states.checked, Some(Toggled::Off));
}
#[test]
#[ignore]
fn state_checked_on_radio() {
let app = h::app_root();
let radios = app.locator("radio_button").elements().unwrap();
let opt_a = radios
.iter()
.find(|n| n.name.as_deref() == Some("Option A"));
assert!(opt_a.is_some());
assert_eq!(opt_a.unwrap().states.checked, Some(Toggled::On));
}
#[test]
#[ignore]
fn state_expanded_collapsed_on_expander() {
let app = h::app_root();
let expander_by_name = app.locator(r#"[name*="Expander"]"#).elements().unwrap();
if expander_by_name.is_empty() {
println!(
"No expandable elements found by name. \
Expand/collapse actions tested separately."
);
}
}
#[test]
#[ignore]
fn state_editable_on_text_field() {
let app = h::app_root();
let text_fields = app.locator(r#"[name="Name"]"#).elements().unwrap();
if text_fields.is_empty() {
let fields = app.locator("text_field").elements().unwrap();
let areas = app.locator("text_area").elements().unwrap();
let all_text: Vec<&Element> = fields.iter().chain(areas.iter()).collect();
assert!(!all_text.is_empty(), "Text entry not found. App: {}", app);
assert!(all_text[0].states.editable, "Text entry should be editable");
} else {
assert!(
text_fields[0].states.editable,
"Text entry should be editable"
);
}
}
#[test]
#[ignore]
fn state_selected_on_list_item() {
let app = h::app_root();
let apple = h::named(&app, "Apple");
let app2 = h::act(&apple, "press");
let apple2 = h::named(&app2, "Apple");
println!(
"Apple selected state after Click: {:?}",
apple2.states.selected
);
}
#[test]
#[ignore]
fn sel_by_role() {
let app = h::app_root();
let buttons = app.locator("button").elements().unwrap();
assert!(buttons.len() >= 2);
for b in &buttons {
assert_eq!(b.role, Role::Button);
}
}
#[test]
#[ignore]
fn sel_by_exact_name() {
let app = h::app_root();
let submit = h::one(&app, r#"button[name="Submit"]"#);
assert_eq!(submit.role, Role::Button);
}
#[test]
#[ignore]
fn sel_by_role_and_name() {
let app = h::app_root();
let results = app.locator(r#"button[name="Cancel"]"#).elements().unwrap();
assert_eq!(results.len(), 1);
}
#[test]
#[ignore]
fn sel_name_contains() {
let app = h::app_root();
let results = app.locator(r#"[name*="agree"]"#).elements().unwrap();
assert!(
!results.is_empty(),
"Should find element with 'agree' in name"
);
}
#[test]
#[ignore]
fn sel_name_starts_with() {
let app = h::app_root();
let results = app.locator(r#"[name^="Welc"]"#).elements().unwrap();
if results.is_empty() {
let results = app.locator(r#"[name^="Sub"]"#).elements().unwrap();
assert!(!results.is_empty());
assert!(results[0]
.name
.as_deref()
.unwrap()
.to_lowercase()
.starts_with("sub"));
} else {
assert!(results[0]
.name
.as_deref()
.unwrap()
.to_lowercase()
.starts_with("welc"));
}
}
#[test]
#[ignore]
fn sel_name_ends_with() {
let app = h::app_root();
let results = app.locator(r#"[name$="xa11y"]"#).elements().unwrap();
if results.is_empty() {
let results = app.locator(r#"[name$="App"]"#).elements().unwrap();
if results.is_empty() {
let results = app.locator(r#"[name$="Submit"]"#).elements().unwrap();
assert!(
!results.is_empty(),
"Should find at least one element with name ending in 'Submit'"
);
}
}
}
#[test]
#[ignore]
fn sel_value_attribute() {
let app = h::app_root();
let results = app.locator(r#"[value*="Red"]"#).elements().unwrap();
if results.is_empty() {
let results = app.locator(r#"[value*="0.75"]"#).elements().unwrap();
assert!(
!results.is_empty(),
"Should find element with value containing '0.75' (ProgressBar)"
);
}
}
#[test]
#[ignore]
fn sel_descendant_combinator() {
let app = h::app_root();
let results = app.locator("group button").elements().unwrap();
if results.is_empty() {
let results = app.locator("window button").elements().unwrap();
assert!(!results.is_empty());
for r in &results {
assert_eq!(r.role, Role::Button);
}
} else {
for r in &results {
assert_eq!(r.role, Role::Button);
}
}
}
#[test]
#[ignore]
fn sel_child_combinator() {
let app = h::app_root();
let results = app.locator("application > window").elements().unwrap();
for r in &results {
assert_eq!(r.role, Role::Window);
}
}
#[test]
#[ignore]
fn sel_nth_pseudo() {
let app = h::app_root();
let first = app.locator("button:nth(1)").elements().unwrap();
assert_eq!(first.len(), 1);
}
#[test]
#[ignore]
fn sel_role_attribute() {
let app = h::app_root();
let results = app.locator(r#"[role="button"]"#).elements().unwrap();
assert!(!results.is_empty());
for r in &results {
assert_eq!(r.role, Role::Button);
}
}
#[test]
#[ignore]
fn sel_complex_chain() {
let app = h::app_root();
let results = app
.locator(r#"menu_bar menu_item[name="File"]"#)
.elements()
.unwrap();
assert!(!results.is_empty(), "Should find File menu item via chain");
assert_eq!(results[0].role, Role::MenuItem);
assert_eq!(results[0].name.as_deref(), Some("File"));
}
#[test]
#[ignore]
fn raw_data_always_present() {
let _app = h::app_root();
#[cfg(target_os = "linux")]
{
let atspi_role = _app
.data
.raw
.get("atspi_role")
.and_then(|v| v.as_str())
.expect("Expected atspi_role in raw data");
assert!(!atspi_role.is_empty());
}
#[cfg(target_os = "macos")]
{
let ax_role = _app
.data
.raw
.get("ax_role")
.and_then(|v| v.as_str())
.expect("Expected ax_role in raw data");
assert!(!ax_role.is_empty());
}
#[cfg(target_os = "windows")]
{
let control_type_id = _app
.data
.raw
.get("control_type_id")
.and_then(|v| v.as_i64())
.expect("Expected control_type_id in raw data");
assert!(control_type_id > 0);
}
}
#[test]
#[ignore]
fn json_roundtrip_real_element() {
let app = h::app_root();
let json = serde_json::to_string(&app.data).unwrap();
let deser: ElementData = serde_json::from_str(&json).unwrap();
assert_eq!(deser.role, app.data.role);
assert_eq!(deser.name, app.data.name);
}
}