render.rs 9.8 KB

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