diff --git a/Cargo.lock b/Cargo.lock index 70b9b8183..5e4890595 100644 --- a/Cargo.lock +++ b/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" diff --git a/Cargo.toml b/Cargo.toml index 88e294507..c98029864 100644 --- a/Cargo.toml +++ b/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", diff --git a/packages/playwright-tests/playwright.config.js b/packages/playwright-tests/playwright.config.js index 321935dd0..6dfeeb7b3 100644 --- a/packages/playwright-tests/playwright.config.js +++ b/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: diff --git a/packages/playwright-tests/web-routing.spec.js b/packages/playwright-tests/web-routing.spec.js new file mode 100644 index 000000000..f380ae2b3 --- /dev/null +++ b/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"); + +}); diff --git a/packages/playwright-tests/web-routing/.gitignore b/packages/playwright-tests/web-routing/.gitignore new file mode 100644 index 000000000..283c01d61 --- /dev/null +++ b/packages/playwright-tests/web-routing/.gitignore @@ -0,0 +1,2 @@ +dist +target diff --git a/packages/playwright-tests/web-routing/Cargo.toml b/packages/playwright-tests/web-routing/Cargo.toml new file mode 100644 index 000000000..bafb50727 --- /dev/null +++ b/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"]} diff --git a/packages/playwright-tests/web-routing/src/main.rs b/packages/playwright-tests/web-routing/src/main.rs new file mode 100644 index 000000000..bf512d792 --- /dev/null +++ b/packages/playwright-tests/web-routing/src/main.rs @@ -0,0 +1,57 @@ +use dioxus::prelude::*; + +fn main() { + launch(|| { + rsx! { + Router:: {} + } + }) +} + +#[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 }, +} + +#[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) -> Element { + rsx! { + div { + id: "not-found", + "NotFound {segments:?}" + } + } +} diff --git a/packages/router/src/contexts/router.rs b/packages/router/src/contexts/router.rs index 2abccfdc9..1a0d9425e 100644 --- a/packages/router/src/contexts/router.rs +++ b/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