mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-14 21:27:23 +01:00
Add Transaction log for gem and hourglass changes (#13589)
* Log all gem transactions to database * Also store hourglass transactions * Fix tests * Display transaction history in hall of heroes for admins * add tests to new API call * hide transaction settings tab for non admins * fix(lint): remove console * fix(lint): various automatic corrections * fix(transactions): use enum expected pluralizations * fix api unit tests * fix lint * fix failing test * Fix minor inconsistencies * Log all gem transactions to database * Also store hourglass transactions * Fix tests * Display transaction history in hall of heroes for admins * add tests to new API call * hide transaction settings tab for non admins * fix(lint): remove console * fix(lint): various automatic corrections * fix(transactions): use enum expected pluralizations * fix api unit tests * fix lint * Fix minor inconsistencies Co-authored-by: Sabe Jones <sabrecat@gmail.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
38
test/api/v4/members/GET-purchase_history.test.js
Normal file
38
test/api/v4/members/GET-purchase_history.test.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import {
|
||||||
|
generateUser,
|
||||||
|
translate as t,
|
||||||
|
} from '../../../helpers/api-integration/v4';
|
||||||
|
|
||||||
|
describe('GET /members/:memberId/purchase-history', () => {
|
||||||
|
let user;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
user = await generateUser({
|
||||||
|
contributor: { admin: true },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('validates req.params.memberId', async () => {
|
||||||
|
await expect(user.get('/members/invalidUUID/purchase-history')).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 400,
|
||||||
|
error: 'BadRequest',
|
||||||
|
message: t('invalidReqParams'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns error if user is not admin', async () => {
|
||||||
|
const member = await generateUser();
|
||||||
|
const nonAdmin = await generateUser();
|
||||||
|
await expect(nonAdmin.get(`/members/${member._id}/purchase-history`)).to.eventually.be.rejected.and.eql({
|
||||||
|
code: 401,
|
||||||
|
error: 'NotAuthorized',
|
||||||
|
message: t('noAdminAccess'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns purchase history based on given user', async () => {
|
||||||
|
const member = await generateUser();
|
||||||
|
const response = await user.get(`/members/${member._id}/purchase-history`);
|
||||||
|
expect(response.length).to.equal(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -40,28 +40,27 @@ describe('shared.ops.buy', () => {
|
|||||||
analytics.track.restore();
|
analytics.track.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when key is not provided', done => {
|
it('returns error when key is not provided', async () => {
|
||||||
try {
|
try {
|
||||||
buy(user);
|
await buy(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(errorMessage('missingKeyParam'));
|
expect(err.message).to.equal(errorMessage('missingKeyParam'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('recovers 15 hp', () => {
|
it('recovers 15 hp', async () => {
|
||||||
user.stats.hp = 30;
|
user.stats.hp = 30;
|
||||||
buy(user, { params: { key: 'potion' } }, analytics);
|
await buy(user, { params: { key: 'potion' } }, analytics);
|
||||||
expect(user.stats.hp).to.eql(45);
|
expect(user.stats.hp).to.eql(45);
|
||||||
|
|
||||||
expect(analytics.track).to.be.calledOnce;
|
expect(analytics.track).to.be.calledOnce;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds equipment to inventory', () => {
|
it('adds equipment to inventory', async () => {
|
||||||
user.stats.gp = 31;
|
user.stats.gp = 31;
|
||||||
|
|
||||||
buy(user, { params: { key: 'armor_warrior_1' } });
|
await buy(user, { params: { key: 'armor_warrior_1' } });
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.eql({
|
expect(user.items.gear.owned).to.eql({
|
||||||
weapon_warrior_0: true,
|
weapon_warrior_0: true,
|
||||||
@@ -90,10 +89,10 @@ describe('shared.ops.buy', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('buys Steampunk Accessories Set', () => {
|
it('buys Steampunk Accessories Set', async () => {
|
||||||
user.purchased.plan.consecutive.trinkets = 1;
|
user.purchased.plan.consecutive.trinkets = 1;
|
||||||
|
|
||||||
buy(user, {
|
await buy(user, {
|
||||||
params: {
|
params: {
|
||||||
key: '301404',
|
key: '301404',
|
||||||
},
|
},
|
||||||
@@ -108,10 +107,10 @@ describe('shared.ops.buy', () => {
|
|||||||
expect(user.items.gear.owned).to.have.property('eyewear_mystery_301404', true);
|
expect(user.items.gear.owned).to.have.property('eyewear_mystery_301404', true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('buys a Quest scroll', () => {
|
it('buys a Quest scroll', async () => {
|
||||||
user.stats.gp = 205;
|
user.stats.gp = 205;
|
||||||
|
|
||||||
buy(user, {
|
await buy(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'dilatoryDistress1',
|
key: 'dilatoryDistress1',
|
||||||
},
|
},
|
||||||
@@ -122,11 +121,11 @@ describe('shared.ops.buy', () => {
|
|||||||
expect(user.stats.gp).to.equal(5);
|
expect(user.stats.gp).to.equal(5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('buys a special item', () => {
|
it('buys a special item', async () => {
|
||||||
user.stats.gp = 11;
|
user.stats.gp = 11;
|
||||||
const item = content.special.thankyou;
|
const item = content.special.thankyou;
|
||||||
|
|
||||||
const [data, message] = buy(user, {
|
const [data, message] = await buy(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'thankyou',
|
key: 'thankyou',
|
||||||
},
|
},
|
||||||
@@ -144,15 +143,15 @@ describe('shared.ops.buy', () => {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows for bulk purchases', () => {
|
it('allows for bulk purchases', async () => {
|
||||||
user.stats.hp = 30;
|
user.stats.hp = 30;
|
||||||
buy(user, { params: { key: 'potion' }, quantity: 2 });
|
await buy(user, { params: { key: 'potion' }, quantity: 2 });
|
||||||
expect(user.stats.hp).to.eql(50);
|
expect(user.stats.hp).to.eql(50);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('errors if user supplies a non-numeric quantity', done => {
|
it('errors if user supplies a non-numeric quantity', async () => {
|
||||||
try {
|
try {
|
||||||
buy(user, {
|
await buy(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'dilatoryDistress1',
|
key: 'dilatoryDistress1',
|
||||||
},
|
},
|
||||||
@@ -162,13 +161,12 @@ describe('shared.ops.buy', () => {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(errorMessage('invalidQuantity'));
|
expect(err.message).to.equal(errorMessage('invalidQuantity'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('errors if user supplies a negative quantity', done => {
|
it('errors if user supplies a negative quantity', async () => {
|
||||||
try {
|
try {
|
||||||
buy(user, {
|
await buy(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'dilatoryDistress1',
|
key: 'dilatoryDistress1',
|
||||||
},
|
},
|
||||||
@@ -178,13 +176,12 @@ describe('shared.ops.buy', () => {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(errorMessage('invalidQuantity'));
|
expect(err.message).to.equal(errorMessage('invalidQuantity'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('errors if user supplies a decimal quantity', done => {
|
it('errors if user supplies a decimal quantity', async () => {
|
||||||
try {
|
try {
|
||||||
buy(user, {
|
await buy(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'dilatoryDistress1',
|
key: 'dilatoryDistress1',
|
||||||
},
|
},
|
||||||
@@ -194,7 +191,6 @@ describe('shared.ops.buy', () => {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(errorMessage('invalidQuantity'));
|
expect(err.message).to.equal(errorMessage('invalidQuantity'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ describe('shared.ops.buyArmoire', () => {
|
|||||||
const YIELD_EXP = 0.9;
|
const YIELD_EXP = 0.9;
|
||||||
const analytics = { track () {} };
|
const analytics = { track () {} };
|
||||||
|
|
||||||
function buyArmoire (_user, _req, _analytics) {
|
async function buyArmoire (_user, _req, _analytics) {
|
||||||
const buyOp = new BuyArmoireOperation(_user, _req, _analytics);
|
const buyOp = new BuyArmoireOperation(_user, _req, _analytics);
|
||||||
|
|
||||||
return buyOp.purchase();
|
return buyOp.purchase();
|
||||||
@@ -61,11 +61,11 @@ describe('shared.ops.buyArmoire', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
context('failure conditions', () => {
|
context('failure conditions', () => {
|
||||||
it('does not open if user does not have enough gold', done => {
|
it('does not open if user does not have enough gold', async () => {
|
||||||
user.stats.gp = 50;
|
user.stats.gp = 50;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buyArmoire(user);
|
await buyArmoire(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
||||||
@@ -74,17 +74,16 @@ describe('shared.ops.buyArmoire', () => {
|
|||||||
});
|
});
|
||||||
expect(user.items.food).to.be.empty;
|
expect(user.items.food).to.be.empty;
|
||||||
expect(user.stats.exp).to.eql(0);
|
expect(user.stats.exp).to.eql(0);
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('non-gear awards', () => {
|
context('non-gear awards', () => {
|
||||||
it('gives Experience', () => {
|
it('gives Experience', async () => {
|
||||||
const previousExp = user.stats.exp;
|
const previousExp = user.stats.exp;
|
||||||
randomValFns.trueRandom.returns(YIELD_EXP);
|
randomValFns.trueRandom.returns(YIELD_EXP);
|
||||||
|
|
||||||
buyArmoire(user);
|
await buyArmoire(user);
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.eql({ weapon_warrior_0: true });
|
expect(user.items.gear.owned).to.eql({ weapon_warrior_0: true });
|
||||||
expect(user.items.food).to.be.empty;
|
expect(user.items.food).to.be.empty;
|
||||||
@@ -92,12 +91,12 @@ describe('shared.ops.buyArmoire', () => {
|
|||||||
expect(user.stats.gp).to.equal(100);
|
expect(user.stats.gp).to.equal(100);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gives food', () => {
|
it('gives food', async () => {
|
||||||
const previousExp = user.stats.exp;
|
const previousExp = user.stats.exp;
|
||||||
|
|
||||||
randomValFns.trueRandom.returns(YIELD_FOOD);
|
randomValFns.trueRandom.returns(YIELD_FOOD);
|
||||||
|
|
||||||
buyArmoire(user);
|
await buyArmoire(user);
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.eql({ weapon_warrior_0: true });
|
expect(user.items.gear.owned).to.eql({ weapon_warrior_0: true });
|
||||||
expect(user.items.food).to.not.be.empty;
|
expect(user.items.food).to.not.be.empty;
|
||||||
@@ -105,12 +104,12 @@ describe('shared.ops.buyArmoire', () => {
|
|||||||
expect(user.stats.gp).to.equal(100);
|
expect(user.stats.gp).to.equal(100);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not give equipment if all equipment has been found', () => {
|
it('does not give equipment if all equipment has been found', async () => {
|
||||||
randomValFns.trueRandom.returns(YIELD_EQUIPMENT);
|
randomValFns.trueRandom.returns(YIELD_EQUIPMENT);
|
||||||
user.items.gear.owned = getFullArmoire();
|
user.items.gear.owned = getFullArmoire();
|
||||||
user.stats.gp = 150;
|
user.stats.gp = 150;
|
||||||
|
|
||||||
buyArmoire(user);
|
await buyArmoire(user);
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.eql(getFullArmoire());
|
expect(user.items.gear.owned).to.eql(getFullArmoire());
|
||||||
const armoireCount = count.remainingGearInSet(user.items.gear.owned, 'armoire');
|
const armoireCount = count.remainingGearInSet(user.items.gear.owned, 'armoire');
|
||||||
@@ -122,13 +121,13 @@ describe('shared.ops.buyArmoire', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
context('gear awards', () => {
|
context('gear awards', () => {
|
||||||
it('always drops equipment the first time', () => {
|
it('always drops equipment the first time', async () => {
|
||||||
delete user.flags.armoireOpened;
|
delete user.flags.armoireOpened;
|
||||||
randomValFns.trueRandom.returns(YIELD_EXP);
|
randomValFns.trueRandom.returns(YIELD_EXP);
|
||||||
|
|
||||||
expect(_.size(user.items.gear.owned)).to.equal(1);
|
expect(_.size(user.items.gear.owned)).to.equal(1);
|
||||||
|
|
||||||
buyArmoire(user);
|
await buyArmoire(user);
|
||||||
|
|
||||||
expect(_.size(user.items.gear.owned)).to.equal(2);
|
expect(_.size(user.items.gear.owned)).to.equal(2);
|
||||||
|
|
||||||
@@ -140,7 +139,7 @@ describe('shared.ops.buyArmoire', () => {
|
|||||||
expect(user.stats.gp).to.equal(100);
|
expect(user.stats.gp).to.equal(100);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('gives more equipment', () => {
|
it('gives more equipment', async () => {
|
||||||
randomValFns.trueRandom.returns(YIELD_EQUIPMENT);
|
randomValFns.trueRandom.returns(YIELD_EQUIPMENT);
|
||||||
user.items.gear.owned = {
|
user.items.gear.owned = {
|
||||||
weapon_warrior_0: true,
|
weapon_warrior_0: true,
|
||||||
@@ -150,7 +149,7 @@ describe('shared.ops.buyArmoire', () => {
|
|||||||
|
|
||||||
expect(_.size(user.items.gear.owned)).to.equal(2);
|
expect(_.size(user.items.gear.owned)).to.equal(2);
|
||||||
|
|
||||||
buyArmoire(user, {}, analytics);
|
await buyArmoire(user, {}, analytics);
|
||||||
|
|
||||||
expect(_.size(user.items.gear.owned)).to.equal(3);
|
expect(_.size(user.items.gear.owned)).to.equal(3);
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import i18n from '../../../../website/common/script/i18n';
|
|||||||
import { BuyGemOperation } from '../../../../website/common/script/ops/buy/buyGem';
|
import { BuyGemOperation } from '../../../../website/common/script/ops/buy/buyGem';
|
||||||
import planGemLimits from '../../../../website/common/script/libs/planGemLimits';
|
import planGemLimits from '../../../../website/common/script/libs/planGemLimits';
|
||||||
|
|
||||||
function buyGem (user, req, analytics) {
|
async function buyGem (user, req, analytics) {
|
||||||
const buyOp = new BuyGemOperation(user, req, analytics);
|
const buyOp = new BuyGemOperation(user, req, analytics);
|
||||||
|
|
||||||
return buyOp.purchase();
|
return buyOp.purchase();
|
||||||
@@ -44,8 +44,8 @@ describe('shared.ops.buyGem', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
context('Gems', () => {
|
context('Gems', () => {
|
||||||
it('purchases gems', () => {
|
it('purchases gems', async () => {
|
||||||
const [, message] = buyGem(user, { params: { type: 'gems', key: 'gem' } }, analytics);
|
const [, message] = await buyGem(user, { params: { type: 'gems', key: 'gem' } }, analytics);
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('plusGem', { count: 1 }));
|
expect(message).to.equal(i18n.t('plusGem', { count: 1 }));
|
||||||
expect(user.balance).to.equal(userGemAmount + 0.25);
|
expect(user.balance).to.equal(userGemAmount + 0.25);
|
||||||
@@ -54,8 +54,8 @@ describe('shared.ops.buyGem', () => {
|
|||||||
expect(analytics.track).to.be.calledOnce;
|
expect(analytics.track).to.be.calledOnce;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('purchases gems with a different language than the default', () => {
|
it('purchases gems with a different language than the default', async () => {
|
||||||
const [, message] = buyGem(user, { params: { type: 'gems', key: 'gem' }, language: 'de' });
|
const [, message] = await buyGem(user, { params: { type: 'gems', key: 'gem' }, language: 'de' });
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('plusGem', { count: 1 }, 'de'));
|
expect(message).to.equal(i18n.t('plusGem', { count: 1 }, 'de'));
|
||||||
expect(user.balance).to.equal(userGemAmount + 0.25);
|
expect(user.balance).to.equal(userGemAmount + 0.25);
|
||||||
@@ -63,8 +63,8 @@ describe('shared.ops.buyGem', () => {
|
|||||||
expect(user.stats.gp).to.equal(goldPoints - planGemLimits.convRate);
|
expect(user.stats.gp).to.equal(goldPoints - planGemLimits.convRate);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('makes bulk purchases of gems', () => {
|
it('makes bulk purchases of gems', async () => {
|
||||||
const [, message] = buyGem(user, {
|
const [, message] = await buyGem(user, {
|
||||||
params: { type: 'gems', key: 'gem' },
|
params: { type: 'gems', key: 'gem' },
|
||||||
quantity: 2,
|
quantity: 2,
|
||||||
});
|
});
|
||||||
@@ -76,63 +76,58 @@ describe('shared.ops.buyGem', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
context('Failure conditions', () => {
|
context('Failure conditions', () => {
|
||||||
it('returns an error when key is not provided', done => {
|
it('returns an error when key is not provided', async () => {
|
||||||
try {
|
try {
|
||||||
buyGem(user, { params: { type: 'gems' } });
|
await buyGem(user, { params: { type: 'gems' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('missingKeyParam'));
|
expect(err.message).to.equal(i18n.t('missingKeyParam'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prevents unsubscribed user from buying gems', done => {
|
it('prevents unsubscribed user from buying gems', async () => {
|
||||||
delete user.purchased.plan.customerId;
|
delete user.purchased.plan.customerId;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buyGem(user, { params: { type: 'gems', key: 'gem' } });
|
await buyGem(user, { params: { type: 'gems', key: 'gem' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('mustSubscribeToPurchaseGems'));
|
expect(err.message).to.equal(i18n.t('mustSubscribeToPurchaseGems'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prevents user with not enough gold from buying gems', done => {
|
it('prevents user with not enough gold from buying gems', async () => {
|
||||||
user.stats.gp = 15;
|
user.stats.gp = 15;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buyGem(user, { params: { type: 'gems', key: 'gem' } });
|
await buyGem(user, { params: { type: 'gems', key: 'gem' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prevents user that have reached the conversion cap from buying gems', done => {
|
it('prevents user that have reached the conversion cap from buying gems', async () => {
|
||||||
user.stats.gp = goldPoints;
|
user.stats.gp = goldPoints;
|
||||||
user.purchased.plan.gemsBought = gemsBought;
|
user.purchased.plan.gemsBought = gemsBought;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buyGem(user, { params: { type: 'gems', key: 'gem' } });
|
await buyGem(user, { params: { type: 'gems', key: 'gem' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('maxBuyGems', { convCap: planGemLimits.convCap }));
|
expect(err.message).to.equal(i18n.t('maxBuyGems', { convCap: planGemLimits.convCap }));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prevents user from buying an invalid quantity', done => {
|
it('prevents user from buying an invalid quantity', async () => {
|
||||||
user.stats.gp = goldPoints;
|
user.stats.gp = goldPoints;
|
||||||
user.purchased.plan.gemsBought = gemsBought;
|
user.purchased.plan.gemsBought = gemsBought;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buyGem(user, { params: { type: 'gems', key: 'gem' }, quantity: 'a' });
|
await buyGem(user, { params: { type: 'gems', key: 'gem' }, quantity: 'a' });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ describe('shared.ops.buyHealthPotion', () => {
|
|||||||
let user;
|
let user;
|
||||||
const analytics = { track () {} };
|
const analytics = { track () {} };
|
||||||
|
|
||||||
function buyHealthPotion (_user, _req, _analytics) {
|
async function buyHealthPotion (_user, _req, _analytics) {
|
||||||
const buyOp = new BuyHealthPotionOperation(_user, _req, _analytics);
|
const buyOp = new BuyHealthPotionOperation(_user, _req, _analytics);
|
||||||
|
|
||||||
return buyOp.purchase();
|
return buyOp.purchase();
|
||||||
@@ -40,83 +40,75 @@ describe('shared.ops.buyHealthPotion', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
context('Potion', () => {
|
context('Potion', () => {
|
||||||
it('recovers 15 hp', () => {
|
it('recovers 15 hp', async () => {
|
||||||
user.stats.hp = 30;
|
user.stats.hp = 30;
|
||||||
buyHealthPotion(user, {}, analytics);
|
await buyHealthPotion(user, {}, analytics);
|
||||||
expect(user.stats.hp).to.eql(45);
|
expect(user.stats.hp).to.eql(45);
|
||||||
expect(analytics.track).to.be.calledOnce;
|
expect(analytics.track).to.be.calledOnce;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not increase hp above 50', () => {
|
it('does not increase hp above 50', async () => {
|
||||||
user.stats.hp = 45;
|
user.stats.hp = 45;
|
||||||
buyHealthPotion(user);
|
await buyHealthPotion(user);
|
||||||
expect(user.stats.hp).to.eql(50);
|
expect(user.stats.hp).to.eql(50);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('deducts 25 gp', () => {
|
it('deducts 25 gp', async () => {
|
||||||
user.stats.hp = 45;
|
user.stats.hp = 45;
|
||||||
buyHealthPotion(user);
|
await buyHealthPotion(user);
|
||||||
|
|
||||||
expect(user.stats.gp).to.eql(175);
|
expect(user.stats.gp).to.eql(175);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not purchase if not enough gp', done => {
|
it('does not purchase if not enough gp', async () => {
|
||||||
user.stats.hp = 45;
|
user.stats.hp = 45;
|
||||||
user.stats.gp = 5;
|
user.stats.gp = 5;
|
||||||
try {
|
try {
|
||||||
buyHealthPotion(user);
|
await buyHealthPotion(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
||||||
expect(user.stats.hp).to.eql(45);
|
expect(user.stats.hp).to.eql(45);
|
||||||
expect(user.stats.gp).to.eql(5);
|
expect(user.stats.gp).to.eql(5);
|
||||||
|
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not purchase if hp is full', done => {
|
it('does not purchase if hp is full', async () => {
|
||||||
user.stats.hp = 50;
|
user.stats.hp = 50;
|
||||||
user.stats.gp = 40;
|
user.stats.gp = 40;
|
||||||
try {
|
try {
|
||||||
buyHealthPotion(user);
|
await buyHealthPotion(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('messageHealthAlreadyMax'));
|
expect(err.message).to.equal(i18n.t('messageHealthAlreadyMax'));
|
||||||
expect(user.stats.hp).to.eql(50);
|
expect(user.stats.hp).to.eql(50);
|
||||||
expect(user.stats.gp).to.eql(40);
|
expect(user.stats.gp).to.eql(40);
|
||||||
|
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not allow potion purchases when hp is zero', done => {
|
it('does not allow potion purchases when hp is zero', async () => {
|
||||||
user.stats.hp = 0;
|
user.stats.hp = 0;
|
||||||
user.stats.gp = 40;
|
user.stats.gp = 40;
|
||||||
try {
|
try {
|
||||||
buyHealthPotion(user);
|
await buyHealthPotion(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('messageHealthAlreadyMin'));
|
expect(err.message).to.equal(i18n.t('messageHealthAlreadyMin'));
|
||||||
expect(user.stats.hp).to.eql(0);
|
expect(user.stats.hp).to.eql(0);
|
||||||
expect(user.stats.gp).to.eql(40);
|
expect(user.stats.gp).to.eql(40);
|
||||||
|
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not allow potion purchases when hp is negative', done => {
|
it('does not allow potion purchases when hp is negative', async () => {
|
||||||
user.stats.hp = -8;
|
user.stats.hp = -8;
|
||||||
user.stats.gp = 40;
|
user.stats.gp = 40;
|
||||||
try {
|
try {
|
||||||
buyHealthPotion(user);
|
await buyHealthPotion(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('messageHealthAlreadyMin'));
|
expect(err.message).to.equal(i18n.t('messageHealthAlreadyMin'));
|
||||||
expect(user.stats.hp).to.eql(-8);
|
expect(user.stats.hp).to.eql(-8);
|
||||||
expect(user.stats.gp).to.eql(40);
|
expect(user.stats.gp).to.eql(40);
|
||||||
|
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
import i18n from '../../../../website/common/script/i18n';
|
import i18n from '../../../../website/common/script/i18n';
|
||||||
import errorMessage from '../../../../website/common/script/libs/errorMessage';
|
import errorMessage from '../../../../website/common/script/libs/errorMessage';
|
||||||
|
|
||||||
function buyGear (user, req, analytics) {
|
async function buyGear (user, req, analytics) {
|
||||||
const buyOp = new BuyMarketGearOperation(user, req, analytics);
|
const buyOp = new BuyMarketGearOperation(user, req, analytics);
|
||||||
|
|
||||||
return buyOp.purchase();
|
return buyOp.purchase();
|
||||||
@@ -57,10 +57,10 @@ describe('shared.ops.buyMarketGear', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
context('Gear', () => {
|
context('Gear', () => {
|
||||||
it('adds equipment to inventory', () => {
|
it('adds equipment to inventory', async () => {
|
||||||
user.stats.gp = 31;
|
user.stats.gp = 31;
|
||||||
|
|
||||||
buyGear(user, { params: { key: 'armor_warrior_1' } }, analytics);
|
await buyGear(user, { params: { key: 'armor_warrior_1' } }, analytics);
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.eql({
|
expect(user.items.gear.owned).to.eql({
|
||||||
weapon_warrior_0: true,
|
weapon_warrior_0: true,
|
||||||
@@ -90,10 +90,10 @@ describe('shared.ops.buyMarketGear', () => {
|
|||||||
expect(analytics.track).to.be.calledOnce;
|
expect(analytics.track).to.be.calledOnce;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds the onboarding achievement to the user and checks the onboarding status', () => {
|
it('adds the onboarding achievement to the user and checks the onboarding status', async () => {
|
||||||
user.stats.gp = 31;
|
user.stats.gp = 31;
|
||||||
|
|
||||||
buyGear(user, { params: { key: 'armor_warrior_1' } }, analytics);
|
await buyGear(user, { params: { key: 'armor_warrior_1' } }, analytics);
|
||||||
|
|
||||||
expect(user.addAchievement).to.be.calledOnce;
|
expect(user.addAchievement).to.be.calledOnce;
|
||||||
expect(user.addAchievement).to.be.calledWith('purchasedEquipment');
|
expect(user.addAchievement).to.be.calledWith('purchasedEquipment');
|
||||||
@@ -102,36 +102,36 @@ describe('shared.ops.buyMarketGear', () => {
|
|||||||
expect(shared.onboarding.checkOnboardingStatus).to.be.calledWith(user);
|
expect(shared.onboarding.checkOnboardingStatus).to.be.calledWith(user);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not add the onboarding achievement to the user if it\'s already been awarded', () => {
|
it('does not add the onboarding achievement to the user if it\'s already been awarded', async () => {
|
||||||
user.stats.gp = 31;
|
user.stats.gp = 31;
|
||||||
user.achievements.purchasedEquipment = true;
|
user.achievements.purchasedEquipment = true;
|
||||||
|
|
||||||
buyGear(user, { params: { key: 'armor_warrior_1' } }, analytics);
|
await buyGear(user, { params: { key: 'armor_warrior_1' } }, analytics);
|
||||||
|
|
||||||
expect(user.addAchievement).to.not.be.called;
|
expect(user.addAchievement).to.not.be.called;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('deducts gold from user', () => {
|
it('deducts gold from user', async () => {
|
||||||
user.stats.gp = 31;
|
user.stats.gp = 31;
|
||||||
|
|
||||||
buyGear(user, { params: { key: 'armor_warrior_1' } });
|
await buyGear(user, { params: { key: 'armor_warrior_1' } });
|
||||||
|
|
||||||
expect(user.stats.gp).to.eql(1);
|
expect(user.stats.gp).to.eql(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('auto equips equipment if user has auto-equip preference turned on', () => {
|
it('auto equips equipment if user has auto-equip preference turned on', async () => {
|
||||||
user.stats.gp = 31;
|
user.stats.gp = 31;
|
||||||
user.preferences.autoEquip = true;
|
user.preferences.autoEquip = true;
|
||||||
|
|
||||||
buyGear(user, { params: { key: 'armor_warrior_1' } });
|
await buyGear(user, { params: { key: 'armor_warrior_1' } });
|
||||||
|
|
||||||
expect(user.items.gear.equipped).to.have.property('armor', 'armor_warrior_1');
|
expect(user.items.gear.equipped).to.have.property('armor', 'armor_warrior_1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('updates the pinnedItems to the next item in the set if one exists', () => {
|
it('updates the pinnedItems to the next item in the set if one exists', async () => {
|
||||||
user.stats.gp = 31;
|
user.stats.gp = 31;
|
||||||
|
|
||||||
buyGear(user, { params: { key: 'armor_warrior_1' } });
|
await buyGear(user, { params: { key: 'armor_warrior_1' } });
|
||||||
|
|
||||||
expect(user.pinnedItems).to.deep.include({
|
expect(user.pinnedItems).to.deep.include({
|
||||||
type: 'marketGear',
|
type: 'marketGear',
|
||||||
@@ -139,155 +139,147 @@ describe('shared.ops.buyMarketGear', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('buyGears equipment but does not auto-equip', () => {
|
it('buyGears equipment but does not auto-equip', async () => {
|
||||||
user.stats.gp = 31;
|
user.stats.gp = 31;
|
||||||
user.preferences.autoEquip = false;
|
user.preferences.autoEquip = false;
|
||||||
|
|
||||||
buyGear(user, { params: { key: 'armor_warrior_1' } });
|
await buyGear(user, { params: { key: 'armor_warrior_1' } });
|
||||||
|
|
||||||
expect(user.items.gear.equipped.property).to.not.equal('armor_warrior_1');
|
expect(user.items.gear.equipped.property).to.not.equal('armor_warrior_1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not buyGear equipment twice', done => {
|
it('does not buyGear equipment twice', async () => {
|
||||||
user.stats.gp = 62;
|
user.stats.gp = 62;
|
||||||
buyGear(user, { params: { key: 'armor_warrior_1' } });
|
await buyGear(user, { params: { key: 'armor_warrior_1' } });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buyGear(user, { params: { key: 'armor_warrior_1' } });
|
await buyGear(user, { params: { key: 'armor_warrior_1' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('equipmentAlreadyOwned'));
|
expect(err.message).to.equal(i18n.t('equipmentAlreadyOwned'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not buy equipment of different class', done => {
|
it('does not buy equipment of different class', async () => {
|
||||||
user.stats.gp = 82;
|
user.stats.gp = 82;
|
||||||
user.stats.class = 'warrior';
|
user.stats.class = 'warrior';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buyGear(user, { params: { key: 'weapon_special_winter2018Rogue' } });
|
await buyGear(user, { params: { key: 'weapon_special_winter2018Rogue' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('cannotBuyItem'));
|
expect(err.message).to.equal(i18n.t('cannotBuyItem'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not buy equipment in bulk', done => {
|
it('does not buy equipment in bulk', async () => {
|
||||||
user.stats.gp = 82;
|
user.stats.gp = 82;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buyGear(user, { params: { key: 'armor_warrior_1' }, quantity: 3 });
|
await buyGear(user, { params: { key: 'armor_warrior_1' }, quantity: 3 });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('messageNotAbleToBuyInBulk'));
|
expect(err.message).to.equal(i18n.t('messageNotAbleToBuyInBulk'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO after user.ops.equip is done
|
// TODO after user.ops.equip is done
|
||||||
xit('removes one-handed weapon and shield if auto-equip is on and a two-hander is bought', () => {
|
xit('removes one-handed weapon and shield if auto-equip is on and a two-hander is bought', async () => {
|
||||||
user.stats.gp = 100;
|
user.stats.gp = 100;
|
||||||
user.preferences.autoEquip = true;
|
user.preferences.autoEquip = true;
|
||||||
buyGear(user, { params: { key: 'shield_warrior_1' } });
|
await buyGear(user, { params: { key: 'shield_warrior_1' } });
|
||||||
user.ops.equip({ params: { key: 'shield_warrior_1' } });
|
user.ops.equip({ params: { key: 'shield_warrior_1' } });
|
||||||
buyGear(user, { params: { key: 'weapon_warrior_1' } });
|
await buyGear(user, { params: { key: 'weapon_warrior_1' } });
|
||||||
user.ops.equip({ params: { key: 'weapon_warrior_1' } });
|
user.ops.equip({ params: { key: 'weapon_warrior_1' } });
|
||||||
|
|
||||||
buyGear(user, { params: { key: 'weapon_wizard_1' } });
|
await buyGear(user, { params: { key: 'weapon_wizard_1' } });
|
||||||
|
|
||||||
expect(user.items.gear.equipped).to.have.property('shield', 'shield_base_0');
|
expect(user.items.gear.equipped).to.have.property('shield', 'shield_base_0');
|
||||||
expect(user.items.gear.equipped).to.have.property('weapon', 'weapon_wizard_1');
|
expect(user.items.gear.equipped).to.have.property('weapon', 'weapon_wizard_1');
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO after user.ops.equip is done
|
// TODO after user.ops.equip is done
|
||||||
xit('buyGears two-handed equipment but does not automatically remove sword or shield', () => {
|
xit('buyGears two-handed equipment but does not automatically remove sword or shield', async () => {
|
||||||
user.stats.gp = 100;
|
user.stats.gp = 100;
|
||||||
user.preferences.autoEquip = false;
|
user.preferences.autoEquip = false;
|
||||||
buyGear(user, { params: { key: 'shield_warrior_1' } });
|
await buyGear(user, { params: { key: 'shield_warrior_1' } });
|
||||||
user.ops.equip({ params: { key: 'shield_warrior_1' } });
|
user.ops.equip({ params: { key: 'shield_warrior_1' } });
|
||||||
buyGear(user, { params: { key: 'weapon_warrior_1' } });
|
await buyGear(user, { params: { key: 'weapon_warrior_1' } });
|
||||||
user.ops.equip({ params: { key: 'weapon_warrior_1' } });
|
user.ops.equip({ params: { key: 'weapon_warrior_1' } });
|
||||||
|
|
||||||
buyGear(user, { params: { key: 'weapon_wizard_1' } });
|
await buyGear(user, { params: { key: 'weapon_wizard_1' } });
|
||||||
|
|
||||||
expect(user.items.gear.equipped).to.have.property('shield', 'shield_warrior_1');
|
expect(user.items.gear.equipped).to.have.property('shield', 'shield_warrior_1');
|
||||||
expect(user.items.gear.equipped).to.have.property('weapon', 'weapon_warrior_1');
|
expect(user.items.gear.equipped).to.have.property('weapon', 'weapon_warrior_1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not buyGear equipment without enough Gold', done => {
|
it('does not buyGear equipment without enough Gold', async () => {
|
||||||
user.stats.gp = 20;
|
user.stats.gp = 20;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buyGear(user, { params: { key: 'armor_warrior_1' } });
|
await buyGear(user, { params: { key: 'armor_warrior_1' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
||||||
expect(user.items.gear.owned).to.not.have.property('armor_warrior_1');
|
expect(user.items.gear.owned).to.not.have.property('armor_warrior_1');
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when key is not provided', done => {
|
it('returns error when key is not provided', async () => {
|
||||||
try {
|
try {
|
||||||
buyGear(user);
|
await buyGear(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(errorMessage('missingKeyParam'));
|
expect(err.message).to.equal(errorMessage('missingKeyParam'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when item is not found', done => {
|
it('returns error when item is not found', async () => {
|
||||||
const params = { key: 'armor_warrior_notExisting' };
|
const params = { key: 'armor_warrior_notExisting' };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buyGear(user, { params });
|
await buyGear(user, { params });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotFound);
|
expect(err).to.be.an.instanceof(NotFound);
|
||||||
expect(err.message).to.equal(errorMessage('itemNotFound', params));
|
expect(err.message).to.equal(errorMessage('itemNotFound', params));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not buyGear equipment without the previous equipment', done => {
|
it('does not buyGear equipment without the previous equipment', async () => {
|
||||||
try {
|
try {
|
||||||
buyGear(user, { params: { key: 'armor_warrior_2' } });
|
await buyGear(user, { params: { key: 'armor_warrior_2' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('previousGearNotOwned'));
|
expect(err.message).to.equal(i18n.t('previousGearNotOwned'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not buyGear equipment if user does not own prior item in sequence', done => {
|
it('does not buyGear equipment if user does not own prior item in sequence', async () => {
|
||||||
user.stats.gp = 200;
|
user.stats.gp = 200;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buyGear(user, { params: { key: 'armor_warrior_2' } });
|
await buyGear(user, { params: { key: 'armor_warrior_2' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('previousGearNotOwned'));
|
expect(err.message).to.equal(i18n.t('previousGearNotOwned'));
|
||||||
expect(user.items.gear.owned).to.not.have.property('armor_warrior_2');
|
expect(user.items.gear.owned).to.not.have.property('armor_warrior_2');
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does buyGear equipment if item is a numbered special item user qualifies for', () => {
|
it('does buyGear equipment if item is a numbered special item user qualifies for', async () => {
|
||||||
user.stats.gp = 200;
|
user.stats.gp = 200;
|
||||||
user.items.gear.owned.head_special_2 = false;
|
user.items.gear.owned.head_special_2 = false;
|
||||||
|
|
||||||
buyGear(user, { params: { key: 'head_special_2' } });
|
await buyGear(user, { params: { key: 'head_special_2' } });
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.have.property('head_special_2', true);
|
expect(user.items.gear.owned).to.have.property('head_special_2', true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does buyGear equipment if it is an armoire item that an user previously lost', () => {
|
it('does buyGear equipment if it is an armoire item that an user previously lost', async () => {
|
||||||
user.stats.gp = 200;
|
user.stats.gp = 200;
|
||||||
user.items.gear.owned.shield_armoire_ramHornShield = false;
|
user.items.gear.owned.shield_armoire_ramHornShield = false;
|
||||||
|
|
||||||
buyGear(user, { params: { key: 'shield_armoire_ramHornShield' } });
|
await buyGear(user, { params: { key: 'shield_armoire_ramHornShield' } });
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.have.property('shield_armoire_ramHornShield', true);
|
expect(user.items.gear.owned).to.have.property('shield_armoire_ramHornShield', true);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -35,18 +35,17 @@ describe('shared.ops.buyMysterySet', () => {
|
|||||||
|
|
||||||
context('Mystery Sets', () => {
|
context('Mystery Sets', () => {
|
||||||
context('failure conditions', () => {
|
context('failure conditions', () => {
|
||||||
it('does not grant mystery sets without Mystic Hourglasses', done => {
|
it('does not grant mystery sets without Mystic Hourglasses', async () => {
|
||||||
try {
|
try {
|
||||||
buyMysterySet(user, { params: { key: '201501' } });
|
await buyMysterySet(user, { params: { key: '201501' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.eql(i18n.t('notEnoughHourglasses'));
|
expect(err.message).to.eql(i18n.t('notEnoughHourglasses'));
|
||||||
expect(user.items.gear.owned).to.have.property('weapon_warrior_0', true);
|
expect(user.items.gear.owned).to.have.property('weapon_warrior_0', true);
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not grant mystery set that has already been purchased', done => {
|
it('does not grant mystery set that has already been purchased', async () => {
|
||||||
user.purchased.plan.consecutive.trinkets = 1;
|
user.purchased.plan.consecutive.trinkets = 1;
|
||||||
user.items.gear.owned = {
|
user.items.gear.owned = {
|
||||||
weapon_warrior_0: true,
|
weapon_warrior_0: true,
|
||||||
@@ -57,30 +56,28 @@ describe('shared.ops.buyMysterySet', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buyMysterySet(user, { params: { key: '301404' } });
|
await buyMysterySet(user, { params: { key: '301404' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotFound);
|
expect(err).to.be.an.instanceof(NotFound);
|
||||||
expect(err.message).to.eql(i18n.t('mysterySetNotFound'));
|
expect(err.message).to.eql(i18n.t('mysterySetNotFound'));
|
||||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when key is not provided', done => {
|
it('returns error when key is not provided', async () => {
|
||||||
try {
|
try {
|
||||||
buyMysterySet(user);
|
await buyMysterySet(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(errorMessage('missingKeyParam'));
|
expect(err.message).to.equal(errorMessage('missingKeyParam'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('successful purchases', () => {
|
context('successful purchases', () => {
|
||||||
it('buys Steampunk Accessories Set', () => {
|
it('buys Steampunk Accessories Set', async () => {
|
||||||
user.purchased.plan.consecutive.trinkets = 1;
|
user.purchased.plan.consecutive.trinkets = 1;
|
||||||
buyMysterySet(user, { params: { key: '301404' } }, analytics);
|
await buyMysterySet(user, { params: { key: '301404' } }, analytics);
|
||||||
|
|
||||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
|
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
|
||||||
expect(user.items.gear.owned).to.have.property('weapon_warrior_0', true);
|
expect(user.items.gear.owned).to.have.property('weapon_warrior_0', true);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ describe('shared.ops.buyQuestGems', () => {
|
|||||||
const goldPoints = 40;
|
const goldPoints = 40;
|
||||||
const analytics = { track () {} };
|
const analytics = { track () {} };
|
||||||
|
|
||||||
function buyQuest (_user, _req, _analytics) {
|
async function buyQuest (_user, _req, _analytics) {
|
||||||
const buyOp = new BuyQuestWithGemOperation(_user, _req, _analytics);
|
const buyOp = new BuyQuestWithGemOperation(_user, _req, _analytics);
|
||||||
|
|
||||||
return buyOp.purchase();
|
return buyOp.purchase();
|
||||||
@@ -44,19 +44,19 @@ describe('shared.ops.buyQuestGems', () => {
|
|||||||
user.pinnedItems.push({ type: 'quests', key: 'gryphon' });
|
user.pinnedItems.push({ type: 'quests', key: 'gryphon' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('purchases quests', () => {
|
it('purchases quests', async () => {
|
||||||
const key = 'gryphon';
|
const key = 'gryphon';
|
||||||
|
|
||||||
buyQuest(user, { params: { key } });
|
await buyQuest(user, { params: { key } });
|
||||||
|
|
||||||
expect(user.items.quests[key]).to.equal(1);
|
expect(user.items.quests[key]).to.equal(1);
|
||||||
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
|
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
|
||||||
});
|
});
|
||||||
it('if a user\'s count of a quest scroll is negative, it will be reset to 0 before incrementing when they buy a new one.', () => {
|
it('if a user\'s count of a quest scroll is negative, it will be reset to 0 before incrementing when they buy a new one.', async () => {
|
||||||
const key = 'dustbunnies';
|
const key = 'dustbunnies';
|
||||||
user.items.quests[key] = -1;
|
user.items.quests[key] = -1;
|
||||||
|
|
||||||
buyQuest(user, { params: { key } });
|
await buyQuest(user, { params: { key } });
|
||||||
|
|
||||||
expect(user.items.quests[key]).to.equal(1);
|
expect(user.items.quests[key]).to.equal(1);
|
||||||
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
|
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
|
||||||
@@ -73,26 +73,25 @@ describe('shared.ops.buyQuestGems', () => {
|
|||||||
user.purchased.plan.customerId = 'customer-id';
|
user.purchased.plan.customerId = 'customer-id';
|
||||||
});
|
});
|
||||||
|
|
||||||
it('errors when user does not have enough gems', done => {
|
it('errors when user does not have enough gems', async () => {
|
||||||
user.balance = 1;
|
user.balance = 1;
|
||||||
const key = 'gryphon';
|
const key = 'gryphon';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buyQuest(user, {
|
await buyQuest(user, {
|
||||||
params: { key },
|
params: { key },
|
||||||
quantity: 2,
|
quantity: 2,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('makes bulk purchases of quests', () => {
|
it('makes bulk purchases of quests', async () => {
|
||||||
const key = 'gryphon';
|
const key = 'gryphon';
|
||||||
|
|
||||||
buyQuest(user, {
|
await buyQuest(user, {
|
||||||
params: { key },
|
params: { key },
|
||||||
quantity: 3,
|
quantity: 3,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ describe('shared.ops.buyQuest', () => {
|
|||||||
let user;
|
let user;
|
||||||
const analytics = { track () {} };
|
const analytics = { track () {} };
|
||||||
|
|
||||||
function buyQuest (_user, _req, _analytics) {
|
async function buyQuest (_user, _req, _analytics) {
|
||||||
const buyOp = new BuyQuestWithGoldOperation(_user, _req, _analytics);
|
const buyOp = new BuyQuestWithGoldOperation(_user, _req, _analytics);
|
||||||
|
|
||||||
return buyOp.purchase();
|
return buyOp.purchase();
|
||||||
@@ -29,9 +29,9 @@ describe('shared.ops.buyQuest', () => {
|
|||||||
analytics.track.restore();
|
analytics.track.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('buys a Quest scroll', () => {
|
it('buys a Quest scroll', async () => {
|
||||||
user.stats.gp = 205;
|
user.stats.gp = 205;
|
||||||
buyQuest(user, {
|
await buyQuest(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'dilatoryDistress1',
|
key: 'dilatoryDistress1',
|
||||||
},
|
},
|
||||||
@@ -43,11 +43,11 @@ describe('shared.ops.buyQuest', () => {
|
|||||||
expect(analytics.track).to.be.calledOnce;
|
expect(analytics.track).to.be.calledOnce;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('if a user\'s count of a quest scroll is negative, it will be reset to 0 before incrementing when they buy a new one.', () => {
|
it('if a user\'s count of a quest scroll is negative, it will be reset to 0 before incrementing when they buy a new one.', async () => {
|
||||||
user.stats.gp = 205;
|
user.stats.gp = 205;
|
||||||
const key = 'dilatoryDistress1';
|
const key = 'dilatoryDistress1';
|
||||||
user.items.quests[key] = -1;
|
user.items.quests[key] = -1;
|
||||||
buyQuest(user, {
|
await buyQuest(user, {
|
||||||
params: { key },
|
params: { key },
|
||||||
}, analytics);
|
}, analytics);
|
||||||
expect(user.items.quests[key]).to.equal(1);
|
expect(user.items.quests[key]).to.equal(1);
|
||||||
@@ -55,14 +55,14 @@ describe('shared.ops.buyQuest', () => {
|
|||||||
expect(analytics.track).to.be.calledOnce;
|
expect(analytics.track).to.be.calledOnce;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('buys a Quest scroll with the right quantity if a string is passed for quantity', () => {
|
it('buys a Quest scroll with the right quantity if a string is passed for quantity', async () => {
|
||||||
user.stats.gp = 1000;
|
user.stats.gp = 1000;
|
||||||
buyQuest(user, {
|
await buyQuest(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'dilatoryDistress1',
|
key: 'dilatoryDistress1',
|
||||||
},
|
},
|
||||||
}, analytics);
|
}, analytics);
|
||||||
buyQuest(user, {
|
await buyQuest(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'dilatoryDistress1',
|
key: 'dilatoryDistress1',
|
||||||
},
|
},
|
||||||
@@ -74,10 +74,10 @@ describe('shared.ops.buyQuest', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not buy a Quest scroll when an invalid quantity is passed', done => {
|
it('does not buy a Quest scroll when an invalid quantity is passed', async () => {
|
||||||
user.stats.gp = 1000;
|
user.stats.gp = 1000;
|
||||||
try {
|
try {
|
||||||
buyQuest(user, {
|
await buyQuest(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'dilatoryDistress1',
|
key: 'dilatoryDistress1',
|
||||||
},
|
},
|
||||||
@@ -88,14 +88,13 @@ describe('shared.ops.buyQuest', () => {
|
|||||||
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
||||||
expect(user.items.quests).to.eql({});
|
expect(user.items.quests).to.eql({});
|
||||||
expect(user.stats.gp).to.equal(1000);
|
expect(user.stats.gp).to.equal(1000);
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not buy Quests without enough Gold', done => {
|
it('does not buy Quests without enough Gold', async () => {
|
||||||
user.stats.gp = 1;
|
user.stats.gp = 1;
|
||||||
try {
|
try {
|
||||||
buyQuest(user, {
|
await buyQuest(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'dilatoryDistress1',
|
key: 'dilatoryDistress1',
|
||||||
},
|
},
|
||||||
@@ -105,14 +104,13 @@ describe('shared.ops.buyQuest', () => {
|
|||||||
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
||||||
expect(user.items.quests).to.eql({});
|
expect(user.items.quests).to.eql({});
|
||||||
expect(user.stats.gp).to.equal(1);
|
expect(user.stats.gp).to.equal(1);
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not buy nonexistent Quests', done => {
|
it('does not buy nonexistent Quests', async () => {
|
||||||
user.stats.gp = 9999;
|
user.stats.gp = 9999;
|
||||||
try {
|
try {
|
||||||
buyQuest(user, {
|
await buyQuest(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'snarfblatter',
|
key: 'snarfblatter',
|
||||||
},
|
},
|
||||||
@@ -122,13 +120,12 @@ describe('shared.ops.buyQuest', () => {
|
|||||||
expect(err.message).to.equal(errorMessage('questNotFound', { key: 'snarfblatter' }));
|
expect(err.message).to.equal(errorMessage('questNotFound', { key: 'snarfblatter' }));
|
||||||
expect(user.items.quests).to.eql({});
|
expect(user.items.quests).to.eql({});
|
||||||
expect(user.stats.gp).to.equal(9999);
|
expect(user.stats.gp).to.equal(9999);
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not buy the Mystery of the Masterclassers', done => {
|
it('does not buy the Mystery of the Masterclassers', async () => {
|
||||||
try {
|
try {
|
||||||
buyQuest(user, {
|
await buyQuest(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'lostMasterclasser1',
|
key: 'lostMasterclasser1',
|
||||||
},
|
},
|
||||||
@@ -137,14 +134,13 @@ describe('shared.ops.buyQuest', () => {
|
|||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('questUnlockLostMasterclasser'));
|
expect(err.message).to.equal(i18n.t('questUnlockLostMasterclasser'));
|
||||||
expect(user.items.quests).to.eql({});
|
expect(user.items.quests).to.eql({});
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not buy Gem-premium Quests', done => {
|
it('does not buy Gem-premium Quests', async () => {
|
||||||
user.stats.gp = 9999;
|
user.stats.gp = 9999;
|
||||||
try {
|
try {
|
||||||
buyQuest(user, {
|
await buyQuest(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'kraken',
|
key: 'kraken',
|
||||||
},
|
},
|
||||||
@@ -154,23 +150,21 @@ describe('shared.ops.buyQuest', () => {
|
|||||||
expect(err.message).to.equal(i18n.t('questNotGoldPurchasable', { key: 'kraken' }));
|
expect(err.message).to.equal(i18n.t('questNotGoldPurchasable', { key: 'kraken' }));
|
||||||
expect(user.items.quests).to.eql({});
|
expect(user.items.quests).to.eql({});
|
||||||
expect(user.stats.gp).to.equal(9999);
|
expect(user.stats.gp).to.equal(9999);
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when key is not provided', done => {
|
it('returns error when key is not provided', async () => {
|
||||||
try {
|
try {
|
||||||
buyQuest(user);
|
await buyQuest(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(errorMessage('missingKeyParam'));
|
expect(err.message).to.equal(errorMessage('missingKeyParam'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not buy a quest without completing previous quests', done => {
|
it('does not buy a quest without completing previous quests', async () => {
|
||||||
try {
|
try {
|
||||||
buyQuest(user, {
|
await buyQuest(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'dilatoryDistress3',
|
key: 'dilatoryDistress3',
|
||||||
},
|
},
|
||||||
@@ -179,7 +173,6 @@ describe('shared.ops.buyQuest', () => {
|
|||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('mustComplete', { quest: 'dilatoryDistress2' }));
|
expect(err.message).to.equal(i18n.t('mustComplete', { quest: 'dilatoryDistress2' }));
|
||||||
expect(user.items.quests).to.eql({});
|
expect(user.items.quests).to.eql({});
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ describe('shared.ops.buySpecialSpell', () => {
|
|||||||
let user;
|
let user;
|
||||||
const analytics = { track () {} };
|
const analytics = { track () {} };
|
||||||
|
|
||||||
function buySpecialSpell (_user, _req, _analytics) {
|
async function buySpecialSpell (_user, _req, _analytics) {
|
||||||
const buyOp = new BuySpellOperation(_user, _req, _analytics);
|
const buyOp = new BuySpellOperation(_user, _req, _analytics);
|
||||||
|
|
||||||
return buyOp.purchase();
|
return buyOp.purchase();
|
||||||
@@ -29,19 +29,18 @@ describe('shared.ops.buySpecialSpell', () => {
|
|||||||
analytics.track.restore();
|
analytics.track.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if params.key is missing', done => {
|
it('throws an error if params.key is missing', async () => {
|
||||||
try {
|
try {
|
||||||
buySpecialSpell(user);
|
await buySpecialSpell(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(errorMessage('missingKeyParam'));
|
expect(err.message).to.equal(errorMessage('missingKeyParam'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if the spell doesn\'t exists', done => {
|
it('throws an error if the spell doesn\'t exists', async () => {
|
||||||
try {
|
try {
|
||||||
buySpecialSpell(user, {
|
await buySpecialSpell(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'notExisting',
|
key: 'notExisting',
|
||||||
},
|
},
|
||||||
@@ -49,14 +48,13 @@ describe('shared.ops.buySpecialSpell', () => {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotFound);
|
expect(err).to.be.an.instanceof(NotFound);
|
||||||
expect(err.message).to.equal(errorMessage('spellNotFound', { spellId: 'notExisting' }));
|
expect(err.message).to.equal(errorMessage('spellNotFound', { spellId: 'notExisting' }));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if the user doesn\'t have enough gold', done => {
|
it('throws an error if the user doesn\'t have enough gold', async () => {
|
||||||
user.stats.gp = 1;
|
user.stats.gp = 1;
|
||||||
try {
|
try {
|
||||||
buySpecialSpell(user, {
|
await buySpecialSpell(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'thankyou',
|
key: 'thankyou',
|
||||||
},
|
},
|
||||||
@@ -64,15 +62,14 @@ describe('shared.ops.buySpecialSpell', () => {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
expect(err.message).to.equal(i18n.t('messageNotEnoughGold'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('buys an item', () => {
|
it('buys an item', async () => {
|
||||||
user.stats.gp = 11;
|
user.stats.gp = 11;
|
||||||
const item = content.special.thankyou;
|
const item = content.special.thankyou;
|
||||||
|
|
||||||
const [data, message] = buySpecialSpell(user, {
|
const [data, message] = await buySpecialSpell(user, {
|
||||||
params: {
|
params: {
|
||||||
key: 'thankyou',
|
key: 'thankyou',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ describe('common.ops.hourglassPurchase', () => {
|
|||||||
let user;
|
let user;
|
||||||
const analytics = { track () {} };
|
const analytics = { track () {} };
|
||||||
|
|
||||||
function buyMount (_user, _req, _analytics) {
|
async function buyMount (_user, _req, _analytics) {
|
||||||
const buyOp = new BuyHourglassMountOperation(_user, _req, _analytics);
|
const buyOp = new BuyHourglassMountOperation(_user, _req, _analytics);
|
||||||
|
|
||||||
return buyOp.purchase();
|
return buyOp.purchase();
|
||||||
@@ -31,116 +31,107 @@ describe('common.ops.hourglassPurchase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
context('failure conditions', () => {
|
context('failure conditions', () => {
|
||||||
it('return error when key is not provided', done => {
|
it('return error when key is not provided', async () => {
|
||||||
try {
|
try {
|
||||||
hourglassPurchase(user, { params: {} });
|
await hourglassPurchase(user, { params: {} });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.eql(errorMessage('missingKeyParam'));
|
expect(err.message).to.eql(errorMessage('missingKeyParam'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when type is not provided', done => {
|
it('returns error when type is not provided', async () => {
|
||||||
try {
|
try {
|
||||||
hourglassPurchase(user, { params: { key: 'Base' } });
|
await hourglassPurchase(user, { params: { key: 'Base' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.eql(errorMessage('missingTypeParam'));
|
expect(err.message).to.eql(errorMessage('missingTypeParam'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when inccorect type is provided', done => {
|
it('returns error when inccorect type is provided', async () => {
|
||||||
try {
|
try {
|
||||||
hourglassPurchase(user, { params: { type: 'notAType', key: 'MantisShrimp-Base' } });
|
await hourglassPurchase(user, { params: { type: 'notAType', key: 'MantisShrimp-Base' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.eql(i18n.t('typeNotAllowedHourglass', { allowedTypes: _.keys(content.timeTravelStable).toString() }));
|
expect(err.message).to.eql(i18n.t('typeNotAllowedHourglass', { allowedTypes: _.keys(content.timeTravelStable).toString() }));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not grant to pets without Mystic Hourglasses', done => {
|
it('does not grant to pets without Mystic Hourglasses', async () => {
|
||||||
try {
|
try {
|
||||||
hourglassPurchase(user, { params: { type: 'pets', key: 'MantisShrimp-Base' } });
|
await hourglassPurchase(user, { params: { type: 'pets', key: 'MantisShrimp-Base' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.eql(i18n.t('notEnoughHourglasses'));
|
expect(err.message).to.eql(i18n.t('notEnoughHourglasses'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not grant to mounts without Mystic Hourglasses', done => {
|
it('does not grant to mounts without Mystic Hourglasses', async () => {
|
||||||
try {
|
try {
|
||||||
buyMount(user, { params: { key: 'MantisShrimp-Base' } });
|
await buyMount(user, { params: { key: 'MantisShrimp-Base' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.eql(i18n.t('notEnoughHourglasses'));
|
expect(err.message).to.eql(i18n.t('notEnoughHourglasses'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not grant pet that is not part of the Time Travel Stable', done => {
|
it('does not grant pet that is not part of the Time Travel Stable', async () => {
|
||||||
user.purchased.plan.consecutive.trinkets = 1;
|
user.purchased.plan.consecutive.trinkets = 1;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
hourglassPurchase(user, { params: { type: 'pets', key: 'Wolf-Veteran' } });
|
await hourglassPurchase(user, { params: { type: 'pets', key: 'Wolf-Veteran' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.eql(i18n.t('notAllowedHourglass'));
|
expect(err.message).to.eql(i18n.t('notAllowedHourglass'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not grant mount that is not part of the Time Travel Stable', done => {
|
it('does not grant mount that is not part of the Time Travel Stable', async () => {
|
||||||
user.purchased.plan.consecutive.trinkets = 1;
|
user.purchased.plan.consecutive.trinkets = 1;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buyMount(user, { params: { key: 'Orca-Base' } });
|
await buyMount(user, { params: { key: 'Orca-Base' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.eql(i18n.t('notAllowedHourglass'));
|
expect(err.message).to.eql(i18n.t('notAllowedHourglass'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not grant pet that has already been purchased', done => {
|
it('does not grant pet that has already been purchased', async () => {
|
||||||
user.purchased.plan.consecutive.trinkets = 1;
|
user.purchased.plan.consecutive.trinkets = 1;
|
||||||
user.items.pets = {
|
user.items.pets = {
|
||||||
'MantisShrimp-Base': true,
|
'MantisShrimp-Base': true,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
hourglassPurchase(user, { params: { type: 'pets', key: 'MantisShrimp-Base' } });
|
await hourglassPurchase(user, { params: { type: 'pets', key: 'MantisShrimp-Base' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.eql(i18n.t('petsAlreadyOwned'));
|
expect(err.message).to.eql(i18n.t('petsAlreadyOwned'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not grant mount that has already been purchased', done => {
|
it('does not grant mount that has already been purchased', async () => {
|
||||||
user.purchased.plan.consecutive.trinkets = 1;
|
user.purchased.plan.consecutive.trinkets = 1;
|
||||||
user.items.mounts = {
|
user.items.mounts = {
|
||||||
'MantisShrimp-Base': true,
|
'MantisShrimp-Base': true,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
buyMount(user, { params: { key: 'MantisShrimp-Base' } });
|
await buyMount(user, { params: { key: 'MantisShrimp-Base' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.eql(i18n.t('mountsAlreadyOwned'));
|
expect(err.message).to.eql(i18n.t('mountsAlreadyOwned'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('successful purchases', () => {
|
context('successful purchases', () => {
|
||||||
it('buys a pet', () => {
|
it('buys a pet', async () => {
|
||||||
user.purchased.plan.consecutive.trinkets = 2;
|
user.purchased.plan.consecutive.trinkets = 2;
|
||||||
|
|
||||||
const [, message] = hourglassPurchase(user, { params: { type: 'pets', key: 'MantisShrimp-Base' } }, analytics);
|
const [, message] = await hourglassPurchase(user, { params: { type: 'pets', key: 'MantisShrimp-Base' } }, analytics);
|
||||||
|
|
||||||
expect(message).to.eql(i18n.t('hourglassPurchase'));
|
expect(message).to.eql(i18n.t('hourglassPurchase'));
|
||||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
||||||
@@ -148,10 +139,10 @@ describe('common.ops.hourglassPurchase', () => {
|
|||||||
expect(analytics.track).to.be.calledOnce;
|
expect(analytics.track).to.be.calledOnce;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('buys a mount', () => {
|
it('buys a mount', async () => {
|
||||||
user.purchased.plan.consecutive.trinkets = 2;
|
user.purchased.plan.consecutive.trinkets = 2;
|
||||||
|
|
||||||
const [, message] = buyMount(user, { params: { key: 'MantisShrimp-Base' } });
|
const [, message] = await buyMount(user, { params: { key: 'MantisShrimp-Base' } });
|
||||||
expect(message).to.eql(i18n.t('hourglassPurchase'));
|
expect(message).to.eql(i18n.t('hourglassPurchase'));
|
||||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
||||||
expect(user.items.mounts).to.eql({ 'MantisShrimp-Base': true });
|
expect(user.items.mounts).to.eql({ 'MantisShrimp-Base': true });
|
||||||
|
|||||||
@@ -33,118 +33,108 @@ describe('shared.ops.purchase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
context('failure conditions', () => {
|
context('failure conditions', () => {
|
||||||
it('returns an error when type is not provided', done => {
|
it('returns an error when type is not provided', async () => {
|
||||||
try {
|
try {
|
||||||
purchase(user, { params: {} });
|
await purchase(user, { params: {} });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('typeRequired'));
|
expect(err.message).to.equal(i18n.t('typeRequired'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when unknown type is provided', done => {
|
it('returns error when unknown type is provided', async () => {
|
||||||
try {
|
try {
|
||||||
purchase(user, { params: { type: 'randomType', key: 'gem' } });
|
await purchase(user, { params: { type: 'randomType', key: 'gem' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotFound);
|
expect(err).to.be.an.instanceof(NotFound);
|
||||||
expect(err.message).to.equal(i18n.t('notAccteptedType'));
|
expect(err.message).to.equal(i18n.t('notAccteptedType'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when user attempts to purchase a piece of gear they own', done => {
|
it('returns error when user attempts to purchase a piece of gear they own', async () => {
|
||||||
user.items.gear.owned['shield_rogue_1'] = true; // eslint-disable-line dot-notation
|
user.items.gear.owned['shield_rogue_1'] = true; // eslint-disable-line dot-notation
|
||||||
|
|
||||||
try {
|
try {
|
||||||
purchase(user, { params: { type: 'gear', key: 'shield_rogue_1' } });
|
await purchase(user, { params: { type: 'gear', key: 'shield_rogue_1' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('alreadyHave'));
|
expect(err.message).to.equal(i18n.t('alreadyHave'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when unknown item is requested', done => {
|
it('returns error when unknown item is requested', async () => {
|
||||||
try {
|
try {
|
||||||
purchase(user, { params: { type: 'gear', key: 'randomKey' } });
|
await purchase(user, { params: { type: 'gear', key: 'randomKey' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotFound);
|
expect(err).to.be.an.instanceof(NotFound);
|
||||||
expect(err.message).to.equal(i18n.t('contentKeyNotFound', { type: 'gear' }));
|
expect(err.message).to.equal(i18n.t('contentKeyNotFound', { type: 'gear' }));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when user does not have permission to buy an item', done => {
|
it('returns error when user does not have permission to buy an item', async () => {
|
||||||
try {
|
try {
|
||||||
purchase(user, { params: { type: 'gear', key: 'eyewear_mystery_301405' } });
|
await purchase(user, { params: { type: 'gear', key: 'eyewear_mystery_301405' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('messageNotAvailable'));
|
expect(err.message).to.equal(i18n.t('messageNotAvailable'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when user does not have enough gems to buy an item', done => {
|
it('returns error when user does not have enough gems to buy an item', async () => {
|
||||||
try {
|
try {
|
||||||
purchase(user, { params: { type: 'gear', key: 'headAccessory_special_wolfEars' } });
|
await purchase(user, { params: { type: 'gear', key: 'headAccessory_special_wolfEars' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when item is not found', done => {
|
it('returns error when item is not found', async () => {
|
||||||
const params = { key: 'notExisting', type: 'food' };
|
const params = { key: 'notExisting', type: 'food' };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
purchase(user, { params });
|
await purchase(user, { params });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotFound);
|
expect(err).to.be.an.instanceof(NotFound);
|
||||||
expect(err.message).to.equal(i18n.t('contentKeyNotFound', params));
|
expect(err.message).to.equal(i18n.t('contentKeyNotFound', params));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when user supplies a non-numeric quantity', done => {
|
it('returns error when user supplies a non-numeric quantity', async () => {
|
||||||
const type = 'eggs';
|
const type = 'eggs';
|
||||||
const key = 'Wolf';
|
const key = 'Wolf';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
purchase(user, { params: { type, key }, quantity: 'jamboree' }, analytics);
|
await purchase(user, { params: { type, key }, quantity: 'jamboree' }, analytics);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when user supplies a negative quantity', done => {
|
it('returns error when user supplies a negative quantity', async () => {
|
||||||
const type = 'eggs';
|
const type = 'eggs';
|
||||||
const key = 'Wolf';
|
const key = 'Wolf';
|
||||||
user.balance = 10;
|
user.balance = 10;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
purchase(user, { params: { type, key }, quantity: -2 }, analytics);
|
await purchase(user, { params: { type, key }, quantity: -2 }, analytics);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error when user supplies a decimal quantity', done => {
|
it('returns error when user supplies a decimal quantity', async () => {
|
||||||
const type = 'eggs';
|
const type = 'eggs';
|
||||||
const key = 'Wolf';
|
const key = 'Wolf';
|
||||||
user.balance = 10;
|
user.balance = 10;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
purchase(user, { params: { type, key }, quantity: 2.9 }, analytics);
|
await purchase(user, { params: { type, key }, quantity: 2.9 }, analytics);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
expect(err.message).to.equal(i18n.t('invalidQuantity'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -164,48 +154,48 @@ describe('shared.ops.purchase', () => {
|
|||||||
user.pinnedItems.push({ type: 'bundles', key: 'featheredFriends' });
|
user.pinnedItems.push({ type: 'bundles', key: 'featheredFriends' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('purchases eggs', () => {
|
it('purchases eggs', async () => {
|
||||||
const type = 'eggs';
|
const type = 'eggs';
|
||||||
const key = 'Wolf';
|
const key = 'Wolf';
|
||||||
|
|
||||||
purchase(user, { params: { type, key } }, analytics);
|
await purchase(user, { params: { type, key } }, analytics);
|
||||||
|
|
||||||
expect(user.items[type][key]).to.equal(1);
|
expect(user.items[type][key]).to.equal(1);
|
||||||
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
|
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
|
||||||
expect(analytics.track).to.be.calledOnce;
|
expect(analytics.track).to.be.calledOnce;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('purchases hatchingPotions', () => {
|
it('purchases hatchingPotions', async () => {
|
||||||
const type = 'hatchingPotions';
|
const type = 'hatchingPotions';
|
||||||
const key = 'Base';
|
const key = 'Base';
|
||||||
|
|
||||||
purchase(user, { params: { type, key } });
|
await purchase(user, { params: { type, key } });
|
||||||
|
|
||||||
expect(user.items[type][key]).to.equal(1);
|
expect(user.items[type][key]).to.equal(1);
|
||||||
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
|
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('purchases food', () => {
|
it('purchases food', async () => {
|
||||||
const type = 'food';
|
const type = 'food';
|
||||||
const key = SEASONAL_FOOD;
|
const key = SEASONAL_FOOD;
|
||||||
|
|
||||||
purchase(user, { params: { type, key } });
|
await purchase(user, { params: { type, key } });
|
||||||
|
|
||||||
expect(user.items[type][key]).to.equal(1);
|
expect(user.items[type][key]).to.equal(1);
|
||||||
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
|
expect(pinnedGearUtils.removeItemByPath.notCalled).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('purchases gear', () => {
|
it('purchases gear', async () => {
|
||||||
const type = 'gear';
|
const type = 'gear';
|
||||||
const key = 'headAccessory_special_tigerEars';
|
const key = 'headAccessory_special_tigerEars';
|
||||||
|
|
||||||
purchase(user, { params: { type, key } });
|
await purchase(user, { params: { type, key } });
|
||||||
|
|
||||||
expect(user.items.gear.owned[key]).to.be.true;
|
expect(user.items.gear.owned[key]).to.be.true;
|
||||||
expect(pinnedGearUtils.removeItemByPath.calledOnce).to.equal(true);
|
expect(pinnedGearUtils.removeItemByPath.calledOnce).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('purchases quest bundles', () => {
|
it('purchases quest bundles', async () => {
|
||||||
const startingBalance = user.balance;
|
const startingBalance = user.balance;
|
||||||
const clock = sandbox.useFakeTimers(moment('2019-05-20').valueOf());
|
const clock = sandbox.useFakeTimers(moment('2019-05-20').valueOf());
|
||||||
const type = 'bundles';
|
const type = 'bundles';
|
||||||
@@ -217,7 +207,7 @@ describe('shared.ops.purchase', () => {
|
|||||||
'owl',
|
'owl',
|
||||||
];
|
];
|
||||||
|
|
||||||
purchase(user, { params: { type, key } });
|
await purchase(user, { params: { type, key } });
|
||||||
|
|
||||||
forEach(questList, bundledKey => {
|
forEach(questList, bundledKey => {
|
||||||
expect(user.items.quests[bundledKey]).to.equal(1);
|
expect(user.items.quests[bundledKey]).to.equal(1);
|
||||||
@@ -240,28 +230,27 @@ describe('shared.ops.purchase', () => {
|
|||||||
user.purchased.plan.customerId = 'customer-id';
|
user.purchased.plan.customerId = 'customer-id';
|
||||||
});
|
});
|
||||||
|
|
||||||
it('errors when user does not have enough gems', done => {
|
it('errors when user does not have enough gems', async () => {
|
||||||
user.balance = 1;
|
user.balance = 1;
|
||||||
const type = 'eggs';
|
const type = 'eggs';
|
||||||
const key = 'TigerCub';
|
const key = 'TigerCub';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
purchase(user, {
|
await purchase(user, {
|
||||||
params: { type, key },
|
params: { type, key },
|
||||||
quantity: 2,
|
quantity: 2,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('makes bulk purchases of eggs', () => {
|
it('makes bulk purchases of eggs', async () => {
|
||||||
const type = 'eggs';
|
const type = 'eggs';
|
||||||
const key = 'TigerCub';
|
const key = 'TigerCub';
|
||||||
|
|
||||||
purchase(user, {
|
await purchase(user, {
|
||||||
params: { type, key },
|
params: { type, key },
|
||||||
quantity: 2,
|
quantity: 2,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,51 +19,48 @@ describe('shared.ops.changeClass', () => {
|
|||||||
user.stats.flagSelected = false;
|
user.stats.flagSelected = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('user is not level 10', done => {
|
it('user is not level 10', async () => {
|
||||||
user.stats.lvl = 9;
|
user.stats.lvl = 9;
|
||||||
try {
|
try {
|
||||||
changeClass(user, { query: { class: 'rogue' } });
|
await await changeClass(user, { query: { class: 'rogue' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('lvl10ChangeClass'));
|
expect(err.message).to.equal(i18n.t('lvl10ChangeClass'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('req.query.class is an invalid class', done => {
|
it('req.query.class is an invalid class', async () => {
|
||||||
user.flags.classSelected = false;
|
user.flags.classSelected = false;
|
||||||
user.preferences.disableClasses = false;
|
user.preferences.disableClasses = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
changeClass(user, { query: { class: 'cellist' } });
|
await changeClass(user, { query: { class: 'cellist' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('invalidClass'));
|
expect(err.message).to.equal(i18n.t('invalidClass'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
context('req.query.class is a valid class', () => {
|
context('req.query.class is a valid class', () => {
|
||||||
it('errors if user.stats.flagSelected is true and user.balance < 0.75', done => {
|
it('errors if user.stats.flagSelected is true and user.balance < 0.75', async () => {
|
||||||
user.flags.classSelected = true;
|
user.flags.classSelected = true;
|
||||||
user.preferences.disableClasses = false;
|
user.preferences.disableClasses = false;
|
||||||
user.balance = 0;
|
user.balance = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
changeClass(user, { query: { class: 'rogue' } });
|
await changeClass(user, { query: { class: 'rogue' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('changes class', () => {
|
it('changes class', async () => {
|
||||||
user.stats.class = 'healer';
|
user.stats.class = 'healer';
|
||||||
user.items.gear.owned.weapon_healer_3 = true;
|
user.items.gear.owned.weapon_healer_3 = true;
|
||||||
user.items.gear.equipped.weapon = 'weapon_healer_3';
|
user.items.gear.equipped.weapon = 'weapon_healer_3';
|
||||||
|
|
||||||
const [data] = changeClass(user, { query: { class: 'rogue' } });
|
const [data] = await changeClass(user, { query: { class: 'rogue' } });
|
||||||
expect(data).to.eql({
|
expect(data).to.eql({
|
||||||
preferences: user.preferences,
|
preferences: user.preferences,
|
||||||
stats: user.stats,
|
stats: user.stats,
|
||||||
@@ -81,7 +78,7 @@ describe('shared.ops.changeClass', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
context('req.query.class is missing or user.stats.flagSelected is true', () => {
|
context('req.query.class is missing or user.stats.flagSelected is true', () => {
|
||||||
it('has user.preferences.disableClasses === true', () => {
|
it('has user.preferences.disableClasses === true', async () => {
|
||||||
user.balance = 1;
|
user.balance = 1;
|
||||||
user.preferences.disableClasses = true;
|
user.preferences.disableClasses = true;
|
||||||
user.preferences.autoAllocate = true;
|
user.preferences.autoAllocate = true;
|
||||||
@@ -92,7 +89,7 @@ describe('shared.ops.changeClass', () => {
|
|||||||
user.stats.int = 4;
|
user.stats.int = 4;
|
||||||
user.flags.classSelected = true;
|
user.flags.classSelected = true;
|
||||||
|
|
||||||
const [data] = changeClass(user);
|
const [data] = await changeClass(user);
|
||||||
expect(data).to.eql({
|
expect(data).to.eql({
|
||||||
preferences: user.preferences,
|
preferences: user.preferences,
|
||||||
stats: user.stats,
|
stats: user.stats,
|
||||||
@@ -112,18 +109,17 @@ describe('shared.ops.changeClass', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
context('has user.preferences.disableClasses !== true', () => {
|
context('has user.preferences.disableClasses !== true', () => {
|
||||||
it('and less than 3 gems', done => {
|
it('and less than 3 gems', async () => {
|
||||||
user.balance = 0.5;
|
user.balance = 0.5;
|
||||||
try {
|
try {
|
||||||
changeClass(user);
|
await changeClass(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('and at least 3 gems', () => {
|
it('and at least 3 gems', async () => {
|
||||||
user.balance = 1;
|
user.balance = 1;
|
||||||
user.stats.points = 45;
|
user.stats.points = 45;
|
||||||
user.stats.str = 1;
|
user.stats.str = 1;
|
||||||
@@ -132,7 +128,7 @@ describe('shared.ops.changeClass', () => {
|
|||||||
user.stats.int = 4;
|
user.stats.int = 4;
|
||||||
user.flags.classSelected = true;
|
user.flags.classSelected = true;
|
||||||
|
|
||||||
const [data] = changeClass(user);
|
const [data] = await changeClass(user);
|
||||||
expect(data).to.eql({
|
expect(data).to.eql({
|
||||||
preferences: user.preferences,
|
preferences: user.preferences,
|
||||||
stats: user.stats,
|
stats: user.stats,
|
||||||
|
|||||||
@@ -24,60 +24,59 @@ describe('shared.ops.rebirth', () => {
|
|||||||
tasks = [generateHabit(), generateDaily(), generateTodo(), generateReward()];
|
tasks = [generateHabit(), generateDaily(), generateTodo(), generateReward()];
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error when user balance is too low and user is less than max level', done => {
|
it('returns an error when user balance is too low and user is less than max level', async () => {
|
||||||
user.balance = 0;
|
user.balance = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rebirths a user with enough gems', () => {
|
it('rebirths a user with enough gems', async () => {
|
||||||
const [, message] = rebirth(user);
|
const [, message] = await rebirth(user);
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('rebirthComplete'));
|
expect(message).to.equal(i18n.t('rebirthComplete'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rebirths a user with not enough gems but max level', () => {
|
it('rebirths a user with not enough gems but max level', async () => {
|
||||||
user.balance = 0;
|
user.balance = 0;
|
||||||
user.stats.lvl = MAX_LEVEL;
|
user.stats.lvl = MAX_LEVEL;
|
||||||
|
|
||||||
const [, message] = rebirth(user);
|
const [, message] = await rebirth(user);
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('rebirthComplete'));
|
expect(message).to.equal(i18n.t('rebirthComplete'));
|
||||||
expect(user.flags.lastFreeRebirth).to.exist;
|
expect(user.flags.lastFreeRebirth).to.exist;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rebirths a user with not enough gems but more than max level', () => {
|
it('rebirths a user with not enough gems but more than max level', async () => {
|
||||||
user.balance = 0;
|
user.balance = 0;
|
||||||
user.stats.lvl = MAX_LEVEL + 1;
|
user.stats.lvl = MAX_LEVEL + 1;
|
||||||
|
|
||||||
const [, message] = rebirth(user);
|
const [, message] = await rebirth(user);
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('rebirthComplete'));
|
expect(message).to.equal(i18n.t('rebirthComplete'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rebirths a user using gems if over max level but rebirthed recently', () => {
|
it('rebirths a user using gems if over max level but rebirthed recently', async () => {
|
||||||
user.stats.lvl = MAX_LEVEL + 1;
|
user.stats.lvl = MAX_LEVEL + 1;
|
||||||
user.flags.lastFreeRebirth = new Date();
|
user.flags.lastFreeRebirth = new Date();
|
||||||
|
|
||||||
const [, message] = rebirth(user);
|
const [, message] = await rebirth(user);
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('rebirthComplete'));
|
expect(message).to.equal(i18n.t('rebirthComplete'));
|
||||||
expect(user.balance).to.equal(0);
|
expect(user.balance).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resets user\'s tasks values except for rewards to 0', () => {
|
it('resets user\'s tasks values except for rewards to 0', async () => {
|
||||||
tasks[0].value = 1;
|
tasks[0].value = 1;
|
||||||
tasks[1].value = 1;
|
tasks[1].value = 1;
|
||||||
tasks[2].value = 1;
|
tasks[2].value = 1;
|
||||||
tasks[3].value = 1; // Reward
|
tasks[3].value = 1; // Reward
|
||||||
|
|
||||||
rebirth(user, tasks);
|
await rebirth(user, tasks);
|
||||||
|
|
||||||
expect(tasks[0].value).to.equal(0);
|
expect(tasks[0].value).to.equal(0);
|
||||||
expect(tasks[1].value).to.equal(0);
|
expect(tasks[1].value).to.equal(0);
|
||||||
@@ -85,99 +84,99 @@ describe('shared.ops.rebirth', () => {
|
|||||||
expect(tasks[3].value).to.equal(1); // Reward
|
expect(tasks[3].value).to.equal(1); // Reward
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resets user\'s daily streaks to 0', () => {
|
it('resets user\'s daily streaks to 0', async () => {
|
||||||
tasks[0].counterDown = 1; // Habit
|
tasks[0].counterDown = 1; // Habit
|
||||||
tasks[0].counterUp = 1; // Habit
|
tasks[0].counterUp = 1; // Habit
|
||||||
tasks[1].streak = 1; // Daily
|
tasks[1].streak = 1; // Daily
|
||||||
|
|
||||||
rebirth(user, tasks);
|
await rebirth(user, tasks);
|
||||||
|
|
||||||
expect(tasks[0].counterDown).to.equal(0);
|
expect(tasks[0].counterDown).to.equal(0);
|
||||||
expect(tasks[0].counterUp).to.equal(0);
|
expect(tasks[0].counterUp).to.equal(0);
|
||||||
expect(tasks[1].streak).to.equal(0);
|
expect(tasks[1].streak).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resets a user\'s buffs', () => {
|
it('resets a user\'s buffs', async () => {
|
||||||
user.stats.buffs = { test: 'test' };
|
user.stats.buffs = { test: 'test' };
|
||||||
|
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
expect(user.stats.buffs).to.be.empty;
|
expect(user.stats.buffs).to.be.empty;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resets a user\'s health points', () => {
|
it('resets a user\'s health points', async () => {
|
||||||
user.stats.hp = 40;
|
user.stats.hp = 40;
|
||||||
|
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
expect(user.stats.hp).to.equal(50);
|
expect(user.stats.hp).to.equal(50);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resets a user\'s class', () => {
|
it('resets a user\'s class', async () => {
|
||||||
user.stats.class = 'rouge';
|
user.stats.class = 'rouge';
|
||||||
|
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
expect(user.stats.class).to.equal('warrior');
|
expect(user.stats.class).to.equal('warrior');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resets a user\'s stats', () => {
|
it('resets a user\'s stats', async () => {
|
||||||
user.stats.class = 'rouge';
|
user.stats.class = 'rouge';
|
||||||
_.each(userStats, value => {
|
_.each(userStats, value => {
|
||||||
user.stats[value] = 10;
|
user.stats[value] = 10;
|
||||||
});
|
});
|
||||||
|
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
_.each(userStats, value => {
|
_.each(userStats, value => {
|
||||||
user.stats[value] = 0;
|
user.stats[value] = 0;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('retains a user\'s gear', () => {
|
it('retains a user\'s gear', async () => {
|
||||||
const prevGearEquipped = user.items.gear.equipped;
|
const prevGearEquipped = user.items.gear.equipped;
|
||||||
const prevGearCostume = user.items.gear.costume;
|
const prevGearCostume = user.items.gear.costume;
|
||||||
const prevPrefCostume = user.preferences.costume;
|
const prevPrefCostume = user.preferences.costume;
|
||||||
|
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
expect(user.items.gear.equipped).to.deep.equal(prevGearEquipped);
|
expect(user.items.gear.equipped).to.deep.equal(prevGearEquipped);
|
||||||
expect(user.items.gear.costume).to.deep.equal(prevGearCostume);
|
expect(user.items.gear.costume).to.deep.equal(prevGearCostume);
|
||||||
expect(user.preferences.costume).to.equal(prevPrefCostume);
|
expect(user.preferences.costume).to.equal(prevPrefCostume);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('retains a user\'s gear owned', () => {
|
it('retains a user\'s gear owned', async () => {
|
||||||
user.items.gear.owned.weapon_warrior_1 = true; // eslint-disable-line camelcase
|
user.items.gear.owned.weapon_warrior_1 = true; // eslint-disable-line camelcase
|
||||||
const prevGearOwned = user.items.gear.owned;
|
const prevGearOwned = user.items.gear.owned;
|
||||||
|
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
expect(user.items.gear.owned).to.equal(prevGearOwned);
|
expect(user.items.gear.owned).to.equal(prevGearOwned);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resets a user\'s current pet', () => {
|
it('resets a user\'s current pet', async () => {
|
||||||
user.items.pets[animal] = true;
|
user.items.pets[animal] = true;
|
||||||
user.items.currentPet = animal;
|
user.items.currentPet = animal;
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
expect(user.items.currentPet).to.be.empty;
|
expect(user.items.currentPet).to.be.empty;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resets a user\'s current mount', () => {
|
it('resets a user\'s current mount', async () => {
|
||||||
user.items.mounts[animal] = true;
|
user.items.mounts[animal] = true;
|
||||||
user.items.currentMount = animal;
|
user.items.currentMount = animal;
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
expect(user.items.currentMount).to.be.empty;
|
expect(user.items.currentMount).to.be.empty;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resets a user\'s flags', () => {
|
it('resets a user\'s flags', async () => {
|
||||||
user.flags.itemsEnabled = true;
|
user.flags.itemsEnabled = true;
|
||||||
user.flags.classSelected = true;
|
user.flags.classSelected = true;
|
||||||
user.flags.rebirthEnabled = true;
|
user.flags.rebirthEnabled = true;
|
||||||
user.flags.levelDrops = { test: 'test' };
|
user.flags.levelDrops = { test: 'test' };
|
||||||
|
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
expect(user.flags.itemsEnabled).to.be.false;
|
expect(user.flags.itemsEnabled).to.be.false;
|
||||||
expect(user.flags.classSelected).to.be.false;
|
expect(user.flags.classSelected).to.be.false;
|
||||||
@@ -185,80 +184,80 @@ describe('shared.ops.rebirth', () => {
|
|||||||
expect(user.flags.levelDrops).to.be.empty;
|
expect(user.flags.levelDrops).to.be.empty;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reset rebirthEnabled even if user has beastMaster', () => {
|
it('reset rebirthEnabled even if user has beastMaster', async () => {
|
||||||
user.achievements.beastMaster = 1;
|
user.achievements.beastMaster = 1;
|
||||||
user.flags.rebirthEnabled = true;
|
user.flags.rebirthEnabled = true;
|
||||||
|
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
expect(user.flags.rebirthEnabled).to.be.false;
|
expect(user.flags.rebirthEnabled).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets rebirth achievement', () => {
|
it('sets rebirth achievement', async () => {
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
expect(user.achievements.rebirths).to.equal(1);
|
expect(user.achievements.rebirths).to.equal(1);
|
||||||
expect(user.achievements.rebirthLevel).to.equal(user.stats.lvl);
|
expect(user.achievements.rebirthLevel).to.equal(user.stats.lvl);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('increments rebirth achievements', () => {
|
it('increments rebirth achievements', async () => {
|
||||||
user.stats.lvl = 2;
|
user.stats.lvl = 2;
|
||||||
user.achievements.rebirths = 1;
|
user.achievements.rebirths = 1;
|
||||||
user.achievements.rebirthLevel = 1;
|
user.achievements.rebirthLevel = 1;
|
||||||
|
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
expect(user.achievements.rebirths).to.equal(2);
|
expect(user.achievements.rebirths).to.equal(2);
|
||||||
expect(user.achievements.rebirthLevel).to.equal(2);
|
expect(user.achievements.rebirthLevel).to.equal(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not increment rebirth achievements when level is lower than previous', () => {
|
it('does not increment rebirth achievements when level is lower than previous', async () => {
|
||||||
user.stats.lvl = 2;
|
user.stats.lvl = 2;
|
||||||
user.achievements.rebirths = 1;
|
user.achievements.rebirths = 1;
|
||||||
user.achievements.rebirthLevel = 3;
|
user.achievements.rebirthLevel = 3;
|
||||||
|
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
expect(user.achievements.rebirths).to.equal(1);
|
expect(user.achievements.rebirths).to.equal(1);
|
||||||
expect(user.achievements.rebirthLevel).to.equal(3);
|
expect(user.achievements.rebirthLevel).to.equal(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('always increments rebirth achievements when level is MAX_LEVEL', () => {
|
it('always increments rebirth achievements when level is MAX_LEVEL', async () => {
|
||||||
user.stats.lvl = MAX_LEVEL;
|
user.stats.lvl = MAX_LEVEL;
|
||||||
user.achievements.rebirths = 1;
|
user.achievements.rebirths = 1;
|
||||||
// this value is not actually possible (actually capped at MAX_LEVEL) but makes a good test
|
// this value is not actually possible (actually capped at MAX_LEVEL) but makes a good test
|
||||||
user.achievements.rebirthLevel = MAX_LEVEL + 1;
|
user.achievements.rebirthLevel = MAX_LEVEL + 1;
|
||||||
|
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
expect(user.achievements.rebirths).to.equal(2);
|
expect(user.achievements.rebirths).to.equal(2);
|
||||||
expect(user.achievements.rebirthLevel).to.equal(MAX_LEVEL);
|
expect(user.achievements.rebirthLevel).to.equal(MAX_LEVEL);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('always increments rebirth achievements when level is greater than MAX_LEVEL', () => {
|
it('always increments rebirth achievements when level is greater than MAX_LEVEL', async () => {
|
||||||
user.stats.lvl = MAX_LEVEL + 1;
|
user.stats.lvl = MAX_LEVEL + 1;
|
||||||
user.achievements.rebirths = 1;
|
user.achievements.rebirths = 1;
|
||||||
// this value is not actually possible (actually capped at MAX_LEVEL) but makes a good test
|
// this value is not actually possible (actually capped at MAX_LEVEL) but makes a good test
|
||||||
user.achievements.rebirthLevel = MAX_LEVEL + 2;
|
user.achievements.rebirthLevel = MAX_LEVEL + 2;
|
||||||
|
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
expect(user.achievements.rebirths).to.equal(2);
|
expect(user.achievements.rebirths).to.equal(2);
|
||||||
expect(user.achievements.rebirthLevel).to.equal(MAX_LEVEL);
|
expect(user.achievements.rebirthLevel).to.equal(MAX_LEVEL);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('keeps automaticAllocation false', () => {
|
it('keeps automaticAllocation false', async () => {
|
||||||
user.preferences.automaticAllocation = false;
|
user.preferences.automaticAllocation = false;
|
||||||
|
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
expect(user.preferences.automaticAllocation).to.be.false;
|
expect(user.preferences.automaticAllocation).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets automaticAllocation to false when true', () => {
|
it('sets automaticAllocation to false when true', async () => {
|
||||||
user.preferences.automaticAllocation = true;
|
user.preferences.automaticAllocation = true;
|
||||||
|
|
||||||
rebirth(user);
|
await rebirth(user);
|
||||||
|
|
||||||
expect(user.preferences.automaticAllocation).to.be.false;
|
expect(user.preferences.automaticAllocation).to.be.false;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,87 +23,85 @@ describe('shared.ops.releaseMounts', () => {
|
|||||||
user.balance = 1;
|
user.balance = 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error when user balance is too low', done => {
|
it('returns an error when user balance is too low', async () => {
|
||||||
user.balance = 0;
|
user.balance = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
releaseMounts(user);
|
await releaseMounts(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error when user does not have all pets', done => {
|
it('returns an error when user does not have all pets', async () => {
|
||||||
const mountsKeys = Object.keys(user.items.mounts);
|
const mountsKeys = Object.keys(user.items.mounts);
|
||||||
delete user.items.mounts[mountsKeys[0]];
|
delete user.items.mounts[mountsKeys[0]];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
releaseMounts(user);
|
await releaseMounts(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('notEnoughMounts'));
|
expect(err.message).to.equal(i18n.t('notEnoughMounts'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('releases mounts', () => {
|
it('releases mounts', async () => {
|
||||||
const message = releaseMounts(user)[1];
|
const result = await releaseMounts(user);
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('mountsReleased'));
|
expect(result[1]).to.equal(i18n.t('mountsReleased'));
|
||||||
expect(user.items.mounts[animal]).to.equal(null);
|
expect(user.items.mounts[animal]).to.equal(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('removes drop currentMount', () => {
|
it('removes drop currentMount', async () => {
|
||||||
const mountInfo = content.mountInfo[user.items.currentMount];
|
const mountInfo = content.mountInfo[user.items.currentMount];
|
||||||
expect(mountInfo.type).to.equal('drop');
|
expect(mountInfo.type).to.equal('drop');
|
||||||
releaseMounts(user);
|
await releaseMounts(user);
|
||||||
|
|
||||||
expect(user.items.currentMount).to.be.empty;
|
expect(user.items.currentMount).to.be.empty;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('leaves non-drop mount equipped', () => {
|
it('leaves non-drop mount equipped', async () => {
|
||||||
const questAnimal = 'Gryphon-Base';
|
const questAnimal = 'Gryphon-Base';
|
||||||
user.items.currentMount = questAnimal;
|
user.items.currentMount = questAnimal;
|
||||||
user.items.mounts[questAnimal] = true;
|
user.items.mounts[questAnimal] = true;
|
||||||
|
|
||||||
const mountInfo = content.mountInfo[user.items.currentMount];
|
const mountInfo = content.mountInfo[user.items.currentMount];
|
||||||
expect(mountInfo.type).to.not.equal('drop');
|
expect(mountInfo.type).to.not.equal('drop');
|
||||||
releaseMounts(user);
|
await releaseMounts(user);
|
||||||
|
|
||||||
expect(user.items.currentMount).to.equal(questAnimal);
|
expect(user.items.currentMount).to.equal(questAnimal);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('increases mountMasterCount achievement', () => {
|
it('increases mountMasterCount achievement', async () => {
|
||||||
releaseMounts(user);
|
await releaseMounts(user);
|
||||||
expect(user.achievements.mountMasterCount).to.equal(1);
|
expect(user.achievements.mountMasterCount).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not increase mountMasterCount achievement if mount is missing (null)', () => {
|
it('does not increase mountMasterCount achievement if mount is missing (null)', async () => {
|
||||||
const mountMasterCountBeforeRelease = user.achievements.mountMasterCount;
|
const mountMasterCountBeforeRelease = user.achievements.mountMasterCount;
|
||||||
user.items.mounts[animal] = null;
|
user.items.mounts[animal] = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
releaseMounts(user);
|
await releaseMounts(user);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(user.achievements.mountMasterCount).to.equal(mountMasterCountBeforeRelease);
|
expect(user.achievements.mountMasterCount).to.equal(mountMasterCountBeforeRelease);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not increase mountMasterCount achievement if mount is missing (undefined)', () => {
|
it('does not increase mountMasterCount achievement if mount is missing (undefined)', async () => {
|
||||||
const mountMasterCountBeforeRelease = user.achievements.mountMasterCount;
|
const mountMasterCountBeforeRelease = user.achievements.mountMasterCount;
|
||||||
delete user.items.mounts[animal];
|
delete user.items.mounts[animal];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
releaseMounts(user);
|
await releaseMounts(user);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(user.achievements.mountMasterCount).to.equal(mountMasterCountBeforeRelease);
|
expect(user.achievements.mountMasterCount).to.equal(mountMasterCountBeforeRelease);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('subtracts gems from balance', () => {
|
it('subtracts gems from balance', async () => {
|
||||||
releaseMounts(user);
|
await releaseMounts(user);
|
||||||
|
|
||||||
expect(user.balance).to.equal(0);
|
expect(user.balance).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,98 +23,96 @@ describe('shared.ops.releasePets', () => {
|
|||||||
user.balance = 1;
|
user.balance = 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error when user balance is too low', done => {
|
it('returns an error when user balance is too low', async () => {
|
||||||
user.balance = 0;
|
user.balance = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
releasePets(user);
|
await releasePets(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error when user does not have all pets', done => {
|
it('returns an error when user does not have all pets', async () => {
|
||||||
const petKeys = Object.keys(user.items.pets);
|
const petKeys = Object.keys(user.items.pets);
|
||||||
delete user.items.pets[petKeys[0]];
|
delete user.items.pets[petKeys[0]];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
releasePets(user);
|
await releasePets(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('notEnoughPets'));
|
expect(err.message).to.equal(i18n.t('notEnoughPets'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('releases pets', () => {
|
it('releases pets', async () => {
|
||||||
const message = releasePets(user)[1];
|
const message = await releasePets(user)[1];
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('petsReleased'));
|
expect(message).to.equal(i18n.t('petsReleased'));
|
||||||
expect(user.items.pets[animal]).to.equal(0);
|
expect(user.items.pets[animal]).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('removes drop currentPet', () => {
|
it('removes drop currentPet', async () => {
|
||||||
const petInfo = content.petInfo[user.items.currentPet];
|
const petInfo = content.petInfo[user.items.currentPet];
|
||||||
expect(petInfo.type).to.equal('drop');
|
expect(petInfo.type).to.equal('drop');
|
||||||
releasePets(user);
|
await releasePets(user);
|
||||||
|
|
||||||
expect(user.items.currentPet).to.be.empty;
|
expect(user.items.currentPet).to.be.empty;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('leaves non-drop pets equipped', () => {
|
it('leaves non-drop pets equipped', async () => {
|
||||||
const questAnimal = 'Gryphon-Base';
|
const questAnimal = 'Gryphon-Base';
|
||||||
user.items.currentPet = questAnimal;
|
user.items.currentPet = questAnimal;
|
||||||
user.items.pets[questAnimal] = 5;
|
user.items.pets[questAnimal] = 5;
|
||||||
|
|
||||||
const petInfo = content.petInfo[user.items.currentPet];
|
const petInfo = content.petInfo[user.items.currentPet];
|
||||||
expect(petInfo.type).to.not.equal('drop');
|
expect(petInfo.type).to.not.equal('drop');
|
||||||
releasePets(user);
|
await releasePets(user);
|
||||||
|
|
||||||
expect(user.items.currentPet).to.equal(questAnimal);
|
expect(user.items.currentPet).to.equal(questAnimal);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('decreases user\'s balance', () => {
|
it('decreases user\'s balance', async () => {
|
||||||
releasePets(user);
|
await releasePets(user);
|
||||||
|
|
||||||
expect(user.balance).to.equal(0);
|
expect(user.balance).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('incremenets beastMasterCount', () => {
|
it('incremenets beastMasterCount', async () => {
|
||||||
releasePets(user);
|
await releasePets(user);
|
||||||
|
|
||||||
expect(user.achievements.beastMasterCount).to.equal(1);
|
expect(user.achievements.beastMasterCount).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not increment beastMasterCount if any pet is level 0 (released)', () => {
|
it('does not increment beastMasterCount if any pet is level 0 (released)', async () => {
|
||||||
const beastMasterCountBeforeRelease = user.achievements.beastMasterCount;
|
const beastMasterCountBeforeRelease = user.achievements.beastMasterCount;
|
||||||
user.items.pets[animal] = 0;
|
user.items.pets[animal] = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
releasePets(user);
|
await releasePets(user);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease);
|
expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not increment beastMasterCount if any pet is missing (null)', () => {
|
it('does not increment beastMasterCount if any pet is missing (null)', async () => {
|
||||||
const beastMasterCountBeforeRelease = user.achievements.beastMasterCount;
|
const beastMasterCountBeforeRelease = user.achievements.beastMasterCount;
|
||||||
user.items.pets[animal] = null;
|
user.items.pets[animal] = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
releasePets(user);
|
await releasePets(user);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease);
|
expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not increment beastMasterCount if any pet is missing (undefined)', () => {
|
it('does not increment beastMasterCount if any pet is missing (undefined)', async () => {
|
||||||
const beastMasterCountBeforeRelease = user.achievements.beastMasterCount;
|
const beastMasterCountBeforeRelease = user.achievements.beastMasterCount;
|
||||||
delete user.items.pets[animal];
|
delete user.items.pets[animal];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
releasePets(user);
|
await releasePets(user);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease);
|
expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,43 +19,42 @@ describe('shared.ops.reroll', () => {
|
|||||||
tasks = [generateDaily(), generateReward()];
|
tasks = [generateDaily(), generateReward()];
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error when user balance is too low', done => {
|
it('returns an error when user balance is too low', async () => {
|
||||||
user.balance = 0;
|
user.balance = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
reroll(user);
|
await reroll(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rerolls a user with enough gems', () => {
|
it('rerolls a user with enough gems', async () => {
|
||||||
const [, message] = reroll(user);
|
const [, message] = await reroll(user);
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('fortifyComplete'));
|
expect(message).to.equal(i18n.t('fortifyComplete'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reduces a user\'s balance', () => {
|
it('reduces a user\'s balance', async () => {
|
||||||
reroll(user);
|
await reroll(user);
|
||||||
|
|
||||||
expect(user.balance).to.equal(0);
|
expect(user.balance).to.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resets a user\'s health points', () => {
|
it('resets a user\'s health points', async () => {
|
||||||
user.stats.hp = 40;
|
user.stats.hp = 40;
|
||||||
|
|
||||||
reroll(user);
|
await reroll(user);
|
||||||
|
|
||||||
expect(user.stats.hp).to.equal(50);
|
expect(user.stats.hp).to.equal(50);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resets user\'s taks values except for rewards to 0', () => {
|
it('resets user\'s taks values except for rewards to 0', async () => {
|
||||||
tasks[0].value = 1;
|
tasks[0].value = 1;
|
||||||
tasks[1].value = 1;
|
tasks[1].value = 1;
|
||||||
|
|
||||||
reroll(user, tasks);
|
await reroll(user, tasks);
|
||||||
|
|
||||||
expect(tasks[0].value).to.equal(0);
|
expect(tasks[0].value).to.equal(0);
|
||||||
expect(tasks[1].value).to.equal(1);
|
expect(tasks[1].value).to.equal(1);
|
||||||
|
|||||||
@@ -19,184 +19,173 @@ describe('shared.ops.unlock', () => {
|
|||||||
user.balance = usersStartingGems;
|
user.balance = usersStartingGems;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error when path is not provided', done => {
|
it('returns an error when path is not provided', async () => {
|
||||||
try {
|
try {
|
||||||
unlock(user);
|
await unlock(user);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('pathRequired'));
|
expect(err.message).to.equal(i18n.t('pathRequired'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not unlock lost gear', done => {
|
it('does not unlock lost gear', async () => {
|
||||||
user.items.gear.owned.headAccessory_special_bearEars = false;
|
user.items.gear.owned.headAccessory_special_bearEars = false;
|
||||||
|
|
||||||
unlock(user, { query: { path: 'items.gear.owned.headAccessory_special_bearEars' } });
|
await unlock(user, { query: { path: 'items.gear.owned.headAccessory_special_bearEars' } });
|
||||||
|
|
||||||
expect(user.balance).to.equal(usersStartingGems);
|
expect(user.balance).to.equal(usersStartingGems);
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error when user balance is too low', done => {
|
it('returns an error when user balance is too low', async () => {
|
||||||
user.balance = 0;
|
user.balance = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
unlock(user, { query: { path: unlockPath } });
|
await unlock(user, { query: { path: unlockPath } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
expect(err.message).to.equal(i18n.t('notEnoughGems'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error when user already owns a full set', done => {
|
it('returns an error when user already owns a full set', async () => {
|
||||||
let expectedBalance;
|
let expectedBalance;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
unlock(user, { query: { path: unlockPath } });
|
await unlock(user, { query: { path: unlockPath } });
|
||||||
expectedBalance = user.balance;
|
expectedBalance = user.balance;
|
||||||
unlock(user, { query: { path: unlockPath } });
|
await unlock(user, { query: { path: unlockPath } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('alreadyUnlocked'));
|
expect(err.message).to.equal(i18n.t('alreadyUnlocked'));
|
||||||
expect(user.balance).to.equal(expectedBalance);
|
expect(user.balance).to.equal(expectedBalance);
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error when user already owns a full set of gear', done => {
|
it('returns an error when user already owns a full set of gear', async () => {
|
||||||
let expectedBalance;
|
let expectedBalance;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
unlock(user, { query: { path: unlockGearSetPath } });
|
await unlock(user, { query: { path: unlockGearSetPath } });
|
||||||
expectedBalance = user.balance;
|
expectedBalance = user.balance;
|
||||||
unlock(user, { query: { path: unlockGearSetPath } });
|
await unlock(user, { query: { path: unlockGearSetPath } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('alreadyUnlocked'));
|
expect(err.message).to.equal(i18n.t('alreadyUnlocked'));
|
||||||
expect(user.balance).to.equal(expectedBalance);
|
expect(user.balance).to.equal(expectedBalance);
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error if an item does not exists', done => {
|
it('returns an error if an item does not exists', async () => {
|
||||||
try {
|
try {
|
||||||
unlock(user, { query: { path: 'background.invalid_background' } });
|
await unlock(user, { query: { path: 'background.invalid_background' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('invalidUnlockSet'));
|
expect(err.message).to.equal(i18n.t('invalidUnlockSet'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error if there are items from multiple sets', done => {
|
it('returns an error if there are items from multiple sets', async () => {
|
||||||
try {
|
try {
|
||||||
unlock(user, { query: { path: 'shirt.convict,skin.0ff591' } });
|
await unlock(user, { query: { path: 'shirt.convict,skin.0ff591' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('invalidUnlockSet'));
|
expect(err.message).to.equal(i18n.t('invalidUnlockSet'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error if gear is not from the animal set', done => {
|
it('returns an error if gear is not from the animal set', async () => {
|
||||||
try {
|
try {
|
||||||
unlock(user, { query: { path: 'items.gear.owned.back_mystery_202004' } });
|
await unlock(user, { query: { path: 'items.gear.owned.back_mystery_202004' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('invalidUnlockSet'));
|
expect(err.message).to.equal(i18n.t('invalidUnlockSet'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error if the item is free', done => {
|
it('returns an error if the item is free', async () => {
|
||||||
try {
|
try {
|
||||||
unlock(user, { query: { path: 'shirt.black' } });
|
await unlock(user, { query: { path: 'shirt.black' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('invalidUnlockSet'));
|
expect(err.message).to.equal(i18n.t('invalidUnlockSet'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error if an item does not belong to a set (appearances)', done => {
|
it('returns an error if an item does not belong to a set (appearances)', async () => {
|
||||||
try {
|
try {
|
||||||
unlock(user, { query: { path: 'shirt.pink' } });
|
await unlock(user, { query: { path: 'shirt.pink' } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(BadRequest);
|
expect(err).to.be.an.instanceof(BadRequest);
|
||||||
expect(err.message).to.equal(i18n.t('invalidUnlockSet'));
|
expect(err.message).to.equal(i18n.t('invalidUnlockSet'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an error when user already owns items in a full set and it would be more expensive to buy the entire set', done => {
|
it('returns an error when user already owns items in a full set and it would be more expensive to buy the entire set', async () => {
|
||||||
try {
|
try {
|
||||||
// There are 11 shirts in the set, each cost 2 gems, the full set 5 gems
|
// There are 11 shirts in the set, each cost 2 gems, the full set 5 gems
|
||||||
// In order for the full purchase not to be worth, we must own 9
|
// In order for the full purchase not to be worth, we must own 9
|
||||||
const partialUnlockPaths = unlockPath.split(',');
|
const partialUnlockPaths = unlockPath.split(',');
|
||||||
unlock(user, { query: { path: partialUnlockPaths[0] } });
|
await unlock(user, { query: { path: partialUnlockPaths[0] } });
|
||||||
unlock(user, { query: { path: partialUnlockPaths[1] } });
|
await unlock(user, { query: { path: partialUnlockPaths[1] } });
|
||||||
unlock(user, { query: { path: partialUnlockPaths[2] } });
|
await unlock(user, { query: { path: partialUnlockPaths[2] } });
|
||||||
unlock(user, { query: { path: partialUnlockPaths[3] } });
|
await unlock(user, { query: { path: partialUnlockPaths[3] } });
|
||||||
unlock(user, { query: { path: partialUnlockPaths[4] } });
|
await unlock(user, { query: { path: partialUnlockPaths[4] } });
|
||||||
unlock(user, { query: { path: partialUnlockPaths[5] } });
|
await unlock(user, { query: { path: partialUnlockPaths[5] } });
|
||||||
unlock(user, { query: { path: partialUnlockPaths[6] } });
|
await unlock(user, { query: { path: partialUnlockPaths[6] } });
|
||||||
unlock(user, { query: { path: partialUnlockPaths[7] } });
|
await unlock(user, { query: { path: partialUnlockPaths[7] } });
|
||||||
unlock(user, { query: { path: partialUnlockPaths[8] } });
|
await unlock(user, { query: { path: partialUnlockPaths[8] } });
|
||||||
|
|
||||||
unlock(user, { query: { path: unlockPath } });
|
await unlock(user, { query: { path: unlockPath } });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||||
expect(err.message).to.equal(i18n.t('alreadyUnlockedPart'));
|
expect(err.message).to.equal(i18n.t('alreadyUnlockedPart'));
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not return an error when user already owns items in a full set and it would not be more expensive to buy the entire set', () => {
|
it('does not return an error when user already owns items in a full set and it would not be more expensive to buy the entire set', async () => {
|
||||||
// There are 11 shirts in the set, each cost 2 gems, the full set 5 gems
|
// There are 11 shirts in the set, each cost 2 gems, the full set 5 gems
|
||||||
// In order for the full purchase to be worth, we can own already 8
|
// In order for the full purchase to be worth, we can own already 8
|
||||||
const partialUnlockPaths = unlockPath.split(',');
|
const partialUnlockPaths = unlockPath.split(',');
|
||||||
unlock(user, { query: { path: partialUnlockPaths[0] } });
|
await unlock(user, { query: { path: partialUnlockPaths[0] } });
|
||||||
unlock(user, { query: { path: partialUnlockPaths[1] } });
|
await unlock(user, { query: { path: partialUnlockPaths[1] } });
|
||||||
unlock(user, { query: { path: partialUnlockPaths[2] } });
|
await unlock(user, { query: { path: partialUnlockPaths[2] } });
|
||||||
unlock(user, { query: { path: partialUnlockPaths[3] } });
|
await unlock(user, { query: { path: partialUnlockPaths[3] } });
|
||||||
unlock(user, { query: { path: partialUnlockPaths[4] } });
|
await unlock(user, { query: { path: partialUnlockPaths[4] } });
|
||||||
unlock(user, { query: { path: partialUnlockPaths[5] } });
|
await unlock(user, { query: { path: partialUnlockPaths[5] } });
|
||||||
unlock(user, { query: { path: partialUnlockPaths[6] } });
|
await unlock(user, { query: { path: partialUnlockPaths[6] } });
|
||||||
unlock(user, { query: { path: partialUnlockPaths[7] } });
|
await unlock(user, { query: { path: partialUnlockPaths[7] } });
|
||||||
|
|
||||||
unlock(user, { query: { path: unlockPath } });
|
await unlock(user, { query: { path: unlockPath } });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('equips an item already owned', () => {
|
it('equips an item already owned', async () => {
|
||||||
expect(user.purchased.background.giant_florals).to.not.exist;
|
expect(user.purchased.background.giant_florals).to.not.exist;
|
||||||
|
|
||||||
unlock(user, { query: { path: backgroundUnlockPath } });
|
await unlock(user, { query: { path: backgroundUnlockPath } });
|
||||||
const afterBalance = user.balance;
|
const afterBalance = user.balance;
|
||||||
const response = unlock(user, { query: { path: backgroundUnlockPath } });
|
const response = await unlock(user, { query: { path: backgroundUnlockPath } });
|
||||||
expect(user.balance).to.equal(afterBalance); // do not bill twice
|
expect(user.balance).to.equal(afterBalance); // do not bill twice
|
||||||
|
|
||||||
expect(response.message).to.not.exist;
|
expect(response.message).to.not.exist;
|
||||||
expect(user.preferences.background).to.equal('giant_florals');
|
expect(user.preferences.background).to.equal('giant_florals');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('un-equips a background already equipped', () => {
|
it('un-equips a background already equipped', async () => {
|
||||||
expect(user.purchased.background.giant_florals).to.not.exist;
|
expect(user.purchased.background.giant_florals).to.not.exist;
|
||||||
|
|
||||||
unlock(user, { query: { path: backgroundUnlockPath } }); // unlock
|
await unlock(user, { query: { path: backgroundUnlockPath } }); // unlock
|
||||||
const afterBalance = user.balance;
|
const afterBalance = user.balance;
|
||||||
unlock(user, { query: { path: backgroundUnlockPath } }); // equip
|
await unlock(user, { query: { path: backgroundUnlockPath } }); // equip
|
||||||
const response = unlock(user, { query: { path: backgroundUnlockPath } });
|
const response = await unlock(user, { query: { path: backgroundUnlockPath } });
|
||||||
expect(user.balance).to.equal(afterBalance); // do not bill twice
|
expect(user.balance).to.equal(afterBalance); // do not bill twice
|
||||||
|
|
||||||
expect(response.message).to.not.exist;
|
expect(response.message).to.not.exist;
|
||||||
expect(user.preferences.background).to.equal('');
|
expect(user.preferences.background).to.equal('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('unlocks a full set of appearance items', () => {
|
it('unlocks a full set of appearance items', async () => {
|
||||||
const initialShirts = Object.keys(user.purchased.shirt).length;
|
const initialShirts = Object.keys(user.purchased.shirt).length;
|
||||||
const [, message] = unlock(user, { query: { path: unlockPath } });
|
const [, message] = await unlock(user, { query: { path: unlockPath } });
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('unlocked'));
|
expect(message).to.equal(i18n.t('unlocked'));
|
||||||
const individualPaths = unlockPath.split(',');
|
const individualPaths = unlockPath.split(',');
|
||||||
@@ -208,11 +197,11 @@ describe('shared.ops.unlock', () => {
|
|||||||
expect(user.balance).to.equal(usersStartingGems - 1.25);
|
expect(user.balance).to.equal(usersStartingGems - 1.25);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('unlocks a full set of hair items', () => {
|
it('unlocks a full set of hair items', async () => {
|
||||||
user.purchased.hair.color = {};
|
user.purchased.hair.color = {};
|
||||||
|
|
||||||
const initialHairColors = Object.keys(user.purchased.hair.color).length;
|
const initialHairColors = Object.keys(user.purchased.hair.color).length;
|
||||||
const [, message] = unlock(user, { query: { path: hairUnlockPath } });
|
const [, message] = await unlock(user, { query: { path: hairUnlockPath } });
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('unlocked'));
|
expect(message).to.equal(i18n.t('unlocked'));
|
||||||
const individualPaths = hairUnlockPath.split(',');
|
const individualPaths = hairUnlockPath.split(',');
|
||||||
@@ -224,13 +213,13 @@ describe('shared.ops.unlock', () => {
|
|||||||
expect(user.balance).to.equal(usersStartingGems - 1.25);
|
expect(user.balance).to.equal(usersStartingGems - 1.25);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('unlocks the facial hair set', () => {
|
it('unlocks the facial hair set', async () => {
|
||||||
user.purchased.hair.mustache = {};
|
user.purchased.hair.mustache = {};
|
||||||
user.purchased.hair.beard = {};
|
user.purchased.hair.beard = {};
|
||||||
|
|
||||||
const initialMustache = Object.keys(user.purchased.hair.mustache).length;
|
const initialMustache = Object.keys(user.purchased.hair.mustache).length;
|
||||||
const initialBeard = Object.keys(user.purchased.hair.mustache).length;
|
const initialBeard = Object.keys(user.purchased.hair.mustache).length;
|
||||||
const [, message] = unlock(user, { query: { path: facialHairUnlockPath } });
|
const [, message] = await unlock(user, { query: { path: facialHairUnlockPath } });
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('unlocked'));
|
expect(message).to.equal(i18n.t('unlocked'));
|
||||||
const individualPaths = facialHairUnlockPath.split(',');
|
const individualPaths = facialHairUnlockPath.split(',');
|
||||||
@@ -242,9 +231,9 @@ describe('shared.ops.unlock', () => {
|
|||||||
expect(user.balance).to.equal(usersStartingGems - 1.25);
|
expect(user.balance).to.equal(usersStartingGems - 1.25);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('unlocks a full set of gear', () => {
|
it('unlocks a full set of gear', async () => {
|
||||||
const initialGear = Object.keys(user.items.gear.owned).length;
|
const initialGear = Object.keys(user.items.gear.owned).length;
|
||||||
const [, message] = unlock(user, { query: { path: unlockGearSetPath } });
|
const [, message] = await unlock(user, { query: { path: unlockGearSetPath } });
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('unlocked'));
|
expect(message).to.equal(i18n.t('unlocked'));
|
||||||
|
|
||||||
@@ -257,9 +246,9 @@ describe('shared.ops.unlock', () => {
|
|||||||
expect(user.balance).to.equal(usersStartingGems - 1.25);
|
expect(user.balance).to.equal(usersStartingGems - 1.25);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('unlocks a full set of backgrounds', () => {
|
it('unlocks a full set of backgrounds', async () => {
|
||||||
const initialBackgrounds = Object.keys(user.purchased.background).length;
|
const initialBackgrounds = Object.keys(user.purchased.background).length;
|
||||||
const [, message] = unlock(user, { query: { path: backgroundSetUnlockPath } });
|
const [, message] = await unlock(user, { query: { path: backgroundSetUnlockPath } });
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('unlocked'));
|
expect(message).to.equal(i18n.t('unlocked'));
|
||||||
const individualPaths = backgroundSetUnlockPath.split(',');
|
const individualPaths = backgroundSetUnlockPath.split(',');
|
||||||
@@ -271,10 +260,10 @@ describe('shared.ops.unlock', () => {
|
|||||||
expect(user.balance).to.equal(usersStartingGems - 3.75);
|
expect(user.balance).to.equal(usersStartingGems - 3.75);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('unlocks an item (appearance)', () => {
|
it('unlocks an item (appearance)', async () => {
|
||||||
const path = unlockPath.split(',')[0];
|
const path = unlockPath.split(',')[0];
|
||||||
const initialShirts = Object.keys(user.purchased.shirt).length;
|
const initialShirts = Object.keys(user.purchased.shirt).length;
|
||||||
const [, message] = unlock(user, { query: { path } });
|
const [, message] = await unlock(user, { query: { path } });
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('unlocked'));
|
expect(message).to.equal(i18n.t('unlocked'));
|
||||||
expect(Object.keys(user.purchased.shirt).length).to.equal(initialShirts + 1);
|
expect(Object.keys(user.purchased.shirt).length).to.equal(initialShirts + 1);
|
||||||
@@ -282,12 +271,12 @@ describe('shared.ops.unlock', () => {
|
|||||||
expect(user.balance).to.equal(usersStartingGems - 0.5);
|
expect(user.balance).to.equal(usersStartingGems - 0.5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('unlocks an item (hair color)', () => {
|
it('unlocks an item (hair color)', async () => {
|
||||||
user.purchased.hair.color = {};
|
user.purchased.hair.color = {};
|
||||||
|
|
||||||
const path = hairUnlockPath.split(',')[0];
|
const path = hairUnlockPath.split(',')[0];
|
||||||
const initialColorHair = Object.keys(user.purchased.hair.color).length;
|
const initialColorHair = Object.keys(user.purchased.hair.color).length;
|
||||||
const [, message] = unlock(user, { query: { path } });
|
const [, message] = await unlock(user, { query: { path } });
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('unlocked'));
|
expect(message).to.equal(i18n.t('unlocked'));
|
||||||
expect(Object.keys(user.purchased.hair.color).length).to.equal(initialColorHair + 1);
|
expect(Object.keys(user.purchased.hair.color).length).to.equal(initialColorHair + 1);
|
||||||
@@ -295,14 +284,14 @@ describe('shared.ops.unlock', () => {
|
|||||||
expect(user.balance).to.equal(usersStartingGems - 0.5);
|
expect(user.balance).to.equal(usersStartingGems - 0.5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('unlocks an item (facial hair)', () => {
|
it('unlocks an item (facial hair)', async () => {
|
||||||
user.purchased.hair.mustache = {};
|
user.purchased.hair.mustache = {};
|
||||||
user.purchased.hair.beard = {};
|
user.purchased.hair.beard = {};
|
||||||
|
|
||||||
const path = facialHairUnlockPath.split(',')[0];
|
const path = facialHairUnlockPath.split(',')[0];
|
||||||
const initialMustache = Object.keys(user.purchased.hair.mustache).length;
|
const initialMustache = Object.keys(user.purchased.hair.mustache).length;
|
||||||
const initialBeard = Object.keys(user.purchased.hair.beard).length;
|
const initialBeard = Object.keys(user.purchased.hair.beard).length;
|
||||||
const [, message] = unlock(user, { query: { path } });
|
const [, message] = await unlock(user, { query: { path } });
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('unlocked'));
|
expect(message).to.equal(i18n.t('unlocked'));
|
||||||
|
|
||||||
@@ -313,10 +302,10 @@ describe('shared.ops.unlock', () => {
|
|||||||
expect(user.balance).to.equal(usersStartingGems - 0.5);
|
expect(user.balance).to.equal(usersStartingGems - 0.5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('unlocks an item (gear)', () => {
|
it('unlocks an item (gear)', async () => {
|
||||||
const path = unlockGearSetPath.split(',')[0];
|
const path = unlockGearSetPath.split(',')[0];
|
||||||
const initialGear = Object.keys(user.items.gear.owned).length;
|
const initialGear = Object.keys(user.items.gear.owned).length;
|
||||||
const [, message] = unlock(user, { query: { path } });
|
const [, message] = await unlock(user, { query: { path } });
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('unlocked'));
|
expect(message).to.equal(i18n.t('unlocked'));
|
||||||
expect(Object.keys(user.items.gear.owned).length).to.equal(initialGear + 1);
|
expect(Object.keys(user.items.gear.owned).length).to.equal(initialGear + 1);
|
||||||
@@ -324,9 +313,9 @@ describe('shared.ops.unlock', () => {
|
|||||||
expect(user.balance).to.equal(usersStartingGems - 0.5);
|
expect(user.balance).to.equal(usersStartingGems - 0.5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('unlocks an item (background)', () => {
|
it('unlocks an item (background)', async () => {
|
||||||
const initialBackgrounds = Object.keys(user.purchased.background).length;
|
const initialBackgrounds = Object.keys(user.purchased.background).length;
|
||||||
const [, message] = unlock(user, { query: { path: backgroundUnlockPath } });
|
const [, message] = await unlock(user, { query: { path: backgroundUnlockPath } });
|
||||||
|
|
||||||
expect(message).to.equal(i18n.t('unlocked'));
|
expect(message).to.equal(i18n.t('unlocked'));
|
||||||
expect(Object.keys(user.purchased.background).length).to.equal(initialBackgrounds + 1);
|
expect(Object.keys(user.purchased.background).length).to.equal(initialBackgrounds + 1);
|
||||||
|
|||||||
@@ -1,205 +1,231 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="row standard-page">
|
<div>
|
||||||
<small
|
<div class="row standard-page">
|
||||||
class="muted"
|
<small
|
||||||
v-html="$t('blurbHallContributors')"
|
class="muted"
|
||||||
></small>
|
v-html="$t('blurbHallContributors')"
|
||||||
<div class="well">
|
></small>
|
||||||
<div v-if="user.contributor.admin">
|
</div>
|
||||||
<h2>Reward User</h2>
|
<div class="row standard-page">
|
||||||
<div class="row">
|
<div>
|
||||||
|
<div v-if="user.contributor.admin">
|
||||||
|
<h2>Reward User</h2>
|
||||||
<div
|
<div
|
||||||
v-if="!hero.profile"
|
v-if="!hero.profile"
|
||||||
class="form col-6"
|
class="row"
|
||||||
>
|
>
|
||||||
<div class="form-group">
|
<div class="form col-6">
|
||||||
<input
|
<div class="form-group">
|
||||||
v-model="heroID"
|
<input
|
||||||
class="form-control"
|
v-model="heroID"
|
||||||
type="text"
|
class="form-control"
|
||||||
:placeholder="'User ID or Username'"
|
type="text"
|
||||||
>
|
:placeholder="'User ID or Username'"
|
||||||
</div>
|
>
|
||||||
<div class="form-group">
|
</div>
|
||||||
<button
|
<div class="form-group">
|
||||||
class="btn btn-secondary"
|
<button
|
||||||
@click="loadHero(heroID)"
|
class="btn btn-secondary"
|
||||||
>
|
@click="loadHero(heroID)"
|
||||||
Load User
|
>
|
||||||
</button>
|
Load User
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div
|
<div
|
||||||
v-if="hero && hero.profile"
|
v-if="hero && hero.profile"
|
||||||
class="form col-6"
|
class="row"
|
||||||
submit="saveHero(hero)"
|
|
||||||
>
|
>
|
||||||
<router-link :to="{'name': 'userProfile', 'params': {'userId': hero._id}}">
|
<div
|
||||||
<h3>@{{ hero.auth.local.username }} / {{ hero.profile.name }}</h3>
|
class="form col-4"
|
||||||
</router-link>
|
submit="saveHero(hero)"
|
||||||
<div class="form-group">
|
>
|
||||||
<label>Contributor Title</label>
|
<router-link :to="{'name': 'userProfile', 'params': {'userId': hero._id}}">
|
||||||
<input
|
<h3>@{{ hero.auth.local.username }} / {{ hero.profile.name }}</h3>
|
||||||
v-model="hero.contributor.text"
|
</router-link>
|
||||||
class="form-control"
|
<div class="form-group">
|
||||||
type="text"
|
<label>Contributor Title</label>
|
||||||
>
|
<input
|
||||||
<small>
|
v-model="hero.contributor.text"
|
||||||
Common titles:
|
class="form-control"
|
||||||
<strong>Ambassador, Artisan, Bard, Blacksmith, Challenger, Comrade, Fletcher, Linguist, Linguistic Scribe, Scribe, Socialite, Storyteller</strong>. Rare titles: Advisor, Chamberlain, Designer, Mathematician, Shirtster, Spokesperson, Statistician, Tinker, Transcriber, Troubadour. <!-- eslint-disable-line max-len -->
|
type="text"
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Contributor Tier</label>
|
|
||||||
<input
|
|
||||||
v-model="hero.contributor.level"
|
|
||||||
class="form-control"
|
|
||||||
type="number"
|
|
||||||
>
|
|
||||||
<small>
|
|
||||||
1-7 for normal contributors, 8 for moderators, 9 for staff. This determines which items, pets, and mounts are available, and name-tag coloring. Tiers 8 and 9 are automatically given admin status. <!-- eslint-disable-line max-len -->
|
|
||||||
<a
|
|
||||||
href="https://habitica.fandom.com/wiki/Contributor_Rewards"
|
|
||||||
target="_blank"
|
|
||||||
>More details</a>
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Contributions</label>
|
|
||||||
<textarea
|
|
||||||
v-model="hero.contributor.contributions"
|
|
||||||
class="form-control"
|
|
||||||
cols="5"
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Balance</label>
|
|
||||||
<input
|
|
||||||
v-model="hero.balance"
|
|
||||||
class="form-control"
|
|
||||||
type="number"
|
|
||||||
step="any"
|
|
||||||
>
|
|
||||||
<small>
|
|
||||||
<span>
|
|
||||||
'{{ hero.balance }}' is in USD,
|
|
||||||
<em>not</em> in Gems. E.g., if this number is 1, it means 4 Gems. Only use this option when manually granting Gems to players, don't use it when granting contributor tiers. Contrib tiers will automatically add Gems. <!-- eslint-disable-line max-len -->
|
|
||||||
</span>
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
<div class="accordion">
|
|
||||||
<div
|
|
||||||
class="accordion-group"
|
|
||||||
heading="Items"
|
|
||||||
>
|
|
||||||
<h4
|
|
||||||
class="expand-toggle"
|
|
||||||
:class="{'open': expandItems}"
|
|
||||||
@click="expandItems = !expandItems"
|
|
||||||
>
|
>
|
||||||
Update Item
|
<small>
|
||||||
</h4>
|
Common titles:
|
||||||
|
<strong>Ambassador, Artisan, Bard, Blacksmith, Challenger, Comrade, Fletcher, Linguist, Linguistic Scribe, Scribe, Socialite, Storyteller</strong>. Rare titles: Advisor, Chamberlain, Designer, Mathematician, Shirtster, Spokesperson, Statistician, Tinker, Transcriber, Troubadour. <!-- eslint-disable-line max-len -->
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Contributor Tier</label>
|
||||||
|
<input
|
||||||
|
v-model="hero.contributor.level"
|
||||||
|
class="form-control"
|
||||||
|
type="number"
|
||||||
|
>
|
||||||
|
<small>
|
||||||
|
1-7 for normal contributors, 8 for moderators, 9 for staff. This determines which items, pets, and mounts are available, and name-tag coloring. Tiers 8 and 9 are automatically given admin status. <!-- eslint-disable-line max-len -->
|
||||||
|
<a
|
||||||
|
href="https://habitica.fandom.com/wiki/Contributor_Rewards"
|
||||||
|
target="_blank"
|
||||||
|
>More details</a>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Contributions</label>
|
||||||
|
<textarea
|
||||||
|
v-model="hero.contributor.contributions"
|
||||||
|
class="form-control"
|
||||||
|
cols="5"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Balance</label>
|
||||||
|
<input
|
||||||
|
v-model="hero.balance"
|
||||||
|
class="form-control"
|
||||||
|
type="number"
|
||||||
|
step="any"
|
||||||
|
>
|
||||||
|
<small>
|
||||||
|
<span>
|
||||||
|
'{{ hero.balance }}' is in USD,
|
||||||
|
<em>not</em> in Gems. E.g., if this number is 1, it means 4 Gems. Only use this option when manually granting Gems to players, don't use it when granting contributor tiers. Contrib tiers will automatically add Gems. <!-- eslint-disable-line max-len -->
|
||||||
|
</span>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<div class="accordion">
|
||||||
<div
|
<div
|
||||||
v-if="expandItems"
|
class="accordion-group"
|
||||||
class="form-group well"
|
heading="Items"
|
||||||
>
|
>
|
||||||
<input
|
<h4
|
||||||
v-model="hero.itemPath"
|
class="expand-toggle"
|
||||||
class="form-control"
|
:class="{'open': expandItems}"
|
||||||
type="text"
|
@click="expandItems = !expandItems"
|
||||||
placeholder="Path (eg, items.pets.BearCub-Base)"
|
|
||||||
>
|
>
|
||||||
<small class="muted">
|
Update Item
|
||||||
Enter the
|
</h4>
|
||||||
<strong>item path</strong>. E.g.,
|
<div
|
||||||
<code>items.pets.BearCub-Zombie</code> or
|
v-if="expandItems"
|
||||||
<code>items.gear.owned.head_special_0</code> or
|
class="form-group well"
|
||||||
<code>items.gear.equipped.head</code>. You can find all the item paths below.
|
|
||||||
</small>
|
|
||||||
<br>
|
|
||||||
<input
|
|
||||||
v-model="hero.itemVal"
|
|
||||||
class="form-control"
|
|
||||||
type="text"
|
|
||||||
placeholder="Value (eg, 5)"
|
|
||||||
>
|
>
|
||||||
<small class="muted">
|
<input
|
||||||
Enter the
|
v-model="hero.itemPath"
|
||||||
<strong>item value</strong>. E.g.,
|
class="form-control"
|
||||||
<code>5</code> or
|
type="text"
|
||||||
<code>false</code> or
|
placeholder="Path (eg, items.pets.BearCub-Base)"
|
||||||
<code>head_warrior_3</code>. All values are listed in the All Item Paths section below. <!-- eslint-disable-line max-len -->
|
|
||||||
</small>
|
|
||||||
<div class="accordion">
|
|
||||||
<div
|
|
||||||
class="accordion-group"
|
|
||||||
heading="All Item Paths"
|
|
||||||
>
|
>
|
||||||
<pre>{{ allItemPaths }}</pre>
|
<small class="muted">
|
||||||
</div>
|
Enter the
|
||||||
<div
|
<strong>item path</strong>. E.g.,
|
||||||
class="accordion-group"
|
<code>items.pets.BearCub-Zombie</code> or
|
||||||
heading="Current Items"
|
<code>items.gear.owned.head_special_0</code> or
|
||||||
|
<code>items.gear.equipped.head</code>. You can find all the item paths below.
|
||||||
|
</small>
|
||||||
|
<br>
|
||||||
|
<input
|
||||||
|
v-model="hero.itemVal"
|
||||||
|
class="form-control"
|
||||||
|
type="text"
|
||||||
|
placeholder="Value (eg, 5)"
|
||||||
>
|
>
|
||||||
<pre>{{ hero.items }}</pre>
|
<small class="muted">
|
||||||
|
Enter the
|
||||||
|
<strong>item value</strong>. E.g.,
|
||||||
|
<code>5</code> or
|
||||||
|
<code>false</code> or
|
||||||
|
<code>head_warrior_3</code>. All values are listed in the All Item Paths section below. <!-- eslint-disable-line max-len -->
|
||||||
|
</small>
|
||||||
|
<div class="accordion">
|
||||||
|
<div
|
||||||
|
class="accordion-group"
|
||||||
|
heading="All Item Paths"
|
||||||
|
>
|
||||||
|
<pre>{{ allItemPaths }}</pre>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="accordion-group"
|
||||||
|
heading="Current Items"
|
||||||
|
>
|
||||||
|
<pre>{{ hero.items }}</pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div
|
||||||
<div
|
class="accordion-group"
|
||||||
class="accordion-group"
|
heading="Auth"
|
||||||
heading="Auth"
|
|
||||||
>
|
|
||||||
<h4
|
|
||||||
class="expand-toggle"
|
|
||||||
:class="{'open': expandAuth}"
|
|
||||||
@click="expandAuth = !expandAuth"
|
|
||||||
>
|
>
|
||||||
Auth
|
<h4
|
||||||
</h4>
|
class="expand-toggle"
|
||||||
<div v-if="expandAuth">
|
:class="{'open': expandAuth}"
|
||||||
<pre>{{ hero.auth }}</pre>
|
@click="expandAuth = !expandAuth"
|
||||||
<div class="form-group">
|
>
|
||||||
<div class="checkbox">
|
Auth
|
||||||
<label>
|
</h4>
|
||||||
<input
|
<div v-if="expandAuth">
|
||||||
v-if="hero.flags"
|
<pre>{{ hero.auth }}</pre>
|
||||||
v-model="hero.flags.chatShadowMuted"
|
<div class="form-group">
|
||||||
type="checkbox"
|
<div class="checkbox">
|
||||||
>
|
<label>
|
||||||
<strong>Chat Shadow Muting On</strong>
|
<input
|
||||||
</label>
|
v-if="hero.flags"
|
||||||
|
v-model="hero.flags.chatShadowMuted"
|
||||||
|
type="checkbox"
|
||||||
|
>
|
||||||
|
<strong>Chat Shadow Muting On</strong>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<div class="checkbox">
|
||||||
<div class="checkbox">
|
<label>
|
||||||
<label>
|
<input
|
||||||
<input
|
v-if="hero.flags"
|
||||||
v-if="hero.flags"
|
v-model="hero.flags.chatRevoked"
|
||||||
v-model="hero.flags.chatRevoked"
|
type="checkbox"
|
||||||
type="checkbox"
|
>
|
||||||
>
|
<strong>Chat Privileges Revoked</strong>
|
||||||
<strong>Chat Privileges Revoked</strong>
|
</label>
|
||||||
</label>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<div class="checkbox">
|
||||||
<div class="checkbox">
|
<label>
|
||||||
<label>
|
<input
|
||||||
<input
|
v-model="hero.auth.blocked"
|
||||||
v-model="hero.auth.blocked"
|
type="checkbox"
|
||||||
type="checkbox"
|
>Blocked
|
||||||
>Blocked
|
</label>
|
||||||
</label>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="accordion-group"
|
||||||
|
heading="Transactions"
|
||||||
|
>
|
||||||
|
<h4
|
||||||
|
class="expand-toggle"
|
||||||
|
:class="{'open': expandTransactions}"
|
||||||
|
@click="toggleTransactionsOpen"
|
||||||
|
>
|
||||||
|
Transactions
|
||||||
|
</h4>
|
||||||
|
<div v-if="expandTransactions">
|
||||||
|
<purchase-history-table
|
||||||
|
:gem-transactions="gemTransactions"
|
||||||
|
:hourglass-transactions="hourglassTransactions"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<!-- h4 Backer Status-->
|
<!-- h4 Backer Status-->
|
||||||
<!-- Add backer stuff like tier, disable adds, etcs-->
|
<!-- Add backer stuff like tier, disable adds, etcs-->
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button
|
<button
|
||||||
class="form-control btn btn-primary"
|
class="form-control btn btn-primary"
|
||||||
@@ -207,10 +233,15 @@
|
|||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
class="form-control btn btn-secondary float-right"
|
||||||
|
@click="clearHero()"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -245,21 +276,40 @@
|
|||||||
<td
|
<td
|
||||||
v-if="user.contributor.admin"
|
v-if="user.contributor.admin"
|
||||||
class="btn-link"
|
class="btn-link"
|
||||||
@click="populateContributorInput(hero._id, index)"
|
:key="hero._id"
|
||||||
>
|
>
|
||||||
{{ hero._id }}
|
<td>
|
||||||
</td>
|
<user-link
|
||||||
<td>{{ hero.contributor.level }}</td>
|
v-if="hero.contributor && hero.contributor.admin"
|
||||||
<td>{{ hero.contributor.text }}</td>
|
:user="hero"
|
||||||
<td>
|
:popover="$t('gamemaster')"
|
||||||
<div
|
popover-trigger="mouseenter"
|
||||||
v-markdown="hero.contributor.contributions"
|
popover-placement="right"
|
||||||
target="_blank"
|
/>
|
||||||
></div>
|
<user-link
|
||||||
</td>
|
v-if="!hero.contributor || !hero.contributor.admin"
|
||||||
</tr>
|
:user="hero"
|
||||||
</tbody>
|
/>
|
||||||
</table>
|
</td>
|
||||||
|
<td
|
||||||
|
v-if="user.contributor.admin"
|
||||||
|
class="btn-link"
|
||||||
|
@click="populateContributorInput(hero._id, index)"
|
||||||
|
>
|
||||||
|
{{ hero._id }}
|
||||||
|
</td>
|
||||||
|
<td>{{ hero.contributor.level }}</td>
|
||||||
|
<td>{{ hero.contributor.text }}</td>
|
||||||
|
<td>
|
||||||
|
<div
|
||||||
|
v-markdown="hero.contributor.contributions"
|
||||||
|
target="_blank"
|
||||||
|
></div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -283,10 +333,12 @@ import content from '@/../../common/script/content';
|
|||||||
import gear from '@/../../common/script/content/gear';
|
import gear from '@/../../common/script/content/gear';
|
||||||
import notifications from '@/mixins/notifications';
|
import notifications from '@/mixins/notifications';
|
||||||
import userLink from '../userLink';
|
import userLink from '../userLink';
|
||||||
|
import PurchaseHistoryTable from '../ui/purchaseHistoryTable.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
userLink,
|
userLink,
|
||||||
|
PurchaseHistoryTable,
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
markdown: markdownDirective,
|
markdown: markdownDirective,
|
||||||
@@ -297,6 +349,8 @@ export default {
|
|||||||
heroes: [],
|
heroes: [],
|
||||||
hero: {},
|
hero: {},
|
||||||
heroID: '',
|
heroID: '',
|
||||||
|
gemTransactions: [],
|
||||||
|
hourglassTransactions: [],
|
||||||
currentHeroIndex: -1,
|
currentHeroIndex: -1,
|
||||||
allItemPaths: this.getAllItemPaths(),
|
allItemPaths: this.getAllItemPaths(),
|
||||||
quests,
|
quests,
|
||||||
@@ -308,6 +362,7 @@ export default {
|
|||||||
gear,
|
gear,
|
||||||
expandItems: false,
|
expandItems: false,
|
||||||
expandAuth: false,
|
expandAuth: false,
|
||||||
|
expandTransactions: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -384,11 +439,24 @@ export default {
|
|||||||
this.heroes[this.currentHeroIndex] = heroUpdated;
|
this.heroes[this.currentHeroIndex] = heroUpdated;
|
||||||
this.currentHeroIndex = -1;
|
this.currentHeroIndex = -1;
|
||||||
},
|
},
|
||||||
|
clearHero () {
|
||||||
|
this.hero = {};
|
||||||
|
this.heroID = -1;
|
||||||
|
this.currentHeroIndex = -1;
|
||||||
|
},
|
||||||
populateContributorInput (id, index) {
|
populateContributorInput (id, index) {
|
||||||
this.heroID = id;
|
this.heroID = id;
|
||||||
window.scrollTo(0, 200);
|
window.scrollTo(0, 200);
|
||||||
this.loadHero(id, index);
|
this.loadHero(id, index);
|
||||||
},
|
},
|
||||||
|
async toggleTransactionsOpen () {
|
||||||
|
this.expandTransactions = !this.expandTransactions;
|
||||||
|
if (this.expandTransactions) {
|
||||||
|
const transactions = await this.$store.dispatch('members:getPurchaseHistory', { memberId: this.hero._id });
|
||||||
|
this.gemTransactions = transactions.filter(transaction => transaction.currency === 'gems');
|
||||||
|
this.hourglassTransactions = transactions.filter(transaction => transaction.currency === 'hourglasses');
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -178,12 +178,12 @@ import sword from '@/assets/svg/sword.svg';
|
|||||||
import { worldStateMixin } from '@/mixins/worldState';
|
import { worldStateMixin } from '@/mixins/worldState';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [
|
|
||||||
worldStateMixin,
|
|
||||||
],
|
|
||||||
components: {
|
components: {
|
||||||
BaseNotification,
|
BaseNotification,
|
||||||
},
|
},
|
||||||
|
mixins: [
|
||||||
|
worldStateMixin,
|
||||||
|
],
|
||||||
data () {
|
data () {
|
||||||
const questData = quests.quests.dysheartener;
|
const questData = quests.quests.dysheartener;
|
||||||
|
|
||||||
|
|||||||
@@ -194,7 +194,10 @@
|
|||||||
<h4 class="popover-content-title">
|
<h4 class="popover-content-title">
|
||||||
{{ context.item.text }}
|
{{ context.item.text }}
|
||||||
</h4>
|
</h4>
|
||||||
<questInfo :quest="context.item" :purchased="true" />
|
<questInfo
|
||||||
|
:quest="context.item"
|
||||||
|
:purchased="true"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<h4 class="popover-content-title">
|
<h4 class="popover-content-title">
|
||||||
|
|||||||
@@ -37,6 +37,14 @@
|
|||||||
>
|
>
|
||||||
{{ $t('subscription') }}
|
{{ $t('subscription') }}
|
||||||
</router-link>
|
</router-link>
|
||||||
|
<router-link
|
||||||
|
v-if="user.contributor.admin"
|
||||||
|
class="nav-link"
|
||||||
|
:to="{name: 'transactions'}"
|
||||||
|
:class="{'active': $route.name === 'transactions'}"
|
||||||
|
>
|
||||||
|
{{ $t('transactions') }}
|
||||||
|
</router-link>
|
||||||
<router-link
|
<router-link
|
||||||
class="nav-link"
|
class="nav-link"
|
||||||
:to="{name: 'notifications'}"
|
:to="{name: 'notifications'}"
|
||||||
@@ -130,6 +138,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState({
|
||||||
currentEventList: 'worldState.data.currentEventList',
|
currentEventList: 'worldState.data.currentEventList',
|
||||||
|
user: 'user.data',
|
||||||
}),
|
}),
|
||||||
currentEvent () {
|
currentEvent () {
|
||||||
return find(this.currentEventList, event => Boolean(event.promo));
|
return find(this.currentEventList, event => Boolean(event.promo));
|
||||||
|
|||||||
38
website/client/src/components/settings/purchaseHistory.vue
Normal file
38
website/client/src/components/settings/purchaseHistory.vue
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<div class="standard-page">
|
||||||
|
<purchase-history-table
|
||||||
|
:gem-transactions="gemTransactions"
|
||||||
|
:hourglass-transactions="hourglassTransactions"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from '@/libs/store';
|
||||||
|
import PurchaseHistoryTable from '../ui/purchaseHistoryTable.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
PurchaseHistoryTable,
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
gemTransactions: [],
|
||||||
|
hourglassTransactions: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({ user: 'user.data' }),
|
||||||
|
},
|
||||||
|
async mounted () {
|
||||||
|
this.$store.dispatch('common:setTitle', {
|
||||||
|
section: this.$t('settings'),
|
||||||
|
subSection: this.$t('transactions'),
|
||||||
|
});
|
||||||
|
|
||||||
|
const history = await this.$store.dispatch('user:getPurchaseHistory');
|
||||||
|
this.gemTransactions = history.filter(transaction => transaction.currency === 'gems');
|
||||||
|
this.hourglassTransactions = history.filter(transaction => transaction.currency === 'hourglasses');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,10 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="notification-animation-holder">
|
<div class="notification-animation-holder">
|
||||||
<div class="notification-holder"
|
<div
|
||||||
@click="handleOnClick()">
|
class="notification-holder"
|
||||||
<div v-if="notification.type === 'drop'"
|
@click="handleOnClick()"
|
||||||
class="icon-item">
|
>
|
||||||
<div :class="notification.icon" class="icon-negative-margin"></div>
|
<div
|
||||||
|
v-if="notification.type === 'drop'"
|
||||||
|
class="icon-item"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
:class="notification.icon"
|
||||||
|
class="icon-negative-margin"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -32,7 +39,10 @@
|
|||||||
class="svg-icon"
|
class="svg-icon"
|
||||||
v-html="icons.gold"
|
v-html="icons.gold"
|
||||||
></div>
|
></div>
|
||||||
<div class="icon-text" v-html="notification.text"></div>
|
<div
|
||||||
|
class="icon-text"
|
||||||
|
v-html="notification.text"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -63,7 +73,10 @@
|
|||||||
class="svg-icon"
|
class="svg-icon"
|
||||||
v-html="icons.mana"
|
v-html="icons.mana"
|
||||||
></div>
|
></div>
|
||||||
<div class="icon-text" v-html="notification.text"></div>
|
<div
|
||||||
|
class="icon-text"
|
||||||
|
v-html="notification.text"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -78,7 +91,10 @@
|
|||||||
class="svg-icon"
|
class="svg-icon"
|
||||||
v-html="icons.sword"
|
v-html="icons.sword"
|
||||||
></div>
|
></div>
|
||||||
<div class="icon-text" v-html="notification.text"></div>
|
<div
|
||||||
|
class="icon-text"
|
||||||
|
v-html="notification.text"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -98,7 +114,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -81,12 +81,12 @@ const DELAY_DELETE_AND_NEW = 60;
|
|||||||
const DELAY_FILLING_ENTRIES = 240;
|
const DELAY_FILLING_ENTRIES = 240;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [
|
|
||||||
worldStateMixin,
|
|
||||||
],
|
|
||||||
components: {
|
components: {
|
||||||
notification,
|
notification,
|
||||||
},
|
},
|
||||||
|
mixins: [
|
||||||
|
worldStateMixin,
|
||||||
|
],
|
||||||
props: {
|
props: {
|
||||||
preventQueue: {
|
preventQueue: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|||||||
155
website/client/src/components/ui/purchaseHistoryTable.vue
Normal file
155
website/client/src/components/ui/purchaseHistoryTable.vue
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
<template>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<h1>{{ $t('gemTransactions') }}</h1>
|
||||||
|
<span v-if="gemTransactions.length === 0">{{ $t('noGemTransactions') }}</span>
|
||||||
|
<table class="table">
|
||||||
|
<tr
|
||||||
|
v-for="entry in gemTransactions"
|
||||||
|
:key="entry.createdAt"
|
||||||
|
>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
v-b-tooltip.hover="entry.createdAt"
|
||||||
|
>{{ entry.createdAt | timeAgo }}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
class="svg-icon inline icon-24"
|
||||||
|
aria-hidden="true"
|
||||||
|
v-html="icons.gem"
|
||||||
|
></span>
|
||||||
|
<span
|
||||||
|
class="amount gems"
|
||||||
|
:class="entry.amount < 0 ? 'deducted' : 'added'"
|
||||||
|
>{{ entry.amount * 4 }}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span>{{ transactionTypeText(entry.transactionType) }}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span v-html="entryReferenceText(entry)"></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<h1>{{ $t('hourglassTransactions') }}</h1>
|
||||||
|
<span v-if="hourglassTransactions.length === 0">{{ $t('noHourglassTransactions') }}</span>
|
||||||
|
<table class="table">
|
||||||
|
<tr
|
||||||
|
v-for="entry in hourglassTransactions"
|
||||||
|
:key="entry.createdAt"
|
||||||
|
>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
v-b-tooltip.hover="entry.createdAt"
|
||||||
|
>{{ entry.createdAt | timeAgo }}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span
|
||||||
|
class="svg-icon inline icon-24"
|
||||||
|
aria-hidden="true"
|
||||||
|
v-html="icons.hourglass"
|
||||||
|
></span>
|
||||||
|
<span
|
||||||
|
class="amount hourglasses"
|
||||||
|
:class="entry.amount < 0 ? 'deducted' : 'added'"
|
||||||
|
>{{ entry.amount }}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span>{{ transactionTypeText(entry.transactionType) }}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span v-html="entryReferenceText(entry)"></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '~@/assets/scss/colors.scss';
|
||||||
|
|
||||||
|
.svg-icon {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.added::before {
|
||||||
|
content: "+";
|
||||||
|
}
|
||||||
|
|
||||||
|
.gems {
|
||||||
|
color: $gems-color;
|
||||||
|
|
||||||
|
&.deducted {
|
||||||
|
color: $red-10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hourglasses {
|
||||||
|
font-weight: bold;
|
||||||
|
color: $hourglass-color;
|
||||||
|
&.deducted {
|
||||||
|
color: $red-10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import moment from 'moment';
|
||||||
|
import svgGem from '@/assets/svg/gem.svg';
|
||||||
|
import svgHourglass from '@/assets/svg/hourglass.svg';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
filters: {
|
||||||
|
timeAgo (value) {
|
||||||
|
return moment(value).fromNow();
|
||||||
|
},
|
||||||
|
date (value) {
|
||||||
|
// @TODO: Vue doesn't support this so we cant user preference
|
||||||
|
return moment(value).toDate().toString();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
gemTransactions: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
hourglassTransactions: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
icons: Object.freeze({
|
||||||
|
gem: svgGem,
|
||||||
|
hourglass: svgHourglass,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
entryReferenceText (entry) {
|
||||||
|
if (entry.reference === undefined && entry.referenceText === undefined) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (entry.referenceText) {
|
||||||
|
return entry.referenceText;
|
||||||
|
}
|
||||||
|
return entry.reference;
|
||||||
|
},
|
||||||
|
transactionTypeText (transactionType) {
|
||||||
|
return this.$t(`transaction_${transactionType}`);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -46,6 +46,7 @@ const Notifications = () => import(/* webpackChunkName: "settings" */'@/componen
|
|||||||
const PromoCode = () => import(/* webpackChunkName: "settings" */'@/components/settings/promoCode');
|
const PromoCode = () => import(/* webpackChunkName: "settings" */'@/components/settings/promoCode');
|
||||||
const Site = () => import(/* webpackChunkName: "settings" */'@/components/settings/site');
|
const Site = () => import(/* webpackChunkName: "settings" */'@/components/settings/site');
|
||||||
const Subscription = () => import(/* webpackChunkName: "settings" */'@/components/settings/subscription');
|
const Subscription = () => import(/* webpackChunkName: "settings" */'@/components/settings/subscription');
|
||||||
|
const Transactions = () => import(/* webpackChunkName: "settings" */'@/components/settings/purchaseHistory');
|
||||||
|
|
||||||
// Hall
|
// Hall
|
||||||
const HallPage = () => import(/* webpackChunkName: "hall" */'@/components/hall/index');
|
const HallPage = () => import(/* webpackChunkName: "hall" */'@/components/hall/index');
|
||||||
@@ -263,6 +264,11 @@ const router = new VueRouter({
|
|||||||
path: 'subscription',
|
path: 'subscription',
|
||||||
component: Subscription,
|
component: Subscription,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'transactions',
|
||||||
|
path: 'transactions',
|
||||||
|
component: Transactions,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'notifications',
|
name: 'notifications',
|
||||||
path: 'notifications',
|
path: 'notifications',
|
||||||
|
|||||||
@@ -113,6 +113,11 @@ export async function removeMember (store, payload) {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getPurchaseHistory (store, payload) {
|
||||||
|
const response = await axios.get(`${apiv4Prefix}/members/${payload.memberId}/purchase-history`);
|
||||||
|
return response.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
// export async function selectMember (uid) {
|
// export async function selectMember (uid) {
|
||||||
// let memberIsReady = _checkIfMemberIsReady(members[uid]);
|
// let memberIsReady = _checkIfMemberIsReady(members[uid]);
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -174,6 +174,12 @@ export function markPrivMessagesRead (store) {
|
|||||||
return axios.post('/api/v4/user/mark-pms-read');
|
return axios.post('/api/v4/user/mark-pms-read');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getPurchaseHistory () {
|
||||||
|
const response = await axios.get('/api/v4/user/purchase-history');
|
||||||
|
return response.data.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function newPrivateMessageTo (store, params) {
|
export function newPrivateMessageTo (store, params) {
|
||||||
const { member } = params;
|
const { member } = params;
|
||||||
|
|
||||||
|
|||||||
@@ -184,5 +184,25 @@
|
|||||||
"suggestMyUsername": "Suggest my username",
|
"suggestMyUsername": "Suggest my username",
|
||||||
"everywhere": "Everywhere",
|
"everywhere": "Everywhere",
|
||||||
"onlyPrivateSpaces": "Only in private spaces",
|
"onlyPrivateSpaces": "Only in private spaces",
|
||||||
"bannedSlurUsedInProfile": "Your Display Name or About text contained a slur, and your chat privileges have been revoked."
|
"bannedSlurUsedInProfile": "Your Display Name or About text contained a slur, and your chat privileges have been revoked.",
|
||||||
|
"transactions": "Transactions",
|
||||||
|
"gemTransactions": "Gem Transactions",
|
||||||
|
"hourglassTransactions": "Hourglass Transactions",
|
||||||
|
"noGemTransactions": "You don't have any gem transactions yet.",
|
||||||
|
"noHourglassTransactions": "You don't have any hourglass transactions yet.",
|
||||||
|
"transaction_debug": "Debug Action",
|
||||||
|
"transaction_buy_money": "Bought with money",
|
||||||
|
"transaction_buy_gold": "Bought with gold",
|
||||||
|
"transaction_contribution": "Through contribution",
|
||||||
|
"transaction_spend": "Spent on",
|
||||||
|
"transaction_gift_send": "Gifted to",
|
||||||
|
"transaction_gift_receive": "Received from",
|
||||||
|
"transaction_create_challenge": "Created challenge",
|
||||||
|
"transaction_create_guild": "Created guild",
|
||||||
|
"transaction_change_class": "Changed class",
|
||||||
|
"transaction_rebirth": "Used orb of rebirth",
|
||||||
|
"transaction_release_pets": "Released pets",
|
||||||
|
"transaction_release_mounts": "Released mounts",
|
||||||
|
"transaction_reroll": "Used fortify potion",
|
||||||
|
"transaction_subscription_perks": "From subscription perk"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import {
|
|||||||
NotImplementedError,
|
NotImplementedError,
|
||||||
BadRequest,
|
BadRequest,
|
||||||
} from '../../libs/errors';
|
} from '../../libs/errors';
|
||||||
|
import updateUserBalance from '../updateUserBalance';
|
||||||
|
import updateUserHourglasses from '../updateUserHourglasses';
|
||||||
|
|
||||||
export class AbstractBuyOperation {
|
export class AbstractBuyOperation {
|
||||||
/**
|
/**
|
||||||
@@ -80,7 +82,7 @@ export class AbstractBuyOperation {
|
|||||||
throw new NotImplementedError('extractAndValidateParams');
|
throw new NotImplementedError('extractAndValidateParams');
|
||||||
}
|
}
|
||||||
|
|
||||||
executeChanges () { // eslint-disable-line class-methods-use-this
|
async executeChanges () { // eslint-disable-line class-methods-use-this
|
||||||
throw new NotImplementedError('executeChanges');
|
throw new NotImplementedError('executeChanges');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,14 +90,14 @@ export class AbstractBuyOperation {
|
|||||||
throw new NotImplementedError('sendToAnalytics');
|
throw new NotImplementedError('sendToAnalytics');
|
||||||
}
|
}
|
||||||
|
|
||||||
purchase () {
|
async purchase () {
|
||||||
if (!this.multiplePurchaseAllowed() && this.quantity > 1) {
|
if (!this.multiplePurchaseAllowed() && this.quantity > 1) {
|
||||||
throw new NotAuthorized(this.i18n('messageNotAbleToBuyInBulk'));
|
throw new NotAuthorized(this.i18n('messageNotAbleToBuyInBulk'));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.extractAndValidateParams(this.user, this.req);
|
this.extractAndValidateParams(this.user, this.req);
|
||||||
|
|
||||||
const resultObj = this.executeChanges(this.user, this.item, this.req, this.analytics);
|
const resultObj = await this.executeChanges(this.user, this.item, this.req, this.analytics);
|
||||||
|
|
||||||
if (this.analytics) {
|
if (this.analytics) {
|
||||||
this.sendToAnalytics(this.analyticsData());
|
this.sendToAnalytics(this.analyticsData());
|
||||||
@@ -141,7 +143,7 @@ export class AbstractGoldItemOperation extends AbstractBuyOperation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
subtractCurrency (user, item) {
|
async subtractCurrency (user, item) {
|
||||||
const itemValue = this.getItemValue(item);
|
const itemValue = this.getItemValue(item);
|
||||||
|
|
||||||
user.stats.gp -= itemValue * this.quantity;
|
user.stats.gp -= itemValue * this.quantity;
|
||||||
@@ -171,10 +173,10 @@ export class AbstractGemItemOperation extends AbstractBuyOperation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
subtractCurrency (user, item) {
|
async subtractCurrency (user, item) {
|
||||||
const itemValue = this.getItemValue(item);
|
const itemValue = this.getItemValue(item);
|
||||||
|
|
||||||
user.balance -= itemValue * this.quantity;
|
await updateUserBalance(user, -(itemValue * this.quantity), 'spend', item.key, item.text());
|
||||||
}
|
}
|
||||||
|
|
||||||
analyticsData () {
|
analyticsData () {
|
||||||
@@ -196,8 +198,8 @@ export class AbstractHourglassItemOperation extends AbstractBuyOperation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
subtractCurrency (user) { // eslint-disable-line class-methods-use-this
|
async subtractCurrency (user, item) { // eslint-disable-line class-methods-use-this
|
||||||
user.purchased.plan.consecutive.trinkets -= 1;
|
await updateUserHourglasses(user, -1, 'spend', item.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
analyticsData () {
|
analyticsData () {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { BuyHourglassMountOperation } from './buyMount';
|
|||||||
|
|
||||||
// @TODO: when we are sure buy is the only function used, let's move the buy files to a folder
|
// @TODO: when we are sure buy is the only function used, let's move the buy files to a folder
|
||||||
|
|
||||||
export default function buy (
|
export default async function buy (
|
||||||
user, req = {}, analytics, options = { quantity: 1, hourglass: false },
|
user, req = {}, analytics, options = { quantity: 1, hourglass: false },
|
||||||
) {
|
) {
|
||||||
const key = get(req, 'params.key');
|
const key = get(req, 'params.key');
|
||||||
@@ -40,35 +40,35 @@ export default function buy (
|
|||||||
case 'armoire': {
|
case 'armoire': {
|
||||||
const buyOp = new BuyArmoireOperation(user, req, analytics);
|
const buyOp = new BuyArmoireOperation(user, req, analytics);
|
||||||
|
|
||||||
buyRes = buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'backgrounds':
|
case 'backgrounds':
|
||||||
if (!hourglass) throw new BadRequest(errorMessage('useUnlockForCosmetics'));
|
if (!hourglass) throw new BadRequest(errorMessage('useUnlockForCosmetics'));
|
||||||
buyRes = hourglassPurchase(user, req, analytics);
|
buyRes = await hourglassPurchase(user, req, analytics);
|
||||||
break;
|
break;
|
||||||
case 'mystery':
|
case 'mystery':
|
||||||
buyRes = buyMysterySet(user, req, analytics);
|
buyRes = await buyMysterySet(user, req, analytics);
|
||||||
break;
|
break;
|
||||||
case 'potion': {
|
case 'potion': {
|
||||||
const buyOp = new BuyHealthPotionOperation(user, req, analytics);
|
const buyOp = new BuyHealthPotionOperation(user, req, analytics);
|
||||||
|
|
||||||
buyRes = buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'gems': {
|
case 'gems': {
|
||||||
const buyOp = new BuyGemOperation(user, req, analytics);
|
const buyOp = new BuyGemOperation(user, req, analytics);
|
||||||
|
|
||||||
buyRes = buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'quests': {
|
case 'quests': {
|
||||||
if (hourglass) {
|
if (hourglass) {
|
||||||
buyRes = hourglassPurchase(user, req, analytics, quantity);
|
buyRes = await hourglassPurchase(user, req, analytics, quantity);
|
||||||
} else {
|
} else {
|
||||||
const buyOp = new BuyQuestWithGemOperation(user, req, analytics);
|
const buyOp = new BuyQuestWithGemOperation(user, req, analytics);
|
||||||
|
|
||||||
buyRes = buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -77,12 +77,12 @@ export default function buy (
|
|||||||
case 'food':
|
case 'food':
|
||||||
case 'gear':
|
case 'gear':
|
||||||
case 'bundles':
|
case 'bundles':
|
||||||
buyRes = purchaseOp(user, req, analytics);
|
buyRes = await purchaseOp(user, req, analytics);
|
||||||
break;
|
break;
|
||||||
case 'mounts': {
|
case 'mounts': {
|
||||||
const buyOp = new BuyHourglassMountOperation(user, req, analytics);
|
const buyOp = new BuyHourglassMountOperation(user, req, analytics);
|
||||||
|
|
||||||
buyRes = buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'pets':
|
case 'pets':
|
||||||
@@ -91,19 +91,19 @@ export default function buy (
|
|||||||
case 'quest': {
|
case 'quest': {
|
||||||
const buyOp = new BuyQuestWithGoldOperation(user, req, analytics);
|
const buyOp = new BuyQuestWithGoldOperation(user, req, analytics);
|
||||||
|
|
||||||
buyRes = buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'special': {
|
case 'special': {
|
||||||
const buyOp = new BuySpellOperation(user, req, analytics);
|
const buyOp = new BuySpellOperation(user, req, analytics);
|
||||||
|
|
||||||
buyRes = buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
const buyOp = new BuyMarketGearOperation(user, req, analytics);
|
const buyOp = new BuyMarketGearOperation(user, req, analytics);
|
||||||
|
|
||||||
buyRes = buyOp.purchase();
|
buyRes = await buyOp.purchase();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
} from '../../libs/errors';
|
} from '../../libs/errors';
|
||||||
import { AbstractGoldItemOperation } from './abstractBuyOperation';
|
import { AbstractGoldItemOperation } from './abstractBuyOperation';
|
||||||
import planGemLimits from '../../libs/planGemLimits';
|
import planGemLimits from '../../libs/planGemLimits';
|
||||||
|
import updateUserBalance from '../updateUserBalance';
|
||||||
|
|
||||||
export class BuyGemOperation extends AbstractGoldItemOperation { // eslint-disable-line import/prefer-default-export, max-len
|
export class BuyGemOperation extends AbstractGoldItemOperation { // eslint-disable-line import/prefer-default-export, max-len
|
||||||
multiplePurchaseAllowed () { // eslint-disable-line class-methods-use-this
|
multiplePurchaseAllowed () { // eslint-disable-line class-methods-use-this
|
||||||
@@ -59,7 +60,7 @@ export class BuyGemOperation extends AbstractGoldItemOperation { // eslint-disab
|
|||||||
}
|
}
|
||||||
|
|
||||||
executeChanges (user, item) {
|
executeChanges (user, item) {
|
||||||
user.balance += 0.25 * this.quantity;
|
updateUserBalance(user, 0.25 * this.quantity, 'buy_gold');
|
||||||
user.purchased.plan.gemsBought += this.quantity;
|
user.purchased.plan.gemsBought += this.quantity;
|
||||||
|
|
||||||
this.subtractCurrency(user, item);
|
this.subtractCurrency(user, item);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export class BuyHourglassMountOperation extends AbstractHourglassItemOperation {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
executeChanges (user) {
|
async executeChanges (user, item) {
|
||||||
user.items.mounts = {
|
user.items.mounts = {
|
||||||
...user.items.mounts,
|
...user.items.mounts,
|
||||||
[this.key]: true,
|
[this.key]: true,
|
||||||
@@ -40,7 +40,7 @@ export class BuyHourglassMountOperation extends AbstractHourglassItemOperation {
|
|||||||
|
|
||||||
if (user.markModified) user.markModified('items.mounts');
|
if (user.markModified) user.markModified('items.mounts');
|
||||||
|
|
||||||
this.subtractCurrency(user);
|
await this.subtractCurrency(user, item);
|
||||||
|
|
||||||
const message = this.i18n('hourglassPurchase');
|
const message = this.i18n('hourglassPurchase');
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ import {
|
|||||||
NotFound,
|
NotFound,
|
||||||
} from '../../libs/errors';
|
} from '../../libs/errors';
|
||||||
import errorMessage from '../../libs/errorMessage';
|
import errorMessage from '../../libs/errorMessage';
|
||||||
|
import updateUserHourglasses from '../updateUserHourglasses';
|
||||||
import { removeItemByPath } from '../pinnedGearUtils';
|
import { removeItemByPath } from '../pinnedGearUtils';
|
||||||
import getItemInfo from '../../libs/getItemInfo';
|
import getItemInfo from '../../libs/getItemInfo';
|
||||||
|
|
||||||
export default function buyMysterySet (user, req = {}, analytics) {
|
export default async function buyMysterySet (user, req = {}, analytics) {
|
||||||
const key = get(req, 'params.key');
|
const key = get(req, 'params.key');
|
||||||
if (!key) throw new BadRequest(errorMessage('missingKeyParam'));
|
if (!key) throw new BadRequest(errorMessage('missingKeyParam'));
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ export default function buyMysterySet (user, req = {}, analytics) {
|
|||||||
|
|
||||||
if (user.markModified) user.markModified('items.gear.owned');
|
if (user.markModified) user.markModified('items.gear.owned');
|
||||||
|
|
||||||
user.purchased.plan.consecutive.trinkets -= 1;
|
await updateUserHourglasses(user, -1, 'spend', mysterySet.text());
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{ items: user.items, purchasedPlanConsecutive: user.purchased.plan.consecutive },
|
{ items: user.items, purchasedPlanConsecutive: user.purchased.plan.consecutive },
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export class BuyQuestWithGemOperation extends AbstractGemItemOperation { // esli
|
|||||||
this.canUserPurchase(user, item);
|
this.canUserPurchase(user, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
executeChanges (user, item, req) {
|
async executeChanges (user, item, req) {
|
||||||
if (
|
if (
|
||||||
!user.items.quests[item.key]
|
!user.items.quests[item.key]
|
||||||
|| user.items.quests[item.key] < 0
|
|| user.items.quests[item.key] < 0
|
||||||
@@ -53,7 +53,7 @@ export class BuyQuestWithGemOperation extends AbstractGemItemOperation { // esli
|
|||||||
};
|
};
|
||||||
if (user.markModified) user.markModified('items.quests');
|
if (user.markModified) user.markModified('items.quests');
|
||||||
|
|
||||||
this.subtractCurrency(user, item, this.quantity);
|
await this.subtractCurrency(user, item, this.quantity);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
user.items.quests,
|
user.items.quests,
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ import {
|
|||||||
import errorMessage from '../../libs/errorMessage';
|
import errorMessage from '../../libs/errorMessage';
|
||||||
import getItemInfo from '../../libs/getItemInfo';
|
import getItemInfo from '../../libs/getItemInfo';
|
||||||
import { removeItemByPath } from '../pinnedGearUtils';
|
import { removeItemByPath } from '../pinnedGearUtils';
|
||||||
|
import updateUserHourglasses from '../updateUserHourglasses';
|
||||||
|
|
||||||
export default function purchaseHourglass (user, req = {}, analytics, quantity = 1) {
|
export default async function purchaseHourglass (user, req = {}, analytics, quantity = 1) {
|
||||||
const key = get(req, 'params.key');
|
const key = get(req, 'params.key');
|
||||||
if (!key) throw new BadRequest(errorMessage('missingKeyParam'));
|
if (!key) throw new BadRequest(errorMessage('missingKeyParam'));
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ export default function purchaseHourglass (user, req = {}, analytics, quantity =
|
|||||||
}
|
}
|
||||||
|
|
||||||
user.purchased.background[key] = true;
|
user.purchased.background[key] = true;
|
||||||
user.purchased.plan.consecutive.trinkets -= 1;
|
await updateUserHourglasses(user, -1, 'spend', key);
|
||||||
const itemInfo = getItemInfo(user, 'background', content.backgroundsFlat[key]);
|
const itemInfo = getItemInfo(user, 'background', content.backgroundsFlat[key]);
|
||||||
removeItemByPath(user, itemInfo.path);
|
removeItemByPath(user, itemInfo.path);
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ export default function purchaseHourglass (user, req = {}, analytics, quantity =
|
|||||||
|
|
||||||
if (!user.items.quests[key] || user.items.quests[key] < 0) user.items.quests[key] = 0;
|
if (!user.items.quests[key] || user.items.quests[key] < 0) user.items.quests[key] = 0;
|
||||||
user.items.quests[key] += quantity;
|
user.items.quests[key] += quantity;
|
||||||
user.purchased.plan.consecutive.trinkets -= quantity;
|
await updateUserHourglasses(user, -quantity, 'spend', key);
|
||||||
|
|
||||||
if (user.markModified) user.markModified('items.quests');
|
if (user.markModified) user.markModified('items.quests');
|
||||||
} else {
|
} else {
|
||||||
@@ -63,7 +64,7 @@ export default function purchaseHourglass (user, req = {}, analytics, quantity =
|
|||||||
throw new NotAuthorized(i18n.t('notEnoughHourglasses', req.language));
|
throw new NotAuthorized(i18n.t('notEnoughHourglasses', req.language));
|
||||||
}
|
}
|
||||||
|
|
||||||
user.purchased.plan.consecutive.trinkets -= 1;
|
await updateUserHourglasses(user, -1, 'spend', key);
|
||||||
|
|
||||||
if (type === 'pets') {
|
if (type === 'pets') {
|
||||||
user.items.pets = {
|
user.items.pets = {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
|
|
||||||
import { removeItemByPath } from '../pinnedGearUtils';
|
import { removeItemByPath } from '../pinnedGearUtils';
|
||||||
import getItemInfo from '../../libs/getItemInfo';
|
import getItemInfo from '../../libs/getItemInfo';
|
||||||
|
import updateUserBalance from '../updateUserBalance';
|
||||||
|
|
||||||
function getItemAndPrice (user, type, key, req) {
|
function getItemAndPrice (user, type, key, req) {
|
||||||
let item;
|
let item;
|
||||||
@@ -42,8 +43,8 @@ function getItemAndPrice (user, type, key, req) {
|
|||||||
return { item, price };
|
return { item, price };
|
||||||
}
|
}
|
||||||
|
|
||||||
function purchaseItem (user, item, price, type, key) {
|
async function purchaseItem (user, item, price, type, key) {
|
||||||
user.balance -= price;
|
await updateUserBalance(user, -price, 'spend', item.key, `${item.text()} ${type}`);
|
||||||
|
|
||||||
if (type === 'gear') {
|
if (type === 'gear') {
|
||||||
user.items.gear.owned = {
|
user.items.gear.owned = {
|
||||||
@@ -74,7 +75,7 @@ function purchaseItem (user, item, price, type, key) {
|
|||||||
|
|
||||||
const acceptedTypes = ['eggs', 'hatchingPotions', 'food', 'gear', 'bundles'];
|
const acceptedTypes = ['eggs', 'hatchingPotions', 'food', 'gear', 'bundles'];
|
||||||
const singlePurchaseTypes = ['gear'];
|
const singlePurchaseTypes = ['gear'];
|
||||||
export default function purchase (user, req = {}, analytics) {
|
export default async function purchase (user, req = {}, analytics) {
|
||||||
const type = get(req.params, 'type');
|
const type = get(req.params, 'type');
|
||||||
const key = get(req.params, 'key');
|
const key = get(req.params, 'key');
|
||||||
|
|
||||||
@@ -108,10 +109,11 @@ export default function purchase (user, req = {}, analytics) {
|
|||||||
removeItemByPath(user, itemInfo.path);
|
removeItemByPath(user, itemInfo.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eslint-disable no-await-in-loop */
|
||||||
for (let i = 0; i < quantity; i += 1) {
|
for (let i = 0; i < quantity; i += 1) {
|
||||||
purchaseItem(user, item, price, type, key);
|
await purchaseItem(user, item, price, type, key);
|
||||||
}
|
}
|
||||||
|
/* eslint-enable no-await-in-loop */
|
||||||
if (analytics) {
|
if (analytics) {
|
||||||
analytics.track('buy', {
|
analytics.track('buy', {
|
||||||
uuid: user._id,
|
uuid: user._id,
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ import {
|
|||||||
BadRequest,
|
BadRequest,
|
||||||
} from '../libs/errors';
|
} from '../libs/errors';
|
||||||
import { removePinnedGearByClass, removePinnedItemsByOwnedGear, addPinnedGearByClass } from './pinnedGearUtils';
|
import { removePinnedGearByClass, removePinnedItemsByOwnedGear, addPinnedGearByClass } from './pinnedGearUtils';
|
||||||
|
import updateUserBalance from './updateUserBalance';
|
||||||
|
|
||||||
function resetClass (user, req = {}) {
|
async function resetClass (user, req = {}) {
|
||||||
removePinnedGearByClass(user);
|
removePinnedGearByClass(user);
|
||||||
|
|
||||||
let balanceRemoved = 0;
|
let balanceRemoved = 0;
|
||||||
@@ -19,7 +20,7 @@ function resetClass (user, req = {}) {
|
|||||||
user.preferences.autoAllocate = false;
|
user.preferences.autoAllocate = false;
|
||||||
} else {
|
} else {
|
||||||
if (user.balance < 0.75) throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
if (user.balance < 0.75) throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||||
user.balance -= 0.75;
|
await updateUserBalance(user, -0.75, 'change_class');
|
||||||
balanceRemoved = 0.75;
|
balanceRemoved = 0.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ function resetClass (user, req = {}) {
|
|||||||
return balanceRemoved;
|
return balanceRemoved;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function changeClass (user, req = {}, analytics) {
|
export default async function changeClass (user, req = {}, analytics) {
|
||||||
const klass = get(req, 'query.class');
|
const klass = get(req, 'query.class');
|
||||||
let balanceRemoved = 0;
|
let balanceRemoved = 0;
|
||||||
// user.flags.classSelected is set to false after the user paid the 3 gems
|
// user.flags.classSelected is set to false after the user paid the 3 gems
|
||||||
@@ -42,10 +43,10 @@ export default function changeClass (user, req = {}, analytics) {
|
|||||||
} else if (!klass) {
|
} else if (!klass) {
|
||||||
// if no class is specified, reset points and set user.flags.classSelected to false.
|
// if no class is specified, reset points and set user.flags.classSelected to false.
|
||||||
// User will have paid 3 gems and will be prompted to select class.
|
// User will have paid 3 gems and will be prompted to select class.
|
||||||
balanceRemoved = resetClass(user, req);
|
balanceRemoved = await resetClass(user, req);
|
||||||
} else if (klass === 'warrior' || klass === 'rogue' || klass === 'wizard' || klass === 'healer') {
|
} else if (klass === 'warrior' || klass === 'rogue' || klass === 'wizard' || klass === 'healer') {
|
||||||
if (user.flags.classSelected) {
|
if (user.flags.classSelected) {
|
||||||
balanceRemoved = resetClass(user, req);
|
balanceRemoved = await resetClass(user, req);
|
||||||
}
|
}
|
||||||
|
|
||||||
user.stats.class = klass;
|
user.stats.class = klass;
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ import equip from './equip';
|
|||||||
import { removePinnedGearByClass } from './pinnedGearUtils';
|
import { removePinnedGearByClass } from './pinnedGearUtils';
|
||||||
import isFreeRebirth from '../libs/isFreeRebirth';
|
import isFreeRebirth from '../libs/isFreeRebirth';
|
||||||
import setDebuffPotionItems from '../libs/setDebuffPotionItems';
|
import setDebuffPotionItems from '../libs/setDebuffPotionItems';
|
||||||
|
import updateUserBalance from './updateUserBalance';
|
||||||
|
|
||||||
const USERSTATSLIST = ['per', 'int', 'con', 'str', 'points', 'gp', 'exp', 'mp'];
|
const USERSTATSLIST = ['per', 'int', 'con', 'str', 'points', 'gp', 'exp', 'mp'];
|
||||||
|
|
||||||
export default function rebirth (user, tasks = [], req = {}, analytics) {
|
export default async function rebirth (user, tasks = [], req = {}, analytics) {
|
||||||
const notFree = !isFreeRebirth(user);
|
const notFree = !isFreeRebirth(user);
|
||||||
|
|
||||||
if (user.balance < 1.5 && notFree) {
|
if (user.balance < 1.5 && notFree) {
|
||||||
@@ -25,7 +26,7 @@ export default function rebirth (user, tasks = [], req = {}, analytics) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (notFree) {
|
if (notFree) {
|
||||||
user.balance -= 1.5;
|
await updateUserBalance(user, -1.5, 'rebirth');
|
||||||
analyticsData.currency = 'Gems';
|
analyticsData.currency = 'Gems';
|
||||||
analyticsData.gemCost = 6;
|
analyticsData.gemCost = 6;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import i18n from '../i18n';
|
|||||||
import {
|
import {
|
||||||
NotAuthorized,
|
NotAuthorized,
|
||||||
} from '../libs/errors';
|
} from '../libs/errors';
|
||||||
|
import updateUserBalance from './updateUserBalance';
|
||||||
|
|
||||||
export default function releaseMounts (user, req = {}, analytics) {
|
export default async function releaseMounts (user, req = {}, analytics) {
|
||||||
if (user.balance < 1) {
|
if (user.balance < 1) {
|
||||||
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||||
}
|
}
|
||||||
@@ -14,7 +15,7 @@ export default function releaseMounts (user, req = {}, analytics) {
|
|||||||
throw new NotAuthorized(i18n.t('notEnoughMounts', req.language));
|
throw new NotAuthorized(i18n.t('notEnoughMounts', req.language));
|
||||||
}
|
}
|
||||||
|
|
||||||
user.balance -= 1;
|
await updateUserBalance(user, -1, 'release_mounts');
|
||||||
|
|
||||||
let giveMountMasterAchievement = true;
|
let giveMountMasterAchievement = true;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import i18n from '../i18n';
|
|||||||
import {
|
import {
|
||||||
NotAuthorized,
|
NotAuthorized,
|
||||||
} from '../libs/errors';
|
} from '../libs/errors';
|
||||||
|
import updateUserBalance from './updateUserBalance';
|
||||||
|
|
||||||
export default function releasePets (user, req = {}, analytics) {
|
export default function releasePets (user, req = {}, analytics) {
|
||||||
if (user.balance < 1) {
|
if (user.balance < 1) {
|
||||||
@@ -14,7 +15,7 @@ export default function releasePets (user, req = {}, analytics) {
|
|||||||
throw new NotAuthorized(i18n.t('notEnoughPets', req.language));
|
throw new NotAuthorized(i18n.t('notEnoughPets', req.language));
|
||||||
}
|
}
|
||||||
|
|
||||||
user.balance -= 1;
|
updateUserBalance(user, -1, 'release_pets');
|
||||||
|
|
||||||
let giveBeastMasterAchievement = true;
|
let giveBeastMasterAchievement = true;
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ import i18n from '../i18n';
|
|||||||
import {
|
import {
|
||||||
NotAuthorized,
|
NotAuthorized,
|
||||||
} from '../libs/errors';
|
} from '../libs/errors';
|
||||||
|
import updateUserBalance from './updateUserBalance';
|
||||||
|
|
||||||
export default function reroll (user, tasks = [], req = {}, analytics) {
|
export default async function reroll (user, tasks = [], req = {}, analytics) {
|
||||||
if (user.balance < 1) {
|
if (user.balance < 1) {
|
||||||
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||||
}
|
}
|
||||||
|
|
||||||
user.balance -= 1;
|
await updateUserBalance(user, -1, 'reroll');
|
||||||
user.stats.hp = 50;
|
user.stats.hp = 50;
|
||||||
|
|
||||||
each(tasks, task => {
|
each(tasks, task => {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { NotAuthorized, BadRequest } from '../libs/errors';
|
|||||||
import { removeItemByPath } from './pinnedGearUtils';
|
import { removeItemByPath } from './pinnedGearUtils';
|
||||||
import getItemInfo from '../libs/getItemInfo';
|
import getItemInfo from '../libs/getItemInfo';
|
||||||
import content from '../content/index';
|
import content from '../content/index';
|
||||||
|
import updateUserBalance from './updateUserBalance';
|
||||||
|
|
||||||
const incentiveBackgrounds = ['blue', 'green', 'red', 'purple', 'yellow'];
|
const incentiveBackgrounds = ['blue', 'green', 'red', 'purple', 'yellow'];
|
||||||
|
|
||||||
@@ -204,7 +205,7 @@ function buildResponse ({ purchased, preference, items }, ownsAlready, language)
|
|||||||
// If item is already purchased -> equip it
|
// If item is already purchased -> equip it
|
||||||
// Otherwise unlock it
|
// Otherwise unlock it
|
||||||
// @TODO refactor and take as parameter the set name, for single items use the buy ops
|
// @TODO refactor and take as parameter the set name, for single items use the buy ops
|
||||||
export default function unlock (user, req = {}, analytics) {
|
export default async function unlock (user, req = {}, analytics) {
|
||||||
const path = get(req.query, 'path');
|
const path = get(req.query, 'path');
|
||||||
|
|
||||||
if (!path) {
|
if (!path) {
|
||||||
@@ -302,7 +303,7 @@ export default function unlock (user, req = {}, analytics) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!unlockedAlready) {
|
if (!unlockedAlready) {
|
||||||
user.balance -= cost;
|
await updateUserBalance(user, -cost, 'spend', path);
|
||||||
|
|
||||||
if (analytics) {
|
if (analytics) {
|
||||||
analytics.track('buy', {
|
analytics.track('buy', {
|
||||||
|
|||||||
11
website/common/script/ops/updateUserBalance.js
Normal file
11
website/common/script/ops/updateUserBalance.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export default async function updateUserBalance (user,
|
||||||
|
amount,
|
||||||
|
transactionType,
|
||||||
|
reference,
|
||||||
|
referenceText) {
|
||||||
|
if (user.constructor.name === 'model') {
|
||||||
|
await user.updateBalance(amount, transactionType, reference, referenceText);
|
||||||
|
} else {
|
||||||
|
user.balance += amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
website/common/script/ops/updateUserHourglasses.js
Normal file
15
website/common/script/ops/updateUserHourglasses.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
export default async function updateUserHourglasses (user,
|
||||||
|
amount,
|
||||||
|
transactionType,
|
||||||
|
reference,
|
||||||
|
referenceText) {
|
||||||
|
if (user.constructor.name === 'model') {
|
||||||
|
await user.purchased.plan.updateHourglasses(user._id,
|
||||||
|
amount,
|
||||||
|
transactionType,
|
||||||
|
reference,
|
||||||
|
referenceText);
|
||||||
|
} else {
|
||||||
|
user.purchased.plan.consecutive.trinkets += amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,7 +34,7 @@ api.addTenGems = {
|
|||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
|
|
||||||
user.balance += 2.5;
|
await user.updateBalance(2.5, 'debug');
|
||||||
|
|
||||||
await user.save();
|
await user.save();
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ api.addHourglass = {
|
|||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
|
|
||||||
user.purchased.plan.consecutive.trinkets += 1;
|
await user.purchased.plan.updateHourglasses(user._id, 1, 'debug');
|
||||||
|
|
||||||
await user.save();
|
await user.save();
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ api.createGroup = {
|
|||||||
|
|
||||||
group.balance = 1;
|
group.balance = 1;
|
||||||
|
|
||||||
user.balance -= 1;
|
await user.updateBalance(-1, 'create_guild', group._id, group.name);
|
||||||
user.guilds.push(group._id);
|
user.guilds.push(group._id);
|
||||||
if (!user.achievements.joinedGuild) {
|
if (!user.achievements.joinedGuild) {
|
||||||
user.achievements.joinedGuild = true;
|
user.achievements.joinedGuild = true;
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ api.updateHero = {
|
|||||||
hero.flags.contributor = true;
|
hero.flags.contributor = true;
|
||||||
let tierDiff = newTier - oldTier; // can be 2+ tier increases at once
|
let tierDiff = newTier - oldTier; // can be 2+ tier increases at once
|
||||||
while (tierDiff) {
|
while (tierDiff) {
|
||||||
hero.balance += gemsPerTier[newTier] / 4; // balance is in $
|
await hero.updateBalance(gemsPerTier[newTier] / 4, 'contribution', newTier); // eslint-disable-line no-await-in-loop
|
||||||
tierDiff -= 1;
|
tierDiff -= 1;
|
||||||
newTier -= 1; // give them gems for the next tier down if they weren't already that tier
|
newTier -= 1; // give them gems for the next tier down if they weren't already that tier
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -714,8 +714,8 @@ api.transferGems = {
|
|||||||
throw new NotAuthorized(res.t('badAmountOfGemsToSend'));
|
throw new NotAuthorized(res.t('badAmountOfGemsToSend'));
|
||||||
}
|
}
|
||||||
|
|
||||||
receiver.balance += amount;
|
await receiver.updateBalance(amount, 'gift_receive', sender._id, sender.profile.name);
|
||||||
sender.balance -= amount;
|
await sender.updateBalance(-amount, 'gift_send', sender._id, receiver.profile.name);
|
||||||
// @TODO necessary? Also saved when sending the inbox message
|
// @TODO necessary? Also saved when sending the inbox message
|
||||||
const promises = [receiver.save(), sender.save()];
|
const promises = [receiver.save(), sender.save()];
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
} from '../../libs/email';
|
} from '../../libs/email';
|
||||||
import * as inboxLib from '../../libs/inbox';
|
import * as inboxLib from '../../libs/inbox';
|
||||||
import * as userLib from '../../libs/user';
|
import * as userLib from '../../libs/user';
|
||||||
|
import logger from '../../libs/logger';
|
||||||
|
|
||||||
const TECH_ASSISTANCE_EMAIL = nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL');
|
const TECH_ASSISTANCE_EMAIL = nconf.get('EMAILS_TECH_ASSISTANCE_EMAIL');
|
||||||
const DELETE_CONFIRMATION = 'DELETE';
|
const DELETE_CONFIRMATION = 'DELETE';
|
||||||
@@ -493,7 +494,7 @@ api.buy = {
|
|||||||
let quantity = 1;
|
let quantity = 1;
|
||||||
if (req.body.quantity) quantity = req.body.quantity;
|
if (req.body.quantity) quantity = req.body.quantity;
|
||||||
req.quantity = quantity;
|
req.quantity = quantity;
|
||||||
const buyRes = common.ops.buy(user, req, res.analytics);
|
const buyRes = await common.ops.buy(user, req, res.analytics);
|
||||||
|
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...buyRes);
|
res.respond(200, ...buyRes);
|
||||||
@@ -541,7 +542,7 @@ api.buyGear = {
|
|||||||
url: '/user/buy-gear/:key',
|
url: '/user/buy-gear/:key',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const buyGearRes = common.ops.buy(user, req, res.analytics);
|
const buyGearRes = await common.ops.buy(user, req, res.analytics);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...buyGearRes);
|
res.respond(200, ...buyGearRes);
|
||||||
},
|
},
|
||||||
@@ -583,7 +584,7 @@ api.buyArmoire = {
|
|||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
req.type = 'armoire';
|
req.type = 'armoire';
|
||||||
req.params.key = 'armoire';
|
req.params.key = 'armoire';
|
||||||
const buyArmoireResponse = common.ops.buy(user, req, res.analytics);
|
const buyArmoireResponse = await common.ops.buy(user, req, res.analytics);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...buyArmoireResponse);
|
res.respond(200, ...buyArmoireResponse);
|
||||||
},
|
},
|
||||||
@@ -623,7 +624,7 @@ api.buyHealthPotion = {
|
|||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
req.type = 'potion';
|
req.type = 'potion';
|
||||||
req.params.key = 'potion';
|
req.params.key = 'potion';
|
||||||
const buyHealthPotionResponse = common.ops.buy(user, req, res.analytics);
|
const buyHealthPotionResponse = await common.ops.buy(user, req, res.analytics);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...buyHealthPotionResponse);
|
res.respond(200, ...buyHealthPotionResponse);
|
||||||
},
|
},
|
||||||
@@ -665,7 +666,7 @@ api.buyMysterySet = {
|
|||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
req.type = 'mystery';
|
req.type = 'mystery';
|
||||||
const buyMysterySetRes = common.ops.buy(user, req, res.analytics);
|
const buyMysterySetRes = await common.ops.buy(user, req, res.analytics);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...buyMysterySetRes);
|
res.respond(200, ...buyMysterySetRes);
|
||||||
},
|
},
|
||||||
@@ -708,7 +709,7 @@ api.buyQuest = {
|
|||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
req.type = 'quest';
|
req.type = 'quest';
|
||||||
const buyQuestRes = common.ops.buy(user, req, res.analytics);
|
const buyQuestRes = await common.ops.buy(user, req, res.analytics);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...buyQuestRes);
|
res.respond(200, ...buyQuestRes);
|
||||||
},
|
},
|
||||||
@@ -750,7 +751,7 @@ api.buySpecialSpell = {
|
|||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
req.type = 'special';
|
req.type = 'special';
|
||||||
const buySpecialSpellRes = common.ops.buy(user, req);
|
const buySpecialSpellRes = await common.ops.buy(user, req);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...buySpecialSpellRes);
|
res.respond(200, ...buySpecialSpellRes);
|
||||||
},
|
},
|
||||||
@@ -941,7 +942,7 @@ api.changeClass = {
|
|||||||
url: '/user/change-class',
|
url: '/user/change-class',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const changeClassRes = common.ops.changeClass(user, req, res.analytics);
|
const changeClassRes = await common.ops.changeClass(user, req, res.analytics);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...changeClassRes);
|
res.respond(200, ...changeClassRes);
|
||||||
},
|
},
|
||||||
@@ -1013,7 +1014,8 @@ api.purchase = {
|
|||||||
if (req.body.quantity) quantity = req.body.quantity;
|
if (req.body.quantity) quantity = req.body.quantity;
|
||||||
req.quantity = quantity;
|
req.quantity = quantity;
|
||||||
|
|
||||||
const purchaseRes = common.ops.buy(user, req, res.analytics);
|
logger.info('AAAAHHHHHH');
|
||||||
|
const purchaseRes = await common.ops.buy(user, req, res.analytics);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...purchaseRes);
|
res.respond(200, ...purchaseRes);
|
||||||
},
|
},
|
||||||
@@ -1053,7 +1055,7 @@ api.userPurchaseHourglass = {
|
|||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const quantity = req.body.quantity || 1;
|
const quantity = req.body.quantity || 1;
|
||||||
if (quantity < 1 || !Number.isInteger(quantity)) throw new BadRequest(res.t('invalidQuantity'), req.language);
|
if (quantity < 1 || !Number.isInteger(quantity)) throw new BadRequest(res.t('invalidQuantity'), req.language);
|
||||||
const purchaseHourglassRes = common.ops.buy(
|
const purchaseHourglassRes = await common.ops.buy(
|
||||||
user,
|
user,
|
||||||
req,
|
req,
|
||||||
res.analytics,
|
res.analytics,
|
||||||
@@ -1185,7 +1187,7 @@ api.userReleasePets = {
|
|||||||
url: '/user/release-pets',
|
url: '/user/release-pets',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const releasePetsRes = common.ops.releasePets(user, req, res.analytics);
|
const releasePetsRes = await common.ops.releasePets(user, req, res.analytics);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...releasePetsRes);
|
res.respond(200, ...releasePetsRes);
|
||||||
},
|
},
|
||||||
@@ -1270,7 +1272,7 @@ api.userReleaseMounts = {
|
|||||||
url: '/user/release-mounts',
|
url: '/user/release-mounts',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const releaseMountsRes = common.ops.releaseMounts(user, req, res.analytics);
|
const releaseMountsRes = await common.ops.releaseMounts(user, req, res.analytics);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...releaseMountsRes);
|
res.respond(200, ...releaseMountsRes);
|
||||||
},
|
},
|
||||||
@@ -1346,7 +1348,7 @@ api.userUnlock = {
|
|||||||
url: '/user/unlock',
|
url: '/user/unlock',
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
const { user } = res.locals;
|
const { user } = res.locals;
|
||||||
const unlockRes = common.ops.unlock(user, req, res.analytics);
|
const unlockRes = await common.ops.unlock(user, req, res.analytics);
|
||||||
await user.save();
|
await user.save();
|
||||||
res.respond(200, ...unlockRes);
|
res.respond(200, ...unlockRes);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { authWithHeaders } from '../../middlewares/auth';
|
import { authWithHeaders } from '../../middlewares/auth';
|
||||||
import { chatReporterFactory } from '../../libs/chatReporting/chatReporterFactory';
|
import { chatReporterFactory } from '../../libs/chatReporting/chatReporterFactory';
|
||||||
|
import { ensureAdmin } from '../../middlewares/ensureAccessRight';
|
||||||
|
import { model as Transaction } from '../../models/transaction';
|
||||||
|
|
||||||
const api = {};
|
const api = {};
|
||||||
|
|
||||||
@@ -48,4 +50,25 @@ api.flagPrivateMessage = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} /api/v4/user/purchase-history Get users purchase history
|
||||||
|
* @apiName UserGetPurchaseHistory
|
||||||
|
* @apiGroup User
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
api.purchaseHistory = {
|
||||||
|
method: 'GET',
|
||||||
|
middlewares: [authWithHeaders(), ensureAdmin],
|
||||||
|
url: '/members/:memberId/purchase-history',
|
||||||
|
async handler (req, res) {
|
||||||
|
req.checkParams('memberId', res.t('memberIdRequired')).notEmpty().isUUID();
|
||||||
|
const validationErrors = req.validationErrors();
|
||||||
|
if (validationErrors) throw validationErrors;
|
||||||
|
const transactions = await Transaction
|
||||||
|
.find({ userId: req.params.memberId })
|
||||||
|
.sort({ createdAt: -1 });
|
||||||
|
res.respond(200, transactions);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export default api;
|
export default api;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { authWithHeaders } from '../../middlewares/auth';
|
|||||||
import * as userLib from '../../libs/user';
|
import * as userLib from '../../libs/user';
|
||||||
import { verifyDisplayName } from '../../libs/user/validation';
|
import { verifyDisplayName } from '../../libs/user/validation';
|
||||||
import common from '../../../common';
|
import common from '../../../common';
|
||||||
|
import { model as Transaction } from '../../models/transaction';
|
||||||
|
|
||||||
const api = {};
|
const api = {};
|
||||||
|
|
||||||
@@ -279,4 +280,21 @@ api.unequip = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @api {get} /api/v4/user/purchase-history Get users purchase history
|
||||||
|
* @apiName UserGetPurchaseHistory
|
||||||
|
* @apiGroup User
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
api.purchaseHistory = {
|
||||||
|
method: 'GET',
|
||||||
|
middlewares: [authWithHeaders()],
|
||||||
|
url: '/user/purchase-history',
|
||||||
|
async handler (req, res) {
|
||||||
|
const { user } = res.locals;
|
||||||
|
const transactions = await Transaction.find({ userId: user._id }).sort({ createdAt: -1 });
|
||||||
|
res.respond(200, transactions);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export default api;
|
export default api;
|
||||||
|
|||||||
@@ -50,6 +50,19 @@ export async function createChallenge (user, req, res) {
|
|||||||
throw new NotAuthorized(res.t('tavChalsMinPrize'));
|
throw new NotAuthorized(res.t('tavChalsMinPrize'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
group.challengeCount += 1;
|
||||||
|
|
||||||
|
if (!req.body.summary) {
|
||||||
|
req.body.summary = req.body.name;
|
||||||
|
}
|
||||||
|
req.body.leader = user._id;
|
||||||
|
req.body.official = !!(user.contributor.admin && req.body.official);
|
||||||
|
const challenge = new Challenge(Challenge.sanitize(req.body));
|
||||||
|
|
||||||
|
// First validate challenge so we don't save group if it's invalid (only runs sync validators)
|
||||||
|
const challengeValidationErrors = challenge.validateSync();
|
||||||
|
if (challengeValidationErrors) throw challengeValidationErrors;
|
||||||
|
|
||||||
if (prize > 0) {
|
if (prize > 0) {
|
||||||
const groupBalance = group.balance && group.leader === user._id ? group.balance : 0;
|
const groupBalance = group.balance && group.leader === user._id ? group.balance : 0;
|
||||||
const prizeCost = prize / 4;
|
const prizeCost = prize / 4;
|
||||||
@@ -65,26 +78,13 @@ export async function createChallenge (user, req, res) {
|
|||||||
// User pays remainder of prize cost after group
|
// User pays remainder of prize cost after group
|
||||||
const remainder = prizeCost - group.balance;
|
const remainder = prizeCost - group.balance;
|
||||||
group.balance = 0;
|
group.balance = 0;
|
||||||
user.balance -= remainder;
|
await user.updateBalance(-remainder, 'create_challenge', challenge._id, challenge.text);
|
||||||
} else {
|
} else {
|
||||||
// User pays for all of prize
|
// User pays for all of prize
|
||||||
user.balance -= prizeCost;
|
await user.updateBalance(-prizeCost, 'create_challenge', challenge._id, challenge.text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
group.challengeCount += 1;
|
|
||||||
|
|
||||||
if (!req.body.summary) {
|
|
||||||
req.body.summary = req.body.name;
|
|
||||||
}
|
|
||||||
req.body.leader = user._id;
|
|
||||||
req.body.official = !!(user.contributor.admin && req.body.official);
|
|
||||||
const challenge = new Challenge(Challenge.sanitize(req.body));
|
|
||||||
|
|
||||||
// First validate challenge so we don't save group if it's invalid (only runs sync validators)
|
|
||||||
const challengeValidationErrors = challenge.validateSync();
|
|
||||||
if (challengeValidationErrors) throw challengeValidationErrors;
|
|
||||||
|
|
||||||
const results = await Promise.all([challenge.save({
|
const results = await Promise.all([challenge.save({
|
||||||
validateBeforeSave: false, // already validated
|
validateBeforeSave: false, // already validated
|
||||||
}), group.save(), user.save()]);
|
}), group.save(), user.save()]);
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const CLEAR_BUFFS = {
|
|||||||
streaks: false,
|
streaks: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
function grantEndOfTheMonthPerks (user, now) {
|
async function grantEndOfTheMonthPerks (user, now) {
|
||||||
// multi-month subscriptions are for multiples of 3 months
|
// multi-month subscriptions are for multiples of 3 months
|
||||||
const SUBSCRIPTION_BASIC_BLOCK_LENGTH = 3;
|
const SUBSCRIPTION_BASIC_BLOCK_LENGTH = 3;
|
||||||
const { plan } = user.purchased;
|
const { plan } = user.purchased;
|
||||||
@@ -135,7 +135,8 @@ function grantEndOfTheMonthPerks (user, now) {
|
|||||||
plan.consecutive.offset = planMonthsLength - 1;
|
plan.consecutive.offset = planMonthsLength - 1;
|
||||||
}
|
}
|
||||||
if (perkAmountNeeded > 0) {
|
if (perkAmountNeeded > 0) {
|
||||||
plan.consecutive.trinkets += perkAmountNeeded; // one Hourglass every 3 months
|
// one Hourglass every 3 months
|
||||||
|
await plan.updateHourglasses(user._id, perkAmountNeeded, 'subscription_perks'); // eslint-disable-line no-await-in-loop
|
||||||
plan.consecutive.gemCapExtra += 5 * perkAmountNeeded; // 5 extra Gems every 3 months
|
plan.consecutive.gemCapExtra += 5 * perkAmountNeeded; // 5 extra Gems every 3 months
|
||||||
// cap it at 50 (hard 25 limit + extra 25)
|
// cap it at 50 (hard 25 limit + extra 25)
|
||||||
if (plan.consecutive.gemCapExtra > 25) plan.consecutive.gemCapExtra = 25;
|
if (plan.consecutive.gemCapExtra > 25) plan.consecutive.gemCapExtra = 25;
|
||||||
@@ -279,7 +280,7 @@ function awardLoginIncentives (user) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Perform various beginning-of-day reset actions.
|
// Perform various beginning-of-day reset actions.
|
||||||
export function cron (options = {}) {
|
export async function cron (options = {}) {
|
||||||
const {
|
const {
|
||||||
user, tasksByType, analytics, now = new Date(), daysMissed, timezoneUtcOffsetFromUserPrefs,
|
user, tasksByType, analytics, now = new Date(), daysMissed, timezoneUtcOffsetFromUserPrefs,
|
||||||
} = options;
|
} = options;
|
||||||
@@ -304,7 +305,7 @@ export function cron (options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (user.isSubscribed()) {
|
if (user.isSubscribed()) {
|
||||||
grantEndOfTheMonthPerks(user, now);
|
await grantEndOfTheMonthPerks(user, now);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { plan } = user.purchased;
|
const { plan } = user.purchased;
|
||||||
|
|||||||
@@ -94,19 +94,19 @@ function getAmountForGems (data) {
|
|||||||
return gemsBlock.gems / 4;
|
return gemsBlock.gems / 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateUserBalance (data, amount) {
|
async function updateUserBalance (data, amount) {
|
||||||
if (data.gift) {
|
if (data.gift) {
|
||||||
data.gift.member.balance += amount;
|
await data.gift.member.updateBalance(amount, 'gift_receive', data.user._id, data.user.profile.name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.user.balance += amount;
|
await data.user.updateBalance(amount, 'buy_money');
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function buyGems (data) {
|
export async function buyGems (data) {
|
||||||
const amt = getAmountForGems(data);
|
const amt = getAmountForGems(data);
|
||||||
|
|
||||||
updateUserBalance(data, amt);
|
await updateUserBalance(data, amt);
|
||||||
data.user.purchased.txnCount += 1;
|
data.user.purchased.txnCount += 1;
|
||||||
|
|
||||||
if (!data.gift) txnEmail(data.user, 'donation');
|
if (!data.gift) txnEmail(data.user, 'donation');
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ async function createSubscription (data) {
|
|||||||
plan.consecutive.offset += months;
|
plan.consecutive.offset += months;
|
||||||
plan.consecutive.gemCapExtra += perks * 5;
|
plan.consecutive.gemCapExtra += perks * 5;
|
||||||
if (plan.consecutive.gemCapExtra > 25) plan.consecutive.gemCapExtra = 25;
|
if (plan.consecutive.gemCapExtra > 25) plan.consecutive.gemCapExtra = 25;
|
||||||
plan.consecutive.trinkets += perks;
|
await plan.updateHourglasses(data.user._id, perks, 'subscription_perks'); // one Hourglass every 3 months
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recipient !== group) {
|
if (recipient !== group) {
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ export async function reroll (req, res, { isV3 = false }) {
|
|||||||
...Tasks.taskIsGroupOrChallengeQuery,
|
...Tasks.taskIsGroupOrChallengeQuery,
|
||||||
};
|
};
|
||||||
const tasks = await Tasks.Task.find(query).exec();
|
const tasks = await Tasks.Task.find(query).exec();
|
||||||
const rerollRes = common.ops.reroll(user, tasks, req, res.analytics);
|
const rerollRes = await common.ops.reroll(user, tasks, req, res.analytics);
|
||||||
if (isV3) {
|
if (isV3) {
|
||||||
rerollRes[0].user = await rerollRes[0].user.toJSONWithInbox();
|
rerollRes[0].user = await rerollRes[0].user.toJSONWithInbox();
|
||||||
}
|
}
|
||||||
@@ -259,7 +259,7 @@ export async function rebirth (req, res, { isV3 = false }) {
|
|||||||
...Tasks.taskIsGroupOrChallengeQuery,
|
...Tasks.taskIsGroupOrChallengeQuery,
|
||||||
}).exec();
|
}).exec();
|
||||||
|
|
||||||
const rebirthRes = common.ops.rebirth(user, tasks, req, res.analytics);
|
const rebirthRes = await common.ops.rebirth(user, tasks, req, res.analytics);
|
||||||
if (isV3) {
|
if (isV3) {
|
||||||
rebirthRes[0].user = await rebirthRes[0].user.toJSONWithInbox();
|
rebirthRes[0].user = await rebirthRes[0].user.toJSONWithInbox();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ async function cronAsync (req, res) {
|
|||||||
tasks.forEach(task => tasksByType[`${task.type}s`].push(task));
|
tasks.forEach(task => tasksByType[`${task.type}s`].push(task));
|
||||||
|
|
||||||
// Run cron
|
// Run cron
|
||||||
const progress = cron({
|
const progress = await cron({
|
||||||
user,
|
user,
|
||||||
tasksByType,
|
tasksByType,
|
||||||
now,
|
now,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
import baseModel from '../libs/baseModel';
|
import baseModel from '../libs/baseModel';
|
||||||
|
import { model as Transaction } from './transaction';
|
||||||
|
|
||||||
export const schema = new mongoose.Schema({
|
export const schema = new mongoose.Schema({
|
||||||
planId: String,
|
planId: String,
|
||||||
@@ -44,4 +45,20 @@ schema.plugin(baseModel, {
|
|||||||
_id: false,
|
_id: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
schema.methods.updateHourglasses = async function updateHourglasses (userId,
|
||||||
|
amount,
|
||||||
|
transactionType,
|
||||||
|
reference,
|
||||||
|
referenceText) {
|
||||||
|
this.consecutive.trinkets += amount;
|
||||||
|
await Transaction.create({
|
||||||
|
currency: 'hourglasses',
|
||||||
|
userId,
|
||||||
|
transactionType,
|
||||||
|
amount,
|
||||||
|
reference,
|
||||||
|
referenceText,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const model = mongoose.model('SubscriptionPlan', schema);
|
export const model = mongoose.model('SubscriptionPlan', schema);
|
||||||
|
|||||||
31
website/server/models/transaction.js
Normal file
31
website/server/models/transaction.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import mongoose from 'mongoose';
|
||||||
|
import validator from 'validator';
|
||||||
|
import baseModel from '../libs/baseModel';
|
||||||
|
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
export const currencies = ['gems', 'hourglasses'];
|
||||||
|
export const transactionTypes = ['buy_money', 'buy_gold', 'contribution', 'spend', 'gift_send', 'gift_receive', 'debug', 'create_challenge', 'create_guild', 'change_class', 'rebirth', 'release_pets', 'release_mounts', 'reroll', 'contribution', 'subscription_perks'];
|
||||||
|
|
||||||
|
export const schema = new Schema({
|
||||||
|
currency: { $type: String, enum: currencies, required: true },
|
||||||
|
transactionType: { $type: String, enum: transactionTypes, required: true },
|
||||||
|
reference: { $type: String },
|
||||||
|
referenceText: { $type: String },
|
||||||
|
amount: { $type: Number, required: true },
|
||||||
|
userId: {
|
||||||
|
$type: String, ref: 'User', required: true, validate: [v => validator.isUUID(v), 'Invalid uuid for Transaction.'],
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
strict: true,
|
||||||
|
minimize: false, // So empty objects are returned
|
||||||
|
typeKey: '$type', // So that we can use fields named `type`
|
||||||
|
});
|
||||||
|
|
||||||
|
schema.plugin(baseModel, {
|
||||||
|
noSet: ['id', '_id', 'userId', 'currency', 'transactionType', 'reference', 'referenceText', 'amount'], // Nothing can be set from the client
|
||||||
|
timestamps: true,
|
||||||
|
_id: false, // using custom _id
|
||||||
|
});
|
||||||
|
|
||||||
|
export const model = mongoose.model('Transaction', schema);
|
||||||
@@ -23,6 +23,7 @@ import amazonPayments from '../../libs/payments/amazon'; // eslint-disable-line
|
|||||||
import stripePayments from '../../libs/payments/stripe'; // eslint-disable-line import/no-cycle
|
import stripePayments from '../../libs/payments/stripe'; // eslint-disable-line import/no-cycle
|
||||||
import paypalPayments from '../../libs/payments/paypal'; // eslint-disable-line import/no-cycle
|
import paypalPayments from '../../libs/payments/paypal'; // eslint-disable-line import/no-cycle
|
||||||
import { model as NewsPost } from '../newsPost';
|
import { model as NewsPost } from '../newsPost';
|
||||||
|
import { model as Transaction } from '../transaction';
|
||||||
|
|
||||||
const { daysSince } = common;
|
const { daysSince } = common;
|
||||||
|
|
||||||
@@ -525,3 +526,30 @@ schema.methods.getSecretData = function getSecretData () {
|
|||||||
|
|
||||||
return user.secret;
|
return user.secret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
schema.methods.updateBalance = async function updateBalance (amount,
|
||||||
|
transactionType,
|
||||||
|
reference,
|
||||||
|
referenceText) {
|
||||||
|
this.balance += amount;
|
||||||
|
|
||||||
|
if (transactionType === 'buy_gold') {
|
||||||
|
// Bulk these together in case the user is not using the bulk-buy feature
|
||||||
|
const lastTransaction = await Transaction.findOne({ userId: this._id },
|
||||||
|
null,
|
||||||
|
{ sort: { createdAt: -1 } });
|
||||||
|
if (lastTransaction.transactionType === transactionType) {
|
||||||
|
lastTransaction.amount += amount;
|
||||||
|
await lastTransaction.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Transaction.create({
|
||||||
|
currency: 'gems',
|
||||||
|
userId: this._id,
|
||||||
|
transactionType,
|
||||||
|
amount,
|
||||||
|
reference,
|
||||||
|
referenceText,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user