diff --git a/packages/core/src/create.rs b/packages/core/src/create.rs index 8dc3bff40..b0c1a10a9 100644 --- a/packages/core/src/create.rs +++ b/packages/core/src/create.rs @@ -216,12 +216,10 @@ impl<'b: 'static> VirtualDom { ) { match *node { // Todo: create the children's template - TemplateNode::Dynamic(_) => { - let id = ElementId(0); - self.mutations - .template_mutations - .push(CreatePlaceholder { id }) - } + TemplateNode::Dynamic(_) => self + .mutations + .template_mutations + .push(CreateStaticPlaceholder {}), TemplateNode::Text(value) => self .mutations .template_mutations @@ -236,15 +234,20 @@ impl<'b: 'static> VirtualDom { children, namespace, tag, - inner_opt, + .. } => { - let id = ElementId(0); - - self.mutations.template_mutations.push(CreateElement { - name: tag, - namespace, - id, - }); + if let Some(namespace) = namespace { + self.mutations + .template_mutations + .push(CreateElementNamespace { + name: tag, + namespace, + }); + } else { + self.mutations + .template_mutations + .push(CreateElement { name: tag }); + } self.mutations .template_mutations @@ -254,16 +257,15 @@ impl<'b: 'static> VirtualDom { value, namespace, .. - } => Some(SetAttribute { + } => Some(SetStaticAttribute { name, value, - id, ns: *namespace, }), _ => None, })); - if children.is_empty() && inner_opt { + if children.is_empty() { return; } diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 4b98ac978..98d7dc56b 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -239,8 +239,72 @@ impl<'b: 'static> VirtualDom { // // Remove the old nodes, except for one self.remove_nodes(&l[1..]); - let id = self.next_element(&l[0], &[]); - // self.replace(left_template, right_template, left, right) + let first = self.find_first_element(&l[0]); + self.remove_all_but_first_node(&l[0]); + + let placeholder = self.next_element(&l[0], &[]); + r.set(placeholder); + self.mutations + .push(Mutation::CreatePlaceholder { id: placeholder }); + self.mutations + .push(Mutation::ReplaceWith { id: first, m: 1 }) + } + + // Remove all the top-level nodes, returning the + fn remove_all_but_first_node(&mut self, node: &'b VNode<'b>) { + match &node.template.roots[0] { + TemplateNode::Text(_) | TemplateNode::Element { .. } => {} + TemplateNode::DynamicText(id) | TemplateNode::Dynamic(id) => { + match &node.dynamic_nodes[*id] { + Text(_) => {} + Fragment(VFragment::Empty(_)) => {} + Fragment(VFragment::NonEmpty(nodes)) => { + self.remove_all_but_first_node(&nodes[0]); + self.remove_nodes(&nodes[1..]); + } + Component(comp) => { + let scope = comp.scope.get().unwrap(); + match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } { + RenderReturn::Sync(Ok(t)) => self.remove_all_but_first_node(t), + _ => todo!("cannot handle nonstandard nodes"), + }; + } + }; + } + } + + // Just remove the rest + for (idx, _) in node.template.roots.iter().enumerate().skip(1) { + self.remove_root_node(node, idx); + } + } + + fn remove_root_node(&mut self, node: &'b VNode<'b>, idx: usize) { + let root = node.template.roots[idx]; + match root { + TemplateNode::Element { .. } | TemplateNode::Text(_) => { + self.mutations.push(Mutation::Remove { + id: node.root_ids[idx].get(), + }) + } + + TemplateNode::DynamicText(id) | TemplateNode::Dynamic(id) => { + match &node.dynamic_nodes[id] { + Text(i) => self.mutations.push(Mutation::Remove { id: i.id.get() }), + Fragment(VFragment::Empty(e)) => { + self.mutations.push(Mutation::Remove { id: e.get() }) + } + Fragment(VFragment::NonEmpty(nodes)) => self.remove_nodes(nodes), + Component(comp) => { + let scope = comp.scope.get().unwrap(); + match unsafe { self.scopes[scope.0].root_node().extend_lifetime_ref() } { + RenderReturn::Sync(Ok(t)) => self.remove_node(t), + _ => todo!("cannot handle nonstandard nodes"), + }; + } + }; + } + } } fn diff_non_empty_fragment(&mut self, new: &'b [VNode<'b>], old: &'b [VNode<'b>]) { diff --git a/packages/core/src/fragment.rs b/packages/core/src/fragment.rs index c044daed4..f2e539d17 100644 --- a/packages/core/src/fragment.rs +++ b/packages/core/src/fragment.rs @@ -96,7 +96,7 @@ impl<'a> Properties for FragmentProps<'a> { type Builder = FragmentBuilder<'a, false>; const IS_STATIC: bool = false; fn builder() -> Self::Builder { - todo!() + FragmentBuilder(VNode::empty()) } unsafe fn memoize(&self, _other: &Self) -> bool { false diff --git a/packages/core/src/mutations.rs b/packages/core/src/mutations.rs index 94cd00d70..3627836df 100644 --- a/packages/core/src/mutations.rs +++ b/packages/core/src/mutations.rs @@ -72,13 +72,15 @@ pub enum Mutation<'a> { CreateElement { name: &'a str, - namespace: Option<&'a str>, - id: ElementId, }, - + CreateElementNamespace { + name: &'a str, + namespace: &'a str, + }, CreatePlaceholder { id: ElementId, }, + CreateStaticPlaceholder, CreateStaticText { value: &'a str, }, @@ -142,6 +144,12 @@ pub enum Mutation<'a> { ns: Option<&'a str>, }, + SetStaticAttribute { + name: &'a str, + value: &'a str, + ns: Option<&'a str>, + }, + SetBoolAttribute { name: &'a str, value: bool, diff --git a/packages/core/tests/boolattrs.rs b/packages/core/tests/boolattrs.rs new file mode 100644 index 000000000..5187e00f9 --- /dev/null +++ b/packages/core/tests/boolattrs.rs @@ -0,0 +1,15 @@ +use dioxus::core::{ElementId, Mutation::*}; +use dioxus::prelude::*; + +#[test] +fn bool_test() { + let mut app = VirtualDom::new(|cx| cx.render(rsx!(div { hidden: false }))); + assert_eq!( + app.rebuild().santize().edits, + [ + LoadTemplate { name: "template", index: 0, id: ElementId(1) }, + SetBoolAttribute { name: "hidden", value: false, id: ElementId(1,) }, + AppendChildren { m: 1 }, + ] + ) +} diff --git a/packages/core/tests/borrowedstate.rs b/packages/core/tests/borrowedstate.rs new file mode 100644 index 000000000..3da5c8d94 --- /dev/null +++ b/packages/core/tests/borrowedstate.rs @@ -0,0 +1,55 @@ +#![allow(non_snake_case)] + +use dioxus::core::{ElementId, Mutation::*}; +use dioxus::prelude::*; + +#[test] +fn test_borrowed_state() { + let mut dom = VirtualDom::new(Parent); + + assert_eq!( + dom.rebuild().santize().edits, + [ + LoadTemplate { name: "template", index: 0, id: ElementId(1,) }, + LoadTemplate { name: "template", index: 0, id: ElementId(2,) }, + LoadTemplate { name: "template", index: 0, id: ElementId(3,) }, + HydrateText { path: &[0,], value: "Hello w1!", id: ElementId(4,) }, + ReplacePlaceholder { path: &[1,], m: 1 }, + ReplacePlaceholder { path: &[0,], m: 1 }, + AppendChildren { m: 1 }, + ] + ) +} + +fn Parent(cx: Scope) -> Element { + let w1 = cx.use_hook(|| String::from("w1")); + + cx.render(rsx! { + div { + Child { name: w1 } + } + }) +} + +#[derive(Props)] +struct ChildProps<'a> { + name: &'a str, +} + +fn Child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element { + cx.render(rsx! { + div { + h1 { "it's nested" } + Child2 { name: cx.props.name } + } + }) +} + +#[derive(Props)] +struct Grandchild<'a> { + name: &'a str, +} + +fn Child2<'a>(cx: Scope<'a, Grandchild<'a>>) -> Element { + cx.render(rsx!(div { "Hello {cx.props.name}!" })) +} diff --git a/packages/core/tests/create_dom.rs b/packages/core/tests/create_dom.rs new file mode 100644 index 000000000..98e7706bd --- /dev/null +++ b/packages/core/tests/create_dom.rs @@ -0,0 +1,217 @@ +#![allow(unused, non_upper_case_globals, non_snake_case)] + +//! Prove that the dom works normally through virtualdom methods. +//! +//! This methods all use "rebuild" which completely bypasses the scheduler. +//! Hard rebuilds don't consume any events from the event queue. + +use dioxus::core::Mutation::*; +use dioxus::prelude::*; +use dioxus_core::ElementId; + +#[test] +fn test_original_diff() { + let mut dom = VirtualDom::new(|cx| { + cx.render(rsx! { + div { + div { + "Hello, world!" + } + } + }) + }); + + let edits = dom.rebuild().santize(); + assert_eq!( + edits.template_mutations, + [ + // create template + CreateElement { name: "div" }, + CreateElement { name: "div" }, + CreateStaticText { value: "Hello, world!" }, + AppendChildren { m: 1 }, + AppendChildren { m: 1 }, + SaveTemplate { name: "template", m: 1 }, + ] + ); + + assert_eq!( + edits.edits, + [ + // add to root + LoadTemplate { name: "template", index: 0, id: ElementId(1) }, + AppendChildren { m: 1 } + ] + ) +} + +#[test] +fn create() { + let mut dom = VirtualDom::new(|cx| { + cx.render(rsx! { + div { + div { + "Hello, world!" + div { + div { + Fragment { + "hello" + "world" + } + } + } + } + } + }) + }); + + let edits = dom.rebuild().santize(); + assert_eq!( + edits.template_mutations, + [ + // create template + CreateElement { name: "div" }, + CreateElement { name: "div" }, + CreateStaticText { value: "Hello, world!" }, + CreateElement { name: "div" }, + CreateElement { name: "div" }, + CreateStaticPlaceholder {}, + AppendChildren { m: 1 }, + AppendChildren { m: 1 }, + AppendChildren { m: 2 }, + AppendChildren { m: 1 }, + SaveTemplate { name: "template", m: 1 }, + // The fragment child template + CreateStaticText { value: "hello" }, + CreateStaticText { value: "world" }, + SaveTemplate { name: "template", m: 2 }, + ] + ); +} + +#[test] +fn create_list() { + let mut dom = VirtualDom::new(|cx| { + cx.render(rsx! { + (0..3).map(|f| rsx!( div { "hello" } )) + }) + }); + + let edits = dom.rebuild().santize(); + assert_eq!( + edits.template_mutations, + [ + // create template + CreateElement { name: "div" }, + CreateStaticText { value: "hello" }, + AppendChildren { m: 1 }, + SaveTemplate { name: "template", m: 1 } + ] + ); +} + +#[test] +fn create_simple() { + let mut dom = VirtualDom::new(|cx| { + cx.render(rsx! { + div {} + div {} + div {} + div {} + }) + }); + + let edits = dom.rebuild().santize(); + assert_eq!( + edits.template_mutations, + [ + // create template + CreateElement { name: "div" }, + CreateElement { name: "div" }, + CreateElement { name: "div" }, + CreateElement { name: "div" }, + // add to root + SaveTemplate { name: "template", m: 4 } + ] + ); +} +#[test] +fn create_components() { + let mut dom = VirtualDom::new(|cx| { + cx.render(rsx! { + Child { "abc1" } + Child { "abc2" } + Child { "abc3" } + }) + }); + + #[derive(Props)] + struct ChildProps<'a> { + children: Element<'a>, + } + + fn Child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element { + cx.render(rsx! { + h1 {} + div { &cx.props.children } + p {} + }) + } + + let edits = dom.rebuild().santize(); + assert_eq!( + edits.template_mutations, + [ + // The "child" template + CreateElement { name: "h1" }, + CreateElement { name: "div" }, + CreateStaticPlaceholder {}, + AppendChildren { m: 1 }, + CreateElement { name: "p" }, + SaveTemplate { name: "template", m: 3 }, + // Sub template for component children + CreateStaticText { value: "abc1" }, + SaveTemplate { name: "template", m: 1 }, + CreateStaticText { value: "abc2" }, + SaveTemplate { name: "template", m: 1 }, + CreateStaticText { value: "abc3" }, + SaveTemplate { name: "template", m: 1 } + ] + ); +} + +#[test] +fn anchors() { + let mut dom = VirtualDom::new(|cx| { + cx.render(rsx! { + if true { + rsx!( div { "hello" } ) + } + if false { + rsx!( div { "goodbye" } ) + } + }) + }); + + // note that the template under "false" doesn't show up since it's not loaded + let edits = dom.rebuild().santize(); + assert_eq!( + edits.template_mutations, + [ + // create each template + CreateElement { name: "div" }, + CreateStaticText { value: "hello" }, + AppendChildren { m: 1 }, + SaveTemplate { m: 1, name: "template" }, + ] + ); + + assert_eq!( + edits.edits, + [ + LoadTemplate { name: "template", index: 0, id: ElementId(1) }, + CreatePlaceholder { id: ElementId(2) }, + AppendChildren { m: 2 } + ] + ) +} diff --git a/packages/core/tests/create_lists.rs b/packages/core/tests/create_lists.rs index 8297081be..5848bc788 100644 --- a/packages/core/tests/create_lists.rs +++ b/packages/core/tests/create_lists.rs @@ -31,18 +31,18 @@ fn list_renders() { edits.template_mutations, [ // Create the outer div - CreateElement { name: "div", namespace: None, id: ElementId(0) }, + CreateElement { name: "div" }, // todo: since this is the only child, we should just use // append when modify the values (IE no need for a placeholder) CreatePlaceholder { id: ElementId(0) }, AppendChildren { m: 1 }, SaveTemplate { name: "template", m: 1 }, // Create the inner template div - CreateElement { name: "div", namespace: None, id: ElementId(0) }, - CreateElement { name: "h1", namespace: None, id: ElementId(0) }, + CreateElement { name: "div" }, + CreateElement { name: "h1" }, CreateStaticText { value: "hello world! " }, AppendChildren { m: 1 }, - CreateElement { name: "p", namespace: None, id: ElementId(0) }, + CreateElement { name: "p" }, CreateStaticText { value: "d" }, AppendChildren { m: 1 }, AppendChildren { m: 2 }, diff --git a/packages/core/tests/diff_unkeyed_list.rs b/packages/core/tests/diff_unkeyed_list.rs index aabd38510..de7252f4e 100644 --- a/packages/core/tests/diff_unkeyed_list.rs +++ b/packages/core/tests/diff_unkeyed_list.rs @@ -73,10 +73,7 @@ fn list_creates_one_by_one() { #[test] fn removes_one_by_one() { let mut dom = VirtualDom::new(|cx| { - let gen = 3 - cx.generation(); - - println!("gen: {:?}", gen); - println!("range: {:?}", 0..gen); + let gen = 3 - cx.generation() % 4; cx.render(rsx! { div { @@ -123,12 +120,154 @@ fn removes_one_by_one() { ); // Remove div(1) and replace with a placeholder + // todo: this should just be a remove with no placeholder dom.mark_dirty_scope(ScopeId(0)); - assert_eq!(dom.render_immediate().santize().edits, []); + assert_eq!( + dom.render_immediate().santize().edits, + [ + CreatePlaceholder { id: ElementId(8) }, + ReplaceWith { id: ElementId(2), m: 1 } + ] + ); - // dom.mark_dirty_scope(ScopeId(0)); - // assert_eq!( - // dom.render_immediate().santize().edits, - // [Remove { id: ElementId(4) }] - // ); + // load the 3 and replace the placeholder + // todo: this should actually be append to, but replace placeholder is fine for now + dom.mark_dirty_scope(ScopeId(0)); + assert_eq!( + dom.render_immediate().santize().edits, + [ + LoadTemplate { name: "template", index: 0, id: ElementId(9) }, + HydrateText { path: &[0], value: "0", id: ElementId(10) }, + LoadTemplate { name: "template", index: 0, id: ElementId(11) }, + HydrateText { path: &[0], value: "1", id: ElementId(12) }, + LoadTemplate { name: "template", index: 0, id: ElementId(13) }, + HydrateText { path: &[0], value: "2", id: ElementId(14) }, + ReplaceWith { id: ElementId(8), m: 3 } + ] + ); +} + +#[test] +fn list_shrink_multiroot() { + let mut dom = VirtualDom::new(|cx| { + let gen = cx.generation(); + cx.render(rsx! { + div { + (0..gen).map(|i| rsx! { + div { "{i}" } + div { "{i}" } + }) + } + }) + }); + + assert_eq!( + dom.rebuild().santize().edits, + [ + LoadTemplate { name: "template", index: 0, id: ElementId(1,) }, + AssignId { path: &[0,], id: ElementId(2,) }, + AppendChildren { m: 1 } + ] + ); + + dom.mark_dirty_scope(ScopeId(0)); + assert_eq!( + dom.render_immediate().santize().edits, + [ + LoadTemplate { name: "template", index: 0, id: ElementId(3) }, + HydrateText { path: &[0], value: "0", id: ElementId(4) }, + LoadTemplate { name: "template", index: 1, id: ElementId(5) }, + HydrateText { path: &[0], value: "0", id: ElementId(6) }, + ReplaceWith { id: ElementId(2), m: 2 } + ] + ); + + dom.mark_dirty_scope(ScopeId(0)); + assert_eq!( + dom.render_immediate().santize().edits, + [ + LoadTemplate { name: "template", index: 0, id: ElementId(7) }, + HydrateText { path: &[0], value: "1", id: ElementId(8) }, + LoadTemplate { name: "template", index: 1, id: ElementId(9) }, + HydrateText { path: &[0], value: "1", id: ElementId(10) }, + InsertAfter { id: ElementId(5), m: 2 } + ] + ); + + dom.mark_dirty_scope(ScopeId(0)); + assert_eq!( + dom.render_immediate().santize().edits, + [ + LoadTemplate { name: "template", index: 0, id: ElementId(11) }, + HydrateText { path: &[0], value: "2", id: ElementId(12) }, + LoadTemplate { name: "template", index: 1, id: ElementId(13) }, + HydrateText { path: &[0], value: "2", id: ElementId(14) }, + InsertAfter { id: ElementId(9), m: 2 } + ] + ); +} + +#[test] +fn removes_one_by_one_multiroot() { + let mut dom = VirtualDom::new(|cx| { + let gen = 3 - cx.generation() % 4; + + cx.render(rsx! { + div { + (0..gen).map(|i| rsx! { + div { "{i}" } + div { "{i}" } + }) + } + }) + }); + + // load the div and then assign the empty fragment as a placeholder + assert_eq!( + dom.rebuild().santize().edits, + [ + LoadTemplate { name: "template", index: 0, id: ElementId(1) }, + // + LoadTemplate { name: "template", index: 0, id: ElementId(2) }, + HydrateText { path: &[0], value: "0", id: ElementId(3) }, + LoadTemplate { name: "template", index: 1, id: ElementId(4) }, + HydrateText { path: &[0], value: "0", id: ElementId(5) }, + // + LoadTemplate { name: "template", index: 0, id: ElementId(6) }, + HydrateText { path: &[0], value: "1", id: ElementId(7) }, + LoadTemplate { name: "template", index: 1, id: ElementId(8) }, + HydrateText { path: &[0], value: "1", id: ElementId(9) }, + // + LoadTemplate { name: "template", index: 0, id: ElementId(10) }, + HydrateText { path: &[0], value: "2", id: ElementId(11) }, + LoadTemplate { name: "template", index: 1, id: ElementId(12) }, + HydrateText { path: &[0], value: "2", id: ElementId(13) }, + // + ReplacePlaceholder { path: &[0], m: 6 }, + // + AppendChildren { m: 1 } + ] + ); + + dom.mark_dirty_scope(ScopeId(0)); + assert_eq!( + dom.render_immediate().santize().edits, + [Remove { id: ElementId(10) }, Remove { id: ElementId(12) }] + ); + + dom.mark_dirty_scope(ScopeId(0)); + assert_eq!( + dom.render_immediate().santize().edits, + [Remove { id: ElementId(6) }, Remove { id: ElementId(8) }] + ); + + dom.mark_dirty_scope(ScopeId(0)); + assert_eq!( + dom.render_immediate().santize().edits, + [ + Remove { id: ElementId(4) }, + CreatePlaceholder { id: ElementId(14) }, + ReplaceWith { id: ElementId(2), m: 1 } + ] + ); } diff --git a/packages/core/tests/suspend.rs b/packages/core/tests/suspend.rs index 21af95771..13e4359d3 100644 --- a/packages/core/tests/suspend.rs +++ b/packages/core/tests/suspend.rs @@ -14,7 +14,7 @@ async fn it_works() { assert_eq!( mutations.template_mutations, [ - CreateElement { name: "div", namespace: None, id: ElementId(0) }, + CreateElement { name: "div" }, CreateStaticText { value: "Waiting for child..." }, CreatePlaceholder { id: ElementId(0) }, AppendChildren { m: 2 }, diff --git a/packages/desktop/src/hot_reload.rs b/packages/desktop/src/hot_reload.rs index 5a6962d5d..e3faf3ef1 100644 --- a/packages/desktop/src/hot_reload.rs +++ b/packages/desktop/src/hot_reload.rs @@ -17,11 +17,11 @@ pub(crate) fn init(dom: &VirtualDom) { // connect to processes for incoming data std::thread::spawn(move || { - if let Ok(listener) = LocalSocketListener::bind("@dioxusin") { - for conn in listener.incoming().filter_map(handle_error) { - *latest_in_connection_handle.lock().unwrap() = Some(BufReader::new(conn)); - } - } + // if let Ok(listener) = LocalSocketListener::bind("@dioxusin") { + // for conn in listener.incoming().filter_map(handle_error) { + // *latest_in_connection_handle.lock().unwrap() = Some(BufReader::new(conn)); + // } + // } }); std::thread::spawn(move || { diff --git a/packages/dioxus/tests/boolattrs.rs b/packages/dioxus/tests/boolattrs.rs deleted file mode 100644 index 4c1834ba0..000000000 --- a/packages/dioxus/tests/boolattrs.rs +++ /dev/null @@ -1,24 +0,0 @@ -fn component(cx: Scope) -> Element { - cx.render(rsx! { - div { hidden: false } - }) -} - -use dioxus::prelude::*; - -#[test] -fn bool_test() { - let mut app = VirtualDom::new(component); - let edits = app.rebuild(); - - use dioxus_core::{ElementId, Mutation::*}; - assert_eq!( - edits.edits, - vec![ - LoadTemplate { name: "packages/dioxus/tests/boolattrs.rs:2:15:66", index: 0 }, - AssignId { path: &[], id: ElementId(2,) }, - SetBoolAttribute { name: "hidden", value: false, id: ElementId(2,) }, - AppendChildren { m: 1 }, - ] - ) -} diff --git a/packages/dioxus/tests/borrowedstate.rs b/packages/dioxus/tests/borrowedstate.rs deleted file mode 100644 index 905339793..000000000 --- a/packages/dioxus/tests/borrowedstate.rs +++ /dev/null @@ -1,46 +0,0 @@ -#![allow(non_snake_case)] - -use dioxus::prelude::*; - -#[test] -fn test_borrowed_state() { - let _ = VirtualDom::new(Parent); -} - -fn Parent(cx: Scope) -> Element { - let value = cx.use_hook(String::new); - - cx.render(rsx! { - div { - Child { name: value } - Child { name: value } - Child { name: value } - Child { name: value } - } - }) -} - -#[derive(Props)] -struct ChildProps<'a> { - name: &'a str, -} - -fn Child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element { - cx.render(rsx! { - div { - h1 { "it's nested" } - Child2 { name: cx.props.name } - } - }) -} - -#[derive(Props)] -struct Grandchild<'a> { - name: &'a str, -} - -fn Child2<'a>(cx: Scope<'a, Grandchild<'a>>) -> Element { - cx.render(rsx! { - div { "Hello {cx.props.name}!" } - }) -} diff --git a/packages/dioxus/tests/passthru.rs b/packages/dioxus/tests/passthru.rs deleted file mode 100644 index ee9667c97..000000000 --- a/packages/dioxus/tests/passthru.rs +++ /dev/null @@ -1,100 +0,0 @@ -#![allow(unused, non_upper_case_globals)] -#![allow(non_snake_case)] - -//! Diffing Tests -//! -//! These tests only verify that the diffing algorithm works properly for single components. -//! -//! It does not validated that component lifecycles work properly. This is done in another test file. - -use dioxus::prelude::*; - -fn new_dom() -> VirtualDom { - VirtualDom::new(|cx| render!("hi")) -} - -use dioxus_core::DomEdit::*; - -/// Should push the text node onto the stack and modify it -#[test] -fn nested_passthru_creates() { - fn app(cx: Scope) -> Element { - cx.render(rsx! { - Child { - Child { - Child { - div { - "hi" - } - } - } - } - }) - }; - - #[inline_props] - fn Child<'a>(cx: Scope, children: Element<'a>) -> Element { - cx.render(rsx! { children }) - }; - - let mut dom = VirtualDom::new(app); - let mut channel = dom.get_scheduler_channel(); - assert!(dom.has_work()); - - let edits = dom.rebuild(); - assert_eq!( - edits.edits, - [ - CreateElement { tag: "div", root: Some(1), children: 0 }, - CreateTextNode { text: "hi", root: Some(2) }, - AppendChildren { root: Some(1), children: vec![2] }, - AppendChildren { root: Some(0), children: vec![1] }, - ] - ) -} - -/// Should push the text node onto the stack and modify it -#[test] -fn nested_passthru_creates_add() { - fn app(cx: Scope) -> Element { - cx.render(rsx! { - Child { - "1" - Child { - "2" - Child { - "3" - div { - "hi" - } - } - } - } - }) - }; - - #[inline_props] - fn Child<'a>(cx: Scope, children: Element<'a>) -> Element { - cx.render(rsx! { - children - }) - }; - - let mut dom = VirtualDom::new(app); - let mut channel = dom.get_scheduler_channel(); - assert!(dom.has_work()); - - let edits = dom.rebuild(); - assert_eq!( - edits.edits, - [ - CreateTextNode { text: "1", root: Some(1) }, - CreateTextNode { text: "2", root: Some(2) }, - CreateTextNode { text: "3", root: Some(3) }, - CreateElement { tag: "div", root: Some(4), children: 0 }, - CreateTextNode { text: "hi", root: Some(5) }, - AppendChildren { root: Some(4), children: vec![5] }, - AppendChildren { root: Some(0), children: vec![1, 2, 3, 4] }, - ] - ) -} diff --git a/packages/dioxus/tests/scheduler.rs b/packages/dioxus/tests/scheduler.rs deleted file mode 100644 index f058dfe4c..000000000 --- a/packages/dioxus/tests/scheduler.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! Tests for the scheduler. -//! -//! TODO -//! - priority lanes -//! - periodic checking -//! diff --git a/packages/dioxus/tests/suspense.rs b/packages/dioxus/tests/suspense.rs index 1d29df962..7e539c423 100644 --- a/packages/dioxus/tests/suspense.rs +++ b/packages/dioxus/tests/suspense.rs @@ -1,18 +1,37 @@ -use std::future::IntoFuture; +use std::{future::IntoFuture, rc::Rc}; use dioxus::prelude::*; use dioxus_core::SuspenseBoundary; +#[tokio::test] +async fn basic_prints() { + let mut dom = VirtualDom::new(|cx| { + cx.render(rsx! { + div { + h1 { "var" } + suspense_boundary { + basic_child { } + async_child { } + } + } + }) + }); + + dbg!(dom.rebuild()); + + dom.wait_for_work().await; + + dbg!(dom.rebuild()); +} + #[inline_props] fn suspense_boundary<'a>(cx: Scope<'a>, children: Element<'a>) -> Element { - cx.use_hook(|| cx.provide_context(SuspenseBoundary::new(cx.scope_id()))); + cx.use_hook(|| cx.provide_context(Rc::new(SuspenseBoundary::new(cx.scope_id())))); cx.render(rsx! { children }) } fn basic_child(cx: Scope) -> Element { - cx.render(rsx! { - div { "basic child 1" } - }) + cx.render(rsx!( div { "basic child 1" } )) } async fn async_child(cx: Scope<'_>) -> Element { @@ -38,28 +57,5 @@ async fn async_child(cx: Scope<'_>) -> Element { let (username, age) = tokio::join!(username.into_future(), age.into_future()); - cx.render(rsx!( - div { "Hello! {username}, you are {age}, {_user} {_age}" } - )) -} - -#[tokio::test] -async fn basic_prints() { - let mut dom = VirtualDom::new(|cx| { - cx.render(rsx! { - div { - h1 { "var" } - suspense_boundary { - basic_child { } - async_child { } - } - } - }) - }); - - dbg!(dom.rebuild()); - - dom.wait_for_work().await; - - dbg!(dom.rebuild()); + cx.render(rsx!( div { "Hello! {username}, you are {age}, {_user} {_age}" } )) }