use crate::models::A11yNode;
use super::ui_map::PageSignals;
pub struct SignalDetector;
impl SignalDetector {
pub fn new() -> Self {
Self
}
pub fn detect(&self, nodes: &[A11yNode]) -> PageSignals {
let mut signals = PageSignals::default();
for node in nodes {
let role = node.role.to_lowercase();
let name_lower = node
.name
.as_deref()
.map(|n| n.to_lowercase())
.unwrap_or_default();
if matches!(role.as_str(), "dialog" | "alertdialog" | "sheet") {
signals.modal_present = true;
}
if name_lower.contains("cookie")
|| name_lower.contains("consent")
|| name_lower.contains("accept all")
{
signals.cookie_banner = true;
}
if role == "alert" || name_lower.contains("error") || name_lower.contains("failed") {
signals.error_banner = true;
}
if role == "progressbar"
|| name_lower.contains("loading")
|| name_lower.contains("spinner")
{
signals.loading_indicator = true;
}
if signals.page_type_hint.is_none() {
if name_lower.contains("sign in")
|| name_lower.contains("log in")
|| name_lower.contains("password")
{
signals.page_type_hint = Some("login".to_string());
} else if name_lower.contains("checkout")
|| name_lower.contains("payment")
|| name_lower.contains("place order")
{
signals.page_type_hint = Some("checkout".to_string());
} else if name_lower.contains("search results")
|| name_lower.contains("results for")
{
signals.page_type_hint = Some("search_results".to_string());
}
}
}
signals
}
}
impl Default for SignalDetector {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::Bounds;
#[test]
fn test_detect_modal() {
let detector = SignalDetector::new();
let nodes = vec![A11yNode {
node_id: "n0".to_string(),
role: "dialog".to_string(),
name: Some("Confirm action".to_string()),
value: None,
bounds: Bounds::new(100.0, 100.0, 400.0, 300.0),
children: vec![],
focusable: false,
focused: false,
disabled: false,
}];
let signals = detector.detect(&nodes);
assert!(signals.modal_present);
}
#[test]
fn test_detect_login_page() {
let detector = SignalDetector::new();
let nodes = vec![A11yNode {
node_id: "n0".to_string(),
role: "button".to_string(),
name: Some("Sign In".to_string()),
value: None,
bounds: Bounds::new(100.0, 100.0, 80.0, 30.0),
children: vec![],
focusable: true,
focused: false,
disabled: false,
}];
let signals = detector.detect(&nodes);
assert_eq!(signals.page_type_hint.as_deref(), Some("login"));
}
}