diff --git a/resources/assets/src/js/__tests__/.editorconfig b/resources/assets/src/js/__tests__/.editorconfig deleted file mode 100644 index 0382a6f8..00000000 --- a/resources/assets/src/js/__tests__/.editorconfig +++ /dev/null @@ -1,4 +0,0 @@ -# http://editorconfig.org - -[*.js] -indent_size = 2 diff --git a/resources/assets/src/js/__tests__/admin.test.js b/resources/assets/src/js/__tests__/admin.test.js deleted file mode 100644 index 7b567805..00000000 --- a/resources/assets/src/js/__tests__/admin.test.js +++ /dev/null @@ -1,1202 +0,0 @@ -/* eslint no-unused-vars: "off" */ - -const $ = require('jquery'); -window.$ = window.jQuery = $; - -jest.useFakeTimers(); - -describe('tests for "customize" module', () => { - const modulePath = '../admin/customize'; - - it('change skin preview after switching color', () => { - window.current_skin = 'skin-blue'; - window.showAjaxError = jest.fn(); - document.body.className = window.current_skin; - document.body.innerHTML = ` -
- -
`; - - require(modulePath); - $('#layout-skins-list [data-skin]').click(); - - expect($('body').hasClass('skin-blue')).toBe(false); - expect($('body').hasClass('skin-purple')).toBe(true); - expect(window.current_skin).toBe('skin-purple'); - }); - - it('submit information of skin', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) - .mockReturnValueOnce(Promise.reject(new Error)); - const url = jest.fn(path => path); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - window.fetch = fetch; - window.url = url; - window.toastr = toastr; - window.current_skin = ''; - window.showAjaxError = jest.fn(); - - document.body.innerHTML = ''; - const submitColor = require(modulePath); - - await submitColor(); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/customize?action=color', - dataType: 'json', - data: { color_scheme: '' } - }); - expect(toastr.warning).not.toBeCalled(); - expect(toastr.success).toBeCalledWith('success'); - - await submitColor(); - expect(toastr.warning).toBeCalledWith('warning'); - - await submitColor(); - expect(window.showAjaxError).toBeCalled(); - }); -}); - -describe('tests for "players" module', () => { - const modulePath = '../admin/players'; - - // TODO: test initializing players table - - it('show "change player texture" modal dialog', () => { - const trans = jest.fn(key => key); - const showModal = jest.fn(); - window.trans = trans; - window.showModal = showModal; - - const changeTexture = require(modulePath).changeTexture; - changeTexture(1, 'name'); - const args = showModal.mock.calls[0]; - expect(args.includes('admin.changePlayerTexture')).toBe(true); - expect(args.includes('default')).toBe(true); - }); - - it('change player preference', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) - .mockReturnValueOnce(Promise.reject(new Error)); - const url = jest.fn(path => path); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - window.fetch = fetch; - window.url = url; - window.toastr = toastr; - window.showAjaxError = jest.fn(); - - document.body.innerHTML = ` -
-
-
- `; - $('select').on('change', require(modulePath).changePreference); - - await $('select').val('slim').trigger('change'); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/players?action=preference', - dataType: 'json', - data: { - pid: '1', - preference: 'slim' - } - }); - expect(toastr.warning).not.toBeCalled(); - expect(toastr.success).toBeCalledWith('success'); - - await $('select').trigger('change'); - expect(toastr.warning).toBeCalledWith('warning'); - - await $('select').trigger('change'); - expect(window.showAjaxError).toBeCalled(); - }); - - it('submit changed texture information', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) - .mockReturnValueOnce(Promise.reject()); - const url = jest.fn(path => path); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - const modal = jest.fn(); - const showAjaxError = jest.fn(); - window.fetch = fetch; - window.url = url; - window.toastr = toastr; - window.$.fn.modal = modal; - window.showAjaxError = showAjaxError; - - document.body.innerHTML = ` - - - - - - `; - const ajaxChangeTexture = require(modulePath).ajaxChangeTexture; - - await ajaxChangeTexture(1); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/players?action=texture', - dataType: 'json', - data: { pid: 1, model: 'default', tid: '1' } - }); - expect(document.getElementById('shouldBeRemoved')).toBeNull(); - expect(document.getElementById('shouldNotBeRemoved')).not.toBeNull(); - expect(modal).toBeCalledWith('hide'); - expect(toastr.warning).not.toBeCalled(); - expect(toastr.success).toBeCalledWith('success'); - expect($('img').attr('src')).toBe('preview/64/1.png'); - - await ajaxChangeTexture(1); - expect(toastr.warning).toBeCalledWith('warning'); - - await ajaxChangeTexture(1); - expect(showAjaxError).toBeCalled(); - }); - - it('change player name', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) - .mockReturnValueOnce(Promise.reject()); - const url = jest.fn(path => path); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - const trans = jest.fn(key => key); - const swal = jest.fn() - .mockImplementationOnce(() => Promise.reject()) - .mockImplementationOnce(options => { - options.inputValidator('newName'); - return Promise.resolve('newName'); - }); - const showAjaxError = jest.fn(); - window.fetch = fetch; - window.url = url; - window.toastr = toastr; - window.trans = trans; - window.swal = swal; - window.showAjaxError = showAjaxError; - - document.body.innerHTML = ` - - - - - - - - - -
- `; - const changePlayerName = require(modulePath).changePlayerName; - - await changePlayerName(1, 'oldName'); - expect(fetch).not.toBeCalled(); - - await changePlayerName(1, 'oldName'); - expect(swal).toBeCalledWith(expect.objectContaining({ - text: 'admin.changePlayerNameNotice', - input: 'text', - inputValue: 'oldName' - })); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/players?action=name', - dataType: 'json', - data: { pid: 1, name: 'newName' } - }); - expect($('tr#1 > td:nth-child(3)').text()).toBe('newName'); - expect(toastr.warning).not.toBeCalled(); - expect(toastr.success).toBeCalledWith('success'); - - await changePlayerName(1, 'oldName'); - expect(toastr.warning).toBeCalledWith('warning'); - - await changePlayerName(1, 'oldName'); - expect(showAjaxError).toBeCalled(); - }); - - it('change owner', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) - .mockReturnValueOnce(Promise.reject(new Error)); - const url = jest.fn(path => path); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - const trans = jest.fn(key => key); - const swal = jest.fn().mockReturnValue(Promise.resolve(2)); - const debounce = jest.fn(fn => fn); - const showAjaxError = jest.fn(); - window.fetch = fetch; - window.url = url; - window.toastr = toastr; - window.trans = trans; - window.swal = swal; - window.debounce = debounce; - window.showAjaxError = showAjaxError; - - document.body.innerHTML = ` - - - - - - - - - -
1
-
- -
-
- `; - const changeOwner = require(modulePath).changeOwner; - - await changeOwner(1, 'oldName'); - expect(debounce).toBeCalled(); - expect(swal).toBeCalledWith({ - html: 'admin.changePlayerOwner
 ', - input: 'number', - inputValue: '1', - showCancelButton: true - }); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/players?action=owner', - dataType: 'json', - data: { pid: 1, uid: 2 } - }); - await changeOwner(1, 'oldName'); - expect($('tr#1 > td:nth-child(2)').text()).toBe('2'); - expect(toastr.warning).not.toBeCalled(); - expect(toastr.success).toBeCalledWith('success'); - - await changeOwner(1, 'oldName'); - expect(toastr.warning).toBeCalledWith('warning'); - - await changeOwner(1, 'oldName'); - expect(showAjaxError).toBeCalled(); - }); - - it('show nickname in swal', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ user: { nickname: 'name' } })) - .mockReturnValueOnce(Promise.reject()); - const url = jest.fn(path => path); - const trans = jest.fn(key => key); - window.fetch = fetch; - window.url = url; - window.trans = trans; - - document.body.innerHTML = ` - -
