Fix: Antidotes to Avatar Transformation Items should be added to Rewards by API (#11353)

* Fix: moved debuffPotions from vue component

- Move logic of choosing proper debuf potion from vue component to website commons
- introduce new function to get debuffSpellItems

* Fix: move debuffPotions to server

* Refactoring: move setting of debuff potion to func

* Fix: sanity

* Refactoring & Tests:

- Create test case for get and set DebuffPotionItems functions
- Fix setDebuffPotionItems function to not create duplicated debuff items
- Make debuff potion type of items unpinnable
- Move list of debuffs to constant to reuse it in tests and functions

* Fix: typo in test describe

* Fix: translation of unpin

* Fix: setDebuffPotionItems on cron buffs reset

* Fix: use full path for debuff potions
This commit is contained in:
Aleksey
2019-10-06 19:41:39 +03:00
committed by Matteo Pagliazzi
parent 85eab76a71
commit 5b57d91a9b
14 changed files with 214 additions and 35 deletions

View File

@@ -15,7 +15,7 @@ describe('GET /user/toggle-pinned-item', () => {
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('cannotUnpinArmoirPotion'),
message: t('cannotUnpinItem'),
});
});

View File

@@ -0,0 +1,47 @@
import {
generateUser,
} from '../../helpers/common.helper';
import getDebuffPotionItems from '../../../website/common/script/libs/getDebuffPotionItems';
import { TRANSFORMATION_DEBUFFS_LIST } from '../../../website/common/script/constants';
describe('getDebuffPotionItems', () => {
let user;
beforeEach(() => {
user = generateUser();
});
for (let key in TRANSFORMATION_DEBUFFS_LIST) {
const debuff = TRANSFORMATION_DEBUFFS_LIST[key];
// Here we itterate whole object to dynamicaly create test suites as it described in dock of mocha
// https://mochajs.org/#dynamically-generating-tests
// That's why we have eslint-disable here
// eslint-disable-next-line no-loop-func
it(`Should return the ${debuff} on ${key} buff`, () => {
user.stats.buffs[key] = true;
let result = getDebuffPotionItems(user);
expect(result).to.be.an('array').that.deep.includes({path: `spells.special.${debuff}`, type: 'debuffPotion'});
});
}
it('Should return all debuff potions for all buffs', () => {
user.stats.buffs.seafoam = true;
user.stats.buffs.spookySparkles = true;
user.stats.buffs.snowball = true;
user.stats.buffs.shinySeed = true;
let result = getDebuffPotionItems(user);
expect(result).to.be.an('array').that.deep.include.members([
{path: 'spells.special.sand', type: 'debuffPotion'},
{path: 'spells.special.petalFreePotion', type: 'debuffPotion'},
{path: 'spells.special.salt', type: 'debuffPotion'},
{path: 'spells.special.opaquePotion', type: 'debuffPotion'},
]);
});
});

View File

@@ -0,0 +1,53 @@
import {
generateUser,
} from '../../helpers/common.helper';
import setDebuffPotionItems from '../../../website/common/script/libs/setDebuffPotionItems';
describe('setDebuffPotionItems', () => {
let user;
beforeEach(() => {
user = generateUser();
});
it('Should push the debuff item to pinned items of user', () => {
user.stats.buffs.spookySparkles = true;
const previousPinnedItemsLength = user.pinnedItems.length;
let result = setDebuffPotionItems(user);
expect(result.pinnedItems.length).to.be.greaterThan(previousPinnedItemsLength);
});
it('Shouldn\'t create duplicate of already added debuff potion', () => {
user.stats.buffs.spookySparkles = true;
const firstSetResult = [...setDebuffPotionItems(user).pinnedItems];
const secondSetResult = [...setDebuffPotionItems(user).pinnedItems];
expect(firstSetResult).to.be.deep.equal(secondSetResult);
});
it('Should remove all debuff items from pinnedItems of the user if user have no buffs', () => {
user.stats.buffs.seafoam = true;
user.stats.buffs.spookySparkles = true;
user.stats.buffs.snowball = true;
user.stats.buffs.shinySeed = true;
const firstSetResult = [...setDebuffPotionItems(user).pinnedItems];
expect(firstSetResult).to.have.lengthOf(4);
user.stats.buffs.seafoam = false;
user.stats.buffs.spookySparkles = false;
user.stats.buffs.snowball = false;
user.stats.buffs.shinySeed = false;
const secondSetResult = [...setDebuffPotionItems(user).pinnedItems];
expect(secondSetResult).to.have.lengthOf(0);
});
});

