mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-23 12:43:08 +00:00
wip: more tests!
This commit is contained in:
parent
662f58c8bc
commit
9c4abcbea0
10 changed files with 203 additions and 26 deletions
|
@ -194,7 +194,7 @@ mod field_info {
|
|||
// children field is automatically defaulted to None
|
||||
if name == "children" {
|
||||
builder_attr.default =
|
||||
Some(syn::parse(quote!(Default::default()).into()).unwrap());
|
||||
Some(syn::parse(quote!(::dioxus::core::VNode::empty()).into()).unwrap());
|
||||
}
|
||||
|
||||
// auto detect optional
|
||||
|
|
|
@ -157,6 +157,28 @@ impl<'b: 'static> VirtualDom {
|
|||
|
||||
/// Insert a new template into the VirtualDom's template registry
|
||||
fn register_template(&mut self, template: &'b VNode<'b>) {
|
||||
// First, make sure we mark the template as seen, regardless if we process it
|
||||
self.templates
|
||||
.insert(template.template.id, template.template);
|
||||
|
||||
// If it's all dynamic nodes, then we don't need to register it
|
||||
// Quickly run through and see if it's all just dynamic nodes
|
||||
let dynamic_roots = template
|
||||
.template
|
||||
.roots
|
||||
.iter()
|
||||
.filter(|root| {
|
||||
matches!(
|
||||
root,
|
||||
TemplateNode::Dynamic(_) | TemplateNode::DynamicText(_)
|
||||
)
|
||||
})
|
||||
.count();
|
||||
|
||||
if dynamic_roots == template.template.roots.len() {
|
||||
return;
|
||||
}
|
||||
|
||||
for node in template.template.roots {
|
||||
self.create_static_node(template, node);
|
||||
}
|
||||
|
@ -165,9 +187,6 @@ impl<'b: 'static> VirtualDom {
|
|||
name: template.template.id,
|
||||
m: template.template.roots.len(),
|
||||
});
|
||||
|
||||
self.templates
|
||||
.insert(template.template.id, template.template);
|
||||
}
|
||||
|
||||
pub(crate) fn create_static_node(
|
||||
|
|
|
@ -155,8 +155,7 @@ impl<'a, 'b> IntoDynNode<'a> for () {
|
|||
}
|
||||
impl<'a, 'b> IntoDynNode<'a> for VNode<'a> {
|
||||
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||
// DynamicNode::Fragment { nodes: cx., inner: () }
|
||||
todo!()
|
||||
DynamicNode::Fragment(VFragment::NonEmpty(_cx.bump().alloc([self])))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,6 +168,15 @@ impl<'a, 'b, T: IntoDynNode<'a>> IntoDynNode<'a> for Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoDynNode<'a> for &Element<'a> {
|
||||
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||
match self.as_ref() {
|
||||
Ok(val) => val.clone().into_vnode(_cx),
|
||||
_ => DynamicNode::Fragment(VFragment::Empty(Cell::new(ElementId(0)))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> IntoDynNode<'a> for LazyNodes<'a, 'b> {
|
||||
fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||
DynamicNode::Fragment(VFragment::NonEmpty(cx.bump().alloc([self.call(cx)])))
|
||||
|
|
|
@ -15,6 +15,25 @@ impl<'a> Mutations<'a> {
|
|||
template_mutations: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A useful tool for testing mutations
|
||||
///
|
||||
/// Rewrites IDs to just be "template", so you can compare the mutations
|
||||
pub fn santize(mut self) -> Self {
|
||||
for edit in self
|
||||
.template_mutations
|
||||
.iter_mut()
|
||||
.chain(self.edits.iter_mut())
|
||||
{
|
||||
match edit {
|
||||
Mutation::LoadTemplate { name, .. } => *name = "template",
|
||||
Mutation::SaveTemplate { name, .. } => *name = "template",
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::ops::Deref for Mutations<'a> {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{any_props::AnyProps, arena::ElementId, ScopeId, ScopeState, UiEvent};
|
||||
use crate::{any_props::AnyProps, arena::ElementId, Element, ScopeId, ScopeState, UiEvent};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::{Cell, RefCell},
|
||||
|
@ -10,7 +10,7 @@ pub type TemplateId = &'static str;
|
|||
///
|
||||
/// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
|
||||
/// static parts of the template.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VNode<'a> {
|
||||
// The ID assigned for the root of this template
|
||||
pub node_id: Cell<ElementId>,
|
||||
|
@ -47,6 +47,23 @@ impl<'a> VNode<'a> {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn empty() -> Element<'a> {
|
||||
Ok(VNode {
|
||||
node_id: Cell::new(ElementId(0)),
|
||||
key: None,
|
||||
parent: None,
|
||||
root_ids: &[],
|
||||
dynamic_nodes: &[],
|
||||
dynamic_attrs: &[],
|
||||
template: Template {
|
||||
id: "dioxus-empty",
|
||||
roots: &[],
|
||||
node_paths: &[],
|
||||
attr_paths: &[],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn template_from_dynamic_node(
|
||||
cx: &'a ScopeState,
|
||||
node: DynamicNode<'a>,
|
||||
|
|
|
@ -20,7 +20,7 @@ impl VirtualDom {
|
|||
pub(super) fn new_scope(&mut self, props: *const dyn AnyProps<'static>) -> &mut ScopeState {
|
||||
let parent = self.acquire_current_scope_raw();
|
||||
let entry = self.scopes.vacant_entry();
|
||||
let height = unsafe { parent.map(|f| (*f).height).unwrap_or(0) + 1 };
|
||||
let height = unsafe { parent.map(|f| (*f).height + 1).unwrap_or(0) };
|
||||
let id = ScopeId(entry.key());
|
||||
|
||||
entry.insert(ScopeState {
|
||||
|
|
|
@ -120,8 +120,27 @@ impl ScopeState {
|
|||
/// Get a handle to the currently active head node arena for this Scope
|
||||
///
|
||||
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
|
||||
///
|
||||
/// Panics if the tree has not been built yet.
|
||||
pub fn root_node<'a>(&'a self) -> &'a RenderReturn<'a> {
|
||||
let r: &RenderReturn = unsafe { &*self.current_frame().node.get() };
|
||||
self.try_root_node()
|
||||
.expect("The tree has not been built yet. Make sure to call rebuild on the tree before accessing its nodes.")
|
||||
}
|
||||
|
||||
/// Try to get a handle to the currently active head node arena for this Scope
|
||||
///
|
||||
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
|
||||
///
|
||||
/// Returns [`None`] if the tree has not been built yet.
|
||||
pub fn try_root_node<'a>(&'a self) -> Option<&'a RenderReturn<'a>> {
|
||||
let ptr = self.current_frame().node.get();
|
||||
|
||||
if ptr.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let r: &RenderReturn = unsafe { &*ptr };
|
||||
|
||||
unsafe { std::mem::transmute(r) }
|
||||
}
|
||||
|
||||
|
|
101
packages/core/tests/passthru.rs
Normal file
101
packages/core/tests/passthru.rs
Normal file
|
@ -0,0 +1,101 @@
|
|||
use dioxus::core::Mutation::*;
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_core::ElementId;
|
||||
|
||||
/// 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<'a>, children: Element<'a>) -> Element {
|
||||
cx.render(rsx! { children })
|
||||
}
|
||||
|
||||
let mut dom = VirtualDom::new(app);
|
||||
let edits = dom.rebuild().santize();
|
||||
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0 },
|
||||
AppendChildren { m: 1 },
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
/// Should load all the templates and append them
|
||||
#[test]
|
||||
fn nested_passthru_creates_add() {
|
||||
fn app(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
child_comp {
|
||||
"1"
|
||||
child_comp {
|
||||
"2"
|
||||
child_comp {
|
||||
"3"
|
||||
div {
|
||||
"hi"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline_props]
|
||||
fn child_comp<'a>(cx: Scope, children: Element<'a>) -> Element {
|
||||
cx.render(rsx! { children })
|
||||
}
|
||||
|
||||
let mut dom = VirtualDom::new(app);
|
||||
|
||||
assert_eq!(
|
||||
dom.rebuild().santize().edits,
|
||||
[
|
||||
LoadTemplate { name: "template", index: 0 },
|
||||
LoadTemplate { name: "template", index: 0 },
|
||||
LoadTemplate { name: "template", index: 0 },
|
||||
LoadTemplate { name: "template", index: 1 },
|
||||
AppendChildren { m: 4 },
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dynamic_node_as_root() {
|
||||
fn app(cx: Scope) -> Element {
|
||||
let a = 123;
|
||||
let b = 456;
|
||||
cx.render(rsx! { "{a}" "{b}" })
|
||||
}
|
||||
|
||||
let mut dom = VirtualDom::new(app);
|
||||
let edits = dom.rebuild().santize();
|
||||
|
||||
// Since the roots were all dynamic, they should not cause any template muations
|
||||
assert_eq!(edits.template_mutations, []);
|
||||
|
||||
// The root node is text, so we just create it on the spot
|
||||
assert_eq!(
|
||||
edits.edits,
|
||||
[
|
||||
CreateTextNode { value: "123", id: ElementId(1) },
|
||||
CreateTextNode { value: "456", id: ElementId(2) },
|
||||
AppendChildren { m: 2 }
|
||||
]
|
||||
)
|
||||
}
|
|
@ -1,15 +1,21 @@
|
|||
//! Tests related to safety of the library.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_core::SuspenseContext;
|
||||
|
||||
/// Ensure no issues with not building the virtualdom before
|
||||
/// Ensure no issues with not calling rebuild
|
||||
#[test]
|
||||
fn root_node_isnt_null() {
|
||||
let dom = VirtualDom::new(|cx| render!("Hello world!"));
|
||||
|
||||
let scope = dom.base_scope();
|
||||
|
||||
// The root should be a valid pointer
|
||||
assert_ne!(scope.root_node() as *const _, std::ptr::null_mut());
|
||||
// We haven't built the tree, so trying to get out the root node should fail
|
||||
assert!(scope.try_root_node().is_none());
|
||||
|
||||
// There should be no way to gain an invalid pointer
|
||||
assert!(scope.current_frame().node.get().is_null());
|
||||
assert!(scope.previous_frame().node.get().is_null());
|
||||
|
||||
// The height should be 0
|
||||
assert_eq!(scope.height(), 0);
|
||||
|
@ -18,13 +24,3 @@ fn root_node_isnt_null() {
|
|||
// todo: there should also be a default error boundary
|
||||
assert!(scope.has_context::<SuspenseContext>().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn elements() {
|
||||
let mut dom = VirtualDom::new(|cx| {
|
||||
//
|
||||
cx.render(rsx!( div { "Hello world!" } ))
|
||||
});
|
||||
|
||||
let muts = dom.rebuild();
|
||||
}
|
|
@ -171,9 +171,7 @@ impl ToTokens for Component {
|
|||
|
||||
toks.append_all(quote! {
|
||||
.children(
|
||||
Some({
|
||||
#renderer
|
||||
})
|
||||
Ok({ #renderer })
|
||||
)
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue