Files
habitica/test/common/ops/unlock.test.js
Phillip Thelen eb3220c96b fix lint
2024-06-10 14:46:32 +02:00

358 lines
14 KiB
JavaScript

import get from 'lodash/get';
import unlock from '../../../website/common/script/ops/unlock';
import i18n from '../../../website/common/script/i18n';
import { generateUser } from '../../helpers/common.helper';
import {
NotAuthorized,
BadRequest,
} from '../../../website/common/script/libs/errors';
describe('shared.ops.unlock', () => {
let user;
let clock;
const unlockPath = 'shirt.convict,shirt.fire,shirt.horizon,shirt.ocean,shirt.purple,shirt.rainbow,shirt.redblue,shirt.thunder,shirt.tropical,shirt.zombie';
const unlockGearSetPath = 'items.gear.owned.headAccessory_special_bearEars,items.gear.owned.headAccessory_special_cactusEars,items.gear.owned.headAccessory_special_foxEars,items.gear.owned.headAccessory_special_lionEars,items.gear.owned.headAccessory_special_pandaEars,items.gear.owned.headAccessory_special_pigEars,items.gear.owned.headAccessory_special_tigerEars,items.gear.owned.headAccessory_special_wolfEars';
const backgroundUnlockPath = 'background.giant_florals';
const hairUnlockPath = 'hair.color.rainbow,hair.color.yellow,hair.color.green,hair.color.purple,hair.color.blue,hair.color.TRUred';
const facialHairUnlockPath = 'hair.mustache.1,hair.mustache.2,hair.beard.1,hair.beard.2,hair.beard.3';
const usersStartingGems = 50 / 4;
beforeEach(() => {
user = generateUser();
user.balance = usersStartingGems;
clock = sandbox.useFakeTimers(new Date('2024-04-10'));
});
afterEach(() => {
clock.restore();
});
it('returns an error when path is not provided', async () => {
try {
await unlock(user);
} catch (err) {
expect(err).to.be.an.instanceof(BadRequest);
expect(err.message).to.equal(i18n.t('pathRequired'));
}
});
it('does not unlock lost gear', async () => {
user.items.gear.owned.headAccessory_special_bearEars = false;
await unlock(user, {
query: { path: 'items.gear.owned.headAccessory_special_bearEars' },
});
expect(user.balance).to.equal(usersStartingGems);
});
it('returns an error when user balance is too low', async () => {
user.balance = 0;
try {
await unlock(user, { query: { path: unlockPath } });
} catch (err) {
expect(err).to.be.an.instanceof(NotAuthorized);
expect(err.message).to.equal(i18n.t('notEnoughGems'));
}
});
it('returns an error when user already owns a full set', async () => {
let expectedBalance;
try {
await unlock(user, { query: { path: unlockPath } });
expectedBalance = user.balance;
await unlock(user, { query: { path: unlockPath } });
} catch (err) {
expect(err).to.be.an.instanceof(NotAuthorized);
expect(err.message).to.equal(i18n.t('alreadyUnlocked'));
expect(user.balance).to.equal(expectedBalance);
}
});
it('returns an error when user already owns a full set of gear', async () => {
let expectedBalance;
try {
await unlock(user, { query: { path: unlockGearSetPath } });
expectedBalance = user.balance;
await unlock(user, { query: { path: unlockGearSetPath } });
} catch (err) {
expect(err).to.be.an.instanceof(NotAuthorized);
expect(err.message).to.equal(i18n.t('alreadyUnlocked'));
expect(user.balance).to.equal(expectedBalance);
}
});
it('returns an error if an item does not exists', async () => {
try {
await unlock(user, { query: { path: 'background.invalid_background' } });
} catch (err) {
expect(err).to.be.an.instanceof(BadRequest);
expect(err.message).to.equal(i18n.t('invalidUnlockSet'));
}
});
it('returns an error if there are items from multiple sets', async () => {
try {
await unlock(user, { query: { path: 'shirt.convict,skin.0ff591' } });
} catch (err) {
expect(err).to.be.an.instanceof(BadRequest);
expect(err.message).to.equal(i18n.t('invalidUnlockSet'));
}
});
it('returns an error if gear is not from the animal set', async () => {
try {
await unlock(user, {
query: { path: 'items.gear.owned.back_mystery_202004' },
});
} catch (err) {
expect(err).to.be.an.instanceof(BadRequest);
expect(err.message).to.equal(i18n.t('invalidUnlockSet'));
}
});
it('returns an error if the item is free', async () => {
try {
await unlock(user, { query: { path: 'shirt.black' } });
} catch (err) {
expect(err).to.be.an.instanceof(BadRequest);
expect(err.message).to.equal(i18n.t('invalidUnlockSet'));
}
});
it('returns an error if an item does not belong to a set (appearances)', async () => {
try {
await unlock(user, { query: { path: 'shirt.pink' } });
} catch (err) {
expect(err).to.be.an.instanceof(BadRequest);
expect(err.message).to.equal(i18n.t('invalidUnlockSet'));
}
});
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 {
// 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
const partialUnlockPaths = unlockPath.split(',');
await unlock(user, { query: { path: partialUnlockPaths[0] } });
await unlock(user, { query: { path: partialUnlockPaths[1] } });
await unlock(user, { query: { path: partialUnlockPaths[2] } });
await unlock(user, { query: { path: partialUnlockPaths[3] } });
await unlock(user, { query: { path: partialUnlockPaths[4] } });
await unlock(user, { query: { path: partialUnlockPaths[5] } });
await unlock(user, { query: { path: partialUnlockPaths[6] } });
await unlock(user, { query: { path: partialUnlockPaths[7] } });
await unlock(user, { query: { path: partialUnlockPaths[8] } });
await unlock(user, { query: { path: unlockPath } });
} catch (err) {
expect(err).to.be.an.instanceof(NotAuthorized);
expect(err.message).to.equal(i18n.t('alreadyUnlockedPart'));
}
});
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
// In order for the full purchase to be worth, we can own already 8
const partialUnlockPaths = unlockPath.split(',');
await unlock(user, { query: { path: partialUnlockPaths[0] } });
await unlock(user, { query: { path: partialUnlockPaths[1] } });
await unlock(user, { query: { path: partialUnlockPaths[2] } });
await unlock(user, { query: { path: partialUnlockPaths[3] } });
await unlock(user, { query: { path: partialUnlockPaths[4] } });
await unlock(user, { query: { path: partialUnlockPaths[5] } });
await unlock(user, { query: { path: partialUnlockPaths[6] } });
await unlock(user, { query: { path: unlockPath } });
});
it('equips an item already owned', async () => {
expect(user.purchased.background.giant_florals).to.not.exist;
await unlock(user, { query: { path: backgroundUnlockPath } });
const afterBalance = user.balance;
const response = await unlock(user, {
query: { path: backgroundUnlockPath },
});
expect(user.balance).to.equal(afterBalance); // do not bill twice
expect(response.message).to.not.exist;
expect(user.preferences.background).to.equal('giant_florals');
});
it('un-equips a background already equipped', async () => {
expect(user.purchased.background.giant_florals).to.not.exist;
await unlock(user, { query: { path: backgroundUnlockPath } }); // unlock
const afterBalance = user.balance;
await unlock(user, { query: { path: backgroundUnlockPath } }); // equip
const response = await unlock(user, {
query: { path: backgroundUnlockPath },
});
expect(user.balance).to.equal(afterBalance); // do not bill twice
expect(response.message).to.not.exist;
expect(user.preferences.background).to.equal('');
});
it('unlocks a full set of appearance items', async () => {
const initialShirts = Object.keys(user.purchased.shirt).length;
const [, message] = await unlock(user, { query: { path: unlockPath } });
expect(message).to.equal(i18n.t('unlocked'));
const individualPaths = unlockPath.split(',');
individualPaths.forEach(path => {
expect(get(user.purchased, path)).to.be.true;
});
expect(Object.keys(user.purchased.shirt).length).to.equal(
initialShirts + individualPaths.length,
);
expect(user.balance).to.equal(usersStartingGems - 1.25);
});
it('unlocks a full set of hair items', async () => {
user.purchased.hair.color = {};
const initialHairColors = Object.keys(user.purchased.hair.color).length;
const [, message] = await unlock(user, { query: { path: hairUnlockPath } });
expect(message).to.equal(i18n.t('unlocked'));
const individualPaths = hairUnlockPath.split(',');
individualPaths.forEach(path => {
expect(get(user.purchased, path)).to.be.true;
});
expect(Object.keys(user.purchased.hair.color).length).to.equal(
initialHairColors + individualPaths.length,
);
expect(user.balance).to.equal(usersStartingGems - 1.25);
});
it('unlocks the facial hair set', async () => {
user.purchased.hair.mustache = {};
user.purchased.hair.beard = {};
const initialMustache = Object.keys(user.purchased.hair.mustache).length;
const initialBeard = Object.keys(user.purchased.hair.mustache).length;
const [, message] = await unlock(user, {
query: { path: facialHairUnlockPath },
});
expect(message).to.equal(i18n.t('unlocked'));
const individualPaths = facialHairUnlockPath.split(',');
individualPaths.forEach(path => {
expect(get(user.purchased, path)).to.be.true;
});
expect(
Object.keys(user.purchased.hair.mustache).length
+ Object.keys(user.purchased.hair.beard).length,
) // eslint-disable-line max-len
.to.equal(initialMustache + initialBeard + individualPaths.length);
expect(user.balance).to.equal(usersStartingGems - 1.25);
});
it('unlocks a full set of gear', async () => {
const initialGear = Object.keys(user.items.gear.owned).length;
const [, message] = await unlock(user, {
query: { path: unlockGearSetPath },
});
expect(message).to.equal(i18n.t('unlocked'));
const individualPaths = unlockGearSetPath.split(',');
individualPaths.forEach(path => {
expect(get(user, path)).to.be.true;
});
expect(Object.keys(user.items.gear.owned).length).to.equal(
initialGear + individualPaths.length,
);
expect(user.balance).to.equal(usersStartingGems - 1.25);
});
it('unlocks an item (appearance)', async () => {
const path = unlockPath.split(',')[0];
const initialShirts = Object.keys(user.purchased.shirt).length;
const [, message] = await unlock(user, { query: { path } });
expect(message).to.equal(i18n.t('unlocked'));
expect(Object.keys(user.purchased.shirt).length).to.equal(
initialShirts + 1,
);
expect(get(user.purchased, path)).to.be.true;
expect(user.balance).to.equal(usersStartingGems - 0.5);
});
it('unlocks an item (hair color)', async () => {
user.purchased.hair.color = {};
const path = hairUnlockPath.split(',')[0];
const initialColorHair = Object.keys(user.purchased.hair.color).length;
const [, message] = await unlock(user, { query: { path } });
expect(message).to.equal(i18n.t('unlocked'));
expect(Object.keys(user.purchased.hair.color).length).to.equal(
initialColorHair + 1,
);
expect(get(user.purchased, path)).to.be.true;
expect(user.balance).to.equal(usersStartingGems - 0.5);
});
it('unlocks an item (facial hair)', async () => {
user.purchased.hair.mustache = {};
user.purchased.hair.beard = {};
const path = facialHairUnlockPath.split(',')[0];
const initialMustache = Object.keys(user.purchased.hair.mustache).length;
const initialBeard = Object.keys(user.purchased.hair.beard).length;
const [, message] = await unlock(user, { query: { path } });
expect(message).to.equal(i18n.t('unlocked'));
expect(Object.keys(user.purchased.hair.mustache).length).to.equal(
initialMustache + 1,
);
expect(Object.keys(user.purchased.hair.beard).length).to.equal(
initialBeard,
);
expect(get(user.purchased, path)).to.be.true;
expect(user.balance).to.equal(usersStartingGems - 0.5);
});
it('unlocks an item (gear)', async () => {
const path = unlockGearSetPath.split(',')[0];
const initialGear = Object.keys(user.items.gear.owned).length;
const [, message] = await unlock(user, { query: { path } });
expect(message).to.equal(i18n.t('unlocked'));
expect(Object.keys(user.items.gear.owned).length).to.equal(initialGear + 1);
expect(get(user, path)).to.be.true;
expect(user.balance).to.equal(usersStartingGems - 0.5);
});
it('unlocks an item (background)', async () => {
const initialBackgrounds = Object.keys(user.purchased.background).length;
const [, message] = await unlock(user, {
query: { path: backgroundUnlockPath },
});
expect(message).to.equal(i18n.t('unlocked'));
expect(Object.keys(user.purchased.background).length).to.equal(
initialBackgrounds + 1,
);
expect(get(user.purchased, backgroundUnlockPath)).to.be.true;
expect(user.balance).to.equal(usersStartingGems - 1.75);
});
it('handles an invalid hair path gracefully', async () => {
try {
await unlock(user, { query: { path: 'hair.invalid' } });
} catch (err) {
expect(err).to.be.an.instanceof(BadRequest);
expect(err.message).to.equal(i18n.t('invalidUnlockSet'));
}
});
});