Adjust fallenPanda's PR

* Use enum for frequency in task model
* Adjust ui of task selection
* Correct check for hasStarted
* Remove semicolons
* Set default start day to today
* Correct perfect day and resting in inn tests to have dailies with start dates of a week ago
* Add tests for dailies/weeklies functionality
This commit is contained in:
Blade Barringer
2015-05-17 21:10:40 -05:00
committed by Allen Pan
parent dd1b2a4b07
commit 664bf491d2
5 changed files with 374 additions and 15 deletions

View File

@@ -76,30 +76,31 @@ api.daysSince = (yesterday, options = {}) ->
### ###
api.shouldDo = (day, dailyTask, options = {}) -> api.shouldDo = (day, dailyTask, options = {}) ->
return false unless dailyTask.type == 'daily' && dailyTask.repeat return false unless dailyTask.type == 'daily' && dailyTask.repeat
day = moment(day).startOf('day')
if !dailyTask.startDate if !dailyTask.startDate
# TODO: Unexpected code path reached. Log a warning. # TODO: Unexpected code path reached. Log a warning.
dailyTask.startDate = moment().toDate(); dailyTask.startDate = moment().toDate()
if dailyTask.startDate instanceof String if dailyTask.startDate instanceof String
#TODO: Unexpected code path reached. Log a warning. #TODO: Unexpected code path reached. Log a warning.
dailyTask.startDate = moment(dailyTask.startDate).toDate(); dailyTask.startDate = moment(dailyTask.startDate).toDate()
o = sanitizeOptions options o = sanitizeOptions options
dayOfWeek = api.startOfDay(_.defaults {now:day}, o).day() dayOfWeek = api.startOfDay(_.defaults {now:day}, o).day()
# check if event is in the future # check if event is today or in the future
hasStartedCheck = day >= dailyTask.startDate hasStartedCheck = day >= moment(dailyTask.startDate).startOf('day')
if dailyTask.frequency == 'daily' if dailyTask.frequency == 'daily'
daysSinceTaskStart = api.numDaysApart(day, dailyTask.startDate, o) daysSinceTaskStart = api.numDaysApart(day, dailyTask.startDate, o)
everyXCheck = (daysSinceTaskStart % dailyTask.everyX == 0) everyXCheck = (daysSinceTaskStart % dailyTask.everyX == 0)
return everyXCheck && hasStartedCheck return everyXCheck && hasStartedCheck
else if dailyTask.frequency == 'weekly' else if dailyTask.frequency == 'weekly'
dayOfWeekCheck = dailyTask.repeat[api.dayMapping[dayOfWeek]]; dayOfWeekCheck = dailyTask.repeat[api.dayMapping[dayOfWeek]]
weeksSinceTaskStartWeek = api.numWeeksApart(day, dailyTask.startDate, o) weeksSinceTaskStartWeek = api.numWeeksApart(day, dailyTask.startDate, o)
everyXCheck = (weeksSinceTaskStartWeek % dailyTask.everyX == 0) everyXCheck = (weeksSinceTaskStartWeek % dailyTask.everyX == 0)
return dayOfWeekCheck && everyXCheck && hasStartedCheck return dayOfWeekCheck && everyXCheck && hasStartedCheck
else else
# unexpected frequency string # unexpected frequency string
return false; return false
api.numDaysApart = (day1, day2, o) -> api.numDaysApart = (day1, day2, o) ->
startOfDay1 = api.startOfDay(_.defaults {now:day1}, o) startOfDay1 = api.startOfDay(_.defaults {now:day1}, o)
@@ -247,7 +248,7 @@ api.taskDefaults = (task={}) ->
_.defaults(task, {up:true,down:true}) if task.type is 'habit' _.defaults(task, {up:true,down:true}) if task.type is 'habit'
_.defaults(task, {history: []}) if task.type in ['habit', 'daily'] _.defaults(task, {history: []}) if task.type in ['habit', 'daily']
_.defaults(task, {completed:false}) if task.type in ['daily', 'todo'] _.defaults(task, {completed:false}) if task.type in ['daily', 'todo']
_.defaults(task, {streak:0, repeat: {su:1,m:1,t:1,w:1,th:1,f:1,s:1}}, startDate: new Date(0), everyX: 1, frequency: 'weekly') if task.type is 'daily' _.defaults(task, {streak:0, repeat: {su:1,m:1,t:1,w:1,th:1,f:1,s:1}}, startDate: new Date(), everyX: 1, frequency: 'weekly') if task.type is 'daily'
task._id = task.id # may need this for TaskSchema if we go back to using it, see http://goo.gl/a5irq4 task._id = task.id # may need this for TaskSchema if we go back to using it, see http://goo.gl/a5irq4
task.value ?= if task.type is 'reward' then 10 else 0 task.value ?= if task.type is 'reward' then 10 else 0
task.priority = 1 unless _.isNumber(task.priority) # hotfix for apiv1. once we're off apiv1, we can remove this task.priority = 1 unless _.isNumber(task.priority) # hotfix for apiv1. once we're off apiv1, we can remove this

