link.rs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. use dioxus::prelude::*;
  2. use dioxus_history::{History, MemoryHistory};
  3. use dioxus_router::components::HistoryProvider;
  4. use std::rc::Rc;
  5. fn prepare<R: Routable>() -> String {
  6. prepare_at::<R>("/")
  7. }
  8. fn prepare_at<R: Routable>(at: impl ToString) -> String {
  9. let mut vdom = VirtualDom::new_with_props(
  10. App,
  11. AppProps::<R> {
  12. at: at.to_string(),
  13. phantom: std::marker::PhantomData,
  14. },
  15. );
  16. vdom.rebuild_in_place();
  17. return dioxus_ssr::render(&vdom);
  18. #[derive(Props)]
  19. struct AppProps<R: Routable> {
  20. at: String,
  21. phantom: std::marker::PhantomData<R>,
  22. }
  23. impl<R: Routable> Clone for AppProps<R> {
  24. fn clone(&self) -> Self {
  25. Self {
  26. at: self.at.clone(),
  27. phantom: std::marker::PhantomData,
  28. }
  29. }
  30. }
  31. impl<R: Routable> PartialEq for AppProps<R> {
  32. fn eq(&self, _other: &Self) -> bool {
  33. false
  34. }
  35. }
  36. #[allow(non_snake_case)]
  37. fn App<R: Routable>(props: AppProps<R>) -> Element {
  38. rsx! {
  39. h1 { "App" }
  40. HistoryProvider {
  41. history: move |_| Rc::new(MemoryHistory::with_initial_path(props.at.clone())) as Rc<dyn History>,
  42. Router::<R> {}
  43. }
  44. }
  45. }
  46. }
  47. #[test]
  48. fn href_internal() {
  49. #[derive(Routable, Clone)]
  50. enum Route {
  51. #[route("/")]
  52. Root {},
  53. #[route("/test")]
  54. Test {},
  55. }
  56. #[component]
  57. fn Test() -> Element {
  58. unimplemented!()
  59. }
  60. #[component]
  61. fn Root() -> Element {
  62. rsx! {
  63. Link {
  64. to: Route::Test {},
  65. "Link"
  66. }
  67. }
  68. }
  69. let expected = format!("<h1>App</h1><a {href}>Link</a>", href = r#"href="/test""#,);
  70. assert_eq!(prepare::<Route>(), expected);
  71. }
  72. #[test]
  73. fn href_external() {
  74. #[derive(Routable, Clone)]
  75. enum Route {
  76. #[route("/")]
  77. Root {},
  78. #[route("/test")]
  79. Test {},
  80. }
  81. #[component]
  82. fn Test() -> Element {
  83. unimplemented!()
  84. }
  85. #[component]
  86. fn Root() -> Element {
  87. rsx! {
  88. Link {
  89. to: "https://dioxuslabs.com/",
  90. "Link"
  91. }
  92. }
  93. }
  94. let expected = format!(
  95. "<h1>App</h1><a {href} {rel}>Link</a>",
  96. href = r#"href="https://dioxuslabs.com/""#,
  97. rel = r#"rel="noopener noreferrer""#,
  98. );
  99. assert_eq!(prepare::<Route>(), expected);
  100. }
  101. #[test]
  102. fn with_class() {
  103. #[derive(Routable, Clone)]
  104. enum Route {
  105. #[route("/")]
  106. Root {},
  107. #[route("/test")]
  108. Test {},
  109. }
  110. #[component]
  111. fn Test() -> Element {
  112. unimplemented!()
  113. }
  114. #[component]
  115. fn Root() -> Element {
  116. rsx! {
  117. Link {
  118. to: Route::Test {},
  119. class: "test_class",
  120. "Link"
  121. }
  122. }
  123. }
  124. let expected = format!(
  125. "<h1>App</h1><a {href} {class}>Link</a>",
  126. href = r#"href="/test""#,
  127. class = r#"class="test_class""#,
  128. );
  129. assert_eq!(prepare::<Route>(), expected);
  130. }
  131. #[test]
  132. fn with_active_class_active() {
  133. #[derive(Routable, Clone)]
  134. enum Route {
  135. #[route("/")]
  136. Root {},
  137. }
  138. #[component]
  139. fn Root() -> Element {
  140. rsx! {
  141. Link {
  142. to: Route::Root {},
  143. active_class: "active_class".to_string(),
  144. class: "test_class",
  145. "Link"
  146. }
  147. }
  148. }
  149. let expected = format!(
  150. "<h1>App</h1><a {href} {class} {aria}>Link</a>",
  151. href = r#"href="/""#,
  152. class = r#"class="test_class active_class""#,
  153. aria = r#"aria-current="page""#,
  154. );
  155. assert_eq!(prepare::<Route>(), expected);
  156. }
  157. #[test]
  158. fn with_active_class_inactive() {
  159. #[derive(Routable, Clone)]
  160. enum Route {
  161. #[route("/")]
  162. Root {},
  163. #[route("/test")]
  164. Test {},
  165. }
  166. #[component]
  167. fn Test() -> Element {
  168. unimplemented!()
  169. }
  170. #[component]
  171. fn Root() -> Element {
  172. rsx! {
  173. Link {
  174. to: Route::Test {},
  175. active_class: "active_class".to_string(),
  176. class: "test_class",
  177. "Link"
  178. }
  179. }
  180. }
  181. let expected = format!(
  182. "<h1>App</h1><a {href} {class}>Link</a>",
  183. href = r#"href="/test""#,
  184. class = r#"class="test_class""#,
  185. );
  186. assert_eq!(prepare::<Route>(), expected);
  187. }
  188. #[test]
  189. fn with_id() {
  190. #[derive(Routable, Clone)]
  191. enum Route {
  192. #[route("/")]
  193. Root {},
  194. #[route("/test")]
  195. Test {},
  196. }
  197. #[component]
  198. fn Test() -> Element {
  199. unimplemented!()
  200. }
  201. #[component]
  202. fn Root() -> Element {
  203. rsx! {
  204. Link {
  205. to: Route::Test {},
  206. id: "test_id",
  207. "Link"
  208. }
  209. }
  210. }
  211. let expected = format!(
  212. "<h1>App</h1><a {href} {id}>Link</a>",
  213. href = r#"href="/test""#,
  214. id = r#"id="test_id""#,
  215. );
  216. assert_eq!(prepare::<Route>(), expected);
  217. }
  218. #[test]
  219. fn with_new_tab() {
  220. #[derive(Routable, Clone)]
  221. enum Route {
  222. #[route("/")]
  223. Root {},
  224. #[route("/test")]
  225. Test {},
  226. }
  227. #[component]
  228. fn Test() -> Element {
  229. unimplemented!()
  230. }
  231. #[component]
  232. fn Root() -> Element {
  233. rsx! {
  234. Link {
  235. to: Route::Test {},
  236. new_tab: true,
  237. "Link"
  238. }
  239. }
  240. }
  241. let expected = format!(
  242. "<h1>App</h1><a {href} {target}>Link</a>",
  243. href = r#"href="/test""#,
  244. target = r#"target="_blank""#
  245. );
  246. assert_eq!(prepare::<Route>(), expected);
  247. }
  248. #[test]
  249. fn with_new_tab_external() {
  250. #[derive(Routable, Clone)]
  251. enum Route {
  252. #[route("/")]
  253. Root {},
  254. }
  255. #[component]
  256. fn Root() -> Element {
  257. rsx! {
  258. Link {
  259. to: "https://dioxuslabs.com/",
  260. new_tab: true,
  261. "Link"
  262. }
  263. }
  264. }
  265. let expected = format!(
  266. "<h1>App</h1><a {href} {rel} {target}>Link</a>",
  267. href = r#"href="https://dioxuslabs.com/""#,
  268. rel = r#"rel="noopener noreferrer""#,
  269. target = r#"target="_blank""#
  270. );
  271. assert_eq!(prepare::<Route>(), expected);
  272. }
  273. #[test]
  274. fn with_rel() {
  275. #[derive(Routable, Clone)]
  276. enum Route {
  277. #[route("/")]
  278. Root {},
  279. #[route("/test")]
  280. Test {},
  281. }
  282. #[component]
  283. fn Test() -> Element {
  284. unimplemented!()
  285. }
  286. #[component]
  287. fn Root() -> Element {
  288. rsx! {
  289. Link {
  290. to: Route::Test {},
  291. rel: "test_rel".to_string(),
  292. "Link"
  293. }
  294. }
  295. }
  296. let expected = format!(
  297. "<h1>App</h1><a {href} {rel}>Link</a>",
  298. href = r#"href="/test""#,
  299. rel = r#"rel="test_rel""#,
  300. );
  301. assert_eq!(prepare::<Route>(), expected);
  302. }
  303. #[test]
  304. fn with_child_route() {
  305. #[derive(Routable, Clone, PartialEq, Debug)]
  306. enum ChildRoute {
  307. #[route("/")]
  308. ChildRoot {},
  309. #[route("/:not_static")]
  310. NotStatic { not_static: String },
  311. }
  312. #[derive(Routable, Clone, PartialEq, Debug)]
  313. enum Route {
  314. #[route("/")]
  315. Root {},
  316. #[route("/test")]
  317. Test {},
  318. #[child("/child")]
  319. Nested { child: ChildRoute },
  320. }
  321. #[component]
  322. fn Test() -> Element {
  323. unimplemented!()
  324. }
  325. #[component]
  326. fn Root() -> Element {
  327. rsx! {
  328. Link {
  329. to: Route::Test {},
  330. "Parent Link"
  331. }
  332. Link {
  333. to: Route::Nested { child: ChildRoute::NotStatic { not_static: "this-is-a-child-route".to_string() } },
  334. "Child Link"
  335. }
  336. }
  337. }
  338. #[component]
  339. fn ChildRoot() -> Element {
  340. rsx! {
  341. Link {
  342. to: Route::Test {},
  343. "Parent Link"
  344. }
  345. Link {
  346. to: ChildRoute::NotStatic { not_static: "this-is-a-child-route".to_string() },
  347. "Child Link 1"
  348. }
  349. Link {
  350. to: Route::Nested { child: ChildRoute::NotStatic { not_static: "this-is-a-child-route".to_string() } },
  351. "Child Link 2"
  352. }
  353. }
  354. }
  355. #[component]
  356. fn NotStatic(not_static: String) -> Element {
  357. unimplemented!()
  358. }
  359. assert_eq!(
  360. prepare_at::<Route>("/"),
  361. "<h1>App</h1><a href=\"/test\">Parent Link</a><a href=\"/child/this-is-a-child-route\">Child Link</a>"
  362. );
  363. assert_eq!(
  364. prepare_at::<Route>("/child"),
  365. "<h1>App</h1><a href=\"/test\">Parent Link</a><a href=\"/child/this-is-a-child-route\">Child Link 1</a><a href=\"/child/this-is-a-child-route\">Child Link 2</a>"
  366. );
  367. }
  368. #[test]
  369. fn with_hash_segment() {
  370. #[derive(Routable, Clone)]
  371. enum Route {
  372. #[route("/#:data")]
  373. Root { data: String },
  374. }
  375. #[component]
  376. fn Root(data: String) -> Element {
  377. rsx! {
  378. Link {
  379. to: Route::Root { data: "test".to_string() },
  380. "Link"
  381. }
  382. Link {
  383. to: Route::Root { data: "".to_string() },
  384. "Empty"
  385. }
  386. }
  387. }
  388. assert_eq!(
  389. prepare_at::<Route>("/#test"),
  390. "<h1>App</h1><a href=\"/#test\" aria-current=\"page\">Link</a><a href=\"/\">Empty</a>"
  391. );
  392. }