godoru 0.1.0

UI Framework for Rust using Godot
use super::*;
use crate::ImageHandle;

#[test]
fn spacingTupleUsesCssOrder() {
    let container = Container::<NoAction>::new()
        .padding((1, 2, 3, 4))
        .margin((5, 6, 7, 8));
    let padding = container.padding.as_ref().unwrap();
    let margin = container.margin.as_ref().unwrap();

    assert_eq!(padding.top, 1);
    assert_eq!(padding.right, 2);
    assert_eq!(padding.bottom, 3);
    assert_eq!(padding.left, 4);
    assert_eq!(margin.top, 5);
    assert_eq!(margin.right, 6);
    assert_eq!(margin.bottom, 7);
    assert_eq!(margin.left, 8);
}

#[test]
fn spacingNumberAppliesAllSides() {
    let container = Container::<NoAction>::new().padding(16).margin(24);

    assert_eq!(container.padding, Some(Spacing::all(16)));
    assert_eq!(container.margin, Some(Spacing::all(24)));
}

#[test]
fn childrenClosureBuildsNodesInOrder() {
    let root = Container::<NoAction>::new().children(|ui| {
        ui.text("Title").class("title");
        ui.container().class("card").empty();
    });

    assert_eq!(root.children.len(), 2);
    assert!(matches!(root.children[0], UiNode::Text(_)));
    assert!(matches!(root.children[1], UiNode::Container(_)));
}

#[test]
fn uiNodeOrderIncludesImageAndButton() {
    let image = ImageHandle::new("logo.svg");
    let root = Container::<NoAction>::new().children(|ui| {
        ui.text("Title");
        ui.image(image);
        ui.button("Save");
    });

    assert!(matches!(root.children[0], UiNode::Text(_)));
    assert!(matches!(root.children[1], UiNode::Image(_)));
    assert!(matches!(root.children[2], UiNode::Button(_)));
}

#[test]
fn classExtendsBaseStyle() {
    let theme = AppTheme::new()
        .class(Component::Button, "base", |style| {
            style.radius(12);
            style.padding(8);
        })
        .class(Component::Button, "primary", |style| {
            style.extends("base");
            style.background(Color::rgb(0.1, 0.2, 0.8));
        });

    let style = theme.styleFor(Component::Button, Some("primary"));

    assert_eq!(style.radius, Some(12));
    assert_eq!(style.padding, Some(Spacing::all(8)));
    assert_eq!(style.background, Some(Color::rgb(0.1, 0.2, 0.8)));
}

#[test]
fn localStyleOverridesThemeClass() {
    let theme = AppTheme::new().class(Component::Container, "card", |style| {
        style.radius(12);
        style.background(Color::rgb(0.1, 0.1, 0.1));
    });
    let mut style = theme.styleFor(Component::Container, Some("card"));
    let mut local = Style::default();
    local.radius(24);
    mergeStyle(&mut style, &local);

    assert_eq!(style.radius, Some(24));
    assert_eq!(style.background, Some(Color::rgb(0.1, 0.1, 0.1)));
}

#[test]
fn stateStyleExtendsBaseClass() {
    let theme = AppTheme::new()
        .class(Component::Button, "primary", |style| {
            style.background(Color::rgb(0.0, 0.2, 1.0));
            style.color(Color::rgb(1.0, 1.0, 1.0));
        })
        .class(Component::Button, "primary:hover", |style| {
            style.background(Color::rgb(0.2, 0.4, 1.0));
        });

    let style = theme.styleForState(Component::Button, Some("primary"), VisualState::Hover);

    assert_eq!(style.background, Some(Color::rgb(0.2, 0.4, 1.0)));
    assert_eq!(style.color, Some(Color::rgb(1.0, 1.0, 1.0)));
}

#[derive(Clone, Debug, PartialEq, Eq)]
enum TestAction {
    Click,
    Hover,
}

#[test]
fn buttonStoresTypedActions() {
    let root = Container::<TestAction>::new().children(|ui| {
        ui.button("Run")
            .onClick(TestAction::Click)
            .onHover(TestAction::Hover);
    });

    let UiNode::Button(button) = &root.children[0] else {
        panic!("expected button");
    };

    assert_eq!(button.interactions.click, Some(TestAction::Click));
    assert_eq!(button.interactions.hover, Some(TestAction::Hover));
    assert!(button.interactions.hasLocalInput());
}

#[test]
fn textInputStoresPayloadCallbacks() {
    let root = Container::<TestAction>::new().children(|ui| {
        ui.textInput("hello")
            .placeholder("Name")
            .onChange(|_| TestAction::Click);
    });

    let UiNode::TextInput(textInput) = &root.children[0] else {
        panic!("expected text input");
    };

    assert_eq!(textInput.value, "hello");
    assert_eq!(textInput.placeholder.as_deref(), Some("Name"));
    assert!(textInput.onChange.is_some());
}

#[test]
fn checkboxStoresPayloadCallback() {
    let root = Container::<TestAction>::new().children(|ui| {
        ui.checkbox(true)
            .text("Enabled")
            .onToggle(|_| TestAction::Click);
    });

    let UiNode::Checkbox(checkbox) = &root.children[0] else {
        panic!("expected checkbox");
    };

    assert!(checkbox.checked);
    assert_eq!(checkbox.text.as_deref(), Some("Enabled"));
    assert!(checkbox.onToggle.is_some());
}

#[test]
fn keyedNodesKeepIdentityWhenReordered() {
    let left = UiNode::<NoAction>::Text(Text::new("A").key("a"));
    let right = UiNode::<NoAction>::Text(Text::new("B").key("a"));
    let different = UiNode::<NoAction>::Button(Button::new("A").key("a"));

    assert!(left.sameIdentity(&right));
    assert!(!left.sameIdentity(&different));
}