render.rs 11 KB

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