Files
dioxus/packages/router/tests/via_ssr/link.rs
T
Evan Almloff 09a4b5eb4e Return a 404 when paring the route fails (#3860)
* return a 404 when paring the route fails

* add a playwright test for fullstack routing status codes

* Fix new playwright test

* document SSRError

* Make SSRError public and fix clippy

* Move the error display bound to the routable trait

* fix clippy

* return a more generic 404 message

* fix router tests
2025-03-14 00:51:44 -07:00

453 lines
9.4 KiB
Rust

use dioxus::prelude::*;
use dioxus_history::{History, MemoryHistory};
use dioxus_router::components::HistoryProvider;
use std::rc::Rc;
fn prepare<R: Routable>() -> String {
prepare_at::<R>("/")
}
fn prepare_at<R: Routable>(at: impl ToString) -> String {
let mut vdom = VirtualDom::new_with_props(
App,
AppProps::<R> {
at: at.to_string(),
phantom: std::marker::PhantomData,
},
);
vdom.rebuild_in_place();
return dioxus_ssr::render(&vdom);
#[derive(Props)]
struct AppProps<R: Routable> {
at: String,
phantom: std::marker::PhantomData<R>,
}
impl<R: Routable> Clone for AppProps<R> {
fn clone(&self) -> Self {
Self {
at: self.at.clone(),
phantom: std::marker::PhantomData,
}
}
}
impl<R: Routable> PartialEq for AppProps<R> {
fn eq(&self, _other: &Self) -> bool {
false
}
}
#[allow(non_snake_case)]
fn App<R: Routable>(props: AppProps<R>) -> Element {
rsx! {
h1 { "App" }
HistoryProvider {
history: move |_| Rc::new(MemoryHistory::with_initial_path(props.at.clone())) as Rc<dyn History>,
Router::<R> {}
}
}
}
}
#[test]
fn href_internal() {
#[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
#[route("/test")]
Test {},
}
#[component]
fn Test() -> Element {
unimplemented!()
}
#[component]
fn Root() -> Element {
rsx! {
Link {
to: Route::Test {},
"Link"
}
}
}
let expected = format!("<h1>App</h1><a {href}>Link</a>", href = r#"href="/test""#,);
assert_eq!(prepare::<Route>(), expected);
}
#[test]
fn href_external() {
#[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
#[route("/test")]
Test {},
}
#[component]
fn Test() -> Element {
unimplemented!()
}
#[component]
fn Root() -> Element {
rsx! {
Link {
to: "https://dioxuslabs.com/",
"Link"
}
}
}
let expected = format!(
"<h1>App</h1><a {href} {rel}>Link</a>",
href = r#"href="https://dioxuslabs.com/""#,
rel = r#"rel="noopener noreferrer""#,
);
assert_eq!(prepare::<Route>(), expected);
}
#[test]
fn with_class() {
#[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
#[route("/test")]
Test {},
}
#[component]
fn Test() -> Element {
unimplemented!()
}
#[component]
fn Root() -> Element {
rsx! {
Link {
to: Route::Test {},
class: "test_class",
"Link"
}
}
}
let expected = format!(
"<h1>App</h1><a {href} {class}>Link</a>",
href = r#"href="/test""#,
class = r#"class="test_class""#,
);
assert_eq!(prepare::<Route>(), expected);
}
#[test]
fn with_active_class_active() {
#[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
}
#[component]
fn Root() -> Element {
rsx! {
Link {
to: Route::Root {},
active_class: "active_class".to_string(),
class: "test_class",
"Link"
}
}
}
let expected = format!(
"<h1>App</h1><a {href} {class} {aria}>Link</a>",
href = r#"href="/""#,
class = r#"class="test_class active_class""#,
aria = r#"aria-current="page""#,
);
assert_eq!(prepare::<Route>(), expected);
}
#[test]
fn with_active_class_inactive() {
#[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
#[route("/test")]
Test {},
}
#[component]
fn Test() -> Element {
unimplemented!()
}
#[component]
fn Root() -> Element {
rsx! {
Link {
to: Route::Test {},
active_class: "active_class".to_string(),
class: "test_class",
"Link"
}
}
}
let expected = format!(
"<h1>App</h1><a {href} {class}>Link</a>",
href = r#"href="/test""#,
class = r#"class="test_class""#,
);
assert_eq!(prepare::<Route>(), expected);
}
#[test]
fn with_id() {
#[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
#[route("/test")]
Test {},
}
#[component]
fn Test() -> Element {
unimplemented!()
}
#[component]
fn Root() -> Element {
rsx! {
Link {
to: Route::Test {},
id: "test_id",
"Link"
}
}
}
let expected = format!(
"<h1>App</h1><a {href} {id}>Link</a>",
href = r#"href="/test""#,
id = r#"id="test_id""#,
);
assert_eq!(prepare::<Route>(), expected);
}
#[test]
fn with_new_tab() {
#[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
#[route("/test")]
Test {},
}
#[component]
fn Test() -> Element {
unimplemented!()
}
#[component]
fn Root() -> Element {
rsx! {
Link {
to: Route::Test {},
new_tab: true,
"Link"
}
}
}
let expected = format!(
"<h1>App</h1><a {href} {target}>Link</a>",
href = r#"href="/test""#,
target = r#"target="_blank""#
);
assert_eq!(prepare::<Route>(), expected);
}
#[test]
fn with_new_tab_external() {
#[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
}
#[component]
fn Root() -> Element {
rsx! {
Link {
to: "https://dioxuslabs.com/",
new_tab: true,
"Link"
}
}
}
let expected = format!(
"<h1>App</h1><a {href} {rel} {target}>Link</a>",
href = r#"href="https://dioxuslabs.com/""#,
rel = r#"rel="noopener noreferrer""#,
target = r#"target="_blank""#
);
assert_eq!(prepare::<Route>(), expected);
}
#[test]
fn with_rel() {
#[derive(Routable, Clone)]
enum Route {
#[route("/")]
Root {},
#[route("/test")]
Test {},
}
#[component]
fn Test() -> Element {
unimplemented!()
}
#[component]
fn Root() -> Element {
rsx! {
Link {
to: Route::Test {},
rel: "test_rel".to_string(),
"Link"
}
}
}
let expected = format!(
"<h1>App</h1><a {href} {rel}>Link</a>",
href = r#"href="/test""#,
rel = r#"rel="test_rel""#,
);
assert_eq!(prepare::<Route>(), expected);
}
#[test]
fn with_child_route() {
#[derive(Routable, Clone, PartialEq, Debug)]
enum ChildRoute {
#[route("/")]
ChildRoot {},
#[route("/:not_static")]
NotStatic { not_static: String },
}
#[derive(Routable, Clone, PartialEq, Debug)]
enum Route {
#[route("/")]
Root {},
#[route("/test")]
Test {},
#[child("/child")]
Nested { child: ChildRoute },
}
#[component]
fn Test() -> Element {
unimplemented!()
}
#[component]
fn Root() -> Element {
rsx! {
Link {
to: Route::Test {},
"Parent Link"
}
Link {
to: Route::Nested { child: ChildRoute::NotStatic { not_static: "this-is-a-child-route".to_string() } },
"Child Link"
}
}
}
#[component]
fn ChildRoot() -> Element {
rsx! {
Link {
to: Route::Test {},
"Parent Link"
}
Link {
to: ChildRoute::NotStatic { not_static: "this-is-a-child-route".to_string() },
"Child Link 1"
}
Link {
to: Route::Nested { child: ChildRoute::NotStatic { not_static: "this-is-a-child-route".to_string() } },
"Child Link 2"
}
}
}
#[component]
fn NotStatic(not_static: String) -> Element {
unimplemented!()
}
assert_eq!(
prepare_at::<Route>("/"),
"<h1>App</h1><a href=\"/test\">Parent Link</a><a href=\"/child/this-is-a-child-route\">Child Link</a>"
);
assert_eq!(
prepare_at::<Route>("/child"),
"<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>"
);
}
#[test]
fn with_hash_segment() {
#[derive(Routable, Clone)]
enum Route {
#[route("/#:data")]
Root { data: String },
}
#[component]
fn Root(data: String) -> Element {
rsx! {
Link {
to: Route::Root { data: "test".to_string() },
"Link"
}
Link {
to: Route::Root { data: "".to_string() },
"Empty"
}
}
}
assert_eq!(
prepare_at::<Route>("/#test"),
"<h1>App</h1><a href=\"/#test\" aria-current=\"page\">Link</a><a href=\"/\">Empty</a>"
);
}