habitica/test/common/algos.mocha.js

1522 lines
41 KiB
JavaScript
Raw Normal View History

2015-11-20 02:27:39 +00:00
/* eslint-disable camelcase, func-names, no-shadow */
import {
DAY_MAPPING,
startOfWeek,
startOfDay,
daysSince,
} from '../../common/script/cron';
2015-11-20 02:27:39 +00:00
let expect = require('expect.js');
let sinon = require('sinon');
let moment = require('moment');
let test_helper = require('./test_helper');
let shared = require('../../common/script/index.js');
let $w = (s) => {
2015-11-15 03:44:10 +00:00
return s.split(' ');
};
2015-11-20 02:27:39 +00:00
shared.i18n.translations = require('../../website/src/libs/i18n.js').translations;
test_helper.addCustomMatchers();
2015-11-15 03:44:10 +00:00
/* Helper Functions */
2015-11-20 02:27:39 +00:00
let newUser = (addTasks = true) => {
let buffs = {
2015-11-15 03:44:10 +00:00
per: 0,
int: 0,
con: 0,
str: 0,
stealth: 0,
2015-11-20 02:27:39 +00:00
streaks: false,
2015-11-15 03:44:10 +00:00
};
2015-11-20 02:27:39 +00:00
let user = {
2015-11-15 03:44:10 +00:00
auth: {
2015-11-20 02:27:39 +00:00
timestamps: {},
2015-11-15 03:44:10 +00:00
},
stats: {
str: 1,
con: 1,
per: 1,
int: 1,
mp: 32,
2015-11-20 02:27:39 +00:00
class: 'warrior',
buffs,
2015-11-15 03:44:10 +00:00
},
items: {
lastDrop: {
2015-11-20 02:27:39 +00:00
count: 0,
2015-11-15 03:44:10 +00:00
},
hatchingPotions: {},
eggs: {},
food: {},
gear: {
equipped: {},
costume: {},
2015-11-20 02:27:39 +00:00
owned: {},
2015-11-15 03:44:10 +00:00
},
2015-11-20 02:27:39 +00:00
quests: {},
2015-11-15 03:44:10 +00:00
},
party: {
quest: {
progress: {
2015-11-20 02:27:39 +00:00
down: 0,
},
},
2015-11-15 03:44:10 +00:00
},
preferences: {
2015-11-20 02:27:39 +00:00
autoEquip: true,
2015-11-15 03:44:10 +00:00
},
dailys: [],
todos: [],
rewards: [],
flags: {},
achievements: {
2015-11-20 02:27:39 +00:00
ultimateGearSets: {},
2015-11-15 03:44:10 +00:00
},
contributor: {
2015-11-20 02:27:39 +00:00
level: 2,
2015-11-15 03:44:10 +00:00
},
2015-11-20 02:27:39 +00:00
_tmp: {},
2015-11-15 03:44:10 +00:00
};
2015-11-20 02:27:39 +00:00
2015-11-15 03:44:10 +00:00
shared.wrap(user);
2015-11-20 02:27:39 +00:00
user.ops.reset(null, () => {});
2015-11-15 03:44:10 +00:00
if (addTasks) {
2015-11-20 02:27:39 +00:00
_.each(['habit', 'todo', 'daily'], (task) => {
user.ops.addTask({
2015-11-15 03:44:10 +00:00
body: {
type: task,
2015-11-20 02:27:39 +00:00
id: shared.uuid(),
},
2015-11-15 03:44:10 +00:00
});
});
}
return user;
};
2015-11-20 02:27:39 +00:00
let rewrapUser = (user) => {
2015-11-15 03:44:10 +00:00
user._wrapped = false;
shared.wrap(user);
return user;
};
2015-11-20 02:27:39 +00:00
let beforeAfter = (options = {}) => {
let lastCron;
let user = newUser();
let ref = [user, _.cloneDeep(user)];
let before = ref[0];
let after = ref[1];
2015-11-15 03:44:10 +00:00
rewrapUser(after);
if (options.dayStart) {
before.preferences.dayStart = after.preferences.dayStart = options.dayStart;
}
before.preferences.timezoneOffset = after.preferences.timezoneOffset = options.timezoneOffset || moment().zone();
if (options.limitOne) {
2015-11-20 02:27:39 +00:00
before[`${options.limitOne}s`] = [before[`${options.limitOne}s`][0]];
after[`${options.limitOne}s`] = [after[`${options.limitOne}s`][0]];
2015-11-15 03:44:10 +00:00
}
if (options.daysAgo) {
2015-11-20 02:27:39 +00:00
lastCron = moment(options.now || Number(new Date())).subtract({
days: options.daysAgo,
2015-11-15 03:44:10 +00:00
});
}
if (options.daysAgo && options.cronAfterStart) {
lastCron.add({
hours: options.dayStart,
2015-11-20 02:27:39 +00:00
minutes: 1,
2015-11-15 03:44:10 +00:00
});
}
if (options.daysAgo) {
2015-11-20 02:27:39 +00:00
lastCron = Number(lastCron);
2015-11-15 03:44:10 +00:00
}
2015-11-20 02:27:39 +00:00
_.each([before, after], (obj) => {
2015-11-15 03:44:10 +00:00
if (options.daysAgo) {
2015-11-20 02:27:39 +00:00
obj.lastCron = lastCron;
2015-11-15 03:44:10 +00:00
}
});
return {
2015-11-20 02:27:39 +00:00
before,
after,
2015-11-15 03:44:10 +00:00
};
};
2015-11-20 02:27:39 +00:00
let expectLostPoints = (before, after, taskType) => {
2015-11-15 03:44:10 +00:00
if (taskType === 'daily' || taskType === 'habit') {
expect(after.stats.hp).to.be.lessThan(before.stats.hp);
2015-11-20 02:27:39 +00:00
expect(after[`${taskType}s`][0].history).to.have.length(1);
2015-11-15 03:44:10 +00:00
} else {
expect(after.history.todos).to.have.length(1);
}
expect(after).toHaveExp(0);
expect(after).toHaveGP(0);
2015-11-20 02:27:39 +00:00
expect(after[`${taskType}s`][0].value).to.be.lessThan(before[`${taskType}s`][0].value);
2015-11-15 03:44:10 +00:00
};
2015-11-20 02:27:39 +00:00
let expectGainedPoints = (before, after, taskType) => {
2015-11-15 03:44:10 +00:00
expect(after.stats.hp).to.be(50);
expect(after.stats.exp).to.be.greaterThan(before.stats.exp);
expect(after.stats.gp).to.be.greaterThan(before.stats.gp);
2015-11-20 02:27:39 +00:00
expect(after[`${taskType}s`][0].value).to.be.greaterThan(before[`${taskType}s`][0].value);
2015-11-15 03:44:10 +00:00
if (taskType === 'habit') {
2015-11-20 02:27:39 +00:00
expect(after[`${taskType}s`][0].history).to.have.length(1);
2015-11-15 03:44:10 +00:00
}
};
2015-11-20 02:27:39 +00:00
let expectNoChange = (before, after) => {
_.each($w('stats items gear dailys todos rewards preferences'), (attr) => {
expect(after[attr]).to.eql(before[attr]);
2015-11-15 03:44:10 +00:00
});
};
2015-11-20 02:27:39 +00:00
let expectClosePoints = (before, after, taskType) => {
2015-11-15 03:44:10 +00:00
expect(Math.abs(after.stats.exp - before.stats.exp)).to.be.lessThan(0.0001);
expect(Math.abs(after.stats.gp - before.stats.gp)).to.be.lessThan(0.0001);
2015-11-20 02:27:39 +00:00
expect(Math.abs(after[taskType + 's'][0].value - before[taskType + 's'][0].value)).to.be.lessThan(0.0001); // eslint-disable-line prefer-template
2015-11-15 03:44:10 +00:00
};
2015-11-20 02:27:39 +00:00
let expectDayResetNoDamage = (b, a) => {
let ref = [_.cloneDeep(b), _.cloneDeep(a)];
let before = ref[0];
let after = ref[1];
_.each(after.dailys, (task, i) => {
2015-11-15 03:44:10 +00:00
expect(task.completed).to.be(false);
expect(before.dailys[i].value).to.be(task.value);
expect(before.dailys[i].streak).to.be(task.streak);
2015-11-20 02:27:39 +00:00
expect(task.history).to.have.length(1);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
_.each(after.todos, (task, i) => {
2015-11-15 03:44:10 +00:00
expect(task.completed).to.be(false);
2015-11-20 02:27:39 +00:00
expect(before.todos[i].value).to.be.greaterThan(task.value);
2015-11-15 03:44:10 +00:00
});
expect(after.history.todos).to.have.length(1);
2015-11-20 02:27:39 +00:00
_.each([before, after], (obj) => {
2015-11-15 03:44:10 +00:00
delete obj.stats.buffs;
2015-11-20 02:27:39 +00:00
_.each($w('dailys todos history lastCron'), (path) => {
2015-11-15 03:44:10 +00:00
return delete obj[path];
});
});
delete after._tmp;
2015-11-20 02:27:39 +00:00
expectNoChange(before, after);
2015-11-15 03:44:10 +00:00
};
2015-11-20 02:27:39 +00:00
let repeatWithoutLastWeekday = () => {
let repeat = {
2015-11-15 03:44:10 +00:00
su: true,
m: true,
t: true,
w: true,
th: true,
f: true,
2015-11-20 02:27:39 +00:00
s: true,
2015-11-15 03:44:10 +00:00
};
2015-11-20 02:27:39 +00:00
if (startOfWeek(moment().zone(0)).isoWeekday() === 1) {
2015-11-15 03:44:10 +00:00
repeat.su = false;
} else {
repeat.s = false;
}
return {
2015-11-20 02:27:39 +00:00
repeat,
2015-11-15 03:44:10 +00:00
};
};
2015-11-20 02:27:39 +00:00
describe('User', () => {
it('sets correct user defaults', () => {
let user = newUser();
let base_gear = {
2015-11-15 03:44:10 +00:00
armor: 'armor_base_0',
weapon: 'weapon_base_0',
head: 'head_base_0',
2015-11-20 02:27:39 +00:00
shield: 'shield_base_0',
2015-11-15 03:44:10 +00:00
};
2015-11-20 02:27:39 +00:00
let buffs = {
2015-11-15 03:44:10 +00:00
per: 0,
int: 0,
con: 0,
str: 0,
stealth: 0,
2015-11-20 02:27:39 +00:00
streaks: false,
2015-11-15 03:44:10 +00:00
};
2015-11-20 02:27:39 +00:00
2015-11-15 03:44:10 +00:00
expect(user.stats).to.eql({
str: 1,
con: 1,
per: 1,
int: 1,
hp: 50,
mp: 32,
lvl: 1,
exp: 0,
gp: 0,
2015-11-20 02:27:39 +00:00
class: 'warrior',
buffs,
2015-11-15 03:44:10 +00:00
});
expect(user.items.gear).to.eql({
equipped: base_gear,
costume: base_gear,
owned: {
2015-11-20 02:27:39 +00:00
weapon_warrior_0: true,
},
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expect(user.preferences).to.eql({
2015-11-15 03:44:10 +00:00
autoEquip: true,
2015-11-20 02:27:39 +00:00
costume: false,
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
it('calculates max MP', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
expect(user).toHaveMaxMP(32);
user.stats.int = 10;
expect(user).toHaveMaxMP(50);
user.stats.lvl = 5;
expect(user).toHaveMaxMP(54);
2015-11-20 02:27:39 +00:00
user.stats.class = 'wizard';
2015-11-15 03:44:10 +00:00
user.items.gear.equipped.weapon = 'weapon_wizard_1';
2015-11-20 02:27:39 +00:00
expect(user).toHaveMaxMP(63);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('handles perfect days', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.dailys = [];
2015-11-20 02:27:39 +00:00
_.times(3, () => {
2015-11-15 03:44:10 +00:00
return user.dailys.push(shared.taskDefaults({
type: 'daily',
2015-11-20 02:27:39 +00:00
startDate: moment().subtract(7, 'days'),
2015-11-15 03:44:10 +00:00
}));
});
2015-11-20 02:27:39 +00:00
let cron = () => {
2015-11-15 03:44:10 +00:00
user.lastCron = moment().subtract(1, 'days');
return user.fns.cron();
};
2015-11-20 02:27:39 +00:00
2015-11-15 03:44:10 +00:00
cron();
expect(user.stats.buffs.str).to.be(0);
expect(user.achievements.perfect).to.not.be.ok();
user.dailys[0].completed = true;
cron();
expect(user.stats.buffs.str).to.be(0);
expect(user.achievements.perfect).to.not.be.ok();
2015-11-20 02:27:39 +00:00
_.each(user.dailys, (d) => {
d.completed = true;
2015-11-15 03:44:10 +00:00
});
cron();
expect(user.stats.buffs.str).to.be(1);
expect(user.achievements.perfect).to.be(1);
2015-11-20 02:27:39 +00:00
let yesterday = moment().subtract(1, 'days');
user.dailys[0].repeat[DAY_MAPPING[yesterday.day()]] = false;
2015-11-20 02:27:39 +00:00
_.each(user.dailys.slice(1), (d) => {
d.completed = true;
2015-11-15 03:44:10 +00:00
});
cron();
expect(user.stats.buffs.str).to.be(1);
2015-11-20 02:27:39 +00:00
expect(user.achievements.perfect).to.be(2);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
describe('Resting in the Inn', () => {
let user = null;
let cron = null;
beforeEach(() => {
2015-11-15 03:44:10 +00:00
user = newUser();
user.preferences.sleep = true;
2015-11-20 02:27:39 +00:00
cron = () => {
2015-11-15 03:44:10 +00:00
user.lastCron = moment().subtract(1, 'days');
return user.fns.cron();
};
user.dailys = [];
2015-11-20 02:27:39 +00:00
_.times(2, () => {
2015-11-15 03:44:10 +00:00
return user.dailys.push(shared.taskDefaults({
type: 'daily',
2015-11-20 02:27:39 +00:00
startDate: moment().subtract(7, 'days'),
2015-11-15 03:44:10 +00:00
}));
});
});
2015-11-20 02:27:39 +00:00
it('remains in the inn on cron', () => {
2015-11-15 03:44:10 +00:00
cron();
2015-11-20 02:27:39 +00:00
expect(user.preferences.sleep).to.be(true);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('resets dailies', () => {
2015-11-15 03:44:10 +00:00
user.dailys[0].completed = true;
cron();
2015-11-20 02:27:39 +00:00
expect(user.dailys[0].completed).to.be(false);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('resets checklist on incomplete dailies', () => {
2015-11-15 03:44:10 +00:00
user.dailys[0].checklist = [
{
2015-11-20 02:27:39 +00:00
text: '1',
id: 'checklist-one',
completed: true,
2015-11-15 03:44:10 +00:00
}, {
2015-11-20 02:27:39 +00:00
text: '2',
id: 'checklist-two',
completed: true,
2015-11-15 03:44:10 +00:00
}, {
2015-11-20 02:27:39 +00:00
text: '3',
id: 'checklist-three',
completed: false,
},
2015-11-15 03:44:10 +00:00
];
cron();
2015-11-20 02:27:39 +00:00
_.each(user.dailys[0].checklist, (box) => {
expect(box.completed).to.be(false);
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
it('resets checklist on complete dailies', () => {
2015-11-15 03:44:10 +00:00
user.dailys[0].checklist = [
{
2015-11-20 02:27:39 +00:00
text: '1',
id: 'checklist-one',
completed: true,
2015-11-15 03:44:10 +00:00
}, {
2015-11-20 02:27:39 +00:00
text: '2',
id: 'checklist-two',
completed: true,
2015-11-15 03:44:10 +00:00
}, {
2015-11-20 02:27:39 +00:00
text: '3',
id: 'checklist-three',
completed: false,
},
2015-11-15 03:44:10 +00:00
];
user.dailys[0].completed = true;
cron();
2015-11-20 02:27:39 +00:00
_.each(user.dailys[0].checklist, (box) => {
expect(box.completed).to.be(false);
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
it('does not reset checklist on grey incomplete dailies', () => {
let yesterday = moment().subtract(1, 'days');
user.dailys[0].repeat[DAY_MAPPING[yesterday.day()]] = false;
2015-11-15 03:44:10 +00:00
user.dailys[0].checklist = [
{
2015-11-20 02:27:39 +00:00
text: '1',
id: 'checklist-one',
completed: true,
2015-11-15 03:44:10 +00:00
}, {
2015-11-20 02:27:39 +00:00
text: '2',
id: 'checklist-two',
completed: true,
2015-11-15 03:44:10 +00:00
}, {
2015-11-20 02:27:39 +00:00
text: '3',
id: 'checklist-three',
completed: true,
},
2015-11-15 03:44:10 +00:00
];
cron();
2015-11-20 02:27:39 +00:00
_.each(user.dailys[0].checklist, (box) => {
expect(box.completed).to.be(true);
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
it('resets checklist on complete grey complete dailies', () => {
let yesterday = moment().subtract(1, 'days');
user.dailys[0].repeat[DAY_MAPPING[yesterday.day()]] = false;
2015-11-15 03:44:10 +00:00
user.dailys[0].checklist = [
{
2015-11-20 02:27:39 +00:00
text: '1',
id: 'checklist-one',
completed: true,
2015-11-15 03:44:10 +00:00
}, {
2015-11-20 02:27:39 +00:00
text: '2',
id: 'checklist-two',
completed: true,
2015-11-15 03:44:10 +00:00
}, {
2015-11-20 02:27:39 +00:00
text: '3',
id: 'checklist-three',
completed: true,
},
2015-11-15 03:44:10 +00:00
];
user.dailys[0].completed = true;
cron();
2015-11-20 02:27:39 +00:00
_.each(user.dailys[0].checklist, (box) => {
expect(box.completed).to.be(false);
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
it('does not damage user for incomplete dailies', () => {
2015-11-15 03:44:10 +00:00
expect(user).toHaveHP(50);
user.dailys[0].completed = true;
user.dailys[1].completed = false;
cron();
2015-11-20 02:27:39 +00:00
expect(user).toHaveHP(50);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('gives credit for complete dailies', () => {
2015-11-15 03:44:10 +00:00
user.dailys[0].completed = true;
expect(user.dailys[0].history).to.be.empty;
cron();
2015-11-20 02:27:39 +00:00
expect(user.dailys[0].history).to.not.be.empty;
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('damages user for incomplete dailies after checkout', () => {
2015-11-15 03:44:10 +00:00
expect(user).toHaveHP(50);
user.dailys[0].completed = true;
user.dailys[1].completed = false;
user.preferences.sleep = false;
cron();
2015-11-20 02:27:39 +00:00
expect(user.stats.hp).to.be.lessThan(50);
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
describe('Death', () => {
let user;
beforeEach(() => {
2015-11-15 03:44:10 +00:00
user = newUser();
2015-11-20 02:27:39 +00:00
});
it('revives correctly', () => {
2015-11-15 03:44:10 +00:00
user.stats = {
gp: 10,
exp: 100,
lvl: 2,
hp: 0,
2015-11-20 02:27:39 +00:00
class: 'warrior',
2015-11-15 03:44:10 +00:00
};
user.ops.revive();
expect(user).toHaveGP(0);
expect(user).toHaveExp(0);
expect(user).toHaveLevel(1);
expect(user).toHaveHP(50);
2015-11-20 02:27:39 +00:00
expect(user.items.gear.owned).to.eql({
weapon_warrior_0: false,
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
it('doesn\'t break unbreakables', () => {
let ce;
2015-11-15 03:44:10 +00:00
ce = shared.countExists;
2015-11-20 02:27:39 +00:00
user.items.gear.owned.shield_warrior_1 = true;
user.items.gear.owned.shield_rogue_1 = true;
user.items.gear.owned.head_special_nye = true;
2015-11-15 03:44:10 +00:00
expect(ce(user.items.gear.owned)).to.be(4);
user.stats.hp = 0;
user.ops.revive();
expect(ce(user.items.gear.owned)).to.be(3);
user.stats.hp = 0;
user.ops.revive();
expect(ce(user.items.gear.owned)).to.be(2);
user.stats.hp = 0;
user.ops.revive();
expect(ce(user.items.gear.owned)).to.be(2);
2015-11-20 02:27:39 +00:00
expect(user.items.gear.owned).to.eql({
2015-11-15 03:44:10 +00:00
weapon_warrior_0: false,
shield_warrior_1: false,
shield_rogue_1: true,
2015-11-20 02:27:39 +00:00
head_special_nye: true,
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
it('handles event items', () => {
user.items.gear.owned.head_special_nye = true;
2015-11-15 03:44:10 +00:00
shared.content.gear.flat.head_special_nye.event.start = '2012-01-01';
shared.content.gear.flat.head_special_nye.event.end = '2012-02-01';
expect(shared.content.gear.flat.head_special_nye.canOwn(user)).to.be(true);
2015-11-20 02:27:39 +00:00
delete user.items.gear.owned.head_special_nye;
2015-11-15 03:44:10 +00:00
expect(shared.content.gear.flat.head_special_nye.canOwn(user)).to.be(false);
shared.content.gear.flat.head_special_nye.event.start = moment().subtract(5, 'days');
shared.content.gear.flat.head_special_nye.event.end = moment().add(5, 'days');
2015-11-20 02:27:39 +00:00
expect(shared.content.gear.flat.head_special_nye.canOwn(user)).to.be(true);
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
describe('Rebirth', () => {
it('removes correct gear', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.stats.lvl = 100;
user.items.gear.owned = {
2015-11-20 02:27:39 +00:00
weapon_warrior_0: true,
weapon_warrior_1: true,
armor_warrior_1: false,
armor_mystery_201402: true,
back_mystery_201402: false,
head_mystery_201402: true,
weapon_armoire_basicCrossbow: true,
2015-11-15 03:44:10 +00:00
};
user.ops.rebirth();
2015-11-20 02:27:39 +00:00
expect(user.items.gear.owned).to.eql({
weapon_warrior_0: true,
weapon_warrior_1: false,
armor_warrior_1: false,
armor_mystery_201402: true,
back_mystery_201402: false,
head_mystery_201402: true,
weapon_armoire_basicCrossbow: false,
2015-11-15 03:44:10 +00:00
});
});
});
2015-11-20 02:27:39 +00:00
describe('store', () => {
it('buys a Quest scroll', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.stats.gp = 205;
user.ops.buyQuest({
params: {
2015-11-20 02:27:39 +00:00
key: 'dilatoryDistress1',
},
2015-11-15 03:44:10 +00:00
});
expect(user.items.quests).to.eql({
2015-11-20 02:27:39 +00:00
dilatoryDistress1: 1,
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expect(user).toHaveGP(5);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('does not buy Quests without enough Gold', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.stats.gp = 1;
user.ops.buyQuest({
params: {
2015-11-20 02:27:39 +00:00
key: 'dilatoryDistress1',
},
2015-11-15 03:44:10 +00:00
});
expect(user.items.quests).to.eql({});
2015-11-20 02:27:39 +00:00
expect(user).toHaveGP(1);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('does not buy nonexistent Quests', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.stats.gp = 9999;
user.ops.buyQuest({
params: {
2015-11-20 02:27:39 +00:00
key: 'snarfblatter',
},
2015-11-15 03:44:10 +00:00
});
expect(user.items.quests).to.eql({});
2015-11-20 02:27:39 +00:00
expect(user).toHaveGP(9999);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('does not buy Gem-premium Quests', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.stats.gp = 9999;
user.ops.buyQuest({
params: {
2015-11-20 02:27:39 +00:00
key: 'kraken',
},
2015-11-15 03:44:10 +00:00
});
expect(user.items.quests).to.eql({});
2015-11-20 02:27:39 +00:00
expect(user).toHaveGP(9999);
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
describe('Gem purchases', () => {
it('does not purchase items without enough Gems', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.ops.purchase({
params: {
type: 'eggs',
2015-11-20 02:27:39 +00:00
key: 'Cactus',
},
2015-11-15 03:44:10 +00:00
});
user.ops.purchase({
params: {
type: 'gear',
2015-11-20 02:27:39 +00:00
key: 'headAccessory_special_foxEars',
},
2015-11-15 03:44:10 +00:00
});
user.ops.unlock({
query: {
2015-11-20 02:27:39 +00:00
path: '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',
},
2015-11-15 03:44:10 +00:00
});
expect(user.items.eggs).to.eql({});
2015-11-20 02:27:39 +00:00
expect(user.items.gear.owned).to.eql({
weapon_warrior_0: true,
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
it('purchases an egg', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.balance = 1;
user.ops.purchase({
params: {
type: 'eggs',
2015-11-20 02:27:39 +00:00
key: 'Cactus',
},
2015-11-15 03:44:10 +00:00
});
expect(user.items.eggs).to.eql({
2015-11-20 02:27:39 +00:00
Cactus: 1,
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expect(user.balance).to.eql(0.25);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('purchases fox ears', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.balance = 1;
user.ops.purchase({
params: {
type: 'gear',
2015-11-20 02:27:39 +00:00
key: 'headAccessory_special_foxEars',
},
2015-11-15 03:44:10 +00:00
});
expect(user.items.gear.owned).to.eql({
weapon_warrior_0: true,
2015-11-20 02:27:39 +00:00
headAccessory_special_foxEars: true,
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expect(user.balance).to.eql(0.5);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('unlocks all the animal ears at once', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.balance = 2;
user.ops.unlock({
query: {
2015-11-20 02:27:39 +00:00
path: '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',
},
2015-11-15 03:44:10 +00:00
});
expect(user.items.gear.owned).to.eql({
weapon_warrior_0: true,
headAccessory_special_bearEars: true,
headAccessory_special_cactusEars: true,
headAccessory_special_foxEars: true,
headAccessory_special_lionEars: true,
headAccessory_special_pandaEars: true,
headAccessory_special_pigEars: true,
headAccessory_special_tigerEars: true,
2015-11-20 02:27:39 +00:00
headAccessory_special_wolfEars: true,
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expect(user.balance).to.eql(0.75);
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
describe('spells', () => {
_.each(shared.content.spells, (spellClass) => {
_.each(spellClass, (spell) => {
it(`${spell.text} has valid values`, () => {
2015-11-15 03:44:10 +00:00
expect(spell.target).to.match(/^(task|self|party|user)$/);
expect(spell.mana).to.be.an('number');
if (spell.lvl) {
expect(spell.lvl).to.be.an('number');
expect(spell.lvl).to.be.above(0);
}
2015-11-20 02:27:39 +00:00
expect(spell.cast).to.be.a('function');
2015-11-15 03:44:10 +00:00
});
});
});
});
2015-11-20 02:27:39 +00:00
describe('drop system', () => {
let user = null;
const MIN_RANGE_FOR_POTION = 0;
const MAX_RANGE_FOR_POTION = 0.3;
const MIN_RANGE_FOR_EGG = 0.4;
const MAX_RANGE_FOR_EGG = 0.6;
const MIN_RANGE_FOR_FOOD = 0.7;
const MAX_RANGE_FOR_FOOD = 1;
beforeEach(function () {
2015-11-15 03:44:10 +00:00
user = newUser();
user.flags.dropsEnabled = true;
this.task_id = shared.uuid();
return user.ops.addTask({
body: {
type: 'daily',
2015-11-20 02:27:39 +00:00
id: this.task_id,
},
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
it('drops a hatching potion', function () {
let results = [];
for (let random = MIN_RANGE_FOR_POTION; random <= MAX_RANGE_FOR_POTION; random += 0.1) {
2015-11-15 03:44:10 +00:00
sinon.stub(user.fns, 'predictableRandom').returns(random);
user.ops.score({
params: {
id: this.task_id,
2015-11-20 02:27:39 +00:00
direction: 'up',
},
2015-11-15 03:44:10 +00:00
});
expect(user.items.eggs).to.be.empty;
expect(user.items.hatchingPotions).to.not.be.empty;
expect(user.items.food).to.be.empty;
results.push(user.fns.predictableRandom.restore());
}
return results;
});
2015-11-20 02:27:39 +00:00
it('drops a pet egg', function () {
let results = [];
for (let random = MIN_RANGE_FOR_EGG; random <= MAX_RANGE_FOR_EGG; random += 0.1) {
2015-11-15 03:44:10 +00:00
sinon.stub(user.fns, 'predictableRandom').returns(random);
user.ops.score({
params: {
id: this.task_id,
2015-11-20 02:27:39 +00:00
direction: 'up',
},
2015-11-15 03:44:10 +00:00
});
expect(user.items.eggs).to.not.be.empty;
expect(user.items.hatchingPotions).to.be.empty;
expect(user.items.food).to.be.empty;
results.push(user.fns.predictableRandom.restore());
}
return results;
});
2015-11-20 02:27:39 +00:00
it('drops food', function () {
let results = [];
for (let random = MIN_RANGE_FOR_FOOD; random <= MAX_RANGE_FOR_FOOD; random += 0.1) {
2015-11-15 03:44:10 +00:00
sinon.stub(user.fns, 'predictableRandom').returns(random);
user.ops.score({
params: {
id: this.task_id,
2015-11-20 02:27:39 +00:00
direction: 'up',
},
2015-11-15 03:44:10 +00:00
});
expect(user.items.eggs).to.be.empty;
expect(user.items.hatchingPotions).to.be.empty;
expect(user.items.food).to.not.be.empty;
results.push(user.fns.predictableRandom.restore());
}
return results;
});
2015-11-20 02:27:39 +00:00
it('does not get a drop', function () {
2015-11-15 03:44:10 +00:00
sinon.stub(user.fns, 'predictableRandom').returns(0.5);
user.ops.score({
params: {
id: this.task_id,
2015-11-20 02:27:39 +00:00
direction: 'up',
},
2015-11-15 03:44:10 +00:00
});
expect(user.items.eggs).to.eql({});
expect(user.items.hatchingPotions).to.eql({});
expect(user.items.food).to.eql({});
2015-11-20 02:27:39 +00:00
user.fns.predictableRandom.restore();
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
describe('Quests', () => {
_.each(shared.content.quests, (quest) => {
it(`${ quest.text() } has valid values`, () => {
2015-11-15 03:44:10 +00:00
expect(quest.notes()).to.be.an('string');
if (quest.completion) {
expect(quest.completion()).to.be.an('string');
}
if (quest.previous) {
expect(quest.previous).to.be.an('string');
}
if (quest.canBuy()) {
expect(quest.value).to.be.greaterThan(0);
}
expect(quest.drop.gp).to.not.be.lessThan(0);
expect(quest.drop.exp).to.not.be.lessThan(0);
expect(quest.category).to.match(/pet|unlockable|gold|world/);
if (quest.drop.items) {
expect(quest.drop.items).to.be.an(Array);
}
if (quest.boss) {
expect(quest.boss.name()).to.be.an('string');
expect(quest.boss.hp).to.be.greaterThan(0);
2015-11-20 02:27:39 +00:00
expect(quest.boss.str).to.be.greaterThan(0);
2015-11-15 03:44:10 +00:00
} else if (quest.collect) {
2015-11-20 02:27:39 +00:00
_.each(quest.collect, (collect) => {
2015-11-15 03:44:10 +00:00
expect(collect.text()).to.be.an('string');
2015-11-20 02:27:39 +00:00
expect(collect.count).to.be.greaterThan(0);
2015-11-15 03:44:10 +00:00
});
}
});
});
});
2015-11-20 02:27:39 +00:00
describe('Achievements', () => {
_.each(shared.content.classes, (klass) => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.stats.gp = 10000;
2015-11-20 02:27:39 +00:00
_.each(shared.content.gearTypes, (type) => {
_.each([1, 2, 3, 4, 5], (i) => {
2015-11-15 03:44:10 +00:00
return user.ops.buy({
2015-11-20 02:27:39 +00:00
params: `${type}_${klass}_${i}`,
2015-11-15 03:44:10 +00:00
});
});
});
2015-11-20 02:27:39 +00:00
it(`does not get ultimateGear ${klass}`, () => {
expect(user.achievements.ultimateGearSets[klass]).to.not.be.ok();
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
_.each(shared.content.gearTypes, (type) => {
2015-11-15 03:44:10 +00:00
return user.ops.buy({
2015-11-20 02:27:39 +00:00
params: `${type}_${klass}_6`,
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
xit(`gets ultimateGear ${klass}`, () => {
expect(user.achievements.ultimateGearSets[klass]).to.be.ok();
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
it('does not remove existing Ultimate Gear achievements', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.achievements.ultimateGearSets = {
2015-11-20 02:27:39 +00:00
healer: true,
wizard: true,
rogue: true,
warrior: true,
2015-11-15 03:44:10 +00:00
};
user.items.gear.owned.shield_warrior_5 = false;
user.items.gear.owned.weapon_rogue_6 = false;
user.ops.buy({
2015-11-20 02:27:39 +00:00
params: 'shield_warrior_5',
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expect(user.achievements.ultimateGearSets).to.eql({
healer: true,
wizard: true,
rogue: true,
warrior: true,
2015-11-15 03:44:10 +00:00
});
});
});
2015-11-20 02:27:39 +00:00
describe('unlocking features', () => {
it('unlocks drops at level 3', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.stats.lvl = 3;
user.fns.updateStats(user.stats);
2015-11-20 02:27:39 +00:00
expect(user.flags.dropsEnabled).to.be.ok();
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('unlocks Rebirth at level 50', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.stats.lvl = 50;
user.fns.updateStats(user.stats);
2015-11-20 02:27:39 +00:00
expect(user.flags.rebirthEnabled).to.be.ok();
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
describe('level-awarded Quests', () => {
it('gets Attack of the Mundane at level 15', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.stats.lvl = 15;
user.fns.updateStats(user.stats);
expect(user.flags.levelDrops.atom1).to.be.ok();
2015-11-20 02:27:39 +00:00
expect(user.items.quests.atom1).to.eql(1);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('gets Vice at level 30', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.stats.lvl = 30;
user.fns.updateStats(user.stats);
expect(user.flags.levelDrops.vice1).to.be.ok();
2015-11-20 02:27:39 +00:00
expect(user.items.quests.vice1).to.eql(1);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('gets Golden Knight at level 40', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.stats.lvl = 40;
user.fns.updateStats(user.stats);
expect(user.flags.levelDrops.goldenknight1).to.be.ok();
2015-11-20 02:27:39 +00:00
expect(user.items.quests.goldenknight1).to.eql(1);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('gets Moonstone Chain at level 60', () => {
let user = newUser();
2015-11-15 03:44:10 +00:00
user.stats.lvl = 60;
user.fns.updateStats(user.stats);
expect(user.flags.levelDrops.moonstone1).to.be.ok();
2015-11-20 02:27:39 +00:00
expect(user.items.quests.moonstone1).to.eql(1);
2015-11-15 03:44:10 +00:00
});
});
});
});
2015-11-20 02:27:39 +00:00
describe('Simple Scoring', () => {
beforeEach(function () {
let ref = beforeAfter();
this.before = ref.before;
this.after = ref.after;
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('Habits : Up', function () {
2015-11-15 03:44:10 +00:00
this.after.ops.score({
params: {
id: this.after.habits[0].id,
2015-11-20 02:27:39 +00:00
direction: 'down',
2015-11-15 03:44:10 +00:00
},
query: {
2015-11-20 02:27:39 +00:00
times: 5,
},
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expectLostPoints(this.before, this.after, 'habit');
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('Habits : Down', function () {
2015-11-15 03:44:10 +00:00
this.after.ops.score({
params: {
id: this.after.habits[0].id,
2015-11-20 02:27:39 +00:00
direction: 'up',
2015-11-15 03:44:10 +00:00
},
query: {
2015-11-20 02:27:39 +00:00
times: 5,
},
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expectGainedPoints(this.before, this.after, 'habit');
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('Dailys : Up', function () {
2015-11-15 03:44:10 +00:00
this.after.ops.score({
params: {
id: this.after.dailys[0].id,
2015-11-20 02:27:39 +00:00
direction: 'up',
},
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expectGainedPoints(this.before, this.after, 'daily');
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('Dailys : Up, Down', function () {
2015-11-15 03:44:10 +00:00
this.after.ops.score({
params: {
id: this.after.dailys[0].id,
2015-11-20 02:27:39 +00:00
direction: 'up',
},
2015-11-15 03:44:10 +00:00
});
this.after.ops.score({
params: {
id: this.after.dailys[0].id,
2015-11-20 02:27:39 +00:00
direction: 'down',
},
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expectClosePoints(this.before, this.after, 'daily');
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('Todos : Up', function () {
2015-11-15 03:44:10 +00:00
this.after.ops.score({
params: {
id: this.after.todos[0].id,
2015-11-20 02:27:39 +00:00
direction: 'up',
},
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expectGainedPoints(this.before, this.after, 'todo');
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('Todos : Up, Down', function () {
2015-11-15 03:44:10 +00:00
this.after.ops.score({
params: {
id: this.after.todos[0].id,
2015-11-20 02:27:39 +00:00
direction: 'up',
},
2015-11-15 03:44:10 +00:00
});
this.after.ops.score({
params: {
id: this.after.todos[0].id,
2015-11-20 02:27:39 +00:00
direction: 'down',
},
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expectClosePoints(this.before, this.after, 'todo');
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
describe('Cron', () => {
it('computes shouldCron', () => {
let user = newUser();
let paths = {};
2015-11-15 03:44:10 +00:00
user.fns.cron({
2015-11-20 02:27:39 +00:00
paths,
2015-11-15 03:44:10 +00:00
});
expect(user.lastCron).to.not.be.ok;
2015-11-20 02:27:39 +00:00
user.lastCron = Number(moment().subtract(1, 'days'));
2015-11-15 03:44:10 +00:00
paths = {};
user.fns.cron({
2015-11-20 02:27:39 +00:00
paths,
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expect(user.lastCron).to.be.greaterThan(0);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('only dailies & todos are affected', () => {
let ref = beforeAfter({
daysAgo: 1,
});
let before = ref.before;
let after = ref.after;
2015-11-15 03:44:10 +00:00
before.dailys = before.todos = after.dailys = after.todos = [];
after.fns.cron();
before.stats.mp = after.stats.mp;
expect(after.lastCron).to.not.be(before.lastCron);
delete after.stats.buffs;
delete before.stats.buffs;
expect(before.stats).to.eql(after.stats);
2015-11-20 02:27:39 +00:00
let beforeTasks = before.habits.concat(before.dailys).concat(before.todos).concat(before.rewards);
let afterTasks = after.habits.concat(after.dailys).concat(after.todos).concat(after.rewards);
expect(beforeTasks).to.eql(afterTasks);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
describe('preening', () => {
beforeEach(function () {
this.clock = sinon.useFakeTimers(Date.parse('2013-11-20'), 'Date');
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
afterEach(function () {
2015-11-15 03:44:10 +00:00
return this.clock.restore();
});
2015-11-20 02:27:39 +00:00
it('should preen user history', function () {
let ref = beforeAfter({
daysAgo: 1,
});
let after = ref.after;
let history = [
2015-11-15 03:44:10 +00:00
{
date: '09/01/2012',
2015-11-20 02:27:39 +00:00
value: 0,
2015-11-15 03:44:10 +00:00
}, {
date: '10/01/2012',
2015-11-20 02:27:39 +00:00
value: 0,
2015-11-15 03:44:10 +00:00
}, {
date: '11/01/2012',
2015-11-20 02:27:39 +00:00
value: 2,
2015-11-15 03:44:10 +00:00
}, {
date: '12/01/2012',
2015-11-20 02:27:39 +00:00
value: 2,
2015-11-15 03:44:10 +00:00
}, {
date: '01/01/2013',
2015-11-20 02:27:39 +00:00
value: 1,
2015-11-15 03:44:10 +00:00
}, {
date: '01/15/2013',
2015-11-20 02:27:39 +00:00
value: 3,
2015-11-15 03:44:10 +00:00
}, {
date: '02/01/2013',
2015-11-20 02:27:39 +00:00
value: 2,
2015-11-15 03:44:10 +00:00
}, {
date: '02/15/2013',
2015-11-20 02:27:39 +00:00
value: 4,
2015-11-15 03:44:10 +00:00
}, {
date: '03/01/2013',
2015-11-20 02:27:39 +00:00
value: 3,
2015-11-15 03:44:10 +00:00
}, {
date: '03/15/2013',
2015-11-20 02:27:39 +00:00
value: 5,
2015-11-15 03:44:10 +00:00
}, {
date: '04/01/2013',
2015-11-20 02:27:39 +00:00
value: 4,
2015-11-15 03:44:10 +00:00
}, {
date: '04/15/2013',
2015-11-20 02:27:39 +00:00
value: 6,
2015-11-15 03:44:10 +00:00
}, {
date: '05/01/2013',
2015-11-20 02:27:39 +00:00
value: 5,
2015-11-15 03:44:10 +00:00
}, {
date: '05/15/2013',
2015-11-20 02:27:39 +00:00
value: 7,
2015-11-15 03:44:10 +00:00
}, {
date: '06/01/2013',
2015-11-20 02:27:39 +00:00
value: 6,
2015-11-15 03:44:10 +00:00
}, {
date: '06/15/2013',
2015-11-20 02:27:39 +00:00
value: 8,
2015-11-15 03:44:10 +00:00
}, {
date: '07/01/2013',
2015-11-20 02:27:39 +00:00
value: 7,
2015-11-15 03:44:10 +00:00
}, {
date: '07/15/2013',
2015-11-20 02:27:39 +00:00
value: 9,
2015-11-15 03:44:10 +00:00
}, {
date: '08/01/2013',
2015-11-20 02:27:39 +00:00
value: 8,
2015-11-15 03:44:10 +00:00
}, {
date: '08/15/2013',
2015-11-20 02:27:39 +00:00
value: 10,
2015-11-15 03:44:10 +00:00
}, {
date: '09/01/2013',
2015-11-20 02:27:39 +00:00
value: 9,
2015-11-15 03:44:10 +00:00
}, {
date: '09/15/2013',
2015-11-20 02:27:39 +00:00
value: 11,
2015-11-15 03:44:10 +00:00
}, {
date: '010/01/2013',
2015-11-20 02:27:39 +00:00
value: 10,
2015-11-15 03:44:10 +00:00
}, {
date: '010/15/2013',
2015-11-20 02:27:39 +00:00
value: 12,
2015-11-15 03:44:10 +00:00
}, {
date: '011/01/2013',
2015-11-20 02:27:39 +00:00
value: 12,
2015-11-15 03:44:10 +00:00
}, {
date: '011/02/2013',
2015-11-20 02:27:39 +00:00
value: 13,
2015-11-15 03:44:10 +00:00
}, {
date: '011/03/2013',
2015-11-20 02:27:39 +00:00
value: 14,
2015-11-15 03:44:10 +00:00
}, {
date: '011/04/2013',
2015-11-20 02:27:39 +00:00
value: 15,
},
2015-11-15 03:44:10 +00:00
];
2015-11-20 02:27:39 +00:00
2015-11-15 03:44:10 +00:00
after.history = {
exp: _.cloneDeep(history),
2015-11-20 02:27:39 +00:00
todos: _.cloneDeep(history),
2015-11-15 03:44:10 +00:00
};
after.habits[0].history = _.cloneDeep(history);
after.fns.cron();
after.history.exp.pop();
after.history.todos.pop();
2015-11-20 02:27:39 +00:00
_.each([after.history.exp, after.history.todos, after.habits[0].history], function (arr) {
expect(_.map(arr, (x) => {
2015-11-15 03:44:10 +00:00
return x.value;
})).to.eql([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
});
});
});
2015-11-20 02:27:39 +00:00
describe('Todos', () => {
it('1 day missed', () => {
let ref = beforeAfter({
daysAgo: 1,
});
let before = ref.before;
let after = ref.after;
2015-11-15 03:44:10 +00:00
before.dailys = after.dailys = [];
after.fns.cron();
expect(after).toHaveHP(50);
expect(after).toHaveExp(0);
expect(after).toHaveGP(0);
expect(before.todos[0].value).to.be(0);
expect(after.todos[0].value).to.be(-1);
2015-11-20 02:27:39 +00:00
expect(after.history.todos).to.have.length(1);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('2 days missed', () => {
let ref = beforeAfter({
daysAgo: 2,
});
let before = ref.before;
let after = ref.after;
2015-11-15 03:44:10 +00:00
before.dailys = after.dailys = [];
after.fns.cron();
expect(before.todos[0].value).to.be(0);
2015-11-20 02:27:39 +00:00
expect(after.todos[0].value).to.be(-1);
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
describe('cron day calculations', () => {
let dayStart = 4;
let fstr = 'YYYY-MM-DD HH: mm: ss';
it('startOfDay before dayStart', () => {
let start = startOfDay({
2015-11-20 02:27:39 +00:00
now: moment('2014-10-09 02: 30: 00'),
dayStart,
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expect(start.format(fstr)).to.eql('2014-10-08 04: 00: 00');
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('startOfDay after dayStart', () => {
let start = startOfDay({
2015-11-20 02:27:39 +00:00
now: moment('2014-10-09 05: 30: 00'),
dayStart,
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expect(start.format(fstr)).to.eql('2014-10-09 04: 00: 00');
});
it('daysSince cron before, now after', () => {
let lastCron = moment('2014-10-09 02: 30: 00');
let days = daysSince(lastCron, {
2015-11-20 02:27:39 +00:00
now: moment('2014-10-09 11: 30: 00'),
dayStart,
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expect(days).to.eql(1);
});
it('daysSince cron before, now before', () => {
let lastCron = moment('2014-10-09 02: 30: 00');
let days = daysSince(lastCron, {
2015-11-20 02:27:39 +00:00
now: moment('2014-10-09 03: 30: 00'),
dayStart,
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expect(days).to.eql(0);
});
it('daysSince cron after, now after', () => {
let lastCron = moment('2014-10-09 05: 30: 00');
let days = daysSince(lastCron, {
2015-11-20 02:27:39 +00:00
now: moment('2014-10-09 06: 30: 00'),
dayStart,
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expect(days).to.eql(0);
});
it('daysSince cron after, now tomorrow before', () => {
let lastCron = moment('2014-10-09 12: 30: 00');
let days = daysSince(lastCron, {
2015-11-20 02:27:39 +00:00
now: moment('2014-10-10 01: 30: 00'),
dayStart,
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expect(days).to.eql(0);
});
it('daysSince cron after, now tomorrow after', () => {
let lastCron = moment('2014-10-09 12: 30: 00');
let days = daysSince(lastCron, {
2015-11-20 02:27:39 +00:00
now: moment('2014-10-10 10: 30: 00'),
dayStart,
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expect(days).to.eql(1);
});
xit('daysSince, last cron before new dayStart', () => {
let lastCron = moment('2014-10-09 01: 00: 00');
let days = daysSince(lastCron, {
2015-11-20 02:27:39 +00:00
now: moment('2014-10-09 05: 00: 00'),
dayStart,
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
expect(days).to.eql(0);
2015-11-15 03:44:10 +00:00
});
});
2015-11-20 02:27:39 +00:00
describe('dailies', () => {
describe('new day', () => {
2015-11-15 03:44:10 +00:00
/*
2015-11-20 02:27:39 +00:00
This section runs through a 'cron matrix' of all permutations (that I can easily account for). It sets
2015-11-15 03:44:10 +00:00
task due days, user custom day start, timezoneOffset, etc - then runs cron, jumps to tomorrow and runs cron,
and so on - testing each possible outcome along the way
*/
2015-11-20 02:27:39 +00:00
function runCron (options) {
_.each([480, 240, 0, -120], function (timezoneOffset) {
let now = startOfWeek({
2015-11-20 02:27:39 +00:00
timezoneOffset,
2015-11-15 03:44:10 +00:00
}).add(options.currentHour || 0, 'hours');
2015-11-20 02:27:39 +00:00
let ref = beforeAfter({
now,
timezoneOffset,
2015-11-15 03:44:10 +00:00
daysAgo: 1,
cronAfterStart: options.cronAfterStart || true,
dayStart: options.dayStart || 0,
2015-11-20 02:27:39 +00:00
limitOne: 'daily',
});
let before = ref.before;
let after = ref.after;
2015-11-15 03:44:10 +00:00
if (options.repeat) {
before.dailys[0].repeat = after.dailys[0].repeat = options.repeat;
}
before.dailys[0].streak = after.dailys[0].streak = 10;
if (options.checked) {
before.dailys[0].completed = after.dailys[0].completed = true;
}
before.dailys[0].startDate = after.dailys[0].startDate = moment().subtract(30, 'days');
if (options.shouldDo) {
expect(shared.shouldDo(now.toDate(), after.dailys[0], {
2015-11-20 02:27:39 +00:00
timezoneOffset,
2015-11-15 03:44:10 +00:00
dayStart: options.dayStart,
2015-11-20 02:27:39 +00:00
now,
2015-11-15 03:44:10 +00:00
})).to.be.ok();
}
after.fns.cron({
2015-11-20 02:27:39 +00:00
now,
2015-11-15 03:44:10 +00:00
});
before.stats.mp = after.stats.mp;
2015-11-20 02:27:39 +00:00
if (options.expect === 'losePoints') {
expectLostPoints(before, after, 'daily');
} else if (options.expect === 'noChange') {
expectNoChange(before, after);
} else if (options.expect === 'noDamage') {
expectDayResetNoDamage(before, after);
2015-11-15 03:44:10 +00:00
}
2015-11-20 02:27:39 +00:00
2015-11-15 03:44:10 +00:00
return {
2015-11-20 02:27:39 +00:00
before,
after,
2015-11-15 03:44:10 +00:00
};
});
2015-11-20 02:27:39 +00:00
}
let cronMatrix = {
2015-11-15 03:44:10 +00:00
steps: {
'due yesterday': {
defaults: {
daysAgo: 1,
cronAfterStart: true,
2015-11-20 02:27:39 +00:00
limitOne: 'daily',
2015-11-15 03:44:10 +00:00
},
steps: {
'(simple)': {
2015-11-20 02:27:39 +00:00
expect: 'losePoints',
2015-11-15 03:44:10 +00:00
},
'due today': {
defaults: {
repeat: {
su: true,
m: true,
t: true,
w: true,
th: true,
f: true,
2015-11-20 02:27:39 +00:00
s: true,
},
2015-11-15 03:44:10 +00:00
},
steps: {
'pre-dayStart': {
defaults: {
currentHour: 3,
dayStart: 4,
2015-11-20 02:27:39 +00:00
shouldDo: true,
2015-11-15 03:44:10 +00:00
},
steps: {
2015-11-20 02:27:39 +00:00
checked: {
2015-11-15 03:44:10 +00:00
checked: true,
2015-11-20 02:27:39 +00:00
expect: 'noChange',
2015-11-15 03:44:10 +00:00
},
'un-checked': {
checked: false,
2015-11-20 02:27:39 +00:00
expect: 'noChange',
},
},
2015-11-15 03:44:10 +00:00
},
'post-dayStart': {
defaults: {
currentHour: 5,
dayStart: 4,
2015-11-20 02:27:39 +00:00
shouldDo: true,
2015-11-15 03:44:10 +00:00
},
steps: {
2015-11-20 02:27:39 +00:00
checked: {
2015-11-15 03:44:10 +00:00
checked: true,
2015-11-20 02:27:39 +00:00
expect: 'noDamage',
2015-11-15 03:44:10 +00:00
},
2015-11-20 02:27:39 +00:00
unchecked: {
2015-11-15 03:44:10 +00:00
checked: false,
2015-11-20 02:27:39 +00:00
expect: 'losePoints',
},
},
},
},
2015-11-15 03:44:10 +00:00
},
'NOT due today': {
defaults: {
repeat: {
su: true,
m: false,
t: true,
w: true,
th: true,
f: true,
2015-11-20 02:27:39 +00:00
s: true,
},
2015-11-15 03:44:10 +00:00
},
steps: {
'pre-dayStart': {
defaults: {
currentHour: 3,
dayStart: 4,
2015-11-20 02:27:39 +00:00
shouldDo: true,
2015-11-15 03:44:10 +00:00
},
steps: {
2015-11-20 02:27:39 +00:00
checked: {
2015-11-15 03:44:10 +00:00
checked: true,
2015-11-20 02:27:39 +00:00
expect: 'noChange',
2015-11-15 03:44:10 +00:00
},
'un-checked': {
checked: false,
2015-11-20 02:27:39 +00:00
expect: 'noChange',
},
},
2015-11-15 03:44:10 +00:00
},
'post-dayStart': {
defaults: {
currentHour: 5,
dayStart: 4,
2015-11-20 02:27:39 +00:00
shouldDo: false,
2015-11-15 03:44:10 +00:00
},
steps: {
2015-11-20 02:27:39 +00:00
checked: {
2015-11-15 03:44:10 +00:00
checked: true,
2015-11-20 02:27:39 +00:00
expect: 'noDamage',
2015-11-15 03:44:10 +00:00
},
2015-11-20 02:27:39 +00:00
unchecked: {
2015-11-15 03:44:10 +00:00
checked: false,
2015-11-20 02:27:39 +00:00
expect: 'losePoints',
},
},
},
},
},
},
2015-11-15 03:44:10 +00:00
},
'not due yesterday': {
defaults: repeatWithoutLastWeekday(),
steps: {
'(simple)': {
2015-11-20 02:27:39 +00:00
expect: 'noDamage',
2015-11-15 03:44:10 +00:00
},
'post-dayStart': {
currentHour: 5,
dayStart: 4,
2015-11-20 02:27:39 +00:00
expect: 'noDamage',
2015-11-15 03:44:10 +00:00
},
'pre-dayStart': {
currentHour: 3,
dayStart: 4,
2015-11-20 02:27:39 +00:00
expect: 'noChange',
},
},
},
},
2015-11-15 03:44:10 +00:00
};
2015-11-20 02:27:39 +00:00
let recurseCronMatrix = (obj, options = {}) => {
2015-11-15 03:44:10 +00:00
if (obj.steps) {
2015-11-20 02:27:39 +00:00
_.each(obj.steps, (step, text) => {
let o = _.cloneDeep(options);
if (!o.text) {
2015-11-15 03:44:10 +00:00
o.text = '';
}
2015-11-20 02:27:39 +00:00
o.text += `${text}`;
2015-11-15 03:44:10 +00:00
return recurseCronMatrix(step, _.defaults(o, obj.defaults));
});
} else {
2015-11-20 02:27:39 +00:00
it(`${options.text}`, () => {
2015-11-15 03:44:10 +00:00
return runCron(_.defaults(obj, options));
});
}
};
2015-11-20 02:27:39 +00:00
2015-11-15 03:44:10 +00:00
return recurseCronMatrix(cronMatrix);
});
});
});
2015-11-20 02:27:39 +00:00
describe('Helper', () => {
it('calculates gold coins', () => {
2015-11-15 03:44:10 +00:00
expect(shared.gold(10)).to.eql(10);
expect(shared.gold(1.957)).to.eql(1);
2015-11-20 02:27:39 +00:00
expect(shared.gold()).to.eql(0);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('calculates silver coins', () => {
2015-11-15 03:44:10 +00:00
expect(shared.silver(10)).to.eql(0);
expect(shared.silver(1.957)).to.eql(95);
2015-11-20 02:27:39 +00:00
expect(shared.silver(0.01)).to.eql('01');
expect(shared.silver()).to.eql('00');
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('calculates experience to next level', () => {
2015-11-15 03:44:10 +00:00
expect(shared.tnl(1)).to.eql(150);
expect(shared.tnl(2)).to.eql(160);
expect(shared.tnl(10)).to.eql(260);
2015-11-20 02:27:39 +00:00
expect(shared.tnl(99)).to.eql(3580);
2015-11-15 03:44:10 +00:00
});
2015-11-20 02:27:39 +00:00
it('calculates the start of the day', () => {
let fstr = 'YYYY-MM-DD HH: mm: ss';
let today = '2013-01-01 00: 00: 00';
let zone = moment(today).zone();
expect(startOfDay({
2015-11-20 02:27:39 +00:00
now: new Date(2013, 0, 1, 0),
2015-11-15 03:44:10 +00:00
}, {
2015-11-20 02:27:39 +00:00
timezoneOffset: zone,
2015-11-15 03:44:10 +00:00
}).format(fstr)).to.eql(today);
expect(startOfDay({
2015-11-20 02:27:39 +00:00
now: new Date(2013, 0, 1, 5),
2015-11-15 03:44:10 +00:00
}, {
2015-11-20 02:27:39 +00:00
timezoneOffset: zone,
2015-11-15 03:44:10 +00:00
}).format(fstr)).to.eql(today);
expect(startOfDay({
2015-11-15 03:44:10 +00:00
now: new Date(2013, 0, 1, 23, 59, 59),
2015-11-20 02:27:39 +00:00
timezoneOffset: zone,
2015-11-15 03:44:10 +00:00
}).format(fstr)).to.eql(today);
});
});