From d1d4c5481808348d7f049ee988de66b59ebff299 Mon Sep 17 00:00:00 2001 From: Pig Fang Date: Mon, 18 Mar 2019 09:55:24 +0800 Subject: [PATCH] Migrate to TypeScript --- package.json | 4 +-- .../src/components/{route.js => route.ts} | 34 +++++++++--------- .../tests/__mocks__/{file.js => file.ts} | 0 .../{skinview3d.js => skinview3d.ts} | 20 +++++++++-- .../tests/__mocks__/{style.js => style.ts} | 0 .../tests/__mocks__/{toastr.js => toastr.ts} | 0 .../{vue-good-table.js => vue-good-table.ts} | 0 ...mization.test.js => Customization.test.ts} | 2 +- .../admin/{Market.test.js => Market.test.ts} | 2 +- .../{Players.test.js => Players.test.ts} | 24 ++++++++----- .../{Plugins.test.js => Plugins.test.ts} | 2 +- .../admin/{Update.test.js => Update.test.ts} | 4 +-- .../admin/{Users.test.js => Users.test.ts} | 36 ++++++++++++------- .../auth/{Forgot.test.js => Forgot.test.ts} | 2 +- .../auth/{Login.test.js => Login.test.ts} | 2 +- .../{Register.test.js => Register.test.ts} | 2 +- .../auth/{Reset.test.js => Reset.test.ts} | 2 +- .../{Previewer.test.js => Previewer.test.ts} | 33 +++++++++++------ .../skinlib/{List.test.js => List.test.ts} | 11 +++--- .../skinlib/{Show.test.js => Show.test.ts} | 35 +++++++++++------- ...kinLibItem.test.js => SkinLibItem.test.ts} | 8 ++--- .../{Upload.test.js => Upload.test.ts} | 32 +++++++++-------- .../user/{Closet.test.js => Closet.test.ts} | 23 ++++++------ ...{ClosetItem.test.js => ClosetItem.test.ts} | 18 +++++----- .../{Dashboard.test.js => Dashboard.test.ts} | 4 +-- ...tion.test.js => EmailVerification.test.ts} | 2 +- .../user/{Players.test.js => Players.test.ts} | 10 +++--- .../user/{Profile.test.js => Profile.test.ts} | 16 ++++----- resources/assets/tests/js/types.d.ts | 3 ++ resources/assets/tests/vue.d.ts | 5 +++ tsconfig.json | 3 ++ 31 files changed, 207 insertions(+), 132 deletions(-) rename resources/assets/src/components/{route.js => route.ts} (53%) rename resources/assets/tests/__mocks__/{file.js => file.ts} (100%) rename resources/assets/tests/__mocks__/{skinview3d.js => skinview3d.ts} (60%) rename resources/assets/tests/__mocks__/{style.js => style.ts} (100%) rename resources/assets/tests/__mocks__/{toastr.js => toastr.ts} (100%) rename resources/assets/tests/__mocks__/{vue-good-table.js => vue-good-table.ts} (100%) rename resources/assets/tests/components/admin/{Customization.test.js => Customization.test.ts} (92%) rename resources/assets/tests/components/admin/{Market.test.js => Market.test.ts} (98%) rename resources/assets/tests/components/admin/{Players.test.js => Players.test.ts} (87%) rename resources/assets/tests/components/admin/{Plugins.test.js => Plugins.test.ts} (98%) rename resources/assets/tests/components/admin/{Update.test.js => Update.test.ts} (95%) rename resources/assets/tests/components/admin/{Users.test.js => Users.test.ts} (95%) rename resources/assets/tests/components/auth/{Forgot.test.js => Forgot.test.ts} (96%) rename resources/assets/tests/components/auth/{Login.test.js => Login.test.ts} (97%) rename resources/assets/tests/components/auth/{Register.test.js => Register.test.ts} (98%) rename resources/assets/tests/components/auth/{Reset.test.js => Reset.test.ts} (97%) rename resources/assets/tests/components/common/{Previewer.test.js => Previewer.test.ts} (77%) rename resources/assets/tests/components/skinlib/{List.test.js => List.test.ts} (94%) rename resources/assets/tests/components/skinlib/{Show.test.js => Show.test.ts} (93%) rename resources/assets/tests/components/skinlib/{SkinLibItem.test.js => SkinLibItem.test.ts} (93%) rename resources/assets/tests/components/skinlib/{Upload.test.js => Upload.test.ts} (84%) rename resources/assets/tests/components/user/{Closet.test.js => Closet.test.ts} (91%) rename resources/assets/tests/components/user/{ClosetItem.test.js => ClosetItem.test.ts} (88%) rename resources/assets/tests/components/user/{Dashboard.test.js => Dashboard.test.ts} (98%) rename resources/assets/tests/components/user/{EmailVerification.test.js => EmailVerification.test.ts} (99%) rename resources/assets/tests/components/user/{Players.test.js => Players.test.ts} (95%) rename resources/assets/tests/components/user/{Profile.test.js => Profile.test.ts} (92%) create mode 100644 resources/assets/tests/vue.d.ts diff --git a/package.json b/package.json index 22753d96..91d22386 100644 --- a/package.json +++ b/package.json @@ -176,8 +176,8 @@ ], "moduleNameMapper": { "^@/(.*)$": "/resources/assets/src/$1", - "\\.css$": "/resources/assets/tests/__mocks__/style.js", - "\\.(png|jpg)$": "/resources/assets/tests/__mocks__/file.js" + "\\.css$": "/resources/assets/tests/__mocks__/style.ts", + "\\.(png|jpg)$": "/resources/assets/tests/__mocks__/file.ts" }, "setupFilesAfterEnv": [ "/resources/assets/tests/setup.js" diff --git a/resources/assets/src/components/route.js b/resources/assets/src/components/route.ts similarity index 53% rename from resources/assets/src/components/route.js rename to resources/assets/src/components/route.ts index 8d0aec63..dd750448 100644 --- a/resources/assets/src/components/route.js +++ b/resources/assets/src/components/route.ts @@ -1,87 +1,87 @@ export default [ { path: 'user', - component: () => import('./user/Dashboard'), + component: () => import('./user/Dashboard.vue'), el: '#usage-box', }, { path: 'user/closet', - component: () => import('./user/Closet'), + component: () => import('./user/Closet.vue'), el: '.content', }, { path: 'user/player', - component: () => import('./user/Players'), + component: () => import('./user/Players.vue'), el: '.content', }, { path: 'user/profile', - component: () => import('./user/Profile'), + component: () => import('./user/Profile.vue'), el: '.content', }, { path: 'admin/users', - component: () => import('./admin/Users'), + component: () => import('./admin/Users.vue'), el: '.content', }, { path: 'admin/players', - component: () => import('./admin/Players'), + component: () => import('./admin/Players.vue'), el: '.content', }, { path: 'admin/customize', - component: () => import('./admin/Customization'), + component: () => import('./admin/Customization.vue'), el: '#change-color', }, { path: 'admin/plugins/manage', - component: () => import('./admin/Plugins'), + component: () => import('./admin/Plugins.vue'), el: '.content', }, { path: 'admin/plugins/market', - component: () => import('./admin/Market'), + component: () => import('./admin/Market.vue'), el: '.content', }, { path: 'admin/update', - component: () => import('./admin/Update'), + component: () => import('./admin/Update.vue'), el: '#update-button', }, { path: 'auth/login', - component: () => import('./auth/Login'), + component: () => import('./auth/Login.vue'), el: 'form', }, { path: 'auth/register', - component: () => import('./auth/Register'), + component: () => import('./auth/Register.vue'), el: 'form', }, { path: 'auth/forgot', - component: () => import('./auth/Forgot'), + component: () => import('./auth/Forgot.vue'), el: 'form', }, { path: 'auth/reset/(\\d+)', - component: () => import('./auth/Reset'), + component: () => import('./auth/Reset.vue'), el: 'form', }, { path: 'skinlib', - component: () => import('./skinlib/List'), + component: () => import('./skinlib/List.vue'), el: '.content-wrapper', }, { path: 'skinlib/show/(\\d+)', - component: () => import('./skinlib/Show'), + component: () => import('./skinlib/Show.vue'), el: '.content > .row:nth-child(1)', }, { path: 'skinlib/upload', - component: () => import('./skinlib/Upload'), + component: () => import('./skinlib/Upload.vue'), el: '.content', }, ] diff --git a/resources/assets/tests/__mocks__/file.js b/resources/assets/tests/__mocks__/file.ts similarity index 100% rename from resources/assets/tests/__mocks__/file.js rename to resources/assets/tests/__mocks__/file.ts diff --git a/resources/assets/tests/__mocks__/skinview3d.js b/resources/assets/tests/__mocks__/skinview3d.ts similarity index 60% rename from resources/assets/tests/__mocks__/skinview3d.js rename to resources/assets/tests/__mocks__/skinview3d.ts index 62ae9afa..7479bb11 100644 --- a/resources/assets/tests/__mocks__/skinview3d.js +++ b/resources/assets/tests/__mocks__/skinview3d.ts @@ -1,10 +1,26 @@ +/* eslint-disable max-params */ /* eslint-disable max-classes-per-file */ export class SkinViewer { + disposed: boolean + + skinUrl: string + + capeUrl: string + + animationPaused: boolean + + camera: { position: { z: number } } + constructor() { + this.skinUrl = '' + this.capeUrl = '' + this.disposed = false this.animationPaused = false this.camera = { - position: {}, + position: { + z: 0, + }, } } @@ -14,7 +30,7 @@ export class SkinViewer { } export class CompositeAnimation { - add(animation) { + add(animation: any) { return animation } } diff --git a/resources/assets/tests/__mocks__/style.js b/resources/assets/tests/__mocks__/style.ts similarity index 100% rename from resources/assets/tests/__mocks__/style.js rename to resources/assets/tests/__mocks__/style.ts diff --git a/resources/assets/tests/__mocks__/toastr.js b/resources/assets/tests/__mocks__/toastr.ts similarity index 100% rename from resources/assets/tests/__mocks__/toastr.js rename to resources/assets/tests/__mocks__/toastr.ts diff --git a/resources/assets/tests/__mocks__/vue-good-table.js b/resources/assets/tests/__mocks__/vue-good-table.ts similarity index 100% rename from resources/assets/tests/__mocks__/vue-good-table.js rename to resources/assets/tests/__mocks__/vue-good-table.ts diff --git a/resources/assets/tests/components/admin/Customization.test.js b/resources/assets/tests/components/admin/Customization.test.ts similarity index 92% rename from resources/assets/tests/components/admin/Customization.test.js rename to resources/assets/tests/components/admin/Customization.test.ts index 97872428..ae43882d 100644 --- a/resources/assets/tests/components/admin/Customization.test.js +++ b/resources/assets/tests/components/admin/Customization.test.ts @@ -1,6 +1,6 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' -import Customization from '@/components/admin/Customization' +import Customization from '@/components/admin/Customization.vue' window.blessing.extra = { currentSkin: 'skin-blue' } diff --git a/resources/assets/tests/components/admin/Market.test.js b/resources/assets/tests/components/admin/Market.test.ts similarity index 98% rename from resources/assets/tests/components/admin/Market.test.js rename to resources/assets/tests/components/admin/Market.test.ts index 95107855..4514c8f3 100644 --- a/resources/assets/tests/components/admin/Market.test.js +++ b/resources/assets/tests/components/admin/Market.test.ts @@ -1,6 +1,6 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' -import Market from '@/components/admin/Market' +import Market from '@/components/admin/Market.vue' import { flushPromises } from '../../utils' import { swal } from '@/js/notify' diff --git a/resources/assets/tests/components/admin/Players.test.js b/resources/assets/tests/components/admin/Players.test.ts similarity index 87% rename from resources/assets/tests/components/admin/Players.test.js rename to resources/assets/tests/components/admin/Players.test.ts index 1819402d..83c11408 100644 --- a/resources/assets/tests/components/admin/Players.test.js +++ b/resources/assets/tests/components/admin/Players.test.ts @@ -1,7 +1,7 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' import { flushPromises } from '../../utils' -import Players from '@/components/admin/Players' +import Players from '@/components/admin/Players.vue' import { swal } from '@/js/notify' jest.mock('@/js/notify') @@ -18,10 +18,16 @@ test('fetch data after initializing', () => { }) test('update tables', () => { + interface Methods { + onPageChange(options: { currentPage: number }): void + onPerPageChange(options: { currentPerPage: number }): void + onSortChange(options: { sortType: 'asc' | 'desc', columnIndex: number }): void + } + Vue.prototype.$http.get.mockResolvedValue({ - data: Array.from({ length: 20 }).map((item, pid) => ({ pid })), + data: Array.from({ length: 20 }).map((_, pid) => ({ pid })), }) - const wrapper = mount(Players) + const wrapper = mount(Players) wrapper.find('.vgt-input').setValue('abc') expect(Vue.prototype.$http.get).toBeCalledWith( @@ -83,7 +89,7 @@ test('change texture', async () => { button.trigger('click') await flushPromises() expect(wrapper.html()).toContain('/preview/64/5.png') - expect(window.$).toBeCalledWith('.modal') + expect($).toBeCalledWith('.modal') }) test('change player name', async () => { @@ -95,11 +101,13 @@ test('change player name', async () => { Vue.prototype.$http.post .mockResolvedValueOnce({ errno: 1, msg: '1' }) .mockResolvedValueOnce({ errno: 0, msg: '0' }) - swal.mockImplementationOnce(() => ({ dismiss: 1 })) + swal.mockImplementationOnce(() => Promise.resolve({ dismiss: 1 })) .mockImplementation(options => { - options.inputValidator() - options.inputValidator('new') - return { value: 'new' } + if (options.inputValidator) { + options.inputValidator('') + options.inputValidator('new') + } + return Promise.resolve({ value: 'new' }) }) const wrapper = mount(Players) await wrapper.vm.$nextTick() diff --git a/resources/assets/tests/components/admin/Plugins.test.js b/resources/assets/tests/components/admin/Plugins.test.ts similarity index 98% rename from resources/assets/tests/components/admin/Plugins.test.js rename to resources/assets/tests/components/admin/Plugins.test.ts index 93d2a024..648cdbdf 100644 --- a/resources/assets/tests/components/admin/Plugins.test.js +++ b/resources/assets/tests/components/admin/Plugins.test.ts @@ -1,6 +1,6 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' -import Plugins from '@/components/admin/Plugins' +import Plugins from '@/components/admin/Plugins.vue' import toastr from 'toastr' import { flushPromises } from '../../utils' import { swal } from '@/js/notify' diff --git a/resources/assets/tests/components/admin/Update.test.js b/resources/assets/tests/components/admin/Update.test.ts similarity index 95% rename from resources/assets/tests/components/admin/Update.test.js rename to resources/assets/tests/components/admin/Update.test.ts index bfba67d9..f1c02185 100644 --- a/resources/assets/tests/components/admin/Update.test.js +++ b/resources/assets/tests/components/admin/Update.test.ts @@ -29,7 +29,7 @@ test('perform update', async () => { button.trigger('click') await flushPromises() - expect(window.$).not.toBeCalled() + expect($).not.toBeCalled() expect(Vue.prototype.$http.post).toBeCalledWith( '/admin/update/download', { action: 'prepare-download' } @@ -37,7 +37,7 @@ test('perform update', async () => { button.trigger('click') jest.runOnlyPendingTimers() await flushPromises() - expect(window.$).toBeCalled() + expect($).toBeCalled() expect(Vue.prototype.$http.get).toBeCalledWith( '/admin/update/download', { action: 'get-progress' } diff --git a/resources/assets/tests/components/admin/Users.test.js b/resources/assets/tests/components/admin/Users.test.ts similarity index 95% rename from resources/assets/tests/components/admin/Users.test.js rename to resources/assets/tests/components/admin/Users.test.ts index c2c079e9..9a72043c 100644 --- a/resources/assets/tests/components/admin/Users.test.js +++ b/resources/assets/tests/components/admin/Users.test.ts @@ -1,14 +1,14 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' -import { flushPromises } from '../../utils' -import Users from '@/components/admin/Users' +import Users from '@/components/admin/Users.vue' import toastr from 'toastr' import { swal } from '@/js/notify' import '@/js/i18n' +import { flushPromises } from '../../utils' jest.mock('@/js/notify') jest.mock('@/js/i18n', () => ({ - trans: key => key, + trans: (key: string) => key, })) test('fetch data after initializing', () => { @@ -23,10 +23,16 @@ test('fetch data after initializing', () => { }) test('update tables', () => { + interface Methods { + onPageChange(options: { currentPage: number }): void + onPerPageChange(options: { currentPerPage: number }): void + onSortChange(options: { sortType: 'asc' | 'desc', columnIndex: number }): void + } + Vue.prototype.$http.get.mockResolvedValue({ - data: Array.from({ length: 20 }).map((item, uid) => ({ uid })), + data: Array.from({ length: 20 }).map((_, uid) => ({ uid })), }) - const wrapper = mount(Users) + const wrapper = mount(Users) wrapper.find('.vgt-input').setValue('abc') expect(Vue.prototype.$http.get).toBeCalledWith( @@ -389,11 +395,13 @@ test('change email', async () => { Vue.prototype.$http.post .mockResolvedValueOnce({ errno: 1, msg: '1' }) .mockResolvedValueOnce({ errno: 0, msg: '0' }) - swal.mockImplementationOnce(() => ({ dismiss: 1 })) + swal.mockImplementationOnce(() => Promise.resolve({ dismiss: 1 })) .mockImplementation(options => { - options.inputValidator() - options.inputValidator('value') - return { value: 'd@e.f' } + if (options.inputValidator) { + options.inputValidator('') + options.inputValidator('value') + } + return Promise.resolve({ value: 'd@e.f' }) }) const wrapper = mount(Users) await wrapper.vm.$nextTick() @@ -449,11 +457,13 @@ test('change nickname', async () => { Vue.prototype.$http.post .mockResolvedValueOnce({ errno: 1, msg: '1' }) .mockResolvedValueOnce({ errno: 0, msg: '0' }) - swal.mockImplementationOnce(() => ({ dismiss: 1 })) + swal.mockImplementationOnce(() => Promise.resolve({ dismiss: 1 })) .mockImplementation(options => { - options.inputValidator() - options.inputValidator('value') - return { value: 'new' } + if (options.inputValidator) { + options.inputValidator('') + options.inputValidator('value') + } + return Promise.resolve({ value: 'new' }) }) const wrapper = mount(Users) await wrapper.vm.$nextTick() diff --git a/resources/assets/tests/components/auth/Forgot.test.js b/resources/assets/tests/components/auth/Forgot.test.ts similarity index 96% rename from resources/assets/tests/components/auth/Forgot.test.js rename to resources/assets/tests/components/auth/Forgot.test.ts index 235e6626..4605375d 100644 --- a/resources/assets/tests/components/auth/Forgot.test.js +++ b/resources/assets/tests/components/auth/Forgot.test.ts @@ -1,6 +1,6 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' -import Forgot from '@/components/auth/Forgot' +import Forgot from '@/components/auth/Forgot.vue' test('click to refresh captcha', () => { jest.spyOn(Date, 'now') diff --git a/resources/assets/tests/components/auth/Login.test.js b/resources/assets/tests/components/auth/Login.test.ts similarity index 97% rename from resources/assets/tests/components/auth/Login.test.js rename to resources/assets/tests/components/auth/Login.test.ts index 2aa04a8e..eeaf78dc 100644 --- a/resources/assets/tests/components/auth/Login.test.js +++ b/resources/assets/tests/components/auth/Login.test.ts @@ -1,6 +1,6 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' -import Login from '@/components/auth/Login' +import Login from '@/components/auth/Login.vue' import { swal } from '@/js/notify' jest.mock('@/js/notify') diff --git a/resources/assets/tests/components/auth/Register.test.js b/resources/assets/tests/components/auth/Register.test.ts similarity index 98% rename from resources/assets/tests/components/auth/Register.test.js rename to resources/assets/tests/components/auth/Register.test.ts index 5af297e0..6cd36818 100644 --- a/resources/assets/tests/components/auth/Register.test.js +++ b/resources/assets/tests/components/auth/Register.test.ts @@ -1,6 +1,6 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' -import Register from '@/components/auth/Register' +import Register from '@/components/auth/Register.vue' import { swal } from '@/js/notify' jest.mock('@/js/notify') diff --git a/resources/assets/tests/components/auth/Reset.test.js b/resources/assets/tests/components/auth/Reset.test.ts similarity index 97% rename from resources/assets/tests/components/auth/Reset.test.js rename to resources/assets/tests/components/auth/Reset.test.ts index a8091b40..3eb83809 100644 --- a/resources/assets/tests/components/auth/Reset.test.js +++ b/resources/assets/tests/components/auth/Reset.test.ts @@ -1,6 +1,6 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' -import Reset from '@/components/auth/Reset' +import Reset from '@/components/auth/Reset.vue' import { swal } from '@/js/notify' jest.mock('@/js/notify') diff --git a/resources/assets/tests/components/common/Previewer.test.js b/resources/assets/tests/components/common/Previewer.test.ts similarity index 77% rename from resources/assets/tests/components/common/Previewer.test.js rename to resources/assets/tests/components/common/Previewer.test.ts index d50f062c..aa48957d 100644 --- a/resources/assets/tests/components/common/Previewer.test.js +++ b/resources/assets/tests/components/common/Previewer.test.ts @@ -1,32 +1,43 @@ +import Vue from 'vue' import { mount } from '@vue/test-utils' -import Previewer from '@/components/common/Previewer' +import Previewer from '@/components/common/Previewer.vue' import * as emitter from '@/js/event' import * as mockedSkinview3d from '../../__mocks__/skinview3d' +type Viewer = Vue & { viewer: mockedSkinview3d.SkinViewer } + +interface Handles { + handles: { + run: { paused: boolean } + walk: { paused: boolean } + rotate: { paused: boolean } + } +} + test('initialize skinview3d', () => { const stub = jest.fn() emitter.on('skinViewerMounted', stub) - const wrapper = mount(Previewer) + const wrapper = mount(Previewer) expect(wrapper.vm.viewer).toBeInstanceOf(mockedSkinview3d.SkinViewer) expect(wrapper.vm.viewer.camera.position.z).toBe(70) expect(stub).toBeCalledWith(expect.any(HTMLElement)) }) test('dispose viewer before destroy', () => { - const wrapper = mount(Previewer) + const wrapper = mount(Previewer) wrapper.destroy() expect(wrapper.vm.viewer.disposed).toBeTrue() }) test('skin URL should be updated', () => { - const wrapper = mount(Previewer) + const wrapper = mount(Previewer) wrapper.setProps({ skin: 'abc' }) expect(wrapper.vm.viewer.skinUrl).toBe('abc') }) test('cape URL should be updated', () => { - const wrapper = mount(Previewer) + const wrapper = mount(Previewer) wrapper.setProps({ cape: 'abc' }) expect(wrapper.vm.viewer.capeUrl).toBe('abc') }) @@ -73,22 +84,24 @@ test('toggle pause', () => { }) test('toggle run', () => { - const wrapper = mount(Previewer) + const wrapper = mount(Previewer) wrapper.find('.fa-forward').trigger('click') expect(wrapper.vm.handles.run.paused).toBeFalse() expect(wrapper.vm.handles.walk.paused).toBeTrue() }) test('toggle rotate', () => { - const wrapper = mount(Previewer) + const wrapper = mount(Previewer) wrapper.find('.fa-redo-alt').trigger('click') expect(wrapper.vm.handles.rotate.paused).toBeTrue() }) test('reset', () => { - mockedSkinview3d.SkinViewer.prototype.dispose = jest.fn(function () { - this.disposed = true - }.bind(mockedSkinview3d.SkinViewer)) + mockedSkinview3d.SkinViewer.prototype.dispose = jest.fn( + function (this: mockedSkinview3d.SkinViewer) { + this.disposed = true + }.bind(new mockedSkinview3d.SkinViewer()) + ) const wrapper = mount(Previewer) wrapper.find('.fa-stop').trigger('click') expect(mockedSkinview3d.SkinViewer.prototype.dispose).toBeCalled() diff --git a/resources/assets/tests/components/skinlib/List.test.js b/resources/assets/tests/components/skinlib/List.test.ts similarity index 94% rename from resources/assets/tests/components/skinlib/List.test.js rename to resources/assets/tests/components/skinlib/List.test.ts index ff3c0d61..fbe593ec 100644 --- a/resources/assets/tests/components/skinlib/List.test.js +++ b/resources/assets/tests/components/skinlib/List.test.ts @@ -1,6 +1,6 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' -import List from '@/components/skinlib/List' +import List from '@/components/skinlib/List.vue' test('fetch data before mounting', () => { Vue.prototype.$http.get.mockResolvedValue({ @@ -169,7 +169,7 @@ test('is anonymous', () => { Vue.prototype.$http.get.mockResolvedValue({ items: [], total_pages: 0, current_uid: 0, }) - const wrapper = mount(List) + const wrapper = mount(List) expect(wrapper.vm.anonymous).toBeTrue() }) @@ -177,7 +177,7 @@ test('on page changed', () => { Vue.prototype.$http.get.mockResolvedValue({ items: [], total_pages: 0, current_uid: 0, }) - const wrapper = mount(List) + const wrapper = mount(List) jest.runAllTimers() wrapper.vm.pageChanged(2) expect(Vue.prototype.$http.get).toBeCalledWith( @@ -196,7 +196,10 @@ test('on like toggled', async () => { total_pages: 1, current_uid: 0, }) - const wrapper = mount(List) + const wrapper = mount + }>(List) await wrapper.vm.$nextTick() wrapper.vm.onLikeToggled(0, true) expect(wrapper.vm.items[0].liked).toBeTrue() diff --git a/resources/assets/tests/components/skinlib/Show.test.js b/resources/assets/tests/components/skinlib/Show.test.ts similarity index 93% rename from resources/assets/tests/components/skinlib/Show.test.js rename to resources/assets/tests/components/skinlib/Show.test.ts index 7f427580..ee66c1f3 100644 --- a/resources/assets/tests/components/skinlib/Show.test.js +++ b/resources/assets/tests/components/skinlib/Show.test.ts @@ -1,12 +1,20 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' -import Show from '@/components/skinlib/Show' +import Show from '@/components/skinlib/Show.vue' import toastr from 'toastr' import { flushPromises } from '../../utils' import { swal } from '@/js/notify' jest.mock('@/js/notify') +type Component = Vue & { + liked: boolean + likes: number + public: boolean + name: string + type: 'steve' | 'alex' | 'cape' +} + window.blessing.extra = { download: true, currentUid: 0, @@ -15,12 +23,11 @@ window.blessing.extra = { inCloset: false, } -/** @type {import('Vue').ComponentOptions} */ -const previewer = { +const previewer = Vue.extend({ render(h) { return h('div', this.$slots.footer) }, -} +}) test('button for adding to closet should be disabled if not auth', () => { Vue.prototype.$http.get.mockResolvedValue({}) @@ -165,7 +172,7 @@ test('add to closet', async () => { Vue.prototype.$http.get.mockResolvedValue({ name: 'wow', likes: 2 }) Vue.prototype.$http.post.mockResolvedValue({ errno: 0, msg: '' }) swal.mockResolvedValue({}) - const wrapper = mount(Show, { + const wrapper = mount(Show, { mocks: { $route: ['/skinlib/show/1', '1'], }, @@ -182,7 +189,7 @@ test('remove from closet', async () => { Vue.prototype.$http.get.mockResolvedValue({ likes: 2 }) Vue.prototype.$http.post.mockResolvedValue({ errno: 0 }) swal.mockResolvedValue({}) - const wrapper = mount(Show, { + const wrapper = mount(Show, { mocks: { $route: ['/skinlib/show/1', '1'], }, @@ -201,13 +208,15 @@ test('change texture name', async () => { .mockResolvedValueOnce({ errno: 1, msg: '1' }) .mockResolvedValue({ errno: 0, msg: '0' }) jest.spyOn(toastr, 'warning') - swal.mockImplementationOnce(() => ({ dismiss: 1 })) + swal.mockImplementationOnce(() => Promise.resolve({ dismiss: 1 })) .mockImplementation(({ inputValidator }) => { - inputValidator() - inputValidator('new-name') - return { value: 'new-name' } + if (inputValidator) { + inputValidator('') + inputValidator('new-name') + } + return Promise.resolve({ value: 'new-name' }) }) - const wrapper = mount(Show, { + const wrapper = mount(Show, { mocks: { $route: ['/skinlib/show/1', '1'], }, @@ -239,7 +248,7 @@ test('change texture model', async () => { jest.spyOn(toastr, 'warning') swal.mockResolvedValueOnce({ dismiss: 1 }) .mockResolvedValue({ value: 'alex' }) - const wrapper = mount(Show, { + const wrapper = mount(Show, { mocks: { $route: ['/skinlib/show/1', '1'], }, @@ -272,7 +281,7 @@ test('toggle privacy', async () => { jest.spyOn(toastr, 'warning') swal.mockResolvedValueOnce({ dismiss: 1 }) .mockResolvedValue({}) - const wrapper = mount(Show, { + const wrapper = mount(Show, { mocks: { $route: ['/skinlib/show/1', '1'], }, diff --git a/resources/assets/tests/components/skinlib/SkinLibItem.test.js b/resources/assets/tests/components/skinlib/SkinLibItem.test.ts similarity index 93% rename from resources/assets/tests/components/skinlib/SkinLibItem.test.js rename to resources/assets/tests/components/skinlib/SkinLibItem.test.ts index ea563813..7520e9b4 100644 --- a/resources/assets/tests/components/skinlib/SkinLibItem.test.js +++ b/resources/assets/tests/components/skinlib/SkinLibItem.test.ts @@ -1,6 +1,6 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' -import SkinLibItem from '@/components/skinlib/SkinLibItem' +import SkinLibItem from '@/components/skinlib/SkinLibItem.vue' import toastr from 'toastr' import { flushPromises } from '../../utils' import { swal } from '@/js/notify' @@ -79,13 +79,13 @@ test('add to closet', async () => { Vue.prototype.$http.post .mockResolvedValueOnce({ errno: 1, msg: '1' }) .mockResolvedValue({ errno: 0 }) - swal.mockImplementationOnce(() => ({ dismiss: 1 })) + swal.mockImplementationOnce(() => Promise.resolve({ dismiss: 1 })) .mockImplementation(({ inputValidator }) => { if (inputValidator) { - inputValidator() + inputValidator('') inputValidator('name') } - return { value: 'name' } + return Promise.resolve({ value: 'name' }) }) jest.spyOn(toastr, 'warning') const wrapper = mount(SkinLibItem, { diff --git a/resources/assets/tests/components/skinlib/Upload.test.js b/resources/assets/tests/components/skinlib/Upload.test.ts similarity index 84% rename from resources/assets/tests/components/skinlib/Upload.test.js rename to resources/assets/tests/components/skinlib/Upload.test.ts index afc59549..2edafacb 100644 --- a/resources/assets/tests/components/skinlib/Upload.test.js +++ b/resources/assets/tests/components/skinlib/Upload.test.ts @@ -1,8 +1,7 @@ /* eslint-disable accessor-pairs */ -/* eslint-disable no-invalid-this */ import Vue from 'vue' import { mount } from '@vue/test-utils' -import Upload from '@/components/skinlib/Upload' +import Upload from '@/components/skinlib/Upload.vue' import { flushPromises } from '../../utils' import toastr from 'toastr' import { swal } from '@/js/notify' @@ -32,14 +31,12 @@ test('display drap and drop notice', () => { }) test('button for removing texture', () => { - const wrapper = mount(Upload, { + const wrapper = mount(Upload, { stubs: ['file-upload'], }) - wrapper.vm.$refs = { - upload: { - clear: jest.fn(), - }, - } + Object.defineProperty(wrapper.vm.$refs.upload, 'clear', { + get: () => jest.fn(), + }) const button = wrapper.find('.btn-default') expect(button.isVisible()).toBeFalse() wrapper.setData({ files: [{}] }) @@ -73,15 +70,15 @@ test('display score cost', () => { test('process input file', () => { window.URL.createObjectURL = jest.fn().mockReturnValue('file-url') - jest.spyOn(window, 'Image') - .mockImplementationOnce(function () { + ;(window as Window & { Image: jest.Mock }).Image = jest.fn() + .mockImplementationOnce(function (this: HTMLImageElement) { this.src = '' this.onload = null Object.defineProperty(this, 'onload', { set: fn => fn(), }) }) - .mockImplementationOnce(function () { + .mockImplementationOnce(function (this: HTMLImageElement) { this.src = '' this.width = 500 this.onload = null @@ -90,7 +87,12 @@ test('process input file', () => { }) }) const blob = new Blob() - const wrapper = mount(Upload, { + type Component = Vue & { + name: string + texture: string + inputFile(attrs?: { file: Blob, name: string }): void + } + const wrapper = mount(Upload, { stubs: ['file-upload'], }) @@ -109,18 +111,18 @@ test('process input file', () => { expect(wrapper.vm.texture).toBe('file-url') - window.Image.mockRestore() + ;(window as Window & { Image: jest.Mock }).Image.mockRestore() }) test('upload file', async () => { - window.Request = jest.fn() + (window as Window & { Request: jest.Mock }).Request = jest.fn() Vue.prototype.$http.post .mockResolvedValueOnce({ errno: 1, msg: '1' }) .mockResolvedValueOnce({ errno: 0, msg: '0', tid: 1, }) jest.spyOn(toastr, 'info') - swal.mockReturnValue() + swal.mockReturnValue(Promise.resolve({})) const wrapper = mount(Upload, { stubs: ['file-upload'], diff --git a/resources/assets/tests/components/user/Closet.test.js b/resources/assets/tests/components/user/Closet.test.ts similarity index 91% rename from resources/assets/tests/components/user/Closet.test.js rename to resources/assets/tests/components/user/Closet.test.ts index 0c4c4297..2ee84c6d 100644 --- a/resources/assets/tests/components/user/Closet.test.js +++ b/resources/assets/tests/components/user/Closet.test.ts @@ -1,8 +1,8 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' -import Closet from '@/components/user/Closet' -import ClosetItem from '@/components/user/ClosetItem' -import Previewer from '@/components/common/Previewer' +import Closet from '@/components/user/Closet.vue' +import ClosetItem from '@/components/user/ClosetItem.vue' +import Previewer from '@/components/common/Previewer.vue' import toastr from 'toastr' import { swal } from '@/js/notify' @@ -80,7 +80,7 @@ test('search textures', () => { const wrapper = mount(Closet) const input = wrapper.find('input') - input.element.value = 'q' + ;(input.element as HTMLInputElement).value = 'q' input.trigger('input') jest.runAllTimers() jest.runAllTicks() @@ -127,7 +127,7 @@ test('render items', async () => { test('reload closet when page changed', () => { Vue.prototype.$http.get.mockResolvedValue({}) - const wrapper = mount(Closet) + const wrapper = mount(Closet) wrapper.vm.pageChanged() jest.runAllTicks() expect(Vue.prototype.$http.get).toBeCalledTimes(2) @@ -135,7 +135,7 @@ test('reload closet when page changed', () => { test('remove skin item', () => { Vue.prototype.$http.get.mockResolvedValue({}) - const wrapper = mount(Closet) + const wrapper = mount(Closet) wrapper.setData({ skinItems: [{ tid: 1 }] }) wrapper.vm.removeSkinItem(0) expect(wrapper.find('#skin-category').text()).toContain('user.emptyClosetMsg') @@ -143,7 +143,7 @@ test('remove skin item', () => { test('remove cape item', () => { Vue.prototype.$http.get.mockResolvedValue({}) - const wrapper = mount(Closet) + const wrapper = mount(Closet) wrapper.setData({ capeItems: [{ tid: 1 }], category: 'cape' }) wrapper.vm.removeCapeItem(0) expect(wrapper.find('#cape-category').text()).toContain('user.emptyClosetMsg') @@ -151,7 +151,8 @@ test('remove cape item', () => { test('compute avatar URL', () => { Vue.prototype.$http.get.mockResolvedValue({}) - const wrapper = mount(Closet) + // eslint-disable-next-line camelcase + const wrapper = mount(Closet) const { avatarUrl } = wrapper.vm expect(avatarUrl({ tid_skin: 1 })).toBe('/avatar/35/1') }) @@ -162,7 +163,7 @@ test('select texture', async () => { .mockResolvedValueOnce({ type: 'steve', hash: 'a' }) .mockResolvedValueOnce({ type: 'cape', hash: 'b' }) - const wrapper = mount(Closet) + const wrapper = mount(Closet) wrapper.setData({ skinItems: [{ tid: 1 }] }) wrapper.find(ClosetItem).vm.$emit('select') await wrapper.vm.$nextTick() @@ -181,7 +182,7 @@ test('select texture', async () => { test('apply texture', async () => { window.$ = jest.fn(() => ({ iCheck: () => ({ - on(evt, cb) { + on(_: Event, cb: CallableFunction) { cb() }, }), @@ -282,7 +283,7 @@ test('select specified texture initially', async () => { window.$ = jest.fn(() => ({ modal() {}, iCheck: () => ({ - on(evt, cb) { + on(_: Event, cb: CallableFunction) { cb() }, }), diff --git a/resources/assets/tests/components/user/ClosetItem.test.js b/resources/assets/tests/components/user/ClosetItem.test.ts similarity index 88% rename from resources/assets/tests/components/user/ClosetItem.test.js rename to resources/assets/tests/components/user/ClosetItem.test.ts index ad9808ca..beb0d177 100644 --- a/resources/assets/tests/components/user/ClosetItem.test.js +++ b/resources/assets/tests/components/user/ClosetItem.test.ts @@ -1,7 +1,7 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' import { flushPromises } from '../../utils' -import ClosetItem from '@/components/user/ClosetItem' +import ClosetItem from '@/components/user/ClosetItem.vue' import { swal } from '@/js/notify' jest.mock('@/js/notify') @@ -40,11 +40,13 @@ test('rename texture', async () => { Vue.prototype.$http.post .mockResolvedValueOnce({ errno: 0 }) .mockResolvedValueOnce({ errno: 1 }) - swal.mockImplementationOnce(() => ({ dismiss: 'cancel' })) + swal.mockImplementationOnce(() => Promise.resolve({ dismiss: 1 })) .mockImplementation(options => { - options.inputValidator('name') - options.inputValidator() - return { value: 'new-name' } + if (options.inputValidator) { + options.inputValidator('name') + options.inputValidator('') + } + return Promise.resolve({ value: 'new-name' }) }) const wrapper = mount(ClosetItem, { propsData: factory() }) const button = wrapper.findAll('.dropdown-menu > li').at(0) @@ -71,7 +73,7 @@ test('remove texture', async () => { .mockResolvedValueOnce({ errno: 0 }) .mockResolvedValueOnce({ errno: 1 }) swal - .mockResolvedValueOnce({ dismiss: 'cancel' }) + .mockResolvedValueOnce({ dismiss: 1 }) .mockResolvedValue({}) const wrapper = mount(ClosetItem, { propsData: factory() }) @@ -96,7 +98,7 @@ test('set as avatar', async () => { .mockResolvedValueOnce({ errno: 0 }) .mockResolvedValueOnce({ errno: 1 }) swal - .mockResolvedValueOnce({ dismiss: 'cancel' }) + .mockResolvedValueOnce({ dismiss: 1 }) .mockResolvedValue({}) const wrapper = mount(ClosetItem, { propsData: factory() }) @@ -115,7 +117,7 @@ test('set as avatar', async () => { await flushPromises() await wrapper.vm.$nextTick() expect(Vue.prototype.$http.post).toBeCalledWith('/user/profile/avatar', { tid: 1 }) - expect(document.querySelector('img').src).toMatch(/\d+$/) + expect(document.querySelector('img')!.src).toMatch(/\d+$/) }) test('no avatar option if texture is cape', () => { diff --git a/resources/assets/tests/components/user/Dashboard.test.js b/resources/assets/tests/components/user/Dashboard.test.ts similarity index 98% rename from resources/assets/tests/components/user/Dashboard.test.js rename to resources/assets/tests/components/user/Dashboard.test.ts index 3f6ec812..1aaaacf1 100644 --- a/resources/assets/tests/components/user/Dashboard.test.js +++ b/resources/assets/tests/components/user/Dashboard.test.ts @@ -1,7 +1,7 @@ /* eslint-disable no-mixed-operators */ import Vue from 'vue' import { mount } from '@vue/test-utils' -import Dashboard from '@/components/user/Dashboard' +import Dashboard from '@/components/user/Dashboard.vue' import toastr from 'toastr' import { swal } from '@/js/notify' @@ -125,7 +125,7 @@ test('remaining time', async () => { test('sign', async () => { jest.spyOn(toastr, 'warning') - swal.mockResolvedValue() + swal.mockResolvedValue({}) Vue.prototype.$http.get.mockResolvedValue(scoreInfo({ user: { lastSignAt: Date.now() - 30 * 3600 * 1000 }, })) diff --git a/resources/assets/tests/components/user/EmailVerification.test.js b/resources/assets/tests/components/user/EmailVerification.test.ts similarity index 99% rename from resources/assets/tests/components/user/EmailVerification.test.js rename to resources/assets/tests/components/user/EmailVerification.test.ts index a66e8a53..35b6494a 100644 --- a/resources/assets/tests/components/user/EmailVerification.test.js +++ b/resources/assets/tests/components/user/EmailVerification.test.ts @@ -1,6 +1,6 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' -import EmailVerification from '@/components/user/EmailVerification' +import EmailVerification from '@/components/user/EmailVerification.vue' import { swal } from '@/js/notify' jest.mock('@/js/notify') diff --git a/resources/assets/tests/components/user/Players.test.js b/resources/assets/tests/components/user/Players.test.ts similarity index 95% rename from resources/assets/tests/components/user/Players.test.js rename to resources/assets/tests/components/user/Players.test.ts index 2b838468..c1ccd963 100644 --- a/resources/assets/tests/components/user/Players.test.js +++ b/resources/assets/tests/components/user/Players.test.ts @@ -1,7 +1,7 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' import { flushPromises } from '../../utils' -import Players from '@/components/user/Players' +import Players from '@/components/user/Players.vue' import { swal } from '@/js/notify' jest.mock('toastr') @@ -67,13 +67,13 @@ test('change player name', async () => { Vue.prototype.$http.post .mockResolvedValueOnce({ errno: 1 }) .mockResolvedValue({ errno: 0 }) - swal.mockImplementationOnce(() => ({ dismiss: 1 })) + swal.mockImplementationOnce(() => Promise.resolve({ dismiss: 1 })) .mockImplementation(({ inputValidator }) => { if (inputValidator) { - inputValidator() + inputValidator('') inputValidator('new-name') - return { value: 'new-name' } } + return Promise.resolve({ value: 'new-name' }) }) const wrapper = mount(Players) await wrapper.vm.$nextTick() @@ -100,7 +100,7 @@ test('load iCheck', async () => { ]) window.$ = jest.fn(() => ({ iCheck: () => ({ - on(evt, cb) { + on(_: Event, cb: CallableFunction) { cb() }, }), diff --git a/resources/assets/tests/components/user/Profile.test.js b/resources/assets/tests/components/user/Profile.test.ts similarity index 92% rename from resources/assets/tests/components/user/Profile.test.js rename to resources/assets/tests/components/user/Profile.test.ts index 535eb166..25fb5247 100644 --- a/resources/assets/tests/components/user/Profile.test.js +++ b/resources/assets/tests/components/user/Profile.test.ts @@ -1,7 +1,7 @@ import Vue from 'vue' import { mount } from '@vue/test-utils' import { flushPromises } from '../../utils' -import Profile from '@/components/user/Profile' +import Profile from '@/components/user/Profile.vue' import toastr from 'toastr' import { swal } from '@/js/notify' @@ -11,15 +11,15 @@ window.blessing.extra = { unverified: false } test('computed values', () => { window.blessing.extra = { admin: true } - const wrapper = mount(Profile) + const wrapper = mount(Profile) expect(wrapper.vm.siteName).toBe('Blessing Skin') expect(wrapper.vm.isAdmin).toBeTrue() window.blessing.extra = { admin: false } - expect(mount(Profile).vm.isAdmin).toBeFalse() + expect(mount(Profile).vm.isAdmin).toBeFalse() }) test('convert linebreak', () => { - const wrapper = mount(Profile) + const wrapper = mount(Profile) expect(wrapper.vm.nl2br('a\nb\nc')).toBe('a
b
c') }) @@ -44,7 +44,7 @@ test('reset avatar', async () => { ) await flushPromises() expect(toastr.success).toBeCalledWith('ok') - expect(document.querySelector('img').src).toMatch(/\d+$/) + expect(document.querySelector('img')!.src).toMatch(/\d+$/) }) test('change password', async () => { @@ -52,7 +52,7 @@ test('change password', async () => { Vue.prototype.$http.post .mockResolvedValueOnce({ errno: 1, msg: 'w' }) .mockResolvedValueOnce({ errno: 0, msg: 'o' }) - swal.mockResolvedValue() + swal.mockResolvedValue({}) const wrapper = mount(Profile) const button = wrapper.find('[data-test=changePassword]') @@ -125,7 +125,7 @@ test('change nickname', async () => { button.trigger('click') await flushPromises() expect(swal).toBeCalledWith({ type: 'success', text: 'o' }) - expect(document.querySelector('.nickname').textContent).toBe('nickname') + expect(document.querySelector('.nickname')!.textContent).toBe('nickname') }) test('change email', async () => { @@ -173,7 +173,7 @@ test('change email', async () => { test('delete account', async () => { window.blessing.extra = { admin: true } - swal.mockResolvedValue() + swal.mockResolvedValue({}) Vue.prototype.$http.post .mockResolvedValueOnce({ errno: 1, msg: 'w' }) .mockResolvedValue({ errno: 0, msg: 'o' }) diff --git a/resources/assets/tests/js/types.d.ts b/resources/assets/tests/js/types.d.ts index be749566..e0e404d5 100644 --- a/resources/assets/tests/js/types.d.ts +++ b/resources/assets/tests/js/types.d.ts @@ -9,7 +9,10 @@ interface Window { blessing: { i18n: object + extra: object } fetch: jest.Mock + + $: jest.Mock } diff --git a/resources/assets/tests/vue.d.ts b/resources/assets/tests/vue.d.ts new file mode 100644 index 00000000..5596f90e --- /dev/null +++ b/resources/assets/tests/vue.d.ts @@ -0,0 +1,5 @@ +declare module '*.vue' { + import Vue from 'vue' + + export default Vue +} diff --git a/tsconfig.json b/tsconfig.json index 48a228c3..3d8fb613 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,9 @@ "@/js/*": [ "./resources/assets/tests/ts-shims/*", "./resources/assets/src/js/*" + ], + "@/components/*": [ + "./resources/assets/src/components/*" ] } },