diff --git a/package.json b/package.json index 4542a4b1d0..bce4ca9e91 100644 --- a/package.json +++ b/package.json @@ -100,6 +100,7 @@ "client:build": "cd website/client && npm run build", "client:unit": "cd website/client && npm run test:unit", "start": "gulp nodemon", + "debug": "gulp nodemon --inspect", "postinstall": "gulp build && cd website/client && npm install", "apidoc": "gulp apidoc" }, diff --git a/test/api/v3/integration/user/POST-user_unlock.js b/test/api/v3/integration/user/POST-user_unlock.js index 4418af54c3..cb6eceeb8d 100644 --- a/test/api/v3/integration/user/POST-user_unlock.js +++ b/test/api/v3/integration/user/POST-user_unlock.js @@ -6,6 +6,7 @@ import { describe('POST /user/unlock', () => { let user; const unlockPath = 'shirt.convict,shirt.cross,shirt.fire,shirt.horizon,shirt.ocean,shirt.purple,shirt.rainbow,shirt.redblue,shirt.thunder,shirt.tropical,shirt.zombie'; + const unlockGearSetPath = 'items.gear.owned.headAccessory_special_bearEars,items.gear.owned.headAccessory_special_cactusEars,items.gear.owned.headAccessory_special_foxEars,items.gear.owned.headAccessory_special_lionEars,items.gear.owned.headAccessory_special_pandaEars,items.gear.owned.headAccessory_special_pigEars,items.gear.owned.headAccessory_special_tigerEars,items.gear.owned.headAccessory_special_wolfEars'; const unlockCost = 1.25; const usersStartingGems = 5; @@ -34,4 +35,25 @@ describe('POST /user/unlock', () => { expect(response.message).to.equal(t('unlocked')); expect(user.balance).to.equal(usersStartingGems - unlockCost); }); + + it('does not reduce a user\'s balance twice', async () => { + await user.update({ + balance: usersStartingGems, + }); + const response = await user.post(`/user/unlock?path=${unlockGearSetPath}`); + await user.sync(); + + expect(response.message).to.equal(t('unlocked')); + expect(user.balance).to.equal(usersStartingGems - unlockCost); + + expect(user.post(`/user/unlock?path=${unlockGearSetPath}`)) + .to.eventually.be.rejected.and.to.eql({ + code: 401, + error: 'NotAuthorized', + message: t('alreadyUnlocked'), + }); + await user.sync(); + + expect(user.balance).to.equal(usersStartingGems - unlockCost); + }); }); diff --git a/test/common/ops/unlock.js b/test/common/ops/unlock.js index 80354c2ffc..8c527d10aa 100644 --- a/test/common/ops/unlock.js +++ b/test/common/ops/unlock.js @@ -1,12 +1,7 @@ import unlock from '../../../website/common/script/ops/unlock'; import i18n from '../../../website/common/script/i18n'; -import { - generateUser, -} from '../../helpers/common.helper'; -import { - NotAuthorized, - BadRequest, -} from '../../../website/common/script/libs/errors'; +import { generateUser } from '../../helpers/common.helper'; +import { NotAuthorized, BadRequest } from '../../../website/common/script/libs/errors'; describe('shared.ops.unlock', () => { let user; @@ -31,6 +26,15 @@ describe('shared.ops.unlock', () => { } }); + it('does not unlock lost gear', done => { + user.items.gear.owned.headAccessory_special_bearEars = false; + + unlock(user, { query: { path: 'items.gear.owned.headAccessory_special_bearEars' } }); + + expect(user.balance).to.equal(usersStartingGems); + done(); + }); + it('returns an error when user balance is too low', done => { user.balance = 0; @@ -50,18 +54,30 @@ describe('shared.ops.unlock', () => { } catch (err) { expect(err).to.be.an.instanceof(NotAuthorized); expect(err.message).to.equal(i18n.t('alreadyUnlocked')); + expect(user.balance).to.equal(3.75); done(); } }); - // disabled until fully implemente - xit('returns an error when user already owns items in a full set', done => { + it('returns an error when user already owns a full set of gear', done => { try { - unlock(user, { query: { path: unlockPath } }); - unlock(user, { query: { path: unlockPath } }); + unlock(user, { query: { path: unlockGearSetPath } }); + unlock(user, { query: { path: unlockGearSetPath } }); } catch (err) { expect(err).to.be.an.instanceof(NotAuthorized); expect(err.message).to.equal(i18n.t('alreadyUnlocked')); + expect(user.balance).to.equal(3.75); + done(); + } + }); + + xit('returns an error when user already owns items in a full set', done => { + try { + unlock(user, { query: { path: unlockPath.split(',').splice(2).join(',') } }); + unlock(user, { query: { path: unlockPath } }); + } catch (err) { + expect(err).to.be.an.instanceof(NotAuthorized); + expect(err.message).to.equal(i18n.t('alreadyUnlockedPart')); done(); } }); @@ -78,7 +94,7 @@ describe('shared.ops.unlock', () => { expect(user.preferences.background).to.equal('giant_florals'); }); - it('un-equips an item already equipped', () => { + it('un-equips a background already equipped', () => { expect(user.purchased.background.giant_florals).to.not.exist; unlock(user, { query: { path: backgroundUnlockPath } }); // unlock @@ -105,7 +121,7 @@ describe('shared.ops.unlock', () => { expect(user.items.gear.owned.headAccessory_special_wolfEars).to.be.true; }); - it('unlocks a an item', () => { + it('unlocks an item', () => { const [, message] = unlock(user, { query: { path: backgroundUnlockPath } }); expect(message).to.equal(i18n.t('unlocked')); diff --git a/website/client/package-lock.json b/website/client/package-lock.json index a216dfd250..c181156859 100644 --- a/website/client/package-lock.json +++ b/website/client/package-lock.json @@ -15158,11 +15158,6 @@ "sha.js": "^2.4.8" } }, - "perfect-scrollbar": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.0.tgz", - "integrity": "sha512-NrNHJn5mUGupSiheBTy6x+6SXCFbLlm8fVZh9moIzw/LgqElN5q4ncR4pbCBCYuCJ8Kcl9mYM0NgDxvW+b4LxA==" - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -15433,17 +15428,6 @@ } } }, - "postcss-import": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz", - "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==", - "requires": { - "postcss": "^7.0.1", - "postcss-value-parser": "^3.2.3", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - } - }, "postcss-load-config": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", @@ -16633,21 +16617,6 @@ "lodash": "^4.0.1" } }, - "read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", - "requires": { - "pify": "^2.3.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } - }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -19722,16 +19691,6 @@ "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==" }, - "vue2-perfect-scrollbar": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vue2-perfect-scrollbar/-/vue2-perfect-scrollbar-1.4.0.tgz", - "integrity": "sha512-kluthjiZOOhAZ/18RTZJr2Y9lpjgkuOuxkH8MMMq1dYrSUdvlEv8V1UPtW7UDVcTAUo048AUE/W4hSPTfluejw==", - "requires": { - "cssnano": "^4.1.3", - "perfect-scrollbar": "^1.4.0", - "postcss-import": "^12.0.0" - } - }, "vuedraggable": { "version": "2.23.2", "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.23.2.tgz", diff --git a/website/client/src/components/messages/messageList.vue b/website/client/src/components/messages/messageList.vue index 4b254f7aad..2dec7de5c6 100644 --- a/website/client/src/components/messages/messageList.vue +++ b/website/client/src/components/messages/messageList.vue @@ -6,7 +6,7 @@
-
+
-
-
-
+
+
+