From 54bc415c833325d0ce24d12b196c19d379879b9b Mon Sep 17 00:00:00 2001 From: JeremyHolcomb Date: Wed, 24 Apr 2019 16:04:41 -0700 Subject: [PATCH] Fix internal URL not in sync --- .vscode/launch.json | 3 +- src/minimalReactRouter.test.js | 153 ++++++++++++++++++++++++++++---- src/minimalReactRouter.ts | 7 +- src/utils/resolveRouteAction.ts | 1 - 4 files changed, 141 insertions(+), 23 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 2432bc5..d841010 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,8 @@ "args": [ "--config=jest.config.js", "--runInBand", - "--no-cache" + "--no-cache", + "--testNamePattern=." ] } ] diff --git a/src/minimalReactRouter.test.js b/src/minimalReactRouter.test.js index 9e216d8..0343f43 100644 --- a/src/minimalReactRouter.test.js +++ b/src/minimalReactRouter.test.js @@ -4,10 +4,22 @@ import ReactDOM from "react-dom"; import { createRouter } from "./minimalReactRouter"; import { cleanup, render, waitForElement, wait } from "react-testing-library"; +function makeLazy(Component) { + return React.lazy( + () => new Promise(r => setTimeout(r, 100, { default: Component })) + ); +} + afterEach(cleanup); test("can create router", () => { - expect(createRouter(window.history, "/")).toBeTruthy(); + expect(createRouter(window.history, "/")).toMatchInlineSnapshot(` + Object { + "push": [Function], + "replace": [Function], + "useRoutes": [Function], + } + `); }); test("can return result from matching route", async () => { @@ -20,15 +32,8 @@ test("can return result from matching route", async () => { const [component = null] = router.useRoutes(routes); return component; } - const { asFragment, getByText } = render(); + const { getByText } = render(); await waitForElement(() => getByText("1")); - expect(asFragment()).toMatchInlineSnapshot(` - -
- 1 -
-
- `); }); test("can return result from multiple routes", async () => { @@ -43,15 +48,37 @@ test("can return result from multiple routes", async () => { const [Component = () => null] = router.useRoutes(appRoutes); return ; } - const { asFragment, getByText } = render(); + const { getByText } = render(); + await waitForElement(() => getByText("foo")); +}); + +test("returns correct result from routes with wildcard", async () => { + const router = createRouter(window.history, "/foo"); + const routes = { + "/foo": () =>
foo
, + "/*": () =>
wildcard
+ }; + function App() { + const [component = null] = router.useRoutes(routes); + return component; + } + const { getByText } = render(); + await waitForElement(() => getByText("foo")); +}); + +test("can navigate to new route", async () => { + const router = createRouter(window.history, "/"); + const routes = { + "/foo": () =>
foo
, + "/*": () =>
wildcard
+ }; + function App() { + const [component = null] = router.useRoutes(routes); + return component; + } + const { getByText } = render(); + router.push("/foo"); await waitForElement(() => getByText("foo")); - expect(asFragment()).toMatchInlineSnapshot(` - -
- foo -
-
- `); }); test("does not call setState on unmounted route", async () => { @@ -73,3 +100,95 @@ test("does not call setState on unmounted route", async () => { console.error.mockRestore(); }); }); + +test("can lazy-load routes", async () => { + const LazyFoo = makeLazy(() =>
foo
); + const Default = () =>
default
; + const Wildcard = () =>
wildcard
; + const router = createRouter(window.history, "/", Default); + const routes = { + "/foo": () => LazyFoo, + "/*": () => Wildcard + }; + function App() { + const [Component] = router.useRoutes(routes); + React.useEffect(() => { + setTimeout(() => router.push("/foo"), 100); + }, []); + return ( + loading}> + + + ); + } + const { getByText } = render(); + await waitForElement(() => getByText("default")); + await waitForElement(() => getByText("wildcard")); + await waitForElement(() => getByText("loading")); + await waitForElement(() => getByText("foo")); +}); + +test("can lazy-load lazy-loaded routes", async () => { + const router = createRouter(window.history, "/foo/bar", () => ( +
default
+ )); + const LazyFooBar = makeLazy(() =>
foobar
); + const RouteLoader = React.memo(function RouteLoader({ routes }) { + const [Component] = router.useRoutes(routes); + return ( + loading}> + + + ); + }); + const fooRoutes = { + "/foo/bar": () => LazyFooBar + }; + const appRoutes = { + "/foo/*": () => makeLazy(() => ) + }; + function App() { + return ; + } + const { getByText } = render(); + await waitForElement(() => getByText("default")); + await waitForElement(() => getByText("loading")); + await waitForElement(() => getByText("foobar")); +}); + +test("can redirect to new route", async () => { + const router = createRouter(window.history, "/bar"); + const routes = { + "/foo": () =>
foo
, + "/bar": ({ redirect }) => redirect("/foo") + }; + function App() { + const [component = null] = router.useRoutes(routes); + return component; + } + const { getByText } = render(); + await waitForElement(() => getByText("foo")); +}); + +test("has path and parameters", async () => { + const router = createRouter(window.history, "/a/b/c?q#h", null); + const routes = { + "/a/:bar/:baz": ({ path, parameters }) => { + return ({ path: path2, parameters: parameters2 }) => { + const stringValues = [ + path.toString(), + path2.toString(), + ...parameters, + ...parameters2 + ]; + return
{stringValues.join()}
; + }; + } + }; + function App() { + const [Component, props] = router.useRoutes(routes); + return Component && ; + } + const { getByText } = render(); + await waitForElement(() => getByText("/a/b/c?q=#h,/a/b/c?q=#h,b,c,b,c")); +}); diff --git a/src/minimalReactRouter.ts b/src/minimalReactRouter.ts index 5f4f12b..332cf28 100644 --- a/src/minimalReactRouter.ts +++ b/src/minimalReactRouter.ts @@ -27,6 +27,7 @@ const [setRoute] = resolveLatest(async function setRoute( ): Promise { // Only set the route if it has changed. if (!new PathURL(url).matches(internalState.url)) { + internalState.url = url; historyFn(null, "", url); } const { routeResolvers } = internalState; @@ -36,10 +37,8 @@ const [setRoute] = resolveLatest(async function setRoute( const resolveRoute = routeResolvers[(resolverKey as unknown) as string]; if (resolveRoute) { const status = await resolveRoute(url); - if (status === undefined) { - return; - } - anyMatch = anyMatch || status; + if (status === undefined) return; + if (status === true) anyMatch = true; } } if (!anyMatch) { diff --git a/src/utils/resolveRouteAction.ts b/src/utils/resolveRouteAction.ts index baca1a3..2c2ac27 100644 --- a/src/utils/resolveRouteAction.ts +++ b/src/utils/resolveRouteAction.ts @@ -55,7 +55,6 @@ export async function resolveRouteAction( if (!isSameState) { setState({ parameters, path: pathURL, result }); } - internalState.url = url; redirectStack.length = 0; return true; }