cranpose-testing 0.0.63

Testing utilities and harness for Cranpose
Documentation
use super::*;
use cranpose_core::useState;

thread_local! {
    static ROOT_RENDER_TEST_INVALIDATED: Cell<bool> = const { Cell::new(false) };
}

#[derive(Default)]
struct TestNode {
    value: i32,
}

impl Node for TestNode {}

#[derive(Default)]
struct TestTextValueNode {
    value: String,
}

impl Node for TestTextValueNode {}

#[test]
fn cranpose_test_rule_reports_content_and_root() {
    run_test_composition(|rule| {
        assert!(!rule.has_content());
        assert!(rule.root_id().is_none());

        let runtime = rule.runtime_handle();
        let state = MutableState::with_runtime(0, runtime.clone());
        let recompositions = Rc::new(Cell::new(0));

        rule.set_content({
            let recompositions = Rc::clone(&recompositions);
            move || {
                recompositions.set(recompositions.get() + 1);
                let id = with_current_composer(|composer| composer.emit_node(TestNode::default));
                let value = state.value();
                with_node_mut(id, |node: &mut TestNode| {
                    node.value = value;
                })
                .expect("update node text");
            }
        })
        .expect("install content");

        assert!(rule.has_content());
        assert_eq!(recompositions.get(), 1);

        let root = rule.root_id().expect("root id available");
        let stored_value = {
            rule.applier_mut()
                .with_node(root, |node: &mut TestNode| node.value)
                .expect("read node")
        };
        assert_eq!(stored_value, 0);

        state.set_value(5);
        rule.pump_until_idle().expect("process invalidation");

        assert_eq!(recompositions.get(), 2);
        let updated_value = {
            rule.applier_mut()
                .with_node(root, |node: &mut TestNode| node.value)
                .expect("read updated node")
        };
        assert_eq!(updated_value, 5);
    });
}

#[test]
fn cranpose_test_rule_replays_root_render_requests_before_returning() {
    run_test_composition(|rule| {
        ROOT_RENDER_TEST_INVALIDATED.with(|flag| flag.set(false));
        let render_count = Rc::new(Cell::new(0));

        rule.set_content({
            let render_count = Rc::clone(&render_count);
            move || {
                let root_trigger = useState(|| false);
                render_count.set(render_count.get() + 1);
                cranpose_core::with_key(&"testing-root-render-probe", || {
                    let _ = root_trigger.value();
                });

                let value = format!("render {}", render_count.get());
                let initial_value = value.clone();
                let id = with_current_composer(|composer| {
                    composer.emit_node(|| TestTextValueNode {
                        value: initial_value,
                    })
                });
                with_node_mut(id, |node: &mut TestTextValueNode| {
                    node.value = value;
                })
                .expect("update test text value node");

                cranpose_core::SideEffect(move || {
                    let already_invalidated =
                        ROOT_RENDER_TEST_INVALIDATED.with(|flag| flag.replace(true));
                    if already_invalidated {
                        return;
                    }
                    root_trigger.set_value(true);
                });
            }
        })
        .expect("install content");

        assert_eq!(
            render_count.get(),
            2,
            "ComposeTestRule::set_content must replay pending root renders before returning"
        );
        assert!(
            !rule.composition().take_root_render_request(),
            "test harness should not leave a pending root render request behind"
        );

        let root = rule.root_id().expect("root id available");
        let stored_value = rule
            .applier_mut()
            .with_node(root, |node: &mut TestTextValueNode| node.value.clone())
            .expect("read test node");
        assert_eq!(stored_value, "render 2");
    });
}