diff --git a/test/api/v3/integration/user/stats/POST-user_allocate.test.js b/test/api/v3/integration/user/stats/POST-user_allocate.test.js index bf10e722de..6ace8731db 100644 --- a/test/api/v3/integration/user/stats/POST-user_allocate.test.js +++ b/test/api/v3/integration/user/stats/POST-user_allocate.test.js @@ -8,7 +8,11 @@ describe('POST /user/allocate', () => { let user; beforeEach(async () => { - user = await generateUser(); + user = await generateUser({ + 'stats.lvl': 10, + 'flags.classSelected': true, + 'preferences.disableClasses': false, + }); }); // More tests in common code unit tests @@ -31,6 +35,16 @@ describe('POST /user/allocate', () => { }); }); + it('returns an error if the user hasn\'t selected class', async () => { + await user.update({'flags.classSelected': false}); + await expect(user.post('/user/allocate')) + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('classNotSelected'), + }); + }); + it('allocates attribute points', async () => { await user.update({'stats.points': 1}); let res = await user.post('/user/allocate?stat=con'); diff --git a/test/api/v3/integration/user/stats/POST-user_allocate_bulk.test.js b/test/api/v3/integration/user/stats/POST-user_allocate_bulk.test.js index 21705c403f..a665521d0d 100644 --- a/test/api/v3/integration/user/stats/POST-user_allocate_bulk.test.js +++ b/test/api/v3/integration/user/stats/POST-user_allocate_bulk.test.js @@ -13,7 +13,11 @@ describe('POST /user/allocate-bulk', () => { }; beforeEach(async () => { - user = await generateUser(); + user = await generateUser({ + 'stats.lvl': 10, + 'flags.classSelected': true, + 'preferences.disableClasses': false, + }); }); // More tests in common code unit tests @@ -27,6 +31,16 @@ describe('POST /user/allocate-bulk', () => { }); }); + it('returns an error if user has not selected class', async () => { + await user.update({'flags.classSelected': false}); + await expect(user.post('/user/allocate-bulk', statsUpdate)) + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('classNotSelected'), + }); + }); + it('allocates attribute points', async () => { await user.update({'stats.points': 3}); diff --git a/test/common/libs/hasClass.test.js b/test/common/libs/hasClass.test.js new file mode 100644 index 0000000000..1b7f9cc6c2 --- /dev/null +++ b/test/common/libs/hasClass.test.js @@ -0,0 +1,52 @@ +import hasClass from '../../../website/common/script/libs/hasClass'; +import { generateUser } from '../../helpers/common.helper'; + +describe('hasClass', () => { + it('returns false for user with level below 10', () => { + let userLvl9 = generateUser({ + 'stats.lvl': 9, + 'flags.classSelected': true, + 'preferences.disableClasses': false, + }); + + let result = hasClass(userLvl9); + + expect(result).to.eql(false); + }); + + it('returns false for user with class not selected', () => { + let userClassNotSelected = generateUser({ + 'stats.lvl': 10, + 'flags.classSelected': false, + 'preferences.disableClasses': false, + }); + + let result = hasClass(userClassNotSelected); + + expect(result).to.eql(false); + }); + + it('returns false for user with classes disabled', () => { + let userClassesDisabled = generateUser({ + 'stats.lvl': 10, + 'flags.classSelected': true, + 'preferences.disableClasses': true, + }); + + let result = hasClass(userClassesDisabled); + + expect(result).to.eql(false); + }); + + it('returns true for user with class', () => { + let userClassSelected = generateUser({ + 'stats.lvl': 10, + 'flags.classSelected': true, + 'preferences.disableClasses': false, + }); + + let result = hasClass(userClassSelected); + + expect(result).to.eql(true); + }); +}); diff --git a/test/common/ops/stats/allocate.js b/test/common/ops/stats/allocate.js index 40f43d3bf2..62e162f55a 100644 --- a/test/common/ops/stats/allocate.js +++ b/test/common/ops/stats/allocate.js @@ -13,7 +13,11 @@ describe('shared.ops.allocate', () => { let user; beforeEach(() => { - user = generateUser(); + user = generateUser({ + 'stats.lvl': 10, + 'flags.classSelected': true, + 'preferences.disableClasses': false, + }); }); it('throws an error if an invalid attribute is supplied', (done) => { @@ -28,6 +32,39 @@ describe('shared.ops.allocate', () => { } }); + it('throws an error if the user is below lvl 10', (done) => { + user.stats.lvl = 9; + try { + allocate(user); + } catch (err) { + expect(err).to.be.an.instanceof(NotAuthorized); + expect(err.message).to.equal(i18n.t('classNotSelected')); + done(); + } + }); + + it('throws an error if the user hasn\'t selected class', (done) => { + user.flags.classSelected = false; + try { + allocate(user); + } catch (err) { + expect(err).to.be.an.instanceof(NotAuthorized); + expect(err.message).to.equal(i18n.t('classNotSelected')); + done(); + } + }); + + it('throws an error if the user has disabled classes', (done) => { + user.preferences.disableClasses = true; + try { + allocate(user); + } catch (err) { + expect(err).to.be.an.instanceof(NotAuthorized); + expect(err.message).to.equal(i18n.t('classNotSelected')); + done(); + } + }); + it('throws an error if the user doesn\'t have attribute points', (done) => { try { allocate(user); diff --git a/test/common/ops/stats/allocateBulk.js b/test/common/ops/stats/allocateBulk.js index 2dbeca5a37..1a46c82214 100644 --- a/test/common/ops/stats/allocateBulk.js +++ b/test/common/ops/stats/allocateBulk.js @@ -13,7 +13,11 @@ describe('shared.ops.allocateBulk', () => { let user; beforeEach(() => { - user = generateUser(); + user = generateUser({ + 'stats.lvl': 10, + 'flags.classSelected': true, + 'preferences.disableClasses': false, + }); }); it('throws an error if an invalid attribute is supplied', (done) => { @@ -43,6 +47,60 @@ describe('shared.ops.allocateBulk', () => { } }); + it('throws an error if the user is below lvl 10', (done) => { + user.stats.lvl = 9; + try { + allocateBulk(user, { + body: { + stats: { + int: 1, + str: 2, + }, + }, + }); + } catch (err) { + expect(err).to.be.an.instanceof(NotAuthorized); + expect(err.message).to.equal(i18n.t('classNotSelected')); + done(); + } + }); + + it('throws an error if the user hasn\'t selected class', (done) => { + user.flags.classSelected = false; + try { + allocateBulk(user, { + body: { + stats: { + int: 1, + str: 2, + }, + }, + }); + } catch (err) { + expect(err).to.be.an.instanceof(NotAuthorized); + expect(err.message).to.equal(i18n.t('classNotSelected')); + done(); + } + }); + + it('throws an error if the user has disabled classes', (done) => { + user.preferences.disableClasses = true; + try { + allocateBulk(user, { + body: { + stats: { + int: 1, + str: 2, + }, + }, + }); + } catch (err) { + expect(err).to.be.an.instanceof(NotAuthorized); + expect(err.message).to.equal(i18n.t('classNotSelected')); + done(); + } + }); + it('throws an error if the user doesn\'t have attribute points', (done) => { try { allocateBulk(user, { diff --git a/website/client/store/getters/members.js b/website/client/store/getters/members.js index c0c246639c..0d66e70bbe 100644 --- a/website/client/store/getters/members.js +++ b/website/client/store/getters/members.js @@ -1,3 +1,5 @@ +import memberHasClass from 'common/script/libs/hasClass'; + export function isBuffed () { return (member) => { const buffs = member.stats.buffs; @@ -6,7 +8,5 @@ export function isBuffed () { } export function hasClass () { - return (member) => { - return member.stats.lvl >= 10 && !member.preferences.disableClasses && member.flags.classSelected; - }; + return memberHasClass; } \ No newline at end of file diff --git a/website/common/locales/en/character.json b/website/common/locales/en/character.json index 7e94958747..e25b19ecf3 100644 --- a/website/common/locales/en/character.json +++ b/website/common/locales/en/character.json @@ -204,6 +204,7 @@ "hideQuickAllocation": "Hide Stat Allocation", "quickAllocationLevelPopover": "Each level earns you one Point to assign to a Stat of your choice. You can do so manually, or let the game decide for you using one of the Automatic Allocation options found in User Icon > Stats.", "notEnoughAttrPoints": "You don't have enough Stat Points.", + "classNotSelected": "You must select Class before you can assign Stat Points.", "style": "Style", "facialhair": "Facial", "photo": "Photo", diff --git a/website/common/script/index.js b/website/common/script/index.js index bc235e9d89..9298468707 100644 --- a/website/common/script/index.js +++ b/website/common/script/index.js @@ -110,6 +110,9 @@ api.achievements = achievements; import randomVal from './libs/randomVal'; api.randomVal = randomVal; +import hasClass from './libs/hasClass'; +api.hasClass = hasClass; + import autoAllocate from './fns/autoAllocate'; import crit from './fns/crit'; import handleTwoHanded from './fns/handleTwoHanded'; diff --git a/website/common/script/libs/hasClass.js b/website/common/script/libs/hasClass.js new file mode 100644 index 0000000000..f9eeabfd5a --- /dev/null +++ b/website/common/script/libs/hasClass.js @@ -0,0 +1,8 @@ +// Check if user has Class system enabled +module.exports = function hasClass (member) { + return ( + member.stats.lvl >= 10 && + !member.preferences.disableClasses && + member.flags.classSelected + ); +}; diff --git a/website/common/script/ops/stats/allocate.js b/website/common/script/ops/stats/allocate.js index d65b2e05cb..0db8c59732 100644 --- a/website/common/script/ops/stats/allocate.js +++ b/website/common/script/ops/stats/allocate.js @@ -8,6 +8,7 @@ import { } from '../../libs/errors'; import i18n from '../../i18n'; import errorMessage from '../../libs/errorMessage'; +import hasClass from '../../libs/hasClass'; module.exports = function allocate (user, req = {}) { let stat = get(req, 'query.stat', 'str'); @@ -16,6 +17,10 @@ module.exports = function allocate (user, req = {}) { throw new BadRequest(errorMessage('invalidAttribute', {attr: stat})); } + if (!hasClass(user)) { + throw new NotAuthorized(i18n.t('classNotSelected', req.language)); + } + if (user.stats.points > 0) { user.stats[stat]++; user.stats.points--; diff --git a/website/common/script/ops/stats/allocateBulk.js b/website/common/script/ops/stats/allocateBulk.js index 7a68972d03..ad6e2aeedf 100644 --- a/website/common/script/ops/stats/allocateBulk.js +++ b/website/common/script/ops/stats/allocateBulk.js @@ -8,6 +8,7 @@ import { } from '../../libs/errors'; import i18n from '../../i18n'; import errorMessage from '../../libs/errorMessage'; +import hasClass from '../../libs/hasClass'; module.exports = function allocateBulk (user, req = {}) { const stats = get(req, 'body.stats'); @@ -21,6 +22,10 @@ module.exports = function allocateBulk (user, req = {}) { throw new BadRequest(errorMessage('invalidAttribute', {attr: invalidStats.join(',')})); } + if (!hasClass(user)) { + throw new NotAuthorized(i18n.t('classNotSelected', req.language)); + } + if (user.stats.points <= 0) { throw new NotAuthorized(i18n.t('notEnoughAttrPoints', req.language)); }