use super::{ElementSnapshot, ScopedWindow, observation};
use crate::{ElementId, Role, Window};
pub trait TestQueryExt {
fn elements(&self) -> Vec<ElementSnapshot>;
fn find_by_label(&self, label: &str) -> ElementSnapshot;
fn try_find_by_label(&self, label: &str) -> Option<ElementSnapshot>;
fn find_all_by_label(&self, label: &str) -> Vec<ElementSnapshot>;
fn find_all_by_role(&self, role: Role) -> Vec<ElementSnapshot>;
}
fn elements(window: &Window, scope: &[ElementId]) -> Vec<ElementSnapshot> {
let mut elements: Vec<_> = observation::snapshots(window)
.into_iter()
.filter(|element| element.path().starts_with(scope) && element.path().len() > scope.len())
.collect();
elements.sort_by_cached_key(|element| {
(
element.bounds().origin.y,
element.bounds().origin.x,
format!("{:?}", element.path()),
)
});
elements
}
fn by_label(window: &Window, scope: &[ElementId], label: &str) -> Vec<ElementSnapshot> {
elements(window, scope)
.into_iter()
.filter(|element| element.label() == Some(label))
.collect()
}
fn unique_label(window: &Window, scope: &[ElementId], label: &str) -> Option<ElementSnapshot> {
let mut matches = by_label(window, scope, label);
assert!(
matches.len() <= 1,
"ambiguous accessible label {label:?} in scope {scope:?}. Matching paths: {:?}",
matches
.iter()
.map(ElementSnapshot::path)
.collect::<Vec<_>>()
);
matches.pop()
}
fn require_label(window: &Window, scope: &[ElementId], label: &str) -> ElementSnapshot {
unique_label(window, scope, label).unwrap_or_else(|| {
panic!(
"missing accessible label {label:?} in scope {scope:?}. Registered paths: {}. Check the accessible label, observation, and completed frame.",
observation::registered_paths(window)
)
})
}
impl TestQueryExt for Window {
fn elements(&self) -> Vec<ElementSnapshot> {
elements(self, &[])
}
fn find_by_label(&self, label: &str) -> ElementSnapshot {
require_label(self, &[], label)
}
fn try_find_by_label(&self, label: &str) -> Option<ElementSnapshot> {
unique_label(self, &[], label)
}
fn find_all_by_label(&self, label: &str) -> Vec<ElementSnapshot> {
by_label(self, &[], label)
}
fn find_all_by_role(&self, role: Role) -> Vec<ElementSnapshot> {
self.elements()
.into_iter()
.filter(|element| element.role() == Some(role))
.collect()
}
}
impl ScopedWindow<'_> {
pub fn elements(&self) -> Vec<ElementSnapshot> {
elements(self.window, &self.scope)
}
pub fn find_by_label(&self, label: &str) -> ElementSnapshot {
require_label(self.window, &self.scope, label)
}
pub fn try_find_by_label(&self, label: &str) -> Option<ElementSnapshot> {
unique_label(self.window, &self.scope, label)
}
pub fn find_all_by_label(&self, label: &str) -> Vec<ElementSnapshot> {
by_label(self.window, &self.scope, label)
}
pub fn find_all_by_role(&self, role: Role) -> Vec<ElementSnapshot> {
self.elements()
.into_iter()
.filter(|element| element.role() == Some(role))
.collect()
}
}