diff --git a/test/common/algos.mocha.coffee b/test/common/algos.mocha.coffee deleted file mode 100644 index 4e911ca06d..0000000000 --- a/test/common/algos.mocha.coffee +++ /dev/null @@ -1,925 +0,0 @@ -_ = require 'lodash' -expect = require 'expect.js' -sinon = require 'sinon' -moment = require 'moment' -shared = require '../../common/script/index.js' -shared.i18n.translations = require('../../website/src/libs/i18n.js').translations -test_helper = require './test_helper' -test_helper.addCustomMatchers() -$w = (s)->s.split(' ') - -### Helper Functions #### -newUser = (addTasks=true)-> - buffs = {per:0, int:0, con:0, str:0, stealth: 0, streaks: false} - user = - auth: - timestamps: {} - stats: {str:1, con:1, per:1, int:1, mp: 32, class: 'warrior', buffs: buffs} - items: - lastDrop: - count: 0 - hatchingPotions: {} - eggs: {} - food: {} - gear: - equipped: {} - costume: {} - owned: {} - quests: {} - party: - quest: - progress: - down: 0 - preferences: { - autoEquip: true - } - dailys: [] - todos: [] - rewards: [] - flags: {} - achievements: - ultimateGearSets: {} - contributor: - level: 2 - _tmp: {} - - shared.wrap(user) - user.ops.reset(null, ->) - if addTasks - _.each ['habit', 'todo', 'daily'], (task)-> - user.ops.addTask {body: {type: task, id: shared.uuid()}} - user - -rewrapUser = (user)-> - user._wrapped = false - shared.wrap(user) - user - -expectStrings = (obj, paths) -> - _.each paths, (path) -> expect(obj[path]).to.be.ok() - -# options.daysAgo: days ago when the last cron was executed -# cronAfterStart: moves the lastCron to be after the dayStart. -# This way the daysAgo works as expected if the test case -# makes the assumption that the lastCron was after dayStart. -beforeAfter = (options={}) -> - user = newUser() - [before, after] = [user, _.cloneDeep(user)] - # avoid closure on the original user - rewrapUser(after) - before.preferences.dayStart = after.preferences.dayStart = options.dayStart if options.dayStart - before.preferences.timezoneOffset = after.preferences.timezoneOffset = (options.timezoneOffset or moment().zone()) - if options.limitOne - before["#{options.limitOne}s"] = [before["#{options.limitOne}s"][0]] - after["#{options.limitOne}s"] = [after["#{options.limitOne}s"][0]] - lastCron = moment(options.now || +new Date).subtract( {days:options.daysAgo} ) if options.daysAgo - lastCron.add( {hours:options.dayStart, minutes:1} ) if options.daysAgo and options.cronAfterStart - lastCron = +lastCron if options.daysAgo - _.each [before,after], (obj) -> - obj.lastCron = lastCron if options.daysAgo - {before:before, after:after} -#TODO calculate actual points - -expectLostPoints = (before, after, taskType) -> - if taskType in ['daily','habit'] - expect(after.stats.hp).to.be.lessThan before.stats.hp - expect(after["#{taskType}s"][0].history).to.have.length(1) - else expect(after.history.todos).to.have.length(1) - expect(after).toHaveExp 0 - expect(after).toHaveGP 0 - expect(after["#{taskType}s"][0].value).to.be.lessThan before["#{taskType}s"][0].value - -expectGainedPoints = (before, after, taskType) -> - 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 - expect(after["#{taskType}s"][0].value).to.be.greaterThan before["#{taskType}s"][0].value - expect(after["#{taskType}s"][0].history).to.have.length(1) if taskType is 'habit' - # daily & todo histories handled on cron - -expectNoChange = (before,after) -> - _.each $w('stats items gear dailys todos rewards preferences'), (attr)-> - expect(after[attr]).to.eql before[attr] - -expectClosePoints = (before, after, taskType) -> - 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 - expect( Math.abs(after["#{taskType}s"][0].value - before["#{taskType}s"][0].value) ).to.be.lessThan 0.0001 - -expectDayResetNoDamage = (b,a) -> - [before,after] = [_.cloneDeep(b), _.cloneDeep(a)] - _.each after.dailys, (task,i) -> - expect(task.completed).to.be false - expect(before.dailys[i].value).to.be task.value - expect(before.dailys[i].streak).to.be task.streak - expect(task.history).to.have.length(1) - _.each after.todos, (task,i) -> - expect(task.completed).to.be false - expect(before.todos[i].value).to.be.greaterThan task.value - expect(after.history.todos).to.have.length(1) - # hack so we can compare user before/after obj equality sans effected paths - _.each [before,after], (obj) -> - delete obj.stats.buffs - _.each $w('dailys todos history lastCron'), (path) -> delete obj[path] - delete after._tmp - expectNoChange(before, after) - -cycle = (array)-> - n = -1 - (seed=0)-> - n++ - return array[n % array.length] - -repeatWithoutLastWeekday = ()-> - repeat = {su:true,m:true,t:true,w:true,th:true,f:true,s:true} - if shared.startOfWeek(moment().zone(0)).isoWeekday() == 1 # Monday - repeat.su = false - else - repeat.s = false - {repeat: repeat} - -###### Specs ###### - -describe 'User', -> - it 'sets correct user defaults', -> - user = newUser() - base_gear = { armor: 'armor_base_0', weapon: 'weapon_base_0', head: 'head_base_0', shield: 'shield_base_0' } - buffs = {per:0, int:0, con:0, str:0, stealth: 0, streaks: false} - expect(user.stats).to.eql { str: 1, con: 1, per: 1, int: 1, hp: 50, mp: 32, lvl: 1, exp: 0, gp: 0, class: 'warrior', buffs: buffs } - expect(user.items.gear).to.eql { equipped: base_gear, costume: base_gear, owned: {weapon_warrior_0: true} } - expect(user.preferences).to.eql { autoEquip: true, costume: false } - - it 'calculates max MP', -> - user = newUser() - expect(user).toHaveMaxMP 32 - user.stats.int = 10 - expect(user).toHaveMaxMP 50 - user.stats.lvl = 5 - expect(user).toHaveMaxMP 54 - user.stats.class = 'wizard' - user.items.gear.equipped.weapon = 'weapon_wizard_1' - expect(user).toHaveMaxMP 63 - - it 'handles perfect days', -> - user = newUser() - user.dailys = [] - _.times 3, ->user.dailys.push shared.taskDefaults({type:'daily', startDate: moment().subtract(7, 'days')}) - cron = -> user.lastCron = moment().subtract(1,'days');user.fns.cron() - - 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() - - _.each user.dailys, (d)->d.completed = true - cron() - expect(user.stats.buffs.str).to.be 1 - expect(user.achievements.perfect).to.be 1 - - # Handle greyed-out dailys - yesterday = moment().subtract(1,'days') - user.dailys[0].repeat[shared.dayMapping[yesterday.day()]] = false - _.each user.dailys[1..], (d)->d.completed = true - cron() - expect(user.stats.buffs.str).to.be 1 - expect(user.achievements.perfect).to.be 2 - - describe 'Resting in the Inn', -> - user = null - cron = null - - beforeEach -> - user = newUser() - user.preferences.sleep = true - cron = -> user.lastCron = moment().subtract(1, 'days');user.fns.cron() - user.dailys = [] - _.times 2, -> user.dailys.push shared.taskDefaults({type:'daily', startDate: moment().subtract(7, 'days')}) - - it 'remains in the inn on cron', -> - cron() - expect(user.preferences.sleep).to.be true - - it 'resets dailies', -> - user.dailys[0].completed = true - cron() - expect(user.dailys[0].completed).to.be false - - it 'resets checklist on incomplete dailies', -> - user.dailys[0].checklist = [ - { - "text" : "1", - "id" : "checklist-one", - "completed" : true - }, - { - "text" : "2", - "id" : "checklist-two", - "completed" : true - }, - { - "text" : "3", - "id" : "checklist-three", - "completed" : false - } - ] - cron() - _.each user.dailys[0].checklist, (box)-> - expect(box.completed).to.be false - - it 'resets checklist on complete dailies', -> - user.dailys[0].checklist = [ - { - "text" : "1", - "id" : "checklist-one", - "completed" : true - }, - { - "text" : "2", - "id" : "checklist-two", - "completed" : true - }, - { - "text" : "3", - "id" : "checklist-three", - "completed" : false - } - ] - user.dailys[0].completed = true - cron() - _.each user.dailys[0].checklist, (box)-> - expect(box.completed).to.be false - - it 'does not reset checklist on grey incomplete dailies', -> - yesterday = moment().subtract(1,'days') - user.dailys[0].repeat[shared.dayMapping[yesterday.day()]] = false - user.dailys[0].checklist = [ - { - "text" : "1", - "id" : "checklist-one", - "completed" : true - }, - { - "text" : "2", - "id" : "checklist-two", - "completed" : true - }, - { - "text" : "3", - "id" : "checklist-three", - "completed" : true - } - ] - - cron() - _.each user.dailys[0].checklist, (box)-> - expect(box.completed).to.be true - - it 'resets checklist on complete grey complete dailies', -> - yesterday = moment().subtract(1,'days') - user.dailys[0].repeat[shared.dayMapping[yesterday.day()]] = false - user.dailys[0].checklist = [ - { - "text" : "1", - "id" : "checklist-one", - "completed" : true - }, - { - "text" : "2", - "id" : "checklist-two", - "completed" : true - }, - { - "text" : "3", - "id" : "checklist-three", - "completed" : true - } - ] - user.dailys[0].completed = true - - cron() - _.each user.dailys[0].checklist, (box)-> - expect(box.completed).to.be false - - it 'does not damage user for incomplete dailies', -> - expect(user).toHaveHP 50 - user.dailys[0].completed = true - user.dailys[1].completed = false - cron() - expect(user).toHaveHP 50 - - it 'gives credit for complete dailies', -> - user.dailys[0].completed = true - expect(user.dailys[0].history).to.be.empty - cron() - expect(user.dailys[0].history).to.not.be.empty - - it 'damages user for incomplete dailies after checkout', -> - expect(user).toHaveHP 50 - user.dailys[0].completed = true - user.dailys[1].completed = false - user.preferences.sleep = false - cron() - expect(user.stats.hp).to.be.lessThan 50 - - describe 'Death', -> - user = undefined - it 'revives correctly', -> - user = newUser() - user.stats = { gp: 10, exp: 100, lvl: 2, hp: 0, class: 'warrior' } - user.ops.revive() - expect(user).toHaveGP 0 - expect(user).toHaveExp 0 - expect(user).toHaveLevel 1 - expect(user).toHaveHP 50 - expect(user.items.gear.owned).to.eql { weapon_warrior_0: false } - - it "doesn't break unbreakables", -> - ce = shared.countExists - user = newUser() - # breakables (includes default weapon_warrior_0): - user.items.gear.owned['shield_warrior_1'] = true - # unbreakables because off-class or 0 value: - user.items.gear.owned['shield_rogue_1'] = true - user.items.gear.owned['head_special_nye'] = true - 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 - expect(user.items.gear.owned).to.eql { weapon_warrior_0: false, shield_warrior_1: false, shield_rogue_1: true, head_special_nye: true } - - it "handles event items", -> - 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 - delete user.items.gear.owned['head_special_nye'] - 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') - expect(shared.content.gear.flat.head_special_nye.canOwn(user)).to.be true - - describe 'Rebirth', -> - user = undefined - it 'removes correct gear', -> - user = newUser() - user.stats.lvl = 100 - user.items.gear.owned = { - "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, - } - user.ops.rebirth() - 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, - } - - describe 'store', -> - it 'buys a Quest scroll', -> - user = newUser() - user.stats.gp = 205 - user.ops.buyQuest {params: {key: 'dilatoryDistress1'}} - expect(user.items.quests).to.eql {dilatoryDistress1: 1} - expect(user).toHaveGP 5 - - it 'does not buy Quests without enough Gold', -> - user = newUser() - user.stats.gp = 1 - user.ops.buyQuest {params: {key: 'dilatoryDistress1'}} - expect(user.items.quests).to.eql {} - expect(user).toHaveGP 1 - - it 'does not buy nonexistent Quests', -> - user = newUser() - user.stats.gp = 9999 - user.ops.buyQuest {params: {key: 'snarfblatter'}} - expect(user.items.quests).to.eql {} - expect(user).toHaveGP 9999 - - it 'does not buy Gem-premium Quests', -> - user = newUser() - user.stats.gp = 9999 - user.ops.buyQuest {params: {key: 'kraken'}} - expect(user.items.quests).to.eql {} - expect(user).toHaveGP 9999 - - describe 'Gem purchases', -> - it 'does not purchase items without enough Gems', -> - user = newUser() - user.ops.purchase {params: {type: 'eggs', key: 'Cactus'}} - user.ops.purchase {params: {type: 'gear', key: 'headAccessory_special_foxEars'}} - user.ops.unlock {query: {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'}} - expect(user.items.eggs).to.eql {} - expect(user.items.gear.owned).to.eql { weapon_warrior_0: true } - - it 'purchases an egg', -> - user = newUser() - user.balance = 1 - user.ops.purchase {params: {type: 'eggs', key: 'Cactus'}} - expect(user.items.eggs).to.eql { Cactus: 1} - expect(user.balance).to.eql 0.25 - - it 'purchases fox ears', -> - user = newUser() - user.balance = 1 - user.ops.purchase {params: {type: 'gear', key: 'headAccessory_special_foxEars'}} - expect(user.items.gear.owned).to.eql { weapon_warrior_0: true, headAccessory_special_foxEars: true } - expect(user.balance).to.eql 0.5 - - it 'unlocks all the animal ears at once', -> - user = newUser() - user.balance = 2 - user.ops.unlock {query: {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'}} - 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, headAccessory_special_wolfEars: true} - expect(user.balance).to.eql 0.75 - - describe 'spells', -> - _.each shared.content.spells, (spellClass)-> - _.each spellClass, (spell)-> - it "#{spell.text} has valid values", -> - 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) - expect(spell.cast).to.be.a('function') - - describe 'drop system', -> - user = null - MIN_RANGE_FOR_POTION = 0 - MAX_RANGE_FOR_POTION = .3 - MIN_RANGE_FOR_EGG = .4 - MAX_RANGE_FOR_EGG = .6 - MIN_RANGE_FOR_FOOD = .7 - MAX_RANGE_FOR_FOOD = 1 - - beforeEach -> - user = newUser() - user.flags.dropsEnabled = true - @task_id = shared.uuid() - user.ops.addTask({body: {type: 'daily', id: @task_id}}) - - it 'drops a hatching potion', -> - for random in [MIN_RANGE_FOR_POTION..MAX_RANGE_FOR_POTION] by .1 - sinon.stub(user.fns, 'predictableRandom').returns random - user.ops.score {params: { id: @task_id, direction: 'up'}} - expect(user.items.eggs).to.be.empty - expect(user.items.hatchingPotions).to.not.be.empty - expect(user.items.food).to.be.empty - user.fns.predictableRandom.restore() - - it 'drops a pet egg', -> - for random in [MIN_RANGE_FOR_EGG..MAX_RANGE_FOR_EGG] by .1 - sinon.stub(user.fns, 'predictableRandom').returns random - user.ops.score {params: { id: @task_id, direction: 'up'}} - expect(user.items.eggs).to.not.be.empty - expect(user.items.hatchingPotions).to.be.empty - expect(user.items.food).to.be.empty - user.fns.predictableRandom.restore() - - it 'drops food', -> - for random in [MIN_RANGE_FOR_FOOD..MAX_RANGE_FOR_FOOD] by .1 - sinon.stub(user.fns, 'predictableRandom').returns random - user.ops.score {params: { id: @task_id, direction: 'up'}} - expect(user.items.eggs).to.be.empty - expect(user.items.hatchingPotions).to.be.empty - expect(user.items.food).to.not.be.empty - user.fns.predictableRandom.restore() - - it 'does not get a drop', -> - sinon.stub(user.fns, 'predictableRandom').returns 0.5 - user.ops.score {params: { id: @task_id, direction: 'up'}} - expect(user.items.eggs).to.eql {} - expect(user.items.hatchingPotions).to.eql {} - expect(user.items.food).to.eql {} - user.fns.predictableRandom.restore() - - describe 'Quests', -> - _.each shared.content.quests, (quest)-> - it "#{quest.text()} has valid values", -> - expect(quest.notes()).to.be.an('string') - expect(quest.completion()).to.be.an('string') if quest.completion - expect(quest.previous).to.be.an('string') if quest.previous - expect(quest.value).to.be.greaterThan 0 if quest.canBuy() - 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 - expect(quest.boss.str).to.be.greaterThan 0 - else if quest.collect - _.each quest.collect, (collect)-> - expect(collect.text()).to.be.an('string') - expect(collect.count).to.be.greaterThan 0 - - describe 'Achievements', -> - _.each shared.content.classes, (klass) -> - user = newUser() - user.stats.gp = 10000 - _.each shared.content.gearTypes, (type) -> - _.each [1..5], (i) -> - user.ops.buy {params:'#{type}_#{klass}_#{i}'} - it 'does not get ultimateGear ' + klass, -> - expect(user.achievements.ultimateGearSets[klass]).to.not.be.ok() - _.each shared.content.gearTypes, (type) -> - user.ops.buy {params:'#{type}_#{klass}_6'} - xit 'gets ultimateGear ' + klass, -> - expect(user.achievements.ultimateGearSets[klass]).to.be.ok() - - it 'does not remove existing Ultimate Gear achievements', -> - user = newUser() - user.achievements.ultimateGearSets = {'healer':true,'wizard':true,'rogue':true,'warrior':true} - user.items.gear.owned.shield_warrior_5 = false - user.items.gear.owned.weapon_rogue_6 = false - user.ops.buy {params:'shield_warrior_5'} - expect(user.achievements.ultimateGearSets).to.eql {'healer':true,'wizard':true,'rogue':true,'warrior':true} - - describe 'unlocking features', -> - it 'unlocks drops at level 3', -> - user = newUser() - user.stats.lvl = 3 - user.fns.updateStats(user.stats) - expect(user.flags.dropsEnabled).to.be.ok() - - it 'unlocks Rebirth at level 50', -> - user = newUser() - user.stats.lvl = 50 - user.fns.updateStats(user.stats) - expect(user.flags.rebirthEnabled).to.be.ok() - - describe 'level-awarded Quests', -> - it 'gets Attack of the Mundane at level 15', -> - user = newUser() - user.stats.lvl = 15 - user.fns.updateStats(user.stats) - expect(user.flags.levelDrops.atom1).to.be.ok() - expect(user.items.quests.atom1).to.eql 1 - - it 'gets Vice at level 30', -> - user = newUser() - user.stats.lvl = 30 - user.fns.updateStats(user.stats) - expect(user.flags.levelDrops.vice1).to.be.ok() - expect(user.items.quests.vice1).to.eql 1 - - it 'gets Golden Knight at level 40', -> - user = newUser() - user.stats.lvl = 40 - user.fns.updateStats(user.stats) - expect(user.flags.levelDrops.goldenknight1).to.be.ok() - expect(user.items.quests.goldenknight1).to.eql 1 - - it 'gets Moonstone Chain at level 60', -> - user = newUser() - user.stats.lvl = 60 - user.fns.updateStats(user.stats) - expect(user.flags.levelDrops.moonstone1).to.be.ok() - expect(user.items.quests.moonstone1).to.eql 1 - -describe 'Simple Scoring', -> - beforeEach -> - {@before, @after} = beforeAfter() - - it 'Habits : Up', -> - @after.ops.score {params: {id: @after.habits[0].id, direction: 'down'}, query: {times: 5}} - expectLostPoints(@before, @after,'habit') - - it 'Habits : Down', -> - @after.ops.score {params: {id: @after.habits[0].id, direction: 'up'}, query: {times: 5}} - expectGainedPoints(@before, @after,'habit') - - it 'Dailys : Up', -> - @after.ops.score {params: {id: @after.dailys[0].id, direction: 'up'}} - expectGainedPoints(@before, @after,'daily') - - it 'Dailys : Up, Down', -> - @after.ops.score {params: {id: @after.dailys[0].id, direction: 'up'}} - @after.ops.score {params: {id: @after.dailys[0].id, direction: 'down'}} - expectClosePoints(@before, @after, 'daily') - - it 'Todos : Up', -> - @after.ops.score {params: {id: @after.todos[0].id, direction: 'up'}} - expectGainedPoints(@before, @after,'todo') - - it 'Todos : Up, Down', -> - @after.ops.score {params: {id: @after.todos[0].id, direction: 'up'}} - @after.ops.score {params: {id: @after.todos[0].id, direction: 'down'}} - expectClosePoints(@before, @after, 'todo') - -describe 'Cron', -> - - it 'computes shouldCron', -> - user = newUser() - - paths = {};user.fns.cron {paths} - expect(user.lastCron).to.not.be.ok # it setup the cron property now - - user.lastCron = +moment().subtract(1,'days') - - paths = {};user.fns.cron {paths} - expect(user.lastCron).to.be.greaterThan 0 - -# user.lastCron = +moment().add(1,'days') -# paths = {};algos.cron user, {paths} -# expect(paths.lastCron).to.be true # busted cron (was set to after today's date) - - it 'only dailies & todos are affected', -> - {before,after} = beforeAfter({daysAgo:1}) - before.dailys = before.todos = after.dailys = after.todos = [] - after.fns.cron() - before.stats.mp=after.stats.mp #FIXME - expect(after.lastCron).to.not.be before.lastCron # make sure cron was run - delete after.stats.buffs;delete before.stats.buffs - expect(before.stats).to.eql after.stats - beforeTasks = before.habits.concat(before.dailys).concat(before.todos).concat(before.rewards) - afterTasks = after.habits.concat(after.dailys).concat(after.todos).concat(after.rewards) - expect(beforeTasks).to.eql afterTasks - - describe 'preening', -> - beforeEach -> - @clock = sinon.useFakeTimers(Date.parse("2013-11-20"), "Date") - - afterEach -> - @clock.restore() - - it 'should preen user history', -> - {before,after} = beforeAfter({daysAgo:1}) - history = [ - # Last year should be condensed to one entry, avg: 1 - {date:'09/01/2012', value: 0} - {date:'10/01/2012', value: 0} - {date:'11/01/2012', value: 2} - {date:'12/01/2012', value: 2} - - # Each month of this year should be condensed to 1/mo, averages follow - {date:'01/01/2013', value: 1} #2 - {date:'01/15/2013', value: 3} - - {date:'02/01/2013', value: 2} #3 - {date:'02/15/2013', value: 4} - - {date:'03/01/2013', value: 3} #4 - {date:'03/15/2013', value: 5} - - {date:'04/01/2013', value: 4} #5 - {date:'04/15/2013', value: 6} - - {date:'05/01/2013', value: 5} #6 - {date:'05/15/2013', value: 7} - - {date:'06/01/2013', value: 6} #7 - {date:'06/15/2013', value: 8} - - {date:'07/01/2013', value: 7} #8 - {date:'07/15/2013', value: 9} - - {date:'08/01/2013', value: 8} #9 - {date:'08/15/2013', value: 10} - - {date:'09/01/2013', value: 9} #10 - {date:'09/15/2013', value: 11} - - {date:'010/01/2013', value: 10} #11 - {date:'010/15/2013', value: 12} - - # This month should condense each week - {date:'011/01/2013', value: 12} - {date:'011/02/2013', value: 13} - {date:'011/03/2013', value: 14} - {date:'011/04/2013', value: 15} - ] - after.history = {exp: _.cloneDeep(history), todos: _.cloneDeep(history)} - after.habits[0].history = _.cloneDeep(history) - after.fns.cron() - - # remove history entries created by cron - after.history.exp.pop() - after.history.todos.pop() - - _.each [after.history.exp, after.history.todos, after.habits[0].history], (arr) -> - expect(_.map(arr, (x)->x.value)).to.eql [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] - - describe 'Todos', -> - it '1 day missed', -> - {before,after} = beforeAfter({daysAgo:1}) - before.dailys = after.dailys = [] - after.fns.cron() - - # todos don't effect stats - expect(after).toHaveHP 50 - expect(after).toHaveExp 0 - expect(after).toHaveGP 0 - - # but they devalue - expect(before.todos[0].value).to.be 0 # sanity check for task setup - expect(after.todos[0].value).to.be -1 # the actual test - expect(after.history.todos).to.have.length 1 - - it '2 days missed', -> - {before,after} = beforeAfter({daysAgo:2}) - before.dailys = after.dailys = [] - after.fns.cron() - - # todos devalue by only one day's worth of devaluation - expect(before.todos[0].value).to.be 0 # sanity check for task setup - expect(after.todos[0].value).to.be -1 # the actual test - - # I used hard-coded dates here instead of 'now' so the tests don't fail - # when you run them between midnight and dayStart. Nothing worse than - # intermittent failures. - describe 'cron day calculations', -> - dayStart = 4 - fstr = "YYYY-MM-DD HH:mm:ss" - - it 'startOfDay before dayStart', -> - # If the time is before dayStart, then we expect the start of the day to be yesterday at dayStart - start = shared.startOfDay {now: moment('2014-10-09 02:30:00'), dayStart} - expect(start.format(fstr)).to.eql '2014-10-08 04:00:00' - - it 'startOfDay after dayStart', -> - # If the time is after dayStart, then we expect the start of the day to be today at dayStart - start = shared.startOfDay {now: moment('2014-10-09 05:30:00'), dayStart} - expect(start.format(fstr)).to.eql '2014-10-09 04:00:00' - - it 'daysSince cron before, now after', -> - # If the lastCron was before dayStart, then a time on the same day after dayStart - # should be 1 day later than lastCron - lastCron = moment('2014-10-09 02:30:00') - days = shared.daysSince(lastCron, {now: moment('2014-10-09 11:30:00'), dayStart}) - expect(days).to.eql 1 - - it 'daysSince cron before, now before', -> - # If the lastCron was before dayStart, then a time on the same day also before dayStart - # should be 0 days later than lastCron - lastCron = moment('2014-10-09 02:30:00') - days = shared.daysSince(lastCron, {now: moment('2014-10-09 03:30:00'), dayStart}) - expect(days).to.eql 0 - - it 'daysSince cron after, now after', -> - # If the lastCron was after dayStart, then a time on the same day also after dayStart - # should be 0 days later than lastCron - lastCron = moment('2014-10-09 05:30:00') - days = shared.daysSince(lastCron, {now: moment('2014-10-09 06:30:00'), dayStart}) - expect(days).to.eql 0 - - it 'daysSince cron after, now tomorrow before', -> - # If the lastCron was after dayStart, then a time on the following day but before dayStart - # should be 0 days later than lastCron - lastCron = moment('2014-10-09 12:30:00') - days = shared.daysSince(lastCron, {now: moment('2014-10-10 01:30:00'), dayStart}) - expect(days).to.eql 0 - - it 'daysSince cron after, now tomorrow after', -> - # If the lastCron was after dayStart, then a time on the following day and after dayStart - # should be 1 day later than lastCron - lastCron = moment('2014-10-09 12:30:00') - days = shared.daysSince(lastCron, {now: moment('2014-10-10 10:30:00'), dayStart}) - expect(days).to.eql 1 - - xit 'daysSince, last cron before new dayStart', -> - # If lastCron was after dayStart (at 1am) with dayStart set at 0, changing dayStart to 4am - # should not trigger another cron the same day - - # dayStart is 0 - lastCron = moment('2014-10-09 01:00:00') - # dayStart is 4 - days = shared.daysSince(lastCron, {now: moment('2014-10-09 05:00:00'), dayStart}) - expect(days).to.eql 0 - - describe 'dailies', -> - - describe 'new day', -> - - ### - This section runs through a "cron matrix" of all permutations (that I can easily account for). It sets - 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 - ### - - runCron = (options) -> - _.each [480, 240, 0, -120], (timezoneOffset) -> # test different timezones - now = shared.startOfWeek({timezoneOffset}).add(options.currentHour||0, 'hours') - {before,after} = beforeAfter({now, timezoneOffset, daysAgo:1, cronAfterStart:options.cronAfterStart||true, dayStart:options.dayStart||0, limitOne:'daily'}) - before.dailys[0].repeat = after.dailys[0].repeat = options.repeat if options.repeat - before.dailys[0].streak = after.dailys[0].streak = 10 - before.dailys[0].completed = after.dailys[0].completed = true if options.checked - before.dailys[0].startDate = after.dailys[0].startDate = moment().subtract(30, 'days') - if options.shouldDo - expect(shared.shouldDo(now.toDate(), after.dailys[0], {timezoneOffset, dayStart:options.dayStart, now})).to.be.ok() - after.fns.cron {now} - before.stats.mp=after.stats.mp #FIXME - switch options.expect - when 'losePoints' then expectLostPoints(before,after,'daily') - when 'noChange' then expectNoChange(before,after) - when 'noDamage' then expectDayResetNoDamage(before,after) - {before,after} - - # These test cases were written assuming that lastCron was run after dayStart - # even if currentHour < dayStart and lastCron = yesterday at currentHour. - # cronAfterStart makes sure that lastCron is moved to be after dayStart. - cronMatrix = - steps: - - 'due yesterday': - defaults: {daysAgo:1, cronAfterStart:true, limitOne: 'daily'} - steps: - - '(simple)': {expect:'losePoints'} - - 'due today': - # NOTE: a strange thing here, moment().startOf('week') is Sunday, but moment.zone(myTimeZone).startOf('week') is Monday. - defaults: {repeat:{su:true,m:true,t:true,w:true,th:true,f:true,s:true}} - steps: - 'pre-dayStart': - defaults: {currentHour:3, dayStart:4, shouldDo:true} - steps: - 'checked': {checked: true, expect:'noChange'} - 'un-checked': {checked: false, expect:'noChange'} - 'post-dayStart': - defaults: {currentHour:5, dayStart:4, shouldDo:true} - steps: - 'checked': {checked:true, expect:'noDamage'} - 'unchecked': {checked:false, expect: 'losePoints'} - - 'NOT due today': - defaults: {repeat:{su:true,m:false,t:true,w:true,th:true,f:true,s:true}} - steps: - 'pre-dayStart': - defaults: {currentHour:3, dayStart:4, shouldDo:true} - steps: - 'checked': {checked: true, expect:'noChange'} - 'un-checked': {checked: false, expect:'noChange'} - 'post-dayStart': - defaults: {currentHour:5, dayStart:4, shouldDo:false} - steps: - 'checked': {checked:true, expect:'noDamage'} - 'unchecked': {checked:false, expect: 'losePoints'} - - 'not due yesterday': - defaults: repeatWithoutLastWeekday() - steps: - '(simple)': {expect:'noDamage'} - 'post-dayStart': {currentHour:5,dayStart:4, expect:'noDamage'} - 'pre-dayStart': {currentHour:3, dayStart:4, expect:'noChange'} - - recurseCronMatrix = (obj, options={}) -> - if obj.steps - _.each obj.steps, (step, text) -> - o = _.cloneDeep options - o.text ?= ''; o.text += " #{text} " - recurseCronMatrix step, _.defaults(o,obj.defaults) - else - it "#{options.text}", -> runCron(_.defaults(obj,options)) - recurseCronMatrix(cronMatrix) - -describe 'Helper', -> - - it 'calculates gold coins', -> - expect(shared.gold(10)).to.eql 10 - expect(shared.gold(1.957)).to.eql 1 - expect(shared.gold()).to.eql 0 - - it 'calculates silver coins', -> - expect(shared.silver(10)).to.eql 0 - expect(shared.silver(1.957)).to.eql 95 - expect(shared.silver(0.01)).to.eql "01" - expect(shared.silver()).to.eql "00" - - it 'calculates experience to next level', -> - expect(shared.tnl 1).to.eql 150 - expect(shared.tnl 2).to.eql 160 - expect(shared.tnl 10).to.eql 260 - expect(shared.tnl 99).to.eql 3580 - - it 'calculates the start of the day', -> - fstr = 'YYYY-MM-DD HH:mm:ss' - today = '2013-01-01 00:00:00' - # get the timezone for the day, so the test case doesn't fail - # if you run it during daylight savings time because by default - # it uses moment().zone() which is the current minute offset - zone = moment(today).zone() - expect(shared.startOfDay({now: new Date(2013, 0, 1, 0)}, timezoneOffset:zone).format(fstr)).to.eql today - expect(shared.startOfDay({now: new Date(2013, 0, 1, 5)}, timezoneOffset:zone).format(fstr)).to.eql today - expect(shared.startOfDay({now: new Date(2013, 0, 1, 23, 59, 59), timezoneOffset:zone}).format(fstr)).to.eql today diff --git a/test/common/algos.mocha.js b/test/common/algos.mocha.js new file mode 100644 index 0000000000..06ebd3044d --- /dev/null +++ b/test/common/algos.mocha.js @@ -0,0 +1,1507 @@ +var $w, _, beforeAfter, cycle, expect, expectClosePoints, expectDayResetNoDamage, expectGainedPoints, expectLostPoints, expectNoChange, expectStrings, moment, newUser, repeatWithoutLastWeekday, rewrapUser, shared, sinon, test_helper; + +_ = require('lodash'); + +expect = require('expect.js'); + +sinon = require('sinon'); + +moment = require('moment'); + +shared = require('../../common/script/index.js'); + +shared.i18n.translations = require('../../website/src/libs/i18n.js').translations; + +test_helper = require('./test_helper'); + +test_helper.addCustomMatchers(); + +$w = function(s) { + return s.split(' '); +}; + + +/* Helper Functions */ + +newUser = function(addTasks) { + var buffs, user; + if (addTasks == null) { + addTasks = true; + } + buffs = { + per: 0, + int: 0, + con: 0, + str: 0, + stealth: 0, + streaks: false + }; + user = { + auth: { + timestamps: {} + }, + stats: { + str: 1, + con: 1, + per: 1, + int: 1, + mp: 32, + "class": 'warrior', + buffs: buffs + }, + items: { + lastDrop: { + count: 0 + }, + hatchingPotions: {}, + eggs: {}, + food: {}, + gear: { + equipped: {}, + costume: {}, + owned: {} + }, + quests: {} + }, + party: { + quest: { + progress: { + down: 0 + } + } + }, + preferences: { + autoEquip: true + }, + dailys: [], + todos: [], + rewards: [], + flags: {}, + achievements: { + ultimateGearSets: {} + }, + contributor: { + level: 2 + }, + _tmp: {} + }; + shared.wrap(user); + user.ops.reset(null, function() {}); + if (addTasks) { + _.each(['habit', 'todo', 'daily'], function(task) { + return user.ops.addTask({ + body: { + type: task, + id: shared.uuid() + } + }); + }); + } + return user; +}; + +rewrapUser = function(user) { + user._wrapped = false; + shared.wrap(user); + return user; +}; + +expectStrings = function(obj, paths) { + return _.each(paths, function(path) { + return expect(obj[path]).to.be.ok(); + }); +}; + +beforeAfter = function(options) { + var after, before, lastCron, ref, user; + if (options == null) { + options = {}; + } + user = newUser(); + ref = [user, _.cloneDeep(user)], before = ref[0], after = ref[1]; + 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) { + before[options.limitOne + "s"] = [before[options.limitOne + "s"][0]]; + after[options.limitOne + "s"] = [after[options.limitOne + "s"][0]]; + } + if (options.daysAgo) { + lastCron = moment(options.now || +(new Date)).subtract({ + days: options.daysAgo + }); + } + if (options.daysAgo && options.cronAfterStart) { + lastCron.add({ + hours: options.dayStart, + minutes: 1 + }); + } + if (options.daysAgo) { + lastCron = +lastCron; + } + _.each([before, after], function(obj) { + if (options.daysAgo) { + return obj.lastCron = lastCron; + } + }); + return { + before: before, + after: after + }; +}; + +expectLostPoints = function(before, after, taskType) { + if (taskType === 'daily' || taskType === 'habit') { + expect(after.stats.hp).to.be.lessThan(before.stats.hp); + expect(after[taskType + "s"][0].history).to.have.length(1); + } else { + expect(after.history.todos).to.have.length(1); + } + expect(after).toHaveExp(0); + expect(after).toHaveGP(0); + return expect(after[taskType + "s"][0].value).to.be.lessThan(before[taskType + "s"][0].value); +}; + +expectGainedPoints = function(before, after, taskType) { + 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); + expect(after[taskType + "s"][0].value).to.be.greaterThan(before[taskType + "s"][0].value); + if (taskType === 'habit') { + return expect(after[taskType + "s"][0].history).to.have.length(1); + } +}; + +expectNoChange = function(before, after) { + return _.each($w('stats items gear dailys todos rewards preferences'), function(attr) { + return expect(after[attr]).to.eql(before[attr]); + }); +}; + +expectClosePoints = function(before, after, taskType) { + 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); + return expect(Math.abs(after[taskType + "s"][0].value - before[taskType + "s"][0].value)).to.be.lessThan(0.0001); +}; + +expectDayResetNoDamage = function(b, a) { + var after, before, ref; + ref = [_.cloneDeep(b), _.cloneDeep(a)], before = ref[0], after = ref[1]; + _.each(after.dailys, function(task, i) { + expect(task.completed).to.be(false); + expect(before.dailys[i].value).to.be(task.value); + expect(before.dailys[i].streak).to.be(task.streak); + return expect(task.history).to.have.length(1); + }); + _.each(after.todos, function(task, i) { + expect(task.completed).to.be(false); + return expect(before.todos[i].value).to.be.greaterThan(task.value); + }); + expect(after.history.todos).to.have.length(1); + _.each([before, after], function(obj) { + delete obj.stats.buffs; + return _.each($w('dailys todos history lastCron'), function(path) { + return delete obj[path]; + }); + }); + delete after._tmp; + return expectNoChange(before, after); +}; + +cycle = function(array) { + var n; + n = -1; + return function(seed) { + if (seed == null) { + seed = 0; + } + n++; + return array[n % array.length]; + }; +}; + +repeatWithoutLastWeekday = function() { + var repeat; + repeat = { + su: true, + m: true, + t: true, + w: true, + th: true, + f: true, + s: true + }; + if (shared.startOfWeek(moment().zone(0)).isoWeekday() === 1) { + repeat.su = false; + } else { + repeat.s = false; + } + return { + repeat: repeat + }; +}; + +describe('User', function() { + it('sets correct user defaults', function() { + var base_gear, buffs, user; + user = newUser(); + base_gear = { + armor: 'armor_base_0', + weapon: 'weapon_base_0', + head: 'head_base_0', + shield: 'shield_base_0' + }; + buffs = { + per: 0, + int: 0, + con: 0, + str: 0, + stealth: 0, + streaks: false + }; + expect(user.stats).to.eql({ + str: 1, + con: 1, + per: 1, + int: 1, + hp: 50, + mp: 32, + lvl: 1, + exp: 0, + gp: 0, + "class": 'warrior', + buffs: buffs + }); + expect(user.items.gear).to.eql({ + equipped: base_gear, + costume: base_gear, + owned: { + weapon_warrior_0: true + } + }); + return expect(user.preferences).to.eql({ + autoEquip: true, + costume: false + }); + }); + it('calculates max MP', function() { + var user; + user = newUser(); + expect(user).toHaveMaxMP(32); + user.stats.int = 10; + expect(user).toHaveMaxMP(50); + user.stats.lvl = 5; + expect(user).toHaveMaxMP(54); + user.stats["class"] = 'wizard'; + user.items.gear.equipped.weapon = 'weapon_wizard_1'; + return expect(user).toHaveMaxMP(63); + }); + it('handles perfect days', function() { + var cron, user, yesterday; + user = newUser(); + user.dailys = []; + _.times(3, function() { + return user.dailys.push(shared.taskDefaults({ + type: 'daily', + startDate: moment().subtract(7, 'days') + })); + }); + cron = function() { + user.lastCron = moment().subtract(1, 'days'); + return user.fns.cron(); + }; + 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(); + _.each(user.dailys, function(d) { + return d.completed = true; + }); + cron(); + expect(user.stats.buffs.str).to.be(1); + expect(user.achievements.perfect).to.be(1); + yesterday = moment().subtract(1, 'days'); + user.dailys[0].repeat[shared.dayMapping[yesterday.day()]] = false; + _.each(user.dailys.slice(1), function(d) { + return d.completed = true; + }); + cron(); + expect(user.stats.buffs.str).to.be(1); + return expect(user.achievements.perfect).to.be(2); + }); + describe('Resting in the Inn', function() { + var cron, user; + user = null; + cron = null; + beforeEach(function() { + user = newUser(); + user.preferences.sleep = true; + cron = function() { + user.lastCron = moment().subtract(1, 'days'); + return user.fns.cron(); + }; + user.dailys = []; + return _.times(2, function() { + return user.dailys.push(shared.taskDefaults({ + type: 'daily', + startDate: moment().subtract(7, 'days') + })); + }); + }); + it('remains in the inn on cron', function() { + cron(); + return expect(user.preferences.sleep).to.be(true); + }); + it('resets dailies', function() { + user.dailys[0].completed = true; + cron(); + return expect(user.dailys[0].completed).to.be(false); + }); + it('resets checklist on incomplete dailies', function() { + user.dailys[0].checklist = [ + { + "text": "1", + "id": "checklist-one", + "completed": true + }, { + "text": "2", + "id": "checklist-two", + "completed": true + }, { + "text": "3", + "id": "checklist-three", + "completed": false + } + ]; + cron(); + return _.each(user.dailys[0].checklist, function(box) { + return expect(box.completed).to.be(false); + }); + }); + it('resets checklist on complete dailies', function() { + user.dailys[0].checklist = [ + { + "text": "1", + "id": "checklist-one", + "completed": true + }, { + "text": "2", + "id": "checklist-two", + "completed": true + }, { + "text": "3", + "id": "checklist-three", + "completed": false + } + ]; + user.dailys[0].completed = true; + cron(); + return _.each(user.dailys[0].checklist, function(box) { + return expect(box.completed).to.be(false); + }); + }); + it('does not reset checklist on grey incomplete dailies', function() { + var yesterday; + yesterday = moment().subtract(1, 'days'); + user.dailys[0].repeat[shared.dayMapping[yesterday.day()]] = false; + user.dailys[0].checklist = [ + { + "text": "1", + "id": "checklist-one", + "completed": true + }, { + "text": "2", + "id": "checklist-two", + "completed": true + }, { + "text": "3", + "id": "checklist-three", + "completed": true + } + ]; + cron(); + return _.each(user.dailys[0].checklist, function(box) { + return expect(box.completed).to.be(true); + }); + }); + it('resets checklist on complete grey complete dailies', function() { + var yesterday; + yesterday = moment().subtract(1, 'days'); + user.dailys[0].repeat[shared.dayMapping[yesterday.day()]] = false; + user.dailys[0].checklist = [ + { + "text": "1", + "id": "checklist-one", + "completed": true + }, { + "text": "2", + "id": "checklist-two", + "completed": true + }, { + "text": "3", + "id": "checklist-three", + "completed": true + } + ]; + user.dailys[0].completed = true; + cron(); + return _.each(user.dailys[0].checklist, function(box) { + return expect(box.completed).to.be(false); + }); + }); + it('does not damage user for incomplete dailies', function() { + expect(user).toHaveHP(50); + user.dailys[0].completed = true; + user.dailys[1].completed = false; + cron(); + return expect(user).toHaveHP(50); + }); + it('gives credit for complete dailies', function() { + user.dailys[0].completed = true; + expect(user.dailys[0].history).to.be.empty; + cron(); + return expect(user.dailys[0].history).to.not.be.empty; + }); + return it('damages user for incomplete dailies after checkout', function() { + expect(user).toHaveHP(50); + user.dailys[0].completed = true; + user.dailys[1].completed = false; + user.preferences.sleep = false; + cron(); + return expect(user.stats.hp).to.be.lessThan(50); + }); + }); + describe('Death', function() { + var user; + user = void 0; + it('revives correctly', function() { + user = newUser(); + user.stats = { + gp: 10, + exp: 100, + lvl: 2, + hp: 0, + "class": 'warrior' + }; + user.ops.revive(); + expect(user).toHaveGP(0); + expect(user).toHaveExp(0); + expect(user).toHaveLevel(1); + expect(user).toHaveHP(50); + return expect(user.items.gear.owned).to.eql({ + weapon_warrior_0: false + }); + }); + it("doesn't break unbreakables", function() { + var ce; + ce = shared.countExists; + user = newUser(); + user.items.gear.owned['shield_warrior_1'] = true; + user.items.gear.owned['shield_rogue_1'] = true; + user.items.gear.owned['head_special_nye'] = true; + 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); + return expect(user.items.gear.owned).to.eql({ + weapon_warrior_0: false, + shield_warrior_1: false, + shield_rogue_1: true, + head_special_nye: true + }); + }); + return it("handles event items", function() { + 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); + delete user.items.gear.owned['head_special_nye']; + 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'); + return expect(shared.content.gear.flat.head_special_nye.canOwn(user)).to.be(true); + }); + }); + describe('Rebirth', function() { + var user; + user = void 0; + return it('removes correct gear', function() { + user = newUser(); + user.stats.lvl = 100; + user.items.gear.owned = { + "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 + }; + user.ops.rebirth(); + return 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 + }); + }); + }); + describe('store', function() { + it('buys a Quest scroll', function() { + var user; + user = newUser(); + user.stats.gp = 205; + user.ops.buyQuest({ + params: { + key: 'dilatoryDistress1' + } + }); + expect(user.items.quests).to.eql({ + dilatoryDistress1: 1 + }); + return expect(user).toHaveGP(5); + }); + it('does not buy Quests without enough Gold', function() { + var user; + user = newUser(); + user.stats.gp = 1; + user.ops.buyQuest({ + params: { + key: 'dilatoryDistress1' + } + }); + expect(user.items.quests).to.eql({}); + return expect(user).toHaveGP(1); + }); + it('does not buy nonexistent Quests', function() { + var user; + user = newUser(); + user.stats.gp = 9999; + user.ops.buyQuest({ + params: { + key: 'snarfblatter' + } + }); + expect(user.items.quests).to.eql({}); + return expect(user).toHaveGP(9999); + }); + return it('does not buy Gem-premium Quests', function() { + var user; + user = newUser(); + user.stats.gp = 9999; + user.ops.buyQuest({ + params: { + key: 'kraken' + } + }); + expect(user.items.quests).to.eql({}); + return expect(user).toHaveGP(9999); + }); + }); + describe('Gem purchases', function() { + it('does not purchase items without enough Gems', function() { + var user; + user = newUser(); + user.ops.purchase({ + params: { + type: 'eggs', + key: 'Cactus' + } + }); + user.ops.purchase({ + params: { + type: 'gear', + key: 'headAccessory_special_foxEars' + } + }); + user.ops.unlock({ + query: { + 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' + } + }); + expect(user.items.eggs).to.eql({}); + return expect(user.items.gear.owned).to.eql({ + weapon_warrior_0: true + }); + }); + it('purchases an egg', function() { + var user; + user = newUser(); + user.balance = 1; + user.ops.purchase({ + params: { + type: 'eggs', + key: 'Cactus' + } + }); + expect(user.items.eggs).to.eql({ + Cactus: 1 + }); + return expect(user.balance).to.eql(0.25); + }); + it('purchases fox ears', function() { + var user; + user = newUser(); + user.balance = 1; + user.ops.purchase({ + params: { + type: 'gear', + key: 'headAccessory_special_foxEars' + } + }); + expect(user.items.gear.owned).to.eql({ + weapon_warrior_0: true, + headAccessory_special_foxEars: true + }); + return expect(user.balance).to.eql(0.5); + }); + return it('unlocks all the animal ears at once', function() { + var user; + user = newUser(); + user.balance = 2; + user.ops.unlock({ + query: { + 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' + } + }); + 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, + headAccessory_special_wolfEars: true + }); + return expect(user.balance).to.eql(0.75); + }); + }); + describe('spells', function() { + return _.each(shared.content.spells, function(spellClass) { + return _.each(spellClass, function(spell) { + return it(spell.text + " has valid values", function() { + 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); + } + return expect(spell.cast).to.be.a('function'); + }); + }); + }); + }); + describe('drop system', function() { + var MAX_RANGE_FOR_EGG, MAX_RANGE_FOR_FOOD, MAX_RANGE_FOR_POTION, MIN_RANGE_FOR_EGG, MIN_RANGE_FOR_FOOD, MIN_RANGE_FOR_POTION, user; + user = null; + MIN_RANGE_FOR_POTION = 0; + MAX_RANGE_FOR_POTION = .3; + MIN_RANGE_FOR_EGG = .4; + MAX_RANGE_FOR_EGG = .6; + MIN_RANGE_FOR_FOOD = .7; + MAX_RANGE_FOR_FOOD = 1; + beforeEach(function() { + user = newUser(); + user.flags.dropsEnabled = true; + this.task_id = shared.uuid(); + return user.ops.addTask({ + body: { + type: 'daily', + id: this.task_id + } + }); + }); + it('drops a hatching potion', function() { + var j, random, ref, ref1, results; + results = []; + for (random = j = ref = MIN_RANGE_FOR_POTION, ref1 = MAX_RANGE_FOR_POTION; j <= ref1; random = j += .1) { + sinon.stub(user.fns, 'predictableRandom').returns(random); + user.ops.score({ + params: { + id: this.task_id, + direction: 'up' + } + }); + 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; + }); + it('drops a pet egg', function() { + var j, random, ref, ref1, results; + results = []; + for (random = j = ref = MIN_RANGE_FOR_EGG, ref1 = MAX_RANGE_FOR_EGG; j <= ref1; random = j += .1) { + sinon.stub(user.fns, 'predictableRandom').returns(random); + user.ops.score({ + params: { + id: this.task_id, + direction: 'up' + } + }); + 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; + }); + it('drops food', function() { + var j, random, ref, ref1, results; + results = []; + for (random = j = ref = MIN_RANGE_FOR_FOOD, ref1 = MAX_RANGE_FOR_FOOD; j <= ref1; random = j += .1) { + sinon.stub(user.fns, 'predictableRandom').returns(random); + user.ops.score({ + params: { + id: this.task_id, + direction: 'up' + } + }); + 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; + }); + return it('does not get a drop', function() { + sinon.stub(user.fns, 'predictableRandom').returns(0.5); + user.ops.score({ + params: { + id: this.task_id, + direction: 'up' + } + }); + expect(user.items.eggs).to.eql({}); + expect(user.items.hatchingPotions).to.eql({}); + expect(user.items.food).to.eql({}); + return user.fns.predictableRandom.restore(); + }); + }); + describe('Quests', function() { + return _.each(shared.content.quests, function(quest) { + return it((quest.text()) + " has valid values", function() { + 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); + return expect(quest.boss.str).to.be.greaterThan(0); + } else if (quest.collect) { + return _.each(quest.collect, function(collect) { + expect(collect.text()).to.be.an('string'); + return expect(collect.count).to.be.greaterThan(0); + }); + } + }); + }); + }); + describe('Achievements', function() { + _.each(shared.content.classes, function(klass) { + var user; + user = newUser(); + user.stats.gp = 10000; + _.each(shared.content.gearTypes, function(type) { + return _.each([1, 2, 3, 4, 5], function(i) { + return user.ops.buy({ + params: '#{type}_#{klass}_#{i}' + }); + }); + }); + it('does not get ultimateGear ' + klass, function() { + return expect(user.achievements.ultimateGearSets[klass]).to.not.be.ok(); + }); + _.each(shared.content.gearTypes, function(type) { + return user.ops.buy({ + params: '#{type}_#{klass}_6' + }); + }); + return xit('gets ultimateGear ' + klass, function() { + return expect(user.achievements.ultimateGearSets[klass]).to.be.ok(); + }); + }); + return it('does not remove existing Ultimate Gear achievements', function() { + var user; + user = newUser(); + user.achievements.ultimateGearSets = { + 'healer': true, + 'wizard': true, + 'rogue': true, + 'warrior': true + }; + user.items.gear.owned.shield_warrior_5 = false; + user.items.gear.owned.weapon_rogue_6 = false; + user.ops.buy({ + params: 'shield_warrior_5' + }); + return expect(user.achievements.ultimateGearSets).to.eql({ + 'healer': true, + 'wizard': true, + 'rogue': true, + 'warrior': true + }); + }); + }); + return describe('unlocking features', function() { + it('unlocks drops at level 3', function() { + var user; + user = newUser(); + user.stats.lvl = 3; + user.fns.updateStats(user.stats); + return expect(user.flags.dropsEnabled).to.be.ok(); + }); + it('unlocks Rebirth at level 50', function() { + var user; + user = newUser(); + user.stats.lvl = 50; + user.fns.updateStats(user.stats); + return expect(user.flags.rebirthEnabled).to.be.ok(); + }); + return describe('level-awarded Quests', function() { + it('gets Attack of the Mundane at level 15', function() { + var user; + user = newUser(); + user.stats.lvl = 15; + user.fns.updateStats(user.stats); + expect(user.flags.levelDrops.atom1).to.be.ok(); + return expect(user.items.quests.atom1).to.eql(1); + }); + it('gets Vice at level 30', function() { + var user; + user = newUser(); + user.stats.lvl = 30; + user.fns.updateStats(user.stats); + expect(user.flags.levelDrops.vice1).to.be.ok(); + return expect(user.items.quests.vice1).to.eql(1); + }); + it('gets Golden Knight at level 40', function() { + var user; + user = newUser(); + user.stats.lvl = 40; + user.fns.updateStats(user.stats); + expect(user.flags.levelDrops.goldenknight1).to.be.ok(); + return expect(user.items.quests.goldenknight1).to.eql(1); + }); + return it('gets Moonstone Chain at level 60', function() { + var user; + user = newUser(); + user.stats.lvl = 60; + user.fns.updateStats(user.stats); + expect(user.flags.levelDrops.moonstone1).to.be.ok(); + return expect(user.items.quests.moonstone1).to.eql(1); + }); + }); + }); +}); + +describe('Simple Scoring', function() { + beforeEach(function() { + var ref; + return ref = beforeAfter(), this.before = ref.before, this.after = ref.after, ref; + }); + it('Habits : Up', function() { + this.after.ops.score({ + params: { + id: this.after.habits[0].id, + direction: 'down' + }, + query: { + times: 5 + } + }); + return expectLostPoints(this.before, this.after, 'habit'); + }); + it('Habits : Down', function() { + this.after.ops.score({ + params: { + id: this.after.habits[0].id, + direction: 'up' + }, + query: { + times: 5 + } + }); + return expectGainedPoints(this.before, this.after, 'habit'); + }); + it('Dailys : Up', function() { + this.after.ops.score({ + params: { + id: this.after.dailys[0].id, + direction: 'up' + } + }); + return expectGainedPoints(this.before, this.after, 'daily'); + }); + it('Dailys : Up, Down', function() { + this.after.ops.score({ + params: { + id: this.after.dailys[0].id, + direction: 'up' + } + }); + this.after.ops.score({ + params: { + id: this.after.dailys[0].id, + direction: 'down' + } + }); + return expectClosePoints(this.before, this.after, 'daily'); + }); + it('Todos : Up', function() { + this.after.ops.score({ + params: { + id: this.after.todos[0].id, + direction: 'up' + } + }); + return expectGainedPoints(this.before, this.after, 'todo'); + }); + return it('Todos : Up, Down', function() { + this.after.ops.score({ + params: { + id: this.after.todos[0].id, + direction: 'up' + } + }); + this.after.ops.score({ + params: { + id: this.after.todos[0].id, + direction: 'down' + } + }); + return expectClosePoints(this.before, this.after, 'todo'); + }); +}); + +describe('Cron', function() { + it('computes shouldCron', function() { + var paths, user; + user = newUser(); + paths = {}; + user.fns.cron({ + paths: paths + }); + expect(user.lastCron).to.not.be.ok; + user.lastCron = +moment().subtract(1, 'days'); + paths = {}; + user.fns.cron({ + paths: paths + }); + return expect(user.lastCron).to.be.greaterThan(0); + }); + it('only dailies & todos are affected', function() { + var after, afterTasks, before, beforeTasks, ref; + ref = beforeAfter({ + daysAgo: 1 + }), before = ref.before, after = ref.after; + 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); + beforeTasks = before.habits.concat(before.dailys).concat(before.todos).concat(before.rewards); + afterTasks = after.habits.concat(after.dailys).concat(after.todos).concat(after.rewards); + return expect(beforeTasks).to.eql(afterTasks); + }); + describe('preening', function() { + beforeEach(function() { + return this.clock = sinon.useFakeTimers(Date.parse("2013-11-20"), "Date"); + }); + afterEach(function() { + return this.clock.restore(); + }); + return it('should preen user history', function() { + var after, before, history, ref; + ref = beforeAfter({ + daysAgo: 1 + }), before = ref.before, after = ref.after; + history = [ + { + date: '09/01/2012', + value: 0 + }, { + date: '10/01/2012', + value: 0 + }, { + date: '11/01/2012', + value: 2 + }, { + date: '12/01/2012', + value: 2 + }, { + date: '01/01/2013', + value: 1 + }, { + date: '01/15/2013', + value: 3 + }, { + date: '02/01/2013', + value: 2 + }, { + date: '02/15/2013', + value: 4 + }, { + date: '03/01/2013', + value: 3 + }, { + date: '03/15/2013', + value: 5 + }, { + date: '04/01/2013', + value: 4 + }, { + date: '04/15/2013', + value: 6 + }, { + date: '05/01/2013', + value: 5 + }, { + date: '05/15/2013', + value: 7 + }, { + date: '06/01/2013', + value: 6 + }, { + date: '06/15/2013', + value: 8 + }, { + date: '07/01/2013', + value: 7 + }, { + date: '07/15/2013', + value: 9 + }, { + date: '08/01/2013', + value: 8 + }, { + date: '08/15/2013', + value: 10 + }, { + date: '09/01/2013', + value: 9 + }, { + date: '09/15/2013', + value: 11 + }, { + date: '010/01/2013', + value: 10 + }, { + date: '010/15/2013', + value: 12 + }, { + date: '011/01/2013', + value: 12 + }, { + date: '011/02/2013', + value: 13 + }, { + date: '011/03/2013', + value: 14 + }, { + date: '011/04/2013', + value: 15 + } + ]; + after.history = { + exp: _.cloneDeep(history), + todos: _.cloneDeep(history) + }; + after.habits[0].history = _.cloneDeep(history); + after.fns.cron(); + after.history.exp.pop(); + after.history.todos.pop(); + return _.each([after.history.exp, after.history.todos, after.habits[0].history], function(arr) { + return expect(_.map(arr, function(x) { + return x.value; + })).to.eql([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); + }); + }); + }); + describe('Todos', function() { + it('1 day missed', function() { + var after, before, ref; + ref = beforeAfter({ + daysAgo: 1 + }), before = ref.before, after = ref.after; + 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); + return expect(after.history.todos).to.have.length(1); + }); + return it('2 days missed', function() { + var after, before, ref; + ref = beforeAfter({ + daysAgo: 2 + }), before = ref.before, after = ref.after; + before.dailys = after.dailys = []; + after.fns.cron(); + expect(before.todos[0].value).to.be(0); + return expect(after.todos[0].value).to.be(-1); + }); + }); + describe('cron day calculations', function() { + var dayStart, fstr; + dayStart = 4; + fstr = "YYYY-MM-DD HH:mm:ss"; + it('startOfDay before dayStart', function() { + var start; + start = shared.startOfDay({ + now: moment('2014-10-09 02:30:00'), + dayStart: dayStart + }); + return expect(start.format(fstr)).to.eql('2014-10-08 04:00:00'); + }); + it('startOfDay after dayStart', function() { + var start; + start = shared.startOfDay({ + now: moment('2014-10-09 05:30:00'), + dayStart: dayStart + }); + return expect(start.format(fstr)).to.eql('2014-10-09 04:00:00'); + }); + it('daysSince cron before, now after', function() { + var days, lastCron; + lastCron = moment('2014-10-09 02:30:00'); + days = shared.daysSince(lastCron, { + now: moment('2014-10-09 11:30:00'), + dayStart: dayStart + }); + return expect(days).to.eql(1); + }); + it('daysSince cron before, now before', function() { + var days, lastCron; + lastCron = moment('2014-10-09 02:30:00'); + days = shared.daysSince(lastCron, { + now: moment('2014-10-09 03:30:00'), + dayStart: dayStart + }); + return expect(days).to.eql(0); + }); + it('daysSince cron after, now after', function() { + var days, lastCron; + lastCron = moment('2014-10-09 05:30:00'); + days = shared.daysSince(lastCron, { + now: moment('2014-10-09 06:30:00'), + dayStart: dayStart + }); + return expect(days).to.eql(0); + }); + it('daysSince cron after, now tomorrow before', function() { + var days, lastCron; + lastCron = moment('2014-10-09 12:30:00'); + days = shared.daysSince(lastCron, { + now: moment('2014-10-10 01:30:00'), + dayStart: dayStart + }); + return expect(days).to.eql(0); + }); + it('daysSince cron after, now tomorrow after', function() { + var days, lastCron; + lastCron = moment('2014-10-09 12:30:00'); + days = shared.daysSince(lastCron, { + now: moment('2014-10-10 10:30:00'), + dayStart: dayStart + }); + return expect(days).to.eql(1); + }); + return xit('daysSince, last cron before new dayStart', function() { + var days, lastCron; + lastCron = moment('2014-10-09 01:00:00'); + days = shared.daysSince(lastCron, { + now: moment('2014-10-09 05:00:00'), + dayStart: dayStart + }); + return expect(days).to.eql(0); + }); + }); + return describe('dailies', function() { + return describe('new day', function() { + + /* + This section runs through a "cron matrix" of all permutations (that I can easily account for). It sets + 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 + */ + var cronMatrix, recurseCronMatrix, runCron; + runCron = function(options) { + return _.each([480, 240, 0, -120], function(timezoneOffset) { + var after, before, now, ref; + now = shared.startOfWeek({ + timezoneOffset: timezoneOffset + }).add(options.currentHour || 0, 'hours'); + ref = beforeAfter({ + now: now, + timezoneOffset: timezoneOffset, + daysAgo: 1, + cronAfterStart: options.cronAfterStart || true, + dayStart: options.dayStart || 0, + limitOne: 'daily' + }), before = ref.before, after = ref.after; + 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], { + timezoneOffset: timezoneOffset, + dayStart: options.dayStart, + now: now + })).to.be.ok(); + } + after.fns.cron({ + now: now + }); + before.stats.mp = after.stats.mp; + switch (options.expect) { + case 'losePoints': + expectLostPoints(before, after, 'daily'); + break; + case 'noChange': + expectNoChange(before, after); + break; + case 'noDamage': + expectDayResetNoDamage(before, after); + } + return { + before: before, + after: after + }; + }); + }; + cronMatrix = { + steps: { + 'due yesterday': { + defaults: { + daysAgo: 1, + cronAfterStart: true, + limitOne: 'daily' + }, + steps: { + '(simple)': { + expect: 'losePoints' + }, + 'due today': { + defaults: { + repeat: { + su: true, + m: true, + t: true, + w: true, + th: true, + f: true, + s: true + } + }, + steps: { + 'pre-dayStart': { + defaults: { + currentHour: 3, + dayStart: 4, + shouldDo: true + }, + steps: { + 'checked': { + checked: true, + expect: 'noChange' + }, + 'un-checked': { + checked: false, + expect: 'noChange' + } + } + }, + 'post-dayStart': { + defaults: { + currentHour: 5, + dayStart: 4, + shouldDo: true + }, + steps: { + 'checked': { + checked: true, + expect: 'noDamage' + }, + 'unchecked': { + checked: false, + expect: 'losePoints' + } + } + } + } + }, + 'NOT due today': { + defaults: { + repeat: { + su: true, + m: false, + t: true, + w: true, + th: true, + f: true, + s: true + } + }, + steps: { + 'pre-dayStart': { + defaults: { + currentHour: 3, + dayStart: 4, + shouldDo: true + }, + steps: { + 'checked': { + checked: true, + expect: 'noChange' + }, + 'un-checked': { + checked: false, + expect: 'noChange' + } + } + }, + 'post-dayStart': { + defaults: { + currentHour: 5, + dayStart: 4, + shouldDo: false + }, + steps: { + 'checked': { + checked: true, + expect: 'noDamage' + }, + 'unchecked': { + checked: false, + expect: 'losePoints' + } + } + } + } + } + } + }, + 'not due yesterday': { + defaults: repeatWithoutLastWeekday(), + steps: { + '(simple)': { + expect: 'noDamage' + }, + 'post-dayStart': { + currentHour: 5, + dayStart: 4, + expect: 'noDamage' + }, + 'pre-dayStart': { + currentHour: 3, + dayStart: 4, + expect: 'noChange' + } + } + } + } + }; + recurseCronMatrix = function(obj, options) { + if (options == null) { + options = {}; + } + if (obj.steps) { + return _.each(obj.steps, function(step, text) { + var o; + o = _.cloneDeep(options); + if (o.text == null) { + o.text = ''; + } + o.text += " " + text + " "; + return recurseCronMatrix(step, _.defaults(o, obj.defaults)); + }); + } else { + return it("" + options.text, function() { + return runCron(_.defaults(obj, options)); + }); + } + }; + return recurseCronMatrix(cronMatrix); + }); + }); +}); + +describe('Helper', function() { + it('calculates gold coins', function() { + expect(shared.gold(10)).to.eql(10); + expect(shared.gold(1.957)).to.eql(1); + return expect(shared.gold()).to.eql(0); + }); + it('calculates silver coins', function() { + expect(shared.silver(10)).to.eql(0); + expect(shared.silver(1.957)).to.eql(95); + expect(shared.silver(0.01)).to.eql("01"); + return expect(shared.silver()).to.eql("00"); + }); + it('calculates experience to next level', function() { + expect(shared.tnl(1)).to.eql(150); + expect(shared.tnl(2)).to.eql(160); + expect(shared.tnl(10)).to.eql(260); + return expect(shared.tnl(99)).to.eql(3580); + }); + return it('calculates the start of the day', function() { + var fstr, today, zone; + fstr = 'YYYY-MM-DD HH:mm:ss'; + today = '2013-01-01 00:00:00'; + zone = moment(today).zone(); + expect(shared.startOfDay({ + now: new Date(2013, 0, 1, 0) + }, { + timezoneOffset: zone + }).format(fstr)).to.eql(today); + expect(shared.startOfDay({ + now: new Date(2013, 0, 1, 5) + }, { + timezoneOffset: zone + }).format(fstr)).to.eql(today); + return expect(shared.startOfDay({ + now: new Date(2013, 0, 1, 23, 59, 59), + timezoneOffset: zone + }).format(fstr)).to.eql(today); + }); +}); diff --git a/test/common/dailies.coffee b/test/common/dailies.coffee deleted file mode 100644 index 71729ff77c..0000000000 --- a/test/common/dailies.coffee +++ /dev/null @@ -1,418 +0,0 @@ -_ = require 'lodash' -expect = require 'expect.js' -sinon = require 'sinon' -moment = require 'moment' -shared = require '../../common/script/index.js' -shared.i18n.translations = require('../../website/src/libs/i18n.js').translations - -repeatWithoutLastWeekday = ()-> - repeat = {su:true,m:true,t:true,w:true,th:true,f:true,s:true} - if shared.startOfWeek(moment().zone(0)).isoWeekday() == 1 # Monday - repeat.su = false - else - repeat.s = false - {repeat: repeat} - -### Helper Functions #### -# @TODO: Refactor into helper file -newUser = (addTasks=true)-> - buffs = {per:0, int:0, con:0, str:0, stealth: 0, streaks: false} - user = - auth: - timestamps: {} - stats: {str:1, con:1, per:1, int:1, mp: 32, class: 'warrior', buffs: buffs} - items: - lastDrop: - count: 0 - hatchingPotions: {} - eggs: {} - food: {} - gear: - equipped: {} - costume: {} - party: - quest: - progress: - down: 0 - preferences: {} - dailys: [] - todos: [] - rewards: [] - flags: {} - achievements: {} - contributor: - level: 2 - shared.wrap(user) - user.ops.reset(null, ->) - if addTasks - _.each ['habit', 'todo', 'daily'], (task)-> - user.ops.addTask {body: {type: task, id: shared.uuid()}} - user - -cron = (usr, missedDays=1) -> - usr.lastCron = moment().subtract(missedDays,'days') - usr.fns.cron() - -describe 'daily/weekly that repeats everyday (default)', -> - user = null - daily = null - weekly = null - - describe 'when startDate is in the future', -> - beforeEach -> - user = newUser() - user.dailys = [ - shared.taskDefaults({type:'daily', startDate: moment().add(7, 'days'), frequency: 'daily'}) - shared.taskDefaults({type:'daily', startDate: moment().add(7, 'days'), frequency: 'weekly', repeat: {su:true,m:true,t:true,w:true,th:true,f:true,s:true}}) - ] - daily = user.dailys[0] - weekly = user.dailys[1] - - it 'does not damage user for not completing it', -> - cron(user) - expect(user.stats.hp).to.be 50 - - it 'does not change value on cron if daily is incomplete', -> - cron(user) - expect(daily.value).to.be 0 - expect(weekly.value).to.be 0 - - it 'does not reset checklists if daily is not marked as complete', -> - checklist = [ - { - 'text' : '1', - 'id' : 'checklist-one', - 'completed' : true - }, - { - 'text' : '2', - 'id' : 'checklist-two', - 'completed' : true - }, - { - 'text' : '3', - 'id' : 'checklist-three', - 'completed' : false - } - ] - daily.checklist = checklist - weekly.checklist = checklist - cron(user) - - expect(daily.checklist[0].completed).to.be true - expect(daily.checklist[1].completed).to.be true - expect(daily.checklist[2].completed).to.be false - - expect(weekly.checklist[0].completed).to.be true - expect(weekly.checklist[1].completed).to.be true - expect(weekly.checklist[2].completed).to.be false - - it 'resets checklists if daily is marked as complete', -> - checklist = [ - { - 'text' : '1', - 'id' : 'checklist-one', - 'completed' : true - }, - { - 'text' : '2', - 'id' : 'checklist-two', - 'completed' : true - }, - { - 'text' : '3', - 'id' : 'checklist-three', - 'completed' : false - } - ] - daily.checklist = checklist - weekly.checklist = checklist - daily.completed = true - weekly.completed = true - cron(user) - - _.each daily.checklist, (box)-> - expect(box.completed).to.be false - - _.each weekly.checklist, (box)-> - expect(box.completed).to.be false - - it 'is due on startDate', -> - daily_due_today = shared.shouldDo moment(), daily - daily_due_on_start_date = shared.shouldDo moment().add(7, 'days'), daily - - expect(daily_due_today).to.be false - expect(daily_due_on_start_date).to.be true - - weekly_due_today = shared.shouldDo moment(), weekly - weekly_due_on_start_date = shared.shouldDo moment().add(7, 'days'), weekly - - expect(weekly_due_today).to.be false - expect(weekly_due_on_start_date).to.be true - - describe 'when startDate is in the past', -> - beforeEach -> - user = newUser() - user.dailys = [ - shared.taskDefaults({type:'daily', startDate: moment().subtract(7, 'days'), frequency: 'daily'}) - shared.taskDefaults({type:'daily', startDate: moment().subtract(7, 'days'), frequency: 'weekly'}) - ] - daily = user.dailys[0] - weekly = user.dailys[1] - - it 'does damage user for not completing it', -> - cron(user) - expect(user.stats.hp).to.be.lessThan 50 - - it 'decreases value on cron if daily is incomplete', -> - cron(user, 1) - expect(daily.value).to.be -1 - expect(weekly.value).to.be -1 - - it 'decreases value on cron once only if daily is incomplete and multiple days are missed', -> - cron(user, 7) - expect(daily.value).to.be -1 - expect(weekly.value).to.be -1 - - it 'resets checklists if daily is not marked as complete', -> - checklist = [ - { - 'text' : '1', - 'id' : 'checklist-one', - 'completed' : true - }, - { - 'text' : '2', - 'id' : 'checklist-two', - 'completed' : true - }, - { - 'text' : '3', - 'id' : 'checklist-three', - 'completed' : false - } - ] - daily.checklist = checklist - weekly.checklist = checklist - cron(user) - - _.each daily.checklist, (box)-> - expect(box.completed).to.be false - - _.each weekly.checklist, (box)-> - expect(box.completed).to.be false - - it 'resets checklists if daily is marked as complete', -> - checklist = [ - { - 'text' : '1', - 'id' : 'checklist-one', - 'completed' : true - }, - { - 'text' : '2', - 'id' : 'checklist-two', - 'completed' : true - }, - { - 'text' : '3', - 'id' : 'checklist-three', - 'completed' : false - } - ] - daily.checklist = checklist - daily.completed = true - weekly.checklist = checklist - weekly.completed = true - cron(user) - - _.each daily.checklist, (box)-> - expect(box.completed).to.be false - - _.each weekly.checklist, (box)-> - expect(box.completed).to.be false - - describe 'when startDate is today', -> - beforeEach -> - user = newUser() - user.dailys = [ - # Must set start date to yesterday, because cron mock sets last cron to yesterday - shared.taskDefaults({type:'daily', startDate: moment().subtract(1, 'days'), frequency: 'daily'}) - shared.taskDefaults({type:'daily', startDate: moment().subtract(1, 'days'), frequency: 'weekly'}) - ] - daily = user.dailys[0] - weekly = user.dailys[1] - - it 'does damage user for not completing it', -> - cron(user) - expect(user.stats.hp).to.be.lessThan 50 - - it 'decreases value on cron if daily is incomplete', -> - cron(user) - expect(daily.value).to.be.lessThan 0 - expect(weekly.value).to.be.lessThan 0 - - it 'resets checklists if daily is not marked as complete', -> - checklist = [ - { - 'text' : '1', - 'id' : 'checklist-one', - 'completed' : true - }, - { - 'text' : '2', - 'id' : 'checklist-two', - 'completed' : true - }, - { - 'text' : '3', - 'id' : 'checklist-three', - 'completed' : false - } - ] - daily.checklist = checklist - weekly.checklist = checklist - cron(user) - - _.each daily.checklist, (box)-> - expect(box.completed).to.be false - - _.each weekly.checklist, (box)-> - expect(box.completed).to.be false - - it 'resets checklists if daily is marked as complete', -> - checklist = [ - { - 'text' : '1', - 'id' : 'checklist-one', - 'completed' : true - }, - { - 'text' : '2', - 'id' : 'checklist-two', - 'completed' : true - }, - { - 'text' : '3', - 'id' : 'checklist-three', - 'completed' : false - } - ] - daily.checklist = checklist - daily.completed = true - weekly.checklist = checklist - weekly.completed = true - cron(user) - - _.each daily.checklist, (box)-> - expect(box.completed).to.be false - - _.each weekly.checklist, (box)-> - expect(box.completed).to.be false - -describe 'daily that repeats every x days', -> - user = null - daily = null - - beforeEach -> - user = newUser() - user.dailys = [ shared.taskDefaults({type:'daily', startDate: moment(), frequency: 'daily'}) ] - daily = user.dailys[0] - - _.times 11, (due) -> - - it 'where x equals ' + due, -> - daily.everyX = due - - _.times 30, (day) -> - isDue = shared.shouldDo moment().add(day, 'days'), daily - expect(isDue).to.be true if day % due == 0 - expect(isDue).to.be false if day % due != 0 - -describe 'daily that repeats every X days when multiple days are missed', -> - everyX = 3 - startDateDaysAgo = everyX * 3 - user = null - daily = null - - describe 'including missing a due date', -> - missedDays = everyX * 2 + 1 - - beforeEach -> - user = newUser() - user.dailys = [ - shared.taskDefaults({type:'daily', startDate: moment().subtract(startDateDaysAgo, 'days'), frequency: 'daily', everyX: everyX}) - ] - daily = user.dailys[0] - - it 'decreases value on cron once only if daily is incomplete', -> - cron(user, missedDays) - expect(daily.value).to.be -1 - - it 'resets checklists if daily is incomplete', -> - checklist = [ - { - 'text' : '1', - 'id' : 'checklist-one', - 'completed' : true - } - ] - daily.checklist = checklist - cron(user, missedDays) - _.each daily.checklist, (box)-> - expect(box.completed).to.be false - - it 'resets checklists if daily is marked as complete', -> - checklist = [ - { - 'text' : '1', - 'id' : 'checklist-one', - 'completed' : true - } - ] - daily.checklist = checklist - daily.completed = true - cron(user, missedDays) - _.each daily.checklist, (box)-> - expect(box.completed).to.be false - - describe 'but not missing a due date', -> - missedDays = everyX - 1 - - beforeEach -> - user = newUser() - user.dailys = [ - shared.taskDefaults({type:'daily', startDate: moment().subtract(startDateDaysAgo, 'days'), frequency: 'daily', everyX: everyX}) - ] - daily = user.dailys[0] - - it 'does not decrease value on cron', -> - cron(user, missedDays) - expect(daily.value).to.be 0 - - it 'does not reset checklists if daily is incomplete', -> - checklist = [ - { - 'text' : '1', - 'id' : 'checklist-one', - 'completed' : true - } - ] - daily.checklist = checklist - cron(user, missedDays) - _.each daily.checklist, (box)-> - expect(box.completed).to.be true - - it 'resets checklists if daily is marked as complete', -> - checklist = [ - { - 'text' : '1', - 'id' : 'checklist-one', - 'completed' : true - } - ] - daily.checklist = checklist - daily.completed = true - cron(user, missedDays) - _.each daily.checklist, (box)-> - expect(box.completed).to.be false diff --git a/test/common/dailies.js b/test/common/dailies.js new file mode 100644 index 0000000000..dcf9f9db32 --- /dev/null +++ b/test/common/dailies.js @@ -0,0 +1,541 @@ +(function() { + var _, cron, expect, moment, newUser, repeatWithoutLastWeekday, shared, sinon; + + _ = require('lodash'); + + expect = require('expect.js'); + + sinon = require('sinon'); + + moment = require('moment'); + + shared = require('../../common/script/index.js'); + + shared.i18n.translations = require('../../website/src/libs/i18n.js').translations; + + repeatWithoutLastWeekday = function() { + var repeat; + repeat = { + su: true, + m: true, + t: true, + w: true, + th: true, + f: true, + s: true + }; + if (shared.startOfWeek(moment().zone(0)).isoWeekday() === 1) { + repeat.su = false; + } else { + repeat.s = false; + } + return { + repeat: repeat + }; + }; + + + /* Helper Functions */ + + newUser = function(addTasks) { + var buffs, user; + if (addTasks == null) { + addTasks = true; + } + buffs = { + per: 0, + int: 0, + con: 0, + str: 0, + stealth: 0, + streaks: false + }; + user = { + auth: { + timestamps: {} + }, + stats: { + str: 1, + con: 1, + per: 1, + int: 1, + mp: 32, + "class": 'warrior', + buffs: buffs + }, + items: { + lastDrop: { + count: 0 + }, + hatchingPotions: {}, + eggs: {}, + food: {}, + gear: { + equipped: {}, + costume: {} + } + }, + party: { + quest: { + progress: { + down: 0 + } + } + }, + preferences: {}, + dailys: [], + todos: [], + rewards: [], + flags: {}, + achievements: {}, + contributor: { + level: 2 + } + }; + shared.wrap(user); + user.ops.reset(null, function() {}); + if (addTasks) { + _.each(['habit', 'todo', 'daily'], function(task) { + return user.ops.addTask({ + body: { + type: task, + id: shared.uuid() + } + }); + }); + } + return user; + }; + + cron = function(usr, missedDays) { + if (missedDays == null) { + missedDays = 1; + } + usr.lastCron = moment().subtract(missedDays, 'days'); + return usr.fns.cron(); + }; + + describe('daily/weekly that repeats everyday (default)', function() { + var daily, user, weekly; + user = null; + daily = null; + weekly = null; + describe('when startDate is in the future', function() { + beforeEach(function() { + user = newUser(); + user.dailys = [ + shared.taskDefaults({ + type: 'daily', + startDate: moment().add(7, 'days'), + frequency: 'daily' + }), shared.taskDefaults({ + type: 'daily', + startDate: moment().add(7, 'days'), + frequency: 'weekly', + repeat: { + su: true, + m: true, + t: true, + w: true, + th: true, + f: true, + s: true + } + }) + ]; + daily = user.dailys[0]; + return weekly = user.dailys[1]; + }); + it('does not damage user for not completing it', function() { + cron(user); + return expect(user.stats.hp).to.be(50); + }); + it('does not change value on cron if daily is incomplete', function() { + cron(user); + expect(daily.value).to.be(0); + return expect(weekly.value).to.be(0); + }); + it('does not reset checklists if daily is not marked as complete', function() { + var checklist; + checklist = [ + { + 'text': '1', + 'id': 'checklist-one', + 'completed': true + }, { + 'text': '2', + 'id': 'checklist-two', + 'completed': true + }, { + 'text': '3', + 'id': 'checklist-three', + 'completed': false + } + ]; + daily.checklist = checklist; + weekly.checklist = checklist; + cron(user); + expect(daily.checklist[0].completed).to.be(true); + expect(daily.checklist[1].completed).to.be(true); + expect(daily.checklist[2].completed).to.be(false); + expect(weekly.checklist[0].completed).to.be(true); + expect(weekly.checklist[1].completed).to.be(true); + return expect(weekly.checklist[2].completed).to.be(false); + }); + it('resets checklists if daily is marked as complete', function() { + var checklist; + checklist = [ + { + 'text': '1', + 'id': 'checklist-one', + 'completed': true + }, { + 'text': '2', + 'id': 'checklist-two', + 'completed': true + }, { + 'text': '3', + 'id': 'checklist-three', + 'completed': false + } + ]; + daily.checklist = checklist; + weekly.checklist = checklist; + daily.completed = true; + weekly.completed = true; + cron(user); + _.each(daily.checklist, function(box) { + return expect(box.completed).to.be(false); + }); + return _.each(weekly.checklist, function(box) { + return expect(box.completed).to.be(false); + }); + }); + return it('is due on startDate', function() { + var daily_due_on_start_date, daily_due_today, weekly_due_on_start_date, weekly_due_today; + daily_due_today = shared.shouldDo(moment(), daily); + daily_due_on_start_date = shared.shouldDo(moment().add(7, 'days'), daily); + expect(daily_due_today).to.be(false); + expect(daily_due_on_start_date).to.be(true); + weekly_due_today = shared.shouldDo(moment(), weekly); + weekly_due_on_start_date = shared.shouldDo(moment().add(7, 'days'), weekly); + expect(weekly_due_today).to.be(false); + return expect(weekly_due_on_start_date).to.be(true); + }); + }); + describe('when startDate is in the past', function() { + beforeEach(function() { + user = newUser(); + user.dailys = [ + shared.taskDefaults({ + type: 'daily', + startDate: moment().subtract(7, 'days'), + frequency: 'daily' + }), shared.taskDefaults({ + type: 'daily', + startDate: moment().subtract(7, 'days'), + frequency: 'weekly' + }) + ]; + daily = user.dailys[0]; + return weekly = user.dailys[1]; + }); + it('does damage user for not completing it', function() { + cron(user); + return expect(user.stats.hp).to.be.lessThan(50); + }); + it('decreases value on cron if daily is incomplete', function() { + cron(user, 1); + expect(daily.value).to.be(-1); + return expect(weekly.value).to.be(-1); + }); + it('decreases value on cron once only if daily is incomplete and multiple days are missed', function() { + cron(user, 7); + expect(daily.value).to.be(-1); + return expect(weekly.value).to.be(-1); + }); + it('resets checklists if daily is not marked as complete', function() { + var checklist; + checklist = [ + { + 'text': '1', + 'id': 'checklist-one', + 'completed': true + }, { + 'text': '2', + 'id': 'checklist-two', + 'completed': true + }, { + 'text': '3', + 'id': 'checklist-three', + 'completed': false + } + ]; + daily.checklist = checklist; + weekly.checklist = checklist; + cron(user); + _.each(daily.checklist, function(box) { + return expect(box.completed).to.be(false); + }); + return _.each(weekly.checklist, function(box) { + return expect(box.completed).to.be(false); + }); + }); + return it('resets checklists if daily is marked as complete', function() { + var checklist; + checklist = [ + { + 'text': '1', + 'id': 'checklist-one', + 'completed': true + }, { + 'text': '2', + 'id': 'checklist-two', + 'completed': true + }, { + 'text': '3', + 'id': 'checklist-three', + 'completed': false + } + ]; + daily.checklist = checklist; + daily.completed = true; + weekly.checklist = checklist; + weekly.completed = true; + cron(user); + _.each(daily.checklist, function(box) { + return expect(box.completed).to.be(false); + }); + return _.each(weekly.checklist, function(box) { + return expect(box.completed).to.be(false); + }); + }); + }); + return describe('when startDate is today', function() { + beforeEach(function() { + user = newUser(); + user.dailys = [ + shared.taskDefaults({ + type: 'daily', + startDate: moment().subtract(1, 'days'), + frequency: 'daily' + }), shared.taskDefaults({ + type: 'daily', + startDate: moment().subtract(1, 'days'), + frequency: 'weekly' + }) + ]; + daily = user.dailys[0]; + return weekly = user.dailys[1]; + }); + it('does damage user for not completing it', function() { + cron(user); + return expect(user.stats.hp).to.be.lessThan(50); + }); + it('decreases value on cron if daily is incomplete', function() { + cron(user); + expect(daily.value).to.be.lessThan(0); + return expect(weekly.value).to.be.lessThan(0); + }); + it('resets checklists if daily is not marked as complete', function() { + var checklist; + checklist = [ + { + 'text': '1', + 'id': 'checklist-one', + 'completed': true + }, { + 'text': '2', + 'id': 'checklist-two', + 'completed': true + }, { + 'text': '3', + 'id': 'checklist-three', + 'completed': false + } + ]; + daily.checklist = checklist; + weekly.checklist = checklist; + cron(user); + _.each(daily.checklist, function(box) { + return expect(box.completed).to.be(false); + }); + return _.each(weekly.checklist, function(box) { + return expect(box.completed).to.be(false); + }); + }); + return it('resets checklists if daily is marked as complete', function() { + var checklist; + checklist = [ + { + 'text': '1', + 'id': 'checklist-one', + 'completed': true + }, { + 'text': '2', + 'id': 'checklist-two', + 'completed': true + }, { + 'text': '3', + 'id': 'checklist-three', + 'completed': false + } + ]; + daily.checklist = checklist; + daily.completed = true; + weekly.checklist = checklist; + weekly.completed = true; + cron(user); + _.each(daily.checklist, function(box) { + return expect(box.completed).to.be(false); + }); + return _.each(weekly.checklist, function(box) { + return expect(box.completed).to.be(false); + }); + }); + }); + }); + + describe('daily that repeats every x days', function() { + var daily, user; + user = null; + daily = null; + beforeEach(function() { + user = newUser(); + user.dailys = [ + shared.taskDefaults({ + type: 'daily', + startDate: moment(), + frequency: 'daily' + }) + ]; + return daily = user.dailys[0]; + }); + return _.times(11, function(due) { + return it('where x equals ' + due, function() { + daily.everyX = due; + return _.times(30, function(day) { + var isDue; + isDue = shared.shouldDo(moment().add(day, 'days'), daily); + if (day % due === 0) { + expect(isDue).to.be(true); + } + if (day % due !== 0) { + return expect(isDue).to.be(false); + } + }); + }); + }); + }); + + describe('daily that repeats every X days when multiple days are missed', function() { + var daily, everyX, startDateDaysAgo, user; + everyX = 3; + startDateDaysAgo = everyX * 3; + user = null; + daily = null; + describe('including missing a due date', function() { + var missedDays; + missedDays = everyX * 2 + 1; + beforeEach(function() { + user = newUser(); + user.dailys = [ + shared.taskDefaults({ + type: 'daily', + startDate: moment().subtract(startDateDaysAgo, 'days'), + frequency: 'daily', + everyX: everyX + }) + ]; + return daily = user.dailys[0]; + }); + it('decreases value on cron once only if daily is incomplete', function() { + cron(user, missedDays); + return expect(daily.value).to.be(-1); + }); + it('resets checklists if daily is incomplete', function() { + var checklist; + checklist = [ + { + 'text': '1', + 'id': 'checklist-one', + 'completed': true + } + ]; + daily.checklist = checklist; + cron(user, missedDays); + return _.each(daily.checklist, function(box) { + return expect(box.completed).to.be(false); + }); + }); + return it('resets checklists if daily is marked as complete', function() { + var checklist; + checklist = [ + { + 'text': '1', + 'id': 'checklist-one', + 'completed': true + } + ]; + daily.checklist = checklist; + daily.completed = true; + cron(user, missedDays); + return _.each(daily.checklist, function(box) { + return expect(box.completed).to.be(false); + }); + }); + }); + return describe('but not missing a due date', function() { + var missedDays; + missedDays = everyX - 1; + beforeEach(function() { + user = newUser(); + user.dailys = [ + shared.taskDefaults({ + type: 'daily', + startDate: moment().subtract(startDateDaysAgo, 'days'), + frequency: 'daily', + everyX: everyX + }) + ]; + return daily = user.dailys[0]; + }); + it('does not decrease value on cron', function() { + cron(user, missedDays); + return expect(daily.value).to.be(0); + }); + it('does not reset checklists if daily is incomplete', function() { + var checklist; + checklist = [ + { + 'text': '1', + 'id': 'checklist-one', + 'completed': true + } + ]; + daily.checklist = checklist; + cron(user, missedDays); + return _.each(daily.checklist, function(box) { + return expect(box.completed).to.be(true); + }); + }); + return it('resets checklists if daily is marked as complete', function() { + var checklist; + checklist = [ + { + 'text': '1', + 'id': 'checklist-one', + 'completed': true + } + ]; + daily.checklist = checklist; + daily.completed = true; + cron(user, missedDays); + return _.each(daily.checklist, function(box) { + return expect(box.completed).to.be(false); + }); + }); + }); + }); + +}).call(this); diff --git a/test/common/mocha.opts b/test/common/mocha.opts index 84bfdce89a..a3c3e0c6dc 100644 --- a/test/common/mocha.opts +++ b/test/common/mocha.opts @@ -5,4 +5,5 @@ --growl --debug --compilers coffee:coffee-script +--compilers js:babel/register --globals io diff --git a/test/common/simulations/autoAllocate.coffee b/test/common/simulations/autoAllocate.coffee deleted file mode 100644 index fec7ccbb31..0000000000 --- a/test/common/simulations/autoAllocate.coffee +++ /dev/null @@ -1,82 +0,0 @@ -### -1) clone the repo -2) npm install -3) coffee ./tests/math_samples.coffee - -Current results at https://gist.github.com/lefnire/8049676 -### - -shared = require '../../../common/script/index.js' -_ = require 'lodash' -$w = (s)->s.split(' ') - -id = shared.uuid() -user = - stats: - class: 'warrior' - lvl:1, hp:50, gp:0, exp:10 - per:0, int:0, con:0, str:0 - buffs: {per:0, int:0, con:0, str:0} - training: {int:0,con:0,per:0,str:0} - preferences: automaticAllocation: false - party: quest: key:'evilsanta', progress: {up:0,down:0} - achievements: {} - items: - eggs: {} - hatchingPotions: {} - food: {} - gear: - equipped: - weapon: 'weapon_warrior_4' - armor: 'armor_warrior_4' - shield: 'shield_warrior_4' - head: 'head_warrior_4' - habits: [ - # we're gonna change this habit's attribute to mess with taskbased allo. Add the others to make sure our _.reduce is legit - {id:'a',value:1,type:'habit',attribute:'str'} - ] - dailys: [ - {id:'b',value:1,type:'daily',attribute:'str'} - ] - todos: [ - {id:'c',value:1,type:'todo',attribute:'con'} - {id:'d',value:1,type:'todo',attribute:'per'} - {id:'e',value:1,type:'todo',attribute:'int'} - ] - rewards: [] - -modes = - flat: _.cloneDeep user - classbased_warrior: _.cloneDeep user - classbased_rogue: _.cloneDeep user - classbased_wizard: _.cloneDeep user - classbased_healer: _.cloneDeep user - taskbased: _.cloneDeep user - -modes.classbased_warrior.stats.class = 'warrior' -modes.classbased_rogue.stats.class = 'rogue' -modes.classbased_wizard.stats.class = 'wizard' -modes.classbased_healer.stats.class = 'healer' - -_.each $w('flat classbased_warrior classbased_rogue classbased_wizard classbased_healer taskbased'), (mode) -> - _.merge modes[mode].preferences, - automaticAllocation: true - allocationMode: if mode.indexOf('classbased') is 0 then 'classbased' else mode - shared.wrap(modes[mode]) - -console.log "\n\n================================================" -console.log "New Simulation" -console.log "================================================\n\n" - - -_.times [20], (lvl) -> - console.log ("[lvl #{lvl}]\n--------------\n") - _.each $w('flat classbased_warrior classbased_rogue classbased_wizard classbased_healer taskbased'), (mode) -> - u = modes[mode] #local var - u.stats.exp = shared.tnl(lvl)+1 # level up - _.merge u.stats, {per:0,con:0,int:0,str:0} if mode is 'taskbased' # if task-based, clear stat so we can see clearly which stat got +1 - u.habits[0].attribute = u.fns.randomVal({str:'str',int:'int',per:'per',con:'con'}) - u.ops.score {params:{id:u.habits[0].id},direction:'up'} - u.fns.updateStats(u.stats) # trigger stats update - str = mode + (if mode is 'taskbased' then " (#{u.habits[0].attribute})" else "") - console.log str, _.pick(u.stats, $w 'per int con str') diff --git a/test/common/simulations/autoAllocate.js b/test/common/simulations/autoAllocate.js new file mode 100644 index 0000000000..3621dbec3b --- /dev/null +++ b/test/common/simulations/autoAllocate.js @@ -0,0 +1,164 @@ +(function() { + var $w, _, id, modes, shared, user; + + shared = require('../../../common/script/index.js'); + + _ = require('lodash'); + + $w = function(s) { + return s.split(' '); + }; + + id = shared.uuid(); + + user = { + stats: { + "class": 'warrior', + lvl: 1, + hp: 50, + gp: 0, + exp: 10, + per: 0, + int: 0, + con: 0, + str: 0, + buffs: { + per: 0, + int: 0, + con: 0, + str: 0 + }, + training: { + int: 0, + con: 0, + per: 0, + str: 0 + } + }, + preferences: { + automaticAllocation: false + }, + party: { + quest: { + key: 'evilsanta', + progress: { + up: 0, + down: 0 + } + } + }, + achievements: {}, + items: { + eggs: {}, + hatchingPotions: {}, + food: {}, + gear: { + equipped: { + weapon: 'weapon_warrior_4', + armor: 'armor_warrior_4', + shield: 'shield_warrior_4', + head: 'head_warrior_4' + } + } + }, + habits: [ + { + id: 'a', + value: 1, + type: 'habit', + attribute: 'str' + } + ], + dailys: [ + { + id: 'b', + value: 1, + type: 'daily', + attribute: 'str' + } + ], + todos: [ + { + id: 'c', + value: 1, + type: 'todo', + attribute: 'con' + }, { + id: 'd', + value: 1, + type: 'todo', + attribute: 'per' + }, { + id: 'e', + value: 1, + type: 'todo', + attribute: 'int' + } + ], + rewards: [] + }; + + modes = { + flat: _.cloneDeep(user), + classbased_warrior: _.cloneDeep(user), + classbased_rogue: _.cloneDeep(user), + classbased_wizard: _.cloneDeep(user), + classbased_healer: _.cloneDeep(user), + taskbased: _.cloneDeep(user) + }; + + modes.classbased_warrior.stats["class"] = 'warrior'; + + modes.classbased_rogue.stats["class"] = 'rogue'; + + modes.classbased_wizard.stats["class"] = 'wizard'; + + modes.classbased_healer.stats["class"] = 'healer'; + + _.each($w('flat classbased_warrior classbased_rogue classbased_wizard classbased_healer taskbased'), function(mode) { + _.merge(modes[mode].preferences, { + automaticAllocation: true, + allocationMode: mode.indexOf('classbased') === 0 ? 'classbased' : mode + }); + return shared.wrap(modes[mode]); + }); + + console.log("\n\n================================================"); + + console.log("New Simulation"); + + console.log("================================================\n\n"); + + _.times([20], function(lvl) { + console.log("[lvl " + lvl + "]\n--------------\n"); + return _.each($w('flat classbased_warrior classbased_rogue classbased_wizard classbased_healer taskbased'), function(mode) { + var str, u; + u = modes[mode]; + u.stats.exp = shared.tnl(lvl) + 1; + if (mode === 'taskbased') { + _.merge(u.stats, { + per: 0, + con: 0, + int: 0, + str: 0 + }); + } + u.habits[0].attribute = u.fns.randomVal({ + str: 'str', + int: 'int', + per: 'per', + con: 'con' + }); + u.ops.score({ + params: { + id: u.habits[0].id + }, + direction: 'up' + }); + u.fns.updateStats(u.stats); + str = mode + (mode === 'taskbased' ? " (" + u.habits[0].attribute + ")" : ""); + return console.log(str, _.pick(u.stats, $w('per int con str'))); + }); + }); + +}).call(this); diff --git a/test/common/simulations/passive_active_attrs.coffee b/test/common/simulations/passive_active_attrs.coffee deleted file mode 100644 index 0788488ec2..0000000000 --- a/test/common/simulations/passive_active_attrs.coffee +++ /dev/null @@ -1,180 +0,0 @@ -### -1) clone the repo -2) npm install -3) coffee ./tests/math_samples.coffee - -Current results at https://gist.github.com/lefnire/8049676 -### - -shared = require '../../../common/script/index.js' -_ = require 'lodash' - -id = shared.uuid() -user = - stats: {class: 'warrior', buffs: {per:0,int:0,con:0,str:0}} - party: quest: key:'evilsanta', progress: {up:0,down:0} - preferences: automaticAllocation:false - achievements:{} - flags: levelDrops: {} - items: - eggs: {} - hatchingPotions: {} - food: {} - quests:{} - gear: - equipped: - weapon: 'weapon_warrior_4' - armor: 'armor_warrior_4' - shield: 'shield_warrior_4' - head: 'head_warrior_4' - habits: [ - shared.taskDefaults({id, value: 0}) - ] - dailys: [{"text" : "1",},{"text" : "1",},{"text" : "1",},{"text" : "1",},{"text" : "1",},{"text" : "1",},{"text" : "1",},{"text" : "1",},{"text" : "1",},{"text" : "1",},{"text" : "1",},{"text" : "1",},{"text" : "1",},{"text" : "1",},{"text" : "1",}], - todos: [] - rewards: [] - -shared.wrap(user) -s = user.stats -task = user.tasks[id] -party = [user] - -console.log "\n\n================================================" -console.log "New Simulation" -console.log "================================================\n\n" - -clearUser = (lvl=1) -> - _.merge user.stats, {exp:0, gp:0, hp:50, lvl:lvl, str:lvl*1.5, con:lvl*1.5, per:lvl*1.5, int:lvl*1.5, mp: 100} - _.merge s.buffs, {str:0,con:0,int:0,per:0} - _.merge user.party.quest.progress, {up:0,down:0} - user.items.lastDrop = {count:0} - -_.each [1,25,50,75,100], (lvl) -> - console.log "[LEVEL #{lvl}] (#{lvl*2} points total in every attr)\n\n" - _.each {red:-25,yellow:0,green:35}, (taskVal, color) -> - console.log "[task.value = #{taskVal} (#{color})]" - console.log "direction\texpΔ\t\thpΔ\tgpΔ\ttask.valΔ\ttask.valΔ bonus\t\tboss-hit" - _.each ['up','down'], (direction) -> - clearUser(lvl) - b4 = {hp:s.hp, taskVal} - task.value = taskVal - task.type = 'daily' if direction is 'up' - delta = user.ops.score params:{id, direction} - console.log "#{if direction is 'up' then '↑' else '↓'}\t\t#{s.exp}/#{shared.tnl(s.lvl)}\t\t#{(b4.hp-s.hp).toFixed(1)}\t#{s.gp.toFixed(1)}\t#{delta.toFixed(1)}\t\t#{(task.value-b4.taskVal-delta).toFixed(1)}\t\t\t#{user.party.quest.progress.up.toFixed(1)}" - - str = '- [Wizard]' - - task.value = taskVal;clearUser(lvl) - b4 = {taskVal} - shared.content.spells.wizard.fireball.cast(user,task) - str += "\tfireball(task.valΔ:#{(task.value-taskVal).toFixed(1)} exp:#{s.exp.toFixed(1)} bossHit:#{user.party.quest.progress.up.toFixed(2)})" - - task.value = taskVal;clearUser(lvl) - _party = [user, {stats:{mp:0}}] - shared.content.spells.wizard.mpheal.cast(user,_party) - str += "\t| mpheal(mp:#{_party[1].stats.mp})" - - task.value = taskVal;clearUser(lvl) - shared.content.spells.wizard.earth.cast(user,party) - str += "\t\t\t\t| earth(buffs.int:#{s.buffs.int})" - s.buffs.int = 0 - - task.value = taskVal;clearUser(lvl) - shared.content.spells.wizard.frost.cast(user,{}) - str += "\t\t\t| frost(N/A)" - - console.log str - str = '- [Warrior]' - - task.value = taskVal;clearUser(lvl) - shared.content.spells.warrior.smash.cast(user,task) - b4 = {taskVal} - str += "\tsmash(task.valΔ:#{(task.value-taskVal).toFixed(1)})" - - task.value = taskVal;clearUser(lvl) - shared.content.spells.warrior.defensiveStance.cast(user,{}) - str += "\t\t| defensiveStance(buffs.con:#{s.buffs.con})" - s.buffs.con = 0 - - task.value = taskVal;clearUser(lvl) - shared.content.spells.warrior.valorousPresence.cast(user,party) - str += "\t\t\t| valorousPresence(buffs.str:#{s.buffs.str})" - s.buffs.str = 0 - - task.value = taskVal;clearUser(lvl) - shared.content.spells.warrior.intimidate.cast(user,party) - str += "\t\t| intimidate(buffs.con:#{s.buffs.con})" - s.buffs.con = 0 - - console.log str - str = '- [Rogue]' - - task.value = taskVal;clearUser(lvl) - shared.content.spells.rogue.pickPocket.cast(user,task) - str += "\tpickPocket(gp:#{s.gp.toFixed(1)})" - - task.value = taskVal;clearUser(lvl) - shared.content.spells.rogue.backStab.cast(user,task) - b4 = {taskVal} - str += "\t\t| backStab(task.valΔ:#{(task.value-b4.taskVal).toFixed(1)} exp:#{s.exp.toFixed(1)} gp:#{s.gp.toFixed(1)})" - - task.value = taskVal;clearUser(lvl) - shared.content.spells.rogue.toolsOfTrade.cast(user,party) - str += "\t| toolsOfTrade(buffs.per:#{s.buffs.per})" - s.buffs.per = 0 - - task.value = taskVal;clearUser(lvl) - shared.content.spells.rogue.stealth.cast(user,{}) - str += "\t\t| stealth(avoiding #{user.stats.buffs.stealth} tasks)" - user.stats.buffs.stealth = 0 - - console.log str - str = '- [Healer]' - - task.value = taskVal;clearUser(lvl) - s.hp=0 - shared.content.spells.healer.heal.cast(user,{}) - str += "\theal(hp:#{s.hp.toFixed(1)})" - - task.value = taskVal;clearUser(lvl) - shared.content.spells.healer.brightness.cast(user,{}) - b4 = {taskVal} - str += "\t\t\t| brightness(task.valΔ:#{(task.value-b4.taskVal).toFixed(1)})" - - task.value = taskVal;clearUser(lvl) - shared.content.spells.healer.protectAura.cast(user,party) - str += "\t\t\t| protectAura(buffs.con:#{s.buffs.con})" - s.buffs.con = 0 - - task.value = taskVal;clearUser(lvl) - s.hp=0 - shared.content.spells.healer.heallAll.cast(user,party) - str += "\t\t| heallAll(hp:#{s.hp.toFixed(1)})" - - console.log str - console.log '\n' - - - console.log '------------------------------------------------------------' - -### -_.each [1,25,50,75,100,125], (lvl) -> - console.log "[LEVEL #{lvl}] (#{lvl*2} points in every attr)\n\n" - _.each {red:-25,yellow:0,green:35}, (taskVal, color) -> - console.log "[task.value = #{taskVal} (#{color})]" - console.log "direction\texpΔ\t\thpΔ\tgpΔ\ttask.valΔ\ttask.valΔ bonus\t\tboss-hit" - _.each ['up','down'], (direction) -> - clearUser(lvl) - b4 = {hp:s.hp, taskVal} - task.value = taskVal - task.type = 'daily' if direction is 'up' - delta = user.ops.score params:{id, direction} - console.log "#{if direction is 'up' then '↑' else '↓'}\t\t#{s.exp}/#{shared.tnl(s.lvl)}\t\t#{(b4.hp-s.hp).toFixed(1)}\t#{s.gp.toFixed(1)}\t#{delta.toFixed(1)}\t\t#{(task.value-b4.taskVal-delta).toFixed(1)}\t\t\t#{user.party.quest.progress.up.toFixed(1)}" - - task.value = taskVal;clearUser(lvl) - shared.content.spells.rogue.stealth.cast(user,{}) - console.log "\t\t| stealth(avoiding #{user.stats.buffs.stealth} tasks)" - user.stats.buffs.stealth = 0 - - console.log user.dailys.length -### diff --git a/test/common/simulations/passive_active_attrs.js b/test/common/simulations/passive_active_attrs.js new file mode 100644 index 0000000000..d22524cec3 --- /dev/null +++ b/test/common/simulations/passive_active_attrs.js @@ -0,0 +1,294 @@ +(function() { + var _, clearUser, id, party, s, shared, task, user; + + shared = require('../../../common/script/index.js'); + + _ = require('lodash'); + + id = shared.uuid(); + + user = { + stats: { + "class": 'warrior', + buffs: { + per: 0, + int: 0, + con: 0, + str: 0 + } + }, + party: { + quest: { + key: 'evilsanta', + progress: { + up: 0, + down: 0 + } + } + }, + preferences: { + automaticAllocation: false + }, + achievements: {}, + flags: { + levelDrops: {} + }, + items: { + eggs: {}, + hatchingPotions: {}, + food: {}, + quests: {}, + gear: { + equipped: { + weapon: 'weapon_warrior_4', + armor: 'armor_warrior_4', + shield: 'shield_warrior_4', + head: 'head_warrior_4' + } + } + }, + habits: [ + shared.taskDefaults({ + id: id, + value: 0 + }) + ], + dailys: [ + { + "text": "1" + }, { + "text": "1" + }, { + "text": "1" + }, { + "text": "1" + }, { + "text": "1" + }, { + "text": "1" + }, { + "text": "1" + }, { + "text": "1" + }, { + "text": "1" + }, { + "text": "1" + }, { + "text": "1" + }, { + "text": "1" + }, { + "text": "1" + }, { + "text": "1" + }, { + "text": "1" + } + ], + todos: [], + rewards: [] + }; + + shared.wrap(user); + + s = user.stats; + + task = user.tasks[id]; + + party = [user]; + + console.log("\n\n================================================"); + + console.log("New Simulation"); + + console.log("================================================\n\n"); + + clearUser = function(lvl) { + if (lvl == null) { + lvl = 1; + } + _.merge(user.stats, { + exp: 0, + gp: 0, + hp: 50, + lvl: lvl, + str: lvl * 1.5, + con: lvl * 1.5, + per: lvl * 1.5, + int: lvl * 1.5, + mp: 100 + }); + _.merge(s.buffs, { + str: 0, + con: 0, + int: 0, + per: 0 + }); + _.merge(user.party.quest.progress, { + up: 0, + down: 0 + }); + return user.items.lastDrop = { + count: 0 + }; + }; + + _.each([1, 25, 50, 75, 100], function(lvl) { + console.log("[LEVEL " + lvl + "] (" + (lvl * 2) + " points total in every attr)\n\n"); + _.each({ + red: -25, + yellow: 0, + green: 35 + }, function(taskVal, color) { + var _party, b4, str; + console.log("[task.value = " + taskVal + " (" + color + ")]"); + console.log("direction\texpΔ\t\thpΔ\tgpΔ\ttask.valΔ\ttask.valΔ bonus\t\tboss-hit"); + _.each(['up', 'down'], function(direction) { + var b4, delta; + clearUser(lvl); + b4 = { + hp: s.hp, + taskVal: taskVal + }; + task.value = taskVal; + if (direction === 'up') { + task.type = 'daily'; + } + delta = user.ops.score({ + params: { + id: id, + direction: direction + } + }); + return console.log((direction === 'up' ? '↑' : '↓') + "\t\t" + s.exp + "/" + (shared.tnl(s.lvl)) + "\t\t" + ((b4.hp - s.hp).toFixed(1)) + "\t" + (s.gp.toFixed(1)) + "\t" + (delta.toFixed(1)) + "\t\t" + ((task.value - b4.taskVal - delta).toFixed(1)) + "\t\t\t" + (user.party.quest.progress.up.toFixed(1))); + }); + str = '- [Wizard]'; + task.value = taskVal; + clearUser(lvl); + b4 = { + taskVal: taskVal + }; + shared.content.spells.wizard.fireball.cast(user, task); + str += "\tfireball(task.valΔ:" + ((task.value - taskVal).toFixed(1)) + " exp:" + (s.exp.toFixed(1)) + " bossHit:" + (user.party.quest.progress.up.toFixed(2)) + ")"; + task.value = taskVal; + clearUser(lvl); + _party = [ + user, { + stats: { + mp: 0 + } + } + ]; + shared.content.spells.wizard.mpheal.cast(user, _party); + str += "\t| mpheal(mp:" + _party[1].stats.mp + ")"; + task.value = taskVal; + clearUser(lvl); + shared.content.spells.wizard.earth.cast(user, party); + str += "\t\t\t\t| earth(buffs.int:" + s.buffs.int + ")"; + s.buffs.int = 0; + task.value = taskVal; + clearUser(lvl); + shared.content.spells.wizard.frost.cast(user, {}); + str += "\t\t\t| frost(N/A)"; + console.log(str); + str = '- [Warrior]'; + task.value = taskVal; + clearUser(lvl); + shared.content.spells.warrior.smash.cast(user, task); + b4 = { + taskVal: taskVal + }; + str += "\tsmash(task.valΔ:" + ((task.value - taskVal).toFixed(1)) + ")"; + task.value = taskVal; + clearUser(lvl); + shared.content.spells.warrior.defensiveStance.cast(user, {}); + str += "\t\t| defensiveStance(buffs.con:" + s.buffs.con + ")"; + s.buffs.con = 0; + task.value = taskVal; + clearUser(lvl); + shared.content.spells.warrior.valorousPresence.cast(user, party); + str += "\t\t\t| valorousPresence(buffs.str:" + s.buffs.str + ")"; + s.buffs.str = 0; + task.value = taskVal; + clearUser(lvl); + shared.content.spells.warrior.intimidate.cast(user, party); + str += "\t\t| intimidate(buffs.con:" + s.buffs.con + ")"; + s.buffs.con = 0; + console.log(str); + str = '- [Rogue]'; + task.value = taskVal; + clearUser(lvl); + shared.content.spells.rogue.pickPocket.cast(user, task); + str += "\tpickPocket(gp:" + (s.gp.toFixed(1)) + ")"; + task.value = taskVal; + clearUser(lvl); + shared.content.spells.rogue.backStab.cast(user, task); + b4 = { + taskVal: taskVal + }; + str += "\t\t| backStab(task.valΔ:" + ((task.value - b4.taskVal).toFixed(1)) + " exp:" + (s.exp.toFixed(1)) + " gp:" + (s.gp.toFixed(1)) + ")"; + task.value = taskVal; + clearUser(lvl); + shared.content.spells.rogue.toolsOfTrade.cast(user, party); + str += "\t| toolsOfTrade(buffs.per:" + s.buffs.per + ")"; + s.buffs.per = 0; + task.value = taskVal; + clearUser(lvl); + shared.content.spells.rogue.stealth.cast(user, {}); + str += "\t\t| stealth(avoiding " + user.stats.buffs.stealth + " tasks)"; + user.stats.buffs.stealth = 0; + console.log(str); + str = '- [Healer]'; + task.value = taskVal; + clearUser(lvl); + s.hp = 0; + shared.content.spells.healer.heal.cast(user, {}); + str += "\theal(hp:" + (s.hp.toFixed(1)) + ")"; + task.value = taskVal; + clearUser(lvl); + shared.content.spells.healer.brightness.cast(user, {}); + b4 = { + taskVal: taskVal + }; + str += "\t\t\t| brightness(task.valΔ:" + ((task.value - b4.taskVal).toFixed(1)) + ")"; + task.value = taskVal; + clearUser(lvl); + shared.content.spells.healer.protectAura.cast(user, party); + str += "\t\t\t| protectAura(buffs.con:" + s.buffs.con + ")"; + s.buffs.con = 0; + task.value = taskVal; + clearUser(lvl); + s.hp = 0; + shared.content.spells.healer.heallAll.cast(user, party); + str += "\t\t| heallAll(hp:" + (s.hp.toFixed(1)) + ")"; + console.log(str); + return console.log('\n'); + }); + return console.log('------------------------------------------------------------'); + }); + + + /* + _.each [1,25,50,75,100,125], (lvl) -> + console.log "[LEVEL #{lvl}] (#{lvl*2} points in every attr)\n\n" + _.each {red:-25,yellow:0,green:35}, (taskVal, color) -> + console.log "[task.value = #{taskVal} (#{color})]" + console.log "direction\texpΔ\t\thpΔ\tgpΔ\ttask.valΔ\ttask.valΔ bonus\t\tboss-hit" + _.each ['up','down'], (direction) -> + clearUser(lvl) + b4 = {hp:s.hp, taskVal} + task.value = taskVal + task.type = 'daily' if direction is 'up' + delta = user.ops.score params:{id, direction} + console.log "#{if direction is 'up' then '↑' else '↓'}\t\t#{s.exp}/#{shared.tnl(s.lvl)}\t\t#{(b4.hp-s.hp).toFixed(1)}\t#{s.gp.toFixed(1)}\t#{delta.toFixed(1)}\t\t#{(task.value-b4.taskVal-delta).toFixed(1)}\t\t\t#{user.party.quest.progress.up.toFixed(1)}" + + task.value = taskVal;clearUser(lvl) + shared.content.spells.rogue.stealth.cast(user,{}) + console.log "\t\t| stealth(avoiding #{user.stats.buffs.stealth} tasks)" + user.stats.buffs.stealth = 0 + + console.log user.dailys.length + */ + +}).call(this); diff --git a/test/common/test_helper.coffee b/test/common/test_helper.coffee deleted file mode 100644 index 5636e7f0ce..0000000000 --- a/test/common/test_helper.coffee +++ /dev/null @@ -1,44 +0,0 @@ -expect = require 'expect.js' - -module.exports.addCustomMatchers = -> - Assertion = expect.Assertion - - Assertion.prototype.toHaveGP = (gp)-> - actual = @obj.stats.gp - @assert( - actual == gp, - -> "expected user to have #{gp} gp, but got #{actual}", - -> "expected user to not have #{gp} gp" - ) - - Assertion.prototype.toHaveHP = (hp)-> - actual = @obj.stats.hp - @assert( - actual == hp, - -> "expected user to have #{hp} hp, but got #{actual}", - -> "expected user to not have #{hp} hp" - ) - - Assertion.prototype.toHaveExp = (exp)-> - actual = @obj.stats.exp - @assert( - actual == exp, - -> "expected user to have #{exp} experience points, but got #{actual}", - -> "expected user to not have #{exp} experience points" - ) - - Assertion.prototype.toHaveLevel = (lvl)-> - actual = @obj.stats.lvl - @assert( - actual == lvl, - -> "expected user to be level #{lvl}, but got #{actual}", - -> "expected user to not be level #{lvl}" - ) - - Assertion.prototype.toHaveMaxMP = (mp)-> - actual = @obj._statsComputed.maxMP - @assert( - actual == mp, - -> "expected user to have #{mp} max mp, but got #{actual}", - -> "expected user to not have #{mp} max mp" - ) diff --git a/test/common/test_helper.js b/test/common/test_helper.js new file mode 100644 index 0000000000..3c482b44d3 --- /dev/null +++ b/test/common/test_helper.js @@ -0,0 +1,53 @@ +var expect; + +expect = require('expect.js'); + +module.exports.addCustomMatchers = function() { + var Assertion; + Assertion = expect.Assertion; + Assertion.prototype.toHaveGP = function(gp) { + var actual; + actual = this.obj.stats.gp; + return this.assert(actual === gp, function() { + return "expected user to have " + gp + " gp, but got " + actual; + }, function() { + return "expected user to not have " + gp + " gp"; + }); + }; + Assertion.prototype.toHaveHP = function(hp) { + var actual; + actual = this.obj.stats.hp; + return this.assert(actual === hp, function() { + return "expected user to have " + hp + " hp, but got " + actual; + }, function() { + return "expected user to not have " + hp + " hp"; + }); + }; + Assertion.prototype.toHaveExp = function(exp) { + var actual; + actual = this.obj.stats.exp; + return this.assert(actual === exp, function() { + return "expected user to have " + exp + " experience points, but got " + actual; + }, function() { + return "expected user to not have " + exp + " experience points"; + }); + }; + Assertion.prototype.toHaveLevel = function(lvl) { + var actual; + actual = this.obj.stats.lvl; + return this.assert(actual === lvl, function() { + return "expected user to be level " + lvl + ", but got " + actual; + }, function() { + return "expected user to not be level " + lvl; + }); + }; + return Assertion.prototype.toHaveMaxMP = function(mp) { + var actual; + actual = this.obj._statsComputed.maxMP; + return this.assert(actual === mp, function() { + return "expected user to have " + mp + " max mp, but got " + actual; + }, function() { + return "expected user to not have " + mp + " max mp"; + }); + }; +};