browsing 0.1.6

Browser automation: navigate, click, extract, screenshot. Standalone browser control via CDP.
Documentation
//! Tests for the perception engine

#[cfg(test)]
mod tests {
    use crate::dom::views::{EnhancedDOMTreeNode, NodeType, PageIntent, SemanticRole};
    use crate::perception::PerceptionEngine;
    use std::collections::HashMap;

    fn make_node(tag: &str, text: &str) -> EnhancedDOMTreeNode {
        let mut node = EnhancedDOMTreeNode::new(
            1,
            1,
            NodeType::ElementNode,
            tag.to_string(),
            text.to_string(),
            "target-1".to_string(),
        );
        node.children_nodes = Some(vec![]);
        node
    }

    fn make_node_with_attrs(tag: &str, attrs: HashMap<String, String>) -> EnhancedDOMTreeNode {
        let mut node = EnhancedDOMTreeNode::new(
            1,
            1,
            NodeType::ElementNode,
            tag.to_string(),
            "".to_string(),
            "target-1".to_string(),
        );
        node.attributes = attrs;
        node.children_nodes = Some(vec![]);
        node
    }

    fn add_child(parent: &mut EnhancedDOMTreeNode, child: EnhancedDOMTreeNode) {
        if let Some(ref mut children) = parent.children_nodes {
            children.push(child);
        }
    }

    #[test]
    fn test_detect_login_form() {
        let mut root = make_node("form", "");
        let password = make_node_with_attrs("input", {
            let mut m = HashMap::new();
            m.insert("type".to_string(), "password".to_string());
            m
        });
        add_child(&mut root, password);

        let engine = PerceptionEngine::new();
        let (intent, _) = engine.perceive(&mut root);

        assert_eq!(root.semantic_role, Some(SemanticRole::LoginForm));
        assert_eq!(intent, Some(PageIntent::Login));
    }

    #[test]
    fn test_detect_search_form() {
        let mut root = make_node("form", "");
        let search_btn = make_node_with_attrs("button", {
            let mut m = HashMap::new();
            m.insert("value".to_string(), "Search".to_string());
            m
        });
        add_child(&mut root, search_btn);

        let engine = PerceptionEngine::new();
        let (intent, _) = engine.perceive(&mut root);

        assert_eq!(root.semantic_role, Some(SemanticRole::SearchForm));
    }

    #[test]
    fn test_detect_captcha() {
        let mut root = make_node("div", "Please complete the CAPTCHA");

        let engine = PerceptionEngine::new();
        let (intent, _) = engine.perceive(&mut root);

        assert_eq!(root.semantic_role, Some(SemanticRole::Captcha));
        assert_eq!(intent, Some(PageIntent::Captcha));
    }

    #[test]
    fn test_detect_cookie_consent() {
        let mut root = make_node("div", "We use cookies. Accept?");
        let mut attrs = HashMap::new();
        attrs.insert("class".to_string(), "cookie-banner".to_string());
        root.attributes = attrs;

        let engine = PerceptionEngine::new();
        let (intent, _) = engine.perceive(&mut root);

        assert_eq!(root.semantic_role, Some(SemanticRole::CookieConsent));
        assert_eq!(intent, Some(PageIntent::CookieConsent));
    }

    #[test]
    fn test_detect_article() {
        let mut root = make_node("article", "");

        let engine = PerceptionEngine::new();
        let (intent, _) = engine.perceive(&mut root);

        assert_eq!(root.semantic_role, Some(SemanticRole::Article));
        assert_eq!(intent, Some(PageIntent::Article));
    }

    #[test]
    fn test_detect_navigation() {
        let mut root = make_node("nav", "");

        let engine = PerceptionEngine::new();
        engine.perceive(&mut root);

        assert_eq!(root.semantic_role, Some(SemanticRole::Navigation));
    }

    #[test]
    fn test_detect_submit_button() {
        let mut root = make_node("button", "Submit");

        let engine = PerceptionEngine::new();
        engine.perceive(&mut root);

        assert_eq!(root.semantic_role, Some(SemanticRole::SubmitButton));
    }

    #[test]
    fn test_detect_pagination() {
        let mut root = make_node("a", "Next »");

        let engine = PerceptionEngine::new();
        engine.perceive(&mut root);

        assert_eq!(root.semantic_role, Some(SemanticRole::Pagination));
    }

    #[test]
    fn test_detect_text_input() {
        let mut root = make_node_with_attrs("input", {
            let mut m = HashMap::new();
            m.insert("type".to_string(), "text".to_string());
            m.insert("placeholder".to_string(), "Enter your name".to_string());
            m
        });

        let engine = PerceptionEngine::new();
        engine.perceive(&mut root);

        assert_eq!(root.semantic_role, Some(SemanticRole::TextInput));
    }

    #[test]
    fn test_detect_dropdown() {
        let mut root = make_node("select", "");

        let engine = PerceptionEngine::new();
        engine.perceive(&mut root);

        assert_eq!(root.semantic_role, Some(SemanticRole::Dropdown));
    }

    #[test]
    fn test_structural_elements() {
        let mut header = make_node("header", "");
        let mut footer = make_node("footer", "");
        let mut aside = make_node("aside", "");
        let mut main = make_node("main", "");

        let engine = PerceptionEngine::new();
        engine.perceive(&mut header);
        engine.perceive(&mut footer);
        engine.perceive(&mut aside);
        engine.perceive(&mut main);

        assert_eq!(header.semantic_role, Some(SemanticRole::Header));
        assert_eq!(footer.semantic_role, Some(SemanticRole::Footer));
        assert_eq!(aside.semantic_role, Some(SemanticRole::Sidebar));
        assert_eq!(main.semantic_role, Some(SemanticRole::MainContent));
    }

    #[test]
    fn test_non_element_nodes_unchanged() {
        let mut text_node = EnhancedDOMTreeNode::new(
            1,
            1,
            NodeType::TextNode,
            "#text".to_string(),
            "Hello world".to_string(),
            "target-1".to_string(),
        );

        let engine = PerceptionEngine::new();
        engine.perceive(&mut text_node);

        assert_eq!(text_node.semantic_role, None);
        assert_eq!(text_node.semantic_confidence, 0.0);
    }

    #[test]
    fn test_perception_config_disabled() {
        let mut root = make_node("form", "");
        let password = make_node_with_attrs("input", {
            let mut m = HashMap::new();
            m.insert("type".to_string(), "password".to_string());
            m
        });
        add_child(&mut root, password);

        let config = crate::perception::PerceptionConfig {
            enable_role_inference: false,
            enable_page_intent: false,
            ..Default::default()
        };
        let engine = PerceptionEngine::with_config(config);
        let (intent, _) = engine.perceive(&mut root);

        // With perception disabled, nothing should be inferred
        assert_eq!(root.semantic_role, None);
        assert_eq!(intent, None);
    }

    #[test]
    fn test_page_intent_confidence_normalization() {
        let mut root = make_node("form", "");
        let password = make_node_with_attrs("input", {
            let mut m = HashMap::new();
            m.insert("type".to_string(), "password".to_string());
            m
        });
        add_child(&mut root, password);

        let engine = PerceptionEngine::new();
        let (_, confidence) = engine.perceive(&mut root);

        // Confidence should be a normalized value between 0 and 1
        assert!(confidence > 0.0 && confidence <= 0.99, "confidence was {confidence}");
    }
}