hypen-engine 0.4.46

A Rust implementation of the Hypen engine
Documentation
/// Tests for Children() slot functionality
#[cfg(test)]
mod tests {
    use super::super::*;

    /// Helper to create a simple text element
    fn text(content: &str) -> Element {
        let mut el = Element::new("Text");
        el.props
            .insert("0".to_string(), Value::Static(serde_json::json!(content)));
        el
    }

    #[test]
    fn test_simple_children_slot() {
        // Create a component that has a Children() placeholder
        let component = Component::new("Card", |_props| {
            let mut container = Element::new("Container");
            // Add a header
            container
                .children
                .push_back(std::sync::Arc::new(text("Card Header")));
            // Add Children() slot
            let children_slot = Element::new("Children");
            container
                .children
                .push_back(std::sync::Arc::new(children_slot));
            // Add a footer
            container
                .children
                .push_back(std::sync::Arc::new(text("Card Footer")));
            container
        });

        let mut registry = ComponentRegistry::new();
        registry.register(component);

        // Create an instance with actual children
        let mut card_instance = Element::new("Card");
        card_instance
            .children
            .push_back(std::sync::Arc::new(text("Child 1")));
        card_instance
            .children
            .push_back(std::sync::Arc::new(text("Child 2")));

        // Expand the component
        let expanded = registry.expand(&card_instance);

        // Should have: header, child1, child2, footer
        assert_eq!(expanded.element_type, "Container");
        assert_eq!(expanded.children.len(), 4);

        // Verify order
        assert_eq!(expanded.children[0].element_type, "Text"); // header
        assert_eq!(expanded.children[1].element_type, "Text"); // child 1
        assert_eq!(expanded.children[2].element_type, "Text"); // child 2
        assert_eq!(expanded.children[3].element_type, "Text"); // footer
    }

    #[test]
    fn test_named_slot() {
        // Component with header and body slots
        let component = Component::new("Dialog", |_props| {
            let mut container = Element::new("Column");

            // Header slot using .slot("header") applicator
            let mut header = Element::new("Container");
            let mut header_slot = Element::new("Children");
            header_slot.props.insert(
                "slot.0".to_string(),
                Value::Static(serde_json::json!("header")),
            );
            header.children.push_back(std::sync::Arc::new(header_slot));
            container.children.push_back(std::sync::Arc::new(header));

            // Body slot using .slot("body") applicator
            let mut body = Element::new("Container");
            let mut body_slot = Element::new("Children");
            body_slot.props.insert(
                "slot.0".to_string(),
                Value::Static(serde_json::json!("body")),
            );
            body.children.push_back(std::sync::Arc::new(body_slot));
            container.children.push_back(std::sync::Arc::new(body));

            container
        });

        let mut registry = ComponentRegistry::new();
        registry.register(component);

        // Create instance with slotted children using .slot() applicator
        let mut dialog = Element::new("Dialog");

        let mut header_child = text("Dialog Title");
        header_child.props.insert(
            "slot.0".to_string(),
            Value::Static(serde_json::json!("header")),
        );
        dialog.children.push_back(std::sync::Arc::new(header_child));

        let mut body_child = text("Dialog Content");
        body_child.props.insert(
            "slot.0".to_string(),
            Value::Static(serde_json::json!("body")),
        );
        dialog.children.push_back(std::sync::Arc::new(body_child));

        let expanded = registry.expand(&dialog);

        // Verify structure
        assert_eq!(expanded.element_type, "Column");
        assert_eq!(expanded.children.len(), 2); // header container, body container

        // Check header slot received header child
        assert_eq!(expanded.children[0].children.len(), 1);
        assert_eq!(expanded.children[0].children[0].element_type, "Text");

        // Check body slot received body child
        assert_eq!(expanded.children[1].children.len(), 1);
        assert_eq!(expanded.children[1].children[0].element_type, "Text");
    }

    #[test]
    fn test_default_slot_without_applicator() {
        // Component with both named and default slots
        let component = Component::new("Panel", |_props| {
            let mut container = Element::new("Column");

            // Header slot (named) using .slot("header")
            let mut header = Element::new("Container");
            let mut header_slot = Element::new("Children");
            header_slot.props.insert(
                "slot.0".to_string(),
                Value::Static(serde_json::json!("header")),
            );
            header.children.push_back(std::sync::Arc::new(header_slot));
            container.children.push_back(std::sync::Arc::new(header));

            // Default slot (no slot applicator)
            let mut body = Element::new("Container");
            let default_slot = Element::new("Children");
            body.children.push_back(std::sync::Arc::new(default_slot));
            container.children.push_back(std::sync::Arc::new(body));

            container
        });

        let mut registry = ComponentRegistry::new();
        registry.register(component);

        let mut panel = Element::new("Panel");

        // Child with .slot("header") applicator
        let mut header_child = text("Panel Header");
        header_child.props.insert(
            "slot.0".to_string(),
            Value::Static(serde_json::json!("header")),
        );
        panel.children.push_back(std::sync::Arc::new(header_child));

        // Children without slot applicator go to default slot
        panel
            .children
            .push_back(std::sync::Arc::new(text("Default Content 1")));
        panel
            .children
            .push_back(std::sync::Arc::new(text("Default Content 2")));

        let expanded = registry.expand(&panel);

        // Header should have 1 child
        assert_eq!(expanded.children[0].children.len(), 1);

        // Default slot should have 2 children
        assert_eq!(expanded.children[1].children.len(), 2);
    }

