From 5094cff07a89782bbd2565a4e92bf603536f5bbe Mon Sep 17 00:00:00 2001 From: Pig Fang Date: Sat, 30 May 2020 10:44:36 +0800 Subject: [PATCH] generate URLs by codegen --- .github/workflows/CI.yml | 11 +- .gitignore | 1 + package.json | 4 +- resources/assets/src/scripts/logout.ts | 3 +- .../views/admin/PlayersManagement/index.tsx | 11 +- .../src/views/admin/UsersManagement/index.tsx | 17 +- resources/assets/src/views/auth/Forgot.tsx | 3 +- resources/assets/src/views/auth/Login.tsx | 3 +- .../assets/src/views/auth/Registration.tsx | 3 +- resources/assets/src/views/auth/Reset.tsx | 3 +- .../src/views/skinlib/Show/addClosetItem.ts | 3 +- .../src/views/skinlib/SkinLibrary/index.tsx | 5 +- resources/assets/src/views/skinlib/Upload.tsx | 7 +- .../src/views/user/Closet/ModalApply.tsx | 5 +- .../assets/src/views/user/Closet/index.tsx | 5 +- .../src/views/user/Closet/removeClosetItem.ts | 3 +- .../src/views/user/Closet/setAsAvatar.ts | 3 +- .../assets/src/views/user/Dashboard/index.tsx | 5 +- .../src/views/user/Players/ModalAddPlayer.tsx | 3 +- .../assets/src/views/user/Players/index.tsx | 7 +- resources/assets/tests/scripts/logout.test.ts | 3 +- .../views/admin/PlayersManagement.test.tsx | 26 ++-- .../views/admin/UsersManagement.test.tsx | 44 +++--- .../assets/tests/views/auth/Forgot.test.tsx | 5 +- .../assets/tests/views/auth/Login.test.tsx | 15 +- .../tests/views/auth/Registration.test.tsx | 7 +- .../assets/tests/views/skinlib/Show.test.tsx | 5 +- .../tests/views/skinlib/SkinLibrary.test.tsx | 31 ++-- .../tests/views/skinlib/Upload.test.tsx | 8 +- .../assets/tests/views/user/Closet.test.tsx | 39 ++--- .../tests/views/user/Dashboard.test.tsx | 5 +- .../assets/tests/views/user/Players.test.tsx | 49 +++--- routes/web.php | 8 +- scripts/build.ps1 | 1 + scripts/generateUrls.ts | 145 ++++++++++++++++++ tsconfig.node.json | 8 + yarn.lock | 39 +++++ 37 files changed, 385 insertions(+), 158 deletions(-) create mode 100644 scripts/generateUrls.ts create mode 100644 tsconfig.node.json diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 543965e3..c397a1c3 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -85,10 +85,13 @@ jobs: - name: Checkout code uses: actions/checkout@v2 - name: Install dependencies - run: yarn + run: | + yarn + composer install --prefer-dist --no-progress --no-suggest --no-dev - name: Run checks run: | # yarn lint + yarn build:urls yarn tsc -p . --noEmit yarn tsc -p ./resources/assets/tests --noEmit jest: @@ -99,7 +102,11 @@ jobs: - name: Checkout code uses: actions/checkout@v2 - name: Install dependencies - run: yarn + run: | + yarn + composer install --prefer-dist --no-progress --no-suggest --no-dev + - name: Build + run: yarn build:urls - name: Run tests run: yarn test --coverage - name: Upload coverage report diff --git a/.gitignore b/.gitignore index bf8e112b..e91f6ba5 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ storage/options.php .php_cs.cache resources/views/overrides public/sw.js +resources/assets/src/scripts/urls.ts diff --git a/package.json b/package.json index fee9fdf4..0ab05358 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "build": "webpack --mode=production -p --progress", "lint": "eslint --ext=ts -f=beauty .", "fmt": "prettier --write resources/assets", - "test": "jest" + "test": "jest", + "build:urls": "ts-node -P tsconfig.node.json scripts/generateUrls.ts" }, "dependencies": { "@emotion/core": "^10.0.28", @@ -79,6 +80,7 @@ "style-loader": "^1.2.1", "ts-jest": "^26.0.0", "ts-loader": "^7.0.4", + "ts-node": "^8.10.2", "typescript": "^3.9.2", "url-loader": "^4.1.0", "webpack": "^4.43.0", diff --git a/resources/assets/src/scripts/logout.ts b/resources/assets/src/scripts/logout.ts index 7564f652..74cd4ca8 100644 --- a/resources/assets/src/scripts/logout.ts +++ b/resources/assets/src/scripts/logout.ts @@ -1,6 +1,7 @@ import { post } from './net' import { t } from './i18n' import { showModal } from './notify' +import urls from './urls' export async function logout() { try { @@ -12,7 +13,7 @@ export async function logout() { return } - await post('/auth/logout') + await post(urls.auth.logout()) window.location.href = blessing.base_url } diff --git a/resources/assets/src/views/admin/PlayersManagement/index.tsx b/resources/assets/src/views/admin/PlayersManagement/index.tsx index 493ae1e0..614d6691 100644 --- a/resources/assets/src/views/admin/PlayersManagement/index.tsx +++ b/resources/assets/src/views/admin/PlayersManagement/index.tsx @@ -6,6 +6,7 @@ import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { Player, Paginator } from '@/scripts/types' import { toast, showModal } from '@/scripts/notify' +import urls from '@/scripts/urls' import Loading from '@/components/Loading' import Pagination from '@/components/Pagination' import Header from '../UsersManagement/Header' @@ -32,7 +33,7 @@ const PlayersManagement: React.FC = () => { const getPlayers = async () => { setIsLoading(true) const { data, last_page }: Paginator = await fetch.get( - '/admin/players/list', + urls.admin.players.list(), { q: query, page, @@ -79,7 +80,7 @@ const PlayersManagement: React.FC = () => { } const { code, message } = await fetch.put( - `/admin/players/${player.pid}/name`, + urls.admin.players.name(player.pid), { player_name: name }, ) if (code === 0) { @@ -107,7 +108,7 @@ const PlayersManagement: React.FC = () => { } const { code, message } = await fetch.put( - `/admin/players/${player.pid}/owner`, + urls.admin.players.owner(player.pid), { uid }, ) if (code === 0) { @@ -124,7 +125,7 @@ const PlayersManagement: React.FC = () => { const handleUpdateTexture = async (type: 'skin' | 'cape', tid: number) => { const { code, message } = await fetch.put( - `/admin/players/${players[textureUpdating].pid}/textures`, + urls.admin.players.texture(players[textureUpdating].pid), { type, tid }, ) @@ -150,7 +151,7 @@ const PlayersManagement: React.FC = () => { } const { code, message } = await fetch.del( - `/admin/players/${player.pid}`, + urls.admin.players.delete(player.pid), ) if (code === 0) { setPlayers((players) => players.filter(({ pid }) => pid !== player.pid)) diff --git a/resources/assets/src/views/admin/UsersManagement/index.tsx b/resources/assets/src/views/admin/UsersManagement/index.tsx index 897a0a20..61a686c0 100644 --- a/resources/assets/src/views/admin/UsersManagement/index.tsx +++ b/resources/assets/src/views/admin/UsersManagement/index.tsx @@ -7,6 +7,7 @@ import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { User, UserPermission, Paginator } from '@/scripts/types' import { toast, showModal } from '@/scripts/notify' +import urls from '@/scripts/urls' import type { Props as ModalInputProps } from '@/components/ModalInput' import Loading from '@/components/Loading' import Pagination from '@/components/Pagination' @@ -36,7 +37,7 @@ const UsersManagement: React.FC = () => { const getUsers = async () => { setIsLoading(true) const { data, last_page }: Paginator = await fetch.get( - '/admin/users/list', + urls.admin.users.list(), { q: query, page, @@ -83,7 +84,7 @@ const UsersManagement: React.FC = () => { } const { code, message } = await fetch.put( - `/admin/users/${user.uid}/email`, + urls.admin.users.email(user.uid), { email }, ) if (code === 0) { @@ -115,7 +116,7 @@ const UsersManagement: React.FC = () => { } const { code, message } = await fetch.put( - `/admin/users/${user.uid}/nickname`, + urls.admin.users.nickname(user.uid), { nickname }, ) if (code === 0) { @@ -143,7 +144,7 @@ const UsersManagement: React.FC = () => { } const { code, message } = await fetch.put( - `/admin/users/${user.uid}/score`, + urls.admin.users.score(user.uid), { score }, ) if (code === 0) { @@ -180,7 +181,7 @@ const UsersManagement: React.FC = () => { } const { code, message } = await fetch.put( - `/admin/users/${user.uid}/permission`, + urls.admin.users.permission(user.uid), { permission }, ) if (code === 0) { @@ -195,7 +196,7 @@ const UsersManagement: React.FC = () => { const handleVerificationToggle = async (user: User, index: number) => { const { code, message } = await fetch.put( - `/admin/users/${user.uid}/verification`, + urls.admin.users.verification(user.uid), ) if (code === 0) { toast.success(message) @@ -222,7 +223,7 @@ const UsersManagement: React.FC = () => { } const { code, message } = await fetch.put( - `/admin/users/${user.uid}/password`, + urls.admin.users.password(user.uid), { password }, ) if (code === 0) { @@ -242,7 +243,7 @@ const UsersManagement: React.FC = () => { return } - const { code, message } = await fetch.del(`/admin/users/${user.uid}`) + const { code, message } = await fetch.del(urls.admin.users.delete(user.uid)) if (code === 0) { toast.success(message) setUsers((users) => users.filter(({ uid }) => uid !== user.uid)) diff --git a/resources/assets/src/views/auth/Forgot.tsx b/resources/assets/src/views/auth/Forgot.tsx index f7e26e2d..c642337e 100644 --- a/resources/assets/src/views/auth/Forgot.tsx +++ b/resources/assets/src/views/auth/Forgot.tsx @@ -2,6 +2,7 @@ import React, { useState, useRef } from 'react' import { hot } from 'react-hot-loader/root' import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' +import urls from '@/scripts/urls' import Alert from '@/components/Alert' import Captcha from '@/components/Captcha' @@ -23,7 +24,7 @@ const Forgot: React.FC = () => { const captcha = await ref.current!.execute() const { code, message } = await fetch.post( - '/auth/forgot', + urls.auth.forgot(), { email, captcha }, ) if (code === 0) { diff --git a/resources/assets/src/views/auth/Login.tsx b/resources/assets/src/views/auth/Login.tsx index 4cd17890..3304a26c 100644 --- a/resources/assets/src/views/auth/Login.tsx +++ b/resources/assets/src/views/auth/Login.tsx @@ -4,6 +4,7 @@ import useBlessingExtra from '@/scripts/hooks/useBlessingExtra' import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { showModal } from '@/scripts/notify' +import urls from '@/scripts/urls' import Alert from '@/components/Alert' import Captcha from '@/components/Captcha' @@ -58,7 +59,7 @@ const Login: React.FC = () => { event.preventDefault() setIsPending(true) - const response = await fetch.post('/auth/login', { + const response = await fetch.post(urls.auth.login(), { identification, password, keep: remember, diff --git a/resources/assets/src/views/auth/Registration.tsx b/resources/assets/src/views/auth/Registration.tsx index 1961e37b..fadaec28 100644 --- a/resources/assets/src/views/auth/Registration.tsx +++ b/resources/assets/src/views/auth/Registration.tsx @@ -4,6 +4,7 @@ import useBlessingExtra from '@/scripts/hooks/useBlessingExtra' import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { toast } from '@/scripts/notify' +import urls from '@/scripts/urls' import Alert from '@/components/Alert' import Captcha from '@/components/Captcha' @@ -55,7 +56,7 @@ const Registration: React.FC = () => { setIsPending(true) const { code, message } = await fetch.post( - '/auth/register', + urls.auth.register(), Object.assign( { email, password, captcha: await captchaRef.current!.execute() }, requirePlayer ? { player_name: playerName } : { nickname: nickName }, diff --git a/resources/assets/src/views/auth/Reset.tsx b/resources/assets/src/views/auth/Reset.tsx index 4682c553..72e30796 100644 --- a/resources/assets/src/views/auth/Reset.tsx +++ b/resources/assets/src/views/auth/Reset.tsx @@ -3,6 +3,7 @@ import { hot } from 'react-hot-loader/root' import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { toast } from '@/scripts/notify' +import urls from '@/scripts/urls' import Alert from '@/components/Alert' const Reset: React.FC = () => { @@ -37,7 +38,7 @@ const Reset: React.FC = () => { if (code === 0) { toast.success(message) setTimeout(() => { - window.location.href = `${blessing.base_url}/auth/login` + window.location.href = blessing.base_url + urls.auth.login() }, 2000) } else { setWarningMessage(message) diff --git a/resources/assets/src/views/skinlib/Show/addClosetItem.ts b/resources/assets/src/views/skinlib/Show/addClosetItem.ts index 73412a56..433e6ea5 100644 --- a/resources/assets/src/views/skinlib/Show/addClosetItem.ts +++ b/resources/assets/src/views/skinlib/Show/addClosetItem.ts @@ -2,6 +2,7 @@ import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { showModal, toast } from '@/scripts/notify' import { Texture } from '@/scripts/types' +import urls from '@/scripts/urls' export default async function addClosetItem( texture: Pick, @@ -25,7 +26,7 @@ export default async function addClosetItem( } const { code, message } = await fetch.post( - '/user/closet', + urls.user.closet.add(), { tid: texture.tid, name }, ) if (code === 0) { diff --git a/resources/assets/src/views/skinlib/SkinLibrary/index.tsx b/resources/assets/src/views/skinlib/SkinLibrary/index.tsx index 213b9bb1..390c6e52 100644 --- a/resources/assets/src/views/skinlib/SkinLibrary/index.tsx +++ b/resources/assets/src/views/skinlib/SkinLibrary/index.tsx @@ -5,6 +5,7 @@ import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { toast } from '@/scripts/notify' import { Paginator, TextureType } from '@/scripts/types' +import urls from '@/scripts/urls' import Loading from '@/components/Loading' import Pagination from '@/components/Pagination' import addClosetItem from '../Show/addClosetItem' @@ -82,7 +83,7 @@ const SkinLibrary: React.FC = () => { window.history.pushState(search.toString(), '', `?${search}`) const result = await fetch.get>( - '/skinlib/list', + urls.skinlib.list(), search, ) setItems(result.data) @@ -94,7 +95,7 @@ const SkinLibrary: React.FC = () => { useEffect(() => { const getCloset = async () => { - const closet = await fetch.get('/user/closet/ids') + const closet = await fetch.get(urls.user.closet.ids()) setCloset(closet) } if (currentUid) { diff --git a/resources/assets/src/views/skinlib/Upload.tsx b/resources/assets/src/views/skinlib/Upload.tsx index 8f217859..83ccee99 100644 --- a/resources/assets/src/views/skinlib/Upload.tsx +++ b/resources/assets/src/views/skinlib/Upload.tsx @@ -8,6 +8,7 @@ import * as fetch from '@/scripts/net' import { showModal, toast } from '@/scripts/notify' import { isAlex } from '@/scripts/textureUtils' import { TextureType } from '@/scripts/types' +import urls from '@/scripts/urls' import FileInput from '@/components/FileInput' import ViewerSkeleton from '@/components/ViewerSkeleton' @@ -86,11 +87,11 @@ const Upload: React.FC = () => { setIsUploading(true) const { code, message, data: { tid } = { tid: 0 } } = await fetch.post< fetch.ResponseBody<{ tid: number }> - >('/skinlib/upload', formData) + >(urls.skinlib.upload(), formData) setIsUploading(false) if (code === 0) { - window.location.href = `${blessing.base_url}/skinlib/show/${tid}` + window.location.href = blessing.base_url + urls.skinlib.show(tid) } else if (code === 2) { try { await showModal({ @@ -98,7 +99,7 @@ const Upload: React.FC = () => { text: message, okButtonText: t('user.viewInSkinlib'), }) - window.location.href = `${blessing.base_url}/skinlib/show/${tid}` + window.location.href = blessing.base_url + urls.skinlib.show(tid) } catch { // } diff --git a/resources/assets/src/views/user/Closet/ModalApply.tsx b/resources/assets/src/views/user/Closet/ModalApply.tsx index 18136186..f3f30651 100644 --- a/resources/assets/src/views/user/Closet/ModalApply.tsx +++ b/resources/assets/src/views/user/Closet/ModalApply.tsx @@ -4,6 +4,7 @@ import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { toast } from '@/scripts/notify' import { Player } from '@/scripts/types' +import urls from '@/scripts/urls' import Loading from '@/components/Loading' import Modal from '@/components/Modal' @@ -29,7 +30,7 @@ const ModalApply: React.FC = (props) => { const getPlayers = async () => { setIsLoading(true) - const players = await fetch.get('/user/player/list') + const players = await fetch.get(urls.user.player.list()) setPlayers(players) setIsLoading(false) } @@ -42,7 +43,7 @@ const ModalApply: React.FC = (props) => { const handleSelect = async (player: Player) => { const { code, message } = await fetch.post( - `/user/player/set/${player.pid}`, + urls.user.player.set(player.pid), { skin: props.skin, cape: props.cape, diff --git a/resources/assets/src/views/user/Closet/index.tsx b/resources/assets/src/views/user/Closet/index.tsx index bbfea392..747059b7 100644 --- a/resources/assets/src/views/user/Closet/index.tsx +++ b/resources/assets/src/views/user/Closet/index.tsx @@ -10,6 +10,7 @@ import { Paginator, TextureType, } from '@/scripts/types' +import urls from '@/scripts/urls' import Loading from '@/components/Loading' import Pagination from '@/components/Pagination' import ClosetItem from './ClosetItem' @@ -43,7 +44,7 @@ const Closet: React.FC = () => { const getItems = async () => { setIsLoading(true) const { data, last_page } = await fetch.get>( - '/user/closet/list', + urls.user.closet.list(), { category, q: query, page }, ) @@ -106,7 +107,7 @@ const Closet: React.FC = () => { } const { code, message } = await fetch.put( - `/user/closet/${item.tid}`, + urls.user.closet.rename(item.tid), { name }, ) if (code === 0) { diff --git a/resources/assets/src/views/user/Closet/removeClosetItem.ts b/resources/assets/src/views/user/Closet/removeClosetItem.ts index da36d5a4..9f62c254 100644 --- a/resources/assets/src/views/user/Closet/removeClosetItem.ts +++ b/resources/assets/src/views/user/Closet/removeClosetItem.ts @@ -1,6 +1,7 @@ import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { showModal, toast } from '@/scripts/notify' +import urls from '@/scripts/urls' export default async function removeClosetItem(tid: number): Promise { try { @@ -13,7 +14,7 @@ export default async function removeClosetItem(tid: number): Promise { } const { code, message } = await fetch.del( - `/user/closet/${tid}`, + urls.user.closet.remove(tid), ) if (code === 0) { toast.success(message) diff --git a/resources/assets/src/views/user/Closet/setAsAvatar.ts b/resources/assets/src/views/user/Closet/setAsAvatar.ts index 54b7f6eb..f8f08e09 100644 --- a/resources/assets/src/views/user/Closet/setAsAvatar.ts +++ b/resources/assets/src/views/user/Closet/setAsAvatar.ts @@ -1,6 +1,7 @@ import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { showModal, toast } from '@/scripts/notify' +import urls from '@/scripts/urls' export default async function setAsAvatar(tid: number) { try { @@ -13,7 +14,7 @@ export default async function setAsAvatar(tid: number) { } const { code, message } = await fetch.post( - '/user/profile/avatar', + urls.user.profile.avatar(), { tid }, ) if (code === 0) { diff --git a/resources/assets/src/views/user/Dashboard/index.tsx b/resources/assets/src/views/user/Dashboard/index.tsx index a0788e68..d5a94905 100644 --- a/resources/assets/src/views/user/Dashboard/index.tsx +++ b/resources/assets/src/views/user/Dashboard/index.tsx @@ -5,6 +5,7 @@ import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { toast } from '@/scripts/notify' import useTween from '@/scripts/hooks/useTween' +import urls from '@/scripts/urls' import * as breakpoints from '@/styles/breakpoints' import InfoBox from './InfoBox' import SignButton from './SignButton' @@ -57,7 +58,7 @@ const Dashboard: React.FC = () => { const fetchInfo = async () => { setLoading(true) const { data } = await fetch.get>( - '/user/score-info', + urls.user.score(), ) setPlayers(data.stats.players) setStorage(data.stats.storage) @@ -74,7 +75,7 @@ const Dashboard: React.FC = () => { setLoading(true) const { code, message, data } = await fetch.post< fetch.ResponseBody - >('/user/sign') + >(urls.user.sign()) if (code === 0) { toast.success(message) diff --git a/resources/assets/src/views/user/Players/ModalAddPlayer.tsx b/resources/assets/src/views/user/Players/ModalAddPlayer.tsx index 336bda71..f65cafff 100644 --- a/resources/assets/src/views/user/Players/ModalAddPlayer.tsx +++ b/resources/assets/src/views/user/Players/ModalAddPlayer.tsx @@ -3,6 +3,7 @@ import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { toast } from '@/scripts/notify' import { Player } from '@/scripts/types' +import urls from '@/scripts/urls' import Modal from '@/components/Modal' type Extra = { @@ -28,7 +29,7 @@ const ModalAddPlayer: React.FC = (props) => { const handleConfirm = async () => { const { code, message, data: player } = await fetch.post< fetch.ResponseBody - >('/user/player/add', { name }) + >(urls.user.player.add(), { name }) if (code === 0) { toast.success(message) props.onAdd(player) diff --git a/resources/assets/src/views/user/Players/index.tsx b/resources/assets/src/views/user/Players/index.tsx index c66c81bd..909dc76b 100644 --- a/resources/assets/src/views/user/Players/index.tsx +++ b/resources/assets/src/views/user/Players/index.tsx @@ -5,6 +5,7 @@ import * as fetch from '@/scripts/net' import { showModal, toast } from '@/scripts/notify' import useTexture from '@/scripts/hooks/useTexture' import { Player, TextureType } from '@/scripts/types' +import urls from '@/scripts/urls' import Loading from '@/components/Loading' import Row from './Row' import Previewer from './Previewer' @@ -67,7 +68,7 @@ const Players: React.FC = () => { } const { code, message } = await fetch.post( - `/user/player/rename/${player.pid}`, + urls.user.player.rename(player.pid), { name }, ) if (code === 0) { @@ -88,7 +89,7 @@ const Players: React.FC = () => { } const { code, message } = await fetch.post( - `/user/player/texture/clear/${selected}`, + urls.user.player.clear(selected), { type: [skin && 'skin', cape && 'cape'].filter(Boolean) }, ) if (code === 0) { @@ -128,7 +129,7 @@ const Players: React.FC = () => { } const { code, message } = await fetch.post( - `/user/player/delete/${player.pid}`, + urls.user.player.delete(player.pid), ) if (code === 0) { toast.success(message) diff --git a/resources/assets/tests/scripts/logout.test.ts b/resources/assets/tests/scripts/logout.test.ts index 8f7d9d3c..aaa7978b 100644 --- a/resources/assets/tests/scripts/logout.test.ts +++ b/resources/assets/tests/scripts/logout.test.ts @@ -1,6 +1,7 @@ import { logout } from '@/scripts/logout' import { post } from '@/scripts/net' import { showModal } from '@/scripts/notify' +import urls from '@/scripts/urls' jest.mock('@/scripts/net') jest.mock('@/scripts/notify') @@ -13,5 +14,5 @@ test('log out', async () => { expect(post).not.toBeCalled() await logout() - expect(post).toBeCalledWith('/auth/logout') + expect(post).toBeCalledWith(urls.auth.logout()) }) diff --git a/resources/assets/tests/views/admin/PlayersManagement.test.tsx b/resources/assets/tests/views/admin/PlayersManagement.test.tsx index cbad523a..945b682f 100644 --- a/resources/assets/tests/views/admin/PlayersManagement.test.tsx +++ b/resources/assets/tests/views/admin/PlayersManagement.test.tsx @@ -4,6 +4,7 @@ import { createPaginator } from '../../utils' import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { Player } from '@/scripts/types' +import urls from '@/scripts/urls' import PlayersManagement from '@/views/admin/PlayersManagement' jest.mock('@/scripts/net') @@ -30,7 +31,10 @@ test('search players', async () => { const { getByTitle, getByText } = render() await waitFor(() => - expect(fetch.get).toBeCalledWith('/admin/players/list', { q: '', page: 1 }), + expect(fetch.get).toBeCalledWith(urls.admin.players.list(), { + q: '', + page: 1, + }), ) fireEvent.input(getByTitle(t('vendor.datatable.search')), { @@ -38,7 +42,7 @@ test('search players', async () => { }) fireEvent.click(getByText(t('vendor.datatable.search'))) await waitFor(() => - expect(fetch.get).toBeCalledWith('/admin/players/list', { + expect(fetch.get).toBeCalledWith(urls.admin.players.list(), { q: 's', page: 1, }), @@ -103,7 +107,7 @@ describe('update player name', () => { fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.put).toBeCalledWith(`/admin/players/${fixture.pid}/name`, { + expect(fetch.put).toBeCalledWith(urls.admin.players.name(fixture.pid), { player_name: 'reina', }), ) @@ -127,7 +131,7 @@ describe('update player name', () => { fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.put).toBeCalledWith(`/admin/players/${fixture.pid}/name`, { + expect(fetch.put).toBeCalledWith(urls.admin.players.name(fixture.pid), { player_name: 'reina', }), ) @@ -170,7 +174,7 @@ describe('update owner', () => { fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.put).toBeCalledWith(`/admin/players/${fixture.pid}/owner`, { + expect(fetch.put).toBeCalledWith(urls.admin.players.owner(fixture.pid), { uid: 2, }), ) @@ -194,7 +198,7 @@ describe('update owner', () => { fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.put).toBeCalledWith(`/admin/players/${fixture.pid}/owner`, { + expect(fetch.put).toBeCalledWith(urls.admin.players.owner(fixture.pid), { uid: 2, }), ) @@ -237,7 +241,7 @@ describe('update texture', () => { await waitFor(() => expect(fetch.put).toBeCalledWith( - `/admin/players/${fixture.pid}/textures`, + urls.admin.players.texture(fixture.pid), { type: 'skin', tid: 2, @@ -265,7 +269,7 @@ describe('update texture', () => { await waitFor(() => expect(fetch.put).toBeCalledWith( - `/admin/players/${fixture.pid}/textures`, + urls.admin.players.texture(fixture.pid), { type: 'cape', tid: 2, @@ -292,7 +296,7 @@ describe('update texture', () => { await waitFor(() => expect(fetch.put).toBeCalledWith( - `/admin/players/${fixture.pid}/textures`, + urls.admin.players.texture(fixture.pid), { type: 'skin', tid: 2, @@ -332,7 +336,7 @@ describe('delete player', () => { fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.del).toBeCalledWith(`/admin/players/${fixture.pid}`), + expect(fetch.del).toBeCalledWith(urls.admin.players.delete(fixture.pid)), ) expect(queryByText('ok')).toBeInTheDocument() expect(queryByRole('status')).toHaveClass('alert-success') @@ -351,7 +355,7 @@ describe('delete player', () => { fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.del).toBeCalledWith(`/admin/players/${fixture.pid}`), + expect(fetch.del).toBeCalledWith(urls.admin.players.delete(fixture.pid)), ) expect(queryByText('failed')).toBeInTheDocument() expect(queryByRole('alert')).toHaveClass('alert-danger') diff --git a/resources/assets/tests/views/admin/UsersManagement.test.tsx b/resources/assets/tests/views/admin/UsersManagement.test.tsx index 02e67b72..6fc160ae 100644 --- a/resources/assets/tests/views/admin/UsersManagement.test.tsx +++ b/resources/assets/tests/views/admin/UsersManagement.test.tsx @@ -4,6 +4,7 @@ import { createPaginator } from '../../utils' import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { User, UserPermission } from '@/scripts/types' +import urls from '@/scripts/urls' import UsersManagement from '@/views/admin/UsersManagement' jest.mock('@/scripts/net') @@ -40,7 +41,10 @@ test('search users', async () => { const { getByTitle, getByText } = render() await waitFor(() => - expect(fetch.get).toBeCalledWith('/admin/users/list', { q: '', page: 1 }), + expect(fetch.get).toBeCalledWith(urls.admin.users.list(), { + q: '', + page: 1, + }), ) fireEvent.input(getByTitle(t('vendor.datatable.search')), { @@ -48,7 +52,7 @@ test('search users', async () => { }) fireEvent.click(getByText(t('vendor.datatable.search'))) await waitFor(() => - expect(fetch.get).toBeCalledWith('/admin/users/list', { + expect(fetch.get).toBeCalledWith(urls.admin.users.list(), { q: 's', page: 1, }), @@ -267,7 +271,7 @@ describe('update email', () => { fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.put).toBeCalledWith(`/admin/users/${fixture.uid}/email`, { + expect(fetch.put).toBeCalledWith(urls.admin.users.email(fixture.uid), { email: 'd@e.f', }), ) @@ -291,7 +295,7 @@ describe('update email', () => { fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.put).toBeCalledWith(`/admin/users/${fixture.uid}/email`, { + expect(fetch.put).toBeCalledWith(urls.admin.users.email(fixture.uid), { email: 'd@e.f', }), ) @@ -339,7 +343,7 @@ describe('update nickname', () => { fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.put).toBeCalledWith(`/admin/users/${fixture.uid}/nickname`, { + expect(fetch.put).toBeCalledWith(urls.admin.users.nickname(fixture.uid), { nickname: 'kumiko', }), ) @@ -363,7 +367,7 @@ describe('update nickname', () => { fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.put).toBeCalledWith(`/admin/users/${fixture.uid}/nickname`, { + expect(fetch.put).toBeCalledWith(urls.admin.users.nickname(fixture.uid), { nickname: 'kumiko', }), ) @@ -403,7 +407,7 @@ describe('update score', () => { fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.put).toBeCalledWith(`/admin/users/${fixture.uid}/score`, { + expect(fetch.put).toBeCalledWith(urls.admin.users.score(fixture.uid), { score: 999, }), ) @@ -427,7 +431,7 @@ describe('update score', () => { fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.put).toBeCalledWith(`/admin/users/${fixture.uid}/score`, { + expect(fetch.put).toBeCalledWith(urls.admin.users.score(fixture.uid), { score: 999, }), ) @@ -466,7 +470,7 @@ describe('update permission', () => { await waitFor(() => expect(fetch.put).toBeCalledWith( - `/admin/users/${fixture.uid}/permission`, + urls.admin.users.permission(fixture.uid), { permission: UserPermission.Banned, }, @@ -491,7 +495,7 @@ describe('update permission', () => { await waitFor(() => expect(fetch.put).toBeCalledWith( - `/admin/users/${fixture.uid}/permission`, + urls.admin.users.permission(fixture.uid), { permission: UserPermission.Banned, }, @@ -523,7 +527,7 @@ describe('update permission', () => { await waitFor(() => expect(fetch.put).toBeCalledWith( - `/admin/users/${fixture.uid}/permission`, + urls.admin.users.permission(fixture.uid), { permission: UserPermission.Admin, }, @@ -550,7 +554,7 @@ describe('toggle verification', () => { await waitFor(() => expect(fetch.put).toBeCalledWith( - `/admin/users/${fixture.uid}/verification`, + urls.admin.users.verification(fixture.uid), ), ) expect(queryByText('ok')).toBeInTheDocument() @@ -568,7 +572,7 @@ describe('toggle verification', () => { await waitFor(() => expect(fetch.put).toBeCalledWith( - `/admin/users/${fixture.uid}/verification`, + urls.admin.users.verification(fixture.uid), ), ) expect(queryByText('failed')).toBeInTheDocument() @@ -610,7 +614,7 @@ describe('update password', () => { fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.put).toBeCalledWith(`/admin/users/${fixture.uid}/password`, { + expect(fetch.put).toBeCalledWith(urls.admin.users.password(fixture.uid), { password: '123', }), ) @@ -636,7 +640,7 @@ describe('update password', () => { fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.put).toBeCalledWith(`/admin/users/${fixture.uid}/password`, { + expect(fetch.put).toBeCalledWith(urls.admin.users.password(fixture.uid), { password: '123', }), ) @@ -670,7 +674,7 @@ describe('delete user', () => { fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.del).toBeCalledWith(`/admin/users/${fixture.uid}`), + expect(fetch.del).toBeCalledWith(urls.admin.users.delete(fixture.uid)), ) expect(queryByText('ok')).toBeInTheDocument() expect(queryByRole('status')).toHaveClass('alert-success') @@ -687,7 +691,7 @@ describe('delete user', () => { fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.del).toBeCalledWith(`/admin/users/${fixture.uid}`), + expect(fetch.del).toBeCalledWith(urls.admin.users.delete(fixture.uid)), ) expect(queryByText('failed')).toBeInTheDocument() expect(queryByRole('alert')).toHaveClass('alert-danger') @@ -764,11 +768,7 @@ describe('table mode', () => { fireEvent.click(getByTitle('Table Mode')) fireEvent.click(getByTitle(t('admin.toggleVerification'))) - await waitFor(() => - expect(fetch.put).toBeCalledWith( - `/admin/users/${fixture.uid}/verification`, - ), - ) + await waitFor(() => expect(fetch.put).toBeCalled()) expect(queryByText('ok')).toBeInTheDocument() expect(queryByRole('status')).toHaveClass('alert-success') expect(queryByText(t('admin.unverified'))).toBeInTheDocument() diff --git a/resources/assets/tests/views/auth/Forgot.test.tsx b/resources/assets/tests/views/auth/Forgot.test.tsx index 1bf9a3d3..3a477300 100644 --- a/resources/assets/tests/views/auth/Forgot.test.tsx +++ b/resources/assets/tests/views/auth/Forgot.test.tsx @@ -2,6 +2,7 @@ import React from 'react' import { render, waitFor, fireEvent } from '@testing-library/react' import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' +import urls from '@/scripts/urls' import Forgot from '@/views/auth/Forgot' jest.mock('@/scripts/net') @@ -20,7 +21,7 @@ describe('submit', () => { }) fireEvent.click(getByText(t('auth.forgot.button'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/auth/forgot', { + expect(fetch.post).toBeCalledWith(urls.auth.forgot(), { email: 'a@b.c', captcha: 'abc', }), @@ -41,7 +42,7 @@ describe('submit', () => { }) fireEvent.click(getByText(t('auth.forgot.button'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/auth/forgot', { + expect(fetch.post).toBeCalledWith(urls.auth.forgot(), { email: 'a@b.c', captcha: 'abc', }), diff --git a/resources/assets/tests/views/auth/Login.test.tsx b/resources/assets/tests/views/auth/Login.test.tsx index b3e9bf3f..61004d9a 100644 --- a/resources/assets/tests/views/auth/Login.test.tsx +++ b/resources/assets/tests/views/auth/Login.test.tsx @@ -2,6 +2,7 @@ import React from 'react' import { render, waitFor, fireEvent } from '@testing-library/react' import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' +import urls from '@/scripts/urls' import Login from '@/views/auth/Login' jest.mock('@/scripts/net') @@ -33,7 +34,7 @@ describe('submit form', () => { }) fireEvent.click(getByText(t('auth.login'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/auth/login', { + expect(fetch.post).toBeCalledWith(urls.auth.login(), { identification: 'a@b.c', password: 'password', keep: false, @@ -60,7 +61,7 @@ describe('submit form', () => { fireEvent.click(getByLabelText(t('auth.keep'))) fireEvent.click(getByText(t('auth.login'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/auth/login', { + expect(fetch.post).toBeCalledWith(urls.auth.login(), { identification: 'a@b.c', password: 'password', keep: true, @@ -84,7 +85,7 @@ describe('submit form', () => { }) fireEvent.click(getByText(t('auth.login'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/auth/login', { + expect(fetch.post).toBeCalledWith(urls.auth.login(), { identification: 'a@b.c', password: 'password', keep: false, @@ -114,7 +115,7 @@ describe('submit form', () => { }) fireEvent.click(getByText(t('auth.login'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/auth/login', { + expect(fetch.post).toBeCalledWith(urls.auth.login(), { identification: 'a@b.c', password: 'password', keep: false, @@ -130,7 +131,7 @@ describe('submit form', () => { }) fireEvent.click(getByText(t('auth.login'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/auth/login', { + expect(fetch.post).toBeCalledWith(urls.auth.login(), { identification: 'a@b.c', password: 'password', keep: false, @@ -156,7 +157,7 @@ describe('submit form', () => { }) fireEvent.click(getByText(t('auth.login'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/auth/login', { + expect(fetch.post).toBeCalledWith(urls.auth.login(), { identification: 'a@b.c', password: 'password', keep: false, @@ -183,7 +184,7 @@ describe('submit form', () => { }) fireEvent.click(getByText(t('auth.login'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/auth/login', { + expect(fetch.post).toBeCalledWith(urls.auth.login(), { identification: 'a@b.c', password: 'password', keep: false, diff --git a/resources/assets/tests/views/auth/Registration.test.tsx b/resources/assets/tests/views/auth/Registration.test.tsx index c6b5c53d..15d130d4 100644 --- a/resources/assets/tests/views/auth/Registration.test.tsx +++ b/resources/assets/tests/views/auth/Registration.test.tsx @@ -2,6 +2,7 @@ import React from 'react' import { render, waitFor, fireEvent } from '@testing-library/react' import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' +import urls from '@/scripts/urls' import Registration from '@/views/auth/Registration' jest.mock('@/scripts/net') @@ -59,7 +60,7 @@ test('succeeded', async () => { }) fireEvent.click(getByText(t('auth.register-button'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/auth/register', { + expect(fetch.post).toBeCalledWith(urls.auth.register(), { email: 'a@b.c', password: 'password', nickname: 't', @@ -94,7 +95,7 @@ test('failed', async () => { }) fireEvent.click(getByText(t('auth.register-button'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/auth/register', { + expect(fetch.post).toBeCalledWith(urls.auth.register(), { email: 'a@b.c', password: 'password', nickname: 't', @@ -128,7 +129,7 @@ test('register with new player', async () => { }) fireEvent.click(getByText(t('auth.register-button'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/auth/register', { + expect(fetch.post).toBeCalledWith(urls.auth.register(), { email: 'a@b.c', password: 'password', player_name: 'player', diff --git a/resources/assets/tests/views/skinlib/Show.test.tsx b/resources/assets/tests/views/skinlib/Show.test.tsx index 3aed7ff1..5cebb717 100644 --- a/resources/assets/tests/views/skinlib/Show.test.tsx +++ b/resources/assets/tests/views/skinlib/Show.test.tsx @@ -3,6 +3,7 @@ import { render, fireEvent, waitFor } from '@testing-library/react' import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { Texture, TextureType } from '@/scripts/types' +import urls from '@/scripts/urls' import Show, { Badge } from '@/views/skinlib/Show' jest.mock('@/scripts/net') @@ -376,7 +377,7 @@ describe('add to closet', () => { }) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/user/closet', { + expect(fetch.post).toBeCalledWith(urls.user.closet.add(), { tid: fixtureSkin.tid, name: 't', }), @@ -400,7 +401,7 @@ describe('add to closet', () => { }) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/user/closet', { + expect(fetch.post).toBeCalledWith(urls.user.closet.add(), { tid: fixtureSkin.tid, name: 't', }), diff --git a/resources/assets/tests/views/skinlib/SkinLibrary.test.tsx b/resources/assets/tests/views/skinlib/SkinLibrary.test.tsx index 07447fa1..017259ab 100644 --- a/resources/assets/tests/views/skinlib/SkinLibrary.test.tsx +++ b/resources/assets/tests/views/skinlib/SkinLibrary.test.tsx @@ -4,6 +4,7 @@ import { createPaginator } from '../../utils' import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { TextureType } from '@/scripts/types' +import urls from '@/scripts/urls' import SkinLibrary from '@/views/skinlib/SkinLibrary' import { LibraryItem } from '@/views/skinlib/SkinLibrary/types' @@ -30,7 +31,7 @@ test('without authenticated', async () => { await waitFor(() => expect(fetch.get).toBeCalled()) expect(fetch.get).toBeCalledWith( - '/skinlib/list', + urls.skinlib.list(), expect.toSatisfy((search: URLSearchParams) => { expect(search.get('filter')).toBe('skin') expect(search.get('sort')).toBe('time') @@ -54,7 +55,7 @@ test('search by keyword', async () => { fireEvent.click(getByText(t('general.submit'))) await waitFor(() => expect(fetch.get).toHaveBeenLastCalledWith( - '/skinlib/list', + urls.skinlib.list(), expect.toSatisfy((search: URLSearchParams) => { expect(search.get('keyword')).toBe('k') return true @@ -73,7 +74,7 @@ test('select uploaded by self', async () => { fireEvent.click(getByText(t('skinlib.seeMyUpload'))) await waitFor(() => expect(fetch.get).toHaveBeenLastCalledWith( - '/skinlib/list', + urls.skinlib.list(), expect.toSatisfy((search: URLSearchParams) => { expect(search.get('uploader')).toBe('1') return true @@ -106,7 +107,7 @@ test('reset query', async () => { fireEvent.click(getByText(t('skinlib.reset'))) await waitFor(() => expect(fetch.get).toHaveBeenLastCalledWith( - '/skinlib/list', + urls.skinlib.list(), expect.toSatisfy((search: URLSearchParams) => { expect(search.get('filter')).toBe('skin') expect(search.get('keyword')).toBeNull() @@ -136,7 +137,7 @@ test('browser goes back', async () => { window.dispatchEvent(event) await waitFor(() => expect(fetch.get).toHaveBeenLastCalledWith( - '/skinlib/list', + urls.skinlib.list(), expect.toSatisfy((search: URLSearchParams) => { expect(search.get('filter')).toBe('skin') return true @@ -155,7 +156,7 @@ test('pagination', async () => { fireEvent.click(getByText('2')) expect(fetch.get).toHaveBeenLastCalledWith( - '/skinlib/list', + urls.skinlib.list(), expect.toSatisfy((search: URLSearchParams) => { expect(search.get('page')).toBe('2') return true @@ -182,7 +183,7 @@ test('library item', async () => { fireEvent.click(getByText(fixtureItem.nickname)) await waitFor(() => expect(fetch.get).toHaveBeenLastCalledWith( - '/skinlib/list', + urls.skinlib.list(), expect.toSatisfy((search: URLSearchParams) => { expect(search.get('uploader')).toBe(fixtureItem.uploader.toString()) return true @@ -217,7 +218,7 @@ describe('by filter', () => { fireEvent.click(getByText(t('general.skin'))) await waitFor(() => expect(fetch.get).toHaveBeenLastCalledWith( - '/skinlib/list', + urls.skinlib.list(), expect.toSatisfy((search: URLSearchParams) => { expect(search.get('filter')).toBe('skin') return true @@ -236,7 +237,7 @@ describe('by filter', () => { fireEvent.click(getByText('Steve')) await waitFor(() => expect(fetch.get).toHaveBeenLastCalledWith( - '/skinlib/list', + urls.skinlib.list(), expect.toSatisfy((search: URLSearchParams) => { expect(search.get('filter')).toBe('steve') return true @@ -255,7 +256,7 @@ describe('by filter', () => { fireEvent.click(getByText('Alex')) await waitFor(() => expect(fetch.get).toHaveBeenLastCalledWith( - '/skinlib/list', + urls.skinlib.list(), expect.toSatisfy((search: URLSearchParams) => { expect(search.get('filter')).toBe('alex') return true @@ -274,7 +275,7 @@ describe('by filter', () => { fireEvent.click(getByText(t('general.cape'))) await waitFor(() => expect(fetch.get).toHaveBeenLastCalledWith( - '/skinlib/list', + urls.skinlib.list(), expect.toSatisfy((search: URLSearchParams) => { expect(search.get('filter')).toBe('cape') return true @@ -301,7 +302,7 @@ describe('sorting', () => { fireEvent.click(getByText(t('skinlib.sort.time'))) await waitFor(() => expect(fetch.get).toHaveBeenLastCalledWith( - '/skinlib/list', + urls.skinlib.list(), expect.toSatisfy((search: URLSearchParams) => { expect(search.get('sort')).toBe('time') return true @@ -319,7 +320,7 @@ describe('sorting', () => { fireEvent.click(getByText(t('skinlib.sort.likes'))) await waitFor(() => expect(fetch.get).toHaveBeenLastCalledWith( - '/skinlib/list', + urls.skinlib.list(), expect.toSatisfy((search: URLSearchParams) => { expect(search.get('sort')).toBe('likes') return true @@ -334,7 +335,7 @@ describe('sorting', () => { describe('add to closet', () => { beforeEach(() => { fetch.get.mockImplementation((url: string) => { - if (url === '/skinlib/list') { + if (url === urls.skinlib.list()) { return Promise.resolve(createPaginator([fixtureItem])) } else { return Promise.resolve([]) @@ -382,7 +383,7 @@ describe('remove from closet', () => { beforeEach(() => { window.blessing.extra.currentUid = 1 fetch.get.mockImplementation((url: string) => { - if (url === '/skinlib/list') { + if (url === urls.skinlib.list()) { return Promise.resolve(createPaginator([fixtureItem])) } else { return Promise.resolve([fixtureItem.tid]) diff --git a/resources/assets/tests/views/skinlib/Upload.test.tsx b/resources/assets/tests/views/skinlib/Upload.test.tsx index 05288b6d..7d0b608f 100644 --- a/resources/assets/tests/views/skinlib/Upload.test.tsx +++ b/resources/assets/tests/views/skinlib/Upload.test.tsx @@ -3,6 +3,7 @@ import { render, fireEvent, waitFor } from '@testing-library/react' import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { isAlex } from '@/scripts/textureUtils' +import urls from '@/scripts/urls' import Upload from '@/views/skinlib/Upload' jest.mock('@/scripts/net') @@ -188,7 +189,7 @@ describe('upload texture', () => { expect(getByRole('alert')).toHaveClass('alert-danger') }) - it('no name', () => { + it('invalid file type', () => { const { getByText, getByTitle, getByRole, queryByText } = render() const file = new File([], 't.png', { type: 'image/jpeg' }) @@ -213,7 +214,10 @@ describe('upload texture', () => { fireEvent.click(getByText(t('skinlib.upload.button'))) expect(queryByText(t('skinlib.uploading'))).toBeInTheDocument() - expect(fetch.post).toBeCalledWith('/skinlib/upload', expect.any(FormData)) + expect(fetch.post).toBeCalledWith( + urls.skinlib.upload(), + expect.any(FormData), + ) const formData: FormData = fetch.post.mock.calls[0][1] expect(formData.get('name')).toBe('t') diff --git a/resources/assets/tests/views/user/Closet.test.tsx b/resources/assets/tests/views/user/Closet.test.tsx index 96036e6f..73491d69 100644 --- a/resources/assets/tests/views/user/Closet.test.tsx +++ b/resources/assets/tests/views/user/Closet.test.tsx @@ -5,6 +5,7 @@ import $ from 'jquery' import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { ClosetItem, Player, TextureType } from '@/scripts/types' +import urls from '@/scripts/urls' import Closet from '@/views/user/Closet' jest.mock('@/scripts/net') @@ -142,9 +143,10 @@ describe('rename item', () => { }) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.put).toBeCalledWith(`/user/closet/${fixtureSkin.tid}`, { - name: 'my skin', - }), + expect(fetch.put).toBeCalledWith( + urls.user.closet.rename(fixtureSkin.tid), + { name: 'my skin' }, + ), ) expect(queryByText('my skin')).toBeInTheDocument() expect(queryByText('success')).toBeInTheDocument() @@ -180,9 +182,10 @@ describe('rename item', () => { }) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.put).toBeCalledWith(`/user/closet/${fixtureSkin.tid}`, { - name: 'my skin', - }), + expect(fetch.put).toBeCalledWith( + urls.user.closet.rename(fixtureSkin.tid), + { name: 'my skin' }, + ), ) expect(queryByText(fixtureSkin.pivot.item_name)).toBeInTheDocument() expect(queryByText('failed')).toBeInTheDocument() @@ -204,7 +207,9 @@ describe('remove item', () => { fireEvent.click(getByText(t('user.removeItem'))) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.del).toBeCalledWith(`/user/closet/${fixtureSkin.tid}`), + expect(fetch.del).toBeCalledWith( + urls.user.closet.remove(fixtureSkin.tid), + ), ) expect(queryByText(/skin library/i)).toBeInTheDocument() expect(queryByText('success')).toBeInTheDocument() @@ -220,7 +225,9 @@ describe('remove item', () => { fireEvent.click(getByText(t('user.removeItem'))) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.del).toBeCalledWith(`/user/closet/${fixtureSkin.tid}`), + expect(fetch.del).toBeCalledWith( + urls.user.closet.remove(fixtureSkin.tid), + ), ) expect(queryByText(fixtureSkin.pivot.item_name)).toBeInTheDocument() expect(queryByText('failed')).toBeInTheDocument() @@ -306,7 +313,7 @@ describe('set avatar', () => { fireEvent.click(getByText(t('user.setAsAvatar'))) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/user/profile/avatar', { + expect(fetch.post).toBeCalledWith(urls.user.profile.avatar(), { tid: fixtureSkin.tid, }), ) @@ -324,7 +331,7 @@ describe('set avatar', () => { fireEvent.click(getByText(t('user.setAsAvatar'))) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/user/profile/avatar', { + expect(fetch.post).toBeCalledWith(urls.user.profile.avatar(), { tid: fixtureSkin.tid, }), ) @@ -401,10 +408,8 @@ describe('apply textures to player', () => { fireEvent.click(getByTitle(fixturePlayer.name)) await waitFor(() => expect(fetch.post).toBeCalledWith( - `/user/player/set/${fixturePlayer.pid}`, - { - skin: fixtureSkin.tid, - }, + urls.user.player.set(fixturePlayer.pid), + { skin: fixtureSkin.tid }, ), ) expect(queryByText('success')).toBeInTheDocument() @@ -432,10 +437,8 @@ describe('apply textures to player', () => { fireEvent.click(getByTitle(fixturePlayer.name)) await waitFor(() => expect(fetch.post).toBeCalledWith( - `/user/player/set/${fixturePlayer.pid}`, - { - skin: fixtureSkin.tid, - }, + urls.user.player.set(fixturePlayer.pid), + { skin: fixtureSkin.tid }, ), ) expect(queryByText('failed')).toBeInTheDocument() diff --git a/resources/assets/tests/views/user/Dashboard.test.tsx b/resources/assets/tests/views/user/Dashboard.test.tsx index 21846c23..5bc81457 100644 --- a/resources/assets/tests/views/user/Dashboard.test.tsx +++ b/resources/assets/tests/views/user/Dashboard.test.tsx @@ -2,6 +2,7 @@ import React from 'react' import { render, fireEvent, waitFor } from '@testing-library/react' import * as fetch from '@/scripts/net' import { t } from '@/scripts/i18n' +import urls from '@/scripts/urls' import Dashboard from '@/views/user/Dashboard' jest.mock('@/scripts/net') @@ -78,7 +79,7 @@ describe('sign', () => { const button = getByRole('button') fireEvent.click(button) - await waitFor(() => expect(fetch.post).toBeCalledWith('/user/sign')) + await waitFor(() => expect(fetch.post).toBeCalledWith(urls.user.sign())) expect(getByText('ok')).toBeInTheDocument() expect(getByRole('status')).toHaveClass('alert-success') expect(button).toBeDisabled() @@ -92,7 +93,7 @@ describe('sign', () => { await waitFor(() => expect(fetch.get).toBeCalledTimes(1)) fireEvent.click(getByRole('button')) - await waitFor(() => expect(fetch.post).toBeCalledWith('/user/sign')) + await waitFor(() => expect(fetch.post).toBeCalledWith(urls.user.sign())) expect(getByText('f')).toBeInTheDocument() expect(getByRole('alert')).toHaveClass('alert-warning') }) diff --git a/resources/assets/tests/views/user/Players.test.tsx b/resources/assets/tests/views/user/Players.test.tsx index da5f23f8..dd9f7a04 100644 --- a/resources/assets/tests/views/user/Players.test.tsx +++ b/resources/assets/tests/views/user/Players.test.tsx @@ -3,6 +3,7 @@ import { render, fireEvent, waitFor } from '@testing-library/react' import { t } from '@/scripts/i18n' import * as fetch from '@/scripts/net' import { Player, TextureType } from '@/scripts/types' +import urls from '@/scripts/urls' import Players from '@/views/user/Players' jest.mock('@/scripts/net') @@ -199,7 +200,7 @@ describe('create player', () => { }) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/user/player/add', { + expect(fetch.post).toBeCalledWith(urls.user.player.add(), { name: fixture.name, }), ) @@ -223,7 +224,7 @@ describe('create player', () => { }) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.post).toBeCalledWith('/user/player/add', { + expect(fetch.post).toBeCalledWith(urls.user.player.add(), { name: fixture.name, }), ) @@ -286,7 +287,7 @@ describe('edit player name', () => { }) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.post).toBeCalledWith(`/user/player/rename/${fixture.pid}`, { + expect(fetch.post).toBeCalledWith(urls.user.player.rename(fixture.pid), { name: 'reina', }), ) @@ -330,7 +331,7 @@ describe('edit player name', () => { }) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.post).toBeCalledWith(`/user/player/rename/${fixture.pid}`, { + expect(fetch.post).toBeCalledWith(urls.user.player.rename(fixture.pid), { name: 'reina', }), ) @@ -376,12 +377,9 @@ describe('reset texture', () => { fireEvent.click(getByLabelText(t('general.cape'))) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.post).toBeCalledWith( - `/user/player/texture/clear/${fixture.pid}`, - { - type: ['skin', 'cape'], - }, - ), + expect(fetch.post).toBeCalledWith(urls.user.player.clear(fixture.pid), { + type: ['skin', 'cape'], + }), ) expect(queryByText('success')).toBeInTheDocument() expect(getByRole('status')).toHaveClass('alert-success') @@ -399,12 +397,9 @@ describe('reset texture', () => { fireEvent.click(getByLabelText(t('general.skin'))) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.post).toBeCalledWith( - `/user/player/texture/clear/${fixture.pid}`, - { - type: ['skin'], - }, - ), + expect(fetch.post).toBeCalledWith(urls.user.player.clear(fixture.pid), { + type: ['skin'], + }), ) expect(queryByText('success')).toBeInTheDocument() expect(getByRole('status')).toHaveClass('alert-success') @@ -422,12 +417,9 @@ describe('reset texture', () => { fireEvent.click(getByLabelText(t('general.cape'))) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.post).toBeCalledWith( - `/user/player/texture/clear/${fixture.pid}`, - { - type: ['cape'], - }, - ), + expect(fetch.post).toBeCalledWith(urls.user.player.clear(fixture.pid), { + type: ['cape'], + }), ) expect(queryByText('success')).toBeInTheDocument() expect(getByRole('status')).toHaveClass('alert-success') @@ -456,12 +448,9 @@ describe('reset texture', () => { fireEvent.click(getByLabelText(t('general.skin'))) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.post).toBeCalledWith( - `/user/player/texture/clear/${fixture.pid}`, - { - type: ['skin'], - }, - ), + expect(fetch.post).toBeCalledWith(urls.user.player.clear(fixture.pid), { + type: ['skin'], + }), ) expect(queryByText('failed')).toBeInTheDocument() expect(getByRole('alert')).toHaveClass('alert-danger') @@ -495,7 +484,7 @@ describe('delete player', () => { fireEvent.click(getByText(t('user.player.delete-player'))) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.post).toBeCalledWith(`/user/player/delete/${fixture.pid}`), + expect(fetch.post).toBeCalledWith(urls.user.player.delete(fixture.pid)), ) expect(getByText('success')).toBeInTheDocument() expect(getByRole('status')).toHaveClass('alert-success') @@ -511,7 +500,7 @@ describe('delete player', () => { fireEvent.click(getByText(t('user.player.delete-player'))) fireEvent.click(getByText(t('general.confirm'))) await waitFor(() => - expect(fetch.post).toBeCalledWith(`/user/player/delete/${fixture.pid}`), + expect(fetch.post).toBeCalledWith(urls.user.player.delete(fixture.pid)), ) expect(getByText('failed')).toBeInTheDocument() expect(getByRole('alert')).toHaveClass('alert-danger') diff --git a/routes/web.php b/routes/web.php index f2ee72e9..39c340e7 100644 --- a/routes/web.php +++ b/routes/web.php @@ -57,10 +57,10 @@ Route::prefix('user') Route::get('reports', 'ReportController@track'); - Route::prefix('profile')->group(function () { - Route::get('', 'UserController@profile')->name('profile'); - Route::post('', 'UserController@handleProfile')->name('profile'); - Route::post('avatar', 'UserController@setAvatar')->name('profile.avatar'); + Route::prefix('profile')->name('profile.')->group(function () { + Route::get('', 'UserController@profile'); + Route::post('', 'UserController@handleProfile'); + Route::post('avatar', 'UserController@setAvatar')->name('avatar'); }); Route::post('email-verification', 'UserController@sendVerificationEmail'); diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 1ee9a983..e88bf785 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -11,6 +11,7 @@ if (Test-Path ./public/app) { } # Run webpack +yarn build:urls yarn build Move-Item -Path ./public/app/sw.js -Destination ./public -Force diff --git a/scripts/generateUrls.ts b/scripts/generateUrls.ts new file mode 100644 index 00000000..9e64537f --- /dev/null +++ b/scripts/generateUrls.ts @@ -0,0 +1,145 @@ +import { spawnSync } from 'child_process' +import * as fs from 'fs' +import ts from 'typescript' +import prettier from 'prettier' + +type Route = { uri: string; name: string | null } +const supportedPrefixes = ['auth.', 'user.', 'skinlib.', 'admin.'] + +type TreeObject = { [key: string]: Tree } +type Tree = TreeObject | string +const tree: TreeObject = {} + +function parseURI(uri: string): ts.ArrowFunction { + const matches = /\{([a-z]+)\}/.exec(uri) + if (matches) { + const param = matches[1] + const type = + param.endsWith('id') || + param === 'texture' || + param === 'user' || + param === 'player' || + param === 'report' + ? ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword) + : ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) + + return ts.createArrowFunction( + undefined, + undefined, + [ + ts.createParameter( + undefined, + undefined, + undefined, + ts.createIdentifier(param), + undefined, + type, + undefined, + ), + ], + undefined, + ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + ts.createTemplateExpression( + ts.createTemplateHead( + '/' + uri.slice(0, matches.index), + '/' + uri.slice(0, matches.index), + ), + [ + ts.createTemplateSpan( + ts.createIdentifier(param), + ts.createTemplateTail( + uri.slice(matches.index + matches[0].length), + uri.slice(matches.index + matches[0].length), + ), + ), + ], + ), + ) + } + + return ts.createArrowFunction( + undefined, + undefined, + [], + undefined, + ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + ts.createAsExpression( + ts.createStringLiteral(`/${uri}`), + ts.createTypeReferenceNode(ts.createIdentifier('const'), undefined), + ), + ) +} + +function parseTree(tree: Tree): ts.ObjectLiteralExpression { + const properties = Object.entries(tree).map(([key, value]) => { + if (typeof value === 'string') { + return ts.createPropertyAssignment( + ts.createIdentifier(key), + parseURI(value), + ) + } else { + return ts.createPropertyAssignment( + ts.createIdentifier(key), + parseTree(value), + ) + } + }) + + return ts.createObjectLiteral(properties) +} + +const routes: Route[] = JSON.parse( + spawnSync('php', ['artisan', 'route:list', '--json', '--columns=uri,name']) + .stdout, +) +routes + .filter( + (route) => + route.name && + supportedPrefixes.some((prefix) => route.name!.startsWith(prefix)) && + !route.name.endsWith('.'), + ) + .forEach((route) => { + const path = route.name!.split('.') + const length = path.length + + path.reduce((object: TreeObject, p, index) => { + if (index === length - 1) { + object[p] = route.uri + return tree as TreeObject + } + + if (!object[p]) { + object[p] = {} + } + return object[p] as TreeObject + }, tree) + }) + +const ast = ts.createExportAssignment( + undefined, + undefined, + undefined, + parseTree(tree), +) +const sourceFile = ts.createSourceFile( + 'urls.ts', + '', + ts.ScriptTarget.ES2017, + false, + ts.ScriptKind.TS, +) +const printer = ts.createPrinter({ + newLine: ts.NewLineKind.LineFeed, +}) +const code = prettier.format( + printer.printNode(ts.EmitHint.Unspecified, ast, sourceFile), + { + parser: 'typescript', + semi: false, + singleQuote: true, + }, +) +fs.writeFileSync('./resources/assets/src/scripts/urls.ts', code, { + encoding: 'utf-8', +}) diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 00000000..16d8a1e8 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noUnusedLocals": false, + "noUnusedParameters": false, + "module": "commonjs" + } +} diff --git a/yarn.lock b/yarn.lock index c3ca16ab..62d8f481 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1587,6 +1587,11 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -3082,6 +3087,11 @@ diff-sequences@^26.0.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.0.0.tgz#0760059a5c287637b842bd7085311db7060e88a6" integrity sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg== +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -5721,6 +5731,11 @@ make-error@1.x: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + makeerror@1.0.x: version "1.0.11" resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" @@ -8135,6 +8150,14 @@ source-map-resolve@^0.5.2: source-map-url "^0.4.0" urix "^0.1.0" +source-map-support@^0.5.17: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@^0.5.6: version "0.5.12" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" @@ -8769,6 +8792,17 @@ ts-loader@^7.0.4: micromatch "^4.0.0" semver "^6.0.0" +ts-node@^8.10.2: + version "8.10.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" + integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA== + dependencies: + arg "^4.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + tslib@^1.8.1, tslib@^1.9.0: version "1.10.0" resolved "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" @@ -9529,6 +9563,11 @@ yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.1" +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + zrender@4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/zrender/-/zrender-4.3.0.tgz#9f056121b20bbae44414d287bf6a119ff7042661"