upgrade Jest and TypeScript

This commit is contained in:
Pig Fang 2021-06-05 17:10:41 +08:00
parent ed82469f88
commit 7381699690
No known key found for this signature in database
GPG Key ID: A8198F548DADA9E2
57 changed files with 1063 additions and 1127 deletions

View File

@ -23,3 +23,4 @@ rules:
- off - off
- checksVoidReturn: false - checksVoidReturn: false
'@typescript-eslint/unbound-method': off '@typescript-eslint/unbound-method': off
'@typescript-eslint/restrict-template-expressions': off

View File

@ -51,13 +51,13 @@
"xterm-addon-fit": "^0.4.0" "xterm-addon-fit": "^0.4.0"
}, },
"devDependencies": { "devDependencies": {
"@gplane/tsconfig": "^1.1.0", "@gplane/tsconfig": "^4.2.0",
"@testing-library/jest-dom": "^5.11.10", "@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^11.2.6", "@testing-library/react": "^11.2.6",
"@types/bootstrap": "^4.3.3", "@types/bootstrap": "^4.3.3",
"@types/css-minimizer-webpack-plugin": "^1.1.0", "@types/css-minimizer-webpack-plugin": "^1.1.0",
"@types/echarts": "^4.6.0", "@types/echarts": "^4.6.0",
"@types/jest": "^26.0.20", "@types/jest": "^26.0.23",
"@types/jquery": "^3.3.38", "@types/jquery": "^3.3.38",
"@types/js-yaml": "^3.12.4", "@types/js-yaml": "^3.12.4",
"@types/lodash.debounce": "^4.0.6", "@types/lodash.debounce": "^4.0.6",
@ -78,7 +78,7 @@
"eslint-formatter-beauty": "^3.0.0", "eslint-formatter-beauty": "^3.0.0",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"html-webpack-plugin": "^5.3.1", "html-webpack-plugin": "^5.3.1",
"jest": "^26.6.3", "jest": "^27.0.4",
"jest-extended": "^0.11.5", "jest-extended": "^0.11.5",
"jquery": "^3.5.1", "jquery": "^3.5.1",
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",
@ -87,10 +87,10 @@
"postcss-loader": "^5.3.0", "postcss-loader": "^5.3.0",
"prettier": "^2.0.5", "prettier": "^2.0.5",
"style-loader": "^2.0.0", "style-loader": "^2.0.0",
"ts-jest": "^26.5.1", "ts-jest": "^27.0.2",
"ts-loader": "^9.2.2", "ts-loader": "^9.2.2",
"ts-node": "^8.10.2", "ts-node": "^10.0.0",
"typescript": "^4.1.3", "typescript": "^4.3.2",
"url-loader": "^4.1.1", "url-loader": "^4.1.1",
"webpack": "^5.38.1", "webpack": "^5.38.1",
"webpack-cli": "^4.7.0", "webpack-cli": "^4.7.0",
@ -115,10 +115,7 @@
"jest": { "jest": {
"preset": "ts-jest", "preset": "ts-jest",
"resetMocks": true, "resetMocks": true,
"timers": "modern", "testEnvironment": "jsdom",
"transform": {
".*\\.tsx?$": "ts-jest"
},
"moduleFileExtensions": [ "moduleFileExtensions": [
"js", "js",
"ts", "ts",

View File

@ -1,7 +1,7 @@
import type { Stdio } from 'blessing-skin-shell' import type { Stdio } from 'blessing-skin-shell'
import cac from 'cac' import cac from 'cac'
import * as fetch from '../net' import * as fetch from '../net'
import { User, Texture } from '../types' import type { User, Texture } from '../types'
type Response = fetch.ResponseBody<{ user: User; texture: Texture }> type Response = fetch.ResponseBody<{ user: User; texture: Texture }>

View File

@ -33,7 +33,7 @@ export default async function rm(stdio: Stdio, args: string[]) {
stdio.println("Try 'rm --help' for more information.") stdio.println("Try 'rm --help' for more information.")
} }
if (opts.force && opts.recursive && path.startsWith('/')) { if (opts.force && opts.recursive && path?.startsWith('/')) {
await fetch.post('/admin/resource?clear-cache') await fetch.post('/admin/resource?clear-cache')
} }
} }

View File

@ -1,4 +1,4 @@
import { Stdio } from 'blessing-skin-shell' import type { Stdio } from 'blessing-skin-shell'
import * as event from '../event' import * as event from '../event'
/* istanbul ignore next */ /* istanbul ignore next */

View File

@ -10,3 +10,5 @@ if (process.env.NODE_ENV === 'development') {
const base = link?.href ?? blessing.base_url const base = link?.href ?? blessing.base_url
__webpack_public_path__ = `${base}/app/` __webpack_public_path__ = `${base}/app/`
} }
export {}

View File

@ -1,9 +1,9 @@
{ const blessingElement = document.querySelector('#blessing-globals')!
const blessingElement = document.querySelector('#blessing-globals')! // @ts-ignore
// @ts-ignore window.blessing = JSON.parse(blessingElement.textContent!)
window.blessing = JSON.parse(blessingElement.textContent!)
window.addEventListener('load', () => { window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js?v6') navigator.serviceWorker.register('/sw.js?v6')
}) })
}
export {}

View File

