test(routing): add test suite to cover buildRouteLink helper function@@ -1,5 +1,6 @@
.DS_Store
.vscode/
+coverage/
dist/
node_modules/
@@ -1,4 +1,5 @@
.DS_Store
+coverage/
dist/
node_modules/
@@ -37,7 +37,7 @@ export enum AppRoute {
REPOSITORY_PULL_REQUEST_CREATE_ACTION = "repository.pull_request.create.action",
REPOSITORY_PULL_REQUEST_DELETE_ACTION = "repository.pull_request.delete.action",
REPOSITORY_PULL_REQUEST_DETAILS = "repository.pull_request.details",
- REPOSITORY_PULL_REQUEST_MERGE_ACTION = "/:orgSlug/:repoSlug/pulls/:pullUid/merge",
+ REPOSITORY_PULL_REQUEST_MERGE_ACTION = "repository.pull_request.merge.action",
REPOSITORY_PULL_REQUEST_UPDATE_ACTION = "repository.pull_request.update.action",
REPOSITORY_PULL_REQUESTS = "repository.pull_requests",
REPOSITORY_SHOW_OBJECT = "repository.show_object",
@@ -0,0 +1,63 @@
+import { AppRoute } from "../../routes.defs";
+import { default as buildRouteLink } from "./buildRouteLink";
+
+describe("buildRouteLink", () => {
+ it(`should return path from route without transform when it gets called with a null as routeParams argument`, () => {
+ // Given
+ const routeName: AppRoute = AppRoute.HOME;
+ // When
+ const link = buildRouteLink(routeName, null);
+ // Then
+ expect(link).toMatchInlineSnapshot(`"/"`);
+ });
+ it(`should return path from route without transform when it gets called with an empty object as routeParams argument`, () => {
+ // Given
+ const routeName: AppRoute = AppRoute.HOME;
+ // When
+ const link = buildRouteLink(routeName, {});
+ // Then
+ expect(link).toMatchInlineSnapshot(`"/"`);
+ });
+ it(`should return path from route with params values when it gets called with an object containing values as routeParams argument`, () => {
+ // Given
+ const routeName: AppRoute = AppRoute.ORGANIZATION_DETAILS;
+ // When
+ const link = buildRouteLink(routeName, {
+ orgSlug: "my-super-org",
+ });
+ // Then
+ expect(link).toMatchInlineSnapshot(`"/my-super-org"`);
+ });
+ it(`should return path from route with params values when it gets called with an object containing values and magic "*" value as routeParams argument`, () => {
+ // Given
+ const routeName: AppRoute = AppRoute.REPOSITORY_BROWSER_WITH_PATH;
+ // When
+ const link = buildRouteLink(routeName, {
+ orgSlug: "my-super-org",
+ repoSlug: "my-super-repo",
+ currentRef: "main",
+ "*": "app.manifest.json",
+ });
+ // Then
+ expect(link).toMatchInlineSnapshot(
+ `"/my-super-org/my-super-repo/main/tree/app.manifest.json"`
+ );
+ });
+ it(`should return path from route with url encoded params values when it gets called with an object containing values as routeParams argument`, () => {
+ // Given
+ const routeName: AppRoute = AppRoute.REPOSITORY_BROWSER_WITH_PATH;
+ // When
+ const link = buildRouteLink(routeName, {
+ orgSlug: "my-super-org",
+ repoSlug: "my-super-repo",
+ currentRef: "main",
+ "*": "app/services/auth/isExistingEmailAddress.ts",
+ });
+ // Then
+ expect(link).toMatchInlineSnapshot(
+ `"/my-super-org/my-super-repo/main/tree/app%2Fservices%2Fauth%2FisExistingEmailAddress.ts"`
+ );
+ });
+});
+
+describe("buildPathLink", () => {});
@@ -7,7 +7,15 @@ export default function buildRouteLink<P extends AppRoute>(
: {} | null
): typeof AppRoutePaths[P] {
const path = AppRoutePaths[route];
+ return buildPathLink(path, routeParams);
+}
+export function buildPathLink<P extends AppRoute>(
+ path: string,
+ routeParams: "params" extends keyof AppRoutesParams[P]
+ ? AppRoutesParams[P]["params"]
+ : {} | null
+): string {
if (
routeParams == null ||
(routeParams != null &&
@@ -17,52 +25,33 @@ export default function buildRouteLink<P extends AppRoute>(
return path;
}
- const paramsEntries =
- typeof routeParams === "object" ? Object.entries(routeParams as never) : [];
-
- let pathParts = path.split("/");
- let linkBuilder = [] as string[];
-
- paramsEntries.forEach(([k, v]) => {
- const keyRegExp = new RegExp(`:${k}`, "g");
- pathParts = pathParts.map((part) => {
- if (Array.isArray(part.match(keyRegExp))) {
- return part.replace(keyRegExp, `${String(v)}`);
- } else if (k === "*" && part === "*") {
- return String(v);
- }
- return part;
- });
- });
-
- pathParts.map((part) => linkBuilder.push(part));
- return linkBuilder.join("/");
-}
-
-export function buildPathLink<P extends AppRoute>(
- path: string,
- routeParams: "params" extends keyof AppRoutesParams[P]
- ? AppRoutesParams[P]["params"]
- : undefined
-): string {
- const paramsEntries =
- typeof routeParams === "object" ? Object.entries(routeParams as never) : [];
+ const paramsEntries = Object.entries(routeParams as never);
let pathParts = path.split("/");
let linkBuilder = [] as string[];
- paramsEntries.forEach(([k, v]) => {
- const keyRegExp = new RegExp(`:${k}`, "g");
- pathParts = pathParts.map((part) => {
- if (Array.isArray(part.match(keyRegExp))) {
- return part.replace(keyRegExp, `${String(v)}`);
- } else if (k === "*" && part === "*") {
- return String(v);
- }
- return part;
- });
+ pathParts.forEach((part) => {
+ if (part.trim() === "") {
+ linkBuilder.push("");
+ } else if (
+ "*" in routeParams &&
+ (routeParams as any)["*"] != null &&
+ part === "*"
+ ) {
+ linkBuilder.push((routeParams as any)["*"]);
+ } else if (part.includes(":")) {
+ paramsEntries.forEach(([k, v]) => {
+ if (k == null || k.trim() === "" || k === "*") return;
+ const keyRegExp = new RegExp(`:${k}`, "g");
+ if (Array.isArray(part.match(keyRegExp))) {
+ linkBuilder.push(part.replace(keyRegExp, `${String(v)}`));
+ }
+ });
+ } else {
+ linkBuilder.push(part);
+ }
});
- pathParts.map((part) => linkBuilder.push(part));
- return linkBuilder.join("/");
+ const link = linkBuilder.map((param) => encodeURIComponent(param)).join("/");
+ return link;
}
@@ -75,6 +75,7 @@
"jest": "^27.5.1",
"npm-run-all": "^4.1.5",
"prisma": "^4.4.0",
+ "ts-jest": "^27.1.5",
"ts-node-dev": "^2.0.0",
"tslib": "^2.4.0",
"typescript": "^4.6.2"
@@ -1373,6 +1373,12 @@ browserslist@^4.20.2:
node-releases "^2.0.5"
update-browserslist-db "^1.0.4"
+bs-logger@0.x:
+ version "0.2.6"
+ resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8"
+ dependencies:
+ fast-json-stable-stringify "2.x"
+
bser@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05"
@@ -2074,7 +2080,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
-fast-json-stable-stringify@^2.0.0:
+fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
@@ -3216,7 +3222,7 @@ jest-snapshot@^27.5.1:
pretty-format "^27.5.1"
semver "^7.3.2"
-jest-util@^27.5.1:
+jest-util@^27.0.0, jest-util@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9"
dependencies:
@@ -3352,7 +3358,7 @@ json-schema-traverse@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
-json5@^2.2.1:
+json5@2.x, json5@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
@@ -3562,6 +3568,10 @@ lodash.isstring@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
+lodash.memoize@4.x:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
+
lodash.once@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
@@ -3623,7 +3633,7 @@ make-dir@^3.0.0:
dependencies:
semver "^6.0.0"
-make-error@^1.1.1:
+make-error@1.x, make-error@^1.1.1:
version "1.3.6"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
@@ -4427,6 +4437,12 @@ semver-store@^0.3.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+semver@7.x:
+ version "7.3.8"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
+ dependencies:
+ lru-cache "^6.0.0"
+
semver@^6.0.0, semver@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
@@ -4850,6 +4866,19 @@ tree-kill@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
+ts-jest@^27.1.5:
+ version "27.1.5"
+ resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.5.tgz#0ddf1b163fbaae3d5b7504a1e65c914a95cff297"
+ dependencies:
+ bs-logger "0.x"
+ fast-json-stable-stringify "2.x"
+ jest-util "^27.0.0"
+ json5 "2.x"
+ lodash.memoize "4.x"
+ make-error "1.x"
+ semver "7.x"
+ yargs-parser "20.x"
+
ts-node-dev@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-2.0.0.tgz#bdd53e17ab3b5d822ef519928dc6b4a7e0f13065"
@@ -5134,7 +5163,7 @@ yallist@^2.0.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
-yargs-parser@^20.2.2:
+yargs-parser@20.x, yargs-parser@^20.2.2:
version "20.2.9"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"