diff --git a/.eslintrc.js b/.eslintrc.js
index cb4c085e..7dd774c4 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -25,9 +25,14 @@ module.exports = {
"getQueryString": false,
"TexturePreview": false
},
+ "parserOptions": {
+ "ecmaVersion": 2017
+ },
"env":{
+ "commonjs": true,
"es6": true,
"browser": true,
+ "jest": true,
"jquery": true
}
};
diff --git a/.gitignore b/.gitignore
index 95fbe922..28b4abc7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
.env
.sass-cache
+coverage
vendor/*
plugins/*
storage/textures/*
diff --git a/package.json b/package.json
index 045bb5e9..941af543 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,9 @@
"scripts": {
"build": "gulp build",
"release": "gulp release",
+ "test": "jest --silent",
+ "test:watch": "jest --silent --watch",
+ "test:cover": "jest --silent --coverage",
"dev": "gulp watch"
},
"devDependencies": {
@@ -27,6 +30,7 @@
"gulp-sass": "^3.1.0",
"gulp-uglify": "^3.0.0",
"gulp-zip": "^4.0.0",
+ "jest": "^20.0.4",
"run-sequence": "^2.0.0"
},
"dependencies": {
@@ -41,5 +45,8 @@
"jquery": "^3.2.1",
"sweetalert2": "^6.6.5",
"toastr": "^2.1.2"
+ },
+ "optionalDependencies": {
+ "@types/jest": "^20.0.2"
}
}
diff --git a/resources/assets/src/js/__tests__/admin.test.js b/resources/assets/src/js/__tests__/admin.test.js
new file mode 100644
index 00000000..3b6acf14
--- /dev/null
+++ b/resources/assets/src/js/__tests__/admin.test.js
@@ -0,0 +1,823 @@
+const $ = require('jquery');
+window.$ = window.jQuery = $;
+
+describe('tests for "customize" module', () => {
+ const modulePath = '../admin/customize';
+
+ it('change skin preview after switching color', () => {
+ window.current_skin = 'skin-blue';
+ 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' }));
+ 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 = '';
+
+ 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');
+ });
+});
+
+describe('tests for "players" module', () => {
+ const modulePath = '../admin/players';
+
+ it('change player reference', async () => {
+ const fetch = jest.fn()
+ .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' }))
+ .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' }));
+ const url = jest.fn(path => path);
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+
+ 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');
+ });
+
+ it('submit changed texture information', async () => {
+ const fetch = jest.fn()
+ .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' }))
+ .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' }));
+ const url = jest.fn(path => path);
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ const modal = jest.fn();
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ window.$.fn.modal = modal;
+
+ 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');
+ });
+
+ it('change player name', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ 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');
+ });
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ window.trans = trans;
+ window.swal = swal;
+
+ document.body.innerHTML = `
+
+ `;
+ const changePlayerName = require(modulePath).changePlayerName;
+
+ 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' }
+ });
+ await changePlayerName(1, 'oldName');
+ expect($('tr#1 > td:nth-child(3)').text()).toBe('newName');
+ expect(toastr.warning).not.toBeCalled();
+ expect(toastr.success).toBeCalledWith('success');
+ });
+
+ it('change owner', async () => {
+ const fetch = jest.fn()
+ .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' }))
+ .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' }));
+ 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);
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ window.trans = trans;
+ window.swal = swal;
+ window.debounce = debounce;
+
+ document.body.innerHTML = `
+
+
+ `;
+ 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).toString());
+ expect(toastr.warning).not.toBeCalled();
+ expect(toastr.success).toBeCalledWith('success');
+ });
+
+ it('delete player', async () => {
+ const fetch = jest.fn()
+ .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' }))
+ .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' }));
+ 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'));
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ window.trans = trans;
+ window.swal = swal;
+
+ document.body.innerHTML = `
+
+ `;
+ const deletePlayer = require(modulePath).deletePlayer;
+
+ 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 }
+ });
+ await deletePlayer(1);
+ expect(document.getElementById('1')).toBeNull();
+ expect(toastr.warning).not.toBeCalled();
+ expect(toastr.success).toBeCalledWith('success');
+ });
+});
+
+describe('tests for "plugins" module', () => {
+ const modulePath = '../admin/plugins';
+
+ it('enable a plugin', async () => {
+ const fetch = jest.fn()
+ .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' }))
+ .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' }));
+ const url = jest.fn(path => path);
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ const reloadTable = jest.fn();
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ $.pluginsTable = {
+ ajax: {
+ reload: reloadTable
+ }
+ };
+
+ const enablePlugin = require(modulePath).enablePlugin;
+
+ await enablePlugin('plugin');
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'admin/plugins?action=enable&name=plugin',
+ dataType: 'json'
+ });
+ expect(toastr.warning).not.toBeCalled();
+ expect(toastr.success).toBeCalledWith('success');
+ expect(reloadTable).toBeCalledWith(null, false);
+
+ await enablePlugin('plugin');
+ expect(toastr.warning).toBeCalledWith('warning');
+ });
+
+ it('disable a plugin', async () => {
+ const fetch = jest.fn()
+ .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' }))
+ .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' }));
+ const url = jest.fn(path => path);
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ const reloadTable = jest.fn();
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ $.pluginsTable = {
+ ajax: {
+ reload: reloadTable
+ }
+ };
+
+ const disablePlugin = require(modulePath).disablePlugin;
+
+ await disablePlugin('plugin');
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'admin/plugins?action=disable&name=plugin',
+ dataType: 'json'
+ });
+ expect(toastr.warning).not.toBeCalled();
+ expect(toastr.success).toBeCalledWith('success');
+ expect(reloadTable).toBeCalledWith(null, false);
+
+ await disablePlugin('plugin');
+ expect(toastr.warning).toBeCalledWith('warning');
+ });
+
+ it('delete a plugin', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ 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());
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ $.pluginsTable = {
+ ajax: {
+ reload: reloadTable
+ }
+ };
+ window.swal = swal;
+
+ const deletePlugin = require(modulePath).deletePlugin;
+
+ await deletePlugin('plugin');
+ expect(swal).toBeCalledWith({
+ text: 'admin.confirmDeletion',
+ type: 'warning',
+ showCancelButton: true
+ });
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'admin/plugins?action=delete&name=plugin',
+ dataType: 'json'
+ });
+ await deletePlugin('plugin');
+ expect(toastr.warning).not.toBeCalled();
+ expect(toastr.success).toBeCalledWith('success');
+ expect(reloadTable).toBeCalledWith(null, false);
+ });
+});
+
+describe('tests for "update" module', () => {
+ it('download updates', async () => {
+ const fetch = jest.fn()
+ .mockReturnValueOnce(Promise.resolve({
+ file_size: 5000
+ }))
+ .mockReturnValueOnce(Promise.resolve());
+ const url = jest.fn(path => path);
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ const modal = jest.fn();
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ $.fn.modal = modal;
+
+ document.body.innerHTML = `
+
+
+ `;
+
+ await require('../admin/update')();
+ expect(fetch).toBeCalledWith(expect.objectContaining({
+ url: 'admin/update/download?action=prepare-download',
+ type: 'GET',
+ dataType: 'json',
+ }));
+ 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'
+ });
+ });
+});
+
+describe('tests for "users" module', () => {
+ const modulePath = '../admin/users';
+
+ it('change user email', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ 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');
+ });
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ window.trans = trans;
+ window.swal = swal;
+
+ document.body.innerHTML = `
+
+ `;
+ const changeUserEmail = require(modulePath).changeUserEmail;
+
+ 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' }
+ });
+ await changeUserEmail(1);
+ expect($('tr > td:nth-child(2)').text()).toBe('a@b.c');
+ expect(toastr.success).toBeCalledWith('success');
+ });
+
+ it('change user nick name', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ 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');
+ });
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ window.trans = trans;
+ window.swal = swal;
+
+ document.body.innerHTML = `
+
+ `;
+ const changeUserNickName = require(modulePath).changeUserNickName;
+
+ 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' }
+ });
+ await changeUserNickName(1);
+ expect($('tr > td:nth-child(3)').text()).toBe('foo');
+ expect(toastr.success).toBeCalledWith('success');
+ });
+
+ it('change user password', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ 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'));
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ window.trans = trans;
+ window.swal = swal;
+
+ const changeUserPwd = require(modulePath).changeUserPwd;
+
+ 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' }
+ });
+ await changeUserPwd(1);
+ expect(toastr.success).toBeCalledWith('success');
+ });
+
+ it('change user score', async () => {
+ const fetch = jest.fn()
+ .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' }))
+ .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' }));
+ const url = jest.fn(path => path);
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+
+ 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');
+ });
+
+ 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' }));
+ const url = jest.fn(path => path);
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ const trans = jest.fn(key => key);
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ window.trans = trans;
+
+ 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');
+ });
+
+ 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' }));
+ const url = jest.fn(path => path);
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ const trans = jest.fn(key => key);
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ window.trans = trans;
+
+ 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');
+ });
+
+ it('delete a user', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ 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());
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ window.trans = trans;
+ window.swal = swal;
+
+ document.body.innerHTML = '
';
+ const deleteUserAccount = require(modulePath).deleteUserAccount;
+
+ 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 }
+ });
+ await deleteUserAccount(1);
+ expect(document.getElementById('user-1')).toBeNull();
+ });
+
+ 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);
+ });
+});
diff --git a/resources/assets/src/js/__tests__/auth.test.js b/resources/assets/src/js/__tests__/auth.test.js
new file mode 100644
index 00000000..cdee354d
--- /dev/null
+++ b/resources/assets/src/js/__tests__/auth.test.js
@@ -0,0 +1,368 @@
+const $ = require('jquery');
+window.$ = window.jQuery = $;
+
+describe('tests for "captcha" module', () => {
+ it('refresh captcha', async () => {
+ const url = jest.fn(path => path);
+ window.url = url;
+
+ document.body.innerHTML = `
+
+
+ `;
+
+ require('../auth/captcha')();
+
+ expect($('.captcha').attr('src')).toEqual(expect.stringContaining('auth/captcha?'));
+ expect($('#captcha').val()).toBe('');
+ });
+});
+
+describe('tests for "login" module', () => {
+ const modulePath = '../auth/login';
+
+ it('login', async () => {
+ const fetch = jest.fn()
+ .mockImplementationOnce(option => {
+ option.beforeSend();
+ return Promise.resolve({ errno: 0, msg: 'success' });
+ })
+ .mockImplementationOnce(() => Promise.resolve(
+ { errno: 1, msg: 'warning1', login_fails: 1 }
+ ))
+ .mockImplementationOnce(() => Promise.resolve(
+ { errno: 1, msg: 'warning2', login_fails: 4 }
+ ));
+ const trans = jest.fn(key => key);
+ const url = jest.fn(path => path);
+ const swal = jest.fn();
+ const refreshCaptcha = jest.fn();
+ window.fetch = fetch;
+ window.trans = trans;
+ window.url = url;
+ window.swal = swal;
+ window.showMsg = jest.fn();
+ window.refreshCaptcha = refreshCaptcha;
+
+ document.body.innerHTML = `
+
+
+
+
+
+
+ `;
+
+ require(modulePath);
+
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.emptyIdentification');
+ expect($('#identification').is(':focus')).toBe(true);
+
+ $('#identification').val('username');
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.emptyPassword');
+ expect($('#password').is(':focus')).toBe(true);
+
+ $('#password').val('password');
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.emptyCaptcha');
+ expect($('#captcha').is(':focus')).toBe(true);
+
+ $('#captcha').val('captcha');
+ await $('button').click();
+ expect(fetch).toBeCalledWith(expect.objectContaining({
+ type: 'POST',
+ url: 'auth/login',
+ dataType: 'json',
+ data: {
+ identification: 'username',
+ password: 'password',
+ keep: true,
+ captcha: 'captcha'
+ }
+ }));
+ expect($('button').html()).toBe(
+ ' auth.loggingIn'
+ );
+ expect($('button').prop('disabled')).toBe(true);
+ expect(swal).toBeCalledWith({ type: 'success', html: 'success' });
+
+ $('#captcha-form').css('display', 'none');
+ await $('button').click();
+ expect($('#captcha-form').css('display')).toBe('none');
+ expect(refreshCaptcha).toBeCalled();
+ expect(showMsg).toBeCalledWith('warning1', 'warning');
+ expect($('button').html()).toBe('auth.login');
+ expect($('button').prop('disabled')).toBe(false);
+
+ await $('button').click();
+ expect(swal).toBeCalledWith({ type: 'error', html: 'auth.tooManyFails' });
+ expect($('#captcha-form').css('display')).not.toBe('none');
+ expect(showMsg).toBeCalledWith('warning2', 'warning');
+ });
+});
+
+describe('tests for "register" module', () => {
+ const modulePath = '../auth/register';
+
+ it('register', async () => {
+ const fetch = jest.fn()
+ .mockImplementationOnce(option => {
+ option.beforeSend();
+ return Promise.resolve({ errno: 0, msg: 'success' });
+ })
+ .mockImplementationOnce(() => Promise.resolve(
+ { errno: 1, msg: 'warning' }
+ ));
+ const trans = jest.fn(key => key);
+ const url = jest.fn(path => path);
+ const swal = jest.fn();
+ const showMsg = jest.fn();
+ const refreshCaptcha = jest.fn();
+ window.fetch = fetch;
+ window.trans = trans;
+ window.url = url;
+ window.swal = swal;
+ window.showMsg = showMsg;
+ window.refreshCaptcha = refreshCaptcha;
+
+ document.body.innerHTML = `
+
+
+
+
+
+
+
+ `;
+
+ require(modulePath);
+
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.emptyEmail');
+ expect($('#email').is(':focus')).toBe(true);
+
+ $('#email').val('email');
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.invalidEmail');
+ expect(showMsg).toBeCalledWith('auth.invalidEmail', 'warning');
+ expect($('#email').is(':focus')).toBe(true);
+
+ $('#email').val('a@b.c');
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.emptyPassword');
+ expect($('#password').is(':focus')).toBe(true);
+
+ $('#password').val('secret');
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.invalidPassword');
+ expect(showMsg).toBeCalledWith('auth.invalidPassword', 'warning');
+ expect($('#password').is(':focus')).toBe(true);
+
+ $('#password').val('too_long_password');
+ $('#password').blur();
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.invalidPassword');
+ expect(showMsg).toBeCalledWith('auth.invalidPassword', 'warning');
+ expect($('#password').is(':focus')).toBe(true);
+
+ $('#password').val('password');
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.emptyConfirmPwd');
+ expect($('#confirm-pwd').is(':focus')).toBe(true);
+
+ $('#confirm-pwd').val('not_same');
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.invalidConfirmPwd');
+ expect(showMsg).toBeCalledWith('auth.invalidConfirmPwd', 'warning');
+ expect($('#confirm-pwd').is(':focus')).toBe(true);
+
+ $('#confirm-pwd').val('password');
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.emptyNickname');
+ expect($('#nickname').is(':focus')).toBe(true);
+
+ $('#nickname').val('nickname');
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.emptyCaptcha');
+ expect($('#captcha').is(':focus')).toBe(true);
+
+ $('#captcha').val('captcha');
+ await $('button').click();
+ expect(fetch).toBeCalledWith(expect.objectContaining({
+ type: 'POST',
+ url: 'auth/register',
+ dataType: 'json',
+ data: {
+ email: 'a@b.c',
+ nickname: 'nickname',
+ password: 'password',
+ captcha: 'captcha'
+ }
+ }));
+ expect($('button').html()).toBe(
+ ' auth.registering'
+ );
+ expect($('button').prop('disabled')).toBe(true);
+ expect(swal).toBeCalledWith({ type: 'success', html: 'success' });
+
+ await $('button').click();
+ expect(refreshCaptcha).toBeCalled();
+ expect(showMsg).toBeCalledWith('warning', 'warning');
+ expect($('button').html()).toBe('auth.register');
+ });
+});
+
+describe('tests for "forgot" module', () => {
+ const modulePath = '../auth/forgot';
+
+ it('forgot password', async () => {
+ const fetch = jest.fn()
+ .mockImplementationOnce(option => {
+ option.beforeSend();
+ return Promise.resolve({ errno: 0, msg: 'success' });
+ })
+ .mockImplementationOnce(() => Promise.resolve(
+ { errno: 1, msg: 'warning' }
+ ));
+ const trans = jest.fn(key => key);
+ const url = jest.fn(path => path);
+ const swal = jest.fn();
+ const showMsg = jest.fn();
+ const refreshCaptcha = jest.fn();
+ window.fetch = fetch;
+ window.trans = trans;
+ window.url = url;
+ window.swal = swal;
+ window.showMsg = showMsg;
+ window.refreshCaptcha = refreshCaptcha;
+
+ document.body.innerHTML = `
+
+
+
+
+ `;
+
+ require(modulePath);
+
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.emptyEmail');
+ expect($('#email').is(':focus')).toBe(true);
+
+ $('#email').val('email');
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.invalidEmail');
+ expect(showMsg).toBeCalledWith('auth.invalidEmail', 'warning');
+ expect($('#email').is(':focus')).toBe(true);
+
+ $('#email').val('a@b.c');
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.emptyCaptcha');
+ expect($('#captcha').is(':focus')).toBe(true);
+
+ $('#captcha').val('captcha');
+ await $('button').click();
+ expect(fetch).toBeCalledWith(expect.objectContaining({
+ type: 'POST',
+ url: 'auth/forgot',
+ dataType: 'json',
+ data: {
+ email: 'a@b.c',
+ captcha: 'captcha'
+ }
+ }));
+ expect($('button').html()).toBe('auth.send');
+ expect($('button').prop('disabled')).toBe(true);
+ expect(showMsg).toBeCalledWith('success', 'success');
+
+ await $('button').click();
+ expect(refreshCaptcha).toBeCalled();
+ expect(showMsg).toBeCalledWith('warning', 'warning');
+ expect($('button').html()).toBe('auth.send');
+ });
+});
+
+describe('tests for "reset" module', () => {
+ const modulePath = '../auth/reset';
+
+ it('reset password', async () => {
+ const fetch = jest.fn()
+ .mockImplementationOnce(option => {
+ option.beforeSend();
+ return Promise.resolve({ errno: 0, msg: 'success' });
+ })
+ .mockImplementationOnce(() => Promise.resolve(
+ { errno: 1, msg: 'warning' }
+ ));
+ const trans = jest.fn(key => key);
+ const url = jest.fn(path => path);
+ const swal = jest.fn().mockReturnValue(Promise.resolve());
+ const showMsg = jest.fn();
+ window.fetch = fetch;
+ window.trans = trans;
+ window.url = url;
+ window.swal = swal;
+ window.showMsg = showMsg;
+ window.refreshCaptcha = jest.fn();
+
+ document.body.innerHTML = `
+
+
+
+
+ `;
+
+ require(modulePath);
+
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.emptyPassword');
+ expect($('#password').is(':focus')).toBe(true);
+
+ $('#password').val('secret');
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.invalidPassword');
+ expect(showMsg).toBeCalledWith('auth.invalidPassword', 'warning');
+ expect($('#password').is(':focus')).toBe(true);
+
+ $('#password').val('too_long_password');
+ $('#password').blur();
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.invalidPassword');
+ expect(showMsg).toBeCalledWith('auth.invalidPassword', 'warning');
+ expect($('#password').is(':focus')).toBe(true);
+
+ $('#password').val('password');
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.emptyConfirmPwd');
+ expect($('#confirm-pwd').is(':focus')).toBe(true);
+
+ $('#confirm-pwd').val('not_same');
+ $('button').click();
+ expect(trans).toBeCalledWith('auth.invalidConfirmPwd');
+ expect(showMsg).toBeCalledWith('auth.invalidConfirmPwd', 'warning');
+ expect($('#confirm-pwd').is(':focus')).toBe(true);
+
+ $('#confirm-pwd').val('password');
+ await $('button').click();
+ expect(fetch).toBeCalledWith(expect.objectContaining({
+ type: 'POST',
+ url: 'auth/reset',
+ dataType: 'json',
+ data: {
+ uid: '1',
+ password: 'password'
+ }
+ }));
+ expect($('button').html()).toBe(
+ ' auth.resetting'
+ );
+ expect($('button').prop('disabled')).toBe(true);
+ expect(swal).toBeCalledWith({ type: 'success', html: 'success' });
+
+ await $('button').click();
+ expect(showMsg).toBeCalledWith('warning', 'warning');
+ expect($('button').html()).toBe('auth.reset');
+ });
+});
diff --git a/resources/assets/src/js/__tests__/common.test.js b/resources/assets/src/js/__tests__/common.test.js
new file mode 100644
index 00000000..5e729fa1
--- /dev/null
+++ b/resources/assets/src/js/__tests__/common.test.js
@@ -0,0 +1,179 @@
+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;
+
+ 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!');
+ });
+});
+
+describe('tests for "logout" module', () => {
+ const modulePath = '../common/logout';
+
+ it('logout', async () => {
+ const swal = jest.fn().mockReturnValue(Promise.resolve());
+ const trans = jest.fn(key => key);
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ msg: 'success' }));
+ window.swal = swal;
+ window.trans = trans;
+ window.fetch = fetch;
+ window.url = jest.fn(path => path);
+
+ document.body.innerHTML = '';
+ require(modulePath);
+
+ 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'
+ });
+ await $('button').click();
+ expect(swal).toBeCalledWith({ type: 'success', html: 'success' });
+ });
+});
+
+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', () => {
+ const warn = jest.fn();
+ window.console.warn = warn;
+ window.trans = jest.fn(key => key);
+
+ const showAjaxError = require(modulePath).showAjaxError;
+
+ showAjaxError('error');
+ expect(warn).toBeCalledWith('error');
+
+ showAjaxError({});
+ expect(warn).toBeCalledWith('Empty Ajax response body.');
+ });
+
+ 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);
+ });
+
+ 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 debounced = require(modulePath).debounce(func, 100);
+
+ debounced();
+ debounced();
+
+ setTimeout(() => {
+ expect(func.mock.calls.length).toBe(1);
+ done();
+ }, 100);
+ });
+
+ 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');
+ });
+});
diff --git a/resources/assets/src/js/__tests__/skinlib.test.js b/resources/assets/src/js/__tests__/skinlib.test.js
new file mode 100644
index 00000000..c1bcf701
--- /dev/null
+++ b/resources/assets/src/js/__tests__/skinlib.test.js
@@ -0,0 +1,490 @@
+const $ = require('jquery');
+window.$ = window.jQuery = $;
+
+window.getQueryString = jest.fn((key, defaultValue) => defaultValue);
+
+describe('tests for "index" module', () => {
+ const modulePath = '../skinlib/index';
+
+ it('render skin library', () => {
+ const trans = jest.fn(key => key);
+ const url = jest.fn(path => path);
+ const jqPaginator = jest.fn();
+ window.trans = trans;
+ window.url = url;
+ $.fn.jqPaginator = jqPaginator;
+
+ document.body.innerHTML = `
+
+
+
+ `;
+ const renderSkinlib = require(modulePath).renderSkinlib;
+
+ renderSkinlib([]);
+ expect($('#skinlib-container').html()).toBe(
+ 'general.noResult
'
+ );
+ expect($('#skinlib-paginator').css('display')).toBe('none');
+ expect($('.overlay').css('display')).toBe('none');
+
+ renderSkinlib([{
+ tid: 1,
+ name: 'name',
+ type: 'steve',
+ public: 0
+ }]);
+ expect($('#skinlib-paginator').css('display')).not.toBe('none');
+ expect($('.item').attr('tid')).toBe('1');
+ expect($('.item-body > img').attr('src')).toBe('preview/1.png');
+ expect($('.texture-name > span').attr('title')).toBe('name');
+ expect($('.texture-name > span > small').text()).toBe('skinlib.filter.steve');
+ expect($('a.more.like').attr('title')).toBe('skinlib.anonymous');
+ expect($('a.more.like').hasClass('liked')).toBe(false);
+ expect($('a.more.like').hasClass('anonymous')).toBe(true);
+ expect($('small.more').hasClass('hide')).toBe(false);
+ expect($('small.private-label').text().trim()).toBe('skinlib.private');
+
+ renderSkinlib([{
+ tid: 1,
+ name: 'name',
+ type: 'steve',
+ public: 0,
+ liked: true
+ }]);
+ expect($('a.more.like').attr('title')).toBe('skinlib.removeFromCloset');
+ expect($('a.more.like').hasClass('liked')).toBe(true);
+ expect($('a.more.like').hasClass('anonymous')).toBe(false);
+ expect($('small.more').hasClass('hide')).toBe(false);
+ expect($('small.private-label').text().trim()).toBe('skinlib.private');
+
+ renderSkinlib([{
+ tid: 1,
+ name: 'name',
+ type: 'steve',
+ public: 0,
+ liked: false
+ }]);
+ expect($('a.more.like').attr('title')).toBe('skinlib.addToCloset');
+ expect($('a.more.like').hasClass('liked')).toBe(false);
+ expect($('a.more.like').hasClass('anonymous')).toBe(false);
+ expect($('small.more').hasClass('hide')).toBe(false);
+ expect($('small.private-label').text().trim()).toBe('skinlib.private');
+
+ renderSkinlib([{
+ tid: 1,
+ name: 'name',
+ type: 'steve',
+ public: 1,
+ liked: false
+ }]);
+ expect($('small.more').hasClass('hide')).toBe(true);
+ });
+
+ it('update paginator', () => {
+ const trans = jest.fn(key => key);
+ const jqPaginator = jest.fn();
+ window.trans = trans;
+ $.fn.jqPaginator = jqPaginator;
+
+ document.body.innerHTML = `
+
+
+
+ `;
+ const updatePaginator = require(modulePath).updatePaginator;
+
+ updatePaginator(2, 2);
+ expect(trans).toBeCalledWith('general.pagination', { page: 2, total: 2 });
+ expect(jqPaginator).toBeCalledWith(expect.objectContaining({
+ currentPage: 2,
+ totalPages: 2
+ }));
+ expect($('option').length).toBe(2);
+ expect($('option[value=1]').prop('selected')).toBe(false);
+ expect($('option[value=2]').prop('selected')).toBe(true);
+
+ $('#skinlib-paginator').html('something');
+ updatePaginator(2, 2);
+ expect(jqPaginator).toBeCalledWith('option', {
+ currentPage: 2,
+ totalPages: 2
+ });
+ });
+
+ it('update breadcrumb', () => {
+ const trans = jest.fn(key => key);
+ const jqPaginator = jest.fn();
+ window.trans = trans;
+ $.fn.jqPaginator = jqPaginator;
+
+ document.body.innerHTML = `
+
+
+
+
+
+ `;
+ const updateBreadCrumb = require(modulePath).updateBreadCrumb;
+
+ updateBreadCrumb();
+ expect($('#filter-indicator').html().replace(/\s/g, '')).toBe(
+ 'general.skinskinlib.filter.skin'
+ );
+
+ $.skinlib.filter = 'cape';
+ updateBreadCrumb();
+ expect($('#filter-indicator').html()).toBe('general.cape');
+
+ expect($('#uploader-indicator').html()).toBe('skinlib.filter.allUsers');
+ $.skinlib.uploader = 1;
+ updateBreadCrumb();
+ expect(trans).toBeCalledWith('skinlib.filter.uploader', { uid: 1 });
+ expect($('#uploader-indicator').html()).toBe('skinlib.filter.uploader');
+
+ expect($('#sort-indicator').html()).toBe('skinlib.sort.time');
+
+ $.skinlib.keyword = '%20q';
+ updateBreadCrumb();
+ expect(trans).lastCalledWith(
+ 'general.searchResult',
+ { keyword: ' q' }
+ );
+ expect($('#search-indicator').html()).toBe('general.searchResult');
+ expect($('#navbar-search-input').val()).toBe(' q');
+ });
+
+ it('reload skin library', async () => {
+ const fetch = jest.fn().mockReturnValue(Promise.resolve({
+ items: []
+ }));
+ const url = jest.fn(path => path);
+ window.fetch = fetch;
+ window.url = url;
+ window.showAjaxError = jest.fn();
+ document.body.innerHTML = `
+
+ `;
+ const reloadSkinlib = require(modulePath).reloadSkinlib;
+ window.history.pushState = jest.fn();
+
+ await reloadSkinlib();
+ expect(fetch).toBeCalledWith(expect.objectContaining({
+ type: 'GET',
+ url: 'skinlib/data',
+ dataType: 'json',
+ data: {
+ page: 2,
+ filter: 'cape',
+ sort: 'time',
+ uploader: 1,
+ keyword: '%20q'
+ }
+ }));
+ });
+
+ it('update query string', () => {
+ const url = jest.fn(path => path);
+ window.url = url;
+ document.body.innerHTML = `
+
+ `;
+ const updateUrlQueryString = require(modulePath).updateUrlQueryString;
+ window.history.pushState = jest.fn();
+
+ const query = 'page=2&filter=cape&sort=time&uploader=1&keyword=%2520q';
+
+ updateUrlQueryString();
+ expect(window.history.pushState).toBeCalledWith(
+ null,
+ null,
+ 'skinlib?' + query);
+ 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}`);
+ });
+});
+
+describe('tests for "operations" module', () => {
+ const modulePath = '../skinlib/operations';
+
+ it('add to closet', async () => {
+ 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('custom');
+ return Promise.resolve('custom');
+ });
+ window.swal = swal;
+ $.getJSON = jest.fn((option, cb) => {
+ cb({ name: 'name' });
+ });
+
+ const addToCloset = require(modulePath).addToCloset;
+
+ await addToCloset(1);
+ expect($.getJSON.mock.calls[0][0]).toBe('skinlib/info/1');
+ expect(swal).toBeCalledWith(expect.objectContaining({
+ title: 'skinlib.setItemName',
+ inputValue: 'name',
+ input: 'text',
+ showCancelButton: true,
+ }));
+ });
+
+ 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' }));
+ 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());
+ window.swal = swal;
+ const modal = jest.fn();
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ window.toastr = toastr;
+ $.fn.modal = modal;
+
+ document.body.innerHTML = `
+
+
+ `;
+ const ajaxAddToCloset = require(modulePath).ajaxAddToCloset;
+
+ await ajaxAddToCloset(1, 'name');
+ expect(document.getElementById('shouldBeRemoved')).toBeNull();
+ expect(document.getElementById('shouldNotBeRemoved')).not.toBeNull();
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'user/closet/add',
+ dataType: 'json',
+ data: { tid: 1, name: 'name' }
+ });
+ expect(swal).toBeCalledWith({ type: 'success', html: 'success' });
+ expect(modal).toBeCalledWith('hide');
+
+ await ajaxAddToCloset(1, 'name');
+ expect(toastr.warning).toBeCalledWith('warning');
+ });
+
+ it('remove from closet', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ 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());
+ window.swal = swal;
+ const modal = jest.fn();
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ window.toastr = toastr;
+
+ const removeFromCloset = require(modulePath).removeFromCloset;
+
+ await removeFromCloset(1);
+ expect(swal).toBeCalledWith({
+ text: 'user.removeFromClosetNotice',
+ type: 'warning',
+ showCancelButton: true,
+ cancelButtonColor: '#3085d6',
+ confirmButtonColor: '#d33'
+ });
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: '/user/closet/remove',
+ dataType: 'json',
+ data: { tid: 1 }
+ });
+ await removeFromCloset(1);
+ expect(swal).toBeCalledWith({ type: 'success', html: 'success' });
+ });
+
+ it('change texture name', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ 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');
+ });
+ window.swal = swal;
+ const modal = jest.fn();
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ window.toastr = toastr;
+
+ document.body.innerHTML = '';
+ const changeTextureName = require(modulePath).changeTextureName;
+
+ await changeTextureName(1, 'oldName');
+ expect(swal).toBeCalledWith(expect.objectContaining({
+ text: 'skinlib.setNewTextureName',
+ input: 'text',
+ inputValue: 'oldName',
+ showCancelButton: true,
+ }));
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'skinlib/rename',
+ dataType: 'json',
+ data: { tid: 1, new_name: 'new-name' }
+ });
+ await changeTextureName(1, 'oldName');
+ expect($('div').text()).toBe('new-name');
+ expect(toastr.success).toBeCalledWith('success');
+ });
+
+ it('update texture status', () => {
+ window.trans = jest.fn(key => key);
+ document.body.innerHTML = `
+ 5
+
+
+ `;
+ const updateTextureStatus = require(modulePath).updateTextureStatus;
+
+ updateTextureStatus(1, 'add');
+ expect($('a[tid=1]').attr('href')).toBe('javascript:removeFromCloset(1);');
+ expect($('a[tid=1]').attr('title')).toBe('skinlib.removeFromCloset');
+ expect($('a[tid=1]').hasClass('liked')).toBe(true);
+ expect($('#1').attr('href')).toBe('javascript:removeFromCloset(1);');
+ expect($('#1').html()).toBe('skinlib.removeFromCloset');
+ expect($('div').html()).toBe('6');
+
+ updateTextureStatus(1, 'remove');
+ expect($('a[tid=1]').attr('href')).toBe('javascript:addToCloset(1);');
+ expect($('a[tid=1]').attr('title')).toBe('skinlib.addToCloset');
+ expect($('a[tid=1]').hasClass('liked')).toBe(false);
+ expect($('#1').attr('href')).toBe('javascript:addToCloset(1);');
+ expect($('#1').html()).toBe('skinlib.addToCloset');
+ expect($('div').html()).toBe('5');
+ });
+
+ it('click changing privacy button', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ 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());
+ window.swal = swal;
+ const modal = jest.fn();
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ window.toastr = toastr;
+
+ document.body.innerHTML = '';
+ require(modulePath);
+
+ await $('a').click();
+ expect(swal).toBeCalledWith({
+ text: 'skinlib.setPublicNotice',
+ type: 'warning',
+ showCancelButton: true
+ });
+ expect(document.getElementsByTagName('a').length).toBe(0);
+ });
+
+ it('change privacy', async () => {
+ 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' }));
+ 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());
+ window.swal = swal;
+ const modal = jest.fn();
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ window.toastr = toastr;
+
+ document.body.innerHTML = `
+ skinlib.setAsPrivate
+ skinlib.setAsPublic
+ `;
+ const changePrivacy = require(modulePath).changePrivacy;
+
+ await changePrivacy(1);
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'skinlib/privacy',
+ dataType: 'json',
+ data: { tid: 1 }
+ });
+ expect(toastr.warning).not.toBeCalled();
+ expect(toastr.success).toBeCalledWith('success');
+ expect($('#1').html()).toBe('skinlib.setAsPublic');
+
+ await changePrivacy(1);
+ expect($('#2').html()).toBe('skinlib.setAsPrivate');
+
+ await changePrivacy(1);
+ expect(toastr.warning).toBeCalledWith('warning');
+ });
+
+ it('delete texture', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ 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());
+ window.swal = swal;
+ const modal = jest.fn();
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ window.toastr = toastr;
+
+ const deleteTexture = require(modulePath).deleteTexture;
+
+ await deleteTexture(1);
+ expect(swal).toBeCalledWith({
+ text: 'skinlib.deleteNotice',
+ type: 'warning',
+ showCancelButton: true
+ });
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'skinlib/delete',
+ dataType: 'json',
+ data: { tid: 1 }
+ });
+ await deleteTexture(1);
+ expect(swal).toBeCalledWith({ type: 'success', html: 'success' });
+ });
+});
diff --git a/resources/assets/src/js/__tests__/user.test.js b/resources/assets/src/js/__tests__/user.test.js
new file mode 100644
index 00000000..141f80e5
--- /dev/null
+++ b/resources/assets/src/js/__tests__/user.test.js
@@ -0,0 +1,868 @@
+const $ = require('jquery');
+window.$ = window.jQuery = $;
+
+describe('tests for "closet" module', () => {
+ const modulePath = '../user/closet';
+
+ $.fn.jqPaginator = jest.fn();
+
+ it('preview textures', async () => {
+ const fetch = jest.fn()
+ .mockReturnValueOnce(Promise.resolve({ type: 'skin', hash: 1 }))
+ .mockReturnValueOnce(Promise.resolve({ type: 'cape', hash: 2 }));
+ const trans = jest.fn(key => key);
+ const url = jest.fn(path => path);
+ const MSP = {
+ changeSkin: jest.fn(),
+ changeCape: jest.fn()
+ };
+ window.fetch = fetch;
+ window.trans = trans;
+ window.url = url;
+ window.MSP = MSP;
+
+ document.body.innerHTML = `
+
+
+
+ `;
+ require(modulePath);
+
+ await $('#next > .item-body').click();
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'skinlib/info/1',
+ dataType: 'json'
+ });
+ expect($('#next').hasClass('item-selected')).toBe(true);
+ expect(MSP.changeSkin).toBeCalledWith('textures/1');
+ expect($('#textures-indicator').text()).toBe('general.skin');
+
+ document.body.innerHTML = `
+
+
+
+ `;
+ window.selectedTextures = [];
+
+ await $('#next > .item-body').click();
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'skinlib/info/2',
+ dataType: 'json'
+ });
+ expect($('#next').hasClass('item-selected')).toBe(true);
+ expect(MSP.changeCape).toBeCalledWith('textures/2');
+ expect($('#textures-indicator').text()).toBe('general.skin & general.cape');
+ });
+
+ it('render closet', () => {
+ const trans = jest.fn(key => key);
+ const url = jest.fn(path => path);
+ window.trans = trans;
+ window.url = url;
+
+ document.body.innerHTML = `
+
+
+
+ `;
+ const renderCloset = require(modulePath).renderCloset;
+
+ renderCloset([], 'skin');
+ expect($('#closet-paginator').css('display')).toBe('none');
+ expect(trans).toBeCalledWith('user.emptyClosetMsg', { url: 'skinlib?filter=skin' });
+ expect($('#skin-category').html()).toBe(
+ 'user.emptyClosetMsg
'
+ );
+
+ $('input').val('q');
+ renderCloset([], 'skin');
+ expect($('#skin-category').html()).toBe(
+ 'general.noResult
'
+ );
+
+ renderCloset([{ tid: 1, name: 'name', type: 'steve' }], 'skin');
+ expect($('#closet-paginator').css('display')).not.toBe('none');
+ expect($('.item').attr('tid')).toBe('1');
+ expect($('img').attr('src')).toBe('/preview/1.png');
+ expect($('.texture-name').html().trim()).toBe(
+ 'name (steve)'
+ );
+ expect($('a.more').attr('href')).toBe('/skinlib/show/1');
+ expect($('a.more').attr('title')).toBe('user.viewInSkinlib');
+ });
+
+ it('reload closet', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({
+ items: [],
+ category: 'skin',
+ total_pages: 1
+ }));
+ 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()
+ };
+ const MSP = {
+ changeSkin: jest.fn(),
+ changeCape: jest.fn()
+ };
+ window.fetch = fetch;
+ window.trans = trans;
+ window.url = url;
+ window.swal = swal;
+ window.toastr = toastr;
+
+ document.body.innerHTML = `
+
+
+ `;
+ const reloadCloset = require(modulePath).reloadCloset;
+
+ await reloadCloset('skin', 1, 'q');
+ expect(fetch).toBeCalledWith({
+ type: 'GET',
+ url: url('user/closet-data'),
+ dataType: 'json',
+ data: {
+ category: 'skin',
+ page: 1,
+ q: 'q'
+ }
+ });
+ expect($('#closet-paginator').attr('last-skin-page')).toBe('1');
+ });
+
+ it('rename item', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ 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()
+ };
+ const MSP = {
+ changeSkin: jest.fn(),
+ changeCape: jest.fn()
+ };
+ window.fetch = fetch;
+ window.trans = trans;
+ window.url = url;
+ window.swal = swal;
+ window.toastr = toastr;
+
+ document.body.innerHTML = `
+
+ `;
+ const renameClosetItem = require(modulePath).renameClosetItem;
+
+ await renameClosetItem(1, 'oldName');
+ expect(swal).toBeCalledWith(expect.objectContaining({
+ title: trans('user.renameClosetItem'),
+ input: 'text',
+ inputValue: 'oldName',
+ showCancelButton: true,
+ }));
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'closet/rename',
+ dataType: 'json',
+ data: { tid: 1, new_name: 'name' }
+ });
+
+ await renameClosetItem(1, 'oldName');
+ expect(toastr.success).toBeCalledWith('success');
+ expect($('span').html('name'));
+ });
+
+ it('remove item from closet', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ const trans = jest.fn(key => key);
+ const url = jest.fn(path => path);
+ const swal = jest.fn().mockReturnValue(Promise.resolve());
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ const MSP = {
+ changeSkin: jest.fn(),
+ changeCape: jest.fn()
+ };
+ window.fetch = fetch;
+ window.trans = trans;
+ window.url = url;
+ window.swal = swal;
+ window.toastr = toastr;
+
+ document.body.innerHTML = `
+
+ `;
+ const removeFromCloset = require(modulePath).removeFromCloset;
+
+ await removeFromCloset(1);
+ expect(swal).toBeCalledWith({
+ text: 'user.removeFromClosetNotice',
+ type: 'warning',
+ showCancelButton: true
+ });
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'closet/remove',
+ 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
'
+ );
+ });
+
+ it('set avatar', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ const trans = jest.fn(key => key);
+ const url = jest.fn(path => path);
+ const swal = jest.fn().mockReturnValue(Promise.resolve());
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ const MSP = {
+ changeSkin: jest.fn(),
+ changeCape: jest.fn()
+ };
+ window.fetch = fetch;
+ window.trans = trans;
+ window.url = url;
+ window.swal = swal;
+ window.toastr = toastr;
+
+ document.body.innerHTML = `
+
+ `;
+ const setAsAvatar = require(modulePath).setAsAvatar;
+
+ await setAsAvatar(1);
+ expect(swal).toBeCalledWith({
+ title: 'user.setAvatar',
+ text: 'user.setAvatarNotice',
+ type: 'question',
+ showCancelButton: true
+ });
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'user/profile/avatar',
+ dataType: 'json',
+ data: { tid: 1 }
+ });
+
+ await setAsAvatar(1);
+ expect(toastr.success).toBeCalledWith('success');
+ expect($('img').attr('src').endsWith('src')).toBe(false);
+ });
+});
+
+describe('tests for "player" module', () => {
+ const modulePath = '../user/player';
+
+ it('show player texture preview', async () => {
+ const url = jest.fn(path => path);
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({
+ tid_steve: 1,
+ tid_alex: 2,
+ tid_cape: 3,
+ preference: 'steve',
+ player_name: 'name'
+ }));
+ window.url = url;
+ window.fetch = fetch;
+ window.TexturePreview = require('../common/texture-preview');
+ window.MSP = {
+ changeSkin: jest.fn(),
+ changeCape: jest.fn(),
+ setStatus: jest.fn(),
+ getStatus: jest.fn()
+ };
+
+ document.body.innerHTML = `
+
+
+ `;
+ require(modulePath);
+
+ await $('#2').click();
+ expect($('#1').hasClass('player-selected')).toBe(false);
+ expect($('#2').hasClass('player-selected')).toBe(true);
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'user/player/show',
+ dataType: 'json',
+ data: { pid: '2' }
+ });
+ });
+
+ it('change player reference', async () => {
+ const fetch = jest.fn()
+ .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' }))
+ .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' }));
+ const url = jest.fn(path => path);
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+
+ document.body.innerHTML = `
+
+ `;
+ $('select').on('change', require(modulePath).changePreference);
+
+ await $('select').val('slim').trigger('change');
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'user/player/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');
+ });
+
+ it('change player name', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ const url = jest.fn(path => path);
+ const trans = jest.fn(key => key);
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ const swal = jest.fn()
+ .mockImplementationOnce(options => {
+ options.inputValidator('name');
+ return Promise.resolve('name');
+ })
+ .mockImplementation(() => Promise.resolve());
+ window.fetch = fetch;
+ window.url = url;
+ window.trans = trans;
+ window.toastr = toastr;
+ window.swal = swal;
+
+ document.body.innerHTML = `
+
+
+ `;
+ const changePlayerName = require(modulePath).changePlayerName;
+
+ await changePlayerName(1);
+ expect(swal).toBeCalledWith(expect.objectContaining({
+ title: 'user.changePlayerName',
+ text: 'placeholder',
+ inputValue: 'old',
+ input: 'text',
+ showCancelButton: true
+ }));
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'user/player/rename',
+ 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');
+ });
+
+ it('submit clearing texture request', async () => {
+ const fetch = jest.fn()
+ .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' }))
+ .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' }));
+ const url = jest.fn(path => path);
+ const trans = jest.fn(key => key);
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ const modal = jest.fn();
+ window.fetch = fetch;
+ window.url = url;
+ window.trans = trans;
+ window.toastr = toastr;
+ $.fn.modal = modal;
+
+ document.body.innerHTML = `
+
+
+
+
+
+ `;
+ const ajaxClearTexture = require(modulePath).ajaxClearTexture;
+
+ ajaxClearTexture(1);
+ expect(document.getElementById('shouldBeRemoved')).toBeNull();
+ expect(document.getElementById('shouldNotBeRemoved')).not.toBeNull();
+ expect(toastr.warning).toBeCalledWith('user.noClearChoice');
+
+ $('#clear-steve').prop('checked', true);
+ await ajaxClearTexture(1);
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'user/player/texture/clear',
+ dataType: 'json',
+ data: { pid: 1, steve: 1, alex: 0, cape: 0 }
+ });
+ expect(swal).toBeCalledWith({ type: 'success', html: 'success' });
+ expect(modal).toBeCalledWith('hide');
+
+ await ajaxClearTexture(1);
+ expect(swal).lastCalledWith({ type: 'error', html: 'warning' });
+ });
+
+ it('delete player', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ const url = jest.fn(path => path);
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ const swal = jest.fn().mockReturnValue(Promise.resolve());
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ window.swal = swal;
+
+ document.body.innerHTML = `
+
+ `;
+ const deletePlayer = require(modulePath).deletePlayer;
+
+ await deletePlayer(1);
+ expect(swal).toBeCalledWith({
+ title: 'user.deletePlayer',
+ text: 'user.deletePlayerNotice',
+ type: 'warning',
+ showCancelButton: true,
+ cancelButtonColor: '#3085d6',
+ confirmButtonColor: '#d33'
+ });
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'user/player/delete',
+ dataType: 'json',
+ data: { pid: 1 }
+ });
+
+ await deletePlayer(1);
+ expect(swal).lastCalledWith({ type: 'success', html: 'success' });
+ expect(document.getElementById('1')).toBeNull();
+ });
+
+ it('add a new player', async () => {
+ const fetch = jest.fn()
+ .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' }))
+ .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' }));
+ const url = jest.fn(path => path);
+ const toastr = {
+ success: jest.fn(),
+ warning: jest.fn()
+ };
+ const swal = jest.fn().mockReturnValue(Promise.resolve());
+ const modal = jest.fn();
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ window.swal = swal;
+ $.fn.modal = modal;
+
+ document.body.innerHTML = `
+
+ `;
+ const addNewPlayer = require(modulePath).addNewPlayer;
+
+ await addNewPlayer();
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'user/player/add',
+ dataType: 'json',
+ data: { player_name: 'name' }
+ });
+ expect(swal).toBeCalledWith({ type: 'success', html: 'success' });
+
+ await addNewPlayer();
+ expect(toastr.warning).toBeCalledWith('warning');
+ expect(modal.mock.calls.length).toBe(1);
+ });
+
+ it('set texture', async () => {
+ const fetch = jest.fn()
+ .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' }))
+ .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' }));
+ 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 modal = jest.fn();
+ window.fetch = fetch;
+ window.url = url;
+ window.toastr = toastr;
+ window.swal = swal;
+ $.fn.modal = modal;
+ window.selectedTextures = {};
+
+ document.body.innerHTML = `
+
+ `;
+ const setTexture = require(modulePath).setTexture;
+
+ setTexture();
+ expect(toastr.info).toBeCalledWith('user.emptySelectedPlayer');
+
+ $('input').prop('checked', true);
+ setTexture();
+ expect(toastr.info).toBeCalledWith('user.emptySelectedTexture');
+
+ window.selectedTextures = { skin: 1, cape: 2 };
+ await setTexture();
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'user/player/set',
+ dataType: 'json',
+ data: { 'pid': '1', 'tid[skin]': 1, 'tid[cape]': 2 }
+ });
+ expect(swal).toBeCalledWith({ type: 'success', html: 'success' });
+ expect(modal).toBeCalledWith('hide');
+
+ await setTexture();
+ expect(toastr.warning).toBeCalledWith('warning');
+ expect(modal.mock.calls.length).toBe(1);
+ });
+});
+
+describe('tests for "profile" module', () => {
+ const modulePath = '../user/profile';
+
+ it('change nickname', async () => {
+ const fetch = jest.fn()
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ const swal = jest.fn().mockReturnValue(Promise.resolve());
+ const trans = jest.fn(key => key);
+ const url = jest.fn(path => path);
+ window.fetch = fetch;
+ window.swal = swal;
+ window.trans = trans;
+ window.url = url;
+ window.debounce = jest.fn(fn => fn);
+
+ document.body.innerHTML = `
+
+
+ `;
+ const changeNickName = require(modulePath).changeNickName;
+
+ await changeNickName();
+ expect(swal).toBeCalledWith({ type: 'error', html: 'user.emptyNewNickName' });
+
+ $('input').val('name');
+ await changeNickName();
+ expect(trans).toBeCalledWith('user.changeNickName', { new_nickname: 'name' });
+ expect(swal).toBeCalledWith({
+ text: 'user.changeNickName',
+ type: 'question',
+ showCancelButton: true
+ });
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'user/profile?action=nickname',
+ dataType: 'json',
+ data: { new_nickname: 'name' }
+ });
+
+ await changeNickName();
+ expect($('.nickname').text()).toBe('name');
+ expect(swal).toBeCalledWith({ type: 'success', html: 'success' });
+ });
+
+ it('change password', async () => {
+ const fetch = jest.fn()
+ .mockReturnValueOnce(Promise.resolve({ errno: 0, msg: 'success' }))
+ .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' }))
+ .mockReturnValue(Promise.resolve({ errno: 0, msg: 'success' }));
+ const swal = jest.fn().mockReturnValue(Promise.resolve());
+ const trans = jest.fn(key => key);
+ const url = jest.fn(path => path);
+ const toastr = {
+ info: jest.fn(),
+ warning: jest.fn()
+ };
+ window.fetch = fetch;
+ window.swal = swal;
+ window.trans = trans;
+ window.url = url;
+ window.toastr = toastr;
+ window.logout = jest.fn().mockReturnValue(Promise.resolve({ errno: 0 }));
+
+ document.body.innerHTML = `
+
+
+
+ `;
+ const changePassword = require(modulePath).changePassword;
+
+ changePassword();
+ expect(toastr.info).toBeCalledWith('user.emptyPassword');
+ expect($('#password').is(':focus')).toBe(true);
+
+ $('#password').val('password');
+ changePassword();
+ expect(toastr.info).toBeCalledWith('user.emptyNewPassword');
+ expect($('#new-passwd').is(':focus')).toBe(true);
+
+ $('#new-passwd').val('new-password');
+ changePassword();
+ expect(toastr.info).toBeCalledWith('auth.emptyConfirmPwd');
+ expect($('#confirm-pwd').is(':focus')).toBe(true);
+
+ $('#confirm-pwd').val('not-same').blur();
+ changePassword();
+ expect(toastr.warning).toBeCalledWith('auth.invalidConfirmPwd');
+ expect($('#confirm-pwd').is(':focus')).toBe(true);
+
+ $('#confirm-pwd').val('new-password');
+ await changePassword();
+ expect(swal).toBeCalledWith({ text: 'success', type: 'success' });
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'user/profile?action=password',
+ dataType: 'json',
+ data: { current_password: 'password', new_password: 'new-password' }
+ });
+
+ await changePassword();
+ expect(swal).toBeCalledWith({ type: 'warning', text: 'warning' });
+
+ await changePassword();
+ expect(logout).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());
+ const trans = jest.fn(key => key);
+ const url = jest.fn(path => path);
+ const toastr = {
+ info: jest.fn(),
+ warning: jest.fn()
+ };
+ window.fetch = fetch;
+ window.swal = swal;
+ window.trans = trans;
+ window.url = url;
+ window.toastr = toastr;
+ window.logout = jest.fn().mockReturnValue(Promise.resolve({ errno: 0 }));
+
+ document.body.innerHTML = `
+
+
+ `;
+ const changeEmail = require(modulePath).changeEmail;
+
+ changeEmail();
+ expect(swal).toBeCalledWith({ type: 'error', html: 'user.emptyNewEmail' });
+
+ $('#new-email').val('email');
+ changeEmail();
+ expect(swal).toBeCalledWith({ type: 'warning', html: 'auth.invalidEmail' });
+
+ $('#new-email').val('a@b.c');
+ await changeEmail();
+ expect(trans).toBeCalledWith('user.changeEmail', { new_email: 'a@b.c' });
+ expect(swal).toBeCalledWith({
+ text: 'user.changeEmail',
+ type: 'question',
+ showCancelButton: true
+ });
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'user/profile?action=email',
+ dataType: 'json',
+ data: { new_email: 'a@b.c', password: 'pwd' }
+ });
+ });
+
+ it('delete account', 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());
+ const trans = jest.fn(key => key);
+ const url = jest.fn(path => path);
+ const toastr = {
+ info: jest.fn(),
+ warning: jest.fn()
+ };
+ window.fetch = fetch;
+ window.swal = swal;
+ window.trans = trans;
+ window.url = url;
+ window.toastr = toastr;
+
+ document.body.innerHTML = `
+
+
+
+ `;
+ const deleteAccount = require(modulePath).deleteAccount;
+
+ deleteAccount();
+ expect(swal).toBeCalledWith({ type: 'warning', html: 'user.emptyDeletePassword' });
+
+ $('#password').val('password');
+ await deleteAccount();
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'user/profile?action=delete',
+ dataType: 'json',
+ data: { password: 'password' }
+ });
+ expect(swal).toBeCalledWith({ type: 'success', html: 'success' });
+
+ await deleteAccount();
+ expect(swal).toBeCalledWith({ type: 'warning', html: 'warning' });
+ });
+});
+
+describe('tests for "sign" module', () => {
+ const modulePath = '../user/sign';
+
+ it('sign', async () => {
+ 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());
+ window.url = url;
+ window.toastr = toastr;
+ window.trans = trans;
+ window.swal = swal;
+ window.debounce = fn => fn;
+ const fetch = jest.fn()
+ .mockReturnValueOnce(Promise.resolve({
+ errno: 0,
+ msg: 'success',
+ score: 100,
+ remaining_time: 24,
+ storage: {
+ used: 50,
+ total: 100,
+ percentage: 50
+ }
+ }))
+ .mockReturnValueOnce(Promise.resolve({
+ errno: 0,
+ msg: 'success',
+ score: 100,
+ remaining_time: 24,
+ storage: {
+ used: 2000,
+ total: 4000,
+ percentage: 50
+ }
+ }))
+ .mockReturnValueOnce(Promise.resolve({ errno: 1, msg: 'warning' }));
+ window.fetch = fetch;
+
+ document.body.innerHTML = `
+
+
+
+
+ `;
+ const sign = require(modulePath);
+
+ await sign();
+ expect(fetch).toBeCalledWith({
+ type: 'POST',
+ url: 'user/sign',
+ dataType: 'json'
+ });
+ expect($('#score').html()).toBe('100');
+ expect(trans).toBeCalledWith('user.signRemainingTime', { time: '24' });
+ expect($('#sign-button').html()).toBe(
+ ' user.signRemainingTime'
+ );
+ expect($('#sign-button').attr('disabled')).toBe('disabled');
+ expect($('#user-storage').html()).toBe('50/ 100 KB');
+ expect($('#user-storage-bar').css('width')).toBe('50%');
+ expect(swal).toBeCalledWith({ type: 'success', html: 'success' });
+
+ await sign();
+ expect($('#user-storage').html()).toBe('2/ 4 MB');
+
+ await sign();
+ expect(toastr.warning).toBeCalledWith('warning');
+ });
+});
diff --git a/resources/assets/src/js/admin/customize.js b/resources/assets/src/js/admin/customize.js
index 2da4a1ea..6dda2a93 100644
--- a/resources/assets/src/js/admin/customize.js
+++ b/resources/assets/src/js/admin/customize.js
@@ -9,7 +9,7 @@ $('#layout-skins-list [data-skin]').click(function (e) {
current_skin = skin_name;
});
-$('#color-submit').click(() => {
+function submitColor() {
fetch({
type: 'POST',
url: url('admin/customize?action=color'),
@@ -18,4 +18,10 @@ $('#color-submit').click(() => {
}).then(({ errno, msg }) => {
(errno == 0) ? toastr.success(msg) : toastr.warning(msg);
}).catch(err => showAjaxError(err));
-});
+}
+
+$('#color-submit').click(submitColor);
+
+if (typeof require !== 'undefined' && typeof module !== 'undefined') {
+ module.exports = submitColor;
+}
diff --git a/resources/assets/src/js/admin/players.js b/resources/assets/src/js/admin/players.js
index 5500da06..f8293ac7 100644
--- a/resources/assets/src/js/admin/players.js
+++ b/resources/assets/src/js/admin/players.js
@@ -162,3 +162,14 @@ function deletePlayer(pid) {
}
}).catch(err => showAjaxError(err));
}
+
+if (typeof require !== 'undefined' && typeof module !== 'undefined') {
+ module.exports = {
+ changePreference,
+ changeTexture,
+ ajaxChangeTexture,
+ changePlayerName,
+ changeOwner,
+ deletePlayer
+ };
+}
diff --git a/resources/assets/src/js/admin/plugins.js b/resources/assets/src/js/admin/plugins.js
index 656e4093..d095dc12 100644
--- a/resources/assets/src/js/admin/plugins.js
+++ b/resources/assets/src/js/admin/plugins.js
@@ -25,7 +25,7 @@ function disablePlugin(name) {
dataType: 'json'
}).then(({ errno, msg }) => {
if (errno == 0) {
- toastr.warning(msg);
+ toastr.success(msg);
$.pluginsTable.ajax.reload(null, false);
} else {
@@ -53,3 +53,11 @@ function deletePlugin(name) {
}
}).catch(err => showAjaxError(err));
}
+
+if (typeof require !== 'undefined' && typeof module !== 'undefined') {
+ module.exports = {
+ enablePlugin,
+ disablePlugin,
+ deletePlugin
+ };
+}
diff --git a/resources/assets/src/js/admin/update.js b/resources/assets/src/js/admin/update.js
index 117b61f8..35b7b79d 100644
--- a/resources/assets/src/js/admin/update.js
+++ b/resources/assets/src/js/admin/update.js
@@ -89,3 +89,7 @@ function downloadUpdates() {
}).catch(err => showAjaxError(err));
}
+
+if (typeof require !== 'undefined' && typeof module !== 'undefined') {
+ module.exports = downloadUpdates;
+}
diff --git a/resources/assets/src/js/admin/users.js b/resources/assets/src/js/admin/users.js
index f65f9277..3471600f 100644
--- a/resources/assets/src/js/admin/users.js
+++ b/resources/assets/src/js/admin/users.js
@@ -167,3 +167,15 @@ $('body').on('keypress', '.score', function(event){
changeUserScore($(this).parent().parent().attr('id'), $(this).val());
}
});
+
+if (typeof require !== 'undefined' && typeof module !== 'undefined') {
+ module.exports = {
+ changeUserEmail,
+ changeUserNickName,
+ changeUserPwd,
+ changeUserScore,
+ changeBanStatus,
+ changeAdminStatus,
+ deleteUserAccount
+ };
+}
diff --git a/resources/assets/src/js/auth/captcha.js b/resources/assets/src/js/auth/captcha.js
index 1bd12edc..3d819568 100644
--- a/resources/assets/src/js/auth/captcha.js
+++ b/resources/assets/src/js/auth/captcha.js
@@ -9,3 +9,7 @@ function refreshCaptcha() {
}
$('.captcha').click(refreshCaptcha);
+
+if (typeof require !== 'undefined' && typeof module !== 'undefined') {
+ module.exports = refreshCaptcha;
+}
diff --git a/resources/assets/src/js/auth/forgot.js b/resources/assets/src/js/auth/forgot.js
new file mode 100644
index 00000000..bd9ad0da
--- /dev/null
+++ b/resources/assets/src/js/auth/forgot.js
@@ -0,0 +1,50 @@
+/* global refreshCaptcha */
+
+'use strict';
+
+$('#forgot-button').click(e => {
+ e.preventDefault();
+
+ let data = {
+ email: $('#email').val(),
+ captcha: $('#captcha').val()
+ };
+
+ (function validate({ email, captcha }, callback) {
+ if (email == '') {
+ showMsg(trans('auth.emptyEmail'));
+ $('#email').focus();
+ } else if (!/\S+@\S+\.\S+/.test(email)) {
+ showMsg(trans('auth.invalidEmail'), 'warning');
+ } else if (captcha == '') {
+ showMsg(trans('auth.emptyCaptcha'));
+ $('#captcha').focus();
+ } else {
+ callback();
+ }
+ })(data, () => {
+ fetch({
+ type: 'POST',
+ url: url('auth/forgot'),
+ dataType: 'json',
+ data: data,
+ beforeSend: () => {
+ $('#forgot-button').html(
+ ' ' + trans('auth.sending')
+ ).prop('disabled', 'disabled');
+ }
+ }).then(({ errno, msg }) => {
+ if (errno == 0) {
+ showMsg(msg, 'success');
+ $('#forgot-button').html(trans('auth.send')).prop('disabled', 'disabled');
+ } else {
+ showMsg(msg, 'warning');
+ refreshCaptcha();
+ $('#forgot-button').html(trans('auth.send')).prop('disabled', '');
+ }
+ }).catch(err => {
+ showAjaxError(err);
+ $('#forgot-button').html(trans('auth.send')).prop('disabled', '');
+ });
+ });
+});
diff --git a/resources/assets/src/js/auth/reset.js b/resources/assets/src/js/auth/reset.js
index 826a1e58..bc209f16 100644
--- a/resources/assets/src/js/auth/reset.js
+++ b/resources/assets/src/js/auth/reset.js
@@ -1,54 +1,5 @@
-/* global refreshCaptcha */
-
'use strict';
-$('#forgot-button').click(e => {
- e.preventDefault();
-
- let data = {
- email: $('#email').val(),
- captcha: $('#captcha').val()
- };
-
- (function validate({ email, captcha }, callback) {
- if (email == '') {
- showMsg(trans('auth.emptyEmail'));
- $('#email').focus();
- } else if (!/\S+@\S+\.\S+/.test(email)) {
- showMsg(trans('auth.invalidEmail'), 'warning');
- } else if (captcha == '') {
- showMsg(trans('auth.emptyCaptcha'));
- $('#captcha').focus();
- } else {
- callback();
- }
- })(data, () => {
- fetch({
- type: 'POST',
- url: url('auth/forgot'),
- dataType: 'json',
- data: data,
- beforeSend: () => {
- $('#forgot-button').html(
- ' ' + trans('auth.sending')
- ).prop('disabled', 'disabled');
- }
- }).then(({ errno, msg }) => {
- if (errno == 0) {
- showMsg(msg, 'success');
- $('#forgot-button').html(trans('auth.send')).prop('disabled', 'disabled');
- } else {
- showMsg(msg, 'warning');
- refreshCaptcha();
- $('#forgot-button').html(trans('auth.send')).prop('disabled', '');
- }
- }).catch(err => {
- showAjaxError(err);
- $('#forgot-button').html(trans('auth.send')).prop('disabled', '');
- });
- });
-});
-
$('#reset-button').click(e => {
e.preventDefault();
diff --git a/resources/assets/src/js/common/i18n.js b/resources/assets/src/js/common/i18n.js
index ab0e6771..bdd20e6c 100644
--- a/resources/assets/src/js/common/i18n.js
+++ b/resources/assets/src/js/common/i18n.js
@@ -49,3 +49,10 @@ function trans(key, parameters = {}) {
return temp;
}
+
+if (typeof require !== 'undefined' && typeof module !== 'undefined') {
+ module.exports = {
+ loadLocales,
+ trans
+ };
+}
diff --git a/resources/assets/src/js/common/notify.js b/resources/assets/src/js/common/notify.js
index cdc5700e..85f0223c 100644
--- a/resources/assets/src/js/common/notify.js
+++ b/resources/assets/src/js/common/notify.js
@@ -57,3 +57,11 @@ function showModal(msg, title = 'Message', type = 'default', options = {}) {
$(dom).modal(options);
}
+
+if (typeof require !== 'undefined' && typeof module !== 'undefined') {
+ module.exports = {
+ showMsg,
+ showAjaxError,
+ showModal
+ };
+}
diff --git a/resources/assets/src/js/common/texture-preview.js b/resources/assets/src/js/common/texture-preview.js
index 0ad98f1d..ed986b4b 100644
--- a/resources/assets/src/js/common/texture-preview.js
+++ b/resources/assets/src/js/common/texture-preview.js
@@ -98,3 +98,7 @@ $('.fa-pause').click(function () {
$('.fa-forward').click(() => MSP.setStatus('running', ! MSP.getStatus('running')));
$('.fa-repeat' ).click(() => MSP.setStatus('rotation', ! MSP.getStatus('rotation')));
+
+if (typeof require !== 'undefined' && typeof module !== 'undefined') {
+ module.exports = TexturePreview;
+}
diff --git a/resources/assets/src/js/common/utils.js b/resources/assets/src/js/common/utils.js
index 3ba9a17d..6cbace9e 100644
--- a/resources/assets/src/js/common/utils.js
+++ b/resources/assets/src/js/common/utils.js
@@ -74,7 +74,7 @@ function getQueryString(key, defaultValue) {
*/
function debounce(func, delay, args = [], context = undefined) {
if (isNaN(delay) || typeof func !== 'function') {
- throw new Error('Arguments type of function "debounce" is incorrent!');
+ throw new Error('Arguments type of function "debounce" is incorrect!');
}
let timer = null;
@@ -96,3 +96,12 @@ function url(relativeUri) {
return blessing.base_url + relativeUri;
}
+
+if (typeof require !== 'undefined' && typeof module !== 'undefined') {
+ module.exports = {
+ isEmpty,
+ fetch,
+ debounce,
+ url
+ };
+}
diff --git a/resources/assets/src/js/skinlib/index.js b/resources/assets/src/js/skinlib/index.js
index 8c2ef749..4045e059 100644
--- a/resources/assets/src/js/skinlib/index.js
+++ b/resources/assets/src/js/skinlib/index.js
@@ -63,9 +63,11 @@ function renderSkinlib(items) {
if (items.length === 0) {
$('#skinlib-paginator').hide();
- container.html(`
- ${ trans('general.noResult') }
-
`);
+ container.html(
+ '' +
+ trans('general.noResult') +
+ '
'
+ );
} else {
$('#skinlib-paginator').show();
@@ -217,3 +219,13 @@ function updateBreadCrumb() {
$('#navbar-search-input').val(decodeURI($.skinlib.keyword));
}
}
+
+if (typeof require !== 'undefined' && typeof module !== 'undefined') {
+ module.exports = {
+ renderSkinlib,
+ reloadSkinlib,
+ updatePaginator,
+ updateUrlQueryString,
+ updateBreadCrumb
+ };
+}
diff --git a/resources/assets/src/js/skinlib/operations.js b/resources/assets/src/js/skinlib/operations.js
index 101357b5..0f497bfa 100644
--- a/resources/assets/src/js/skinlib/operations.js
+++ b/resources/assets/src/js/skinlib/operations.js
@@ -104,16 +104,21 @@ function changeTextureName(tid, oldName) {
/**
* Update button action & likes of texture.
*
- * @param {int} tid
- * @param {string} action add|remove
+ * @param {number} tid
+ * @param {'add'|'remove'} action
* @return {null}
*/
function updateTextureStatus(tid, action) {
let likes = parseInt($('#likes').html()) + (action == 'add' ? 1 : -1);
action = (action == 'add') ? 'removeFromCloset' : 'addToCloset';
- $(`a[tid=${tid}]`).attr('href', `javascript:${action}(${tid});`).attr('title', trans(`skinlib.${action}`)).toggleClass('liked');
- $(`#${tid}`).attr('href', `javascript:${action}(${tid});`).html(trans(`skinlib.${action}`));
+ $(`a[tid=${tid}]`)
+ .attr('href', `javascript:${action}(${tid});`)
+ .attr('title', trans(`skinlib.${action}`))
+ .toggleClass('liked');
+ $(`#${tid}`)
+ .attr('href', `javascript:${action}(${tid});`)
+ .html(trans(`skinlib.${action}`));
$('#likes').html(likes);
}
@@ -171,3 +176,15 @@ function deleteTexture(tid) {
}
}).catch(err => showAjaxError(err));
}
+
+if (typeof require !== 'undefined' && typeof module !== 'undefined') {
+ module.exports = {
+ addToCloset,
+ ajaxAddToCloset,
+ removeFromCloset,
+ changeTextureName,
+ updateTextureStatus,
+ changePrivacy,
+ deleteTexture
+ };
+}
diff --git a/resources/assets/src/js/user/closet.js b/resources/assets/src/js/user/closet.js
index 2aa956a5..455c12ad 100644
--- a/resources/assets/src/js/user/closet.js
+++ b/resources/assets/src/js/user/closet.js
@@ -242,3 +242,13 @@ function setAsAvatar(tid) {
}
}).catch(err => showAjaxError(err));
}
+
+if (typeof require !== 'undefined' && typeof module !== 'undefined') {
+ module.exports = {
+ renderCloset,
+ reloadCloset,
+ renameClosetItem,
+ removeFromCloset,
+ setAsAvatar
+ };
+}
diff --git a/resources/assets/src/js/user/player.js b/resources/assets/src/js/user/player.js
index e99565a3..0f63ecb1 100644
--- a/resources/assets/src/js/user/player.js
+++ b/resources/assets/src/js/user/player.js
@@ -56,13 +56,14 @@ $('body').on('change', '#preference', function () {
}).catch(err => showAjaxError(err));
});
-function changePlayerName(pid, currentPlayerName) {
+function changePlayerName(pid) {
let newPlayerName = '';
+ const $playerName = $(`td:contains("${pid}")`).next();
swal({
title: trans('user.changePlayerName'),
text: $('#player_name').attr('placeholder'),
- inputValue: currentPlayerName,
+ inputValue: $playerName.html(),
input: 'text',
showCancelButton: true,
inputValidator: value => (new Promise((resolve, reject) => {
@@ -77,7 +78,7 @@ function changePlayerName(pid, currentPlayerName) {
if (errno == 0) {
swal({ type: 'success', html: msg });
- $(`td:contains("${pid}")`).next().html(newPlayerName);
+ $playerName.html(newPlayerName);
} else {
swal({ type: 'error', html: msg });
}
@@ -207,3 +208,13 @@ function setTexture() {
}).catch(err => showAjaxError(err));
}
}
+
+if (typeof require !== 'undefined' && typeof module !== 'undefined') {
+ module.exports = {
+ changePlayerName,
+ ajaxClearTexture,
+ deletePlayer,
+ addNewPlayer,
+ setTexture
+ };
+}
diff --git a/resources/assets/src/js/user/profile.js b/resources/assets/src/js/user/profile.js
index 31102021..b059c835 100644
--- a/resources/assets/src/js/user/profile.js
+++ b/resources/assets/src/js/user/profile.js
@@ -62,8 +62,8 @@ function changePassword() {
if (errno == 0) {
return swal({
type: 'success',
- text: msg }
- ).then(() => {
+ text: msg
+ }).then(() => {
return logout();
}).then(({ errno }) => {
if (errno == 0) {
@@ -151,3 +151,12 @@ function deleteAccount() {
}
}).catch(err => showAjaxError(err));
}
+
+if (typeof require !== 'undefined' && typeof module !== 'undefined') {
+ module.exports = {
+ changeNickName,
+ changePassword,
+ changeEmail,
+ deleteAccount
+ };
+}
diff --git a/resources/assets/src/js/user/sign.js b/resources/assets/src/js/user/sign.js
index f0f90884..d25a0620 100644
--- a/resources/assets/src/js/user/sign.js
+++ b/resources/assets/src/js/user/sign.js
@@ -13,7 +13,9 @@ function sign() {
$('#sign-button').attr('disabled', 'disabled').html(dom);
if (result.storage.used > 1024) {
- $('#user-storage').html(`${Math.round(result.storage.used)}/ ${Math.round(result.storage.total)} MB`);
+ $('#user-storage').html(
+ `${Math.round(result.storage.used / 1024)}/ ${Math.round(result.storage.total / 1024)} MB`
+ );
} else {
$('#user-storage').html(`${Math.round(result.storage.used)}/ ${Math.round(result.storage.total)} KB`);
}
@@ -26,3 +28,7 @@ function sign() {
}
}).catch(err => showAjaxError(err));
}
+
+if (typeof require !== 'undefined' && typeof module !== 'undefined') {
+ module.exports = sign;
+}
diff --git a/yarn.lock b/yarn.lock
index cbffac4d..b6f33f62 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,10 +2,20 @@
# yarn lockfile v1
+abab@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d"
+
abbrev@1:
version "1.1.0"
resolved "http://registry.npm.taobao.org/abbrev/download/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f"
+acorn-globals@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf"
+ dependencies:
+ acorn "^4.0.4"
+
acorn-jsx@^3.0.0:
version "3.0.1"
resolved "http://registry.npm.taobao.org/acorn-jsx/download/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
@@ -16,6 +26,10 @@ acorn@^3.0.4:
version "3.3.0"
resolved "http://registry.npm.taobao.org/acorn/download/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
+acorn@^4.0.4:
+ version "4.0.13"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
+
acorn@^5.0.1:
version "5.1.1"
resolved "http://registry.npm.taobao.org/acorn/download/acorn-5.1.1.tgz#53fe161111f912ab999ee887a90a0bc52822fd75"
@@ -44,15 +58,27 @@ ajv@^5.2.0:
json-schema-traverse "^0.3.0"
json-stable-stringify "^1.0.1"
+align-text@^0.1.1, align-text@^0.1.3:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
+ dependencies:
+ kind-of "^3.0.2"
+ longest "^1.0.1"
+ repeat-string "^1.5.2"
+
amdefine@>=0.0.4:
version "1.0.1"
resolved "http://registry.npm.taobao.org/amdefine/download/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
+ansi-escapes@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
+
ansi-escapes@^2.0.0:
version "2.0.0"
resolved "http://registry.npm.taobao.org/ansi-escapes/download/ansi-escapes-2.0.0.tgz#5bae52be424878dd9783e8910e3fc2922e83c81b"
-ansi-regex@^2.0.0:
+ansi-regex@^2.0.0, ansi-regex@^2.1.1:
version "2.1.1"
resolved "http://registry.npm.taobao.org/ansi-regex/download/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
@@ -64,12 +90,25 @@ ansi-styles@^2.2.1:
version "2.2.1"
resolved "http://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
-ansi-styles@^3.1.0:
+ansi-styles@^3.0.0, ansi-styles@^3.1.0:
version "3.1.0"
resolved "http://registry.npm.taobao.org/ansi-styles/download/ansi-styles-3.1.0.tgz#09c202d5c917ec23188caa5c9cb9179cd9547750"
dependencies:
color-convert "^1.0.0"
+anymatch@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507"
+ dependencies:
+ arrify "^1.0.0"
+ micromatch "^2.1.5"
+
+append-transform@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991"
+ dependencies:
+ default-require-extensions "^1.0.0"
+
aproba@^1.0.3:
version "1.1.2"
resolved "http://registry.npm.taobao.org/aproba/download/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1"
@@ -109,6 +148,10 @@ array-each@^1.0.1:
version "1.0.1"
resolved "http://registry.npm.taobao.org/array-each/download/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f"
+array-equal@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
+
array-find-index@^1.0.1:
version "1.0.2"
resolved "http://registry.npm.taobao.org/array-find-index/download/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
@@ -131,7 +174,7 @@ array-unique@^0.2.1:
version "0.2.1"
resolved "http://registry.npm.taobao.org/array-unique/download/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53"
-arrify@^1.0.0:
+arrify@^1.0.0, arrify@^1.0.1:
version "1.0.1"
resolved "http://registry.npm.taobao.org/arrify/download/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
@@ -151,6 +194,16 @@ async-foreach@^0.1.3:
version "0.1.3"
resolved "http://registry.npm.taobao.org/async-foreach/download/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542"
+async@^1.4.0:
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
+
+async@^2.1.4:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/async/-/async-2.5.0.tgz#843190fd6b7357a0b9e1c956edddd5ec8462b54d"
+ dependencies:
+ lodash "^4.14.0"
+
asynckit@^0.4.0:
version "0.4.0"
resolved "http://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@@ -171,7 +224,7 @@ babel-code-frame@^6.22.0:
esutils "^2.0.2"
js-tokens "^3.0.0"
-babel-core@^6.0.2, babel-core@^6.24.1:
+babel-core@^6.0.0, babel-core@^6.0.2, babel-core@^6.24.1:
version "6.25.0"
resolved "http://registry.npm.taobao.org/babel-core/download/babel-core-6.25.0.tgz#7dd42b0463c742e9d5296deb3ec67a9322dad729"
dependencies:
@@ -195,7 +248,7 @@ babel-core@^6.0.2, babel-core@^6.24.1:
slash "^1.0.0"
source-map "^0.5.0"
-babel-generator@^6.25.0:
+babel-generator@^6.18.0, babel-generator@^6.25.0:
version "6.25.0"
resolved "http://registry.npm.taobao.org/babel-generator/download/babel-generator-6.25.0.tgz#33a1af70d5f2890aeb465a4a7793c1df6a9ea9fc"
dependencies:
@@ -283,6 +336,14 @@ babel-helpers@^6.24.1:
babel-runtime "^6.22.0"
babel-template "^6.24.1"
+babel-jest@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-20.0.3.tgz#e4a03b13dc10389e140fc645d09ffc4ced301671"
+ dependencies:
+ babel-core "^6.0.0"
+ babel-plugin-istanbul "^4.0.0"
+ babel-preset-jest "^20.0.3"
+
babel-messages@^6.23.0:
version "6.23.0"
resolved "http://registry.npm.taobao.org/babel-messages/download/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e"
@@ -295,6 +356,18 @@ babel-plugin-check-es2015-constants@^6.22.0:
dependencies:
babel-runtime "^6.22.0"
+babel-plugin-istanbul@^4.0.0:
+ version "4.1.4"
+ resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.4.tgz#18dde84bf3ce329fddf3f4103fae921456d8e587"
+ dependencies:
+ find-up "^2.1.0"
+ istanbul-lib-instrument "^1.7.2"
+ test-exclude "^4.1.1"
+
+babel-plugin-jest-hoist@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-20.0.3.tgz#afedc853bd3f8dc3548ea671fbe69d03cc2c1767"
+
babel-plugin-transform-es2015-arrow-functions@^6.22.0:
version "6.22.0"
resolved "http://registry.npm.taobao.org/babel-plugin-transform-es2015-arrow-functions/download/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221"
@@ -505,6 +578,12 @@ babel-preset-es2015@^6.24.1:
babel-plugin-transform-es2015-unicode-regex "^6.24.1"
babel-plugin-transform-regenerator "^6.24.1"
+babel-preset-jest@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-20.0.3.tgz#cbacaadecb5d689ca1e1de1360ebfc66862c178a"
+ dependencies:
+ babel-plugin-jest-hoist "^20.0.3"
+
babel-register@^6.24.1:
version "6.24.1"
resolved "http://registry.npm.taobao.org/babel-register/download/babel-register-6.24.1.tgz#7e10e13a2f71065bdfad5a1787ba45bca6ded75f"
@@ -524,7 +603,7 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0:
core-js "^2.4.0"
regenerator-runtime "^0.10.0"
-babel-template@^6.24.1, babel-template@^6.25.0:
+babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.25.0:
version "6.25.0"
resolved "http://registry.npm.taobao.org/babel-template/download/babel-template-6.25.0.tgz#665241166b7c2aa4c619d71e192969552b10c071"
dependencies:
@@ -534,7 +613,7 @@ babel-template@^6.24.1, babel-template@^6.25.0:
babylon "^6.17.2"
lodash "^4.2.0"
-babel-traverse@^6.24.1, babel-traverse@^6.25.0:
+babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.25.0:
version "6.25.0"
resolved "http://registry.npm.taobao.org/babel-traverse/download/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1"
dependencies:
@@ -548,7 +627,7 @@ babel-traverse@^6.24.1, babel-traverse@^6.25.0:
invariant "^2.2.0"
lodash "^4.2.0"
-babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.25.0:
+babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.25.0:
version "6.25.0"
resolved "http://registry.npm.taobao.org/babel-types/download/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e"
dependencies:
@@ -557,7 +636,7 @@ babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.25.0:
lodash "^4.2.0"
to-fast-properties "^1.0.1"
-babylon@^6.17.2:
+babylon@^6.17.2, babylon@^6.17.4:
version "6.17.4"
resolved "http://registry.npm.taobao.org/babylon/download/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a"
@@ -610,6 +689,24 @@ braces@^1.8.2:
preserve "^0.2.0"
repeat-element "^1.1.2"
+browser-resolve@^1.11.2:
+ version "1.11.2"
+ resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce"
+ dependencies:
+ resolve "1.1.7"
+
+bser@1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/bser/-/bser-1.0.2.tgz#381116970b2a6deea5646dd15dd7278444b56169"
+ dependencies:
+ node-int64 "^0.4.0"
+
+bser@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719"
+ dependencies:
+ node-int64 "^0.4.0"
+
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "http://registry.npm.taobao.org/buffer-crc32/download/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
@@ -628,6 +725,10 @@ callsites@^0.2.0:
version "0.2.0"
resolved "http://registry.npm.taobao.org/callsites/download/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca"
+callsites@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
+
camelcase-keys@^2.0.0:
version "2.1.0"
resolved "http://registry.npm.taobao.org/camelcase-keys/download/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
@@ -635,6 +736,10 @@ camelcase-keys@^2.0.0:
camelcase "^2.0.0"
map-obj "^1.0.0"
+camelcase@^1.0.2:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
+
camelcase@^2.0.0:
version "2.1.1"
resolved "http://registry.npm.taobao.org/camelcase/download/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
@@ -647,6 +752,13 @@ caseless@~0.12.0:
version "0.12.0"
resolved "http://registry.npm.taobao.org/caseless/download/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+center-align@^0.1.1:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad"
+ dependencies:
+ align-text "^0.1.3"
+ lazy-cache "^1.0.3"
+
chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
version "1.1.3"
resolved "http://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
@@ -665,6 +777,10 @@ chalk@^2.0.0:
escape-string-regexp "^1.0.5"
supports-color "^4.0.0"
+ci-info@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.0.0.tgz#dc5285f2b4e251821683681c381c3388f46ec534"
+
circular-json@^0.3.1:
version "0.3.1"
resolved "http://registry.npm.taobao.org/circular-json/download/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d"
@@ -685,6 +801,14 @@ cli-width@^2.0.0:
version "2.1.0"
resolved "http://registry.npm.taobao.org/cli-width/download/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a"
+cliui@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1"
+ dependencies:
+ center-align "^0.1.1"
+ right-align "^0.1.1"
+ wordwrap "0.0.2"
+
cliui@^3.2.0:
version "3.2.0"
resolved "http://registry.npm.taobao.org/cliui/download/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
@@ -777,7 +901,11 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "http://registry.npm.taobao.org/console-control-strings/download/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
-convert-source-map@^1.1.0:
+content-type-parser@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.1.tgz#c3e56988c53c65127fb46d4032a3a900246fdc94"
+
+convert-source-map@^1.1.0, convert-source-map@^1.4.0:
version "1.5.0"
resolved "http://registry.npm.taobao.org/convert-source-map/download/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5"
@@ -802,6 +930,16 @@ cryptiles@2.x.x:
dependencies:
boom "2.x.x"
+cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b"
+
+"cssstyle@>= 0.2.37 < 0.3.0":
+ version "0.2.37"
+ resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54"
+ dependencies:
+ cssom "0.3.x"
+
currently-unhandled@^0.4.1:
version "0.4.1"
resolved "http://registry.npm.taobao.org/currently-unhandled/download/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
@@ -818,13 +956,13 @@ dateformat@^2.0.0:
version "2.0.0"
resolved "http://registry.npm.taobao.org/dateformat/download/dateformat-2.0.0.tgz#2743e3abb5c3fc2462e527dca445e04e9f4dee17"
-debug@^2.1.1, debug@^2.2.0, debug@^2.6.8:
+debug@^2.1.1, debug@^2.2.0, debug@^2.6.3, debug@^2.6.8:
version "2.6.8"
resolved "http://registry.npm.taobao.org/debug/download/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
dependencies:
ms "2.0.0"
-decamelize@^1.1.1, decamelize@^1.1.2:
+decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
version "1.2.0"
resolved "http://registry.npm.taobao.org/decamelize/download/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -832,6 +970,12 @@ deep-is@~0.1.3:
version "0.1.3"
resolved "http://registry.npm.taobao.org/deep-is/download/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+default-require-extensions@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8"
+ dependencies:
+ strip-bom "^2.0.0"
+
defaults@^1.0.0:
version "1.0.3"
resolved "http://registry.npm.taobao.org/defaults/download/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
@@ -885,6 +1029,10 @@ detect-indent@^4.0.0:
dependencies:
repeating "^2.0.0"
+diff@^3.2.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.0.tgz#056695150d7aa93237ca7e378ac3b1682b7963b9"
+
doctrine@^2.0.0:
version "2.0.0"
resolved "http://registry.npm.taobao.org/doctrine/download/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63"
@@ -914,6 +1062,12 @@ end-of-stream@~0.1.5:
dependencies:
once "~1.3.0"
+errno@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d"
+ dependencies:
+ prr "~0.0.0"
+
error-ex@^1.2.0:
version "1.3.1"
resolved "http://registry.npm.taobao.org/error-ex/download/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc"
@@ -928,6 +1082,17 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "http://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+escodegen@^1.6.1:
+ version "1.8.1"
+ resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018"
+ dependencies:
+ esprima "^2.7.1"
+ estraverse "^1.9.1"
+ esutils "^2.0.2"
+ optionator "^0.8.1"
+ optionalDependencies:
+ source-map "~0.2.0"
+
eslint-scope@^3.7.1:
version "3.7.1"
resolved "http://registry.npm.taobao.org/eslint-scope/download/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
@@ -980,6 +1145,10 @@ espree@^3.4.3:
acorn "^5.0.1"
acorn-jsx "^3.0.0"
+esprima@^2.7.1:
+ version "2.7.3"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
+
esprima@^4.0.0:
version "4.0.0"
resolved "http://registry.npm.taobao.org/esprima/download/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
@@ -997,6 +1166,10 @@ esrecurse@^4.1.0:
estraverse "^4.1.0"
object-assign "^4.0.1"
+estraverse@^1.9.1:
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44"
+
estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
version "4.2.0"
resolved "http://registry.npm.taobao.org/estraverse/download/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
@@ -1017,6 +1190,12 @@ event-stream@latest:
stream-combiner "~0.0.4"
through "~2.3.1"
+exec-sh@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.0.tgz#14f75de3f20d286ef933099b2ce50a90359cef10"
+ dependencies:
+ merge "^1.1.3"
+
expand-brackets@^0.1.4:
version "0.1.5"
resolved "http://registry.npm.taobao.org/expand-brackets/download/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
@@ -1078,6 +1257,18 @@ fast-levenshtein@~2.0.4:
version "2.0.6"
resolved "http://registry.npm.taobao.org/fast-levenshtein/download/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+fb-watchman@^1.8.0:
+ version "1.9.2"
+ resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-1.9.2.tgz#a24cf47827f82d38fb59a69ad70b76e3b6ae7383"
+ dependencies:
+ bser "1.0.2"
+
+fb-watchman@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58"
+ dependencies:
+ bser "^2.0.0"
+
figures@^2.0.0:
version "2.0.0"
resolved "http://registry.npm.taobao.org/figures/download/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
@@ -1095,6 +1286,13 @@ filename-regex@^2.0.0:
version "2.0.1"
resolved "http://registry.npm.taobao.org/filename-regex/download/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
+fileset@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0"
+ dependencies:
+ glob "^7.0.3"
+ minimatch "^3.0.3"
+
fill-range@^2.1.0:
version "2.2.3"
resolved "http://registry.npm.taobao.org/fill-range/download/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723"
@@ -1116,6 +1314,12 @@ find-up@^1.0.0:
path-exists "^2.0.0"
pinkie-promise "^2.0.0"
+find-up@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
+ dependencies:
+ locate-path "^2.0.0"
+
findup-sync@^0.4.2:
version "0.4.3"
resolved "http://registry.npm.taobao.org/findup-sync/download/findup-sync-0.4.3.tgz#40043929e7bc60adf0b7f4827c4c6e75a0deca12"
@@ -1293,7 +1497,7 @@ glob@^4.3.1:
minimatch "^2.0.1"
once "^1.3.0"
-glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.2, glob@~7.1.1:
+glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1:
version "7.1.2"
resolved "http://registry.npm.taobao.org/glob/download/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
dependencies:
@@ -1381,7 +1585,7 @@ graceful-fs@^3.0.0:
dependencies:
natives "^1.1.0"
-graceful-fs@^4.1.2:
+graceful-fs@^4.1.11, graceful-fs@^4.1.2:
version "4.1.11"
resolved "http://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
@@ -1526,6 +1730,16 @@ gulplog@^1.0.0:
dependencies:
glogg "^1.0.0"
+handlebars@^4.0.3:
+ version "4.0.10"
+ resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.10.tgz#3d30c718b09a3d96f23ea4cc1f403c4d3ba9ff4f"
+ dependencies:
+ async "^1.4.0"
+ optimist "^0.6.1"
+ source-map "^0.4.4"
+ optionalDependencies:
+ uglify-js "^2.6"
+
har-schema@^1.0.5:
version "1.0.5"
resolved "http://registry.npm.taobao.org/har-schema/download/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
@@ -1543,6 +1757,10 @@ has-ansi@^2.0.0:
dependencies:
ansi-regex "^2.0.0"
+has-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
+
has-flag@^2.0.0:
version "2.0.0"
resolved "http://registry.npm.taobao.org/has-flag/download/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
@@ -1591,6 +1809,12 @@ hosted-git-info@^2.1.4:
version "2.5.0"
resolved "http://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c"
+html-encoding-sniffer@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.1.tgz#79bf7a785ea495fe66165e734153f363ff5437da"
+ dependencies:
+ whatwg-encoding "^1.0.1"
+
http-signature@~1.1.0:
version "1.1.1"
resolved "http://registry.npm.taobao.org/http-signature/download/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf"
@@ -1603,6 +1827,10 @@ icheck@^1.0.2:
version "1.0.2"
resolved "http://registry.npm.taobao.org/icheck/download/icheck-1.0.2.tgz#06d08da3d47ae448c153b2639b86e9ad7fdf7128"
+iconv-lite@0.4.13:
+ version "0.4.13"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2"
+
iconv-lite@^0.4.17:
version "0.4.18"
resolved "http://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2"
@@ -1698,6 +1926,12 @@ is-builtin-module@^1.0.0:
dependencies:
builtin-modules "^1.0.0"
+is-ci@^1.0.10:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.0.10.tgz#f739336b2632365061a9d48270cd56ae3369318e"
+ dependencies:
+ ci-info "^1.0.0"
+
is-dotfile@^1.0.0:
version "1.0.3"
resolved "http://registry.npm.taobao.org/is-dotfile/download/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1"
@@ -1844,6 +2078,280 @@ isstream@~0.1.2:
version "0.1.2"
resolved "http://registry.npm.taobao.org/isstream/download/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+istanbul-api@^1.1.1:
+ version "1.1.10"
+ resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.1.10.tgz#f27e5e7125c8de13f6a80661af78f512e5439b2b"
+ dependencies:
+ async "^2.1.4"
+ fileset "^2.0.2"
+ istanbul-lib-coverage "^1.1.1"
+ istanbul-lib-hook "^1.0.7"
+ istanbul-lib-instrument "^1.7.3"
+ istanbul-lib-report "^1.1.1"
+ istanbul-lib-source-maps "^1.2.1"
+ istanbul-reports "^1.1.1"
+ js-yaml "^3.7.0"
+ mkdirp "^0.5.1"
+ once "^1.4.0"
+
+istanbul-lib-coverage@^1.0.1, istanbul-lib-coverage@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz#73bfb998885299415c93d38a3e9adf784a77a9da"
+
+istanbul-lib-hook@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.0.7.tgz#dd6607f03076578fe7d6f2a630cf143b49bacddc"
+ dependencies:
+ append-transform "^0.4.0"
+
+istanbul-lib-instrument@^1.4.2, istanbul-lib-instrument@^1.7.2, istanbul-lib-instrument@^1.7.3:
+ version "1.7.3"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.3.tgz#925b239163eabdd68cc4048f52c2fa4f899ecfa7"
+ dependencies:
+ babel-generator "^6.18.0"
+ babel-template "^6.16.0"
+ babel-traverse "^6.18.0"
+ babel-types "^6.18.0"
+ babylon "^6.17.4"
+ istanbul-lib-coverage "^1.1.1"
+ semver "^5.3.0"
+
+istanbul-lib-report@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz#f0e55f56655ffa34222080b7a0cd4760e1405fc9"
+ dependencies:
+ istanbul-lib-coverage "^1.1.1"
+ mkdirp "^0.5.1"
+ path-parse "^1.0.5"
+ supports-color "^3.1.2"
+
+istanbul-lib-source-maps@^1.1.0, istanbul-lib-source-maps@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.1.tgz#a6fe1acba8ce08eebc638e572e294d267008aa0c"
+ dependencies:
+ debug "^2.6.3"
+ istanbul-lib-coverage "^1.1.1"
+ mkdirp "^0.5.1"
+ rimraf "^2.6.1"
+ source-map "^0.5.3"
+
+istanbul-reports@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.1.1.tgz#042be5c89e175bc3f86523caab29c014e77fee4e"
+ dependencies:
+ handlebars "^4.0.3"
+
+jest-changed-files@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-20.0.3.tgz#9394d5cc65c438406149bef1bf4d52b68e03e3f8"
+
+jest-cli@^20.0.4:
+ version "20.0.4"
+ resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-20.0.4.tgz#e532b19d88ae5bc6c417e8b0593a6fe954b1dc93"
+ dependencies:
+ ansi-escapes "^1.4.0"
+ callsites "^2.0.0"
+ chalk "^1.1.3"
+ graceful-fs "^4.1.11"
+ is-ci "^1.0.10"
+ istanbul-api "^1.1.1"
+ istanbul-lib-coverage "^1.0.1"
+ istanbul-lib-instrument "^1.4.2"
+ istanbul-lib-source-maps "^1.1.0"
+ jest-changed-files "^20.0.3"
+ jest-config "^20.0.4"
+ jest-docblock "^20.0.3"
+ jest-environment-jsdom "^20.0.3"
+ jest-haste-map "^20.0.4"
+ jest-jasmine2 "^20.0.4"
+ jest-message-util "^20.0.3"
+ jest-regex-util "^20.0.3"
+ jest-resolve-dependencies "^20.0.3"
+ jest-runtime "^20.0.4"
+ jest-snapshot "^20.0.3"
+ jest-util "^20.0.3"
+ micromatch "^2.3.11"
+ node-notifier "^5.0.2"
+ pify "^2.3.0"
+ slash "^1.0.0"
+ string-length "^1.0.1"
+ throat "^3.0.0"
+ which "^1.2.12"
+ worker-farm "^1.3.1"
+ yargs "^7.0.2"
+
+jest-config@^20.0.4:
+ version "20.0.4"
+ resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-20.0.4.tgz#e37930ab2217c913605eff13e7bd763ec48faeea"
+ dependencies:
+ chalk "^1.1.3"
+ glob "^7.1.1"
+ jest-environment-jsdom "^20.0.3"
+ jest-environment-node "^20.0.3"
+ jest-jasmine2 "^20.0.4"
+ jest-matcher-utils "^20.0.3"
+ jest-regex-util "^20.0.3"
+ jest-resolve "^20.0.4"
+ jest-validate "^20.0.3"
+ pretty-format "^20.0.3"
+
+jest-diff@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-20.0.3.tgz#81f288fd9e675f0fb23c75f1c2b19445fe586617"
+ dependencies:
+ chalk "^1.1.3"
+ diff "^3.2.0"
+ jest-matcher-utils "^20.0.3"
+ pretty-format "^20.0.3"
+
+jest-docblock@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-20.0.3.tgz#17bea984342cc33d83c50fbe1545ea0efaa44712"
+
+jest-environment-jsdom@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-20.0.3.tgz#048a8ac12ee225f7190417713834bb999787de99"
+ dependencies:
+ jest-mock "^20.0.3"
+ jest-util "^20.0.3"
+ jsdom "^9.12.0"
+
+jest-environment-node@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-20.0.3.tgz#d488bc4612af2c246e986e8ae7671a099163d403"
+ dependencies:
+ jest-mock "^20.0.3"
+ jest-util "^20.0.3"
+
+jest-haste-map@^20.0.4:
+ version "20.0.4"
+ resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-20.0.4.tgz#653eb55c889ce3c021f7b94693f20a4159badf03"
+ dependencies:
+ fb-watchman "^2.0.0"
+ graceful-fs "^4.1.11"
+ jest-docblock "^20.0.3"
+ micromatch "^2.3.11"
+ sane "~1.6.0"
+ worker-farm "^1.3.1"
+
+jest-jasmine2@^20.0.4:
+ version "20.0.4"
+ resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-20.0.4.tgz#fcc5b1411780d911d042902ef1859e852e60d5e1"
+ dependencies:
+ chalk "^1.1.3"
+ graceful-fs "^4.1.11"
+ jest-diff "^20.0.3"
+ jest-matcher-utils "^20.0.3"
+ jest-matchers "^20.0.3"
+ jest-message-util "^20.0.3"
+ jest-snapshot "^20.0.3"
+ once "^1.4.0"
+ p-map "^1.1.1"
+
+jest-matcher-utils@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-20.0.3.tgz#b3a6b8e37ca577803b0832a98b164f44b7815612"
+ dependencies:
+ chalk "^1.1.3"
+ pretty-format "^20.0.3"
+
+jest-matchers@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-matchers/-/jest-matchers-20.0.3.tgz#ca69db1c32db5a6f707fa5e0401abb55700dfd60"
+ dependencies:
+ jest-diff "^20.0.3"
+ jest-matcher-utils "^20.0.3"
+ jest-message-util "^20.0.3"
+ jest-regex-util "^20.0.3"
+
+jest-message-util@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-20.0.3.tgz#6aec2844306fcb0e6e74d5796c1006d96fdd831c"
+ dependencies:
+ chalk "^1.1.3"
+ micromatch "^2.3.11"
+ slash "^1.0.0"
+
+jest-mock@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-20.0.3.tgz#8bc070e90414aa155c11a8d64c869a0d5c71da59"
+
+jest-regex-util@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-20.0.3.tgz#85bbab5d133e44625b19faf8c6aa5122d085d762"
+
+jest-resolve-dependencies@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-20.0.3.tgz#6e14a7b717af0f2cb3667c549de40af017b1723a"
+ dependencies:
+ jest-regex-util "^20.0.3"
+
+jest-resolve@^20.0.4:
+ version "20.0.4"
+ resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-20.0.4.tgz#9448b3e8b6bafc15479444c6499045b7ffe597a5"
+ dependencies:
+ browser-resolve "^1.11.2"
+ is-builtin-module "^1.0.0"
+ resolve "^1.3.2"
+
+jest-runtime@^20.0.4:
+ version "20.0.4"
+ resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-20.0.4.tgz#a2c802219c4203f754df1404e490186169d124d8"
+ dependencies:
+ babel-core "^6.0.0"
+ babel-jest "^20.0.3"
+ babel-plugin-istanbul "^4.0.0"
+ chalk "^1.1.3"
+ convert-source-map "^1.4.0"
+ graceful-fs "^4.1.11"
+ jest-config "^20.0.4"
+ jest-haste-map "^20.0.4"
+ jest-regex-util "^20.0.3"
+ jest-resolve "^20.0.4"
+ jest-util "^20.0.3"
+ json-stable-stringify "^1.0.1"
+ micromatch "^2.3.11"
+ strip-bom "3.0.0"
+ yargs "^7.0.2"
+
+jest-snapshot@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-20.0.3.tgz#5b847e1adb1a4d90852a7f9f125086e187c76566"
+ dependencies:
+ chalk "^1.1.3"
+ jest-diff "^20.0.3"
+ jest-matcher-utils "^20.0.3"
+ jest-util "^20.0.3"
+ natural-compare "^1.4.0"
+ pretty-format "^20.0.3"
+
+jest-util@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-20.0.3.tgz#0c07f7d80d82f4e5a67c6f8b9c3fe7f65cfd32ad"
+ dependencies:
+ chalk "^1.1.3"
+ graceful-fs "^4.1.11"
+ jest-message-util "^20.0.3"
+ jest-mock "^20.0.3"
+ jest-validate "^20.0.3"
+ leven "^2.1.0"
+ mkdirp "^0.5.1"
+
+jest-validate@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-20.0.3.tgz#d0cfd1de4f579f298484925c280f8f1d94ec3cab"
+ dependencies:
+ chalk "^1.1.3"
+ jest-matcher-utils "^20.0.3"
+ leven "^2.1.0"
+ pretty-format "^20.0.3"
+
+jest@^20.0.4:
+ version "20.0.4"
+ resolved "https://registry.yarnpkg.com/jest/-/jest-20.0.4.tgz#3dd260c2989d6dad678b1e9cc4d91944f6d602ac"
+ dependencies:
+ jest-cli "^20.0.4"
+
jqPaginator@^1.2.0:
version "1.2.0"
resolved "http://registry.npm.taobao.org/jqPaginator/download/jqPaginator-1.2.0.tgz#9ccab8024dae9e7ab5f7aca0573149c967b298d1"
@@ -1860,7 +2368,7 @@ js-tokens@^3.0.0:
version "3.0.2"
resolved "http://registry.npm.taobao.org/js-tokens/download/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
-js-yaml@^3.8.4:
+js-yaml@^3.7.0, js-yaml@^3.8.4:
version "3.9.0"
resolved "http://registry.npm.taobao.org/js-yaml/download/js-yaml-3.9.0.tgz#4ffbbf25c2ac963b8299dc74da7e3740de1c18ce"
dependencies:
@@ -1875,6 +2383,30 @@ jschardet@^1.4.2:
version "1.4.2"
resolved "http://registry.npm.taobao.org/jschardet/download/jschardet-1.4.2.tgz#2aa107f142af4121d145659d44f50830961e699a"
+jsdom@^9.12.0:
+ version "9.12.0"
+ resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.12.0.tgz#e8c546fffcb06c00d4833ca84410fed7f8a097d4"
+ dependencies:
+ abab "^1.0.3"
+ acorn "^4.0.4"
+ acorn-globals "^3.1.0"
+ array-equal "^1.0.0"
+ content-type-parser "^1.0.1"
+ cssom ">= 0.3.2 < 0.4.0"
+ cssstyle ">= 0.2.37 < 0.3.0"
+ escodegen "^1.6.1"
+ html-encoding-sniffer "^1.0.1"
+ nwmatcher ">= 1.3.9 < 2.0.0"
+ parse5 "^1.5.1"
+ request "^2.79.0"
+ sax "^1.2.1"
+ symbol-tree "^3.2.1"
+ tough-cookie "^2.3.2"
+ webidl-conversions "^4.0.0"
+ whatwg-encoding "^1.0.1"
+ whatwg-url "^4.3.0"
+ xml-name-validator "^2.0.1"
+
jsesc@^1.3.0:
version "1.3.0"
resolved "http://registry.npm.taobao.org/jsesc/download/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
@@ -1930,12 +2462,20 @@ kind-of@^4.0.0:
dependencies:
is-buffer "^1.1.5"
+lazy-cache@^1.0.3:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
+
lcid@^1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/lcid/download/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
dependencies:
invert-kv "^1.0.0"
+leven@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
+
levn@^0.3.0, levn@~0.3.0:
version "0.3.0"
resolved "http://registry.npm.taobao.org/levn/download/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
@@ -1967,6 +2507,13 @@ load-json-file@^1.0.0:
pinkie-promise "^2.0.0"
strip-bom "^2.0.0"
+locate-path@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
+ dependencies:
+ p-locate "^2.0.0"
+ path-exists "^3.0.0"
+
lodash._basecopy@^3.0.0:
version "3.0.1"
resolved "http://registry.npm.taobao.org/lodash._basecopy/download/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36"
@@ -2087,7 +2634,7 @@ lodash.templatesettings@^4.0.0:
dependencies:
lodash._reinterpolate "~3.0.0"
-lodash@^4.0.0, lodash@^4.13.1, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@~4.17.4:
+lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@~4.17.4:
version "4.17.4"
resolved "http://registry.npm.taobao.org/lodash/download/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
@@ -2095,6 +2642,10 @@ lodash@~1.0.1:
version "1.0.2"
resolved "http://registry.npm.taobao.org/lodash/download/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551"
+longest@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
+
loose-envify@^1.0.0:
version "1.3.1"
resolved "http://registry.npm.taobao.org/loose-envify/download/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
@@ -2129,6 +2680,12 @@ make-error@^1.2.0:
version "1.3.0"
resolved "http://registry.npm.taobao.org/make-error/download/make-error-1.3.0.tgz#52ad3a339ccf10ce62b4040b708fe707244b8b96"
+makeerror@1.0.x:
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c"
+ dependencies:
+ tmpl "1.0.x"
+
map-cache@^0.2.0:
version "0.2.2"
resolved "http://registry.npm.taobao.org/map-cache/download/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
@@ -2156,7 +2713,11 @@ meow@^3.7.0:
redent "^1.0.0"
trim-newlines "^1.0.0"
-micromatch@^2.3.7:
+merge@^1.1.3:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da"
+
+micromatch@^2.1.5, micromatch@^2.3.11, micromatch@^2.3.7:
version "2.3.11"
resolved "http://registry.npm.taobao.org/micromatch/download/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
dependencies:
@@ -2194,7 +2755,7 @@ minimatch@^2.0.1:
dependencies:
brace-expansion "^1.0.0"
-minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2:
+minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2:
version "3.0.4"
resolved "http://registry.npm.taobao.org/minimatch/download/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
dependencies:
@@ -2211,10 +2772,14 @@ minimist@0.0.8:
version "0.0.8"
resolved "http://registry.npm.taobao.org/minimist/download/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
-minimist@^1.1.0, minimist@^1.1.3:
+minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3:
version "1.2.0"
resolved "http://registry.npm.taobao.org/minimist/download/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+minimist@~0.0.1:
+ version "0.0.10"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
+
"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1:
version "0.5.1"
resolved "http://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
@@ -2265,7 +2830,11 @@ node-gyp@^3.3.1:
tar "^2.0.0"
which "1"
-node-notifier@^5.0.1:
+node-int64@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
+
+node-notifier@^5.0.1, node-notifier@^5.0.2:
version "5.1.2"
resolved "http://registry.npm.taobao.org/node-notifier/download/node-notifier-5.1.2.tgz#2fa9e12605fa10009d44549d6fcd8a63dde0e4ff"
dependencies:
@@ -2337,6 +2906,10 @@ number-is-nan@^1.0.0:
version "1.0.1"
resolved "http://registry.npm.taobao.org/number-is-nan/download/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+"nwmatcher@>= 1.3.9 < 2.0.0":
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.1.tgz#7ae9b07b0ea804db7e25f05cb5fe4097d4e4949f"
+
oauth-sign@~0.8.1:
version "0.8.2"
resolved "http://registry.npm.taobao.org/oauth-sign/download/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
@@ -2377,13 +2950,26 @@ once@^1.3.0, once@~1.3.0:
dependencies:
wrappy "1"
+once@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ dependencies:
+ wrappy "1"
+
onetime@^2.0.0:
version "2.0.1"
resolved "http://registry.npm.taobao.org/onetime/download/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
dependencies:
mimic-fn "^1.0.0"
-optionator@^0.8.2:
+optimist@^0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
+ dependencies:
+ minimist "~0.0.1"
+ wordwrap "~0.0.2"
+
+optionator@^0.8.1, optionator@^0.8.2:
version "0.8.2"
resolved "http://registry.npm.taobao.org/optionator/download/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
dependencies:
@@ -2427,6 +3013,16 @@ osenv@0:
os-homedir "^1.0.0"
os-tmpdir "^1.0.0"
+p-limit@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc"
+
+p-locate@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
+ dependencies:
+ p-limit "^1.1.0"
+
p-map@^1.1.1:
version "1.1.1"
resolved "http://registry.npm.taobao.org/p-map/download/p-map-1.1.1.tgz#05f5e4ae97a068371bc2a5cc86bfbdbc19c4ae7a"
@@ -2458,12 +3054,20 @@ parse-passwd@^1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/parse-passwd/download/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
+parse5@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94"
+
path-exists@^2.0.0:
version "2.1.0"
resolved "http://registry.npm.taobao.org/path-exists/download/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
dependencies:
pinkie-promise "^2.0.0"
+path-exists@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "http://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
@@ -2504,7 +3108,7 @@ performance-now@^0.2.0:
version "0.2.0"
resolved "http://registry.npm.taobao.org/performance-now/download/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
-pify@^2.0.0:
+pify@^2.0.0, pify@^2.3.0:
version "2.3.0"
resolved "http://registry.npm.taobao.org/pify/download/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@@ -2534,6 +3138,13 @@ preserve@^0.2.0:
version "0.2.0"
resolved "http://registry.npm.taobao.org/preserve/download/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
+pretty-format@^20.0.3:
+ version "20.0.3"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-20.0.3.tgz#020e350a560a1fe1a98dc3beb6ccffb386de8b14"
+ dependencies:
+ ansi-regex "^2.1.1"
+ ansi-styles "^3.0.0"
+
pretty-hrtime@^1.0.0:
version "1.0.3"
resolved "http://registry.npm.taobao.org/pretty-hrtime/download/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1"
@@ -2550,6 +3161,10 @@ progress@^2.0.0:
version "2.0.0"
resolved "http://registry.npm.taobao.org/progress/download/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
+prr@~0.0.0:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a"
+
pseudomap@^1.0.2:
version "1.0.2"
resolved "http://registry.npm.taobao.org/pseudomap/download/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
@@ -2747,7 +3362,11 @@ resolve-from@^1.0.0:
version "1.0.1"
resolved "http://registry.npm.taobao.org/resolve-from/download/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
-resolve@^1.1.6, resolve@^1.1.7:
+resolve@1.1.7:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
+
+resolve@^1.1.6, resolve@^1.1.7, resolve@^1.3.2:
version "1.3.3"
resolved "http://registry.npm.taobao.org/resolve/download/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5"
dependencies:
@@ -2760,7 +3379,13 @@ restore-cursor@^2.0.0:
onetime "^2.0.0"
signal-exit "^3.0.2"
-rimraf@2, rimraf@^2.2.8:
+right-align@^0.1.1:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
+ dependencies:
+ align-text "^0.1.1"
+
+rimraf@2, rimraf@^2.2.8, rimraf@^2.6.1:
version "2.6.1"
resolved "http://registry.npm.taobao.org/rimraf/download/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d"
dependencies:
@@ -2793,6 +3418,18 @@ safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.1"
resolved "http://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
+sane@~1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/sane/-/sane-1.6.0.tgz#9610c452307a135d29c1fdfe2547034180c46775"
+ dependencies:
+ anymatch "^1.3.0"
+ exec-sh "^0.2.0"
+ fb-watchman "^1.8.0"
+ minimatch "^3.0.2"
+ minimist "^1.1.1"
+ walker "~1.0.5"
+ watch "~0.10.0"
+
sass-graph@^2.1.1:
version "2.2.4"
resolved "http://registry.npm.taobao.org/sass-graph/download/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49"
@@ -2802,6 +3439,10 @@ sass-graph@^2.1.1:
scss-tokenizer "^0.2.3"
yargs "^7.0.0"
+sax@^1.2.1:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+
scss-tokenizer@^0.2.3:
version "0.2.3"
resolved "http://registry.npm.taobao.org/scss-tokenizer/download/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1"
@@ -2857,16 +3498,22 @@ source-map-support@^0.4.2:
dependencies:
source-map "^0.5.6"
-source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.6, source-map@~0.5.1:
+source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1:
version "0.5.6"
resolved "http://registry.npm.taobao.org/source-map/download/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
-source-map@^0.4.2:
+source-map@^0.4.2, source-map@^0.4.4:
version "0.4.4"
resolved "http://registry.npm.taobao.org/source-map/download/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
dependencies:
amdefine ">=0.0.4"
+source-map@~0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d"
+ dependencies:
+ amdefine ">=0.0.4"
+
sparkles@^1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/sparkles/download/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3"
@@ -2925,6 +3572,12 @@ stream-consume@~0.1.0:
version "0.1.0"
resolved "http://registry.npm.taobao.org/stream-consume/download/stream-consume-0.1.0.tgz#a41ead1a6d6081ceb79f65b061901b6d8f3d1d0f"
+string-length@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac"
+ dependencies:
+ strip-ansi "^3.0.0"
+
string-width@^1.0.1, string-width@^1.0.2:
version "1.0.2"
resolved "http://registry.npm.taobao.org/string-width/download/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
@@ -2966,6 +3619,10 @@ strip-ansi@^4.0.0:
dependencies:
ansi-regex "^3.0.0"
+strip-bom@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+
strip-bom@^1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/strip-bom/download/strip-bom-1.0.0.tgz#85b8862f3844b5a6d5ec8467a93598173a36f794"
@@ -2993,6 +3650,12 @@ supports-color@^2.0.0:
version "2.0.0"
resolved "http://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+supports-color@^3.1.2:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
+ dependencies:
+ has-flag "^1.0.0"
+
supports-color@^4.0.0:
version "4.2.0"
resolved "http://registry.npm.taobao.org/supports-color/download/supports-color-4.2.0.tgz#ad986dc7eb2315d009b4d77c8169c2231a684037"
@@ -3003,6 +3666,10 @@ sweetalert2@^6.6.5:
version "6.6.6"
resolved "http://registry.npm.taobao.org/sweetalert2/download/sweetalert2-6.6.6.tgz#3630279d98f04fb8b89f2e8b098fc42f03bcb555"
+symbol-tree@^3.2.1:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
+
table@^4.0.1:
version "4.0.1"
resolved "http://registry.npm.taobao.org/table/download/table-4.0.1.tgz#a8116c133fac2c61f4a420ab6cdf5c4d61f0e435"
@@ -3022,10 +3689,24 @@ tar@^2.0.0:
fstream "^1.0.2"
inherits "2"
+test-exclude@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.1.1.tgz#4d84964b0966b0087ecc334a2ce002d3d9341e26"
+ dependencies:
+ arrify "^1.0.1"
+ micromatch "^2.3.11"
+ object-assign "^4.1.0"
+ read-pkg-up "^1.0.1"
+ require-main-filename "^1.0.1"
+
text-table@~0.2.0:
version "0.2.0"
resolved "http://registry.npm.taobao.org/text-table/download/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+throat@^3.0.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/throat/-/throat-3.2.0.tgz#50cb0670edbc40237b9e347d7e1f88e4620af836"
+
through2@2.0.3, through2@^2.0.0, through2@^2.0.1, through2@^2.0.3:
version "2.0.3"
resolved "http://registry.npm.taobao.org/through2/download/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"
@@ -3060,6 +3741,10 @@ tmp@^0.0.31:
dependencies:
os-tmpdir "~1.0.1"
+tmpl@1.0.x:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"
+
to-fast-properties@^1.0.1:
version "1.0.3"
resolved "http://registry.npm.taobao.org/to-fast-properties/download/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
@@ -3068,12 +3753,16 @@ toastr@^2.1.2:
version "2.1.2"
resolved "http://registry.npm.taobao.org/toastr/download/toastr-2.1.2.tgz#fd69066ae7578a5b3357725fc9c7c335e9b681df"
-tough-cookie@~2.3.0:
+tough-cookie@^2.3.2, tough-cookie@~2.3.0:
version "2.3.2"
resolved "http://registry.npm.taobao.org/tough-cookie/download/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a"
dependencies:
punycode "^1.4.1"
+tr46@~0.0.3:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
+
trim-newlines@^1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/trim-newlines/download/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
@@ -3106,6 +3795,15 @@ typedarray@^0.0.6:
version "0.0.6"
resolved "http://registry.npm.taobao.org/typedarray/download/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+uglify-js@^2.6:
+ version "2.8.29"
+ resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
+ dependencies:
+ source-map "~0.5.1"
+ yargs "~3.10.0"
+ optionalDependencies:
+ uglify-to-browserify "~1.0.0"
+
uglify-js@^3.0.5:
version "3.0.24"
resolved "http://registry.npm.taobao.org/uglify-js/download/uglify-js-3.0.24.tgz#ee93400ad9857fb7a1671778db83f6a23f033121"
@@ -3113,6 +3811,10 @@ uglify-js@^3.0.5:
commander "~2.9.0"
source-map "~0.5.1"
+uglify-to-browserify@~1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
+
unc-path-regex@^0.1.0:
version "0.1.2"
resolved "http://registry.npm.taobao.org/unc-path-regex/download/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
@@ -3197,6 +3899,37 @@ vinyl@^2.0.0:
remove-trailing-separator "^1.0.1"
replace-ext "^1.0.0"
+walker@~1.0.5:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"
+ dependencies:
+ makeerror "1.0.x"
+
+watch@~0.10.0:
+ version "0.10.0"
+ resolved "https://registry.yarnpkg.com/watch/-/watch-0.10.0.tgz#77798b2da0f9910d595f1ace5b0c2258521f21dc"
+
+webidl-conversions@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
+
+webidl-conversions@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.1.tgz#8015a17ab83e7e1b311638486ace81da6ce206a0"
+
+whatwg-encoding@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.1.tgz#3c6c451a198ee7aec55b1ec61d0920c67801a5f4"
+ dependencies:
+ iconv-lite "0.4.13"
+
+whatwg-url@^4.3.0:
+ version "4.8.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.8.0.tgz#d2981aa9148c1e00a41c5a6131166ab4683bbcc0"
+ dependencies:
+ tr46 "~0.0.3"
+ webidl-conversions "^3.0.0"
+
which-module@^1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/which-module/download/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
@@ -3213,10 +3946,29 @@ wide-align@^1.1.0:
dependencies:
string-width "^1.0.2"
+window-size@0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
+
+wordwrap@0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
+
+wordwrap@~0.0.2:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
+
wordwrap@~1.0.0:
version "1.0.0"
resolved "http://registry.npm.taobao.org/wordwrap/download/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
+worker-farm@^1.3.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.4.1.tgz#a438bc993a7a7d133bcb6547c95eca7cff4897d8"
+ dependencies:
+ errno "^0.1.4"
+ xtend "^4.0.1"
+
wrap-ansi@^2.0.0:
version "2.1.0"
resolved "http://registry.npm.taobao.org/wrap-ansi/download/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
@@ -3234,7 +3986,11 @@ write@^0.2.1:
dependencies:
mkdirp "^0.5.1"
-"xtend@>=4.0.0 <4.1.0-0", xtend@~4.0.1:
+xml-name-validator@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635"
+
+"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.1, xtend@~4.0.1:
version "4.0.1"
resolved "http://registry.npm.taobao.org/xtend/download/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
@@ -3252,7 +4008,7 @@ yargs-parser@^5.0.0:
dependencies:
camelcase "^3.0.0"
-yargs@^7.0.0:
+yargs@^7.0.0, yargs@^7.0.2:
version "7.1.0"
resolved "http://registry.npm.taobao.org/yargs/download/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"
dependencies:
@@ -3270,6 +4026,15 @@ yargs@^7.0.0:
y18n "^3.2.1"
yargs-parser "^5.0.0"
+yargs@~3.10.0:
+ version "3.10.0"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"
+ dependencies:
+ camelcase "^1.0.2"
+ cliui "^2.1.0"
+ decamelize "^1.0.0"
+ window-size "0.1.0"
+
yazl@^2.1.0:
version "2.4.2"
resolved "http://registry.npm.taobao.org/yazl/download/yazl-2.4.2.tgz#14cb19083e1e25a70092c1588aabe0f4e4dd4d88"