mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[ADD] router: initial implementation
This commit is contained in:
@@ -30,6 +30,7 @@ import "./props_validation";
|
||||
*/
|
||||
export interface Env {
|
||||
qweb: QWeb;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,10 +10,13 @@ import { QWeb } from "./qweb/index";
|
||||
import { ConnectedComponent } from "./store/connected_component";
|
||||
import { Store } from "./store/store";
|
||||
import * as _utils from "./utils";
|
||||
import { Link } from "./router/Link";
|
||||
import { activate } from "./router/plugin";
|
||||
|
||||
export { Component } from "./component/component";
|
||||
export { QWeb };
|
||||
export const core = { EventBus, Observer };
|
||||
export const router = { activate, Link };
|
||||
export const store = { Store, ConnectedComponent };
|
||||
export const utils = _utils;
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Component } from "../component/component";
|
||||
import { Destination, RouterEnv } from "./plugin";
|
||||
|
||||
export const LINK_TEMPLATE_NAME = "__owl__-router-link";
|
||||
export const LINK_TEMPLATE = `
|
||||
<a t-att-class="{'router-link-active': isActive }"
|
||||
t-att-href="href"
|
||||
t-on-click.prevent="navigate">
|
||||
<t t-slot="default"/>
|
||||
</a>`;
|
||||
|
||||
type Props = Destination;
|
||||
|
||||
export class Link<Env extends RouterEnv> extends Component<Env, Props, {}> {
|
||||
template = LINK_TEMPLATE_NAME;
|
||||
href: string = this.env.router.info.destToUrl(this.props);
|
||||
|
||||
async willUpdateProps(nextProps) {
|
||||
this.href = this.env.router.info.destToUrl(nextProps);
|
||||
}
|
||||
|
||||
get isActive() {
|
||||
if (this.env.router.info.mode === "hash") {
|
||||
return document.location.hash === this.href;
|
||||
}
|
||||
return document.location.pathname === this.href;
|
||||
}
|
||||
|
||||
navigate() {
|
||||
this.env.router.navigate(this.props);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { RouterEnv } from "./plugin";
|
||||
|
||||
export function makeDirective(env: RouterEnv) {
|
||||
return {
|
||||
name: "routecomponent",
|
||||
priority: 13,
|
||||
atNodeEncounter({ node }): boolean {
|
||||
let first = true;
|
||||
const info = env.router.info;
|
||||
for (let name of info.routeIds) {
|
||||
const route = info.routes[name];
|
||||
if (route.component) {
|
||||
// make new t t-component element
|
||||
const comp = node.ownerDocument.createElement("t");
|
||||
comp.setAttribute("t-component", "__component__" + route.name);
|
||||
comp.setAttribute(first ? "t-if" : "t-elif", `env.router.routeName === '${route.name}'`);
|
||||
first = false;
|
||||
for (let param of route.params) {
|
||||
comp.setAttribute(param, `env.router.routeParams.${param}`);
|
||||
}
|
||||
node.appendChild(comp);
|
||||
}
|
||||
}
|
||||
node.removeAttribute("t-routecomponent");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
import { Env } from "../component/component";
|
||||
import { QWeb } from "../qweb/index";
|
||||
import { makeDirective } from "./directive";
|
||||
import { LINK_TEMPLATE, LINK_TEMPLATE_NAME } from "./Link";
|
||||
|
||||
interface Route {
|
||||
name?: string;
|
||||
path: string;
|
||||
component?: any;
|
||||
}
|
||||
|
||||
export interface RouteDescription extends Route {
|
||||
name: string;
|
||||
params: string[];
|
||||
}
|
||||
|
||||
export type RouteParams = { [key: string]: string | number };
|
||||
|
||||
export interface RouterEnv extends Env {
|
||||
router: Router;
|
||||
}
|
||||
|
||||
export interface Destination {
|
||||
to?: string;
|
||||
route?: string;
|
||||
params?: RouteParams;
|
||||
}
|
||||
interface Router {
|
||||
navigate: (dest: Destination) => void;
|
||||
routeName: string | null;
|
||||
routeParams: RouteParams;
|
||||
info: RouterInfo;
|
||||
}
|
||||
|
||||
interface RouterInfo {
|
||||
mode: Options["mode"];
|
||||
routes: { [id: string]: RouteDescription };
|
||||
routeIds: string[];
|
||||
destToUrl: (dest: Destination) => string;
|
||||
}
|
||||
|
||||
interface Options {
|
||||
mode: "history" | "hash";
|
||||
}
|
||||
|
||||
const paramRegexp = /\{\{(.*?)\}\}/;
|
||||
|
||||
function findParams(str: string): string[] {
|
||||
const globalParamRegexp = /\{\{(.*?)\}\}/g;
|
||||
const result: string[] = [];
|
||||
let m;
|
||||
do {
|
||||
m = globalParamRegexp.exec(str);
|
||||
if (m) {
|
||||
result.push(m[1].split('.')[0]);
|
||||
}
|
||||
} while (m);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function activate(env: Env, routes: Route[], options?: Options) {
|
||||
// process routes and build proper internal data structures
|
||||
const mode = options ? options.mode : "hash";
|
||||
const info: RouterInfo = { routes: {}, routeIds: [], mode, destToUrl: destToURL.bind(null, env) };
|
||||
let nextId = 1;
|
||||
for (let route of routes) {
|
||||
if (!route.name) {
|
||||
route.name = "__route__" + nextId++;
|
||||
}
|
||||
if (route.component) {
|
||||
QWeb.register("__component__" + route.name, route.component);
|
||||
}
|
||||
(<RouteDescription>route).params = findParams(route.path);
|
||||
info.routes[route.name] = <RouteDescription>route;
|
||||
info.routeIds.push(route.name);
|
||||
}
|
||||
|
||||
const router: Router = { navigate, routeName: null, routeParams: {}, info };
|
||||
|
||||
env.router = router;
|
||||
|
||||
checkRoute(<RouterEnv>env);
|
||||
|
||||
function navigate(dest: Destination) {
|
||||
const to = destToURL(<RouterEnv>env, dest);
|
||||
history.pushState({}, to, location.origin + to);
|
||||
checkAndUpdateRoute(<RouterEnv>env);
|
||||
}
|
||||
|
||||
addEventListener("popstate", () => checkAndUpdateRoute(<RouterEnv>env));
|
||||
|
||||
// setup link and directive
|
||||
env.qweb.addTemplate(LINK_TEMPLATE_NAME, LINK_TEMPLATE);
|
||||
QWeb.addDirective(makeDirective(<RouterEnv>env));
|
||||
}
|
||||
|
||||
function checkRoute(env: RouterEnv): void {
|
||||
const info = env.router.info;
|
||||
let currentPath =
|
||||
info.mode === "history" ? window.location.pathname : window.location.hash.slice(1);
|
||||
currentPath = currentPath || "/";
|
||||
for (let routeId of info.routeIds) {
|
||||
let route = info.routes[routeId];
|
||||
let params = matchRoute(route, currentPath);
|
||||
if (params) {
|
||||
env.router.routeName = route.name!;
|
||||
env.router.routeParams = params;
|
||||
return;
|
||||
}
|
||||
}
|
||||
env.router.routeName = null;
|
||||
env.router.routeParams = {};
|
||||
}
|
||||
|
||||
function checkAndUpdateRoute(env: RouterEnv): void {
|
||||
const currentRoute = env.router.routeName;
|
||||
checkRoute(env);
|
||||
if (env.router.routeName !== currentRoute) {
|
||||
env.qweb.forceUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
export function matchRoute(route: Route, path: string): RouteParams | false {
|
||||
if (route.path === "*") {
|
||||
return {};
|
||||
}
|
||||
const descrParts = route.path.split("/");
|
||||
const targetParts = path.split("/");
|
||||
const l = descrParts.length;
|
||||
if (l !== targetParts.length) {
|
||||
return false;
|
||||
}
|
||||
const result = {};
|
||||
for (let i = 0; i < l; i++) {
|
||||
const descr = descrParts[i];
|
||||
let target: string | number = targetParts[i];
|
||||
const match = descr.match(paramRegexp);
|
||||
if (match) {
|
||||
const [key, suffix] = match[1].split(".");
|
||||
if (suffix === "number") {
|
||||
target = parseInt(target, 10);
|
||||
}
|
||||
result[key] = target;
|
||||
} else if (descr !== target) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function destToURL(env: RouterEnv, dest: Destination): string {
|
||||
return dest.to || routeToURL(env.router.info.routes[dest.route!].path, dest.params!);
|
||||
}
|
||||
|
||||
export function routeToURL(path: string, params: RouteParams): string {
|
||||
const parts = path.split("/");
|
||||
const l = parts.length;
|
||||
for (let i = 0; i < l; i++) {
|
||||
const part = parts[i];
|
||||
const match = part.match(paramRegexp);
|
||||
if (match) {
|
||||
const key = match[1].split(".")[0];
|
||||
parts[i] = <string>params[key];
|
||||
}
|
||||
}
|
||||
return parts.join("/");
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`router directive t-routecomponent can render parameterized route 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
if (context['env'].router.routeName==='book') {
|
||||
//COMPONENT
|
||||
let def3;
|
||||
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||
let _2_index = c1.length;
|
||||
c1.push(null);
|
||||
let props4 = {title:context['env'].router.routeParams.title};
|
||||
if (w4 && w4.__owl__.renderPromise && !w4.__owl__.vnode) {
|
||||
if (utils.shallowEqual(props4, w4.__owl__.renderProps)) {
|
||||
def3 = w4.__owl__.renderPromise;
|
||||
} else {
|
||||
w4.destroy();
|
||||
w4 = false;
|
||||
}
|
||||
}
|
||||
if (!w4) {
|
||||
let componentKey4 = \`__component__book\`;
|
||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||
def3 = w4.__prepare();
|
||||
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||
} else {
|
||||
def3 = def3 || w4.__updateProps(props4, extra.forceUpdate, extra.patchQueue);
|
||||
def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;});
|
||||
}
|
||||
extra.promises.push(def3);
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`router directive t-routecomponent can render parameterized route with suffixes 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
if (context['env'].router.routeName==='book') {
|
||||
//COMPONENT
|
||||
let def3;
|
||||
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||
let _2_index = c1.length;
|
||||
c1.push(null);
|
||||
let props4 = {title:context['env'].router.routeParams.title,val:context['env'].router.routeParams.val};
|
||||
if (w4 && w4.__owl__.renderPromise && !w4.__owl__.vnode) {
|
||||
if (utils.shallowEqual(props4, w4.__owl__.renderProps)) {
|
||||
def3 = w4.__owl__.renderPromise;
|
||||
} else {
|
||||
w4.destroy();
|
||||
w4 = false;
|
||||
}
|
||||
}
|
||||
if (!w4) {
|
||||
let componentKey4 = \`__component__book\`;
|
||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||
def3 = w4.__prepare();
|
||||
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||
} else {
|
||||
def3 = def3 || w4.__updateProps(props4, extra.forceUpdate, extra.patchQueue);
|
||||
def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;});
|
||||
}
|
||||
extra.promises.push(def3);
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`router directive t-routecomponent can render simple cases 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
if (context['env'].router.routeName==='about') {
|
||||
//COMPONENT
|
||||
let def3;
|
||||
let w4 = 4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[4]] : false;
|
||||
let _2_index = c1.length;
|
||||
c1.push(null);
|
||||
let props4 = {};
|
||||
if (w4 && w4.__owl__.renderPromise && !w4.__owl__.vnode) {
|
||||
if (utils.shallowEqual(props4, w4.__owl__.renderProps)) {
|
||||
def3 = w4.__owl__.renderPromise;
|
||||
} else {
|
||||
w4.destroy();
|
||||
w4 = false;
|
||||
}
|
||||
}
|
||||
if (!w4) {
|
||||
let componentKey4 = \`__component__about\`;
|
||||
let W4 = context.components && context.components[componentKey4] || QWeb.components[componentKey4];
|
||||
if (!W4) {throw new Error('Cannot find the definition of component \\"' + componentKey4 + '\\"')}
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[4] = w4.__owl__.id;
|
||||
def3 = w4.__prepare();
|
||||
def3 = def3.then(vnode=>{let pvnode=h(vnode.sel, {key: 4, hook: {insert(vn) {let nvn=w4.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});c1[_2_index]=pvnode;w4.__owl__.pvnode = pvnode;});
|
||||
} else {
|
||||
def3 = def3 || w4.__updateProps(props4, extra.forceUpdate, extra.patchQueue);
|
||||
def3 = def3.then(()=>{if (w4.__owl__.isDestroyed) {return};let pvnode=w4.__owl__.pvnode;c1[_2_index]=pvnode;});
|
||||
}
|
||||
extra.promises.push(def3);
|
||||
}
|
||||
else if (context['env'].router.routeName==='users') {
|
||||
//COMPONENT
|
||||
let def6;
|
||||
let w7 = 7 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[7]] : false;
|
||||
let _5_index = c1.length;
|
||||
c1.push(null);
|
||||
let props7 = {};
|
||||
if (w7 && w7.__owl__.renderPromise && !w7.__owl__.vnode) {
|
||||
if (utils.shallowEqual(props7, w7.__owl__.renderProps)) {
|
||||
def6 = w7.__owl__.renderPromise;
|
||||
} else {
|
||||
w7.destroy();
|
||||
w7 = false;
|
||||
}
|
||||
}
|
||||
if (!w7) {
|
||||
let componentKey7 = \`__component__users\`;
|
||||
let W7 = context.components && context.components[componentKey7] || QWeb.components[componentKey7];
|
||||
if (!W7) {throw new Error('Cannot find the definition of component \\"' + componentKey7 + '\\"')}
|
||||
w7 = new W7(parent, props7);
|
||||
parent.__owl__.cmap[7] = w7.__owl__.id;
|
||||
def6 = w7.__prepare();
|
||||
def6 = def6.then(vnode=>{let pvnode=h(vnode.sel, {key: 7, hook: {insert(vn) {let nvn=w7.__mount(vnode, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}});c1[_5_index]=pvnode;w7.__owl__.pvnode = pvnode;});
|
||||
} else {
|
||||
def6 = def6 || w7.__updateProps(props7, extra.forceUpdate, extra.patchQueue);
|
||||
def6 = def6.then(()=>{if (w7.__owl__.isDestroyed) {return};let pvnode=w7.__owl__.pvnode;c1[_5_index]=pvnode;});
|
||||
}
|
||||
extra.promises.push(def6);
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
@@ -0,0 +1,108 @@
|
||||
import { Component } from "../../src/component/component";
|
||||
import { QWeb } from "../../src/qweb/index";
|
||||
import { activate, RouterEnv } from "../../src/router/plugin";
|
||||
import { makeTestEnv, makeTestFixture, nextTick } from "../helpers";
|
||||
|
||||
describe("router directive t-routecomponent", () => {
|
||||
let fixture: HTMLElement;
|
||||
let env: RouterEnv;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = makeTestFixture();
|
||||
env = <RouterEnv>makeTestEnv();
|
||||
delete QWeb.DIRECTIVE_NAMES.routecomponent;
|
||||
QWeb.DIRECTIVES = QWeb.DIRECTIVES.filter(d => d.name !== "routecomponent");
|
||||
for (let key in QWeb.components) {
|
||||
delete QWeb.components[key];
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.remove();
|
||||
});
|
||||
|
||||
test("can render simple cases", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="App">
|
||||
<t t-routecomponent="1" />
|
||||
</div>
|
||||
<span t-name="About">About</span>
|
||||
<span t-name="Users">Users</span>
|
||||
</templates>
|
||||
`);
|
||||
class About extends Component<any, any, any> {}
|
||||
class Users extends Component<any, any, any> {}
|
||||
class App extends Component<any, any, any> {
|
||||
components = { About, Users };
|
||||
}
|
||||
|
||||
const routes = [
|
||||
{ name: "about", path: "/about", component: About },
|
||||
{ name: "users", path: "/users", component: Users }
|
||||
];
|
||||
activate(env, routes, {mode: 'history'});
|
||||
const router = env.router;
|
||||
router.navigate({ route: "about" });
|
||||
const app = new App(env);
|
||||
await app.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>About</span></div>");
|
||||
|
||||
router.navigate({ route: "users" });
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>Users</span></div>");
|
||||
expect(env.qweb.templates.App.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("can render parameterized route", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="App">
|
||||
<t t-routecomponent="1" />
|
||||
</div>
|
||||
<span t-name="Book">Book <t t-esc="props.title"/></span>
|
||||
</templates>
|
||||
`);
|
||||
class Book extends Component<any, any, any> {}
|
||||
class App extends Component<any, any, any> {
|
||||
components = { Book };
|
||||
}
|
||||
|
||||
const routes = [{ name: "book", path: "/book/{{title}}", component: Book }];
|
||||
activate(env, routes, {mode: 'history'});
|
||||
const router = env.router;
|
||||
router.navigate({ route: "book", params: { title: "1984" } });
|
||||
const app = new App(env);
|
||||
await app.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>Book 1984</span></div>");
|
||||
expect(env.qweb.templates.App.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("can render parameterized route with suffixes", async () => {
|
||||
env.qweb.addTemplates(`
|
||||
<templates>
|
||||
<div t-name="App">
|
||||
<t t-routecomponent="1" />
|
||||
</div>
|
||||
<span t-name="Book">Book <t t-esc="props.title"/>|<t t-esc="incVal"/></span>
|
||||
</templates>
|
||||
`);
|
||||
class Book extends Component<any, any, any> {
|
||||
get incVal() {
|
||||
return this.props.val + 1;
|
||||
}
|
||||
}
|
||||
class App extends Component<any, any, any> {
|
||||
components = { Book };
|
||||
}
|
||||
|
||||
const routes = [{ name: "book", path: "/book/{{title}}/{{val.number}}", component: Book }];
|
||||
activate(env, routes, {mode: 'history'});
|
||||
const router = env.router;
|
||||
router.navigate({ route: "book", params: { title: "1984", val: "123" } });
|
||||
const app = new App(env);
|
||||
await app.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>Book 1984|124</span></div>");
|
||||
expect(env.qweb.templates.App.fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
import { matchRoute, routeToURL } from "../../src/router/plugin";
|
||||
|
||||
describe("routeToURL", () => {
|
||||
test("simple non parameterized path", () => {
|
||||
expect(routeToURL("/abc", {})).toBe("/abc");
|
||||
expect(routeToURL("/abc/def", {})).toBe("/abc/def");
|
||||
expect(routeToURL("/abc", {val: 12})).toBe("/abc");
|
||||
});
|
||||
|
||||
test("simple parameterized path", () => {
|
||||
expect(routeToURL("/abc/{{def}}", {def: 34})).toBe("/abc/34");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("match routes", () => {
|
||||
test("properly match simple routes", () => {
|
||||
// simple route
|
||||
expect(matchRoute({ path: "/home", name: "someroute" }, "/home")).toEqual({});
|
||||
|
||||
// no match
|
||||
expect(matchRoute({ path: "/home", name: "someroute" }, "/otherpath")).toEqual(false);
|
||||
|
||||
// fallback route
|
||||
expect(matchRoute({ path: "*", name: "someroute" }, "somepath")).toEqual({});
|
||||
});
|
||||
|
||||
test("match some parameterized routes", () => {
|
||||
expect(matchRoute({ path: "/invoices/{{id}}", name: "someroute" }, "/invoices/3")).toEqual({
|
||||
id: "3"
|
||||
});
|
||||
});
|
||||
|
||||
test("can convert to number if needed", () => {
|
||||
expect(matchRoute({ path: "/invoices/{{id.number}}", name: "someroute" }, "/invoices/3")).toEqual({
|
||||
id: 3
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user