View File

@@ -157,7 +157,7 @@ describe 'User', ->
it 'handles perfect days', -> it 'handles perfect days', ->
user = newUser() user = newUser()
user.dailys = [] user.dailys = []
_.times 3, ->user.dailys.push shared.taskDefaults({type:'daily'}) _.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 = -> user.lastCron = moment().subtract(1,'days');user.fns.cron()
cron() cron()
@@ -191,7 +191,7 @@ describe 'User', ->
user.preferences.sleep = true user.preferences.sleep = true
cron = -> user.lastCron = moment().subtract(1, 'days');user.fns.cron() cron = -> user.lastCron = moment().subtract(1, 'days');user.fns.cron()
user.dailys = [] user.dailys = []
_.times 2, -> user.dailys.push shared.taskDefaults({type:'daily'}) _.times 2, -> user.dailys.push shared.taskDefaults({type:'daily', startDate: moment().subtract(7, 'days')})
it 'remains in the inn on cron', -> it 'remains in the inn on cron', ->
cron() cron()

359
test/common/dailies.coffee Normal file
View File

@@ -0,0 +1,359 @@
_ = require 'lodash'
expect = require 'expect.js'
sinon = require 'sinon'
moment = require 'moment'
shared = require '../../common/script/index.coffee'
shared.i18n.translations = require('../../website/src/i18n.js').translations
repeatWithoutLastWeekday = ()->
repeat = {su:1,m:1,t:1,w:1,th:1,f:1,s:1}
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) ->
usr.lastCron = moment().subtract(1,'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:1,m:1,t:1,w:1,th:1,f:1,s:1}})
]
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', ->
completeDaily = null
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)
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 'when startDate is today', ->
completeDaily = null
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 10, (n) ->
due = n + 1
it 'where x equals ' + due, ->
daily.everyX = due
_.times 100, (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 'weekly that repeats on M,W,F every x days', ->
user = null
weekly = null
start_date = moment()
beforeEach ->
user = newUser()
user.dailys = [ shared.taskDefaults({type:'daily', startDate: start_date, frequency: 'weekly', repeat: {su:false,m:1,t:false,w:1,th:false,f:1,s:false}}) ]
weekly = user.dailys[0]
_.times 10, (n) ->
due = n + 1
it 'where x equals ' + due, ->
weekly.everyX = due
_.times 100, (day) ->
valid_days = { Mon: true, Wed: true, Fri: true }
day_to_test = start_date.add(day, 'days')
day_of_week = moment(day_to_test).format('ddd')
isDue = shared.shouldDo day_to_test, weekly
is_correct_week = shared.numWeeksApart(day_to_test, start_date) % due == 0
if valid_days[day_of_week] && is_correct_week
expect(isDue).to.be true
else
expect(isDue).to.be false

View File

@@ -51,8 +51,7 @@ var checklist = [{
var DailySchema = new Schema( var DailySchema = new Schema(
_.defaults({ _.defaults({
type: {type: String, 'default': 'daily'}, type: {type: String, 'default': 'daily'},
//TODO: Cleaner to store interval as enum instead of str? frequency: {type: String, 'default': 'weekly', enum: ['daily', 'weekly']},
frequency: {type: String, 'default': 'weekly'}, // 'daily', 'weekly'
everyX: {type: Number, 'default': 1}, // e.g. once every X weeks everyX: {type: Number, 'default': 1}, // e.g. once every X weeks
startDate: {type: Date}, startDate: {type: Date},
history: Array, history: Array,

View File

@@ -209,11 +209,11 @@ li(bindonce='list', id='task-{{::task.id}}', ng-repeat='task in obj[list.type+"s
button(ng-class='{active: task.repeat.s}', type='button', ng-click='task.challenge.id || (task.repeat.s = !task.repeat.s)') {{::moment.weekdaysMin(6)}} button(ng-class='{active: task.repeat.s}', type='button', ng-click='task.challenge.id || (task.repeat.s = !task.repeat.s)') {{::moment.weekdaysMin(6)}}
label.option-title=env.t('startDate') label.option-title=env.t('startDate')
p(class='input-group') p.input-group
input(type='text' class='form-control' datepicker-popup ng-model='task._tempDateForPicker' ng-change='updateTaskStartDate(task)' is-open='task._isDatePickerOpen') input(type='text' class='form-control' datepicker-popup ng-model='task._tempDateForPicker' ng-change='updateTaskStartDate(task)' is-open='task._isDatePickerOpen')
span(class='input-group-btn') span.input-group-btn
button(type="button" ng-click="openDatePicker($event, task)") button.btn.btn-default(type='button' ng-click='openDatePicker($event, task)')
span.input-group-addon.glyphicon.glyphicon-calendar .glyphicon.glyphicon-calendar
// if Reward, pricing // if Reward, pricing