useMyRouter + jest initial

This commit is contained in:
Kalle (Sendou) 2021-06-16 22:40:08 +03:00
parent 2ccbfdf4e6
commit 80a7647f10
7 changed files with 8110 additions and 18 deletions

View File

@ -1,12 +1,11 @@
import { Playstyle } from "@prisma/client";
import { countries } from "countries-list";
import { useUser } from "hooks/common";
import { useRouter } from "next/router";
import { Dispatch, useMemo, useReducer } from "react";
import { setManySearchParams, setSearchParams } from "utils/setSearchParams";
import { getBooleanFromString, getWeaponFromString } from "utils/strings";
import { trpc } from "utils/trpc";
import { isFreeAgentPlaystyle, isFreeAgentRegion } from "utils/typeGuards";
import { useMyRouter } from "./useMyRouter";
export type FreeAgentRegion = "EUROPE" | "AMERICAS" | "ASIA";
@ -48,7 +47,7 @@ export type UseFreeAgentsDispatch = Dispatch<Action>;
const defaultState: UseFreeAgentsState = { xp: false, plusServer: false };
export function useFreeAgents() {
const router = useRouter();
const router = useMyRouter();
const [user] = useUser();
const posts = trpc.useQuery(["freeAgents.posts"], {
@ -67,27 +66,27 @@ export function useFreeAgents() {
(oldState: UseFreeAgentsState, action: Action) => {
switch (action.type) {
case "SET_PLAYSTYLE":
setSearchParams("playstyle", action.value);
router.setSearchParams(["playstyle", action.value]);
return { ...oldState, playstyle: action.value };
case "SET_XP_VALUE":
setSearchParams("xp", "" + action.value);
router.setSearchParams(["xp", action.value]);
return { ...oldState, xp: action.value };
case "SET_PLUS_SERVER_VALUE":
setSearchParams("plusServer", "" + action.value);
router.setSearchParams(["plusServer", action.value]);
return { ...oldState, plusServer: action.value };
case "SET_WEAPON":
setSearchParams("weapon", action.value);
router.setSearchParams(["weapon", action.value]);
return { ...oldState, weapon: action.value };
case "SET_REGION":
setSearchParams("region", action.value);
router.setSearchParams(["region", action.value]);
return { ...oldState, region: action.value };
case "RESET_FILTERS":
setManySearchParams([], true);
router.resetSearchParams();
return defaultState;
default:

View File

@ -1,11 +1,10 @@
import { SalmonRunRecordCategory } from "@prisma/client";
import { useRouter } from "next/router";
import { salmonRunCategoryToNatural } from "pages/sr/leaderboards/new";
import { GetAllSalmonRunRecordsData } from "prisma/queries/getAllSalmonRunRecords";
import { useReducer } from "react";
import useSWR from "swr";
import { salmonRunStages } from "utils/lists/stages";
import { setSearchParams } from "utils/setSearchParams";
import { useMyRouter } from "./useMyRouter";
export type WeaponsFilter =
| "NORMAL"
@ -41,23 +40,22 @@ type Action =
};
export function useSalmonRunRecords() {
const router = useRouter();
const { data: recordsData } = useSWR<GetAllSalmonRunRecordsData>(
"/api/sr/records"
);
const router = useMyRouter();
const { data: recordsData } =
useSWR<GetAllSalmonRunRecordsData>("/api/sr/records");
const [state, dispatch] = useReducer(
(oldState: UseSalmonRunRecordsState, action: Action) => {
switch (action.type) {
case "SET_STAGE":
setSearchParams("stage", action.stage);
router.setSearchParams(["stage", action.stage]);
return { ...oldState, stage: action.stage };
case "SET_CATEGORY":
setSearchParams("category", action.category);
router.setSearchParams(["category", action.category]);
return { ...oldState, category: action.category };
case "SET_WEAPONS_FILTER":
setSearchParams("filter", action.filter);
router.setSearchParams(["filter", action.filter]);
return { ...oldState, weaponsFilter: action.filter };
default:

45
hooks/useMyRouter.test.ts Normal file
View File

@ -0,0 +1,45 @@
import { adjustedSearchParams } from "./useMyRouter";
const TEST_URL = "https://sendou.ink/freeagents?xp=true&playstyle=MIDLINE";
describe("adjustedSearchParams", () => {
test("no changes return search params unchanged", () => {
const newParams = adjustedSearchParams(TEST_URL, []);
const newParamsObj = Object.fromEntries(Array.from(newParams.entries()));
expect(newParamsObj.xp).toBe("true");
expect(newParamsObj.playstyle).toBe("MIDLINE");
expect(Object.keys(newParamsObj).length).toBe(2);
});
test("adds search params while retaining old", () => {
const newParams = adjustedSearchParams(TEST_URL, [["test", "works"]]);
const newParamsObj = Object.fromEntries(Array.from(newParams.entries()));
expect(newParamsObj.xp).toBe("true");
expect(newParamsObj.playstyle).toBe("MIDLINE");
expect(newParamsObj.test).toBe("works");
expect(Object.keys(newParamsObj).length).toBe(3);
});
test("adds search params of number and boolean types", () => {
const newParams = adjustedSearchParams(TEST_URL, [
["number", 2],
["boolean", true],
]);
const newParamsObj = Object.fromEntries(Array.from(newParams.entries()));
expect(newParamsObj.number).toBe("2");
expect(newParamsObj.boolean).toBe("true");
});
test("falsy value removes the key", () => {
const newParams = adjustedSearchParams(TEST_URL, [
["xp", ""],
["playstyle", null],
]);
const newParamsObj = Object.fromEntries(Array.from(newParams.entries()));
expect(Object.keys(newParamsObj).length).toBe(0);
});
});

84
hooks/useMyRouter.ts Normal file
View File

@ -0,0 +1,84 @@
import { NextRouter, useRouter } from "next/router";
import { URLSearchParams } from "url";
type SearchParamsTuple = [
key: string,
value:
| string
| string[]
| number
| number[]
| boolean
| boolean[]
| null
| undefined
];
export const adjustedSearchParams = (
url: string,
newParams: SearchParamsTuple[]
): URLSearchParams => {
const result = new URL(url).searchParams;
for (const [key, value] of newParams) {
if (!value && typeof value !== "boolean") {
result.delete(key);
continue;
}
if (Array.isArray(value)) {
result.delete(key);
for (const element of value) {
result.append(key, String(element));
}
continue;
}
result.set(key, String(value));
}
result.sort();
return result;
};
const isSearchParamsTuple = (
newParams: SearchParamsTuple | SearchParamsTuple[]
): newParams is SearchParamsTuple => {
return !Array.isArray(newParams[0]);
};
const setRouterSearchParams = (
router: NextRouter,
newParams: SearchParamsTuple | SearchParamsTuple[]
) => {
const newSearchParams = adjustedSearchParams(
window.location.href,
isSearchParamsTuple(newParams) ? [newParams] : newParams
);
router.replace(
`${router.pathname}?${newSearchParams.toString()}`,
undefined,
{
shallow: true,
}
);
};
const resetSearchParams = (router: NextRouter) =>
router.replace(router.pathname, undefined, { shallow: true });
export const useMyRouter = (): NextRouter & {
resetSearchParams: () => void;
setSearchParams: (newParams: SearchParamsTuple | SearchParamsTuple[]) => void;
} => {
const router = useRouter();
return {
...router,
resetSearchParams: () => resetSearchParams(router),
setSearchParams: (newParams: SearchParamsTuple | SearchParamsTuple[]) =>
setRouterSearchParams(router, newParams),
};
};

4
jest.config.js Normal file
View File

@ -0,0 +1,4 @@
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
};

7959
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@
"version": "1.0.0",
"private": true,
"scripts": {
"test": "jest",
"dev": "next dev",
"build": "lingui compile && npm run prebuild && next build",
"build:analyze": "lingui compile && npm run prebuild && ANALYZE=true next build",
@ -68,6 +69,7 @@
"@babel/core": "^7.14.5",
"@lingui/cli": "^3.8.3",
"@lingui/macro": "^3.8.3",
"@types/jest": "^26.0.23",
"@types/node": "^15.12.2",
"@types/nprogress": "^0.2.0",
"@types/react": "^17.0.11",
@ -80,6 +82,7 @@
"fishery": "^1.3.1",
"prettier": "^2.3.1",
"prisma": "^2.24.1",
"ts-jest": "^27.0.3",
"ts-node": "^10.0.0",
"typescript": "^4.3.2"
},