From 8915a0a41e8406df1efbbfa0bc1e5dd9664a2628 Mon Sep 17 00:00:00 2001 From: Willy Brauner Date: Thu, 9 Nov 2023 15:04:13 +0100 Subject: [PATCH] Big change on createUrl -> internalize logic --- src/core/core.ts | 272 ++++++------ src/tests/core.addLangToUrl.test.ts | 13 + .../core.applyMiddlewaresToRoutes.test.ts | 17 + src/tests/core.createUrl.test.ts | 104 +++++ src/tests/core.getLangPath.test.ts | 15 + src/tests/core.getPathByRouteName.test.ts | 16 + src/tests/core.getRouteFromUrl.test.ts | 101 +++++ .../core.getStaticPropsFromRoute.test.ts | 22 + src/tests/core.getSubRouterBase.test.ts | 37 ++ src/tests/core.getSubRouterRoutes.test.ts | 16 + src/tests/core.patchMissingRootRoute.test.ts | 22 + src/tests/core.test.ts | 388 ------------------ 12 files changed, 514 insertions(+), 509 deletions(-) create mode 100644 src/tests/core.addLangToUrl.test.ts create mode 100644 src/tests/core.applyMiddlewaresToRoutes.test.ts create mode 100644 src/tests/core.createUrl.test.ts create mode 100644 src/tests/core.getLangPath.test.ts create mode 100644 src/tests/core.getPathByRouteName.test.ts create mode 100644 src/tests/core.getRouteFromUrl.test.ts create mode 100644 src/tests/core.getStaticPropsFromRoute.test.ts create mode 100644 src/tests/core.getSubRouterBase.test.ts create mode 100644 src/tests/core.getSubRouterRoutes.test.ts create mode 100644 src/tests/core.patchMissingRootRoute.test.ts delete mode 100644 src/tests/core.test.ts diff --git a/src/core/core.ts b/src/core/core.ts index 49267b2..f8a8a7a 100644 --- a/src/core/core.ts +++ b/src/core/core.ts @@ -4,6 +4,7 @@ import { compile, match } from "path-to-regexp" import { TRoute } from "../components/Router" import LangService from "./LangService" import { joinPaths, removeLastCharFromString } from "./helpers" +import { al, ar } from "vitest/dist/reporters-5f784f42" const componentName: string = "core" const log = debug(`router:${componentName}`) @@ -24,13 +25,13 @@ export type TOpenRouteParams = { * createUrl URL for setLocation * (Get URL to push in history) * - * @param args can be string or TOpenRouteParams object + * @param openRouteParams can be string or TOpenRouteParams object * @param base * @param allRoutes * @param langService */ export function createUrl( - args: string | TOpenRouteParams, + openRouteParams: string | TOpenRouteParams, base: string = Routers.base, allRoutes: TRoute[] = Routers.routes, langService = Routers.langService, @@ -39,8 +40,8 @@ export function createUrl( let urlToPush: string // STRING param - if (typeof args === "string") { - urlToPush = args as string + if (typeof openRouteParams === "string") { + urlToPush = openRouteParams as string if (!!langService) { urlToPush = addLangToUrl(urlToPush) } @@ -49,33 +50,62 @@ export function createUrl( } // OBJECT param, add lang to params if no exist - else if (typeof args === "object" && args?.name) { - if (langService && !args.params?.lang) { - args.params = { - ...args.params, + else if (typeof openRouteParams === "object" && openRouteParams?.name) { + if (langService && !openRouteParams.params?.lang) { + openRouteParams.params = { + ...openRouteParams.params, lang: langService.currentLang.key, } } // add params to URL if exist let queryParams = "" - if (args?.queryParams) { + if (openRouteParams?.queryParams) { queryParams = "?" - queryParams += Object.keys(args.queryParams) - .map((key) => `${key}=${args?.queryParams[key]}`) + queryParams += Object.keys(openRouteParams.queryParams) + .map((key) => `${key}=${openRouteParams?.queryParams[key]}`) .join("&") } // add hash to URL if exist let hash = "" - if (args?.hash) { - hash = "#" + args.hash + if (openRouteParams?.hash) { + hash = "#" + openRouteParams.hash } - return getUrlByRouteName(allRoutes, args, base) + queryParams + hash + function getUrlByRouteName( + allRoutes: TRoute[], + args: TOpenRouteParams, + base = Routers.base || "/", + langService = Routers.langService, + ): string { + const next = (routes, args, curBase): string => { + for (let route of routes) { + const lang = args.params?.lang || langService?.currentLang.key + const langPath = route._langPath?.[lang] + const routePath = route.path + // console.log("---------",{ lang, langPath, routePath }) + if (route?.name === args.name || route.component?.displayName === args.name) { + // prettier-ignore + return (curBase + compile(langPath || routePath)(args.params)).replace(/(\/)+/g, "/") + } else if (route.children?.length > 0) { + const match = next( + route.children, + args, + curBase + compile(langPath || routePath)(args.params), + ) + if (match) return match + } + } + } + return next(allRoutes, args, base) + } - // in other case return. + const url = getUrlByRouteName(allRoutes, openRouteParams, base) + if (url) return url + queryParams + hash + + // in other case return } else { - console.warn("createUrl param isn't valid. to use createUrl return.", args) + console.warn("createUrl param isn't valid. to use createUrl return.", openRouteParams) return } } @@ -488,111 +518,111 @@ export function compileUrl(path: string, params?: TParams): string { return compile(path)(params) } -/** - * Get full path by path - * if path "/foo" is a children of path "/bar", his full url is "/bar/foo" - * With the second URL part "/foo", this function will returns "/bar/foo" - * @returns string - */ -export function getFullPathByPath( - routes: TRoute[], - path: string | { [x: string]: string }, - routeName: string, - lang: string = Routers.langService?.currentLang.key || undefined, - basePath: string = null, -): string { - let localPath: string[] = [basePath] - - for (let route of routes) { - const langPath = route._langPath?.[lang] - const routePath = route.path as string - - const pathMatch = - (langPath === path || routePath === path) && route.name === routeName - - // if path match on first level, keep path in local array and return it, stop here. - if (pathMatch) { - localPath.push(langPath || routePath) - return joinPaths(localPath) - } - - // if not matching but as children, return it - else if (route?.children?.length > 0) { - // no match, recall recursively on children - const matchChildrenPath = getFullPathByPath( - route.children, - path, - routeName, - lang, - joinPaths(localPath), - ) - // return recursive Fn only if match, else continue to next iteration - if (matchChildrenPath) { - // keep path in local array - localPath.push(langPath || routePath) - // Return the function after localPath push - return getFullPathByPath( - route.children, - path, - routeName, - lang, - joinPaths(localPath), - ) - } - } - } -} - -/** - * Get "full" URL by route name and params - * @returns string - */ -export function getUrlByRouteName( - pRoutes: TRoute[], - pParams: TOpenRouteParams, - base?: string, -): string { - // need to wrap the function to be able to access the preserved "pRoutes" param - // in local scope after recursion - const next = (routes: TRoute[], params: TOpenRouteParams): string => { - for (let route of routes) { - const match = - route?.name === params.name || route.component?.displayName === params.name - if (match) { - if (!route?.path) { - log("getUrlByRouteName > There is no route with this name, exit", params.name) - return - } - - let path = - typeof route.path === "object" - ? route.path[Object.keys(route.path)[0]] - : route.path - - // get full path - const _fullPath = getFullPathByPath( - pRoutes, - path, - route.name, - pParams?.params?.lang, - base, - ) - // build URL - // console.log("_fullPath", _fullPath, params); - return compileUrl(_fullPath, params.params) - } - - // if route has children - else if (route.children?.length > 0) { - // getUrlByRouteName > no match, recall recursively on children - const match = next(route.children, params) - // return recursive Fn only if match, else, continue to next iteration - if (match) return match - } - } - } - return next(pRoutes, pParams) -} +// /** +// * Get full path by path +// * if path "/foo" is a children of path "/bar", his full url is "/bar/foo" +// * With the second URL part "/foo", this function will returns "/bar/foo" +// * @returns string +// */ +// export function getFullPathByPath( +// routes: TRoute[], +// path: string | { [x: string]: string }, +// routeName: string, +// lang: string = Routers.langService?.currentLang.key || undefined, +// basePath: string = null, +// ): string { +// let localPath: string[] = [basePath] +// +// for (let route of routes) { +// const langPath = route._langPath?.[lang] +// const routePath = route.path as string +// +// const pathMatch = +// (langPath === path || routePath === path) && route.name === routeName +// +// // if path match on first level, keep path in local array and return it, stop here. +// if (pathMatch) { +// localPath.push(langPath || routePath) +// return joinPaths(localPath) +// } +// +// // if not matching but as children, return it +// else if (route?.children?.length > 0) { +// // no match, recall recursively on children +// const matchChildrenPath = getFullPathByPath( +// route.children, +// path, +// routeName, +// lang, +// joinPaths(localPath), +// ) +// // return recursive Fn only if match, else continue to next iteration +// if (matchChildrenPath) { +// // keep path in local array +// localPath.push(langPath || routePath) +// // Return the function after localPath push +// return getFullPathByPath( +// route.children, +// path, +// routeName, +// lang, +// joinPaths(localPath), +// ) +// } +// } +// } +// } +// +// /** +// * Get "full" URL by route name and params +// * @returns string +// */ +// export function getUrlByRouteNameOld( +// pRoutes: TRoute[], +// pParams: TOpenRouteParams, +// base?: string, +// ): string { +// // need to wrap the function to be able to access the preserved "pRoutes" param +// // in local scope after recursion +// const next = (routes: TRoute[], params: TOpenRouteParams): string => { +// for (let route of routes) { +// const match = +// route?.name === params.name || route.component?.displayName === params.name +// if (match) { +// if (!route?.path) { +// log("getUrlByRouteName > There is no route with this name, exit", params.name) +// return +// } +// +// let path = +// typeof route.path === "object" +// ? route.path[Object.keys(route.path)[0]] +// : route.path +// +// // get full path +// const _fullPath = getFullPathByPath( +// pRoutes, +// path, +// route.name, +// pParams?.params?.lang, +// base, +// ) +// // build URL +// // console.log("_fullPath", _fullPath, params); +// return compileUrl(_fullPath, params.params) +// } +// +// // if route has children +// else if (route.children?.length > 0) { +// // getUrlByRouteName > no match, recall recursively on children +// const match = next(route.children, params) +// // return recursive Fn only if match, else, continue to next iteration +// if (match) return match +// } +// } +// } +// return next(pRoutes, pParams) +// } /** * returns lang path diff --git a/src/tests/core.addLangToUrl.test.ts b/src/tests/core.addLangToUrl.test.ts new file mode 100644 index 0000000..b309050 --- /dev/null +++ b/src/tests/core.addLangToUrl.test.ts @@ -0,0 +1,13 @@ +/** + * @vitest-environment jsdom + */ +import { describe, expect, it } from "vitest" +import { addLangToUrl } from "../core/core" + +describe("addLangToUrl", () => { + it("should add lang to Url", () => { + const url = "/foo/en/bar" + expect(addLangToUrl(url, "en", true)).toBe(`/en${url}`) + expect(addLangToUrl(url, "en", false)).toBe(`${url}`) + }) +}) diff --git a/src/tests/core.applyMiddlewaresToRoutes.test.ts b/src/tests/core.applyMiddlewaresToRoutes.test.ts new file mode 100644 index 0000000..2289129 --- /dev/null +++ b/src/tests/core.applyMiddlewaresToRoutes.test.ts @@ -0,0 +1,17 @@ +/** + * @vitest-environment jsdom + */ + +import { describe, expect, it } from "vitest" +import { applyMiddlewaresToRoutes } from "../core/core" + +describe("applyMiddlewaresToRoutes", () => { + it("should apply middleware to routes", () => { + // TODO + const transformFn = (r) => r.forEach((e) => (e.path = `-${e.path}`)) + const routes = [{ path: "/" }, { path: "/foo" }] + const afterMiddlewareRoutes = [{ path: "-/" }, { path: "-/foo" }] + const transformRoutes = applyMiddlewaresToRoutes(routes, [transformFn]) + expect(transformRoutes).toEqual(afterMiddlewareRoutes) + }) +}) diff --git a/src/tests/core.createUrl.test.ts b/src/tests/core.createUrl.test.ts new file mode 100644 index 0000000..dc6ecd4 --- /dev/null +++ b/src/tests/core.createUrl.test.ts @@ -0,0 +1,104 @@ +/** + * @vitest-environment jsdom + */ + +import { it, expect, describe } from "vitest" +import { createUrl } from "../core/core" +import { routeList } from "./_fixtures/routeList" + +// prettier-ignore +describe("createUrl", () => { + it("should create URL properly", () => { + const base = "/" + expect(createUrl("/", base, routeList)).toBe("/") + expect(createUrl("/foo", base, routeList)).toBe("/foo") + expect(createUrl({name: "ZooPage"}, base, routeList)).toBe("/hello/foo/zoo") + }) + + it("should create URL properly if is base URL", () => { + const routes = [ + {path: "/a", name: "a-page"}, + { + path: "/b", + name: "b-page", + children: [ + {path: "/c", name: "c-page"}, + {path: "/d", name: "d-page"}, + ], + }, + ] + expect(createUrl("/a", "/foo/", routes)).toBe("/foo/a") + expect(createUrl("/d", "/foo/", routes)).toBe("/foo/d") + }) + + it.only("should create URL with _langPath ", () => { + const routes = [ + { + // path is set automatically by selected langPath + path: "", + // _langPath is auto-generated on formatRoute step + // values used instead of path when we create URL + _langPath: {en: "/a-en", fr: "/a-fr"}, + name: "a-page", + }, + { + path: "/b", + name: "b-page", + children: [ + { + path: "", + _langPath: {en: "/c-en", fr: "/c-fr"}, + name: "c-page", + }, + {path: "/d", name: "d-page"}, + ], + }, + ] + expect(createUrl({name: "a-page", params: {lang: "fr"}}, "/", routes)) + .toBe("/a-fr") + expect(createUrl({name: "d-page", params: {lang: "fr"}}, "/foo/", routes)) + .toBe("/foo/b/d") + expect(createUrl({name: "c-page", params: {lang: "en"}}, "/foo/", routes)) + .toBe("/foo/b/c-en") + }) + + it("should create URL with params and hash", () => { + const base = "/custom-base/" + const routes = [ + {path: "/a"}, + { + path: "/b", + name: "b-page", + children: [{path: "/c", name: "c-page"}, {path: "/d"}], + }, + ] + // test single param + expect(createUrl({ + name: "b-page", + queryParams: {foo: "bar"} + }, base, routes)) + .toBe(`${base}b?foo=bar`) + + // test multiple params + expect(createUrl({ + name: "b-page", + queryParams: {foo: "bar", zoo: "a,b"} + }, base, routes)) + .toBe(`${base}b?foo=bar&zoo=a,b`) + + // test hash + expect(createUrl({name: "b-page", hash: "hello"}, base, routes)) + .toBe(`${base}b#hello`) + expect(createUrl({name: "c-page", hash: "hello"}, base, routes)) + .toBe(`${base}b/c#hello`) + + // test both + expect(createUrl({ + name: "c-page", + hash: "hello", + queryParams: {foo: "bar"} + }, base, routes)) + .toBe(`${base}b/c?foo=bar#hello`) + }) + + }) diff --git a/src/tests/core.getLangPath.test.ts b/src/tests/core.getLangPath.test.ts new file mode 100644 index 0000000..d7b816c --- /dev/null +++ b/src/tests/core.getLangPath.test.ts @@ -0,0 +1,15 @@ +/** + * @vitest-environment jsdom + */ +import { describe, expect, it } from "vitest" +import { getLangPath } from "../core/core" + +describe("getLangPath", () => { + it("should format routes properly", () => { + const path = "/:lang/foo" + const pathObj = { fr: "/:lang/foo-fr", en: "/:lang/foo-en" } + expect(getLangPath(path, "fr")).toEqual("/foo") + expect(getLangPath(pathObj, "en")).toEqual("/foo-en") + expect(getLangPath(pathObj, "de")).toBeUndefined() + }) +}) diff --git a/src/tests/core.getPathByRouteName.test.ts b/src/tests/core.getPathByRouteName.test.ts new file mode 100644 index 0000000..903663d --- /dev/null +++ b/src/tests/core.getPathByRouteName.test.ts @@ -0,0 +1,16 @@ +/** + * @vitest-environment jsdom + */ + +import { describe, expect, it } from "vitest" +import { getPathByRouteName } from "../core/core" +import { routeList } from "./_fixtures/routeList" + +describe("getPathByRouteName", () => { + it("should return the right path with name", () => { + expect(getPathByRouteName(routeList, "HelloPage")).toEqual("/hello") + expect(getPathByRouteName(routeList, "EndPage")).toEqual("/end") + expect(getPathByRouteName(routeList, "FooPage")).toEqual("/foo") + expect(getPathByRouteName(routeList, "ZooPage")).toEqual("/zoo/:id?") + }) +}) diff --git a/src/tests/core.getRouteFromUrl.test.ts b/src/tests/core.getRouteFromUrl.test.ts new file mode 100644 index 0000000..9a7ff1d --- /dev/null +++ b/src/tests/core.getRouteFromUrl.test.ts @@ -0,0 +1,101 @@ +/** + * @vitest-environment jsdom + */ + +import { describe, expect, it } from "vitest" +import { getRouteFromUrl } from "../core/core" +import { preventSlashes } from "../core/helpers" +import { routeList } from "./_fixtures/routeList" + +describe("getRouteFromUrl", () => { + const base = "/custom/base" + + it("should get right route from URL", () => { + let getRoute = getRouteFromUrl({ + pUrl: preventSlashes(`${base}/bar/my-id`), + pRoutes: routeList, + pBase: base, + }) + expect(getRoute._fullUrl).toBe(`${base}/bar/my-id`) + expect(getRoute._fullPath).toBe(`${base}/bar/:id`) + expect(getRoute.path).toBe("/bar/:id") + expect(getRoute.url).toBe("/bar/my-id") + expect(getRoute.name).toBe(`BarPage`) + const routeProps = { params: { id: "my-id" }, color: "blue" } + expect(getRoute.props).toEqual(routeProps) + // no parent route, so context object need to return same route information + expect(getRoute._context.props).toEqual(routeProps) + + getRoute = getRouteFromUrl({ + pUrl: "/hello-2", + pRoutes: routeList, + pBase: "/", + }) + expect(getRoute._fullPath).toBe(`/hello-2`) + + getRoute = getRouteFromUrl({ + pUrl: "/end", + pRoutes: routeList, + pBase: "/", + }) + expect(getRoute.name).toBe(`EndPage`) + }) + + it("should get right route from URL with subRoute", () => { + const getRoute = getRouteFromUrl({ + pUrl: "/about/route2/super-param/foo4", + pRoutes: routeList, + pBase: "/", + }) + + expect(getRoute._fullPath).toBe(`/about/route2/:testParam?/foo4`) + expect(getRoute.path).toBe("/foo4") + expect(getRoute._fullUrl).toBe(`/about/route2/super-param/foo4`) + expect(getRoute.url).toBe("/foo4") + expect(getRoute.base).toBe("/about/route2/:testParam?") + expect(getRoute.name).toBe("Foo4Page") + expect(getRoute.props).toEqual({ + color: "red", + params: { testParam: "super-param" }, + }) + }) + + it("should not get route from bad URL and return undefined", () => { + const getRoute = getRouteFromUrl({ + pUrl: preventSlashes(`${base}/bar/foo/bar/`), + pRoutes: routeList, + pBase: base, + }) + expect(getRoute).toBeUndefined() + }) + + it("should get route from URL with params and hash", () => { + const pRoutes = [ + { path: "/a" }, + { + path: "/b", + children: [{ path: "/c" }, { path: "/d" }], + }, + ] + // only params + let getRoute = getRouteFromUrl({ pRoutes, pUrl: "/b?foo=bar&lang=en" }) + expect(getRoute.queryParams).toEqual({ foo: "bar", lang: "en" }) + expect(getRoute._fullPath).toEqual("/b") + + // only hash + getRoute = getRouteFromUrl({ pRoutes, pUrl: "/b/c#hash" }) + expect(getRoute._fullPath).toEqual("/b/c") + expect(getRoute.queryParams).toEqual({}) + expect(getRoute.hash).toEqual("hash") + + // params and hash + getRoute = getRouteFromUrl({ pRoutes, pUrl: "/b/c?foo=bar#hash" }) + expect(getRoute.queryParams).toEqual({ foo: "bar" }) + expect(getRoute.hash).toEqual("hash") + + // not hash and params + getRoute = getRouteFromUrl({ pRoutes, pUrl: "/a" }) + expect(getRoute.queryParams).toEqual({}) + expect(getRoute.hash).toEqual(null) + }) +}) diff --git a/src/tests/core.getStaticPropsFromRoute.test.ts b/src/tests/core.getStaticPropsFromRoute.test.ts new file mode 100644 index 0000000..6bfc186 --- /dev/null +++ b/src/tests/core.getStaticPropsFromRoute.test.ts @@ -0,0 +1,22 @@ +/** + * @vitest-environment jsdom + */ + +import { describe, expect, it } from "vitest" +import { requestStaticPropsFromRoute } from "../core/core" +import { routeList } from "./_fixtures/routeList" + +describe("getStaticPropsFromRoute", () => { + it("should return promise result of staticProps request", async () => { + const ssrStaticProps = await requestStaticPropsFromRoute({ + url: "/hello", + base: "/", + routes: routeList, + }) + expect(ssrStaticProps).toEqual({ + props: { data: {} }, + name: "HelloPage", + url: "/hello", + }) + }) +}) diff --git a/src/tests/core.getSubRouterBase.test.ts b/src/tests/core.getSubRouterBase.test.ts new file mode 100644 index 0000000..8b4767e --- /dev/null +++ b/src/tests/core.getSubRouterBase.test.ts @@ -0,0 +1,37 @@ +/** + * @vitest-environment jsdom + */ + +import { it, expect, describe } from "vitest" +import { getSubRouterBase } from "../core/core" +import { Routers, LangService } from ".." + +describe("getSubRouterBase", () => { + it("should return subRouter base URL", () => { + expect(getSubRouterBase("/foo", "")).toBe("/foo") + expect(getSubRouterBase("/foo", "/")).toBe("/foo") + expect(getSubRouterBase("/foo", "/hello/")).toBe("/hello/foo") + expect(getSubRouterBase("/foo", "/hello")).toBe("/hello/foo") + expect(getSubRouterBase("/foo", "/custom/base/hello/")).toBe("/custom/base/hello/foo") + + Routers.langService = new LangService({ + languages: [{ key: "en" }, { key: "fr" }], + }) + const langPathTest = { en: "/foo-en", fr: "/foo-fr" } + expect(getSubRouterBase(langPathTest, "/base/", true)).toBe("/base/:lang/foo-en") + expect(getSubRouterBase(langPathTest, "/base/", false)).toBe("/base/foo-en") + Routers.langService = undefined + }) + + it("should return subRouter base URL with 'showDefaultLangInUrl: false' option", () => { + Routers.langService = new LangService({ + languages: [{ key: "en" }, { key: "fr" }], + showDefaultLangInUrl: false, + }) + ;["/", "/foo", "/foo/bar/biz"].forEach((e) => { + expect(getSubRouterBase(e, "/base/", true)).toBe(`/base${e}`) + expect(getSubRouterBase(e, "/base/", false)).toBe(`/base${e}`) + }) + Routers.langService = undefined + }) +}) diff --git a/src/tests/core.getSubRouterRoutes.test.ts b/src/tests/core.getSubRouterRoutes.test.ts new file mode 100644 index 0000000..4793ba0 --- /dev/null +++ b/src/tests/core.getSubRouterRoutes.test.ts @@ -0,0 +1,16 @@ +/** + * @vitest-environment jsdom + */ + +import { describe, expect, it } from "vitest" +import { getSubRouterRoutes } from "../core/core" +import { routeList } from "./_fixtures/routeList" + +describe("getSubRouterRoutes", () => { + it("should return subRouter route list", () => { + const homeChildren = getSubRouterRoutes("/", routeList) + expect(homeChildren).toEqual(routeList.find((e) => e.name === "HomePage").children) + const aboutChildren = getSubRouterRoutes("/about", routeList) + expect(aboutChildren).toEqual(routeList.find((e) => e.name === "AboutPage").children) + }) +}) diff --git a/src/tests/core.patchMissingRootRoute.test.ts b/src/tests/core.patchMissingRootRoute.test.ts new file mode 100644 index 0000000..6cb3239 --- /dev/null +++ b/src/tests/core.patchMissingRootRoute.test.ts @@ -0,0 +1,22 @@ +/** + * @vitest-environment jsdom + */ +import { describe, expect, it } from "vitest" +import { patchMissingRootRoute } from "../core/core" +import { routeList } from "./_fixtures/routeList" + +describe("patchMissingRootRoute", () => { + it("should patch missing route", () => { + const patchedRoutes = patchMissingRootRoute(routeList[0].children) + const firstRouteAdded = patchedRoutes[0] + expect(firstRouteAdded.path).toBe("/") + expect(firstRouteAdded.name).toContain("auto-generate-slash-route") + }) + + it("should not patch missing route if '/' route already exist", () => { + const patchedRoutes = patchMissingRootRoute(routeList) + const firstRouteAdded = patchedRoutes[0] + expect(firstRouteAdded.path).toBe("/") + expect(firstRouteAdded.name).toBe("HomePage") + }) +}) diff --git a/src/tests/core.test.ts b/src/tests/core.test.ts deleted file mode 100644 index b6f5fe5..0000000 --- a/src/tests/core.test.ts +++ /dev/null @@ -1,388 +0,0 @@ -/** - * @vitest-environment jsdom - */ - -import { it, expect, describe } from "vitest" -import { - compileUrl, - getPathByRouteName, - getFullPathByPath, - getUrlByRouteName, - requestStaticPropsFromRoute, - getRouteFromUrl, - createUrl, - getNotFoundRoute, - getLangPath, - addLangToUrl, - getSubRouterBase, - getSubRouterRoutes, - patchMissingRootRoute, - applyMiddlewaresToRoutes, -} from "../core/core" -import { preventSlashes } from "../core/helpers" -import { routeList } from "./_fixtures/routeList" -import { Routers, LangService } from ".." - -/** - * Public - * - * - * - */ - -describe("public", () => { - describe("createUrl", () => { - it("should create URL properly", () => { - const base = "/" - expect(createUrl("/", base, routeList)).toBe("/") - expect(createUrl("/foo", base, routeList)).toBe("/foo") - expect(createUrl({ name: "ZooPage" }, base, routeList)).toBe("/hello/foo/zoo") - }) - - it("should create URL properly if is base URL", () => { - const routes = [ - { path: "/a", name: "a-page" }, - { - path: "/b", - name: "b-page", - children: [ - { path: "/c", name: "c-page" }, - { path: "/d", name: "d-page" }, - ], - }, - ] - expect(createUrl("/a", "/foo/", routes)).toBe("/foo/a") - // FIXME never parse routes list, it just add base URL if is a string - expect(createUrl("/d", "/foo/", routes)).toBe("/foo/d") - }) - - it("should create URL with params and hash", () => { - const base = "/custom-base/" - const routes = [ - { path: "/a" }, - { - path: "/b", - name: "b-page", - children: [{ path: "/c", name: "c-page" }, { path: "/d" }], - }, - ] - // test single param - expect( - createUrl({ name: "b-page", queryParams: { foo: "bar" } }, base, routes), - ).toBe(`${base}b?foo=bar`) - - // test multiple params - expect( - createUrl( - { name: "b-page", queryParams: { foo: "bar", zoo: "a,b" } }, - base, - routes, - ), - ).toBe(`${base}b?foo=bar&zoo=a,b`) - - // test hash - expect(createUrl({ name: "b-page", hash: "hello" }, base, routes)).toBe( - `${base}b#hello`, - ) - expect(createUrl({ name: "c-page", hash: "hello" }, base, routes)).toBe( - `${base}b/c#hello`, - ) - - // test both - expect( - createUrl( - { name: "c-page", hash: "hello", queryParams: { foo: "bar" } }, - base, - routes, - ), - ).toBe(`${base}b/c?foo=bar#hello`) - }) - }) - - describe("getSubRouterBase", () => { - it("should return subRouter base URL", () => { - expect(getSubRouterBase("/foo", "")).toBe("/foo") - expect(getSubRouterBase("/foo", "/")).toBe("/foo") - expect(getSubRouterBase("/foo", "/hello/")).toBe("/hello/foo") - expect(getSubRouterBase("/foo", "/hello")).toBe("/hello/foo") - expect(getSubRouterBase("/foo", "/custom/base/hello/")).toBe( - "/custom/base/hello/foo", - ) - - Routers.langService = new LangService({ - languages: [{ key: "en" }, { key: "fr" }], - }) - const langPathTest = { en: "/foo-en", fr: "/foo-fr" } - expect(getSubRouterBase(langPathTest, "/base/", true)).toBe("/base/:lang/foo-en") - expect(getSubRouterBase(langPathTest, "/base/", false)).toBe("/base/foo-en") - Routers.langService = undefined - }) - - it("should return subRouter base URL with 'showDefaultLangInUrl: false' option", () => { - Routers.langService = new LangService({ - languages: [{ key: "en" }, { key: "fr" }], - showDefaultLangInUrl: false, - }) - ;["/", "/foo", "/foo/bar/biz"].forEach((e) => { - expect(getSubRouterBase(e, "/base/", true)).toBe(`/base${e}`) - expect(getSubRouterBase(e, "/base/", false)).toBe(`/base${e}`) - }) - Routers.langService = undefined - }) - }) - - describe("getSubRouterRoutes", () => { - it("should return subRouter route list", () => { - const homeChildren = getSubRouterRoutes("/", routeList) - expect(homeChildren).toEqual(routeList.find((e) => e.name === "HomePage").children) - const aboutChildren = getSubRouterRoutes("/about", routeList) - expect(aboutChildren).toEqual( - routeList.find((e) => e.name === "AboutPage").children, - ) - }) - }) - - describe("getPathByRouteName", () => { - it("should return the right path with name", () => { - expect(getPathByRouteName(routeList, "HelloPage")).toEqual("/hello") - expect(getPathByRouteName(routeList, "EndPage")).toEqual("/end") - expect(getPathByRouteName(routeList, "FooPage")).toEqual("/foo") - expect(getPathByRouteName(routeList, "ZooPage")).toEqual("/zoo/:id?") - }) - }) - - describe("getStaticPropsFromRoute", () => { - it("should return promise result of staticProps request", async () => { - const ssrStaticProps = await requestStaticPropsFromRoute({ - url: "/hello", - base: "/", - routes: routeList, - }) - expect(ssrStaticProps).toEqual({ - props: { data: {} }, - name: "HelloPage", - url: "/hello", - }) - }) - }) -}) - -/** - * Matcher - * - * - * - */ -describe("matcher", () => { - const base = "/custom/base" - - describe("getRouteFromUrl", () => { - it("should get right route from URL", () => { - let getRoute = getRouteFromUrl({ - pUrl: preventSlashes(`${base}/bar/my-id`), - pRoutes: routeList, - pBase: base, - }) - expect(getRoute._fullUrl).toBe(`${base}/bar/my-id`) - expect(getRoute._fullPath).toBe(`${base}/bar/:id`) - expect(getRoute.path).toBe("/bar/:id") - expect(getRoute.url).toBe("/bar/my-id") - expect(getRoute.name).toBe(`BarPage`) - const routeProps = { params: { id: "my-id" }, color: "blue" } - expect(getRoute.props).toEqual(routeProps) - // no parent route, so context object need to return same route information - expect(getRoute._context.props).toEqual(routeProps) - - getRoute = getRouteFromUrl({ - pUrl: "/hello-2", - pRoutes: routeList, - pBase: "/", - }) - expect(getRoute._fullPath).toBe(`/hello-2`) - - getRoute = getRouteFromUrl({ - pUrl: "/end", - pRoutes: routeList, - pBase: "/", - }) - expect(getRoute.name).toBe(`EndPage`) - }) - - it("should get right route from URL with subRoute", () => { - const getRoute = getRouteFromUrl({ - pUrl: "/about/route2/super-param/foo4", - pRoutes: routeList, - pBase: "/", - }) - - expect(getRoute._fullPath).toBe(`/about/route2/:testParam?/foo4`) - expect(getRoute.path).toBe("/foo4") - expect(getRoute._fullUrl).toBe(`/about/route2/super-param/foo4`) - expect(getRoute.url).toBe("/foo4") - expect(getRoute.base).toBe("/about/route2/:testParam?") - expect(getRoute.name).toBe("Foo4Page") - expect(getRoute.props).toEqual({ - color: "red", - params: { testParam: "super-param" }, - }) - }) - - it("should not get route from bad URL and return undefined", () => { - const getRoute = getRouteFromUrl({ - pUrl: preventSlashes(`${base}/bar/foo/bar/`), - pRoutes: routeList, - pBase: base, - }) - expect(getRoute).toBeUndefined() - }) - - it("should get route from URL with params and hash", () => { - const pRoutes = [ - { path: "/a" }, - { - path: "/b", - children: [{ path: "/c" }, { path: "/d" }], - }, - ] - // only params - let getRoute = getRouteFromUrl({ pRoutes, pUrl: "/b?foo=bar&lang=en" }) - expect(getRoute.queryParams).toEqual({ foo: "bar", lang: "en" }) - expect(getRoute._fullPath).toEqual("/b") - - // only hash - getRoute = getRouteFromUrl({ pRoutes, pUrl: "/b/c#hash" }) - expect(getRoute._fullPath).toEqual("/b/c") - expect(getRoute.queryParams).toEqual({}) - expect(getRoute.hash).toEqual("hash") - - // params and hash - getRoute = getRouteFromUrl({ pRoutes, pUrl: "/b/c?foo=bar#hash" }) - expect(getRoute.queryParams).toEqual({ foo: "bar" }) - expect(getRoute.hash).toEqual("hash") - - // not hash and params - getRoute = getRouteFromUrl({ pRoutes, pUrl: "/a" }) - expect(getRoute.queryParams).toEqual({}) - expect(getRoute.hash).toEqual(null) - }) - }) - - describe("getNotFoundRoute", () => { - it("should return not found route", () => { - expect(getNotFoundRoute(routeList)).toEqual({ - path: "/:rest", - name: "NotFoundPage", - }) - }) - }) -}) - -/*** - * Routes - * - * - * - */ -describe("routes", () => { - describe("patchMissingRootRoute", () => { - it("should patch missing route", () => { - const pathchedRoutes = patchMissingRootRoute(routeList[0].children) - const firstRouteAdded = pathchedRoutes[0] - expect(firstRouteAdded.path).toBe("/") - expect(firstRouteAdded.name).toContain("auto-generate-slash-route") - }) - - it("should not patch missing route if '/' route already exist", () => { - const pathchedRoutes = patchMissingRootRoute(routeList) - const firstRouteAdded = pathchedRoutes[0] - expect(firstRouteAdded.path).toBe("/") - expect(firstRouteAdded.name).toBe("HomePage") - }) - }) - - describe("applyMiddlewaresToRoutes", () => { - it("should apply middleware to routes", () => { - // TODO - const transformFn = (r) => r.forEach((e) => (e.path = `-${e.path}`)) - const routes = [{ path: "/" }, { path: "/foo" }] - const afterMiddlewareRoutes = [{ path: "-/" }, { path: "-/foo" }] - const transformRoutes = applyMiddlewaresToRoutes(routes, [transformFn]) - expect(transformRoutes).toEqual(afterMiddlewareRoutes) - }) - }) -}) - -/*** - * Urls / paths - * - * - * - */ -describe("URLs and paths", () => { - describe("compileUrl", () => { - it("should build url", () => { - const parh = compileUrl("/foo/:id/bar", { id: "2" }) - expect(parh).toBe("/foo/2/bar") - }) - }) - - describe("getFullPathByPath", () => { - it("should return the full path", () => { - expect(getFullPathByPath(routeList, "/foo", "FooPage")).toBe("/hello/foo") - expect(getFullPathByPath(routeList, "/yes", "YesPage")).toBe("/hello/foo/bla/yes") - expect(getFullPathByPath(routeList, "/", "FirstLevelRoute-2")).toBe( - "/hello/foo/bla/", - ) - expect(getFullPathByPath(routeList, "/no", "NoPage")).toBe("/hello/foo/bla/no") - }) - }) - - describe("getUrlByRouteName", () => { - it("should return full URL with only page name and params", () => { - expect(getUrlByRouteName(routeList, { name: "HelloPage" })).toBe("/hello") - expect(getUrlByRouteName(routeList, { name: "FooPage" })).toBe("/hello/foo") - expect(getUrlByRouteName(routeList, { name: "BlaPage", params: { id: 2 } })).toBe( - "/hello/foo/bla", - ) - expect(getUrlByRouteName(routeList, { name: "NoPage", params: { id: 4 } })).toBe( - "/hello/foo/bla/no", - ) - }) - - it("should return full URL with only page name with base URL", () => { - // prettier-ignore - const routes = [ - { path: "/a", name: "a-page" }, - { - path: "/b", - name: "b-page", - children: [ - { path: "/c", name: "c-page" }, - { path: "/d", name: "d-page" } - ], - }, - ] - expect(getUrlByRouteName(routes, { name: "b-page" })).toBe("/b") - expect(getUrlByRouteName(routes, { name: "b-page" }, "/test/")).toBe("/test/b") - expect(getUrlByRouteName(routes, { name: "c-page" }, "/test/")).toBe("/test/b/c") - }) - }) - - describe("getLangPath", () => { - it("should format routes properly", () => { - const path = "/:lang/foo" - const pathObj = { fr: "/:lang/foo-fr", en: "/:lang/foo-en" } - expect(getLangPath(path, "fr")).toEqual("/foo") - expect(getLangPath(pathObj, "en")).toEqual("/foo-en") - expect(getLangPath(pathObj, "de")).toBeUndefined() - }) - }) - - describe("addLangToUrl", () => { - it("should add lang to Url", () => { - const url = "/foo/en/bar" - expect(addLangToUrl(url, "en", true)).toBe(`/en${url}`) - expect(addLangToUrl(url, "en", false)).toBe(`${url}`) - }) - }) -})