render.rs 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. use dioxus_core::{prelude::*};
  2. use dioxus_core_macro::*;
  3. use dioxus_html as dioxus_elements;
  4. use dioxus_rsx_interpreter::{
  5. captuered_context::{CapturedContext, FormattedArg, IfmtArgs},
  6. CodeLocation,
  7. };
  8. #[test]
  9. #[allow(non_snake_case)]
  10. fn render_basic() {
  11. fn Base(cx: Scope) -> Element {
  12. rsx!(cx, div {})
  13. }
  14. let dom = VirtualDom::new(Base);
  15. let static_vnodes = rsx!(div{"hello world"});
  16. let location = CodeLocation {
  17. file: String::new(),
  18. line: 0,
  19. column: 0,
  20. };
  21. let empty_context = CapturedContext {
  22. captured: IfmtArgs {
  23. named_args: Vec::new(),
  24. },
  25. components: Vec::new(),
  26. iterators: Vec::new(),
  27. expressions: Vec::new(),
  28. listeners: Vec::new(),
  29. location: location.clone(),
  30. };
  31. let interperted_vnodes = LazyNodes::new(|factory| {
  32. dioxus_rsx_interpreter::resolve_scope(
  33. location,
  34. "div{\"hello world\"}",
  35. empty_context,
  36. factory,
  37. )
  38. });
  39. let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
  40. let static_vnodes = dom.render_vnodes(static_vnodes);
  41. assert!(check_eq(interperted_vnodes, static_vnodes));
  42. }
  43. #[test]
  44. #[allow(non_snake_case)]
  45. fn render_nested() {
  46. fn Base(cx: Scope) -> Element {
  47. rsx!(cx, div {})
  48. }
  49. let dom = VirtualDom::new(Base);
  50. let static_vnodes = rsx! {
  51. div {
  52. p { "hello world" }
  53. div {
  54. p { "hello world" }
  55. }
  56. }
  57. };
  58. let location = CodeLocation {
  59. file: String::new(),
  60. line: 1,
  61. column: 0,
  62. };
  63. let empty_context = CapturedContext {
  64. captured: IfmtArgs {
  65. named_args: Vec::new(),
  66. },
  67. components: Vec::new(),
  68. iterators: Vec::new(),
  69. expressions: Vec::new(),
  70. listeners: Vec::new(),
  71. location: location.clone(),
  72. };
  73. let interperted_vnodes = LazyNodes::new(|factory| {
  74. dioxus_rsx_interpreter::resolve_scope(
  75. location,
  76. r#"div {
  77. p { "hello world" }
  78. div {
  79. p { "hello world" }
  80. }
  81. }"#,
  82. empty_context,
  83. factory,
  84. )
  85. });
  86. let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
  87. let static_vnodes = dom.render_vnodes(static_vnodes);
  88. assert!(check_eq(interperted_vnodes, static_vnodes));
  89. }
  90. #[test]
  91. #[allow(non_snake_case)]
  92. fn render_component() {
  93. fn Comp(cx: Scope) -> Element {
  94. rsx!(cx, div {})
  95. }
  96. fn Base(cx: Scope) -> Element {
  97. rsx!(cx, div {})
  98. }
  99. let dom = VirtualDom::new(Base);
  100. let static_vnodes = rsx! {
  101. div {
  102. Comp {}
  103. }
  104. };
  105. let location = CodeLocation {
  106. file: String::new(),
  107. line: 2,
  108. column: 0,
  109. };
  110. let interperted_vnodes = LazyNodes::new(|factory| {
  111. let context = CapturedContext {
  112. captured: IfmtArgs {
  113. named_args: Vec::new(),
  114. },
  115. components: vec![(
  116. r#"__cx.component(Comp, fc_to_builder(Comp).build(), None, "Comp")"#,
  117. factory.component(Comp, (), None, "Comp"),
  118. )],
  119. iterators: Vec::new(),
  120. expressions: Vec::new(),
  121. listeners: Vec::new(),
  122. location: location.clone(),
  123. };
  124. dioxus_rsx_interpreter::resolve_scope(
  125. location,
  126. r#"div {
  127. Comp {}
  128. }"#,
  129. context,
  130. factory,
  131. )
  132. });
  133. let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
  134. let static_vnodes = dom.render_vnodes(static_vnodes);
  135. println!("{:#?}", interperted_vnodes);
  136. println!("{:#?}", static_vnodes);
  137. assert!(check_eq(interperted_vnodes, static_vnodes));
  138. }
  139. #[test]
  140. #[allow(non_snake_case)]
  141. fn render_iterator() {
  142. fn Base(cx: Scope) -> Element {
  143. rsx!(cx, div {})
  144. }
  145. let dom = VirtualDom::new(Base);
  146. let iter = (0..10).map(|i| dom.render_vnodes(rsx! {"{i}"}));
  147. let static_vnodes = rsx! {
  148. div {
  149. iter
  150. }
  151. };
  152. let location = CodeLocation {
  153. file: String::new(),
  154. line: 3,
  155. column: 0,
  156. };
  157. let interperted_vnodes = LazyNodes::new(|factory| {
  158. let context = CapturedContext {
  159. captured: IfmtArgs {
  160. named_args: Vec::new(),
  161. },
  162. components: Vec::new(),
  163. iterators: vec![(
  164. r#"
  165. (0..10).map(|i| dom.render_vnodes(rsx!{"{i}"}))"#,
  166. factory.fragment_from_iter((0..10).map(|i| factory.text(format_args!("{i}")))),
  167. )],
  168. expressions: Vec::new(),
  169. listeners: Vec::new(),
  170. location: location.clone(),
  171. };
  172. dioxus_rsx_interpreter::resolve_scope(
  173. location,
  174. r#"div {
  175. (0..10).map(|i| dom.render_vnodes(rsx!{"{i}"}))
  176. }"#,
  177. context,
  178. factory,
  179. )
  180. });
  181. let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
  182. let static_vnodes = dom.render_vnodes(static_vnodes);
  183. println!("{:#?}", interperted_vnodes);
  184. println!("{:#?}", static_vnodes);
  185. assert!(check_eq(interperted_vnodes, static_vnodes));
  186. }
  187. #[test]
  188. #[allow(non_snake_case)]
  189. fn render_captured_variable() {
  190. fn Base(cx: Scope) -> Element {
  191. rsx!(cx, div {})
  192. }
  193. let dom = VirtualDom::new(Base);
  194. let x = 10;
  195. let static_vnodes = rsx! {
  196. div {
  197. "{x}"
  198. }
  199. };
  200. let location = CodeLocation {
  201. file: String::new(),
  202. line: 4,
  203. column: 0,
  204. };
  205. let interperted_vnodes = LazyNodes::new(|factory| {
  206. let context = CapturedContext {
  207. captured: IfmtArgs {
  208. named_args: vec![FormattedArg {
  209. expr: "x",
  210. format_args: "",
  211. result: x.to_string(),
  212. }],
  213. },
  214. components: Vec::new(),
  215. iterators: Vec::new(),
  216. expressions: Vec::new(),
  217. listeners: Vec::new(),
  218. location: location.clone(),
  219. };
  220. dioxus_rsx_interpreter::resolve_scope(
  221. location,
  222. r#"div {
  223. "{x}"
  224. }"#,
  225. context,
  226. factory,
  227. )
  228. });
  229. let interperted_vnodes = dom.render_vnodes(interperted_vnodes);
  230. let static_vnodes = dom.render_vnodes(static_vnodes);
  231. println!("{:#?}", interperted_vnodes);
  232. println!("{:#?}", static_vnodes);
  233. assert!(check_eq(interperted_vnodes, static_vnodes));
  234. }
  235. #[test]
  236. #[allow(non_snake_case)]
  237. fn render_listener() {
  238. fn Base(cx: Scope) -> Element {
  239. rsx!(cx, div {})
  240. }
  241. let dom = VirtualDom::new(Base);
  242. let static_vnodes = rsx! {
  243. div {
  244. onclick: |_| println!("clicked")
  245. }
  246. };
  247. let location = CodeLocation {
  248. file: 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. }