فهرست منبع

add tests for the interperter

Evan Almloff 3 سال پیش
والد
کامیت
0f2ff34a30
3فایلهای تغییر یافته به همراه367 افزوده شده و 2 حذف شده
  1. 5 1
      packages/rsx_interpreter/Cargo.toml
  2. 6 1
      packages/rsx_interpreter/src/interperter.rs
  3. 356 0
      packages/rsx_interpreter/tests/render.rs

+ 5 - 1
packages/rsx_interpreter/Cargo.toml

@@ -16,4 +16,8 @@ dioxus-rsx = { path = "../rsx", default-features = false }
 dioxus-ssr = { path = "../ssr" }
 dioxus-core = { path = "../core" }
 dioxus-html = { path = "../html" }
-dioxus-hooks = { path = "../hooks"}
+dioxus-hooks = { path = "../hooks"}
+
+[dev-dependencies]
+dioxus-core-macro = { path = "../core-macro" }
+bumpalo = { version = "3.6", features = ["collections", "boxed"] }

+ 6 - 1
packages/rsx_interpreter/src/interperter.rs

@@ -53,7 +53,12 @@ pub fn build<'a>(
     for child in rsx.roots {
         children_built.push(build_node(child, &mut ctx, factory)?);
     }
-    Ok(factory.fragment_from_iter(children_built.iter()))
+
+    if children_built.len() == 1 {
+        return Ok(children_built.pop().unwrap());
+    } else {
+        Ok(factory.fragment_from_iter(children_built.iter()))
+    }
 }
 
 fn build_node<'a>(

+ 356 - 0
packages/rsx_interpreter/tests/render.rs

@@ -0,0 +1,356 @@
+use std::cell::{Cell, RefCell};
+
+use bumpalo::boxed::Box;
+use dioxus_core::{prelude::*, AnyEvent};
+use dioxus_core_macro::*;
+use dioxus_html as dioxus_elements;
+use dioxus_rsx_interpreter::{
+    captuered_context::{CapturedContext, FormattedArg, IfmtArgs},
+    CodeLocation, ErrorHandler,
+};
+
+#[test]
+#[allow(non_snake_case)]
+fn render_basic() {
+    fn Base(cx: Scope) -> Element {
+        rsx!(cx, div {})
+    }
+
+    let dom = VirtualDom::new(Base);
+    let static_vnodes = rsx!(div{"hello world"});
+    let location = CodeLocation {
+        file: String::new(),
+        line: 0,
+        column: 0,
+    };
+    let empty_context = CapturedContext {
+        captured: IfmtArgs {
+            named_args: Vec::new(),
+        },
+        components: Vec::new(),
+        iterators: Vec::new(),
+        expressions: Vec::new(),
+        listeners: Vec::new(),
+        location: location.clone(),
+    };
+    let interperted_vnodes = LazyNodes::new(|factory| {
+        dioxus_rsx_interpreter::resolve_scope(
+            location,
+            "div{\"hello world\"}",
+            empty_context,
+            factory,
+        )
+    });
+
+    let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
+    let static_vnodes = dom.render_vnodes(static_vnodes);
+    assert!(check_eq(interperted_vnodes, static_vnodes));
+}
+
+#[test]
+#[allow(non_snake_case)]
+fn render_nested() {
+    fn Base(cx: Scope) -> Element {
+        rsx!(cx, div {})
+    }
+
+    let dom = VirtualDom::new(Base);
+    let static_vnodes = rsx! {
+        div {
+            p { "hello world" }
+            div {
+                p { "hello world" }
+            }
+        }
+    };
+    let location = CodeLocation {
+        file: String::new(),
+        line: 1,
+        column: 0,
+    };
+    let empty_context = CapturedContext {
+        captured: IfmtArgs {
+            named_args: Vec::new(),
+        },
+        components: Vec::new(),
+        iterators: Vec::new(),
+        expressions: Vec::new(),
+        listeners: Vec::new(),
+        location: location.clone(),
+    };
+    let interperted_vnodes = LazyNodes::new(|factory| {
+        dioxus_rsx_interpreter::resolve_scope(
+            location,
+            r#"div {
+                p { "hello world" }
+                div {
+                    p { "hello world" }
+                }
+            }"#,
+            empty_context,
+            factory,
+        )
+    });
+
+    let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
+    let static_vnodes = dom.render_vnodes(static_vnodes);
+    assert!(check_eq(interperted_vnodes, static_vnodes));
+}
+
+#[test]
+#[allow(non_snake_case)]
+fn render_component() {
+    fn Comp(cx: Scope) -> Element {
+        rsx!(cx, div {})
+    }
+
+    fn Base(cx: Scope) -> Element {
+        rsx!(cx, div {})
+    }
+
+    let dom = VirtualDom::new(Base);
+    let static_vnodes = rsx! {
+        div {
+            Comp {}
+        }
+    };
+    let location = CodeLocation {
+        file: String::new(),
+        line: 2,
+        column: 0,
+    };
+
+    let interperted_vnodes = LazyNodes::new(|factory| {
+        let context = CapturedContext {
+            captured: IfmtArgs {
+                named_args: Vec::new(),
+            },
+            components: vec![(
+                r#"__cx.component(Comp, fc_to_builder(Comp).build(), None, "Comp")"#,
+                factory.component(Comp, (), None, "Comp"),
+            )],
+            iterators: Vec::new(),
+            expressions: Vec::new(),
+            listeners: Vec::new(),
+            location: location.clone(),
+        };
+        dioxus_rsx_interpreter::resolve_scope(
+            location,
+            r#"div {
+                Comp {}
+            }"#,
+            context,
+            factory,
+        )
+    });
+
+    let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
+    let static_vnodes = dom.render_vnodes(static_vnodes);
+    println!("{:#?}", interperted_vnodes);
+    println!("{:#?}", static_vnodes);
+    assert!(check_eq(interperted_vnodes, static_vnodes));
+}
+
+#[test]
+#[allow(non_snake_case)]
+fn render_iterator() {
+    fn Base(cx: Scope) -> Element {
+        rsx!(cx, div {})
+    }
+
+    let dom = VirtualDom::new(Base);
+    let iter = (0..10).map(|i| dom.render_vnodes(rsx! {"{i}"}));
+    let static_vnodes = rsx! {
+        div {
+            iter
+        }
+    };
+    let location = CodeLocation {
+        file: String::new(),
+        line: 3,
+        column: 0,
+    };
+
+    let interperted_vnodes = LazyNodes::new(|factory| {
+        let context = CapturedContext {
+            captured: IfmtArgs {
+                named_args: Vec::new(),
+            },
+            components: Vec::new(),
+            iterators: vec![(
+                r#"
+            (0..10).map(|i| dom.render_vnodes(rsx!{"{i}"}))"#,
+                factory.fragment_from_iter((0..10).map(|i| factory.text(format_args!("{i}")))),
+            )],
+            expressions: Vec::new(),
+            listeners: Vec::new(),
+            location: location.clone(),
+        };
+        dioxus_rsx_interpreter::resolve_scope(
+            location,
+            r#"div {
+                (0..10).map(|i| dom.render_vnodes(rsx!{"{i}"}))
+            }"#,
+            context,
+            factory,
+        )
+    });
+
+    let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
+    let static_vnodes = dom.render_vnodes(static_vnodes);
+    println!("{:#?}", interperted_vnodes);
+    println!("{:#?}", static_vnodes);
+    assert!(check_eq(interperted_vnodes, static_vnodes));
+}
+
+#[test]
+#[allow(non_snake_case)]
+fn render_captured_variable() {
+    fn Base(cx: Scope) -> Element {
+        rsx!(cx, div {})
+    }
+
+    let dom = VirtualDom::new(Base);
+
+    let x = 10;
+    let static_vnodes = rsx! {
+        div {
+            "{x}"
+        }
+    };
+    let location = CodeLocation {
+        file: String::new(),
+        line: 4,
+        column: 0,
+    };
+
+    let interperted_vnodes = LazyNodes::new(|factory| {
+        let context = CapturedContext {
+            captured: IfmtArgs {
+                named_args: vec![FormattedArg {
+                    expr: "x",
+                    format_args: "",
+                    result: x.to_string(),
+                }],
+            },
+            components: Vec::new(),
+            iterators: Vec::new(),
+            expressions: Vec::new(),
+            listeners: Vec::new(),
+            location: location.clone(),
+        };
+        dioxus_rsx_interpreter::resolve_scope(
+            location,
+            r#"div {
+                "{x}"
+            }"#,
+            context,
+            factory,
+        )
+    });
+
+    let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
+    let static_vnodes = dom.render_vnodes(static_vnodes);
+    println!("{:#?}", interperted_vnodes);
+    println!("{:#?}", static_vnodes);
+    assert!(check_eq(interperted_vnodes, static_vnodes));
+}
+
+#[test]
+#[allow(non_snake_case)]
+fn render_listener() {
+    fn Base(cx: Scope) -> Element {
+        rsx!(cx, div {})
+    }
+
+    let dom = VirtualDom::new(Base);
+    let static_vnodes = rsx! {
+        div {
+            onclick: |_| println!("clicked")
+        }
+    };
+    let location = CodeLocation {
+        file: String::new(),
+        line: 5,
+        column: 0,
+    };
+
+    let interperted_vnodes = LazyNodes::new(|factory| {
+        let f = |_| println!("clicked");
+        let f = factory.bump().alloc(f);
+        let context = CapturedContext {
+            captured: IfmtArgs {
+                named_args: Vec::new(),
+            },
+            components: Vec::new(),
+            iterators: Vec::new(),
+            expressions: Vec::new(),
+            listeners: vec![(
+                r#"dioxus_elements::on::onclick(__cx, |_| println!("clicked"))"#,
+                dioxus_elements::on::onclick(factory, f),
+            )],
+            location: location.clone(),
+        };
+        dioxus_rsx_interpreter::resolve_scope(
+            location,
+            r#"div {
+                onclick: |_| println!("clicked")
+            }"#,
+            context,
+            factory,
+        )
+    });
+
+    let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
+    let static_vnodes = dom.render_vnodes(static_vnodes);
+    println!("{:#?}", interperted_vnodes);
+    println!("{:#?}", static_vnodes);
+    assert!(check_eq(interperted_vnodes, static_vnodes));
+}
+
+fn check_eq<'a>(a: &'a VNode<'a>, b: &'a VNode<'a>) -> bool {
+    match (a, b) {
+        (VNode::Text(t_a), VNode::Text(t_b)) => t_a.text == t_b.text,
+        (VNode::Element(e_a), VNode::Element(e_b)) => {
+            e_a.attributes
+                .iter()
+                .zip(e_b.attributes.iter())
+                .all(|(a, b)| {
+                    a.is_static == b.is_static
+                        && a.is_volatile == b.is_volatile
+                        && a.name == b.name
+                        && a.value == b.value
+                        && a.namespace == b.namespace
+                })
+                && e_a
+                    .children
+                    .iter()
+                    .zip(e_b.children.iter())
+                    .all(|(a, b)| check_eq(a, b))
+                && e_a.key == e_b.key
+                && e_a.tag == e_b.tag
+                && e_a.namespace == e_b.namespace
+                && e_a
+                    .listeners
+                    .iter()
+                    .zip(e_b.listeners.iter())
+                    .all(|(a, b)| a.event == b.event)
+        }
+        (VNode::Fragment(f_a), VNode::Fragment(f_b)) => {
+            f_a.key == f_b.key
+                && f_a
+                    .children
+                    .iter()
+                    .zip(f_b.children.iter())
+                    .all(|(a, b)| check_eq(a, b))
+        }
+        (VNode::Component(c_a), VNode::Component(c_b)) => {
+            c_a.can_memoize == c_b.can_memoize
+                && c_a.key == c_b.key
+                && c_a.fn_name == c_b.fn_name
+                && c_a.user_fc == c_b.user_fc
+        }
+        (VNode::Placeholder(_), VNode::Placeholder(_)) => true,
+        _ => false,
+    }
+}