From 42a42ef8c714e488c3a60188c4107c94b4922e97 Mon Sep 17 00:00:00 2001 From: Pig Fang Date: Wed, 8 Nov 2017 13:27:35 +0800 Subject: [PATCH] Use async/await and update tests --- gulpfile.js | 16 +- package.json | 1 + .../assets/src/js/__tests__/admin.test.js | 261 +++++++++++--- .../assets/src/js/__tests__/auth.test.js | 35 +- .../assets/src/js/__tests__/common.test.js | 77 ++++- .../assets/src/js/__tests__/skinlib.test.js | 90 ++++- .../assets/src/js/__tests__/user.test.js | 321 ++++++++++++++---- resources/assets/src/js/admin/common.js | 27 +- resources/assets/src/js/admin/customize.js | 21 +- resources/assets/src/js/admin/players.js | 174 ++++++---- resources/assets/src/js/admin/plugins.js | 65 ++-- resources/assets/src/js/admin/update.js | 9 +- resources/assets/src/js/admin/users.js | 216 +++++++----- resources/assets/src/js/auth/forgot.js | 31 +- resources/assets/src/js/auth/login.js | 33 +- resources/assets/src/js/auth/register.js | 31 +- resources/assets/src/js/auth/reset.js | 31 +- resources/assets/src/js/common/logout.js | 37 +- resources/assets/src/js/skinlib/index.js | 12 +- resources/assets/src/js/skinlib/operations.js | 203 ++++++----- resources/assets/src/js/skinlib/upload.js | 43 +-- resources/assets/src/js/user/closet.js | 196 ++++++----- resources/assets/src/js/user/player.js | 202 ++++++----- resources/assets/src/js/user/profile.js | 147 +++++--- resources/assets/src/js/user/sign.js | 18 +- yarn.lock | 4 + 26 files changed, 1539 insertions(+), 762 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 1f73af85..5dea5e70 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -11,6 +11,7 @@ var gulp = require('gulp'), zip = require('gulp-zip'), replace = require('gulp-batch-replace'), notify = require('gulp-notify'), + merge = require('merge2'), runSequence = require('run-sequence'); var version = require('./package.json').version; @@ -30,10 +31,13 @@ var vendorScripts = [ 'es6-promise/dist/es6-promise.auto.min.js', 'sweetalert2/dist/sweetalert2.min.js', 'jqPaginator/dist/1.2.0/jqPaginator.min.js', - 'regenerator-runtime/runtime.js', 'resources/assets/dist/js/common.js', ]; +var vendorScriptsToBeMinified = [ + 'regenerator-runtime/runtime.js', +]; + var vendorStyles = [ 'bootstrap/dist/css/bootstrap.min.css', 'admin-lte/dist/css/AdminLTE.min.css', @@ -95,9 +99,12 @@ gulp.task('lint', () => { // Concentrate all vendor scripts & styles to one dist file gulp.task('publish-vendor', ['compile-es6'], callback => { // JavaScript files - gulp.src(convertNpmRelativePath(vendorScripts)) + var js = gulp.src(convertNpmRelativePath(vendorScripts)) + .pipe(replace(scriptReplacements)); + var jsToBeMinified = gulp.src(convertNpmRelativePath(vendorScriptsToBeMinified)) + .pipe(uglify()); + merge(js, jsToBeMinified) .pipe(concat('app.js')) - .pipe(replace(scriptReplacements)) .pipe(gulp.dest(`${distPath}/js/`)); // CSS files gulp.src(convertNpmRelativePath(vendorStyles)) @@ -164,6 +171,7 @@ gulp.task('zip', () => { '!node_modules/**/*.*', '!storage/textures/**/*.*', '!.env', + '!.babelrc', '!.bowerrc', '!.gitignore', '!.git/**/*.*', @@ -201,7 +209,7 @@ gulp.task('watch', () => { // watch .scss files gulp.watch(`${srcPath}/sass/*.scss`, ['compile-sass'], () => notify('Sass files compiled!')); // watch .js files - gulp.watch(`${srcPath}/js/*.js`, ['compile-es6'], () => notify('ES6 scripts compiled!')); + gulp.watch(`${srcPath}/js/**/*.js`, ['compile-es6'], () => notify('ES6 scripts compiled!')); gulp.watch(`${srcPath}/js/general.js`, ['publish-vendor']); }); diff --git a/package.json b/package.json index 0ad214b1..91958f62 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "gulp-uglify": "^3.0.0", "gulp-zip": "^4.0.0", "jest": "^20.0.4", + "merge2": "^1.2.0", "node-sass": "^4.5.3", "run-sequence": "^2.0.0" }, diff --git a/resources/assets/src/js/__tests__/admin.test.js b/resources/assets/src/js/__tests__/admin.test.js index e0cdcea1..d89c31a7 100644 --- a/resources/assets/src/js/__tests__/admin.test.js +++ b/resources/assets/src/js/__tests__/admin.test.js @@ -3,6 +3,8 @@ const $ = require('jquery'); window.$ = window.jQuery = $; +jest.useFakeTimers(); + describe('tests for "customize" module', () => { const modulePath = '../admin/customize'; @@ -26,7 +28,8 @@ describe('tests for "customize" module', () => { 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.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject(new Error)); const url = jest.fn(path => path); const toastr = { success: jest.fn(), @@ -53,16 +56,33 @@ describe('tests for "customize" module', () => { await submitColor(); expect(toastr.warning).toBeCalledWith('warning'); + + await submitColor(); + expect(window.showAjaxError).toBeCalled(); }); }); describe('tests for "players" module', () => { const modulePath = '../admin/players'; - it('change player reference', async () => { + 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.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject(new Error)); const url = jest.fn(path => path); const toastr = { success: jest.fn(), @@ -98,23 +118,28 @@ describe('tests for "players" module', () => { 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.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 = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = ` @@ -141,27 +166,35 @@ describe('tests for "players" module', () => { await ajaxChangeTexture(1); expect(toastr.warning).toBeCalledWith('warning'); + + await ajaxChangeTexture(1); + expect(showAjaxError).toBeCalled(); }); it('change player name', async () => { const fetch = jest.fn() - .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' })); + .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(options => { - options.inputValidator('newName'); - return Promise.resolve('newName'); - }); + 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 = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = ` @@ -177,6 +210,9 @@ describe('tests for "players" module', () => { `; 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', @@ -189,16 +225,22 @@ describe('tests for "players" module', () => { dataType: 'json', data: { pid: 1, name: 'newName' } }); - await changePlayerName(1, 'oldName'); 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.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject(new Error)); const url = jest.fn(path => path); const toastr = { success: jest.fn(), @@ -207,13 +249,14 @@ describe('tests for "players" module', () => { 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 = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = `
@@ -248,28 +291,38 @@ describe('tests for "players" module', () => { data: { pid: 1, uid: 2 } }); await changeOwner(1, 'oldName'); - expect($('tr#1 > td:nth-child(2)').text()).toBe((2).toString()); + 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('delete player', async () => { const fetch = jest.fn() .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })); + .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().mockReturnValue(Promise.resolve('newName')); + 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 = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = `
@@ -282,6 +335,9 @@ describe('tests for "players" module', () => { `; const deletePlayer = require(modulePath).deletePlayer; + await deletePlayer(1); + expect(fetch).not.toBeCalled(); + await deletePlayer(1); expect(swal).toBeCalledWith({ text: 'admin.deletePlayerNotice', @@ -294,10 +350,15 @@ describe('tests for "players" module', () => { dataType: 'json', data: { pid: 1 } }); - await deletePlayer(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(); }); }); @@ -307,17 +368,19 @@ describe('tests for "plugins" module', () => { it('enable a plugin', async () => { const fetch = jest.fn() .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })); + .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 = jest.fn(); + window.showAjaxError = showAjaxError; $.pluginsTable = { ajax: { reload: reloadTable @@ -338,22 +401,27 @@ describe('tests for "plugins" module', () => { await enablePlugin('plugin'); expect(toastr.warning).toBeCalledWith('warning'); + + 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.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 = jest.fn(); + window.showAjaxError = showAjaxError; $.pluginsTable = { ajax: { reload: reloadTable @@ -374,22 +442,30 @@ describe('tests for "plugins" module', () => { await disablePlugin('plugin'); expect(toastr.warning).toBeCalledWith('warning'); + + await disablePlugin('plugin'); + expect(showAjaxError).toBeCalled(); }); it('delete a plugin', async () => { const fetch = jest.fn() - .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' })); + .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().mockReturnValue(Promise.resolve()); + 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 = jest.fn(); + window.showAjaxError = showAjaxError; $.pluginsTable = { ajax: { reload: reloadTable @@ -399,6 +475,9 @@ describe('tests for "plugins" module', () => { const deletePlugin = require(modulePath).deletePlugin; + await deletePlugin('plugin'); + expect(fetch).not.toBeCalled(); + await deletePlugin('plugin'); expect(swal).toBeCalledWith({ text: 'admin.confirmDeletion', @@ -410,10 +489,15 @@ describe('tests for "plugins" module', () => { url: 'admin/plugins/manage?action=delete&name=plugin', dataType: 'json' }); - await deletePlugin('plugin'); expect(toastr.warning).not.toBeCalled(); expect(toastr.success).toBeCalledWith('success'); expect(reloadTable).toBeCalledWith(null, false); + + await deletePlugin('plugin'); + expect(toastr.warning).toBeCalledWith('warning'); + + await deletePlugin('plugin'); + expect(showAjaxError).toBeCalled(); }); }); @@ -502,23 +586,28 @@ describe('tests for "users" module', () => { it('change user email', async () => { const fetch = jest.fn() - .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' })); + .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(options => { - options.inputValidator('a@b.c'); - return Promise.resolve('a@b.c'); - }); + 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 = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = `
@@ -532,6 +621,9 @@ describe('tests for "users" module', () => { `; const changeUserEmail = require(modulePath).changeUserEmail; + await changeUserEmail(1); + expect(fetch).not.toBeCalled(); + await changeUserEmail(1); expect(swal).toBeCalledWith(expect.objectContaining({ text: 'admin.newUserEmail', @@ -545,30 +637,40 @@ describe('tests for "users" module', () => { dataType: 'json', data: { uid: 1, email: 'a@b.c' } }); - await changeUserEmail(1); 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() - .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' })); + .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(options => { - options.inputValidator('foo'); - return Promise.resolve('foo'); - }); + 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 = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = `
@@ -583,6 +685,9 @@ describe('tests for "users" module', () => { `; const changeUserNickName = require(modulePath).changeUserNickName; + await changeUserNickName(1); + expect(fetch).not.toBeCalled(); + await changeUserNickName(1); expect(swal).toBeCalledWith(expect.objectContaining({ text: 'admin.newUserNickname', @@ -596,30 +701,43 @@ describe('tests for "users" module', () => { dataType: 'json', data: { uid: 1, nickname: 'foo' } }); - await changeUserNickName(1); 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() - .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' })); + .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().mockReturnValue(Promise.resolve('secret')); + 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 = jest.fn(); + 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', @@ -632,23 +750,30 @@ describe('tests for "users" module', () => { dataType: 'json', data: { uid: 1, password: 'secret' } }); - await changeUserPwd(1); 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.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 = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = `
@@ -675,6 +800,9 @@ describe('tests for "users" module', () => { await changeUserScore('user-1', 50); expect(toastr.warning).toBeCalledWith('warning'); + + await changeUserScore('user-1', 50); + expect(showAjaxError).toBeCalled(); }); it('change ban status', async () => { @@ -689,18 +817,20 @@ describe('tests for "users" module', () => { msg: 'success', permission: -1 })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })); + .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 = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = `
@@ -748,6 +878,9 @@ describe('tests for "users" module', () => { await require(modulePath).changeBanStatus(1); expect(toastr.warning).toBeCalledWith('warning'); + + await require(modulePath).changeBanStatus(1); + expect(showAjaxError).toBeCalled(); }); it('change admin status', async () => { @@ -762,18 +895,20 @@ describe('tests for "users" module', () => { msg: 'success', permission: 1 })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })); + .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 = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = `
@@ -821,28 +956,39 @@ describe('tests for "users" module', () => { 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() - .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' })); + .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().mockReturnValue(Promise.resolve()); + 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 = jest.fn(); + 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', @@ -855,8 +1001,13 @@ describe('tests for "users" module', () => { dataType: 'json', data: { uid: 1 } }); - await deleteUserAccount(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', () => { diff --git a/resources/assets/src/js/__tests__/auth.test.js b/resources/assets/src/js/__tests__/auth.test.js index a479efc3..7105b676 100644 --- a/resources/assets/src/js/__tests__/auth.test.js +++ b/resources/assets/src/js/__tests__/auth.test.js @@ -40,17 +40,20 @@ describe('tests for "login" module', () => { )) .mockImplementationOnce(() => Promise.resolve( { errno: 1, msg: 'warning2', login_fails: 4 } - )); + )) + .mockImplementationOnce(() => Promise.reject()); const trans = jest.fn(key => key); const url = jest.fn(path => path); const swal = jest.fn(); const refreshCaptcha = jest.fn(); + const showAjaxError = jest.fn(); window.fetch = fetch; window.trans = trans; window.url = url; window.swal = swal; window.showMsg = jest.fn(); window.refreshCaptcha = refreshCaptcha; + window.showAjaxError = showAjaxError; document.body.innerHTML = ` @@ -108,6 +111,9 @@ describe('tests for "login" module', () => { expect(swal).toBeCalledWith({ type: 'error', html: 'auth.tooManyFails' }); expect($('#captcha-form').css('display')).not.toBe('none'); expect(showMsg).toBeCalledWith('warning2', 'warning'); + + await $('button').click(); + expect(showAjaxError).toBeCalled(); }); }); @@ -126,18 +132,21 @@ describe('tests for "register" module', () => { }) .mockImplementationOnce(() => Promise.resolve( { errno: 1, msg: 'warning' } - )); + )) + .mockImplementationOnce(() => Promise.reject(new Error)); const trans = jest.fn(key => key); const url = jest.fn(path => path); const swal = jest.fn().mockImplementation(() => Promise.resolve()); const showMsg = jest.fn(); const refreshCaptcha = jest.fn(); + const showAjaxError = jest.fn(); window.fetch = fetch; window.trans = trans; window.url = url; window.swal = swal; window.showMsg = showMsg; window.refreshCaptcha = refreshCaptcha; + window.showAjaxError = showAjaxError; document.body.innerHTML = ` @@ -232,6 +241,9 @@ describe('tests for "register" module', () => { expect(refreshCaptcha).toBeCalled(); expect(showMsg).toBeCalledWith('warning', 'warning'); expect($('button').html()).toBe('auth.register'); + + await $('button').click(); + expect(showAjaxError).toBeCalled(); }); }); @@ -246,18 +258,21 @@ describe('tests for "forgot" module', () => { }) .mockImplementationOnce(() => Promise.resolve( { errno: 1, msg: 'warning' } - )); + )) + .mockImplementationOnce(() => Promise.reject(new Error)); const trans = jest.fn(key => key); const url = jest.fn(path => path); const swal = jest.fn(); const showMsg = jest.fn(); const refreshCaptcha = jest.fn(); + const showAjaxError = jest.fn(); window.fetch = fetch; window.trans = trans; window.url = url; window.swal = swal; window.showMsg = showMsg; window.refreshCaptcha = refreshCaptcha; + window.showAjaxError = showAjaxError; document.body.innerHTML = ` @@ -302,6 +317,11 @@ describe('tests for "forgot" module', () => { expect(refreshCaptcha).toBeCalled(); expect(showMsg).toBeCalledWith('warning', 'warning'); expect($('button').html()).toBe('auth.send'); + + await $('button').click(); + expect($('button').html()).toBe('auth.send'); + expect($('button').prop('disabled')).toBe(false); + expect(showAjaxError).toBeCalled(); }); }); @@ -316,12 +336,14 @@ describe('tests for "reset" module', () => { }) .mockImplementationOnce(() => Promise.resolve( { errno: 1, msg: 'warning' } - )); + )) + .mockImplementationOnce(() => Promise.reject(new Error)); const trans = jest.fn(key => key); const url = jest.fn(path => path); const swal = jest.fn().mockReturnValue(Promise.resolve()); const showMsg = jest.fn(); const getQueryString = jest.fn().mockReturnValue('token'); + const showAjaxError = jest.fn(); window.fetch = fetch; window.trans = trans; window.url = url; @@ -329,6 +351,7 @@ describe('tests for "reset" module', () => { window.showMsg = showMsg; window.refreshCaptcha = jest.fn(); window.getQueryString = getQueryString; + window.showAjaxError = showAjaxError; document.body.innerHTML = ` @@ -389,5 +412,9 @@ describe('tests for "reset" module', () => { await $('button').click(); expect(showMsg).toBeCalledWith('warning', 'warning'); expect($('button').html()).toBe('auth.reset'); + + await $('button').click(); + expect($('button').html()).toBe('auth.reset'); + expect(showAjaxError).toBeCalled(); }); }); diff --git a/resources/assets/src/js/__tests__/common.test.js b/resources/assets/src/js/__tests__/common.test.js index 8df7eba0..c9a93da1 100644 --- a/resources/assets/src/js/__tests__/common.test.js +++ b/resources/assets/src/js/__tests__/common.test.js @@ -3,6 +3,43 @@ const $ = require('jquery'); window.jQuery = window.$ = $; +describe('tests for "cookie" module', () => { + it('operates cookies', () => { + const cookies = require('../common/cookie'); + + expect(cookies.hasItem('key1')).toBe(false); + expect(cookies.getItem('key1')).toBeNull(); + + expect(cookies.setItem('key1', 'value1')).toBe(true); + expect(document.cookie).toBe('key1=value1'); + expect(cookies.setItem('key2', 'value2')).toBe(true); + expect(document.cookie).toBe('key1=value1; key2=value2'); + expect(cookies.hasItem('key1')).toBe(true); + expect(cookies.getItem('key1')).toBe('value1'); + expect(cookies.hasItem('key2')).toBe(true); + expect(cookies.getItem('key2')).toBe('value2'); + expect(cookies.keys()).toEqual(['key1', 'key2']); + + expect(cookies.setItem('domain', 'value')).toBe(false); + + expect(cookies.removeItem('key0')).toBe(false); + expect(cookies.removeItem('key2')).toBe(true); + expect(cookies.hasItem('key2')).toBe(false); + expect(document.cookie).toBe('key1=value1'); + expect(cookies.removeItem('key1')).toBe(true); + + expect(cookies.setItem('key3', 'value3', 50)); + expect(cookies.getItem('key3')).toBe('value3'); + expect(cookies.setItem('key3', 'value3', Infinity)); + expect(cookies.getItem('key3')).toBe('value3'); + expect(cookies.setItem('key3', 'value3', '60')); + expect(cookies.getItem('key3')).toBe('value3'); + expect(cookies.setItem('key4', 'value3', new Date)); + expect(cookies.removeItem('key3')).toBe(true); + expect(document.cookie).toBe(''); + }); +}); + describe('tests for "i18n" module', () => { const modulePath = '../common/i18n'; @@ -21,6 +58,11 @@ describe('tests for "i18n" module', () => { 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!'); @@ -28,22 +70,32 @@ describe('tests for "i18n" module', () => { }); }); +jest.useFakeTimers(); + describe('tests for "logout" module', () => { const modulePath = '../common/logout'; it('logout', async () => { - const swal = jest.fn().mockReturnValue(Promise.resolve()); + const swal = jest.fn() + .mockReturnValueOnce(Promise.reject()) + .mockReturnValueOnce(Promise.resolve()); const trans = jest.fn(key => key); const fetch = jest.fn() - .mockReturnValue(Promise.resolve({ msg: 'success' })); + .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', @@ -57,11 +109,19 @@ describe('tests for "logout" module', () => { 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'; @@ -87,6 +147,7 @@ describe('tests for "notify" module', () => { const warn = jest.fn(); window.console.warn = warn; window.trans = jest.fn(key => key); + $.fn.modal = jest.fn(); const showAjaxError = require(modulePath).showAjaxError; @@ -95,6 +156,9 @@ describe('tests for "notify" module', () => { showAjaxError({}); expect(warn).toBeCalledWith('Empty Ajax response body.'); + + showAjaxError({ responseText: 'error' }); + expect(window.trans).toBeCalledWith('general.fatalError'); }); it('show modal dialog', () => { @@ -146,6 +210,9 @@ describe('tests for "utils" module', () => { 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); }); it('fake fetch', () => { @@ -159,7 +226,8 @@ describe('tests for "utils" module', () => { it('make a debounced function', done => { const func = jest.fn(); - const debounced = require(modulePath).debounce(func, 100); + const debounce = require(modulePath).debounce; + const debounced = debounce(func, 100); debounced(); debounced(); @@ -168,6 +236,9 @@ describe('tests for "utils" module', () => { expect(func.mock.calls.length).toBe(1); done(); }, 100); + + expect(() => debounce(func, 'string')).toThrow(); + expect(() => debounce('not a function', 100)).toThrow(); }); it('get a absolute url', () => { diff --git a/resources/assets/src/js/__tests__/skinlib.test.js b/resources/assets/src/js/__tests__/skinlib.test.js index f8905697..bd349636 100644 --- a/resources/assets/src/js/__tests__/skinlib.test.js +++ b/resources/assets/src/js/__tests__/skinlib.test.js @@ -157,13 +157,14 @@ describe('tests for "index" module', () => { }); it('reload skin library', async () => { - const fetch = jest.fn().mockReturnValue(Promise.resolve({ + const fetch = jest.fn().mockReturnValueOnce(Promise.resolve({ items: [] - })); + })).mockReturnValueOnce(Promise.reject()); const url = jest.fn(path => path); + const showAjaxError = jest.fn(); window.fetch = fetch; window.url = url; - window.showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = `
`; @@ -241,7 +242,8 @@ describe('tests for "operations" module', () => { it('add to closet (by ajax)', async () => { const fetch = jest.fn() .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })); + .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject()); window.fetch = fetch; const url = jest.fn(path => path); window.url = url; @@ -255,6 +257,8 @@ describe('tests for "operations" module', () => { warning: jest.fn() }; window.toastr = toastr; + const showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; $.fn.modal = modal; document.body.innerHTML = ` @@ -277,17 +281,24 @@ describe('tests for "operations" module', () => { await ajaxAddToCloset(1, 'name'); expect(toastr.warning).toBeCalledWith('warning'); + + await ajaxAddToCloset(1, 'name'); + expect(showAjaxError).toBeCalled(); }); it('remove from closet', async () => { const fetch = jest.fn() - .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' })); + .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) + .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject()); window.fetch = fetch; const url = jest.fn(path => path); window.url = url; const trans = jest.fn(key => key); window.trans = trans; - const swal = jest.fn().mockReturnValue(Promise.resolve()); + const swal = jest.fn() + .mockReturnValueOnce(Promise.reject()) + .mockReturnValueOnce(Promise.resolve()); window.swal = swal; const modal = jest.fn(); const toastr = { @@ -295,9 +306,14 @@ describe('tests for "operations" module', () => { warning: jest.fn() }; window.toastr = toastr; + const showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; const removeFromCloset = require(modulePath).removeFromCloset; + await removeFromCloset(1); + expect(fetch).not.toBeCalled(); + await removeFromCloset(1); expect(swal).toBeCalledWith({ text: 'user.removeFromClosetNotice', @@ -312,22 +328,31 @@ describe('tests for "operations" module', () => { dataType: 'json', data: { tid: 1 } }); - await removeFromCloset(1); expect(swal).toBeCalledWith({ type: 'success', html: 'success' }); + + await removeFromCloset(1); + expect(toastr.warning).toBeCalledWith('warning'); + + await removeFromCloset(1); + expect(showAjaxError).toBeCalled(); }); it('change texture name', async () => { const fetch = jest.fn() - .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' })); + .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) + .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject()); window.fetch = fetch; const url = jest.fn(path => path); window.url = url; const trans = jest.fn(key => key); window.trans = trans; - const swal = jest.fn(option => { - option.inputValidator('new-name'); - return Promise.resolve('new-name'); - }); + const swal = jest.fn() + .mockImplementationOnce(() => Promise.reject()) + .mockImplementationOnce(option => { + option.inputValidator('new-name'); + return Promise.resolve('new-name'); + }); window.swal = swal; const modal = jest.fn(); const toastr = { @@ -335,10 +360,15 @@ describe('tests for "operations" module', () => { warning: jest.fn() }; window.toastr = toastr; + const showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = '
'; const changeTextureName = require(modulePath).changeTextureName; + await changeTextureName(1, 'oldName'); + expect(fetch).not.toBeCalled(); + await changeTextureName(1, 'oldName'); expect(swal).toBeCalledWith(expect.objectContaining({ text: 'skinlib.setNewTextureName', @@ -352,9 +382,14 @@ describe('tests for "operations" module', () => { dataType: 'json', data: { tid: 1, new_name: 'new-name' } }); - await changeTextureName(1, 'oldName'); expect($('div').text()).toBe('new-name'); expect(toastr.success).toBeCalledWith('success'); + + await changeTextureName(1, 'oldName'); + expect(toastr.warning).toBeCalledWith('warning'); + + await changeTextureName(1, 'oldName'); + expect(showAjaxError).toBeCalled(); }); it('update texture status', () => { @@ -416,7 +451,8 @@ describe('tests for "operations" module', () => { const fetch = jest.fn() .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success', public: '0' })) .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })); + .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject()); window.fetch = fetch; const url = jest.fn(path => path); window.url = url; @@ -430,6 +466,8 @@ describe('tests for "operations" module', () => { warning: jest.fn() }; window.toastr = toastr; + const showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = ` skinlib.setAsPrivate @@ -453,17 +491,24 @@ describe('tests for "operations" module', () => { await changePrivacy(1); expect(toastr.warning).toBeCalledWith('warning'); + + await changePrivacy(1); + expect(showAjaxError).toBeCalled(); }); it('delete texture', async () => { const fetch = jest.fn() - .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' })); + .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) + .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject()); window.fetch = fetch; const url = jest.fn(path => path); window.url = url; const trans = jest.fn(key => key); window.trans = trans; - const swal = jest.fn().mockReturnValue(Promise.resolve()); + const swal = jest.fn() + .mockReturnValueOnce(Promise.reject()) + .mockReturnValueOnce(Promise.resolve()); window.swal = swal; const modal = jest.fn(); const toastr = { @@ -471,9 +516,14 @@ describe('tests for "operations" module', () => { warning: jest.fn() }; window.toastr = toastr; + const showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; const deleteTexture = require(modulePath).deleteTexture; + await deleteTexture(1); + expect(fetch).not.toBeCalled(); + await deleteTexture(1); expect(swal).toBeCalledWith({ text: 'skinlib.deleteNotice', @@ -486,7 +536,13 @@ describe('tests for "operations" module', () => { dataType: 'json', data: { tid: 1 } }); - await deleteTexture(1); expect(swal).toBeCalledWith({ type: 'success', html: 'success' }); + expect(url).toBeCalledWith('skinlib'); + + await deleteTexture(1); + expect(swal).toBeCalledWith({ type: 'warning', html: 'warning' }); + + await deleteTexture(1); + expect(showAjaxError).toBeCalled(); }); }); diff --git a/resources/assets/src/js/__tests__/user.test.js b/resources/assets/src/js/__tests__/user.test.js index 5eee068f..c65929f1 100644 --- a/resources/assets/src/js/__tests__/user.test.js +++ b/resources/assets/src/js/__tests__/user.test.js @@ -11,18 +11,20 @@ describe('tests for "closet" module', () => { it('preview textures', async () => { const fetch = jest.fn() .mockReturnValueOnce(Promise.resolve({ type: 'skin', hash: 1 })) - .mockReturnValueOnce(Promise.resolve({ type: 'cape', hash: 2 })); + .mockReturnValueOnce(Promise.resolve({ type: 'cape', hash: 2 })) + .mockReturnValueOnce(Promise.reject()); const trans = jest.fn(key => key); const url = jest.fn(path => path); const MSP = { changeSkin: jest.fn(), changeCape: jest.fn() }; + const showAjaxError = jest.fn(); window.fetch = fetch; window.trans = trans; window.url = url; window.MSP = MSP; - window.showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = `
@@ -54,7 +56,6 @@ describe('tests for "closet" module', () => {
`; - window.selectedTextures = []; await $('#next > .item-body').click(); expect(fetch).toBeCalledWith({ @@ -65,6 +66,9 @@ describe('tests for "closet" module', () => { expect($('#next').hasClass('item-selected')).toBe(true); expect(MSP.changeCape).toBeCalledWith('textures/2'); expect($('#textures-indicator').text()).toBe('general.skin & general.cape'); + + await $('#next > .item-body').click(); + expect(showAjaxError).toBeCalled(); }); it('render closet', () => { @@ -107,14 +111,14 @@ describe('tests for "closet" module', () => { it('reload closet', async () => { const fetch = jest.fn() - .mockReturnValue(Promise.resolve({ + .mockReturnValueOnce(Promise.resolve({ items: [], category: 'skin', total_pages: 1 - })); + })) + .mockReturnValueOnce(Promise.reject()); const trans = jest.fn(key => key); const url = jest.fn(path => path); - const swal = jest.fn().mockReturnValue(Promise.resolve('name')); const toastr = { success: jest.fn(), warning: jest.fn() @@ -123,12 +127,12 @@ describe('tests for "closet" module', () => { changeSkin: jest.fn(), changeCape: jest.fn() }; + const showAjaxError = jest.fn(); window.fetch = fetch; window.trans = trans; window.url = url; - window.swal = swal; window.toastr = toastr; - window.showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = `
@@ -157,6 +161,9 @@ describe('tests for "closet" module', () => { } }); expect($('#closet-paginator').attr('last-skin-page')).toBe('1'); + + await reloadCloset('skin', 1, 'q'); + expect(showAjaxError).toBeCalled(); }); it('calculate capacity of closet', () => { @@ -172,10 +179,17 @@ describe('tests for "closet" module', () => { it('rename item', async () => { const fetch = jest.fn() - .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' })); + .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) + .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject()); const trans = jest.fn(key => key); const url = jest.fn(path => path); - const swal = jest.fn().mockReturnValue(Promise.resolve('name')); + const swal = jest.fn() + .mockImplementationOnce(() => Promise.reject()) + .mockImplementationOnce(({ inputValidator }) => { + inputValidator('name'); + return Promise.resolve('name'); + }); const toastr = { success: jest.fn(), warning: jest.fn() @@ -184,12 +198,13 @@ describe('tests for "closet" module', () => { changeSkin: jest.fn(), changeCape: jest.fn() }; + const showAjaxError = jest.fn(); window.fetch = fetch; window.trans = trans; window.url = url; window.swal = swal; window.toastr = toastr; - window.showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = `
@@ -204,6 +219,9 @@ describe('tests for "closet" module', () => { `; const renameClosetItem = require(modulePath).renameClosetItem; + await renameClosetItem(1, 'oldName'); + expect(fetch).not.toBeCalled(); + await renameClosetItem(1, 'oldName'); expect(swal).toBeCalledWith(expect.objectContaining({ title: trans('user.renameClosetItem'), @@ -217,18 +235,26 @@ describe('tests for "closet" module', () => { dataType: 'json', data: { tid: 1, new_name: 'name' } }); - - await renameClosetItem(1, 'oldName'); expect(toastr.success).toBeCalledWith('success'); expect($('span').html('name')); + + await renameClosetItem(1, 'oldName'); + expect(toastr.warning).toBeCalledWith('warning'); + + await renameClosetItem(1, 'oldName'); + expect(showAjaxError).toBeCalled(); }); it('remove item from closet', async () => { const fetch = jest.fn() - .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' })); + .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) + .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject()); const trans = jest.fn(key => key); const url = jest.fn(path => path); - const swal = jest.fn().mockReturnValue(Promise.resolve()); + const swal = jest.fn() + .mockReturnValueOnce(Promise.reject()) + .mockReturnValueOnce(Promise.resolve()); const toastr = { success: jest.fn(), warning: jest.fn() @@ -237,12 +263,13 @@ describe('tests for "closet" module', () => { changeSkin: jest.fn(), changeCape: jest.fn() }; + const showAjaxError = jest.fn(); window.fetch = fetch; window.trans = trans; window.url = url; window.swal = swal; window.toastr = toastr; - window.showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = `
@@ -251,6 +278,9 @@ describe('tests for "closet" module', () => { `; const removeFromCloset = require(modulePath).removeFromCloset; + await removeFromCloset(1); + expect(fetch).not.toBeCalled(); + await removeFromCloset(1); expect(swal).toBeCalledWith({ text: 'user.removeFromClosetNotice', @@ -263,22 +293,30 @@ describe('tests for "closet" module', () => { dataType: 'json', data: { tid: 1 } }); - - await removeFromCloset(1); expect(swal).toBeCalledWith({ type: 'success', html: 'success' }); expect(document.getElementById('shouldBeRemoved')).toBeNull(); expect(trans).toBeCalledWith('user.emptyClosetMsg', { url: url('skinlib?filter=skin') }); expect($('#skin-category').html()).toBe( '
user.emptyClosetMsg
' ); + + await removeFromCloset(1); + expect(toastr.warning).toBeCalledWith('warning'); + + await removeFromCloset(1); + expect(showAjaxError).toBeCalled(); }); it('set avatar', async () => { const fetch = jest.fn() - .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' })); + .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) + .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject()); const trans = jest.fn(key => key); const url = jest.fn(path => path); - const swal = jest.fn().mockReturnValue(Promise.resolve()); + const swal = jest.fn() + .mockReturnValueOnce(Promise.reject()) + .mockReturnValueOnce(Promise.resolve()); const toastr = { success: jest.fn(), warning: jest.fn() @@ -287,18 +325,22 @@ describe('tests for "closet" module', () => { changeSkin: jest.fn(), changeCape: jest.fn() }; + const showAjaxError = jest.fn(); window.fetch = fetch; window.trans = trans; window.url = url; window.swal = swal; window.toastr = toastr; - window.showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = ` User Image `; const setAsAvatar = require(modulePath).setAsAvatar; + await setAsAvatar(1); + expect(fetch).not.toBeCalled(); + await setAsAvatar(1); expect(swal).toBeCalledWith({ title: 'user.setAvatar', @@ -312,10 +354,14 @@ describe('tests for "closet" module', () => { dataType: 'json', data: { tid: 1 } }); - - await setAsAvatar(1); expect(toastr.success).toBeCalledWith('success'); expect($('img').attr('src').endsWith('src')).toBe(false); + + await setAsAvatar(1); + expect(toastr.warning).toBeCalledWith('warning'); + + await setAsAvatar(1); + expect(showAjaxError).toBeCalled(); }); }); @@ -325,27 +371,32 @@ describe('tests for "player" module', () => { it('show player texture preview', async () => { const url = jest.fn(path => path); const fetch = jest.fn() - .mockReturnValue(Promise.resolve({ + .mockReturnValueOnce(Promise.resolve({ tid_steve: 1, tid_alex: 2, tid_cape: 3, - preference: 'steve', + preference: 'default', player_name: 'name' - })); - window.url = url; - window.fetch = fetch; - window.showAjaxError = jest.fn(); - window.TexturePreview = require('../common/texture-preview'); - window.MSP = { + })) + .mockReturnValueOnce(Promise.reject()); + const showAjaxError = jest.fn(); + const MSP = { changeSkin: jest.fn(), changeCape: jest.fn(), setStatus: jest.fn(), getStatus: jest.fn() }; + window.url = url; + window.fetch = fetch; + window.showAjaxError = showAjaxError; + window.TexturePreview = require('../common/texture-preview'); + window.MSP = MSP; + window.defaultSkin = 'steve_base64'; document.body.innerHTML = `
+
`; require(modulePath); @@ -358,21 +409,29 @@ describe('tests for "player" module', () => { dataType: 'json', data: { pid: '2' } }); + + await $('#2').click(); + expect(showAjaxError).toBeCalled(); + + $('#preview-switch').click(); + expect(window.TexturePreview.previewType).toBe('2D'); }); - it('change player reference', async () => { + 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.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 = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = ` @@ -432,6 +497,9 @@ describe('tests for "player" module', () => { `; const changePlayerName = require(modulePath).changePlayerName; + await changePlayerName(1); + expect(fetch).not.toBeCalled(); + await changePlayerName(1); expect(swal).toBeCalledWith(expect.objectContaining({ title: 'user.changePlayerName', @@ -446,27 +514,47 @@ describe('tests for "player" module', () => { dataType: 'json', data: { pid: 1, new_player_name: 'name' } }); - await changePlayerName(1); expect(swal).toBeCalledWith({ type: 'success', html: 'success' }); expect($('#player-name').html()).toBe('name'); + + await changePlayerName(1); + expect(swal).toBeCalledWith({ type: 'warning', html: 'warning' }); + + await changePlayerName(1); + expect(showAjaxError).toBeCalled(); + }); + + it('show "clear texture" modal dialog', () => { + const { clearTexture } = require(modulePath); + const trans = jest.fn(key => key); + const showModal = jest.fn(); + window.trans = trans; + window.showModal = showModal; + + clearTexture(); + const args = showModal.mock.calls[0]; + expect(args.includes('user.chooseClearTexture')).toBe(true); + expect(args.includes('default')).toBe(true); }); it('submit clearing texture request', async () => { const fetch = jest.fn() .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })); + .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject()); const url = jest.fn(path => path); const trans = jest.fn(key => key); const toastr = { success: jest.fn(), warning: jest.fn() }; + const showAjaxError = jest.fn(); const modal = jest.fn(); window.fetch = fetch; window.url = url; window.trans = trans; window.toastr = toastr; - window.showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; $.fn.modal = modal; document.body.innerHTML = ` @@ -496,28 +584,39 @@ describe('tests for "player" module', () => { await ajaxClearTexture(1); expect(swal).lastCalledWith({ type: 'error', html: 'warning' }); + + await ajaxClearTexture(1); + expect(showAjaxError).toBeCalled(); }); it('delete player', async () => { const fetch = jest.fn() - .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' })); + .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 swal = jest.fn().mockReturnValue(Promise.resolve()); + const swal = jest.fn() + .mockReturnValueOnce(Promise.reject()) + .mockReturnValueOnce(Promise.resolve()); + const showAjaxError = jest.fn(); window.fetch = fetch; window.url = url; window.toastr = toastr; window.swal = swal; - window.showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = `
`; const deletePlayer = require(modulePath).deletePlayer; + await deletePlayer(1); + expect(fetch).not.toBeCalled(); + await deletePlayer(1); expect(swal).toBeCalledWith({ title: 'user.deletePlayer', @@ -533,16 +632,21 @@ describe('tests for "player" module', () => { dataType: 'json', data: { pid: 1 } }); - - await deletePlayer(1); expect(swal).lastCalledWith({ type: 'success', html: 'success' }); expect(document.getElementById('1')).toBeNull(); + + await deletePlayer(1); + expect(toastr.warning).toBeCalledWith('warning'); + + await deletePlayer(1); + expect(showAjaxError).toBeCalled(); }); it('add a new player', async () => { const fetch = jest.fn() .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })); + .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject()); const url = jest.fn(path => path); const toastr = { success: jest.fn(), @@ -550,11 +654,12 @@ describe('tests for "player" module', () => { }; const swal = jest.fn().mockReturnValue(Promise.resolve()); const modal = jest.fn(); + const showAjaxError = jest.fn(); window.fetch = fetch; window.url = url; window.toastr = toastr; window.swal = swal; - window.showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; $.fn.modal = modal; document.body.innerHTML = ` @@ -570,31 +675,36 @@ describe('tests for "player" module', () => { data: { player_name: 'name' } }); expect(swal).toBeCalledWith({ type: 'success', html: 'success' }); + expect(modal).toBeCalled(); await addNewPlayer(); expect(toastr.warning).toBeCalledWith('warning'); - expect(modal.mock.calls.length).toBe(1); + + await addNewPlayer(); + expect(showAjaxError).toBeCalled(); }); it('set texture', async () => { const fetch = jest.fn() .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })); + .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject()); const url = jest.fn(path => path); const toastr = { success: jest.fn(), warning: jest.fn(), info: jest.fn() }; - const swal = jest.fn().mockReturnValue(Promise.resolve()); + const swal = jest.fn(); const modal = jest.fn(); + const showAjaxError = jest.fn(); window.fetch = fetch; window.url = url; window.toastr = toastr; window.swal = swal; $.fn.modal = modal; window.selectedTextures = {}; - window.showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = ` @@ -622,6 +732,9 @@ describe('tests for "player" module', () => { await setTexture(); expect(toastr.warning).toBeCalledWith('warning'); expect(modal.mock.calls.length).toBe(1); + + await setTexture(); + expect(showAjaxError).toBeCalled(); }); }); @@ -630,16 +743,22 @@ describe('tests for "profile" module', () => { it('change nickname', async () => { const fetch = jest.fn() - .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' })); - const swal = jest.fn().mockReturnValue(Promise.resolve()); + .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) + .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject()); + const swal = jest.fn() + .mockReturnValueOnce(Promise.resolve()) + .mockReturnValueOnce(Promise.reject()) + .mockReturnValueOnce(Promise.resolve()); const trans = jest.fn(key => key); const url = jest.fn(path => path); + const showAjaxError = jest.fn(); window.fetch = fetch; window.swal = swal; window.trans = trans; window.url = url; window.debounce = jest.fn(fn => fn); - window.showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = `
@@ -649,8 +768,12 @@ describe('tests for "profile" module', () => { await changeNickName(); expect(swal).toBeCalledWith({ type: 'error', html: 'user.emptyNewNickName' }); + expect(fetch).not.toBeCalled(); $('input').val('name'); + await changeNickName(); + expect(fetch).not.toBeCalled(); + await changeNickName(); expect(trans).toBeCalledWith('user.changeNickName', { new_nickname: 'name' }); expect(swal).toBeCalledWith({ @@ -664,17 +787,22 @@ describe('tests for "profile" module', () => { dataType: 'json', data: { new_nickname: 'name' } }); - - await changeNickName(); expect($('.nickname').text()).toBe('name'); expect(swal).toBeCalledWith({ type: 'success', html: 'success' }); + + await changeNickName(); + expect(swal).toBeCalled(); + + await changeNickName(); + expect(showAjaxError).toBeCalled(); }); it('change password', async () => { const fetch = jest.fn() + .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) - .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' })); + .mockReturnValueOnce(Promise.reject()); const swal = jest.fn().mockReturnValue(Promise.resolve()); const trans = jest.fn(key => key); const url = jest.fn(path => path); @@ -682,13 +810,20 @@ describe('tests for "profile" module', () => { info: jest.fn(), warning: jest.fn() }; + const showAjaxError = jest.fn(); + const docCookies = { + removeItem: jest.fn() + }; window.fetch = fetch; window.swal = swal; window.trans = trans; window.url = url; window.toastr = toastr; - window.showAjaxError = jest.fn(); - window.logout = jest.fn().mockReturnValue(Promise.resolve({ errno: 0 })); + window.showAjaxError = showAjaxError; + window.logout = jest.fn() + .mockReturnValueOnce(Promise.resolve({ errno: 0 })) + .mockReturnValueOnce(Promise.reject()); + window.docCookies = docCookies; document.body.innerHTML = ` @@ -725,32 +860,49 @@ describe('tests for "profile" module', () => { dataType: 'json', data: { current_password: 'password', new_password: 'new-password' } }); + expect(logout).toBeCalled(); + + await changePassword(); + expect(docCookies.removeItem).toBeCalledWith('token'); await changePassword(); expect(swal).toBeCalledWith({ type: 'warning', text: 'warning' }); await changePassword(); - expect(logout).toBeCalled(); + expect(showAjaxError).toBeCalled(); }); it('change email', async () => { const fetch = jest.fn() .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })); - const swal = jest.fn().mockReturnValue(Promise.resolve()); + .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) + .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject()); + const swal = jest.fn() + .mockReturnValueOnce(Promise.resolve()) + .mockReturnValueOnce(Promise.resolve()) + .mockReturnValueOnce(Promise.reject()) + .mockReturnValue(Promise.resolve()); const trans = jest.fn(key => key); const url = jest.fn(path => path); const toastr = { info: jest.fn(), warning: jest.fn() }; + const showAjaxError = jest.fn(); + const docCookies = { + removeItem: jest.fn() + }; window.fetch = fetch; window.swal = swal; window.trans = trans; window.url = url; window.toastr = toastr; - window.showAjaxError = jest.fn(); - window.logout = jest.fn().mockReturnValue(Promise.resolve({ errno: 0 })); + window.showAjaxError = showAjaxError; + window.logout = jest.fn() + .mockReturnValueOnce(Promise.resolve({ errno: 0 })) + .mockReturnValueOnce(Promise.reject()); + window.docCookies = docCookies; document.body.innerHTML = ` @@ -758,14 +910,17 @@ describe('tests for "profile" module', () => { `; const changeEmail = require(modulePath).changeEmail; - changeEmail(); + await changeEmail(); expect(swal).toBeCalledWith({ type: 'error', html: 'user.emptyNewEmail' }); + expect(fetch).not.toBeCalled(); $('#new-email').val('email'); - changeEmail(); + await changeEmail(); expect(swal).toBeCalledWith({ type: 'warning', html: 'auth.invalidEmail' }); $('#new-email').val('a@b.c'); + await changeEmail(); // Suppose the user cancelled changing email + await changeEmail(); expect(trans).toBeCalledWith('user.changeEmail', { new_email: 'a@b.c' }); expect(swal).toBeCalledWith({ @@ -779,12 +934,24 @@ describe('tests for "profile" module', () => { dataType: 'json', data: { new_email: 'a@b.c', password: 'pwd' } }); + expect(swal).toBeCalledWith({ type: 'success', text: 'success' }); + expect(logout).toBeCalled(); + + await changeEmail(); + expect(docCookies.removeItem).toBeCalled(); + + await changeEmail(); + expect(swal).toBeCalledWith({ type: 'warning', text: 'warning' }); + + await changeEmail(); + expect(showAjaxError).toBeCalled(); }); it('delete account', async () => { const fetch = jest.fn() .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' })) - .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })); + .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' })) + .mockReturnValueOnce(Promise.reject()); const swal = jest.fn().mockReturnValue(Promise.resolve()); const trans = jest.fn(key => key); const url = jest.fn(path => path); @@ -792,12 +959,13 @@ describe('tests for "profile" module', () => { info: jest.fn(), warning: jest.fn() }; + const showAjaxError = jest.fn(); window.fetch = fetch; window.swal = swal; window.trans = trans; window.url = url; window.toastr = toastr; - window.showAjaxError = jest.fn(); + window.showAjaxError = showAjaxError; document.body.innerHTML = `