|
@@ -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,
|
|
|
+ }
|
|
|
+}
|