lifecycle.rs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #![allow(unused, non_upper_case_globals)]
  2. #![allow(non_snake_case)]
  3. //! Tests for the lifecycle of components.
  4. use dioxus::{core_macro::rsx_without_templates, prelude::*};
  5. use dioxus_core::DomEdit::*;
  6. use std::sync::{Arc, Mutex};
  7. type Shared<T> = Arc<Mutex<T>>;
  8. #[test]
  9. fn manual_diffing() {
  10. struct AppProps {
  11. value: Shared<&'static str>,
  12. }
  13. static App: Component<AppProps> = |cx| {
  14. let val = cx.props.value.lock().unwrap();
  15. cx.render(rsx_without_templates! { div { "{val}" } })
  16. };
  17. let value = Arc::new(Mutex::new("Hello"));
  18. let mut dom = VirtualDom::new_with_props(App, AppProps { value: value.clone() });
  19. let _ = dom.rebuild();
  20. *value.lock().unwrap() = "goodbye";
  21. let edits = dom.rebuild();
  22. println!("edits: {:?}", edits);
  23. }
  24. #[test]
  25. fn events_generate() {
  26. fn app(cx: Scope) -> Element {
  27. let count = cx.use_hook(|| 0);
  28. let inner = match *count {
  29. 0 => {
  30. rsx_without_templates! {
  31. div {
  32. onclick: move |_| *count += 1,
  33. div {
  34. "nested"
  35. }
  36. "Click me!"
  37. }
  38. }
  39. }
  40. _ => todo!(),
  41. };
  42. cx.render(inner)
  43. };
  44. let mut dom = VirtualDom::new(app);
  45. let mut channel = dom.get_scheduler_channel();
  46. assert!(dom.has_work());
  47. let edits = dom.rebuild();
  48. assert_eq!(
  49. edits.edits,
  50. [
  51. CreateElement { root: Some(1), tag: "div", children: 0 },
  52. NewEventListener { event_name: "click", scope: ScopeId(0), root: Some(1) },
  53. CreateElement { root: Some(2), tag: "div", children: 0 },
  54. CreateTextNode { root: Some(3), text: "nested" },
  55. AppendChildren { root: Some(2), children: vec![3] },
  56. CreateTextNode { root: Some(4), text: "Click me!" },
  57. AppendChildren { root: Some(1), children: vec![2, 4] },
  58. AppendChildren { root: Some(0), children: vec![1] }
  59. ]
  60. )
  61. }
  62. #[test]
  63. fn components_generate() {
  64. fn app(cx: Scope) -> Element {
  65. let render_phase = cx.use_hook(|| 0);
  66. *render_phase += 1;
  67. cx.render(match *render_phase {
  68. 1 => rsx_without_templates!("Text0"),
  69. 2 => rsx_without_templates!(div {}),
  70. 3 => rsx_without_templates!("Text2"),
  71. 4 => rsx_without_templates!(Child {}),
  72. 5 => rsx_without_templates!({ None as Option<()> }),
  73. 6 => rsx_without_templates!("text 3"),
  74. 7 => rsx_without_templates!({ (0..2).map(|f| rsx_without_templates!("text {f}")) }),
  75. 8 => rsx_without_templates!(Child {}),
  76. _ => todo!(),
  77. })
  78. };
  79. fn Child(cx: Scope) -> Element {
  80. println!("Running child");
  81. cx.render(rsx_without_templates! {
  82. h1 {}
  83. })
  84. }
  85. let mut dom = VirtualDom::new(app);
  86. let edits = dom.rebuild();
  87. assert_eq!(
  88. edits.edits,
  89. [
  90. CreateTextNode { root: Some(1), text: "Text0" },
  91. AppendChildren { root: Some(0), children: vec![1] }
  92. ]
  93. );
  94. assert_eq!(
  95. dom.hard_diff(ScopeId(0)).edits,
  96. [
  97. CreateElement { root: Some(2), tag: "div", children: 0 },
  98. ReplaceWith { root: Some(1), nodes: vec![2] }
  99. ]
  100. );
  101. assert_eq!(
  102. dom.hard_diff(ScopeId(0)).edits,
  103. [
  104. CreateTextNode { root: Some(1), text: "Text2" },
  105. ReplaceWith { root: Some(2), nodes: vec![1] }
  106. ]
  107. );
  108. // child {}
  109. assert_eq!(
  110. dom.hard_diff(ScopeId(0)).edits,
  111. [
  112. CreateElement { root: Some(2), tag: "h1", children: 0 },
  113. ReplaceWith { root: Some(1), nodes: vec![2] }
  114. ]
  115. );
  116. // placeholder
  117. assert_eq!(
  118. dom.hard_diff(ScopeId(0)).edits,
  119. [
  120. CreatePlaceholder { root: Some(1) },
  121. ReplaceWith { root: Some(2), nodes: vec![1] }
  122. ]
  123. );
  124. assert_eq!(
  125. dom.hard_diff(ScopeId(0)).edits,
  126. [
  127. CreateTextNode { root: Some(2), text: "text 3" },
  128. ReplaceWith { root: Some(1), nodes: vec![2] }
  129. ]
  130. );
  131. assert_eq!(
  132. dom.hard_diff(ScopeId(0)).edits,
  133. [
  134. CreateTextNode { text: "text 0", root: Some(1) },
  135. CreateTextNode { text: "text 1", root: Some(3) },
  136. ReplaceWith { root: Some(2), nodes: vec![1, 3] },
  137. ]
  138. );
  139. assert_eq!(
  140. dom.hard_diff(ScopeId(0)).edits,
  141. [
  142. CreateElement { tag: "h1", root: Some(2), children: 0 },
  143. ReplaceWith { root: Some(1), nodes: vec![2] },
  144. Remove { root: Some(3) },
  145. ]
  146. );
  147. }
  148. #[test]
  149. fn component_swap() {
  150. fn app(cx: Scope) -> Element {
  151. let render_phase = cx.use_hook(|| 0);
  152. *render_phase += 1;
  153. cx.render(match *render_phase {
  154. 0 => rsx_without_templates!(
  155. div {
  156. NavBar {}
  157. Dashboard {}
  158. }
  159. ),
  160. 1 => rsx_without_templates!(
  161. div {
  162. NavBar {}
  163. Results {}
  164. }
  165. ),
  166. 2 => rsx_without_templates!(
  167. div {
  168. NavBar {}
  169. Dashboard {}
  170. }
  171. ),
  172. 3 => rsx_without_templates!(
  173. div {
  174. NavBar {}
  175. Results {}
  176. }
  177. ),
  178. 4 => rsx_without_templates!(
  179. div {
  180. NavBar {}
  181. Dashboard {}
  182. }
  183. ),
  184. _ => rsx_without_templates!("blah"),
  185. })
  186. };
  187. static NavBar: Component = |cx| {
  188. println!("running navbar");
  189. cx.render(rsx_without_templates! {
  190. h1 {
  191. "NavBar"
  192. {(0..3).map(|f| rsx_without_templates!(NavLink {}))}
  193. }
  194. })
  195. };
  196. static NavLink: Component = |cx| {
  197. println!("running navlink");
  198. cx.render(rsx_without_templates! {
  199. h1 {
  200. "NavLink"
  201. }
  202. })
  203. };
  204. static Dashboard: Component = |cx| {
  205. println!("running dashboard");
  206. cx.render(rsx_without_templates! {
  207. div {
  208. "dashboard"
  209. }
  210. })
  211. };
  212. static Results: Component = |cx| {
  213. println!("running results");
  214. cx.render(rsx_without_templates! {
  215. div {
  216. "results"
  217. }
  218. })
  219. };
  220. let mut dom = VirtualDom::new(app);
  221. let edits = dom.rebuild();
  222. dbg!(&edits);
  223. let edits = dom.work_with_deadline(|| false);
  224. dbg!(&edits);
  225. let edits = dom.work_with_deadline(|| false);
  226. dbg!(&edits);
  227. let edits = dom.work_with_deadline(|| false);
  228. dbg!(&edits);
  229. let edits = dom.work_with_deadline(|| false);
  230. dbg!(&edits);
  231. }