render.rs 9.9 KB

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