@ -58,7 +58,7 @@ export async function walkFetch(request: Request): Promise<any> {
} = body } = body
return { return {
code: 1, code: 1,
message: Object.keys(errors).map((field) => errors[field][0])[0], message: Object.keys(errors).map((field) => errors[field]![0])[0],
} }
} else if (response.status === 419) { } else if (response.status === 419) {
return showModal({ return showModal({

View File

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import { showModal } from '@/scripts/notify' import { showModal } from '@/scripts/notify'
import { Player } from '@/scripts/types' import type { Player } from '@/scripts/types'
import { Box } from './styles' import { Box } from './styles'
interface Props { interface Props {

View File

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import { Player } from '@/scripts/types' import type { Player } from '@/scripts/types'
import ButtonEdit from '@/components/ButtonEdit' import ButtonEdit from '@/components/ButtonEdit'
interface Props { interface Props {

View File

@ -4,7 +4,7 @@ import { useImmer } from 'use-immer'
import useIsLargeScreen from '@/scripts/hooks/useIsLargeScreen' import useIsLargeScreen from '@/scripts/hooks/useIsLargeScreen'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import * as fetch from '@/scripts/net' import * as fetch from '@/scripts/net'
import { Player, Paginator } from '@/scripts/types' import type { Player, Paginator } from '@/scripts/types'
import { toast, showModal } from '@/scripts/notify' import { toast, showModal } from '@/scripts/notify'
import urls from '@/scripts/urls' import urls from '@/scripts/urls'
import Pagination from '@/components/Pagination' import Pagination from '@/components/Pagination'
@ -87,7 +87,7 @@ const PlayersManagement: React.FC = () => {
if (code === 0) { if (code === 0) {
toast.success(message) toast.success(message)
setPlayers((players) => { setPlayers((players) => {
players[index].name = name players[index]!.name = name
}) })
} else { } else {
toast.error(message) toast.error(message)
@ -115,7 +115,7 @@ const PlayersManagement: React.FC = () => {
if (code === 0) { if (code === 0) {
toast.success(message) toast.success(message)
setPlayers((players) => { setPlayers((players) => {
players[index].uid = uid players[index]!.uid = uid
}) })
} else { } else {
toast.error(message) toast.error(message)
@ -126,15 +126,15 @@ const PlayersManagement: React.FC = () => {
const handleUpdateTexture = async (type: 'skin' | 'cape', tid: number) => { const handleUpdateTexture = async (type: 'skin' | 'cape', tid: number) => {
const { code, message } = await fetch.put<fetch.ResponseBody>( const { code, message } = await fetch.put<fetch.ResponseBody>(
urls.admin.players.texture(players[textureUpdating].pid), urls.admin.players.texture(players[textureUpdating]!.pid),
{ type, tid }, { type, tid },
) )
if (code === 0) { if (code === 0) {
toast.success(message) toast.success(message)
setPlayers((players) => { setPlayers((players) => {
const field = `tid_${type}` as 'tid_skin' | 'tid_cape' const field = `tid_${type}` as const
players[textureUpdating][field] = tid players[textureUpdating]![field] = tid
}) })
} else { } else {
toast.error(message) toast.error(message)

View File

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import styled from '@emotion/styled' import styled from '@emotion/styled'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import { Plugin } from './types' import type { Plugin } from './types'
const Box = styled.div` const Box = styled.div`
cursor: default; cursor: default;

View File

@ -7,7 +7,7 @@ import { toast, showModal } from '@/scripts/notify'
import FileInput from '@/components/FileInput' import FileInput from '@/components/FileInput'
import Loading from '@/components/Loading' import Loading from '@/components/Loading'
import InfoBox from './InfoBox' import InfoBox from './InfoBox'
import { Plugin } from './types' import type { Plugin } from './types'
const PluginsManagement: React.FC = () => { const PluginsManagement: React.FC = () => {
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)
@ -43,7 +43,7 @@ const PluginsManagement: React.FC = () => {
if (code === 0) { if (code === 0) {
toast.success(message) toast.success(message)
setPlugins((plugins) => { setPlugins((plugins) => {
plugins[i].enabled = true plugins[i]!.enabled = true
}) })
} else { } else {
showModal({ showModal({
@ -73,7 +73,7 @@ const PluginsManagement: React.FC = () => {
if (code === 0) { if (code === 0) {
toast.success(message) toast.success(message)
setPlugins((plugins) => { setPlugins((plugins) => {
plugins[i].enabled = false plugins[i]!.enabled = false
}) })
} else { } else {
toast.error(message) toast.error(message)
@ -108,7 +108,7 @@ const PluginsManagement: React.FC = () => {
} }
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setFile(event.target.files![0]) setFile(event.target.files![0]!)
} }
const handleUrlChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleUrlChange = (event: React.ChangeEvent<HTMLInputElement>) => {
@ -161,7 +161,7 @@ const PluginsManagement: React.FC = () => {
const chunks = Array(Math.ceil(plugins.length / 2)) const chunks = Array(Math.ceil(plugins.length / 2))
.fill(null) .fill(null)
.map((_, i) => plugins.slice(i * 2, (i + 1) * 2)) .map((_, i) => plugins.slice(i * 2, (i + 1) * 2) as [Plugin, Plugin?])
return ( return (
<div className="row"> <div className="row">
@ -173,7 +173,7 @@ const PluginsManagement: React.FC = () => {
) : ( ) : (
chunks.map((chunk, i) => ( chunks.map((chunk, i) => (
<div className="row" key={`${chunk[0].name}&${chunk[1]?.name}`}> <div className="row" key={`${chunk[0].name}&${chunk[1]?.name}`}>
{chunk.map((plugin, j) => ( {(chunk as Plugin[]).map((plugin, j) => (
<div className="col-md-6" key={plugin.name}> <div className="col-md-6" key={plugin.name}>
<InfoBox <InfoBox
plugin={plugin} plugin={plugin}

View File

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import { Plugin } from './types' import type { Plugin } from './types'
interface Props { interface Props {
plugin: Plugin plugin: Plugin

View File

@ -7,7 +7,7 @@ import * as fetch from '@/scripts/net'
import { toast, showModal } from '@/scripts/notify' import { toast, showModal } from '@/scripts/notify'
import Loading from '@/components/Loading' import Loading from '@/components/Loading'
import Pagination from '@/components/Pagination' import Pagination from '@/components/Pagination'
import { Plugin } from './types' import type { Plugin } from './types'
import Row from './Row' import Row from './Row'
enableMapSet() enableMapSet()
@ -64,8 +64,8 @@ const PluginsMarket: React.FC = () => {
if (code === 0) { if (code === 0) {
toast.success(message) toast.success(message)
setPlugins((plugins) => { setPlugins((plugins) => {
plugins[index].can_update = false plugins[index]!.can_update = false
plugins[index].installed = plugins[index].version plugins[index]!.installed = plugins[index]!.version
}) })
} else { } else {
showModal({ showModal({

View File

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import styled from '@emotion/styled' import styled from '@emotion/styled'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import { Texture } from '@/scripts/types' import type { Texture } from '@/scripts/types'
import { Report, Status } from './types' import { Report, Status } from './types'
const Card = styled.div` const Card = styled.div`

View File

@ -8,7 +8,7 @@ import { toast, showModal } from '@/scripts/notify'
import Loading from '@/components/Loading' import Loading from '@/components/Loading'
import Pagination from '@/components/Pagination' import Pagination from '@/components/Pagination'
import ViewerSkeleton from '@/components/ViewerSkeleton' import ViewerSkeleton from '@/components/ViewerSkeleton'
import { Report, Status } from './types' import type { Report, Status } from './types'
import ImageBox from './ImageBox' import ImageBox from './ImageBox'
const Previewer = React.lazy(() => import('@/components/Viewer')) const Previewer = React.lazy(() => import('@/components/Viewer'))
@ -62,7 +62,7 @@ const ReportsManagement: React.FC = () => {
if (resp.code === 0) { if (resp.code === 0) {
toast.success(resp.message) toast.success(resp.message)
setReports((reports) => { setReports((reports) => {
reports[index].status = resp.data.status reports[index]!.status = resp.data.status
}) })
} else { } else {
toast.error(resp.message) toast.error(resp.message)

View File

@ -1,4 +1,4 @@
import { Texture, User } from '@/scripts/types' import type { Texture, User } from '@/scripts/types'
export const enum Status { export const enum Status {
Pending = 0, Pending = 0,

View File

@ -1,7 +1,7 @@
import styled from '@emotion/styled' import styled from '@emotion/styled'
import React from 'react' import React from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import { Line } from './types' import type { Line } from './types'
const Group = styled.td` const Group = styled.td`
width: 15%; width: 15%;

View File

@ -49,7 +49,7 @@ const Translations: React.FC = () => {
if (code === 0) { if (code === 0) {
toast.success(message) toast.success(message)
setLines((lines) => { setLines((lines) => {
lines[index].text[blessing.locale] = text lines[index]!.text[blessing.locale] = text
}) })
} else { } else {
toast.error(message) toast.error(message)

View File

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import { User } from '@/scripts/types' import type { User } from '@/scripts/types'
import { Box, Icon, InfoTable } from './styles' import { Box, Icon, InfoTable } from './styles'
import { import {
humanizePermission, humanizePermission,

View File

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import { User } from '@/scripts/types' import type { User } from '@/scripts/types'
import ButtonEdit from '@/components/ButtonEdit' import ButtonEdit from '@/components/ButtonEdit'
import { import {
humanizePermission, humanizePermission,

View File

@ -92,7 +92,7 @@ const UsersManagement: React.FC = () => {
if (code === 0) { if (code === 0) {
toast.success(message) toast.success(message)
setUsers((users) => { setUsers((users) => {
users[index].email = email users[index]!.email = email
}) })
} else { } else {
toast.error(message) toast.error(message)
@ -124,7 +124,7 @@ const UsersManagement: React.FC = () => {
if (code === 0) { if (code === 0) {
toast.success(message) toast.success(message)
setUsers((users) => { setUsers((users) => {
users[index].nickname = nickname users[index]!.nickname = nickname
}) })
} else { } else {
toast.error(message) toast.error(message)
@ -152,7 +152,7 @@ const UsersManagement: React.FC = () => {
if (code === 0) { if (code === 0) {
toast.success(message) toast.success(message)
setUsers((users) => { setUsers((users) => {
users[index].score = score users[index]!.score = score
}) })
} else { } else {
toast.error(message) toast.error(message)
@ -189,7 +189,7 @@ const UsersManagement: React.FC = () => {
if (code === 0) { if (code === 0) {
toast.success(message) toast.success(message)
setUsers((users) => { setUsers((users) => {
users[index].permission = permission users[index]!.permission = permission
}) })
} else { } else {
toast.error(message) toast.error(message)
@ -203,7 +203,7 @@ const UsersManagement: React.FC = () => {
if (code === 0) { if (code === 0) {
toast.success(message) toast.success(message)
setUsers((users) => { setUsers((users) => {
users[index].verified = !users[index].verified users[index]!.verified = !users[index]!.verified
}) })
} else { } else {
toast.error(message) toast.error(message)

View File

@ -1,7 +1,7 @@
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import * as fetch from '@/scripts/net' import * as fetch from '@/scripts/net'
import { showModal, toast } from '@/scripts/notify' import { showModal, toast } from '@/scripts/notify'
import { Texture } from '@/scripts/types' import type { Texture } from '@/scripts/types'
import urls from '@/scripts/urls' import urls from '@/scripts/urls'
export default async function addClosetItem( export default async function addClosetItem(

View File

@ -2,7 +2,7 @@ import React from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import { TextureType } from '@/scripts/types' import { TextureType } from '@/scripts/types'
import Button from './Button' import Button from './Button'
import { Filter } from './types' import type { Filter } from './types'
import { humanizeType } from './utils' import { humanizeType } from './utils'
interface Props { interface Props {

View File

@ -2,7 +2,7 @@ import React from 'react'
import styled from '@emotion/styled' import styled from '@emotion/styled'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import * as cssUtils from '@/styles/utils' import * as cssUtils from '@/styles/utils'
import { LibraryItem } from './types' import type { LibraryItem } from './types'
import { humanizeType } from './utils' import { humanizeType } from './utils'
const Card = styled.div` const Card = styled.div`

View File

@ -14,7 +14,7 @@ import removeClosetItem from '@/views/user/Closet/removeClosetItem'
import FilterSelector from './FilterSelector' import FilterSelector from './FilterSelector'
import Button from './Button' import Button from './Button'
import Item from './Item' import Item from './Item'
import { Filter, LibraryItem } from './types' import type { Filter, LibraryItem } from './types'
const SkinLibrary: React.FC = () => { const SkinLibrary: React.FC = () => {
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)

View File

@ -1,4 +1,4 @@
import { TextureType } from '@/scripts/types' import type { TextureType } from '@/scripts/types'
export type Filter = 'skin' | TextureType export type Filter = 'skin' | TextureType

View File

@ -1,6 +1,6 @@
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import { TextureType } from '@/scripts/types' import { TextureType } from '@/scripts/types'
import { Filter } from './types' import type { Filter } from './types'
export function humanizeType(type: Filter): string { export function humanizeType(type: Filter): string {
switch (type) { switch (type) {

View File

@ -1,13 +1,13 @@
import React from 'react' import React from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import { ClosetItem } from '@/scripts/types' import type { ClosetItem as ClosetItemType } from '@/scripts/types'
import setAsAvatar from './setAsAvatar' import setAsAvatar from './setAsAvatar'
import { Card, DropdownButton } from './styles' import { Card, DropdownButton } from './styles'
interface Props { interface Props {
item: ClosetItem item: ClosetItemType
selected: boolean selected: boolean
onClick(item: ClosetItem): void onClick(item: ClosetItemType): void
onRename(): void onRename(): void
onRemove(): void onRemove(): void
} }

View File

@ -3,7 +3,7 @@ import $ from 'jquery'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import * as fetch from '@/scripts/net' import * as fetch from '@/scripts/net'
import { toast } from '@/scripts/notify' import { toast } from '@/scripts/notify'
import { Player } from '@/scripts/types' import type { Player } from '@/scripts/types'
import urls from '@/scripts/urls' import urls from '@/scripts/urls'
import Loading from '@/components/Loading' import Loading from '@/components/Loading'
import Modal from '@/components/Modal' import Modal from '@/components/Modal'

View File

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import ButtonEdit from '@/components/ButtonEdit' import ButtonEdit from '@/components/ButtonEdit'
import { App } from './types' import type { App } from './types'
interface Props { interface Props {
app: App app: App

View File

@ -6,7 +6,7 @@ import { showModal, toast } from '@/scripts/notify'
import Loading from '@/components/Loading' import Loading from '@/components/Loading'
import Row from './Row' import Row from './Row'
import ModalCreate from './ModalCreate' import ModalCreate from './ModalCreate'
import { App } from './types' import type { App } from './types'
type Exception = { type Exception = {
message: string message: string

View File

@ -2,7 +2,7 @@ import React, { useState } from 'react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import * as fetch from '@/scripts/net' import * as fetch from '@/scripts/net'
import { toast } from '@/scripts/notify' import { toast } from '@/scripts/notify'
import { Player } from '@/scripts/types' import type { Player } from '@/scripts/types'
import urls from '@/scripts/urls' import urls from '@/scripts/urls'
import Modal from '@/components/Modal' import Modal from '@/components/Modal'

View File

@ -1,7 +1,7 @@
/** @jsxImportSource @emotion/react */ /** @jsxImportSource @emotion/react */
import { css } from '@emotion/react' import { css } from '@emotion/react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import { Player } from '@/scripts/types' import type { Player } from '@/scripts/types'
import ButtonEdit from '@/components/ButtonEdit' import ButtonEdit from '@/components/ButtonEdit'
import * as cssUtils from '@/styles/utils' import * as cssUtils from '@/styles/utils'

View File

@ -39,7 +39,7 @@ const Players: React.FC = () => {
const players = await fetch.get<Player[]>('/user/player/list') const players = await fetch.get<Player[]>('/user/player/list')
setPlayers(players) setPlayers(players)
if (players.length === 1) { if (players.length === 1) {
selectPlayer(players[0]) selectPlayer(players[0]!)
} }
setIsLoading(false) setIsLoading(false)
} }

View File

@ -1,6 +1,6 @@
/* eslint-disable max-params */ /* eslint-disable max-params */
/* eslint-disable max-classes-per-file */ /* eslint-disable max-classes-per-file */
import { PlayerObject, SkinObject, CapeObject } from 'skinview3d' import type { PlayerObject, SkinObject, CapeObject } from 'skinview3d'
export class SkinViewer { export class SkinViewer {
disposed: boolean disposed: boolean

View File

@ -4,14 +4,28 @@ import { t } from '@/scripts/i18n'
import FileInput from '@/components/FileInput' import FileInput from '@/components/FileInput'
test('click to select file', () => { test('click to select file', () => {
const { getAllByText } = render(<FileInput file={null} onChange={() => {}} />) const { getAllByText } = render(
<FileInput
file={null}
onChange={() => {
/* */
}}
/>,
)
fireEvent.click(getAllByText(t('skinlib.upload.select-file'))[1]) fireEvent.click(getAllByText(t('skinlib.upload.select-file'))[1]!)
}) })
test('display file name', () => { test('display file name', () => {
const file = new File([], 'f.txt') const file = new File([], 'f.txt')
const { queryByText } = render(<FileInput file={file} onChange={() => {}} />) const { queryByText } = render(
<FileInput
file={file}
onChange={() => {
/* */
}}
/>,
)
expect(queryByText('f.txt')).toBeInTheDocument() expect(queryByText('f.txt')).toBeInTheDocument()
}) })

View File

@ -50,7 +50,7 @@ describe('remove plugin', () => {
it('cancelled', async () => { it('cancelled', async () => {
const stdio = new Stdio() const stdio = new Stdio()
setImmediate(() => process.stdin.emit('keypress', 'n', 'n')) setTimeout(() => process.stdin.emit('keypress', 'n', 'n'), 0)
await apt(stdio, ['remove', 'test']) await apt(stdio, ['remove', 'test'])
expect(fetch.post).not.toBeCalled() expect(fetch.post).not.toBeCalled()
}) })
@ -59,7 +59,7 @@ describe('remove plugin', () => {
fetch.post.mockResolvedValue({ code: 0, message: 'ok' }) fetch.post.mockResolvedValue({ code: 0, message: 'ok' })
const stdio = new Stdio() const stdio = new Stdio()
setImmediate(() => process.stdin.emit('keypress', 'y', 'y')) setTimeout(() => process.stdin.emit('keypress', 'y', 'y'), 0)
await apt(stdio, ['remove', 'test']) await apt(stdio, ['remove', 'test'])
expect(fetch.post).toBeCalledWith('/admin/plugins/manage', { expect(fetch.post).toBeCalledWith('/admin/plugins/manage', {
action: 'delete', action: 'delete',

View File

@ -50,7 +50,7 @@ describe('remove plugin', () => {
it('cancelled', async () => { it('cancelled', async () => {
const stdio = new Stdio() const stdio = new Stdio()
setImmediate(() => process.stdin.emit('keypress', 'n', 'n')) setTimeout(() => process.stdin.emit('keypress', 'n', 'n'), 0)
await dnf(stdio, ['remove', 'test']) await dnf(stdio, ['remove', 'test'])
expect(fetch.post).not.toBeCalled() expect(fetch.post).not.toBeCalled()
}) })
@ -59,7 +59,7 @@ describe('remove plugin', () => {
fetch.post.mockResolvedValue({ code: 0, message: 'ok' }) fetch.post.mockResolvedValue({ code: 0, message: 'ok' })
const stdio = new Stdio() const stdio = new Stdio()
setImmediate(() => process.stdin.emit('keypress', 'y', 'y')) setTimeout(() => process.stdin.emit('keypress', 'y', 'y'), 0)
await dnf(stdio, ['remove', 'test']) await dnf(stdio, ['remove', 'test'])
expect(fetch.post).toBeCalledWith('/admin/plugins/manage', { expect(fetch.post).toBeCalledWith('/admin/plugins/manage', {
action: 'delete', action: 'delete',

View File

@ -46,7 +46,7 @@ describe('remove plugin', () => {
it('cancelled', async () => { it('cancelled', async () => {
const stdio = new Stdio() const stdio = new Stdio()
setImmediate(() => process.stdin.emit('keypress', 'n', 'n')) setTimeout(() => process.stdin.emit('keypress', 'n', 'n'), 0)
await pacman(stdio, ['-R', 'test']) await pacman(stdio, ['-R', 'test'])
expect(fetch.post).not.toBeCalled() expect(fetch.post).not.toBeCalled()
}) })
@ -55,7 +55,7 @@ describe('remove plugin', () => {
fetch.post.mockResolvedValue({ code: 0, message: 'ok' }) fetch.post.mockResolvedValue({ code: 0, message: 'ok' })
const stdio = new Stdio() const stdio = new Stdio()
setImmediate(() => process.stdin.emit('keypress', 'y', 'y')) setTimeout(() => process.stdin.emit('keypress', 'y', 'y'), 0)
await pacman(stdio, ['-R', 'test']) await pacman(stdio, ['-R', 'test'])
expect(fetch.post).toBeCalledWith('/admin/plugins/manage', { expect(fetch.post).toBeCalledWith('/admin/plugins/manage', {
action: 'delete', action: 'delete',

View File

@ -1,13 +1,15 @@
import { Spinner } from '@/scripts/cli/Spinner' import { Spinner } from '@/scripts/cli/Spinner'
import { Stdio } from './stdio' import { Stdio } from './stdio'
test('run', () => { test('run', async () => {
jest.useRealTimers()
const stdio = new Stdio() const stdio = new Stdio()
const spinner = new Spinner(stdio) const spinner = new Spinner(stdio)
spinner.start() spinner.start()
jest.runTimersToTime(500) await new Promise((resolve) => setTimeout(resolve, 500))
expect(stdio.getStdout().length).toBeGreaterThan(0) expect(stdio.getStdout().length).toBeGreaterThan(0)
spinner.stop() spinner.stop()

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/indent */ /* eslint-disable @typescript-eslint/indent */
import * as net from '../../src/scripts/net' import type * as net from '../../src/scripts/net'
export { ResponseBody } from '../../src/scripts/net' export type { ResponseBody } from '../../src/scripts/net'
export const init = {} as typeof net.init export const init = {} as typeof net.init

View File

@ -1,5 +1,5 @@
import { ModalOptions, ModalResult } from '../../src/components/Modal' import type { ModalOptions, ModalResult } from '../../src/components/Modal'
import { Toast } from '../../src/scripts/toast' import type { Toast } from '../../src/scripts/toast'
export const showModal = {} as jest.Mock< export const showModal = {} as jest.Mock<
Promise<ModalResult>, Promise<ModalResult>,

View File

@ -1,7 +1,7 @@
import type { Paginator } from '@/scripts/types' import type { Paginator } from '@/scripts/types'
export function flushPromises() { export function flushPromises() {
return new Promise((resolve) => setImmediate(resolve)) return new Promise((resolve) => setTimeout(resolve, 0))
} }
export function createPaginator<T>(data: T[], perPage = 6): Paginator<T> { export function createPaginator<T>(data: T[], perPage = 6): Paginator<T> {

View File

@ -3,7 +3,7 @@ import { render, waitFor, fireEvent } from '@testing-library/react'
import { createPaginator } from '../../utils' import { createPaginator } from '../../utils'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import * as fetch from '@/scripts/net' import * as fetch from '@/scripts/net'
import { Player } from '@/scripts/types' import type { Player } from '@/scripts/types'
import urls from '@/scripts/urls' import urls from '@/scripts/urls'
import PlayersManagement from '@/views/admin/PlayersManagement' import PlayersManagement from '@/views/admin/PlayersManagement'

View File

@ -236,7 +236,7 @@ describe('upload plugin archive', () => {
const { getAllByText } = render(<PluginsManagement />) const { getAllByText } = render(<PluginsManagement />)
await waitFor(() => expect(fetch.get).toBeCalledTimes(1)) await waitFor(() => expect(fetch.get).toBeCalledTimes(1))
fireEvent.click(getAllByText(t('general.submit'))[0]) fireEvent.click(getAllByText(t('general.submit'))[0]!)
expect(fetch.post).not.toBeCalled() expect(fetch.post).not.toBeCalled()
}) })
@ -253,7 +253,7 @@ describe('upload plugin archive', () => {
fireEvent.change(getByTitle(t('skinlib.upload.select-file')), { fireEvent.change(getByTitle(t('skinlib.upload.select-file')), {
target: { files: [file] }, target: { files: [file] },
}) })
fireEvent.click(getAllByText(t('general.submit'))[0]) fireEvent.click(getAllByText(t('general.submit'))[0]!)
await waitFor(() => { await waitFor(() => {
expect(fetch.get).toBeCalledTimes(2) expect(fetch.get).toBeCalledTimes(2)
expect(fetch.post).toBeCalledWith( expect(fetch.post).toBeCalledWith(
@ -261,7 +261,7 @@ describe('upload plugin archive', () => {
expect.any(FormData), expect.any(FormData),
) )
}) })
const formData = fetch.post.mock.calls[0][1] as FormData const formData = fetch.post.mock.calls[0]![1] as FormData
expect(formData.get('file')).toStrictEqual(file) expect(formData.get('file')).toStrictEqual(file)
expect(queryByText('plugin.zip')).not.toBeInTheDocument() expect(queryByText('plugin.zip')).not.toBeInTheDocument()
expect(queryByText('ok')).toBeInTheDocument() expect(queryByText('ok')).toBeInTheDocument()
@ -281,7 +281,7 @@ describe('upload plugin archive', () => {
fireEvent.change(getByTitle(t('skinlib.upload.select-file')), { fireEvent.change(getByTitle(t('skinlib.upload.select-file')), {
target: { files: [file] }, target: { files: [file] },
}) })
fireEvent.click(getAllByText(t('general.submit'))[0]) fireEvent.click(getAllByText(t('general.submit'))[0]!)
await waitFor(() => { await waitFor(() => {
expect(fetch.get).toBeCalledTimes(1) expect(fetch.get).toBeCalledTimes(1)
expect(fetch.post).toBeCalledWith( expect(fetch.post).toBeCalledWith(
@ -312,7 +312,7 @@ describe('submit remote URL', () => {
fireEvent.input(getByLabelText('URL'), { fireEvent.input(getByLabelText('URL'), {
target: { value: 'https://example.com/a.zip' }, target: { value: 'https://example.com/a.zip' },
}) })
fireEvent.click(getAllByText(t('general.submit'))[1]) fireEvent.click(getAllByText(t('general.submit'))[1]!)
await waitFor(() => { await waitFor(() => {
expect(fetch.get).toBeCalledTimes(2) expect(fetch.get).toBeCalledTimes(2)
expect(fetch.post).toBeCalledWith('/admin/plugins/wget', { expect(fetch.post).toBeCalledWith('/admin/plugins/wget', {
@ -342,7 +342,7 @@ describe('submit remote URL', () => {
fireEvent.input(getByLabelText('URL'), { fireEvent.input(getByLabelText('URL'), {
target: { value: 'https://example.com/a.zip' }, target: { value: 'https://example.com/a.zip' },
}) })
fireEvent.click(getAllByText(t('general.submit'))[1]) fireEvent.click(getAllByText(t('general.submit'))[1]!)
await waitFor(() => { await waitFor(() => {
expect(fetch.get).toBeCalledTimes(1) expect(fetch.get).toBeCalledTimes(1)
expect(fetch.post).toBeCalledWith('/admin/plugins/wget', { expect(fetch.post).toBeCalledWith('/admin/plugins/wget', {

View File

@ -3,7 +3,7 @@ import { render, waitFor, fireEvent } from '@testing-library/react'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import * as fetch from '@/scripts/net' import * as fetch from '@/scripts/net'
import PluginsMarket from '@/views/admin/PluginsMarket' import PluginsMarket from '@/views/admin/PluginsMarket'
import { Plugin } from '@/views/admin/PluginsMarket/types' import type { Plugin } from '@/views/admin/PluginsMarket/types'
jest.mock('@/scripts/net') jest.mock('@/scripts/net')

View File

@ -40,7 +40,7 @@ describe('edit line', () => {
await waitFor(() => expect(fetch.get).toBeCalledTimes(1)) await waitFor(() => expect(fetch.get).toBeCalledTimes(1))
fireEvent.click(getByText(t('admin.i18n.modify'))) fireEvent.click(getByText(t('admin.i18n.modify')))
fireEvent.input(getByDisplayValue(fixtureLine.text.en), { fireEvent.input(getByDisplayValue(fixtureLine.text.en!), {
target: { value: 'finish' }, target: { value: 'finish' },
}) })
fireEvent.click(getByText(t('general.confirm'))) fireEvent.click(getByText(t('general.confirm')))
@ -63,7 +63,7 @@ describe('edit line', () => {
await waitFor(() => expect(fetch.get).toBeCalledTimes(1)) await waitFor(() => expect(fetch.get).toBeCalledTimes(1))
fireEvent.click(getByText(t('admin.i18n.modify'))) fireEvent.click(getByText(t('admin.i18n.modify')))
fireEvent.input(getByDisplayValue(fixtureLine.text.en), { fireEvent.input(getByDisplayValue(fixtureLine.text.en!), {
target: { value: 'finish' }, target: { value: 'finish' },
}) })
fireEvent.click(getByText(t('general.confirm'))) fireEvent.click(getByText(t('general.confirm')))
@ -72,7 +72,7 @@ describe('edit line', () => {
text: 'finish', text: 'finish',
}), }),
) )
expect(queryByText(fixtureLine.text.en)).toBeInTheDocument() expect(queryByText(fixtureLine.text.en!)).toBeInTheDocument()
expect(queryByText('failed')).toBeInTheDocument() expect(queryByText('failed')).toBeInTheDocument()
expect(getByRole('alert')).toHaveClass('alert-danger') expect(getByRole('alert')).toHaveClass('alert-danger')
}) })
@ -84,12 +84,12 @@ describe('edit line', () => {
await waitFor(() => expect(fetch.get).toBeCalledTimes(1)) await waitFor(() => expect(fetch.get).toBeCalledTimes(1))
fireEvent.click(getByText(t('admin.i18n.modify'))) fireEvent.click(getByText(t('admin.i18n.modify')))
fireEvent.input(getByDisplayValue(fixtureLine.text.en), { fireEvent.input(getByDisplayValue(fixtureLine.text.en!), {
target: { value: 'finish' }, target: { value: 'finish' },
}) })
fireEvent.click(getByText(t('general.cancel'))) fireEvent.click(getByText(t('general.cancel')))
await waitFor(() => expect(fetch.put).not.toBeCalled()) await waitFor(() => expect(fetch.put).not.toBeCalled())
expect(queryByText(fixtureLine.text.en)).toBeInTheDocument() expect(queryByText(fixtureLine.text.en!)).toBeInTheDocument()
}) })
}) })
@ -109,7 +109,7 @@ describe('delete line', () => {
await waitFor(() => await waitFor(() =>
expect(fetch.del).toBeCalledWith(`/admin/i18n/${fixtureLine.id}`), expect(fetch.del).toBeCalledWith(`/admin/i18n/${fixtureLine.id}`),
) )
expect(queryByText(fixtureLine.text.en)).not.toBeInTheDocument() expect(queryByText(fixtureLine.text.en!)).not.toBeInTheDocument()
expect(queryByText('ok')).toBeInTheDocument() expect(queryByText('ok')).toBeInTheDocument()
expect(getByRole('status')).toHaveClass('alert-success') expect(getByRole('status')).toHaveClass('alert-success')
}) })
@ -121,6 +121,6 @@ describe('delete line', () => {
fireEvent.click(getByText(t('admin.i18n.delete'))) fireEvent.click(getByText(t('admin.i18n.delete')))
fireEvent.click(getByText(t('general.cancel'))) fireEvent.click(getByText(t('general.cancel')))
await waitFor(() => expect(fetch.del).not.toBeCalled()) await waitFor(() => expect(fetch.del).not.toBeCalled())
expect(queryByText(fixtureLine.text.en)).toBeInTheDocument() expect(queryByText(fixtureLine.text.en!)).toBeInTheDocument()
}) })
}) })

View File

@ -199,7 +199,7 @@ describe('edit texture name', () => {
) )
await waitFor(() => expect(fetch.get).toBeCalledTimes(1)) await waitFor(() => expect(fetch.get).toBeCalledTimes(1))
fireEvent.click(getAllByTitle(t('skinlib.show.edit'))[0]) fireEvent.click(getAllByTitle(t('skinlib.show.edit'))[0]!)
fireEvent.input(getByDisplayValue(fixtureSkin.name), { fireEvent.input(getByDisplayValue(fixtureSkin.name), {
target: { value: '' }, target: { value: '' },
}) })
@ -223,7 +223,7 @@ describe('edit texture name', () => {
} = render(<Show />) } = render(<Show />)
await waitFor(() => expect(fetch.get).toBeCalledTimes(1)) await waitFor(() => expect(fetch.get).toBeCalledTimes(1))
fireEvent.click(getAllByTitle(t('skinlib.show.edit'))[0]) fireEvent.click(getAllByTitle(t('skinlib.show.edit'))[0]!)
fireEvent.input(getByDisplayValue(fixtureSkin.name), { fireEvent.input(getByDisplayValue(fixtureSkin.name), {
target: { value: 't' }, target: { value: 't' },
}) })
@ -250,7 +250,7 @@ describe('edit texture name', () => {
} = render(<Show />) } = render(<Show />)
await waitFor(() => expect(fetch.get).toBeCalledTimes(1)) await waitFor(() => expect(fetch.get).toBeCalledTimes(1))
fireEvent.click(getAllByTitle(t('skinlib.show.edit'))[0]) fireEvent.click(getAllByTitle(t('skinlib.show.edit'))[0]!)
fireEvent.input(getByDisplayValue(fixtureSkin.name), { fireEvent.input(getByDisplayValue(fixtureSkin.name), {
target: { value: 't' }, target: { value: 't' },
}) })
@ -278,7 +278,7 @@ describe('edit texture type', () => {
) )
await waitFor(() => expect(fetch.get).toBeCalledTimes(1)) await waitFor(() => expect(fetch.get).toBeCalledTimes(1))
fireEvent.click(getAllByTitle(t('skinlib.show.edit'))[1]) fireEvent.click(getAllByTitle(t('skinlib.show.edit'))[1]!)
fireEvent.click(getByLabelText('Alex')) fireEvent.click(getByLabelText('Alex'))
fireEvent.click(getByText(t('general.cancel'))) fireEvent.click(getByText(t('general.cancel')))
await waitFor(() => expect(fetch.put).not.toBeCalled()) await waitFor(() => expect(fetch.put).not.toBeCalled())
@ -297,7 +297,7 @@ describe('edit texture type', () => {
} = render(<Show />) } = render(<Show />)
await waitFor(() => expect(fetch.get).toBeCalledTimes(1)) await waitFor(() => expect(fetch.get).toBeCalledTimes(1))
fireEvent.click(getAllByTitle(t('skinlib.show.edit'))[1]) fireEvent.click(getAllByTitle(t('skinlib.show.edit'))[1]!)
fireEvent.click(getByLabelText('Alex')) fireEvent.click(getByLabelText('Alex'))
fireEvent.click(getByText(t('general.confirm'))) fireEvent.click(getByText(t('general.confirm')))
await waitFor(() => await waitFor(() =>
@ -322,7 +322,7 @@ describe('edit texture type', () => {
} = render(<Show />) } = render(<Show />)
await waitFor(() => expect(fetch.get).toBeCalledTimes(1)) await waitFor(() => expect(fetch.get).toBeCalledTimes(1))
fireEvent.click(getAllByTitle(t('skinlib.show.edit'))[1]) fireEvent.click(getAllByTitle(t('skinlib.show.edit'))[1]!)
fireEvent.click(getByLabelText('Alex')) fireEvent.click(getByLabelText('Alex'))
fireEvent.click(getByText(t('general.confirm'))) fireEvent.click(getByText(t('general.confirm')))
await waitFor(() => await waitFor(() =>

View File

@ -6,7 +6,7 @@ import * as fetch from '@/scripts/net'
import { TextureType } from '@/scripts/types' import { TextureType } from '@/scripts/types'
import urls from '@/scripts/urls' import urls from '@/scripts/urls'
import SkinLibrary from '@/views/skinlib/SkinLibrary' import SkinLibrary from '@/views/skinlib/SkinLibrary'
import { LibraryItem } from '@/views/skinlib/SkinLibrary/types' import type { LibraryItem } from '@/views/skinlib/SkinLibrary/types'
jest.mock('@/scripts/net') jest.mock('@/scripts/net')

View File

@ -221,7 +221,7 @@ describe('upload texture', () => {
expect.any(FormData), expect.any(FormData),
) )
const formData = fetch.post.mock.calls[0][1] as FormData const formData = fetch.post.mock.calls[0]![1] as FormData
expect(formData.get('name')).toBe('t') expect(formData.get('name')).toBe('t')
expect(formData.get('type')).toBe('steve') expect(formData.get('type')).toBe('steve')
expect(formData.get('file')).toStrictEqual(file) expect(formData.get('file')).toStrictEqual(file)
@ -241,7 +241,7 @@ describe('upload texture', () => {
fireEvent.click(getByText(t('skinlib.upload.button'))) fireEvent.click(getByText(t('skinlib.upload.button')))
await waitFor(() => expect(fetch.post).toBeCalled()) await waitFor(() => expect(fetch.post).toBeCalled())
const formData = fetch.post.mock.calls[0][1] as FormData const formData = fetch.post.mock.calls[0]![1] as FormData
expect(formData.get('public')).toBe('0') expect(formData.get('public')).toBe('0')
}) })

View File

@ -441,7 +441,7 @@ describe('apply textures to player', () => {
expect(await findByText(fixturePlayer.name)).toBeInTheDocument() expect(await findByText(fixturePlayer.name)).toBeInTheDocument()
fireEvent.input(getAllByPlaceholderText(t('user.typeToSearch'))[1], { fireEvent.input(getAllByPlaceholderText(t('user.typeToSearch'))[1]!, {
target: { value: 'reina' }, target: { value: 'reina' },
}) })
expect(queryByText(fixturePlayer.name)).not.toBeInTheDocument() expect(queryByText(fixturePlayer.name)).not.toBeInTheDocument()

View File

@ -3,7 +3,7 @@ import { render, fireEvent, waitFor } from '@testing-library/react'
import * as fetch from '@/scripts/net' import * as fetch from '@/scripts/net'
import { t } from '@/scripts/i18n' import { t } from '@/scripts/i18n'
import OAuth from '@/views/user/OAuth' import OAuth from '@/views/user/OAuth'
import { App } from '@/views/user/OAuth/types' import type { App } from '@/views/user/OAuth/types'
jest.mock('@/scripts/net') jest.mock('@/scripts/net')

View File

@ -12,7 +12,7 @@ const tree: TreeObject = {}
function parseURI(uri: string): ts.ArrowFunction { function parseURI(uri: string): ts.ArrowFunction {
const matches = /\{([a-z]+)\}/.exec(uri) const matches = /\{([a-z]+)\}/.exec(uri)
if (matches) { if (matches?.[0] && matches?.[1]) {
const param = matches[1] const param = matches[1]
const type = const type =
param.endsWith('id') || param.endsWith('id') ||

View File

@ -5,6 +5,7 @@
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
"declaration": false, "declaration": false,
"declarationMap": false,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@/*": ["./resources/assets/src/*"] "@/*": ["./resources/assets/src/*"]

1947
yarn.lock

File diff suppressed because it is too large Load Diff