- `; - const { showNicknameInSwal } = require(modulePath); - - await showNicknameInSwal(); - expect(fetch).not.toBeCalled(); - - $('input').val('-8'); - await showNicknameInSwal(); - expect(fetch).not.toBeCalled(); - - $('input').val('8'); - await showNicknameInSwal(); - expect(fetch).toBeCalledWith({ - type: 'GET', - url: 'admin/user/8', - dataType: 'json' - }); - expect($('div').html().includes('name')); - - await showNicknameInSwal(); - expect($('div').html().includes('admin.noSuchUser')); - }); - - it('delete player', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) - .mockReturnValueOnce(Promise.reject()); - const url = jest.fn(path => path); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - const trans = jest.fn(key => key); - const swal = jest.fn() - .mockReturnValueOnce(Promise.reject()) - .mockReturnValueOnce(Promise.resolve()); - const showAjaxError = jest.fn(); - window.fetch = fetch; - window.url = url; - window.toastr = toastr; - window.trans = trans; - window.swal = swal; - window.showAjaxError = showAjaxError; - - document.body.innerHTML = ` - - - - - - -
- `; - const deletePlayer = require(modulePath).deletePlayer; - - await deletePlayer(1); - expect(fetch).not.toBeCalled(); - - await deletePlayer(1); - expect(swal).toBeCalledWith({ - text: 'admin.deletePlayerNotice', - type: 'warning', - showCancelButton: true - }); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/players?action=delete', - dataType: 'json', - data: { pid: 1 } - }); - expect(document.getElementById('1')).toBeNull(); - expect(toastr.warning).not.toBeCalled(); - expect(toastr.success).toBeCalledWith('success'); - - await deletePlayer(1); - expect(toastr.warning).toBeCalledWith('warning'); - - await deletePlayer(1); - expect(showAjaxError).toBeCalled(); - }); -}); - -describe('tests for "plugins" module', () => { - const modulePath = '../admin/plugins'; - - // TODO: test initializing plugins table - - it('enable a plugin', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ requirements: [] })) - .mockReturnValueOnce(Promise.resolve({ requirements: [] })) - .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ - isRequirementsSatisfied: false, - requirements: { 'a': '^1.1.0', 'b': '^2.1.0', 'c': '^3.3.0' }, - unsatisfiedRequirements: { 'c': '^3.3.0' } - })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'notice', reason: ['reason1', 'reason2'] })) - .mockReturnValueOnce(Promise.reject()); - const url = jest.fn(path => path); - const swal = jest.fn() - .mockReturnValueOnce(Promise.reject()) - .mockReturnValueOnce(Promise.resolve()); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - const reloadTable = jest.fn(); - const showAjaxError = jest.fn(); - window.fetch = fetch; - window.url = url; - window.swal = swal; - window.toastr = toastr; - window.showAjaxError = showAjaxError; - $.pluginsTable = { - ajax: { - reload: reloadTable - } - }; - - const enablePlugin = require(modulePath).enablePlugin; - - await enablePlugin('plugin'); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/plugins/manage?action=requirements&name=plugin', - dataType: 'json' - }); - expect(swal).toBeCalledWith({ - text: 'admin.noDependenciesNotice', - type: 'warning', - showCancelButton: true - }); - expect(fetch.mock.calls.length).toBe(1); - - await enablePlugin('plugin'); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/plugins/manage?action=enable&name=plugin', - dataType: 'json' - }); - expect(swal).not.toBeCalledWith({ type: 'warning' }); - expect(toastr.success).toBeCalledWith('success'); - expect(reloadTable).toBeCalledWith(null, false); - - await enablePlugin('plugin'); - expect(swal).toBeCalledWith({ type: 'warning', html: '

notice

' }); - - await enablePlugin('plugin'); - expect(showAjaxError).toBeCalled(); - }); - - it('disable a plugin', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) - .mockReturnValueOnce(Promise.reject()); - const url = jest.fn(path => path); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - const reloadTable = jest.fn(); - const showAjaxError = jest.fn(); - window.fetch = fetch; - window.url = url; - window.toastr = toastr; - window.showAjaxError = showAjaxError; - $.pluginsTable = { - ajax: { - reload: reloadTable - } - }; - - const disablePlugin = require(modulePath).disablePlugin; - - await disablePlugin('plugin'); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/plugins/manage?action=disable&name=plugin', - dataType: 'json' - }); - expect(swal).not.toBeCalledWith({ type: 'warning' }); - expect(toastr.success).toBeCalledWith('success'); - expect(reloadTable).toBeCalledWith(null, false); - - await disablePlugin('plugin'); - expect(swal).toBeCalledWith({ type: 'warning', html: 'warning' }); - - await disablePlugin('plugin'); - expect(showAjaxError).toBeCalled(); - }); - - it('delete a plugin', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) - .mockReturnValueOnce(Promise.reject()); - const url = jest.fn(path => path); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - const reloadTable = jest.fn(); - const swal = jest.fn() - .mockReturnValueOnce(Promise.reject()) - .mockReturnValueOnce(Promise.resolve()); - const showAjaxError = jest.fn(); - window.fetch = fetch; - window.url = url; - window.toastr = toastr; - window.showAjaxError = showAjaxError; - $.pluginsTable = { - ajax: { - reload: reloadTable - } - }; - window.swal = swal; - - const deletePlugin = require(modulePath).deletePlugin; - - await deletePlugin('plugin'); - expect(fetch).not.toBeCalled(); - - await deletePlugin('plugin'); - expect(swal).toBeCalledWith({ - text: 'admin.confirmDeletion', - type: 'warning', - showCancelButton: true - }); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/plugins/manage?action=delete&name=plugin', - dataType: 'json' - }); - expect(swal).not.toBeCalledWith({ type: 'warning' }); - expect(toastr.success).toBeCalledWith('success'); - expect(reloadTable).toBeCalledWith(null, false); - - await deletePlugin('plugin'); - expect(swal).toBeCalledWith({ type: 'warning', html: 'warning' }); - - await deletePlugin('plugin'); - expect(showAjaxError).toBeCalled(); - }); -}); - -describe('tests for "update" module', () => { - const modulePath = '../admin/update'; - - it('download updates', async () => { - const fetch = jest.fn() - .mockImplementationOnce(({ beforeSend }) => { - beforeSend && beforeSend(); - return Promise.resolve({ - file_size: 5000 - }); - }) - .mockImplementationOnce(() => Promise.resolve()) - .mockImplementationOnce(() => Promise.resolve({ msg: 'ok' })) - .mockImplementationOnce(() => Promise.reject()) - .mockImplementationOnce(({ beforeSend }) => { - beforeSend && beforeSend(); - return Promise.resolve({ - file_size: 5000 - }); - }) - .mockImplementationOnce(() => Promise.resolve()) - .mockImplementationOnce(() => Promise.resolve({ msg: 'ok' })); - const url = jest.fn(path => path); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - const modal = jest.fn(); - const swal = jest.fn() - .mockReturnValueOnce(Promise.resolve()) - .mockReturnValueOnce(Promise.reject()); - window.fetch = fetch; - window.url = url; - window.toastr = toastr; - window.swal = swal; - window.showAjaxError = jest.fn(); - $.fn.modal = modal; - - document.body.innerHTML = ` -
- - - - - `; - - const downloadUpdates = require(modulePath).downloadUpdates; - - await downloadUpdates(); - expect(fetch).toBeCalledWith(expect.objectContaining({ - url: 'admin/update/download?action=prepare-download', - type: 'GET', - dataType: 'json', - })); - expect($('#update-button').prop('disabled')).toBe(true); - expect($('#file-size').html()).toBe('5000'); - expect(modal).toBeCalledWith({ - backdrop: 'static', - keyboard: false - }); - expect(fetch).toBeCalledWith({ - url: 'admin/update/download?action=start-download', - type: 'POST', - dataType: 'json' - }); - expect($('.modal-title').html().includes('admin.extracting')).toBe(true); - expect($('.modal-body').html().includes('admin.downloadCompleted')).toBe(true); - expect(swal).toBeCalledWith({ type: 'success', html: 'ok' }); - expect(url).toBeCalledWith('/'); - - await downloadUpdates(); - expect(window.showAjaxError).toBeCalled(); - - await downloadUpdates(); - expect(url).toBeCalledWith('/'); - }); - - it('download progress polling', async () => { - const fetch = jest.fn().mockReturnValueOnce(Promise.resolve({ size: 50 })); - const url = jest.fn(path => path); - window.fetch = fetch; - window.url = url; - - document.body.innerHTML = ` -
-
- `; - - const { progressPolling } = require(modulePath); - await progressPolling(100)(); - expect(fetch).toBeCalledWith({ - url: 'admin/update/download?action=get-file-size', - type: 'GET' - }); - expect($('#imported-progress').html()).toBe('50.00'); - expect($('.progress-bar').css('width')).toBe('50%'); - expect($('.progress-bar').attr('aria-valuenow')).toBe('50.00'); - }); - - it('check for updates', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ - available: false, - latest: '1.1.4' - })) - .mockReturnValueOnce(Promise.resolve({ - available: true, - latest: '5.1.4' - })); - const url = jest.fn(path => path); - - window.fetch = fetch; - window.url = url; - - document.body.innerHTML = ` - Check Update - `; - - const checkForUpdates = require(modulePath).checkForUpdates; - - await checkForUpdates(); - expect($('#target').html()).toBe( - ' Check Update' - ); - - await checkForUpdates(); - expect($('#target').html()).toBe( - ' Check Update'+ - 'v5.1.4' - ); - }); -}); - -describe('tests for "users" module', () => { - const modulePath = '../admin/users'; - - // TODO: test initializing users table - - it('change user email', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) - .mockReturnValueOnce(Promise.reject()); - const url = jest.fn(path => path); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - const trans = jest.fn(key => key); - const swal = jest.fn() - .mockImplementationOnce(() => Promise.reject()) - .mockImplementationOnce(options => { - options.inputValidator('a@b.c'); - return Promise.resolve('a@b.c'); - }); - const showAjaxError = jest.fn(); - window.fetch = fetch; - window.url = url; - window.toastr = toastr; - window.trans = trans; - window.swal = swal; - window.showAjaxError = showAjaxError; - - document.body.innerHTML = ` - - - - - - - -
d@e.f
- `; - const changeUserEmail = require(modulePath).changeUserEmail; - - await changeUserEmail(1); - expect(fetch).not.toBeCalled(); - - await changeUserEmail(1); - expect(swal).toBeCalledWith(expect.objectContaining({ - text: 'admin.newUserEmail', - showCancelButton: true, - input: 'text', - inputValue: 'd@e.f' - })); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/users?action=email', - dataType: 'json', - data: { uid: 1, email: 'a@b.c' } - }); - expect($('tr > td:nth-child(2)').text()).toBe('a@b.c'); - expect(toastr.success).toBeCalledWith('success'); - - await changeUserEmail(1); - expect(toastr.warning).toBeCalledWith('warning'); - - await changeUserEmail(1); - expect(showAjaxError).toBeCalled(); - }); - - it('change user nick name', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) - .mockReturnValueOnce(Promise.reject()); - const url = jest.fn(path => path); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - const trans = jest.fn(key => key); - const swal = jest.fn() - .mockImplementationOnce(() => Promise.reject()) - .mockImplementationOnce(options => { - options.inputValidator('foo'); - return Promise.resolve('foo'); - }); - const showAjaxError = jest.fn(); - window.fetch = fetch; - window.url = url; - window.toastr = toastr; - window.trans = trans; - window.swal = swal; - window.showAjaxError = showAjaxError; - - document.body.innerHTML = ` - - - - - - - - -
hhh
- `; - const changeUserNickName = require(modulePath).changeUserNickName; - - await changeUserNickName(1); - expect(fetch).not.toBeCalled(); - - await changeUserNickName(1); - expect(swal).toBeCalledWith(expect.objectContaining({ - text: 'admin.newUserNickname', - showCancelButton: true, - input: 'text', - inputValue: 'hhh' - })); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/users?action=nickname', - dataType: 'json', - data: { uid: 1, nickname: 'foo' } - }); - expect($('tr > td:nth-child(3)').text()).toBe('foo'); - expect(toastr.success).toBeCalledWith('success'); - - await changeUserNickName(1); - expect(toastr.warning).toBeCalledWith('warning'); - - await changeUserNickName(1); - expect(showAjaxError).toBeCalled(); - }); - - it('change user password', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) - .mockReturnValueOnce(Promise.reject()); - const url = jest.fn(path => path); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - const trans = jest.fn(key => key); - const swal = jest.fn() - .mockReturnValueOnce(Promise.reject()) - .mockReturnValueOnce(Promise.resolve('secret')); - const showAjaxError = jest.fn(); - window.fetch = fetch; - window.url = url; - window.toastr = toastr; - window.trans = trans; - window.swal = swal; - window.showAjaxError = showAjaxError; - - const changeUserPwd = require(modulePath).changeUserPwd; - - await changeUserPwd(1); - expect(fetch).not.toBeCalled(); - - await changeUserPwd(1); - expect(swal).toBeCalledWith(expect.objectContaining({ - text: 'admin.newUserPassword', - showCancelButton: true, - input: 'password', - })); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/users?action=password', - dataType: 'json', - data: { uid: 1, password: 'secret' } - }); - expect(toastr.success).toBeCalledWith('success'); - - await changeUserPwd(1); - expect(toastr.warning).toBeCalledWith('warning'); - - await changeUserPwd(1); - expect(showAjaxError).toBeCalled(); - }); - - it('change user score', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) - .mockReturnValueOnce(Promise.reject()); - const url = jest.fn(path => path); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - const showAjaxError = jest.fn(); - window.fetch = fetch; - window.url = url; - window.toastr = toastr; - window.showAjaxError = showAjaxError; - - document.body.innerHTML = ` - - - - - - - - -
- `; - const changeUserScore = require(modulePath).changeUserScore; - - await changeUserScore('user-1', 50); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/users?action=score', - dataType: 'json', - data: { uid: '1', score: 50 } - }); - expect(toastr.warning).not.toBeCalled(); - expect(toastr.success).toBeCalledWith('success'); - - await changeUserScore('user-1', 50); - expect(toastr.warning).toBeCalledWith('warning'); - - await changeUserScore('user-1', 50); - expect(showAjaxError).toBeCalled(); - }); - - it('change ban status', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ - errno: 0, - msg: 'success', - permission: 0 - })) - .mockReturnValueOnce(Promise.resolve({ - errno: 0, - msg: 'success', - permission: -1 - })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) - .mockReturnValueOnce(Promise.reject()); - const url = jest.fn(path => path); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - const trans = jest.fn(key => key); - const showAjaxError = jest.fn(); - window.fetch = fetch; - window.url = url; - window.toastr = toastr; - window.trans = trans; - window.showAjaxError = showAjaxError; - - document.body.innerHTML = ` - - - - - - - -
- `; - await require(modulePath).changeBanStatus(1); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/users?action=ban', - dataType: 'json', - data: { uid: 1 } - }); - expect($('#ban-1').attr('data')).toBe('normal'); - expect($('#ban-1').text()).toBe('admin.ban'); - expect($('.status').text()).toBe('admin.normal'); - expect(toastr.warning).not.toBeCalled(); - expect(toastr.success).toBeCalledWith('success'); - - document.body.innerHTML = ` - - - - - - - -
- `; - await require(modulePath).changeBanStatus(1); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/users?action=ban', - dataType: 'json', - data: { uid: 1 } - }); - expect($('#ban-1').attr('data')).toBe('banned'); - expect($('#ban-1').text()).toBe('admin.unban'); - expect($('.status').text()).toBe('admin.banned'); - - await require(modulePath).changeBanStatus(1); - expect(toastr.warning).toBeCalledWith('warning'); - - await require(modulePath).changeBanStatus(1); - expect(showAjaxError).toBeCalled(); - }); - - it('change admin status', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ - errno: 0, - msg: 'success', - permission: 0 - })) - .mockReturnValueOnce(Promise.resolve({ - errno: 0, - msg: 'success', - permission: 1 - })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) - .mockReturnValueOnce(Promise.reject()); - const url = jest.fn(path => path); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - const trans = jest.fn(key => key); - const showAjaxError = jest.fn(); - window.fetch = fetch; - window.url = url; - window.toastr = toastr; - window.trans = trans; - window.showAjaxError = showAjaxError; - - document.body.innerHTML = ` - - - - - - - -
- `; - await require(modulePath).changeAdminStatus(1); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/users?action=admin', - dataType: 'json', - data: { uid: 1 } - }); - expect($('#admin-1').attr('data')).toBe('normal'); - expect($('#admin-1').text()).toBe('admin.setAdmin'); - expect($('.status').text()).toBe('admin.normal'); - expect(toastr.warning).not.toBeCalled(); - expect(toastr.success).toBeCalledWith('success'); - - document.body.innerHTML = ` - - - - - - - -
- `; - await require(modulePath).changeAdminStatus(1); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/users?action=admin', - dataType: 'json', - data: { uid: 1 } - }); - expect($('#admin-1').attr('data')).toBe('admin'); - expect($('#admin-1').text()).toBe('admin.unsetAdmin'); - expect($('.status').text()).toBe('admin.admin'); - - await require(modulePath).changeAdminStatus(1); - expect(toastr.warning).toBeCalledWith('warning'); - - await require(modulePath).changeAdminStatus(1); - expect(showAjaxError).toBeCalled(); - }); - - it('delete a user', async () => { - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) - .mockReturnValueOnce(Promise.reject()); - const url = jest.fn(path => path); - const toastr = { - success: jest.fn(), - warning: jest.fn() - }; - const trans = jest.fn(key => key); - const swal = jest.fn() - .mockReturnValueOnce(Promise.reject()) - .mockReturnValueOnce(Promise.resolve()); - const showAjaxError = jest.fn(); - window.fetch = fetch; - window.url = url; - window.toastr = toastr; - window.trans = trans; - window.swal = swal; - window.showAjaxError = showAjaxError; - - document.body.innerHTML = ''; - const deleteUserAccount = require(modulePath).deleteUserAccount; - - await deleteUserAccount(1); - expect(fetch).not.toBeCalled(); - - await deleteUserAccount(1); - expect(swal).toBeCalledWith({ - text: 'admin.deleteUserNotice', - type: 'warning', - showCancelButton: true - }); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'admin/users?action=delete', - dataType: 'json', - data: { uid: 1 } - }); - expect(document.getElementById('user-1')).toBeNull(); - - await deleteUserAccount(1); - expect(toastr.warning).toBeCalledWith('warning'); - - await deleteUserAccount(1); - expect(showAjaxError).toBeCalled(); - }); - - it('"input" element should be focused out when press enter key', () => { - document.body.innerHTML = ` -
-
- -
-
- `; - - require(modulePath); - - $('.score').focus(); - const event = $.Event('keypress'); - event.which = 13; - $('.score').trigger(event); - - expect($('.score').is(':focus')).toBe(false); - }); -}); - -describe('tests for "common" module', () => { - const modulePath = '../admin/common'; - - it('send feedbacks', async () => { - const fetch = jest.fn() - .mockReturnValue(Promise.resolve({ errno: 0, msg: 'Recorded.' })); - - $.fn.dataTable = { defaults: {}, ext: { errMode: '' } }; - window.document.cookie = ''; - window.fetch = fetch; - window.blessing = { - site_name: 'inm', - base_url: 'http://tdkr.mur', - version: '8.1.0' - }; - - const { sendFeedback } = require(modulePath); - - await sendFeedback(); - expect(fetch).toBeCalledWith({ - url: 'https://work.prinzeugen.net/statistics/feedback', - type: 'POST', - dataType: 'json', - data: { site_name: 'inm', site_url: 'http://tdkr.mur', version: '8.1.0' } - }); - expect(window.document.cookie).not.toBe(''); - - await sendFeedback(); - expect(fetch).toHaveBeenCalledTimes(1); - }); - - it('handle DataTables AJAX error', () => { - const { handleDataTablesAjaxError } = require(modulePath); - const showModal = jest.fn(); - window.trans = jest.fn(t => t); - window.showModal = showModal; - $.fn.dataTable = { defaults: {}, ext: { errMode: '' } }; - - handleDataTablesAjaxError(undefined, undefined, '{}'); - expect(showModal).not.toBeCalled(); - - handleDataTablesAjaxError(undefined, undefined, null, { - responseText: 'error' - }); - expect(showModal).toBeCalledWith( - 'error', - 'general.fatalError', - 'danger' - ); - }); -}); diff --git a/resources/assets/src/js/__tests__/common.test.js b/resources/assets/src/js/__tests__/common.test.js deleted file mode 100644 index f83412b4..00000000 --- a/resources/assets/src/js/__tests__/common.test.js +++ /dev/null @@ -1,244 +0,0 @@ -/* eslint no-unused-vars: "off" */ - -const $ = require('jquery'); -window.jQuery = window.$ = $; - -describe('tests for "i18n" module', () => { - const modulePath = '../common/i18n'; - - it('load locales', () => { - window.isEmpty = obj => !obj; // Just for test - const loadLocales = require(modulePath).loadLocales; - - $.locales = { - en: { text: 'text', nested: { sth: ':sth here!' } } - }; - - loadLocales(); - expect($.currentLocale).toEqual({ text: 'text', nested: { sth: ':sth here!' } }); - }); - - it('get translated text', () => { - const trans = require(modulePath).trans; - - $.currentLocale = undefined; // Should load locale automatically - $.locales = { - en: { text: 'text', nested: { sth: ':sth here!' } } - }; - - expect(trans('text')).toBe('text'); - expect(trans('text.nothing')).toBe('text.nothing'); - expect(trans('nested.sth')).toBe(':sth here!'); - expect(trans('nested.sth', { sth: 'abc' })).toBe('abc here!'); - }); -}); - -jest.useFakeTimers(); - -describe('tests for "logout" module', () => { - const modulePath = '../common/logout'; - - it('logout', async () => { - const swal = jest.fn() - .mockReturnValueOnce(Promise.reject()) - .mockReturnValueOnce(Promise.resolve()); - const trans = jest.fn(key => key); - const fetch = jest.fn() - .mockReturnValueOnce(Promise.resolve({ msg: 'success' })) - .mockReturnValueOnce(Promise.reject()); - const showAjaxError = jest.fn(); - window.swal = swal; - window.trans = trans; - window.fetch = fetch; - window.url = jest.fn(path => path); - window.showAjaxError = showAjaxError; - - document.body.innerHTML = ''; - require(modulePath); - - await $('button').click(); - expect(fetch).not.toBeCalled(); - - await $('button').click(); - expect(swal).toBeCalledWith({ - text: 'general.confirmLogout', - type: 'warning', - showCancelButton: true, - confirmButtonText: 'general.confirm', - cancelButtonText: 'general.cancel' - }); - expect(fetch).toBeCalledWith({ - type: 'POST', - url: 'auth/logout', - dataType: 'json' - }); - jest.runAllTimers(); - expect(url).toBeCalled(); - - await $('button').click(); - expect(swal).toBeCalledWith({ type: 'success', html: 'success' }); - - await $('button').click(); - expect(showAjaxError).toBeCalled(); - }); -}); - -jest.useRealTimers(); - -describe('tests for "notify" module', () => { - const modulePath = '../common/notify'; - - it('show message', () => { - document.body.innerHTML = '
'; - - const showMsg = require(modulePath).showMsg; - - showMsg('msg1'); - expect($('div').hasClass('a-class')).toBe(false); - expect($('div').hasClass('callout')).toBe(true); - expect($('div').hasClass('callout-info')).toBe(true); - expect($('div').html()).toBe('msg1'); - - showMsg('msg2', 'warning'); - expect($('div').hasClass('callout-info')).toBe(false); - expect($('div').hasClass('callout')).toBe(true); - expect($('div').hasClass('callout-warning')).toBe(true); - expect($('div').html()).toBe('msg2'); - }); - - it('show ajax error', () => { - window.trans = jest.fn(key => key); - $.fn.modal = jest.fn(); - - const showAjaxError = require(modulePath).showAjaxError; - - showAjaxError({ responseText: 'error' }); - expect(window.trans).toBeCalledWith('general.fatalError'); - }); - - it('show modal dialog', () => { - const modal = jest.fn(); - $.fn.modal = modal; - - const showModal = require(modulePath).showModal; - showModal(''); - expect(modal).toBeCalled(); - }); -}); - -describe('tests for "polyfill" module', () => { - const modulePath = '../common/polyfill'; - - String.prototype.includes = undefined; - String.prototype.endsWith = undefined; - require(modulePath); - - it('String#includes', () => { - expect('blessing-skin'.includes('skin')).toBe(true); - expect('blessing-skin'.includes('server')).toBe(false); - expect('blessing-skin'.includes('skin', 9)).toBe(true); - expect('blessing-skin'.includes('blessing', 9)).toBe(false); - }); - - it('String#endsWith', () => { - expect('blessing-skin'.endsWith('skin')).toBe(true); - expect('blessing-skin'.endsWith('server')).toBe(false); - expect('blessing-skin'.endsWith('blessing', 8)).toBe(true); - }); -}); - -describe('tests for "utils" module', () => { - const modulePath = '../common/utils'; - - window.blessing = { - version: '' - }; - - it('check a variable if it is empty', () => { - const isEmpty = require(modulePath).isEmpty; - - expect(isEmpty()).toBe(true); - expect(isEmpty(null)).toBe(true); - expect(isEmpty(undefined)).toBe(true); - expect(isEmpty(0)).toBe(false); - expect(isEmpty(false)).toBe(false); - expect(isEmpty('')).toBe(true); - expect(isEmpty({})).toBe(true); - expect(isEmpty({ sth: '' })).toBe(false); - expect(isEmpty([])).toBe(true); - expect(isEmpty([1])).toBe(false); - expect(isEmpty('something')).toBe(false); - expect(isEmpty(Symbol())).toBe(true); - }); - - it('fake fetch', () => { - $.ajax = jest.fn(); - const fetch = require(modulePath).fetch; - const xhr = fetch({ type: 'GET' }); - - expect($.ajax).toBeCalledWith({ type: 'GET' }); - expect(Object.getPrototypeOf(xhr)).toBe(Promise.prototype); - }); - - it('make a debounced function', done => { - const func = jest.fn(); - const debounce = require(modulePath).debounce; - const debounced = debounce(func, 100); - - debounced(); - debounced(); - - setTimeout(() => { - expect(func.mock.calls.length).toBe(1); - done(); - }, 100); - jest.runAllTimers(); - - expect(() => debounce(func, 'string')).toThrow(); - expect(() => debounce('not a function', 100)).toThrow(); - }); - - it('get a absolute url', () => { - window.blessing = { base_url: 'http://localhost' }; - const url = require(modulePath).url; - - expect(url()).toBe('http://localhost/'); - expect(url('test')).toBe('http://localhost/test'); - expect(url('/test')).toBe('http://localhost/test'); - }); - - it('get argument from query string', () => { - const { getQueryString } = require(modulePath); - - Object.defineProperty(window.location, 'search', { - value: '?key1=val1', - writable: true - }); - expect(getQueryString('key1')).toBe('val1'); - expect(getQueryString('key2')).toBeUndefined(); - expect(getQueryString('key2', 'val2')).toBe('val2'); - window.location.search = '?key1=val1&key2=val2'; - expect(getQueryString('key2', 'val2')).toBe('val2'); - }); - - it('test `isMobileBrowserScrolling` function', () => { - const { isMobileBrowserScrolling } = require(modulePath); - expect(isMobileBrowserScrolling()).toBe(false); - - $.cachedWindowWidth = 50; - $.cachedWindowHeight = 50; - expect(isMobileBrowserScrolling()).toBe(false); - - $.cachedWindowWidth = 60; - $.cachedWindowHeight = 0; - expect(isMobileBrowserScrolling()).toBe(false); - - $.cachedWindowWidth = 0; - $.cachedWindowHeight = 50; - expect(isMobileBrowserScrolling()).toBe(true); - expect(isMobileBrowserScrolling()).toBe(true); // For the second condition - - $.lastWindowHeight = 60; - expect(isMobileBrowserScrolling()).toBe(false); - }); -}); diff --git a/resources/assets/src/js/admin/customize.js b/resources/assets/src/js/admin/customize.js deleted file mode 100644 index 53e49823..00000000 --- a/resources/assets/src/js/admin/customize.js +++ /dev/null @@ -1,30 +0,0 @@ -/* global current_skin:true */ - -'use strict'; - -$('#layout-skins-list [data-skin]').click(function (e) { - e.preventDefault(); - const skin_name = $(this).data('skin'); - $('body').removeClass(current_skin).addClass(skin_name); - current_skin = skin_name; -}); - -async function submitColor() { - try { - const { errno, msg } = await fetch({ - type: 'POST', - url: url('admin/customize?action=color'), - dataType: 'json', - data: { color_scheme: current_skin } - }); - errno === 0 ? toastr.success(msg) : toastr.warning(msg); - } catch (error) { - showAjaxError(error); - } -} - -$('#color-submit').click(submitColor); - -if (process.env.NODE_ENV === 'test') { - module.exports = submitColor; -} diff --git a/resources/assets/src/js/admin/players.js b/resources/assets/src/js/admin/players.js deleted file mode 100644 index c423833c..00000000 --- a/resources/assets/src/js/admin/players.js +++ /dev/null @@ -1,288 +0,0 @@ -'use strict'; - -if ($('#player-table').length === 1) { - $(document).ready(initPlayersTable); -} - -function initPlayersTable() { - const specificUid = getQueryString('uid'); - const query = specificUid ? `?uid=${specificUid}` : ''; - - $('#player-table').DataTable({ - ajax: url(`admin/player-data${query}`), - scrollY: ($('.content-wrapper').height() - $('.content-header').outerHeight()) * 0.7, - fnDrawCallback: () => $('[data-toggle="tooltip"]').tooltip(), - columnDefs: playersTableColumnDefs - }).on('xhr.dt', handleDataTablesAjaxError); -} - -const playersTableColumnDefs = [ - { - targets: 0, - data: 'pid', - width: '1%' - }, - { - targets: 1, - data: 'uid', - render: (data, type, row) => `${data}` - }, - { - targets: 2, - data: 'player_name' - }, - { - targets: 3, - data: 'preference', - render: data => { - return ` - `; - } - }, - { - targets: 4, - searchable: false, - orderable: false, - render: (data, type, row) => ['steve', 'alex', 'cape'].reduce((html, type) => { - const currentTypeTid = row[`tid_${type}`]; - const imageId = `${row.pid}-${currentTypeTid}`; - - if (currentTypeTid === 0) { - return html + ``; - } else { - return html + ` - - - `; - } - }, '') - }, - { - targets: 5, - data: 'last_modified' - }, - { - targets: 6, - searchable: false, - orderable: false, - render: (data, type, row) => ` -
- - -
- ${trans('admin.deletePlayer')}` - } -]; - -async function changePreference() { - try { - const { errno, msg } = await fetch({ - type: 'POST', - url: url('admin/players?action=preference'), - dataType: 'json', - data: { - pid: $(this).parent().parent().attr('id'), - preference: $(this).val() - } - }); - errno === 0 ? toastr.success(msg) : toastr.warning(msg); - } catch (error) { - showAjaxError(error); - } -} - -function changeTexture(pid, playerName) { - const dom = ` -
- - -
-
- - -
`; - - showModal(dom, trans('admin.changePlayerTexture', { 'player': playerName }), 'default', { - callback: `ajaxChangeTexture(${pid})` - }); - return; -} - -async function ajaxChangeTexture(pid) { - // Remove interference of modal which is hide - $('.modal').each(function () { - if ($(this).css('display') === 'none') $(this).remove(); - }); - - const model = $('#model').val(); - const tid = $('#tid').val(); - - try { - const { errno, msg } = await fetch({ - type: 'POST', - url: url('admin/players?action=texture'), - dataType: 'json', - data: { pid: pid, model: model, tid: tid } - }); - if (errno === 0) { - $(`#${pid}-${model}`).attr('src', url(`preview/64/${tid}.png`)); - $('.modal').modal('hide'); - - toastr.success(msg); - } else { - toastr.warning(msg); - } - } catch (error) { - showAjaxError(error); - } -} - -async function changePlayerName(pid, oldName) { - const dom = $(`tr#${pid} > td:nth-child(3)`); - let newPlayerName; - - try { - newPlayerName = await swal({ - text: trans('admin.changePlayerNameNotice'), - input: 'text', - inputValue: oldName, - inputValidator: name => (new Promise((resolve, reject) => { - name ? resolve() : reject(trans('admin.emptyPlayerName')); - })) - }); - } catch (error) { - return; - } - - try { - const { errno, msg } = await fetch({ - type: 'POST', - url: url('admin/players?action=name'), - dataType: 'json', - data: { pid: pid, name: newPlayerName } - }); - if (errno === 0) { - dom.text(newPlayerName); - - toastr.success(msg); - } else { - toastr.warning(msg); - } - } catch (error) { - showAjaxError(error); - } -} - -function changeOwner(pid) { - const dom = $(`#${pid} > td:nth-child(2)`); - let owner = 0; - - swal({ - html: `${trans('admin.changePlayerOwner')}
 `, - input: 'number', - inputValue: dom.text(), - showCancelButton: true - }).then(async uid => { - owner = uid; - - try { - const { errno, msg } = await fetch({ - type: 'POST', - url: url('admin/players?action=owner'), - dataType: 'json', - data: { pid, uid } - }); - if (errno === 0) { - dom.text(owner); - toastr.success(msg); - } else { - toastr.warning(msg); - } - } catch (error) { - showAjaxError(error); - } - }); - - $('.swal2-input').on('input', debounce(showNicknameInSwal, 350)); -} - -async function showNicknameInSwal() { - const uid = $('.swal2-input').val(); - - if (isNaN(uid) || uid <= 0) - return; - - try { - const { user } = await fetch({ - type: 'GET', - url: url(`admin/user/${uid}`), - dataType: 'json' - }); - $('.swal2-content').html( - trans('admin.changePlayerOwner') + - '' + - trans('admin.targetUser', { nickname: user.nickname }) + - '' - ); - } catch (error) { - $('.swal2-content').html(` - ${trans('admin.changePlayerOwner')}