    #[test]
    fn test_nested_children_slots() {
        // Outer component with Children slot
        let outer = Component::new("Outer", |_props| {
            let mut container = Element::new("Container");
            container
                .children
                .push_back(std::sync::Arc::new(text("Outer Start")));

            let mut inner = Element::new("Inner");
            let children_slot = Element::new("Children");
            inner.children.push_back(std::sync::Arc::new(children_slot));

            container.children.push_back(std::sync::Arc::new(inner));
            container
                .children
                .push_back(std::sync::Arc::new(text("Outer End")));
            container
        });

        // Inner component also with Children slot
        let inner = Component::new("Inner", |_props| {
            let mut wrapper = Element::new("Container");
            wrapper
                .children
                .push_back(std::sync::Arc::new(text("Inner Start")));
            wrapper
                .children
                .push_back(std::sync::Arc::new(Element::new("Children")));
            wrapper
                .children
                .push_back(std::sync::Arc::new(text("Inner End")));
            wrapper
        });

        let mut registry = ComponentRegistry::new();
        registry.register(outer);
        registry.register(inner);

        // Create nested usage
        let mut outer_instance = Element::new("Outer");
        outer_instance
            .children
            .push_back(std::sync::Arc::new(text("Actual Content")));

        let expanded = registry.expand(&outer_instance);

        // Should have: Outer Start, Inner (with nested structure), Outer End
        assert_eq!(expanded.children.len(), 3);
        assert_eq!(expanded.children[0].element_type, "Text"); // Outer Start
        assert_eq!(expanded.children[1].element_type, "Container"); // Inner expanded
        assert_eq!(expanded.children[2].element_type, "Text"); // Outer End

        // Check Inner expanded correctly
        let inner_expanded = &expanded.children[1];
        assert_eq!(inner_expanded.children.len(), 3);
        assert_eq!(inner_expanded.children[0].element_type, "Text"); // Inner Start
        assert_eq!(inner_expanded.children[1].element_type, "Text"); // Actual Content
        assert_eq!(inner_expanded.children[2].element_type, "Text"); // Inner End
    }

    #[test]
    fn test_multiple_children_of_same_slot() {
        let component = Component::new("Section", |_props| {
            let mut container = Element::new("Column");

            let mut actions = Element::new("Row");
            let mut actions_slot = Element::new("Children");
            actions_slot.props.insert(
                "slot.0".to_string(),
                Value::Static(serde_json::json!("actions")),
            );
            actions
                .children
                .push_back(std::sync::Arc::new(actions_slot));
            container.children.push_back(std::sync::Arc::new(actions));

            container
        });

        let mut registry = ComponentRegistry::new();
        registry.register(component);

        let mut section = Element::new("Section");

        // Add multiple children with same .slot("actions") applicator
        let mut action1 = Element::new("Button");
        action1.props.insert(
            "slot.0".to_string(),
            Value::Static(serde_json::json!("actions")),
        );
        action1
            .children
            .push_back(std::sync::Arc::new(text("Action 1")));

        let mut action2 = Element::new("Button");
        action2.props.insert(
            "slot.0".to_string(),
            Value::Static(serde_json::json!("actions")),
        );
        action2
            .children
            .push_back(std::sync::Arc::new(text("Action 2")));

        section.children.push_back(std::sync::Arc::new(action1));
        section.children.push_back(std::sync::Arc::new(action2));

        let expanded = registry.expand(&section);

        // Actions row should have both buttons
        assert_eq!(expanded.children[0].children.len(), 2);
        assert_eq!(expanded.children[0].children[0].element_type, "Button");
        assert_eq!(expanded.children[0].children[1].element_type, "Button");
    }

    #[test]
    fn test_empty_children_slot() {
        let component = Component::new("Optional", |_props| {
            let mut container = Element::new("Container");
            container
                .children
                .push_back(std::sync::Arc::new(text("Before")));
            container
                .children
                .push_back(std::sync::Arc::new(Element::new("Children")));
            container
                .children
                .push_back(std::sync::Arc::new(text("After")));
            container
        });

        let mut registry = ComponentRegistry::new();
        registry.register(component);

        // Create instance with NO children
        let instance = Element::new("Optional");

        let expanded = registry.expand(&instance);

        // Should just have before and after, no children in between
        assert_eq!(expanded.children.len(), 2);
        assert_eq!(expanded.children[0].element_type, "Text"); // Before
        assert_eq!(expanded.children[1].element_type, "Text"); // After
    }

    #[test]
    fn test_children_slot_deep_in_tree() {
        // Component with Children slot nested deep in the tree
        let component = Component::new("DeepCard", |_props| {
            let mut root = Element::new("Container");
            let mut column = Element::new("Column");
            let mut row = Element::new("Row");
            let mut inner = Element::new("Container");

            inner
                .children
                .push_back(std::sync::Arc::new(Element::new("Children")));
            row.children.push_back(std::sync::Arc::new(inner));
            column.children.push_back(std::sync::Arc::new(row));
            root.children.push_back(std::sync::Arc::new(column));

            root
        });

        let mut registry = ComponentRegistry::new();
        registry.register(component);

        let mut instance = Element::new("DeepCard");
        instance
            .children
            .push_back(std::sync::Arc::new(text("Deep Child")));

        let expanded = registry.expand(&instance);

        // Navigate deep into the tree
        let deep_child = &expanded.children[0].children[0].children[0].children[0];
        assert_eq!(deep_child.element_type, "Text");
    }
}