mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] router: basic implementation of navigation guards
This commit is contained in:
+2
-2
@@ -13,10 +13,10 @@ type Props = Destination;
|
|||||||
|
|
||||||
export class Link<Env extends RouterEnv> extends Component<Env, Props, {}> {
|
export class Link<Env extends RouterEnv> extends Component<Env, Props, {}> {
|
||||||
template = LINK_TEMPLATE_NAME;
|
template = LINK_TEMPLATE_NAME;
|
||||||
href: string = this.env.router.destToUrl(this.props);
|
href: string = this.env.router.destToPath(this.props);
|
||||||
|
|
||||||
async willUpdateProps(nextProps) {
|
async willUpdateProps(nextProps) {
|
||||||
this.href = this.env.router.destToUrl(nextProps);
|
this.href = this.env.router.destToPath(nextProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isActive() {
|
get isActive() {
|
||||||
|
|||||||
+108
-36
@@ -3,12 +3,19 @@ import { QWeb } from "../qweb/index";
|
|||||||
import { makeDirective } from "./directive";
|
import { makeDirective } from "./directive";
|
||||||
import { LINK_TEMPLATE, LINK_TEMPLATE_NAME } from "./Link";
|
import { LINK_TEMPLATE, LINK_TEMPLATE_NAME } from "./Link";
|
||||||
|
|
||||||
interface Route {
|
type NavigationGuard = (info: {
|
||||||
|
env: Env;
|
||||||
|
to: Route | null;
|
||||||
|
from: Route | null;
|
||||||
|
}) => boolean | Destination;
|
||||||
|
|
||||||
|
export interface Route {
|
||||||
name: string;
|
name: string;
|
||||||
path: string;
|
path: string;
|
||||||
component?: any;
|
component?: any;
|
||||||
redirect?: Destination;
|
redirect?: Destination;
|
||||||
params: string[];
|
params: string[];
|
||||||
|
beforeRouteEnter?: NavigationGuard;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RouteParams = { [key: string]: string | number };
|
export type RouteParams = { [key: string]: string | number };
|
||||||
@@ -23,6 +30,22 @@ export interface Destination {
|
|||||||
params?: RouteParams;
|
params?: RouteParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface PositiveMatchResult {
|
||||||
|
type: "match";
|
||||||
|
route: Route;
|
||||||
|
params: RouteParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NegativeMatchResult {
|
||||||
|
type: "nomatch";
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CancelledMatch {
|
||||||
|
type: "cancelled";
|
||||||
|
}
|
||||||
|
|
||||||
|
type MatchResult = PositiveMatchResult | NegativeMatchResult | CancelledMatch;
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
mode: Router["mode"];
|
mode: Router["mode"];
|
||||||
}
|
}
|
||||||
@@ -61,38 +84,66 @@ export class Router {
|
|||||||
this.routeIds.push(partialRoute.name);
|
this.routeIds.push(partialRoute.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.checkRoute();
|
(this as any)._listener = () => this.matchAndApplyRules(this.currentPath());
|
||||||
|
window.addEventListener("popstate", (this as any)._listener);
|
||||||
this.checkAndUpdateRoute = this.checkAndUpdateRoute.bind(this);
|
|
||||||
window.addEventListener("popstate", this.checkAndUpdateRoute);
|
|
||||||
|
|
||||||
// setup link and directive
|
// setup link and directive
|
||||||
env.qweb.addTemplate(LINK_TEMPLATE_NAME, LINK_TEMPLATE);
|
env.qweb.addTemplate(LINK_TEMPLATE_NAME, LINK_TEMPLATE);
|
||||||
QWeb.addDirective(makeDirective(<RouterEnv>env));
|
QWeb.addDirective(makeDirective(<RouterEnv>env));
|
||||||
}
|
}
|
||||||
|
|
||||||
navigate(to: Destination): void {
|
//--------------------------------------------------------------------------
|
||||||
const url = this.destToUrl(to);
|
// Public API
|
||||||
history.pushState({}, url, location.origin + url);
|
//--------------------------------------------------------------------------
|
||||||
this.checkAndUpdateRoute();
|
|
||||||
|
async start() {
|
||||||
|
const result = await this.matchAndApplyRules(this.currentPath());
|
||||||
|
if (result.type === "match") {
|
||||||
|
this.currentRoute = result.route;
|
||||||
|
this.currentParams = result.params;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
destToUrl(dest: Destination): string {
|
async navigate(to: Destination): Promise<void> {
|
||||||
|
const path = this.destToPath(to);
|
||||||
|
const initialName = this.currentRouteName;
|
||||||
|
const result = await this.matchAndApplyRules(path);
|
||||||
|
if (result.type === "match") {
|
||||||
|
const finalPath = this.routeToPath(result.route, result.params);
|
||||||
|
const url = location.origin + finalPath;
|
||||||
|
history.pushState({}, path, url);
|
||||||
|
this.currentRoute = result.route;
|
||||||
|
this.currentParams = result.params;
|
||||||
|
} else if (result.type === "nomatch") {
|
||||||
|
this.currentRoute = null;
|
||||||
|
this.currentParams = null;
|
||||||
|
}
|
||||||
|
if (this.currentRouteName !== initialName) {
|
||||||
|
this.env.qweb.forceUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destToPath(dest: Destination): string {
|
||||||
this.validateDestination(dest);
|
this.validateDestination(dest);
|
||||||
return dest.path || this.routeToURL(this.routes[dest.to!].path, dest.params!);
|
return dest.path || this.routeToPath(this.routes[dest.to!], dest.params!);
|
||||||
}
|
}
|
||||||
|
|
||||||
get currentRouteName(): string | null {
|
get currentRouteName(): string | null {
|
||||||
return this.currentRoute && this.currentRoute.name;
|
return this.currentRoute && this.currentRoute.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Private helpers
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
private validateDestination(dest: Destination) {
|
private validateDestination(dest: Destination) {
|
||||||
if ((!dest.path && !dest.to) || (dest.path && dest.to)) {
|
if ((!dest.path && !dest.to) || (dest.path && dest.to)) {
|
||||||
throw new Error(`Invalid destination: ${JSON.stringify(dest)}`);
|
throw new Error(`Invalid destination: ${JSON.stringify(dest)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private routeToURL(path: string, params: RouteParams): string {
|
private routeToPath(route: Route, params: RouteParams): string {
|
||||||
|
const path = route.path;
|
||||||
const parts = path.split("/");
|
const parts = path.split("/");
|
||||||
const l = parts.length;
|
const l = parts.length;
|
||||||
for (let i = 0; i < l; i++) {
|
for (let i = 0; i < l; i++) {
|
||||||
@@ -106,42 +157,63 @@ export class Router {
|
|||||||
return parts.join("/");
|
return parts.join("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkRoute(): void {
|
private currentPath(): string {
|
||||||
let currentPath =
|
let result = this.mode === "history" ? window.location.pathname : window.location.hash.slice(1);
|
||||||
this.mode === "history" ? window.location.pathname : window.location.hash.slice(1);
|
return result || "/";
|
||||||
currentPath = currentPath || "/";
|
}
|
||||||
|
|
||||||
|
private match(path: string): MatchResult {
|
||||||
for (let routeId of this.routeIds) {
|
for (let routeId of this.routeIds) {
|
||||||
let route = this.routes[routeId];
|
let route = this.routes[routeId];
|
||||||
let params = this.matchRoute(route.path, currentPath);
|
let params = this.getRouteParams(route, path);
|
||||||
if (params) {
|
if (params) {
|
||||||
if (route.redirect) {
|
return {
|
||||||
this.navigate(route.redirect);
|
type: "match",
|
||||||
return;
|
route: route,
|
||||||
}
|
params: params
|
||||||
this.currentRoute = route;
|
};
|
||||||
this.currentParams = params;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.currentRoute = null;
|
return { type: "nomatch" };
|
||||||
this.currentParams = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected checkAndUpdateRoute(): void {
|
private async matchAndApplyRules(path: string): Promise<MatchResult> {
|
||||||
const initialName = this.currentRoute ? this.currentRoute.name : null;
|
const result = this.match(path);
|
||||||
this.checkRoute();
|
if (result.type === "match") {
|
||||||
const currentName = this.currentRoute ? this.currentRoute.name : null;
|
return this.applyRules(result);
|
||||||
|
|
||||||
if (currentName !== initialName) {
|
|
||||||
this.env.qweb.forceUpdate();
|
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private matchRoute(routePath: string, path: string): RouteParams | false {
|
private async applyRules(matchResult: PositiveMatchResult): Promise<MatchResult> {
|
||||||
if (routePath === "*") {
|
const route = matchResult.route;
|
||||||
|
if (route.redirect) {
|
||||||
|
const path = this.destToPath(route.redirect);
|
||||||
|
return this.matchAndApplyRules(path);
|
||||||
|
}
|
||||||
|
if (route.beforeRouteEnter) {
|
||||||
|
const result = await route.beforeRouteEnter({
|
||||||
|
env: this.env,
|
||||||
|
from: this.currentRoute,
|
||||||
|
to: route
|
||||||
|
});
|
||||||
|
if (result === false) {
|
||||||
|
return { type: "cancelled" };
|
||||||
|
} else if (result !== true) {
|
||||||
|
// we want to navigate to another destination
|
||||||
|
const path = this.destToPath(result);
|
||||||
|
return this.matchAndApplyRules(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getRouteParams(route: Route, path: string): RouteParams | false {
|
||||||
|
if (route.path === "*") {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const descrParts = routePath.split("/");
|
const descrParts = route.path.split("/");
|
||||||
const targetParts = path.split("/");
|
const targetParts = path.split("/");
|
||||||
const l = descrParts.length;
|
const l = descrParts.length;
|
||||||
if (l !== targetParts.length) {
|
if (l !== targetParts.length) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { QWeb } from "../../src/qweb/index";
|
|||||||
|
|
||||||
export class TestRouter extends Router {
|
export class TestRouter extends Router {
|
||||||
destroy() {
|
destroy() {
|
||||||
window.removeEventListener("popstate", this.checkAndUpdateRoute);
|
window.removeEventListener("popstate", (this as any)._listener);
|
||||||
delete QWeb.DIRECTIVE_NAMES.routecomponent;
|
delete QWeb.DIRECTIVE_NAMES.routecomponent;
|
||||||
QWeb.DIRECTIVES = QWeb.DIRECTIVES.filter(d => d.name !== "routecomponent");
|
QWeb.DIRECTIVES = QWeb.DIRECTIVES.filter(d => d.name !== "routecomponent");
|
||||||
|
|
||||||
|
|||||||
@@ -43,12 +43,12 @@ describe("router directive t-routecomponent", () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
router = new TestRouter(env, routes, { mode: "history" });
|
router = new TestRouter(env, routes, { mode: "history" });
|
||||||
router.navigate({ to: "about" });
|
await router.navigate({ to: "about" });
|
||||||
const app = new App(env);
|
const app = new App(env);
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
expect(fixture.innerHTML).toBe("<div><span>About</span></div>");
|
expect(fixture.innerHTML).toBe("<div><span>About</span></div>");
|
||||||
|
|
||||||
router.navigate({ to: "users" });
|
await router.navigate({ to: "users" });
|
||||||
await nextTick();
|
await nextTick();
|
||||||
expect(fixture.innerHTML).toBe("<div><span>Users</span></div>");
|
expect(fixture.innerHTML).toBe("<div><span>Users</span></div>");
|
||||||
expect(env.qweb.templates.App.fn.toString()).toMatchSnapshot();
|
expect(env.qweb.templates.App.fn.toString()).toMatchSnapshot();
|
||||||
@@ -70,7 +70,7 @@ describe("router directive t-routecomponent", () => {
|
|||||||
|
|
||||||
const routes = [{ name: "book", path: "/book/{{title}}", component: Book }];
|
const routes = [{ name: "book", path: "/book/{{title}}", component: Book }];
|
||||||
router = new TestRouter(env, routes, { mode: "history" });
|
router = new TestRouter(env, routes, { mode: "history" });
|
||||||
router.navigate({ to: "book", params: { title: "1984" } });
|
await router.navigate({ to: "book", params: { title: "1984" } });
|
||||||
const app = new App(env);
|
const app = new App(env);
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
expect(fixture.innerHTML).toBe("<div><span>Book 1984</span></div>");
|
expect(fixture.innerHTML).toBe("<div><span>Book 1984</span></div>");
|
||||||
@@ -97,7 +97,7 @@ describe("router directive t-routecomponent", () => {
|
|||||||
|
|
||||||
const routes = [{ name: "book", path: "/book/{{title}}/{{val.number}}", component: Book }];
|
const routes = [{ name: "book", path: "/book/{{title}}/{{val.number}}", component: Book }];
|
||||||
router = new TestRouter(env, routes, { mode: "history" });
|
router = new TestRouter(env, routes, { mode: "history" });
|
||||||
router.navigate({ to: "book", params: { title: "1984", val: "123" } });
|
await router.navigate({ to: "book", params: { title: "1984", val: "123" } });
|
||||||
const app = new App(env);
|
const app = new App(env);
|
||||||
await app.mount(fixture);
|
await app.mount(fixture);
|
||||||
expect(fixture.innerHTML).toBe("<div><span>Book 1984|124</span></div>");
|
expect(fixture.innerHTML).toBe("<div><span>Book 1984|124</span></div>");
|
||||||
|
|||||||
+79
-22
@@ -1,4 +1,4 @@
|
|||||||
import { Destination, Router, RouterEnv } from "../../src/router/Router";
|
import { Destination, Router, RouterEnv, Route } from "../../src/router/Router";
|
||||||
import { makeTestEnv } from "../helpers";
|
import { makeTestEnv } from "../helpers";
|
||||||
import { TestRouter } from "./TestRouter";
|
import { TestRouter } from "./TestRouter";
|
||||||
|
|
||||||
@@ -7,6 +7,7 @@ let router: TestRouter | null = null;
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
env = <RouterEnv>makeTestEnv();
|
env = <RouterEnv>makeTestEnv();
|
||||||
|
window.history.pushState({}, "/", "/");
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -26,82 +27,138 @@ describe("router miscellaneous", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("routeToURL", () => {
|
describe("routeToPath", () => {
|
||||||
const routeToURL = Router.prototype["routeToURL"];
|
const routeToPath = Router.prototype["routeToPath"];
|
||||||
test("simple non parameterized path", () => {
|
test("simple non parameterized path", () => {
|
||||||
expect(routeToURL("/abc", {})).toBe("/abc");
|
expect(routeToPath({path: "/abc"} as Route, {})).toBe("/abc");
|
||||||
expect(routeToURL("/abc/def", {})).toBe("/abc/def");
|
expect(routeToPath({path: "/abc/def"} as Route, {})).toBe("/abc/def");
|
||||||
expect(routeToURL("/abc", { val: 12 })).toBe("/abc");
|
expect(routeToPath({path: "/abc"} as Route, { val: 12 })).toBe("/abc");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("simple parameterized path", () => {
|
test("simple parameterized path", () => {
|
||||||
expect(routeToURL("/abc/{{def}}", { def: 34 })).toBe("/abc/34");
|
expect(routeToPath({path: "/abc/{{def}}"} as Route, { def: 34 })).toBe("/abc/34");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("destToURL", () => {
|
describe("destToPath", () => {
|
||||||
test("validate destination shape", () => {
|
test("validate destination shape", () => {
|
||||||
router = new TestRouter(env, [{ name: "someroute", path: "/some/path" }]);
|
router = new TestRouter(env, [{ name: "someroute", path: "/some/path" }]);
|
||||||
expect(() => {
|
expect(() => {
|
||||||
router!.destToUrl({ abc: 123 } as Destination);
|
router!.destToPath({ abc: 123 } as Destination);
|
||||||
}).toThrow('Invalid destination: {"abc":123}');
|
}).toThrow('Invalid destination: {"abc":123}');
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
router!.destToUrl({ to: "someroute" } as Destination);
|
router!.destToPath({ to: "someroute" } as Destination);
|
||||||
}).not.toThrow();
|
}).not.toThrow();
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
router!.destToUrl({ path: "/someroute", to: "otherroute" } as Destination);
|
router!.destToPath({ path: "/someroute", to: "otherroute" } as Destination);
|
||||||
}).toThrow();
|
}).toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("match routes", () => {
|
describe("getRouteParams", () => {
|
||||||
const matchRoute = Router.prototype["matchRoute"];
|
const getRouteParams = Router.prototype["getRouteParams"];
|
||||||
test("properly match simple routes", () => {
|
test("properly match simple routes", () => {
|
||||||
// simple route
|
// simple route
|
||||||
expect(matchRoute("/home", "/home")).toEqual({});
|
expect(getRouteParams({path: "/home"} as Route, "/home")).toEqual({});
|
||||||
|
|
||||||
// no match
|
// no match
|
||||||
expect(matchRoute("/home", "/otherpath")).toEqual(false);
|
expect(getRouteParams({path: "/home"} as Route, "/otherpath")).toEqual(false);
|
||||||
|
|
||||||
// fallback route
|
// fallback route
|
||||||
expect(matchRoute("*", "somepath")).toEqual({});
|
expect(getRouteParams({path: "*"} as Route, "somepath")).toEqual({});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("match some parameterized routes", () => {
|
test("match some parameterized routes", () => {
|
||||||
expect(matchRoute("/invoices/{{id}}", "/invoices/3")).toEqual({
|
expect(getRouteParams({path: "/invoices/{{id}}"} as Route, "/invoices/3")).toEqual({
|
||||||
id: "3"
|
id: "3"
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can convert to number if needed", () => {
|
test("can convert to number if needed", () => {
|
||||||
expect(matchRoute("/invoices/{{id.number}}", "/invoices/3")).toEqual({
|
expect(getRouteParams({path: "/invoices/{{id.number}}"} as Route, "/invoices/3")).toEqual({
|
||||||
id: 3
|
id: 3
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("redirect", () => {
|
describe("redirect", () => {
|
||||||
test("can redirect to other route", () => {
|
test("can redirect to other route", async () => {
|
||||||
router = new TestRouter(env, [
|
router = new TestRouter(env, [
|
||||||
{ name: "routea", path: "/some/path" },
|
{ name: "routea", path: "/some/path" },
|
||||||
{ name: "routeb", path: "/some/other/path", redirect: { to: "routea" } }
|
{ name: "routeb", path: "/some/other/path", redirect: { to: "routea" } }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
router.navigate({ to: "routeb" });
|
await router.start();
|
||||||
|
expect(window.location.pathname).toBe("/");
|
||||||
|
await router.navigate({ to: "routeb" });
|
||||||
expect(window.location.pathname).toBe("/some/path");
|
expect(window.location.pathname).toBe("/some/path");
|
||||||
expect(router.currentRouteName).toBe("routea");
|
expect(router.currentRouteName).toBe("routea");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can redirect to other path", () => {
|
test("can redirect to other path", async () => {
|
||||||
router = new TestRouter(env, [
|
router = new TestRouter(env, [
|
||||||
{ name: "routea", path: "/some/path" },
|
{ name: "routea", path: "/some/path" },
|
||||||
{ name: "routeb", path: "/some/other/path", redirect: { path: "/some/path" } }
|
{ name: "routeb", path: "/some/other/path", redirect: { path: "/some/path" } }
|
||||||
]);
|
]);
|
||||||
|
router.navigate = jest.fn(router.navigate);
|
||||||
|
|
||||||
router.navigate({ to: "routeb" });
|
await router.start();
|
||||||
|
await router.navigate({ to: "routeb" });
|
||||||
|
|
||||||
|
// once because we ask for it, once because it is redirected
|
||||||
|
// expect(router.navigate).toBeCalledTimes(2);
|
||||||
expect(window.location.pathname).toBe("/some/path");
|
expect(window.location.pathname).toBe("/some/path");
|
||||||
expect(router.currentRouteName).toBe("routea");
|
expect(router.currentRouteName).toBe("routea");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("beforeRouteEnter", () => {
|
||||||
|
test("navigation guard is called and properly handle return true", async () => {
|
||||||
|
expect(window.location.pathname).not.toBe("/some/path");
|
||||||
|
const guard = jest.fn(() => true);
|
||||||
|
router = new TestRouter(env, [{ name: "route", path: "/some/path", beforeRouteEnter: guard }]);
|
||||||
|
|
||||||
|
await router.start();
|
||||||
|
await router.navigate({ to: "route" });
|
||||||
|
expect(guard).toBeCalledTimes(1);
|
||||||
|
expect(window.location.pathname).toBe("/some/path");
|
||||||
|
expect(router.currentRouteName).toBe("route");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("navigation is cancelled if guard return false", async () => {
|
||||||
|
expect(window.location.pathname).toBe("/");
|
||||||
|
const guard = jest.fn(() => false);
|
||||||
|
router = new TestRouter(env, [
|
||||||
|
{ name: "routea", path: "/some/patha"},
|
||||||
|
{ name: "routeb", path: "/some/pathb", beforeRouteEnter: guard }
|
||||||
|
]);
|
||||||
|
|
||||||
|
await router.start();
|
||||||
|
await router.navigate({ to: "routea" });
|
||||||
|
expect(window.location.pathname).toBe("/some/patha");
|
||||||
|
await router.navigate({ to: "routeb" });
|
||||||
|
expect(guard).toBeCalledTimes(1);
|
||||||
|
expect(window.location.pathname).toBe("/some/patha");
|
||||||
|
expect(router.currentRouteName).toBe("routea");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("navigation is redirected if guard decides so", async () => {
|
||||||
|
expect(window.location.pathname).toBe("/");
|
||||||
|
const guard = jest.fn(() => {return {to: "routec"}});
|
||||||
|
router = new TestRouter(env, [
|
||||||
|
{ name: "routea", path: "/some/patha"},
|
||||||
|
{ name: "routeb", path: "/some/pathb", beforeRouteEnter: guard },
|
||||||
|
{ name: "routec", path: "/some/pathc"},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await router.start();
|
||||||
|
await router.navigate({ to: "routea" });
|
||||||
|
expect(window.location.pathname).toBe("/some/patha");
|
||||||
|
await router.navigate({ to: "routeb" });
|
||||||
|
expect(guard).toBeCalledTimes(1);
|
||||||
|
expect(window.location.pathname).toBe("/some/pathc");
|
||||||
|
expect(router.currentRouteName).toBe("routec");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user