- ${trans('admin.noSuchUser')} - `); - } -} - -async function deletePlayer(pid) { - try { - await swal({ - text: trans('admin.deletePlayerNotice'), - type: 'warning', - showCancelButton: true - }); - } catch (error) { - return; - } - - try { - const { errno, msg } = await fetch({ - type: 'POST', - url: url('admin/players?action=delete'), - dataType: 'json', - data: { pid: pid } - }); - if (errno === 0) { - $(`tr#${pid}`).remove(); - toastr.success(msg); - } else { - toastr.warning(msg); - } - } catch (error) { - showAjaxError(error); - } -} - -if (process.env.NODE_ENV === 'test') { - module.exports = { - initPlayersTable, - changeOwner, - showNicknameInSwal, - deletePlayer, - changeTexture, - changePlayerName, - changePreference, - ajaxChangeTexture, - }; -} diff --git a/resources/assets/src/js/admin/plugins.js b/resources/assets/src/js/admin/plugins.js deleted file mode 100644 index ed0a6136..00000000 --- a/resources/assets/src/js/admin/plugins.js +++ /dev/null @@ -1,178 +0,0 @@ -'use strict'; - -if ($('#plugin-table').length === 1) { - $(document).ready(initPluginsTable); -} - -function initPluginsTable() { - $.pluginsTable = $('#plugin-table').DataTable({ - ajax: url('admin/plugins/data'), - fnDrawCallback: () => $('[data-toggle="tooltip"]').tooltip(), - columnDefs: pluginsTableColumnDefs - }).on('xhr.dt', handleDataTablesAjaxError); -} - -const pluginsTableColumnDefs = [ - { - targets: 0, - data: 'title' - }, - { - targets: 1, - data: 'description', - orderable: false, - width: '35%' - }, - { - targets: 2, - data: 'author', - render: data => isEmpty(data.url) ? data.author : `${data.author}` - }, - { - targets: 3, - data: 'version', - orderable: false - }, - { - targets: 4, - data: 'dependencies', - searchable: false, - orderable: false, - render: data => { - if (data.requirements.length === 0) { - return `${trans('admin.noDependencies')}`; - } - - let result = data.isRequirementsSatisfied ? '' : `${trans('admin.whyDependencies')}
`; - - for (const name in data.requirements) { - const constraint = data.requirements[name]; - const color = (name in data.unsatisfiedRequirements) ? 'red' : 'green'; - - result += `${name}: ${constraint}
`; - } - - return result; - } - }, - { - targets: 5, - data: 'status' - }, - { - targets: 6, - data: 'operations', - searchable: false, - orderable: false, - render: (data, type, row) => { - let toggleButton, configViewButton; - - if (data.enabled) { - toggleButton = `${trans('admin.disablePlugin')}`; - } else { - toggleButton = `${trans('admin.enablePlugin')}`; - } - - if (data.enabled && data.hasConfigView) { - configViewButton = `${trans('admin.configurePlugin')}`; - } else { - configViewButton = `${trans('admin.configurePlugin')}`; - } - - const deletePluginButton = `${trans('admin.deletePlugin')}`; - - return toggleButton + configViewButton + deletePluginButton; - } - } -]; - -async function enablePlugin(name) { - try { - const { requirements } = await fetch({ - type: 'POST', - url: url(`admin/plugins/manage?action=requirements&name=${name}`), - dataType: 'json' - }); - - if (requirements.length === 0) { - await swal({ - text: trans('admin.noDependenciesNotice'), - type: 'warning', - showCancelButton: true - }); - } - - const { errno, msg, reason } = await fetch({ - type: 'POST', - url: url(`admin/plugins/manage?action=enable&name=${name}`), - dataType: 'json' - }); - - if (errno === 0) { - toastr.success(msg); - - $.pluginsTable.ajax.reload(null, false); - } else { - swal({ type: 'warning', html: `

