Skip to content

Commit

Permalink
Fix internal URL not in sync
Browse files Browse the repository at this point in the history
  • Loading branch information
JeremyHolcomb committed Apr 24, 2019
1 parent 1ebfec7 commit 54bc415
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 23 deletions.
3 changes: 2 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"args": [
"--config=jest.config.js",
"--runInBand",
"--no-cache"
"--no-cache",
"--testNamePattern=."
]
}
]
Expand Down
153 changes: 136 additions & 17 deletions src/minimalReactRouter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand All @@ -20,15 +32,8 @@ test("can return result from matching route", async () => {
const [component = null] = router.useRoutes(routes);
return component;
}
const { asFragment, getByText } = render(<App />);
const { getByText } = render(<App />);
await waitForElement(() => getByText("1"));
expect(asFragment()).toMatchInlineSnapshot(`
<DocumentFragment>
<div>
1
</div>
</DocumentFragment>
`);
});

test("can return result from multiple routes", async () => {
Expand All @@ -43,15 +48,37 @@ test("can return result from multiple routes", async () => {
const [Component = () => null] = router.useRoutes(appRoutes);
return <Component />;
}
const { asFragment, getByText } = render(<App />);
const { getByText } = render(<App />);
await waitForElement(() => getByText("foo"));
});

test("returns correct result from routes with wildcard", async () => {
const router = createRouter(window.history, "/foo");
const routes = {
"/foo": () => <div>foo</div>,
"/*": () => <div>wildcard</div>
};
function App() {
const [component = null] = router.useRoutes(routes);
return component;
}
const { getByText } = render(<App />);
await waitForElement(() => getByText("foo"));
});

test("can navigate to new route", async () => {
const router = createRouter(window.history, "/");
const routes = {
"/foo": () => <div>foo</div>,
"/*": () => <div>wildcard</div>
};
function App() {
const [component = null] = router.useRoutes(routes);
return component;
}
const { getByText } = render(<App />);
router.push("/foo");
await waitForElement(() => getByText("foo"));
expect(asFragment()).toMatchInlineSnapshot(`
<DocumentFragment>
<div>
foo
</div>
</DocumentFragment>
`);
});

test("does not call setState on unmounted route", async () => {
Expand All @@ -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(() => <div>foo</div>);
const Default = () => <div>default</div>;
const Wildcard = () => <div>wildcard</div>;
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 (
<React.Suspense fallback={<div>loading</div>}>
<Component />
</React.Suspense>
);
}
const { getByText } = render(<App />);
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", () => (
<div>default</div>
));
const LazyFooBar = makeLazy(() => <div>foobar</div>);
const RouteLoader = React.memo(function RouteLoader({ routes }) {
const [Component] = router.useRoutes(routes);
return (
<React.Suspense fallback={<div>loading</div>}>
<Component />
</React.Suspense>
);
});
const fooRoutes = {
"/foo/bar": () => LazyFooBar
};
const appRoutes = {
"/foo/*": () => makeLazy(() => <RouteLoader routes={fooRoutes} />)
};
function App() {
return <RouteLoader routes={appRoutes} />;
}
const { getByText } = render(<App />);
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": () => <div>foo</div>,
"/bar": ({ redirect }) => redirect("/foo")
};
function App() {
const [component = null] = router.useRoutes(routes);
return component;
}
const { getByText } = render(<App />);
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 <div>{stringValues.join()}</div>;
};
}
};
function App() {
const [Component, props] = router.useRoutes(routes);
return Component && <Component {...props} />;
}
const { getByText } = render(<App />);
await waitForElement(() => getByText("/a/b/c?q=#h,/a/b/c?q=#h,b,c,b,c"));
});
7 changes: 3 additions & 4 deletions src/minimalReactRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const [setRoute] = resolveLatest(async function setRoute(
): Promise<void> {
// Only set the route if it has changed.
if (!new PathURL(url).matches(internalState.url)) {
internalState.url = url;
historyFn(null, "", url);
}
const { routeResolvers } = internalState;
Expand All @@ -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) {
Expand Down
1 change: 0 additions & 1 deletion src/utils/resolveRouteAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ export async function resolveRouteAction(
if (!isSameState) {
setState({ parameters, path: pathURL, result });
}
internalState.url = url;
redirectStack.length = 0;
return true;
}

0 comments on commit 54bc415

Please sign in to comment.