render.rs 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. use dioxus::prelude::*;
  2. #[test]
  3. #[allow(non_snake_case)]
  4. fn render_basic() {
  5. fn Base(cx: Scope) -> Element {
  6. rsx!(cx, div {})
  7. }
  8. let dom = VirtualDom::new(Base);
  9. let static_vnodes = rsx!(div{"hello world"});
  10. let location = CodeLocation {
  11. file_path: String::new(),
  12. crate_path: String::new(),
  13. line: 0,
  14. column: 0,
  15. };
  16. let empty_context = CapturedContext {
  17. captured: IfmtArgs {
  18. named_args: Vec::new(),
  19. },
  20. components: Vec::new(),
  21. iterators: Vec::new(),
  22. expressions: Vec::new(),
  23. listeners: Vec::new(),
  24. location: location.clone(),
  25. };
  26. let interperted_vnodes = LazyNodes::new(|factory| {
  27. dioxus_rsx_interpreter::resolve_scope(
  28. location,
  29. "div{\"hello world\"}",
  30. empty_context,
  31. factory,
  32. )
  33. });
  34. let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
  35. let static_vnodes = dom.render_vnodes(static_vnodes);
  36. assert!(check_eq(interperted_vnodes, static_vnodes));
  37. }
  38. #[test]
  39. #[allow(non_snake_case)]
  40. fn render_nested() {
  41. fn Base(cx: Scope) -> Element {
  42. rsx!(cx, div {})
  43. }
  44. let dom = VirtualDom::new(Base);
  45. let static_vnodes = rsx! {
  46. div {
  47. p { "hello world" }
  48. div {
  49. p { "hello world" }
  50. }
  51. }
  52. };
  53. let location = CodeLocation {
  54. file_path: String::new(),
  55. crate_path: String::new(),
  56. line: 1,
  57. column: 0,
  58. };
  59. let empty_context = CapturedContext {
  60. captured: IfmtArgs {
  61. named_args: Vec::new(),
  62. },
  63. components: Vec::new(),
  64. iterators: Vec::new(),
  65. expressions: Vec::new(),
  66. listeners: Vec::new(),
  67. location: location.clone(),
  68. };
  69. let interperted_vnodes = LazyNodes::new(|factory| {
  70. dioxus_rsx_interpreter::resolve_scope(
  71. location,
  72. r#"div {
  73. p { "hello world" }
  74. div {
  75. p { "hello world" }
  76. }
  77. }"#,
  78. empty_context,
  79. factory,
  80. )
  81. });
  82. let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
  83. let static_vnodes = dom.render_vnodes(static_vnodes);
  84. assert!(check_eq(interperted_vnodes, static_vnodes));
  85. }
  86. #[test]
  87. #[allow(non_snake_case)]
  88. fn render_component() {
  89. fn Comp(cx: Scope) -> Element {
  90. rsx!(cx, div {})
  91. }
  92. fn Base(cx: Scope) -> Element {
  93. rsx!(cx, div {})
  94. }
  95. let dom = VirtualDom::new(Base);
  96. let static_vnodes = rsx! {
  97. div {
  98. Comp {}
  99. }
  100. };
  101. let location = CodeLocation {
  102. file_path: String::new(),
  103. crate_path: String::new(),
  104. line: 2,
  105. column: 0,
  106. };
  107. let interperted_vnodes = LazyNodes::new(|factory| {
  108. let context = CapturedContext {
  109. captured: IfmtArgs {
  110. named_args: Vec::new(),
  111. },
  112. components: vec![(
  113. r#"__cx.component(Comp, fc_to_builder(Comp).build(), None, "Comp")"#,
  114. factory.component(Comp, (), None, "Comp"),
  115. )],
  116. iterators: Vec::new(),
  117. expressions: Vec::new(),
  118. listeners: Vec::new(),
  119. location: location.clone(),
  120. };
  121. dioxus_rsx_interpreter::resolve_scope(
  122. location,
  123. r#"div {
  124. Comp {}
  125. }"#,
  126. context,
  127. factory,
  128. )
  129. });
  130. let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
  131. let static_vnodes = dom.render_vnodes(static_vnodes);
  132. println!("{:#?}", interperted_vnodes);
  133. println!("{:#?}", static_vnodes);
  134. assert!(check_eq(interperted_vnodes, static_vnodes));
  135. }
  136. #[test]
  137. #[allow(non_snake_case)]
  138. fn render_iterator() {
  139. fn Base(cx: Scope) -> Element {
  140. rsx!(cx, div {})
  141. }
  142. let dom = VirtualDom::new(Base);
  143. let iter = (0..10).map(|i| dom.render_vnodes(rsx! {"{i}"}));
  144. let static_vnodes = rsx! {
  145. div {
  146. iter
  147. }
  148. };
  149. let location = CodeLocation {
  150. file_path: String::new(),
  151. crate_path: String::new(),
  152. line: 3,
  153. column: 0,
  154. };
  155. let interperted_vnodes = LazyNodes::new(|factory| {
  156. let context = CapturedContext {
  157. captured: IfmtArgs {
  158. named_args: Vec::new(),
  159. },
  160. components: Vec::new(),
  161. iterators: vec![(
  162. r#"
  163. (0..10).map(|i| dom.render_vnodes(rsx!{"{i}"}))"#,
  164. factory.fragment_from_iter((0..10).map(|i| factory.text(format_args!("{i}")))),
  165. )],
  166. expressions: Vec::new(),
  167. listeners: Vec::new(),
  168. location: location.clone(),
  169. };
  170. dioxus_rsx_interpreter::resolve_scope(
  171. location,
  172. r#"div {
  173. (0..10).map(|i| dom.render_vnodes(rsx!{"{i}"}))
  174. }"#,
  175. context,
  176. factory,
  177. )
  178. });
  179. let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
  180. let static_vnodes = dom.render_vnodes(static_vnodes);
  181. println!("{:#?}", interperted_vnodes);
  182. println!("{:#?}", static_vnodes);
  183. assert!(check_eq(interperted_vnodes, static_vnodes));
  184. }
  185. #[test]
  186. #[allow(non_snake_case)]
  187. fn render_captured_variable() {
  188. fn Base(cx: Scope) -> Element {
  189. rsx!(cx, div {})
  190. }
  191. let dom = VirtualDom::new(Base);
  192. let x = 10;
  193. let static_vnodes = rsx! {
  194. div {
  195. "{x}"
  196. }
  197. };
  198. let location = CodeLocation {
  199. file_path: String::new(),
  200. crate_path: String::new(),
  201. line: 4,
  202. column: 0,
  203. };
  204. let interperted_vnodes = LazyNodes::new(|factory| {
  205. let context = CapturedContext {
  206. captured: IfmtArgs {
  207. named_args: vec![FormattedArg {
  208. expr: "x",
  209. format_args: "",
  210. result: x.to_string(),
  211. }],
  212. },
  213. components: Vec::new(),
  214. iterators: Vec::new(),
  215. expressions: Vec::new(),
  216. listeners: Vec::new(),
  217. location: location.clone(),
  218. };
  219. dioxus_rsx_interpreter::resolve_scope(
  220. location,
  221. r#"div {
  222. "{x}"
  223. }"#,
  224. context,
  225. factory,
  226. )
  227. });
  228. let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
  229. let static_vnodes = dom.render_vnodes(static_vnodes);
  230. println!("{:#?}", interperted_vnodes);
  231. println!("{:#?}", static_vnodes);
  232. assert!(check_eq(interperted_vnodes, static_vnodes));
  233. }
  234. #[test]
  235. #[allow(non_snake_case)]
  236. fn render_listener() {
  237. fn Base(cx: Scope) -> Element {
  238. rsx!(cx, div {})
  239. }
  240. let dom = VirtualDom::new(Base);
  241. let static_vnodes = rsx! {
  242. div {
  243. onclick: |_| println!("clicked")
  244. }
  245. };
  246. let location = CodeLocation {
  247. file_path: String::new(),
  248. crate_path: String::new(),
  249. line: 5,
  250. column: 0,
  251. };
  252. let interperted_vnodes = LazyNodes::new(|factory| {
  253. let f = |_| println!("clicked");
  254. let f = factory.bump().alloc(f);
  255. let context = CapturedContext {
  256. captured: IfmtArgs {
  257. named_args: Vec::new(),
  258. },
  259. components: Vec::new(),
  260. iterators: Vec::new(),
  261. expressions: Vec::new(),
  262. listeners: vec![(
  263. r#"dioxus_elements::on::onclick(__cx, |_| println!("clicked"))"#,
  264. dioxus_elements::on::onclick(factory, f),
  265. )],
  266. location: location.clone(),
  267. };
  268. dioxus_rsx_interpreter::resolve_scope(
  269. location,
  270. r#"div {
  271. onclick: |_| println!("clicked")
  272. }"#,
  273. context,
  274. factory,
  275. )
  276. });
  277. let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
  278. let static_vnodes = dom.render_vnodes(static_vnodes);
  279. println!("{:#?}", interperted_vnodes);
  280. println!("{:#?}", static_vnodes);
  281. assert!(check_eq(interperted_vnodes, static_vnodes));
  282. }
  283. fn check_eq<'a>(a: &'a VNode<'a>, b: &'a VNode<'a>) -> bool {
  284. match (a, b) {
  285. (VNode::Text(t_a), VNode::Text(t_b)) => t_a.text == t_b.text,
  286. (VNode::Element(e_a), VNode::Element(e_b)) => {
  287. e_a.attributes
  288. .iter()
  289. .zip(e_b.attributes.iter())
  290. .all(|(a, b)| {
  291. a.is_static == b.is_static
  292. && a.is_volatile == b.is_volatile
  293. && a.name == b.name
  294. && a.value == b.value
  295. && a.namespace == b.namespace
  296. })
  297. && e_a
  298. .children
  299. .iter()
  300. .zip(e_b.children.iter())
  301. .all(|(a, b)| check_eq(a, b))
  302. && e_a.key == e_b.key
  303. && e_a.tag == e_b.tag
  304. && e_a.namespace == e_b.namespace
  305. && e_a
  306. .listeners
  307. .iter()
  308. .zip(e_b.listeners.iter())
  309. .all(|(a, b)| a.event == b.event)
  310. }
  311. (VNode::Fragment(f_a), VNode::Fragment(f_b)) => {
  312. f_a.key == f_b.key
  313. && f_a
  314. .children
  315. .iter()
  316. .zip(f_b.children.iter())
  317. .all(|(a, b)| check_eq(a, b))
  318. }
  319. (VNode::Component(c_a), VNode::Component(c_b)) => {
  320. c_a.can_memoize == c_b.can_memoize
  321. && c_a.key == c_b.key
  322. && c_a.fn_name == c_b.fn_name
  323. && c_a.user_fc == c_b.user_fc
  324. }
  325. (VNode::Placeholder(_), VNode::Placeholder(_)) => true,
  326. _ => false,
  327. }
  328. }