${msg}

` }); - } - } catch (error) { - showAjaxError(error); - } -} - -async function disablePlugin(name) { - try { - const { errno, msg } = await fetch({ - type: 'POST', - url: url(`admin/plugins/manage?action=disable&name=${name}`), - dataType: 'json' - }); - if (errno === 0) { - toastr.success(msg); - - $.pluginsTable.ajax.reload(null, false); - } else { - swal({ type: 'warning', html: msg }); - } - } catch (error) { - showAjaxError(error); - } -} - -async function deletePlugin(name) { - try { - await swal({ - text: trans('admin.confirmDeletion'), - type: 'warning', - showCancelButton: true - }); - } catch (error) { - return; - } - - try { - const { errno, msg } = await fetch({ - type: 'POST', - url: url(`admin/plugins/manage?action=delete&name=${name}`), - dataType: 'json' - }); - if (errno === 0) { - toastr.success(msg); - - $.pluginsTable.ajax.reload(null, false); - } else { - swal({ type: 'warning', html: msg }); - } - } catch (error) { - showAjaxError(error); - } -} - -if (process.env.NODE_ENV === 'test') { - module.exports = { - initPluginsTable, - deletePlugin, - enablePlugin, - disablePlugin, - }; -} diff --git a/resources/assets/src/js/admin/users.js b/resources/assets/src/js/admin/users.js deleted file mode 100644 index b01c7a04..00000000 --- a/resources/assets/src/js/admin/users.js +++ /dev/null @@ -1,346 +0,0 @@ -'use strict'; - -if ($('#user-table').length === 1) { - $(document).ready(initUsersTable); -} - -function initUsersTable() { - const specificUid = getQueryString('uid'); - const query = specificUid ? `?uid=${specificUid}` : ''; - - $('#user-table').DataTable({ - ajax: url(`admin/user-data${query}`), - scrollY: ($('.content-wrapper').height() - $('.content-header').outerHeight()) * 0.7, - fnDrawCallback: () => $('[data-toggle="tooltip"]').tooltip(), - rowCallback: (row, data) => $(row).attr('id', `user-${data.uid}`), - columnDefs: usersTableColumnDefs - }).on('xhr.dt', handleDataTablesAjaxError); -} - -const userPermissions = { - '-1': 'banned', - '0': 'normal', - '1': 'admin', - '2': 'superAdmin' -}; - -const usersTableColumnDefs = [ - { - targets: 0, - data: 'uid', - width: '1%' - }, - { - targets: 1, - data: 'email' - }, - { - targets: 2, - data: 'nickname' - }, - { - targets: 3, - data: 'score', - render: data => `` - }, - { - targets: 4, - data: 'players_count', - searchable: false, - orderable: false, - render: (data, type, row) => `${data}` - }, - { - targets: 5, - data: 'permission', - className: 'status', - render: data => trans('admin.' + userPermissions[data]) - }, - { - targets: 6, - data: 'register_at' - }, - { - targets: 7, - data: 'operations', - searchable: false, - orderable: false, - render: renderUsersTableOperations - } -]; - -function renderUsersTableOperations(currentUserPermission, type, row) { - let adminOption = '', bannedOption = '', deleteUserButton; - - if (row.permission !== 2) { - // Only SUPER admins are allowed to set/unset admins - if (currentUserPermission === 2) { - const adminStatus = row.permission === 1 ? 'admin' : 'normal'; - adminOption = `
  • - ${ adminStatus === 'admin' ? trans('admin.unsetAdmin') : trans('admin.setAdmin') } -
  • `; - } - - const banStatus = row.permission === -1 ? 'banned' : 'normal'; - bannedOption = `
  • - ${ banStatus === 'banned' ? trans('admin.unban') : trans('admin.ban') } -
  • `; - } - - if (currentUserPermission === 2) { - if (row.permission === 2) { - deleteUserButton = `${trans('admin.deleteUser')}`; - } else { - deleteUserButton = `${trans('admin.deleteUser')}`; - } - } else { - if (row.permission === 1 || row.permission === 2) { - deleteUserButton = `${trans('admin.deleteUser')}`; - } else { - deleteUserButton = `${trans('admin.deleteUser')}`; - } - } - - return ` -
    - - -
    - ${deleteUserButton}`; -} - -async function changeUserEmail(uid) { - const dom = $(`tr#user-${uid} > td:nth-child(2)`); - let newUserEmail = ''; - - try { - newUserEmail = await swal({ - text: trans('admin.newUserEmail'), - showCancelButton: true, - input: 'text', - inputValue: dom.text(), - inputValidator: value => (new Promise((resolve, reject) => { - value ? resolve() : reject(trans('auth.emptyEmail')); - })) - }); - } catch (error) { - return; - } - - try { - const { errno, msg } = await fetch({ - type: 'POST', - url: url('admin/users?action=email'), - dataType: 'json', - data: { uid: uid, email: newUserEmail } - }); - - if (errno === 0) { - dom.text(newUserEmail); - - toastr.success(msg); - } else { - toastr.warning(msg); - } - } catch (error) { - showAjaxError(error); - } -} - -async function changeUserNickName(uid) { - const dom = $(`tr#user-${uid} > td:nth-child(3)`); - let newNickName = ''; - - try { - newNickName = await swal({ - text: trans('admin.newUserNickname'), - showCancelButton: true, - input: 'text', - inputValue: dom.text(), - inputValidator: value => (new Promise((resolve, reject) => { - value ? resolve() : reject(trans('auth.emptyNickname')); - })) - }); - } catch (error) { - return; - } - - try { - const { errno, msg } = await fetch({ - type: 'POST', - url: url('admin/users?action=nickname'), - dataType: 'json', - data: { uid: uid, nickname: newNickName } - }); - if (errno === 0) { - dom.text(newNickName); - - toastr.success(msg); - } else { - toastr.warning(msg); - } - } catch (error) { - showAjaxError(error); - } -} - -async function changeUserPwd(uid) { - let password; - try { - password = await swal({ - text: trans('admin.newUserPassword'), - showCancelButton: true, - input: 'password', - }); - } catch (error) { - return; - } - - try { - const { errno, msg } = await fetch({ - type: 'POST', - url: url('admin/users?action=password'), - dataType: 'json', - data: { uid: uid, password: password } - }); - errno === 0 ? toastr.success(msg) : toastr.warning(msg); - } catch (error) { - showAjaxError(error); - } -} - -async function changeUserScore(uid, score) { - try { - const { errno, msg } = await fetch({ - type: 'POST', - url: url('admin/users?action=score'), - dataType: 'json', - // Handle id formatted as '#user-1234' - data: { uid: uid.slice(5), score: score } - }); - errno === 0 ? toastr.success(msg) : toastr.warning(msg); - } catch (error) { - showAjaxError(error); - } -} - -async function changeBanStatus(uid) { - try { - const { errno, msg, permission } = await fetch({ - type: 'POST', - url: url('admin/users?action=ban'), - dataType: 'json', - data: { uid: uid } - }); - - if (errno === 0) { - const dom = $(`#ban-${uid}`); - - if (dom.attr('data') === 'banned') { - dom.text(trans('admin.ban')).attr('data', 'normal'); - } else { - dom.text(trans('admin.unban')).attr('data', 'banned'); - } - - $(`#user-${uid} > td.status`).text( - permission === -1 ? trans('admin.banned') : trans('admin.normal') - ); - - toastr.success(msg); - } else { - toastr.warning(msg); - } - } catch (error) { - showAjaxError(error); - } -} - -async function changeAdminStatus(uid) { - try { - const { errno, msg, permission } = await fetch({ - type: 'POST', - url: url('admin/users?action=admin'), - dataType: 'json', - data: { uid: uid } - }); - - if (errno === 0) { - const dom = $(`#admin-${uid}`); - - if (dom.attr('data') === 'admin') { - dom.text(trans('admin.setAdmin')).attr('data', 'normal'); - } else { - dom.text(trans('admin.unsetAdmin')).attr('data', 'admin'); - } - - $(`#user-${uid} > td.status`).text( - (permission === 1) ? trans('admin.admin') : trans('admin.normal') - ); - - toastr.success(msg); - } else { - toastr.warning(msg); - } - } catch (error) { - showAjaxError(error); - } -} - -async function deleteUserAccount(uid) { - try { - await swal({ - text: trans('admin.deleteUserNotice'), - type: 'warning', - showCancelButton: true - }); - } catch (error) { - return; - } - - try { - const { errno, msg } = await fetch({ - type: 'POST', - url: url('admin/users?action=delete'), - dataType: 'json', - data: { uid: uid } - }); - - if (errno === 0) { - $('tr#user-' + uid).remove(); - toastr.success(msg); - } else { - toastr.warning(msg); - } - } catch (error) { - showAjaxError(error); - } -} - -$('body').on('keypress', '.score', function(event){ - // Change score when Enter key is pressed - if (event.which === 13) { - $(this).blur(); - changeUserScore($(this).parent().parent().attr('id'), $(this).val()); - } -}); - -if (process.env.NODE_ENV === 'test') { - module.exports = { - initUsersTable, - changeUserPwd, - changeBanStatus, - changeUserEmail, - changeUserScore, - changeAdminStatus, - deleteUserAccount, - changeUserNickName, - }; -} diff --git a/resources/assets/src/js/common/i18n.js b/resources/assets/src/js/common/i18n.js deleted file mode 100644 index 6671fde9..00000000 --- a/resources/assets/src/js/common/i18n.js +++ /dev/null @@ -1,56 +0,0 @@ -'use strict'; - -$.locales = Object.create(null); -$.currentLocale = Object.create(null); - -/** - * Load current selected language. - * - * @return void - */ -function loadLocales() { - for (const lang in $.locales) { - if (!isEmpty($.locales[lang])) { - $.currentLocale = $.locales[lang] || Object.create(null); - } - } -} - -/** - * Translate according to given key. - * - * @param {string} key - * @param {dict} parameters - * @return {string} - */ -function trans(key, parameters = {}) { - if (isEmpty($.currentLocale)) { - loadLocales(); - } - - const segments = key.split('.'); - let temp = $.currentLocale || {}; - - for (const i in segments) { - if (isEmpty(temp[segments[i]])) { - return key; - } else { - temp = temp[segments[i]]; - } - } - - for (const i in parameters) { - if (!isEmpty(parameters[i])) { - temp = temp.replace(':'+i, parameters[i]); - } - } - - return temp; -} - -if (process.env.NODE_ENV === 'test') { - module.exports = { - trans, - loadLocales, - }; -} diff --git a/resources/assets/src/js/common/layout.js b/resources/assets/src/js/common/layout.js deleted file mode 100644 index 886b725a..00000000 --- a/resources/assets/src/js/common/layout.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -$.defaultPaginatorConfig = { - visiblePages: 5, - currentPage: 1, - first: '
  • «
  • ', - prev: '
  • ', - next: '
  • ', - last: '
  • »
  • ', - page: '
  • {{page}}
  • ', - wrapper: '' -}; - -$(document).ready(() => { - $('input').iCheck({ - radioClass: 'iradio_square-blue', - checkboxClass: 'icheckbox_square-blue' - }); - - $('[data-toggle="tooltip"]').tooltip(); - - swal.setDefaults({ - confirmButtonText: trans('general.confirm'), - cancelButtonText: trans('general.cancel') - }); -}); diff --git a/resources/assets/src/js/common/logout.js b/resources/assets/src/js/common/logout.js deleted file mode 100644 index efba28cf..00000000 --- a/resources/assets/src/js/common/logout.js +++ /dev/null @@ -1,36 +0,0 @@ -'use strict'; - -async function confirmLogout() { - try { - await swal({ - text: trans('general.confirmLogout'), - type: 'warning', - showCancelButton: true, - confirmButtonText: trans('general.confirm'), - cancelButtonText: trans('general.cancel') - }); - } catch (error) { - return; - } - - try { - const { msg } = await logout(); - setTimeout(() => window.location = url(), 1000); - swal({ - type: 'success', - html: msg - }); - } catch (error) { - showAjaxError(error); - } -} - -function logout() { - return fetch({ - type: 'POST', - url: url('auth/logout'), - dataType: 'json' - }); -} - -$('#logout-button').click(confirmLogout); diff --git a/resources/assets/src/js/common/notify.js b/resources/assets/src/js/common/notify.js deleted file mode 100644 index 02abae7a..00000000 --- a/resources/assets/src/js/common/notify.js +++ /dev/null @@ -1,78 +0,0 @@ -'use strict'; - -/** - * Show message to div#msg with level - * - * @param {string} msg - * @param {string} type - * @return {void} - */ -function showMsg(msg, type = 'info') { - $('[id=msg]').removeClass().addClass('callout').addClass(`callout-${type}`).html(msg); -} - -/** - * Show modal if error occured when sending an ajax request. - * - * @param {object} json - * @return {void} - */ -function showAjaxError(json) { - if (typeof json === 'string') { - return console.warn(json); - } - - if (! json.responseText) { - return console.warn('Empty Ajax response body.'); - } - - showModal(json.responseText.replace(/\n/g, '
    '), trans('general.fatalError'), 'danger'); -} - -/** - * Show a bootstrap modal. - * - * @param {string} msg Modal content - * @param {string} title Modal title - * @param {string} type Modal type, default|info|success|warning|error - * @param {object} options All $.fn.modal options, plus { btnText, callback, destroyOnClose } - * @return {void} - */ -function showModal(msg, title = 'Message', type = 'default', options = {}) { - const btnType = (type !== 'default') ? 'btn-outline' : 'btn-primary'; - const btnText = options.btnText || 'OK'; - const onClick = (options.callback === undefined) ? 'data-dismiss="modal"' : `onclick="${options.callback}"`; - const destroyOnClose = (options.destroyOnClose === false) ? false : true; - - const dom = ` - `; - - $(dom).on('hidden.bs.modal', function () { - destroyOnClose && $(this).remove(); - }).modal(options); -} - -if (process.env.NODE_ENV === 'test') { - module.exports = { - showMsg, - showModal, - showAjaxError, - }; -} diff --git a/resources/assets/src/js/common/polyfill.js b/resources/assets/src/js/common/polyfill.js deleted file mode 100644 index 4c39123b..00000000 --- a/resources/assets/src/js/common/polyfill.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -// polyfill of String.prototype.includes -if (!String.prototype.includes) { - String.prototype.includes = function(search) { - // Copied from core-js - return !!~this.indexOf(search, arguments.length > 1 ? arguments[1] : undefined); - }; -} - -// polyfill of String.prototype.endsWith -if (!String.prototype.endsWith) { - String.prototype.endsWith = function (searchString, position) { - const subjectString = this.toString(); - if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { - position = subjectString.length; - } - position -= searchString.length; - const lastIndex = subjectString.lastIndexOf(searchString, position); - return lastIndex !== -1 && lastIndex === position; - }; -} diff --git a/resources/assets/src/js/common/skinview3d.js b/resources/assets/src/js/common/skinview3d.js deleted file mode 100644 index d2d096f3..00000000 --- a/resources/assets/src/js/common/skinview3d.js +++ /dev/null @@ -1,81 +0,0 @@ -/* global skinview3d */ - -// TODO: Help wanted. This file needs to be tested. - -$.msp = {}; -$.msp.handles = {}; -$.msp.control = null; -$.msp.config = { - domElement: document.getElementById('preview-3d-container'), - slim: false, - width: $('#preview-3d-container').width(), - height: $('#preview-3d-container').height(), - skinUrl: '', - capeUrl: '' -}; - -function initSkinViewer(cameraPositionZ = 70) { - disposeSkinViewer(); - - $.msp.viewer = new skinview3d.SkinViewer($.msp.config); - $.msp.viewer.camera.position.z = cameraPositionZ; - - $.msp.viewer.animation = new skinview3d.CompositeAnimation(); - - // Init all available animations and pause them - $.msp.handles.walk = $.msp.viewer.animation.add(skinview3d.WalkingAnimation); - $.msp.handles.run = $.msp.viewer.animation.add(skinview3d.RunningAnimation); - $.msp.handles.rotate = $.msp.viewer.animation.add(skinview3d.RotatingAnimation); - $.msp.handles.run.paused = true; - - $.msp.control = skinview3d.createOrbitControls($.msp.viewer); -} - -function applySkinViewerConfig(config) { - config = config || $.msp.config; - - for (const key in config) { - $.msp.viewer[key] = config[key]; - } -} - -function disposeSkinViewer() { - if ($.msp.viewer instanceof skinview3d.SkinViewer) { - $.msp.viewer.dispose(); - $.msp.handles = {}; - $.msp.control = undefined; - } -} - -function registerAnimationController() { - $('.fa-pause').click(function () { - $.msp.viewer.animationPaused = !$.msp.viewer.animationPaused; - $(this).toggleClass('fa-pause').toggleClass('fa-play'); - }); - - $('.fa-forward').click(function () { - $.msp.handles.run.paused = !$.msp.handles.run.paused; - $.msp.handles.walk.paused = !$.msp.handles.run.paused; - }); - - $('.fa-repeat').click(() => ($.msp.handles.rotate.paused = !$.msp.handles.rotate.paused)); - $('.fa-stop').click(() => { - initSkinViewer(); - - // Pause all animations respectively - for (const key in $.msp.handles) { - $.msp.handles[key].paused = true; - } - }); -} - -function registerWindowResizeHandler() { - $(window).resize(function () { - $.msp.viewer.width = $('#preview-3d-container').width(); - $.msp.viewer.height = $('#preview-3d-container').height(); - }); -} - -if (process.env.NODE_ENV === 'test') { - module.exports = { initSkinViewer, applySkinViewerConfig, registerAnimationController, registerWindowResizeHandler }; -} diff --git a/resources/assets/src/js/common/utils.js b/resources/assets/src/js/common/utils.js deleted file mode 100644 index f20044d5..00000000 --- a/resources/assets/src/js/common/utils.js +++ /dev/null @@ -1,158 +0,0 @@ -'use strict'; - -console.log( - `%c Blessing Skin %c v${blessing.version} %c Made with %c<3%c by printempw.%c https://blessing.studio`, - 'color:#fadfa3;background:#030307;padding:5px 0;margin:10px 0;', - 'background:#fadfa3;padding:5px 0;', - 'font-style:italic;', - 'color:red;', - 'font-style:italic;', '' -); - -/** - * Check if given value is empty. - * - * @param {any} obj - * @return {Boolean} - */ -function isEmpty(obj) { - - // null and undefined are "empty" - if (obj == null) return true; // eslint-disable-line eqeqeq - - if (typeof (obj) === 'number' || typeof (obj) === 'boolean') return false; - - // Assume if it has a length property with a non-zero value - // that that property is correct. - if (obj.length > 0) return false; - if (obj.length === 0) return true; - - // If it isn't an object at this point - // it is empty, but it can't be anything *but* empty - // Is it empty? Depends on your application. - if (typeof obj !== 'object') return true; - - // Otherwise, does it have any properties of its own? - // Note that this doesn't handle - // toString and valueOf enumeration bugs in IE < 9 - for (const key in obj) { - if (hasOwnProperty.call(obj, key)) return false; - } - - return true; -} - -/** - * A fake fetch API. Returns Promises. - * - * @param {array} option Same as options of jQuery.ajax() - * @return {Promise} - */ -function fetch(option) { - return Promise.resolve($.ajax(option)); -} - -/** - * Get parameters in query string with key. - * - * @param {string} key - * @param {string} defaultValue - * @return {string} - */ -function getQueryString(key, defaultValue) { - const result = location.search.match(new RegExp('[?&]'+key+'=([^&]+)','i')); - - if (result === null || result.length < 1){ - return defaultValue; - } else { - return result[1]; - } -} - -/** - * Check if the `resize` event is fired by scrolling on a mobile browser - * whose address bar (e.g. Chrome) will hide automatically when scrolling. - * - * @return {Boolean} - */ -function isMobileBrowserScrolling() { - const currentWindowWidth = $(window).width(); - const currentWindowHeight = $(window).height(); - - if ($.cachedWindowWidth === undefined) { - $.cachedWindowWidth = currentWindowWidth; - } - - if ($.cachedWindowHeight === undefined) { - $.cachedWindowHeight = currentWindowHeight; - } - - const isWidthChanged = (currentWindowWidth !== $.cachedWindowWidth); - const isHeightChanged = (currentWindowHeight !== $.cachedWindowHeight); - - // If the window width & height changes simultaneously, the resize can't be fired by scrolling. - if (isWidthChanged && isHeightChanged) { - return false; - } - - // If only width was changed, it also can't be. - if (isWidthChanged) { - return false; - } - - // If width didn't change but height changed ? - if (isHeightChanged) { - const last = $.lastWindowHeight; - $.lastWindowHeight = currentWindowHeight; - - if (last === undefined || currentWindowHeight === last) { - return true; - } - } - - // If both width & height did not change - return false; -} - -/** - * Return a debounced function - * - * @param {Function} func - * @param {number} delay - * @param {Array} args - * @param {Object} context - */ -function debounce(func, delay, args = [], context = undefined) { - if (isNaN(delay) || typeof func !== 'function') { - throw new Error('Arguments type of function "debounce" is incorrect!'); - } - - let timer = null; - return function () { - clearTimeout(timer); - timer = setTimeout(() => { - func.apply(context, args); - }, delay); - }; -} - -function url(relativeUri = '') { - blessing.base_url = blessing.base_url || ''; - - if (relativeUri[0] !== '/') { - relativeUri = '/' + relativeUri; - } - - return blessing.base_url + relativeUri; -} - -if (process.env.NODE_ENV === 'test') { - module.exports = { - url, - fetch, - isEmpty, - debounce, - getQueryString, - isMobileBrowserScrolling, - }; -} diff --git a/resources/assets/src/js/index.js b/resources/assets/src/js/index.js index 95feb8e0..7d5063ad 100644 --- a/resources/assets/src/js/index.js +++ b/resources/assets/src/js/index.js @@ -3,3 +3,12 @@ import './i18n'; import './net'; import './layout'; import './logout'; + +console.log( + `%c Blessing Skin %c v${blessing.version} %c Made with %c<3%c by printempw.%c https://blessing.studio`, + 'color:#fadfa3;background:#030307;padding:5px 0;margin:10px 0;', + 'background:#fadfa3;padding:5px 0;', + 'font-style:italic;', + 'color:red;', + 'font-style:italic;', '' +);