View File

@@ -273,7 +273,6 @@ import BuyQuestModal from 'client/components/shops/quests/buyQuestModal.vue';
import notifications from 'client/mixins/notifications';
import { shouldDo } from 'common/script/cron';
import inAppRewards from 'common/script/libs/inAppRewards';
import spells from 'common/script/content/spells';
import taskDefaults from 'common/script/libs/taskDefaults';
import {
@@ -383,26 +382,6 @@ export default {
let watchRefresh = this.forceRefresh; // eslint-disable-line
let rewards = inAppRewards(this.user);
// Add season rewards if user is affected
// @TODO: Add buff conditional
const seasonalSkills = {
snowball: 'salt',
spookySparkles: 'opaquePotion',
shinySeed: 'petalFreePotion',
seafoam: 'sand',
};
for (let key in seasonalSkills) {
if (this.getUserBuffs(key)) {
let debuff = seasonalSkills[key];
let item = Object.assign({}, spells.special[debuff]);
item.text = item.text();
item.notes = item.notes();
item.class = `shop_${key}`;
rewards.push(item);
}
}
return rewards;
},
hasRewardsList () {

View File

@@ -76,7 +76,7 @@
"wrongItemType": "The item type \"<%= type %>\" is not valid.",
"wrongItemPath": "The item path \"<%= path %>\" is not valid.",
"unpinnedItem": "You unpinned <%= item %>! It will no longer display in your Rewards column.",
"cannotUnpinArmoirPotion": "The Health Potion and Enchanted Armoire cannot be unpinned.",
"cannotUnpinItem": "This item cannot be unpinned.",
"purchasedItem": "You bought <%= itemName %>",
"ian": "Ian",

View File

@@ -23,3 +23,10 @@ export const SUPPORTED_SOCIAL_NETWORKS = [
export const GUILDS_PER_PAGE = 30; // number of guilds to return per page when using pagination
export const PARTY_LIMIT_MEMBERS = 30;
export const TRANSFORMATION_DEBUFFS_LIST = {
snowball: 'salt',
spookySparkles: 'opaquePotion',
shinySeed: 'petalFreePotion',
seafoam: 'sand',
};

View File

@@ -289,6 +289,8 @@ spells.special = {
cast (user) {
user.stats.buffs.snowball = false;
user.stats.gp -= 5;
// Remove antidote from pinned items
user.pinnedItems = user.pinnedItems.filter(item => !item.path.includes('spells.special.salt'));
},
},
spookySparkles: {
@@ -320,6 +322,8 @@ spells.special = {
cast (user) {
user.stats.buffs.spookySparkles = false;
user.stats.gp -= 5;
// Remove antidote from pinned items
user.pinnedItems = user.pinnedItems.filter(item => !item.path.includes('spells.special.opaquePotion'));
},
},
shinySeed: {
@@ -351,6 +355,8 @@ spells.special = {
cast (user) {
user.stats.buffs.shinySeed = false;
user.stats.gp -= 5;
// Remove antidote from pinned items
user.pinnedItems = user.pinnedItems.filter(item => !item.path.includes('spells.special.petalFreePotion'));
},
},
seafoam: {
@@ -382,6 +388,7 @@ spells.special = {
cast (user) {
user.stats.buffs.seafoam = false;
user.stats.gp -= 5;
user.pinnedItems = user.pinnedItems.filter(item => !item.path.includes('spells.special.sand'));
},
},
nye: {

View File

@@ -77,6 +77,12 @@ api.updateStore = updateStore;
import inAppRewards from './libs/inAppRewards';
api.inAppRewards = inAppRewards;
import setDebuffPotionItems from './libs/setDebuffPotionItems';
api.setDebuffPotionItems = setDebuffPotionItems;
import getDebuffPotionItems from './libs/getDebuffPotionItems';
api.getDebuffPotionItems = getDebuffPotionItems;
import uuid from './libs/uuid';
api.uuid = uuid;

View File

@@ -0,0 +1,22 @@
import { TRANSFORMATION_DEBUFFS_LIST } from '../constants';
module.exports = function getDebuffPotionItems (user) {
const items = [];
const userBuffs = user.stats.buffs;
if (user) {
for (let key in TRANSFORMATION_DEBUFFS_LIST) {
if (userBuffs[key]) {
let debuff = TRANSFORMATION_DEBUFFS_LIST[key];
const item = {
path: `spells.special.${debuff}`,
type: 'debuffPotion',
};
items.push(item);
}
}
return items;
}
};

View File

@@ -177,6 +177,25 @@ module.exports = function getItemInfo (user, type, item, officialPinnedItems, la
pinType: 'seasonalSpell',
};
break;
case 'debuffPotion':
itemInfo = {
key: item.key,
mana: item.mana,
cast: item.cast,
immediateUse: item.immediateUse,
target: item.target,
text: item.text(language),
notes: item.notes(language),
value: item.value,
type: 'debuffPotion',
currency: 'gold',
locked: false,
purchaseType: 'debuffPotion',
class: `inventory_special_${item.key}`,
path: `spells.special.${item.key}`,
pinType: 'debuffPotion',
};
break;
case 'seasonalQuest':
itemInfo = {
key: item.key,

View File

@@ -0,0 +1,30 @@
import getDebuffPotionItems from './getDebuffPotionItems';
module.exports = function setDebuffPotionItems (user) {
const debuffPotionItems = getDebuffPotionItems(user);
if (debuffPotionItems.length) {
let isPresent = false;
const isUserHaveDebuffInPinnedItems = user.pinnedItems.find(pinnedItem => {
debuffPotionItems.forEach(debuffPotion => {
if (!isPresent) {
isPresent = debuffPotion.path === pinnedItem.path;
}
});
return isPresent;
});
if (!isUserHaveDebuffInPinnedItems) {
user.pinnedItems.push(...debuffPotionItems);
}
} else {
user.pinnedItems = user.pinnedItems.filter(item => {
return item.type !== 'debuffPotion';
});
}
return user;
};

View File

@@ -146,8 +146,9 @@ function togglePinnedItem (user, {item, type, path}, req = {}) {
}
if (path === 'armoire' || path === 'potion') {
throw new BadRequest(i18n.t('cannotUnpinArmoirPotion', req.language));
if (path === 'armoire' || path === 'potion' || type === 'debuffPotion') {
// @TODO: take into considertation debuffPotion type in message
throw new BadRequest(i18n.t('cannotUnpinItem', req.language));
}
const isOfficialPinned = pathExistsInArray(officialPinnedItems, path) !== -1;

View File

@@ -456,6 +456,8 @@ export function cron (options = {}) {
user.stats.buffs = _.cloneDeep(CLEAR_BUFFS);
}
common.setDebuffPotionItems(user);
// Add 10 MP, or 10% of max MP if that'd be more. Perform this after Perfect Day for maximum benefit
// Adjust for fraction of dailies completed
if (!user.preferences.sleep) {

View File

@@ -68,6 +68,9 @@ async function castSelfSpell (req, user, spell, quantity = 1) {
for (let i = 0; i < quantity; i += 1) {
spell.cast(user, null, req);
}
common.setDebuffPotionItems(user);
await user.save();
}
@@ -94,34 +97,37 @@ async function castPartySpell (req, party, partyMembers, user, spell, quantity =
return partyMembers;
}
async function castUserSpell (res, req, party, partyMembers, targetId, user, spell, quantity = 1) {
async function castUserSpell (res, req, party, partyMember, targetId, user, spell, quantity = 1) {
if (!party && (!targetId || user._id === targetId)) {
partyMembers = user;
partyMember = user;
} else {
if (!targetId) throw new BadRequest(res.t('targetIdUUID'));
if (!party) throw new NotFound(res.t('partyNotFound'));
partyMembers = await User
partyMember = await User
.findOne({_id: targetId, 'party._id': party._id})
.select(partyMembersFields)
// We need all fields due to adding debuf spell to pinned items of target of the spell
// .select(partyMembersFields)
.exec();
}
if (!partyMembers) throw new NotFound(res.t('userWithIDNotFound', {userId: targetId}));
if (!partyMember) throw new NotFound(res.t('userWithIDNotFound', {userId: targetId}));
for (let i = 0; i < quantity; i += 1) {
spell.cast(user, partyMembers, req);
spell.cast(user, partyMember, req);
}
if (partyMembers !== user) {
common.setDebuffPotionItems(partyMember);
if (partyMember !== user) {
await Promise.all([
user.save(),
partyMembers.save(),
partyMember.save(),
]);
} else {
await partyMembers.save(); // partyMembers is user
await partyMember.save(); // partyMembers is user
}
return partyMembers;
return partyMember;
}
async function castSpell (req, res, {isV3 = false}) {