Update JavaScript tests

This commit is contained in:
Pig Fang 2017-11-26 23:57:52 +08:00
parent 14b7cc670c
commit 4ebd54707e
12 changed files with 449 additions and 121 deletions

View File

@ -302,6 +302,42 @@ describe('tests for "players" module', () => {
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 = `
<input class="swal2-input" />
<div class="swal2-content"></div>
`;
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' }))
@ -506,10 +542,13 @@ describe('tests for "update" module', () => {
it('download updates', async () => {
const fetch = jest.fn()
.mockReturnValueOnce(Promise.resolve({
file_size: 5000
}))
.mockReturnValueOnce(Promise.resolve());
.mockImplementationOnce(({ beforeSend }) => {
beforeSend && beforeSend();
return Promise.resolve({
file_size: 5000
});
})
.mockImplementationOnce(() => Promise.resolve());
const url = jest.fn(path => path);
const toastr = {
success: jest.fn(),
@ -525,6 +564,7 @@ describe('tests for "update" module', () => {
document.body.innerHTML = `
<div id="file-size"></div>
<div id="modal-start-download"></div>
<button id="update-button"></button>
`;
const downloadUpdates = require(modulePath).downloadUpdates;
@ -535,6 +575,7 @@ describe('tests for "update" module', () => {
type: 'GET',
dataType: 'json',
}));
expect($('#update-button').prop('disabled')).toBe(true);
expect($('#file-size').html()).toBe('5000');
expect(modal).toBeCalledWith({
backdrop: 'static',
@ -1048,7 +1089,7 @@ describe('tests for "common" module', () => {
version: '8.1.0'
};
const sendFeedback = require(modulePath);
const { sendFeedback } = require(modulePath);
await sendFeedback();
expect(fetch).toBeCalledWith({
@ -1064,4 +1105,27 @@ describe('tests for "common" module', () => {
expect(fetch).toHaveBeenCalledTimes(1);
expect(console.log).toHaveBeenCalledTimes(1);
});
it('initialize data tables', () => {
$.fn.dataTable = { defaults: {} };
const initUsersTable = jest.fn();
const initPlayersTable = jest.fn();
const initPluginsTable = jest.fn();
window.initUsersTable = initUsersTable;
window.initPlayersTable = initPlayersTable;
window.initPluginsTable = initPluginsTable;
const { initTables } = require(modulePath);
document.body.innerHTML = '<div id="user-table"></div>';
initTables();
expect(initUsersTable).toBeCalled();
document.body.innerHTML = '<div id="player-table"></div>';
initTables();
expect(initPlayersTable).toBeCalled();
document.body.innerHTML = '<div id="plugin-table"></div>';
initTables();
expect($.pluginsTable).not.toBeNull();
});
});

View File

@ -192,6 +192,135 @@ describe('tests for "polyfill" module', () => {
});
});
describe('tests for "texture-preview" module', () => {
const TexturePreview = require('../common/texture-preview');
it('change 2d preview', () => {
const url = jest.fn(path => path);
window.url = url;
document.body.innerHTML = `
<a><img id="steve" /></a>
<div></div>
`;
const instance = new TexturePreview('steve', 5, 'default');
instance.change2dPreview();
expect($('img').attr('src')).toBe('preview/200/5.png');
expect($('a').attr('href')).toBe('skinlib/show/5');
expect($('div').css('display')).toBe('none');
});
it('change 3d preview', async () => {
const url = jest.fn(path => path);
const fetch = jest.fn()
.mockReturnValueOnce(Promise.resolve({ hash: '1' }))
.mockReturnValueOnce(Promise.resolve({ hash: '2' }))
.mockReturnValueOnce(Promise.reject());
const showAjaxError = jest.fn();
const MSP = {
changeSkin: jest.fn(),
changeCape: jest.fn()
};
window.url = url;
window.fetch = fetch;
window.showAjaxError = showAjaxError;
window.MSP = MSP;
let instance = new TexturePreview('alex', 5, 'default');
instance.change3dPreview();
expect(fetch).not.toBeCalled();
instance = new TexturePreview('steve', 5, 'default');
await instance.change3dPreview();
expect(fetch).toBeCalledWith({
type: 'GET',
url: 'skinlib/info/5',
dataType: 'json'
});
expect(MSP.changeSkin).toBeCalledWith('textures/1');
instance = new TexturePreview('cape', 5, 'default');
await instance.change3dPreview();
expect(fetch).toBeCalledWith({
type: 'GET',
url: 'skinlib/info/5',
dataType: 'json'
});
expect(MSP.changeCape).toBeCalledWith('textures/2');
await instance.change3dPreview();
expect(showAjaxError).toBeCalled();
});
it('show not uploaded', () => {
const MSP = {
changeSkin: jest.fn(),
changeCape: jest.fn()
};
window.MSP = MSP;
document.body.innerHTML = `
<a><img id="steve" /></a>
<div></div>
`;
let instance = new TexturePreview('steve', 5, 'default');
instance.showNotUploaded();
expect($('img').css('display')).toBe('none');
expect($('div').css('display')).not.toBe('none');
instance = new TexturePreview('cape', 7, 'cape');
instance.showNotUploaded();
expect(MSP.changeCape).toBeCalledWith('');
});
it('show 3d preview', () => {
document.body.innerHTML = `
<a id="preview-2d"></a>
<a class="operations" style="display: none"></a>
<a id="preview-switch"></a>
<div id="skinpreview"></div>
`;
const trans = jest.fn(key => key);
const MSP = {
changeSkin: jest.fn(),
changeCape: jest.fn(),
get3dSkinCanvas: jest.fn(() => '<canvas></canvas>')
};
window.trans = trans;
window.MSP = MSP;
TexturePreview.previewType = '3D';
TexturePreview.show3dPreview();
expect($('#preview-2d').css('display')).toBe('none');
expect($('.operations').css('display')).not.toBe('none');
expect($('#preview-switch').html()).toBe('user.switch2dPreview');
expect(MSP.get3dSkinCanvas).toBeCalledWith(0, 0);
});
it('show 2d preview', () => {
document.body.innerHTML = `
<div id="canvas3d"></div>
<a id="preview-2d" style="display: none"></a>
<a class="operations"></a>
<a id="preview-switch"></a>
`;
const trans = jest.fn(key => key);
const MSP = {
changeSkin: jest.fn(),
changeCape: jest.fn()
};
window.trans = trans;
window.MSP = MSP;
TexturePreview.show2dPreview();
expect(document.getElementById('canvas3d')).toBeNull();
expect($('#preview-2d').css('display')).not.toBe('none');
expect($('.operations').css('display')).toBe('none');
expect($('#preview-switch').html()).toBe('user.switch3dPreview');
});
});
describe('tests for "utils" module', () => {
const modulePath = '../common/utils';
@ -213,6 +342,7 @@ describe('tests for "utils" module', () => {
expect(isEmpty([])).toBe(true);
expect(isEmpty([1])).toBe(false);
expect(isEmpty('something')).toBe(false);
expect(isEmpty(Symbol())).toBe(true);
});
it('fake fetch', () => {
@ -249,4 +379,39 @@ describe('tests for "utils" module', () => {
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);
});
});

View File

@ -1,5 +1,6 @@
/* eslint no-unused-vars: "off" */
jest.dontMock('jquery');
const $ = require('jquery');
window.$ = window.jQuery = $;
@ -184,6 +185,9 @@ describe('tests for "index" module', () => {
keyword: '%20q'
}
}));
await reloadSkinlib();
expect(showAjaxError).toBeCalled();
});
it('update query string', () => {
@ -208,6 +212,32 @@ describe('tests for "index" module', () => {
expect($('li[data-code=zh_CN] > a').prop('href')).toBe(`?lang=zh_CN&${query}`);
expect($('li[data-code=en] > a').prop('href')).toBe(`?lang=en&${query}`);
});
it('change page', () => {
document.body.innerHTML = `
<div class="overlay" style="display: none"></div>
`;
const { onPageChange } = require(modulePath);
onPageChange(1, 'init');
expect($.skinlib.page).toBe(1);
onPageChange(2);
expect($('div').css('display')).not.toBe('none');
expect($.skinlib.page).toBe(2);
});
it('update filter', () => {
document.body.innerHTML = `
<div data-filter="skin"></div>
`;
const { updateFilter } = require(modulePath);
updateFilter.call($('div'), new Event('click'));
expect($.skinlib.filter).toBe('skin');
$('div').data('filter', 'uploader').data('uid', 4);
updateFilter.call($('div'), new Event('click'));
expect($.skinlib.uploader).toBe(4);
});
});
describe('tests for "operations" module', () => {

View File

@ -363,6 +363,43 @@ describe('tests for "closet" module', () => {
await setAsAvatar(1);
expect(showAjaxError).toBeCalled();
});
it('initialize closet', async () => {
const fetch = jest.fn()
.mockReturnValueOnce(Promise.reject())
.mockReturnValueOnce(Promise.resolve({ items: [], category: 'skin', total_pages: 0 }));
const trans = jest.fn(key => key);
const url = jest.fn(path => path);
const showAjaxError = jest.fn();
const debounce = jest.fn((func, timer) => func());
window.fetch = fetch;
window.trans = trans;
window.url = url;
window.showAjaxError = showAjaxError;
window.debounce = debounce;
$.defaultPaginatorConfig = {};
$.fn.jqPaginator = jest.fn(({ onPageChange }) => onPageChange(0));
const { initCloset } = require(modulePath);
await initCloset();
expect(fetch).not.toBeCalled();
document.body.innerHTML = `
<div id="closet-container"></div>
<div id="skin-category" value="val"></div>
`;
await initCloset();
expect(showAjaxError).toBeCalled();
await initCloset();
expect(debounce.mock.calls[0][1]).toBe(350);
expect(fetch).toBeCalledWith({
type: 'GET',
url: '/user/closet-data',
dataType: 'json'
});
expect($.fn.jqPaginator).toBeCalled();
});
});
describe('tests for "player" module', () => {

View File

@ -4,7 +4,9 @@
$.pluginsTable = null;
$(document).ready(() => {
$(document).ready(initTables);
function initTables() {
$.extend(true, $.fn.dataTable.defaults, {
language: trans('vendor.datatables'),
scrollX: true,
@ -14,14 +16,14 @@ $(document).ready(() => {
serverSide: true
});
if (window.location.pathname.includes('admin/users')) {
if ($('#user-table').length === 1) {
initUsersTable();
} else if (window.location.pathname.includes('admin/players')) {
} else if ($('#player-table').length === 1) {
initPlayersTable();
} else if (window.location.pathname.includes('admin/plugins/manage')) {
} else if ($('#plugin-table').length === 1) {
$.pluginsTable = initPluginsTable();
}
});
}
async function sendFeedback() {
if (docCookies.getItem('feedback_sent') !== null)
@ -50,5 +52,8 @@ async function sendFeedback() {
}
if (typeof require !== 'undefined' && typeof module !== 'undefined') {
module.exports = sendFeedback;
module.exports = {
sendFeedback,
initTables
};
}

View File

@ -133,31 +133,33 @@ function changeOwner(pid) {
}
});
$('.swal2-input').on('input', debounce(async () => {
let uid = $('.swal2-input').val();
$('.swal2-input').on('input', debounce(showNicknameInSwal, 350));
}
if (isNaN(uid) || uid <= 0)
return;
async function showNicknameInSwal() {
let uid = $('.swal2-input').val();
try {
const { user } = await fetch({
type: 'GET',
url: url(`admin/user/${uid}`),
dataType: 'json'
});
$('.swal2-content').html(
trans('admin.changePlayerOwner') +
'<small style="display: block; margin-top: .5em;">' +
trans('admin.targetUser', { nickname: user.nickname }) +
'</small>'
);
} catch (error) {
$('.swal2-content').html(`
${trans('admin.changePlayerOwner')}<br>
<small>${trans('admin.noSuchUser')}</small>
`);
}
}, 350));
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') +
'<small style="display: block; margin-top: .5em;">' +
trans('admin.targetUser', { nickname: user.nickname }) +
'</small>'
);
} catch (error) {
$('.swal2-content').html(`
${trans('admin.changePlayerOwner')}<br>
<small>${trans('admin.noSuchUser')}</small>
`);
}
}
async function deletePlayer(pid) {
@ -192,6 +194,7 @@ async function deletePlayer(pid) {
if (typeof require !== 'undefined' && typeof module !== 'undefined') {
module.exports = {
changeOwner,
showNicknameInSwal,
deletePlayer,
changeTexture,
changePlayerName,

View File

@ -42,7 +42,7 @@ function downloadUpdates() {
}).catch(showAjaxError);
// Downloading progress polling
let interval_id = window.setInterval(() => {
let interval_id = setInterval(() => {
$('#imported-progress').html(progress);
$('.progress-bar').css('width', progress+'%').attr('aria-valuenow', progress);

View File

@ -33,4 +33,4 @@ function logout() {
});
}
$('#logout-button').click(() => confirmLogout());
$('#logout-button').click(confirmLogout);

View File

@ -1,10 +1,23 @@
/* global MSP */
class TexturePreview {
/**
*
* @param {'steve'|'alex'|'cape'} type
* @param {number} tid
* @param {'default'|'slim'} preference
*/
constructor(type, tid, preference) {
this.tid = tid;
this.type = type;
/**
* @type {JQuery<HTMLImageElement>}
*/
this.selector = $(`#${type}`);
/**
* @type {'default'|'slim'}
*/
this.preference = (type == 'steve') ? 'default' : 'slim';
this.playerPreference = preference;
}
@ -19,13 +32,15 @@ class TexturePreview {
return this;
}
change3dPreview() {
async change3dPreview() {
if (this.playerPreference == this.preference || this.type == 'cape') {
fetch({
type: 'GET',
url: url(`skinlib/info/${this.tid}`),
dataType: 'json'
}).then(({ hash }) => {
try {
const { hash } = await fetch({
type: 'GET',
url: url(`skinlib/info/${this.tid}`),
dataType: 'json'
});
let textureUrl = url(`textures/${hash}`);
if (this.type == 'cape') {
@ -33,7 +48,9 @@ class TexturePreview {
} else {
MSP.changeSkin(textureUrl);
}
}).catch(showAjaxError);
} catch (error) {
showAjaxError(error);
}
}
return this;
@ -49,44 +66,44 @@ class TexturePreview {
return this;
}
static init3dPreview() {
if (TexturePreview.previewType == '2D') return;
$('#preview-2d').hide();
let canvas = null;
if ($(window).width() < 800) {
canvas = MSP.get3dSkinCanvas($('#skinpreview').width(), $('#skinpreview').width());
$('#skinpreview').append($(canvas).prop('id', 'canvas3d'));
} else {
canvas = MSP.get3dSkinCanvas(350, 350);
$('#skinpreview').append($(canvas).prop('id', 'canvas3d'));
}
}
static show3dPreview() {
TexturePreview.previewType = '3D';
TexturePreview.init3dPreview();
$('#preview-2d').hide();
$('.operations').show();
$('#preview-switch').html(trans('user.switch2dPreview'));
}
static show2dPreview() {
TexturePreview.previewType = '2D';
$('#canvas3d').remove();
$('.operations').hide();
$('#preview-2d').show();
$('#preview-switch').html(trans('user.switch3dPreview')).attr('onclick', 'show3dPreview();');
}
}
TexturePreview.previewType = '3D';
TexturePreview.init3dPreview = () => {
if (TexturePreview.previewType == '2D') return;
$('#preview-2d').hide();
let canvas = null;
if ($(window).width() < 800) {
canvas = MSP.get3dSkinCanvas($('#skinpreview').width(), $('#skinpreview').width());
$('#skinpreview').append($(canvas).prop('id', 'canvas3d'));
} else {
canvas = MSP.get3dSkinCanvas(350, 350);
$('#skinpreview').append($(canvas).prop('id', 'canvas3d'));
}
};
TexturePreview.show3dPreview = () => {
TexturePreview.previewType = '3D';
TexturePreview.init3dPreview();
$('#preview-2d').hide();
$('.operations').show();
$('#preview-switch').html(trans('user.switch2dPreview'));
};
TexturePreview.show2dPreview = () => {
TexturePreview.previewType = '2D';
$('#canvas3d').remove();
$('.operations').hide();
$('#preview-2d').show();
$('#preview-switch').html(trans('user.switch3dPreview')).attr('onclick', 'show3dPreview();');
};
// change 3D preview status
$('.fa-pause').click(function () {
MSP.setStatus('rotation', ! MSP.getStatus('rotation'));

View File

@ -26,20 +26,7 @@ $('select.pagination').on('change', function () {
onPageChange(parseInt($(this).val()));
});
$('.filter').click(function (e) {
e.preventDefault();
let selectedFilter = $(this).data('filter');
if (selectedFilter == 'uploader') {
$.skinlib.uploader = $(this).data('uid');
console.log('Show items uploaded by uid ' + $.skinlib.uploader);
} else {
$.skinlib.filter = selectedFilter;
console.log('Filter by ' + $.skinlib.filter);
}
reloadSkinlib();
});
$('.filter').click(updateFilter);
$('.sort').click(function (e) {
e.preventDefault();
@ -174,6 +161,21 @@ function updatePaginator(currentPage, totalPages) {
}
}
function updateFilter(e) {
e.preventDefault();
let selectedFilter = $(this).data('filter');
if (selectedFilter == 'uploader') {
$.skinlib.uploader = $(this).data('uid');
console.log('Show items uploaded by uid ' + $.skinlib.uploader);
} else {
$.skinlib.filter = selectedFilter;
console.log('Filter by ' + $.skinlib.filter);
}
reloadSkinlib();
}
function onPageChange(page, type) {
$.skinlib.page = page;
updateBreadCrumb();
@ -231,5 +233,7 @@ if (typeof require !== 'undefined' && typeof module !== 'undefined') {
updatePaginator,
updateBreadCrumb,
updateUrlQueryString,
onPageChange,
updateFilter,
};
}

View File

@ -4,35 +4,7 @@
var selectedTextures = [];
$(document).ready(async function () {
if (! window.location.pathname.includes('/user/closet'))
return;
$('input[name=q]').on('input', debounce(() => {
let category = $('#skin-category').hasClass('active') ? 'skin' : 'cape';
reloadCloset(category, 1, $('input[name=q]').val());
}, 350));
try {
const { items, category, total_pages } = await fetch({
type: 'GET',
url: url('/user/closet-data'),
dataType: 'json'
});
renderCloset(items, category);
$('#closet-paginator').jqPaginator($.extend({}, $.defaultPaginatorConfig, {
totalPages: total_pages,
onPageChange: page => reloadCloset(
$('#skin-category').hasClass('active') ? 'skin' : 'cape',
page, $('input[name=q]').val()
)
}));
} catch (error) {
showAjaxError(error);
}
});
$(document).ready(initCloset);
$('body').on('click', '.item-body', async function () {
$('.item-selected').parent().removeClass('item-selected');
@ -82,6 +54,36 @@ $('body').on('click', '.category-switch', () => {
reloadCloset(category, page, search);
});
async function initCloset() {
if ($('#closet-container').length !== 1)
return;
$('input[name=q]').on('input', debounce(() => {
let category = $('#skin-category').hasClass('active') ? 'skin' : 'cape';
reloadCloset(category, 1, $('input[name=q]').val());
}, 350));
try {
const { items, category, total_pages } = await fetch({
type: 'GET',
url: url('/user/closet-data'),
dataType: 'json'
});
renderCloset(items, category);
$('#closet-paginator').jqPaginator($.extend({}, $.defaultPaginatorConfig, {
totalPages: total_pages,
onPageChange: page => reloadCloset(
$('#skin-category').hasClass('active') ? 'skin' : 'cape',
page, $('input[name=q]').val()
)
}));
} catch (error) {
showAjaxError(error);
}
}
function renderClosetItemComponent(item) {
return `
<div class="item" tid="${item.tid}" data-texture-type="${item.type}">
@ -305,5 +307,6 @@ if (typeof require !== 'undefined' && typeof module !== 'undefined') {
getCapacityOfCloset,
renameClosetItem,
removeFromCloset,
initCloset,
};
}

View File

@ -23,7 +23,7 @@
<!-- Left col -->
<div class="col-md-8">
<!-- Custom tabs -->
<div class="nav-tabs-custom">
<div class="nav-tabs-custom" id="closet-container">
<!-- Tabs within a box -->
<ul class="nav nav-tabs">
<li class="active"><a href="#skin-category" class="category-switch" data-toggle="tab">{{ trans('general.skin') }}</a></li>