瀏覽代碼

Fix web redirect routes (#4109)

Evan Almloff 1 月之前
父節點
當前提交
53ef78823d

+ 7 - 0
Cargo.lock

@@ -4349,6 +4349,13 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "dioxus-playwright-web-routing-test"
+version = "0.0.1"
+dependencies = [
+ "dioxus",
+]
+
 [[package]]
 name = "dioxus-playwright-web-test"
 version = "0.0.1"

+ 1 - 0
Cargo.toml

@@ -114,6 +114,7 @@ members = [
     # Playwright tests
     "packages/playwright-tests/liveview",
     "packages/playwright-tests/web",
+    "packages/playwright-tests/web-routing",
     "packages/playwright-tests/barebones-template",
     "packages/playwright-tests/fullstack",
     "packages/playwright-tests/fullstack-mounted",

+ 9 - 0
packages/playwright-tests/playwright.config.js

@@ -93,6 +93,15 @@ module.exports = defineConfig({
       reuseExistingServer: !process.env.CI,
       stdout: "pipe",
     },
+    {
+      cwd: path.join(process.cwd(), "web-routing"),
+      command:
+        'cargo run --package dioxus-cli --release -- serve --force-sequential --platform web --addr "127.0.0.1" --port 2020',
+      port: 2020,
+      timeout: 50 * 60 * 1000,
+      reuseExistingServer: !process.env.CI,
+      stdout: "pipe",
+    },
     {
       cwd: path.join(process.cwd(), "fullstack"),
       command:

+ 32 - 0
packages/playwright-tests/web-routing.spec.js

@@ -0,0 +1,32 @@
+// @ts-check
+const { test, expect } = require("@playwright/test");
+
+test("redirect", async ({ page }) => {
+  // Going to the root url should redirect to /other.
+  await page.goto("http://localhost:2020");
+
+  // Expect the page to the text Other
+  const main = page.locator("#other");
+  await expect(main).toContainText("Other");
+
+  // Expect the url to be /other
+  await expect(page).toHaveURL("http://localhost:2020/other");
+});
+
+test("links", async ({ page }) => {
+  await page.goto("http://localhost:2020/other");
+
+  // Expect clicking the link to /other/123 to navigate to /other/123
+  const link = page.locator("a[href='/other/123']");
+  await link.click();
+  await expect(page).toHaveURL("http://localhost:2020/other/123");
+});
+
+test("fallback", async ({ page }) => {
+  await page.goto("http://localhost:2020/my/404/route");
+
+  // Expect the page to contain the text Fallback
+  const main = page.locator("#not-found");
+  await expect(main).toContainText("NotFound");
+
+});

+ 2 - 0
packages/playwright-tests/web-routing/.gitignore

@@ -0,0 +1,2 @@
+dist
+target

+ 10 - 0
packages/playwright-tests/web-routing/Cargo.toml

@@ -0,0 +1,10 @@
+[package]
+name = "dioxus-playwright-web-routing-test"
+version = "0.0.1"
+edition = "2021"
+description = "Playwright test for Dioxus Web Routing"
+license = "MIT OR Apache-2.0"
+publish = false
+
+[dependencies]
+dioxus = { workspace = true, features = ["web", "router"]}

+ 57 - 0
packages/playwright-tests/web-routing/src/main.rs

@@ -0,0 +1,57 @@
+use dioxus::prelude::*;
+
+fn main() {
+    launch(|| {
+        rsx! {
+            Router::<Route> {}
+        }
+    })
+}
+
+#[derive(Routable, Clone, PartialEq)]
+#[rustfmt::skip]
+enum Route {
+    #[redirect("/",|| Route::Other)]
+    #[route("/other")]
+    Other,
+    #[route("/other/:id")]
+    OtherId { id: String },
+    #[route("/:..segments")]
+    NotFound { segments: Vec<String> },
+}
+
+#[component]
+fn Other() -> Element {
+    rsx! {
+        div {
+            id: "other",
+            "Other"
+        }
+
+        Link {
+            id: "other-id-link",
+            to: Route::OtherId { id: "123".to_string() },
+            "go to OtherId"
+        }
+    }
+}
+
+#[component]
+fn OtherId(id: String) -> Element {
+    rsx! {
+        div {
+            id: "other-id",
+            "OtherId {id}"
+        }
+    }
+}
+
+#[component]
+fn NotFound(segments: Vec<String>) -> Element {
+    rsx! {
+        div {
+            id: "not-found",
+            "NotFound {segments:?}"
+        }
+    }
+}

+ 13 - 2
packages/router/src/contexts/router.rs

@@ -141,16 +141,27 @@ impl RouterContext {
             site_map: R::SITE_MAP,
         };
 
+        let history = history();
+
         // set the updater
-        history().updater(Arc::new(move || {
+        history.updater(Arc::new(move || {
             for &rc in subscribers.lock().unwrap().iter() {
                 rc.mark_dirty();
             }
         }));
 
-        Self {
+        let myself = Self {
             inner: CopyValue::new_in_scope(myself, ScopeId::ROOT),
+        };
+
+        // If the current route is different from the one in the browser, replace the current route
+        let current_route: R = myself.current();
+
+        if current_route.to_string() != history.current_route() {
+            myself.replace(current_route);
         }
+
+        myself
     }
 
     /// Check if the router is running in a liveview context