Merge branch 'master' into production

This commit is contained in:
Nick Winter 2014-11-21 13:09:02 -08:00
commit 472b05a5e0
13 changed files with 174 additions and 149 deletions

View file

@ -342,6 +342,7 @@ module.exports = Surface = class Surface extends CocoClass
@ended = true @ended = true
@setPaused true @setPaused true
Backbone.Mediator.publish 'surface:playback-ended', {} Backbone.Mediator.publish 'surface:playback-ended', {}
@updatePaths() # TODO: this is a hack to make sure paths are on the first time the level loads
else if @currentFrame < @world.totalFrames and @ended else if @currentFrame < @world.totalFrames and @ended
@ended = false @ended = false
@setPaused false @setPaused false
@ -586,7 +587,6 @@ module.exports = Surface = class Surface extends CocoClass
updatePaths: -> updatePaths: ->
return unless @options.paths and @heroLank return unless @options.paths and @heroLank
return unless me.isAdmin() # TODO: Fix world thang points, targets, then remove this
@hidePaths() @hidePaths()
return if @world.showPaths is 'never' return if @world.showPaths is 'never'
layerAdapter = @lankBoss.layerAdapters['Path'] layerAdapter = @lankBoss.layerAdapters['Path']

View file

@ -66,7 +66,7 @@
&.disabled &.disabled
@include opacity(0.8) @include opacity(0.8)
.ace_cursor, .executing, .ace_active-line, .ace_gutter-active-line .ace_cursor, .executing, .ace_active-line, .ace_gutter-active-line
@include opacity(0.2) @include opacity(0.1)
.ace_gutter .ace_gutter
background-color: transparent background-color: transparent

View file

@ -45,11 +45,15 @@
// color: rgb(197, 6, 11) // color: rgb(197, 6, 11)
color: rgb(243, 169, 49) color: rgb(243, 169, 49)
body:not(.dialogue-view-active)
.spell-palette-popover.popover
right: 45%
min-width: 350px
.spell-palette-popover.popover .spell-palette-popover.popover
// Only those popovers which are our direct children (spell documentation) // Only those popovers which are our direct children (spell documentation)
max-width: 600px max-width: 600px
right: 45%
min-width: 350px
&.pinned &.pinned
left: auto !important left: auto !important

View file

@ -378,11 +378,13 @@ module.exports = class InventoryModal extends ModalView
unless itemModel and heroClass in itemModel.classes unless itemModel and heroClass in itemModel.classes
console.log 'Unequipping', itemModel.get('heroClass'), 'item', itemModel.get('name'), 'from slot due to class restrictions.' console.log 'Unequipping', itemModel.get('heroClass'), 'item', itemModel.get('name'), 'from slot due to class restrictions.'
@unequipItemFromSlot @$el.find(".item-slot[data-slot='#{slot}']") @unequipItemFromSlot @$el.find(".item-slot[data-slot='#{slot}']")
delete equipment[slot]
for slot, item of restrictedGear for slot, item of restrictedGear
equipped = equipment[slot] equipped = equipment[slot]
if equipped and equipped is gear[restrictedGear[slot]] if equipped and equipped is gear[restrictedGear[slot]]
console.log 'Unequipping restricted item', restrictedGear[slot], 'for', slot, 'before level', @options.levelID console.log 'Unequipping restricted item', restrictedGear[slot], 'for', slot, 'before level', @options.levelID
@unequipItemFromSlot @$el.find(".item-slot[data-slot='#{slot}']") @unequipItemFromSlot @$el.find(".item-slot[data-slot='#{slot}']")
delete equipment[slot]
if heroClass is 'Warrior' if heroClass is 'Warrior'
# After they switch to a ranger or wizard, we stop being so finicky about gear. # After they switch to a ranger or wizard, we stop being so finicky about gear.
for slot, item of requiredGear for slot, item of requiredGear

View file

@ -40,7 +40,7 @@ module.exports = class WorldMapView extends RootView
@levelStatusMap = {} @levelStatusMap = {}
@levelPlayCountMap = {} @levelPlayCountMap = {}
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', null, 0).model @sessions = @supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', null, 0).model
# Temporary attempt to make sure all earned rewards are accounted for. Figure out a better solution... # Temporary attempt to make sure all earned rewards are accounted for. Figure out a better solution...
@earnedAchievements = new CocoCollection([], {url: '/db/earned_achievement', model:EarnedAchievement, project: ['earnedRewards']}) @earnedAchievements = new CocoCollection([], {url: '/db/earned_achievement', model:EarnedAchievement, project: ['earnedRewards']})
@listenToOnce @earnedAchievements, 'sync', -> @listenToOnce @earnedAchievements, 'sync', ->
@ -56,7 +56,7 @@ module.exports = class WorldMapView extends RootView
earned[group].push(reward) earned[group].push(reward)
addedSomething = true addedSomething = true
@supermodel.loadCollection(@earnedAchievements, 'achievements') @supermodel.loadCollection(@earnedAchievements, 'achievements')
@listenToOnce @sessions, 'sync', @onSessionsLoaded @listenToOnce @sessions, 'sync', @onSessionsLoaded
@getLevelPlayCounts() @getLevelPlayCounts()
$(window).on 'resize', @onWindowResize $(window).on 'resize', @onWindowResize
@ -136,7 +136,7 @@ module.exports = class WorldMapView extends RootView
if levelID = @$el.find('.level.next').data('level-id') if levelID = @$el.find('.level.next').data('level-id')
@$levelInfo = @$el.find(".level-info-container[data-level-id=#{levelID}]").show() @$levelInfo = @$el.find(".level-info-container[data-level-id=#{levelID}]").show()
pos = @$el.find('.level.next').offset() pos = @$el.find('.level.next').offset()
@adjustLevelInfoPosition pageX: pos.left, pageY: pos.top + 250 @adjustLevelInfoPosition pageX: pos.left, pageY: pos.top
@manuallyPositionedLevelInfoID = levelID @manuallyPositionedLevelInfoID = levelID
afterInsert: -> afterInsert: ->
@ -203,6 +203,7 @@ module.exports = class WorldMapView extends RootView
@$levelInfo = @$el.find(".level-info-container[data-level-id=#{levelID}]").show() @$levelInfo = @$el.find(".level-info-container[data-level-id=#{levelID}]").show()
@adjustLevelInfoPosition e @adjustLevelInfoPosition e
@endHighlight() @endHighlight()
@manuallyPositionedLevelInfoID = false
onMouseLeaveLevel: (e) -> onMouseLeaveLevel: (e) ->
return if application.isIPadApp return if application.isIPadApp

View file

@ -19,13 +19,10 @@ module.exports = class LevelDialogueView extends CocoView
onClick: (e) -> onClick: (e) ->
Backbone.Mediator.publish 'tome:focus-editor', {} Backbone.Mediator.publish 'tome:focus-editor', {}
onFrameChanged: (e) ->
@timeProgress = e.progress
@update()
onSpriteDialogue: (e) -> onSpriteDialogue: (e) ->
return unless e.message return unless e.message
@$el.addClass 'active speaking' @$el.addClass 'active speaking'
$('body').addClass('dialogue-view-active')
@setMessage e.message, e.mood, e.responses @setMessage e.message, e.mood, e.responses
window.tracker?.trackEvent 'Heard Sprite', {message: e.message, label: e.message}, ['Google Analytics'] window.tracker?.trackEvent 'Heard Sprite', {message: e.message, label: e.message}, ['Google Analytics']
@ -35,6 +32,7 @@ module.exports = class LevelDialogueView extends CocoView
onSpriteClearDialogue: -> onSpriteClearDialogue: ->
@$el.removeClass 'active speaking' @$el.removeClass 'active speaking'
$('body').removeClass('dialogue-view-active')
setMessage: (message, mood, responses) -> setMessage: (message, mood, responses) ->
message = marked message message = marked message

View file

@ -56,6 +56,7 @@ module.exports = class LevelHUDView extends CocoView
setThang: (thang, thangType) -> setThang: (thang, thangType) ->
if not thang? and not @thang? then return if not thang? and not @thang? then return
if thang? and @thang? and thang.id is @thang.id then return if thang? and @thang? and thang.id is @thang.id then return
if thang? and @hidesHUD and thang.id isnt 'Hero Placeholder' then return # Don't let them find the names of their opponents this way
@thang = thang @thang = thang
@thangType = thangType @thangType = thangType
return unless @thang return unless @thang

View file

@ -37,10 +37,11 @@ module.exports = class SpellPaletteEntryView extends CocoView
afterRender: -> afterRender: ->
super() super()
@$el.addClass(@doc.type) @$el.addClass(@doc.type)
placement = -> if $('body').hasClass('dialogue-view-active') then 'top' else 'left'
@$el.popover( @$el.popover(
animation: false animation: false
html: true html: true
placement: 'left' placement: placement
trigger: 'manual' # Hover, until they click, which will then pin it until unclick. trigger: 'manual' # Hover, until they click, which will then pin it until unclick.
content: @docFormatter.formatPopover() content: @docFormatter.formatPopover()
container: 'body' container: 'body'

View file

@ -131,13 +131,12 @@ module.exports = class SpellView extends CocoView
addCommand addCommand
name: 'toggle-playing' name: 'toggle-playing'
bindKey: {win: 'Ctrl-P', mac: 'Command-P|Ctrl-P'} bindKey: {win: 'Ctrl-P', mac: 'Command-P|Ctrl-P'}
readOnly: true
exec: -> Backbone.Mediator.publish 'level:toggle-playing', {} exec: -> Backbone.Mediator.publish 'level:toggle-playing', {}
addCommand addCommand
name: 'end-current-script' name: 'end-current-script'
bindKey: {win: 'Shift-Space', mac: 'Shift-Space'} bindKey: {win: 'Shift-Space', mac: 'Shift-Space'}
# passEvent: true # https://github.com/ajaxorg/ace/blob/master/lib/ace/keyboard/keybinding.js#L114 readOnly: true
# No easy way to selectively cancel shift+space, since we don't get access to the event.
# Maybe we could temporarily set ourselves to read-only if we somehow know that a script is active?
exec: => exec: =>
if @scriptRunning if @scriptRunning
Backbone.Mediator.publish 'level:shift-space-pressed', {} Backbone.Mediator.publish 'level:shift-space-pressed', {}
@ -147,34 +146,44 @@ module.exports = class SpellView extends CocoView
addCommand addCommand
name: 'end-all-scripts' name: 'end-all-scripts'
bindKey: {win: 'Escape', mac: 'Escape'} bindKey: {win: 'Escape', mac: 'Escape'}
exec: -> Backbone.Mediator.publish 'level:escape-pressed', {} readOnly: true
exec: ->
console.log 'esc pressed'
Backbone.Mediator.publish 'level:escape-pressed', {}
addCommand addCommand
name: 'toggle-grid' name: 'toggle-grid'
bindKey: {win: 'Ctrl-G', mac: 'Command-G|Ctrl-G'} bindKey: {win: 'Ctrl-G', mac: 'Command-G|Ctrl-G'}
readOnly: true
exec: -> Backbone.Mediator.publish 'level:toggle-grid', {} exec: -> Backbone.Mediator.publish 'level:toggle-grid', {}
addCommand addCommand
name: 'toggle-debug' name: 'toggle-debug'
bindKey: {win: 'Ctrl-\\', mac: 'Command-\\|Ctrl-\\'} bindKey: {win: 'Ctrl-\\', mac: 'Command-\\|Ctrl-\\'}
readOnly: true
exec: -> Backbone.Mediator.publish 'level:toggle-debug', {} exec: -> Backbone.Mediator.publish 'level:toggle-debug', {}
addCommand addCommand
name: 'toggle-pathfinding' name: 'toggle-pathfinding'
bindKey: {win: 'Ctrl-O', mac: 'Command-O|Ctrl-O'} bindKey: {win: 'Ctrl-O', mac: 'Command-O|Ctrl-O'}
readOnly: true
exec: -> Backbone.Mediator.publish 'level:toggle-pathfinding', {} exec: -> Backbone.Mediator.publish 'level:toggle-pathfinding', {}
addCommand addCommand
name: 'level-scrub-forward' name: 'level-scrub-forward'
bindKey: {win: 'Ctrl-]', mac: 'Command-]|Ctrl-]'} bindKey: {win: 'Ctrl-]', mac: 'Command-]|Ctrl-]'}
readOnly: true
exec: -> Backbone.Mediator.publish 'level:scrub-forward', {} exec: -> Backbone.Mediator.publish 'level:scrub-forward', {}
addCommand addCommand
name: 'level-scrub-back' name: 'level-scrub-back'
bindKey: {win: 'Ctrl-[', mac: 'Command-[|Ctrl-]'} bindKey: {win: 'Ctrl-[', mac: 'Command-[|Ctrl-]'}
readOnly: true
exec: -> Backbone.Mediator.publish 'level:scrub-back', {} exec: -> Backbone.Mediator.publish 'level:scrub-back', {}
addCommand addCommand
name: 'spell-step-forward' name: 'spell-step-forward'
bindKey: {win: 'Ctrl-Alt-]', mac: 'Command-Alt-]|Ctrl-Alt-]'} bindKey: {win: 'Ctrl-Alt-]', mac: 'Command-Alt-]|Ctrl-Alt-]'}
readOnly: true
exec: -> Backbone.Mediator.publish 'tome:spell-step-forward', {} exec: -> Backbone.Mediator.publish 'tome:spell-step-forward', {}
addCommand addCommand
name: 'spell-step-backward' name: 'spell-step-backward'
bindKey: {win: 'Ctrl-Alt-[', mac: 'Command-Alt-[|Ctrl-Alt-]'} bindKey: {win: 'Ctrl-Alt-[', mac: 'Command-Alt-[|Ctrl-Alt-]'}
readOnly: true
exec: -> Backbone.Mediator.publish 'tome:spell-step-backward', {} exec: -> Backbone.Mediator.publish 'tome:spell-step-backward', {}
addCommand addCommand
name: 'spell-beautify' name: 'spell-beautify'
@ -815,10 +824,11 @@ module.exports = class SpellView extends CocoView
return if enabled is @controlsEnabled return if enabled is @controlsEnabled
@controlsEnabled = enabled and @writable @controlsEnabled = enabled and @writable
disabled = not enabled disabled = not enabled
$('body').focus() if disabled and $(document.activeElement).is('.ace_text-input') wasFocused = @ace.isFocused()
@ace.setReadOnly disabled @ace.setReadOnly disabled
@ace[if disabled then 'setStyle' else 'unsetStyle'] 'disabled' @ace[if disabled then 'setStyle' else 'unsetStyle'] 'disabled'
@toggleBackground() @toggleBackground()
$('body').focus() if disabled and wasFocused
toggleBackground: => toggleBackground: =>
# TODO: make the background an actual background and do the CSS trick # TODO: make the background an actual background and do the CSS trick

View file

@ -67,7 +67,7 @@ class EarnedAchievementHandler extends Handler
if achievement.get('proportionalTo') if achievement.get('proportionalTo')
return @sendBadInputError(res, 'Cannot currently do this to repeatable docs...') return @sendBadInputError(res, 'Cannot currently do this to repeatable docs...')
EarnedAchievement.createForAchievement(achievement, trigger, null, (earnedAchievementDoc) => EarnedAchievement.createForAchievement(achievement, trigger, null, (earnedAchievementDoc) =>
@sendSuccess(res, earnedAchievementDoc.toObject()) @sendCreated(res, earnedAchievementDoc.toObject())
) )
) )

View file

@ -88,10 +88,13 @@ PaymentHandler = class PaymentHandler extends Handler
#- Check existence #- Check existence
transactionID = transaction.transaction_id transactionID = transaction.transaction_id
criteria = { recipient: req.user._id, 'ios.transactionID': transactionID } criteria = { 'ios.transactionID': transactionID }
Payment.findOne(criteria).exec((err, payment) => Payment.findOne(criteria).exec((err, payment) =>
if payment if payment
unless payment.get('recipient').equals(req.user._id)
return @sendForbiddenError(res)
@recalculateGemsFor(req.user, (err) => @recalculateGemsFor(req.user, (err) =>
return @sendDatabaseError(res, err) if err return @sendDatabaseError(res, err) if err
@sendSuccess(res, @formatEntity(req, payment)) @sendSuccess(res, @formatEntity(req, payment))

View file

@ -122,134 +122,133 @@ describe 'Achievement', ->
# TODO: Took level achievements out of this auto achievement business, so fix these tests # TODO: Took level achievements out of this auto achievement business, so fix these tests
#describe 'Achieving Achievements', -> describe 'Level Session Achievement', ->
# it 'wait for achievements to be loaded', (done) -> it 'does not generate earned achievements automatically, they need to be created manually', (done) ->
# Achievement.loadAchievements (achievements) -> unittest.getNormalJoe (joe) ->
# expect(Object.keys(achievements).length).toBe(1) session = new LevelSession
# permissions: simplePermissions
# loadedAchievements = Achievement.getLoadedAchievements() creator: joe._id
# expect(Object.keys(loadedAchievements).length).toBe(1) level: original: 'dungeon-arena'
# done() session.save (err, session) ->
# expect(err).toBeNull()
# it 'saving an object that should trigger an unlockable achievement', (done) -> expect(session).toBeDefined()
# unittest.getNormalJoe (joe) -> expect(session.creator).toBe(session.creator)
# session = new LevelSession
# permissions: simplePermissions EarnedAchievement.find {}, (err, earnedAchievements) ->
# creator: joe._id expect(err).toBeNull()
# level: original: 'dungeon-arena' expect(earnedAchievements.length).toBe(0)
# session.save (err, doc) ->
# expect(err).toBeNull() json = {achievement: unlockable._id, triggeredBy: session._id, collection: 'level.sessions'}
# expect(doc).toBeDefined() request.post {uri: getURL('/db/earned_achievement'), json: json}, (err, res, body) ->
# expect(doc.creator).toBe(session.creator) expect(res.statusCode).toBe(201)
# done() expect(body.achievement).toBe unlockable._id+''
# expect(body.user).toBe joe._id.toHexString()
# it 'verify that an unlockable achievement has been earned', (done) -> expect(body.notified).toBeFalsy()
# unittest.getNormalJoe (joe) -> expect(body.earnedPoints).toBe unlockable.worth
# EarnedAchievement.find {}, (err, docs) -> expect(body.achievedAmount).toBeUndefined()
# expect(err).toBeNull() expect(body.previouslyAchievedAmount).toBeUndefined()
# expect(docs.length).toBe(1) done()
# achievement = docs[0]
# expect(achievement).toBeDefined()
#
# expect(achievement.get 'achievement').toBe unlockable._id
# expect(achievement.get 'user').toBe joe._id.toHexString()
# expect(achievement.get 'notified').toBeFalsy()
# expect(achievement.get 'earnedPoints').toBe unlockable.worth
# expect(achievement.get 'achievedAmount').toBeUndefined()
# expect(achievement.get 'previouslyAchievedAmount').toBeUndefined()
# done()
#
# it 'saving an object that should trigger a repeatable achievement', (done) ->
# unittest.getNormalJoe (joe) ->
# expect(joe.get 'simulatedBy').toBeFalsy()
# joe.set('simulatedBy', 2)
# joe.save (err, doc) ->
# expect(err).toBeNull()
# done()
#
# it 'verify that a repeatable achievement has been earned', (done) ->
# unittest.getNormalJoe (joe) ->
# EarnedAchievement.find {achievementName: repeatable.name}, (err, docs) ->
# expect(err).toBeNull()
# expect(docs.length).toBe(1)
# achievement = docs[0]
#
# expect(achievement.get 'achievement').toBe repeatable._id
# expect(achievement.get 'user').toBe joe._id.toHexString()
# expect(achievement.get 'notified').toBeFalsy()
# expect(achievement.get 'earnedPoints').toBe 2 * repeatable.worth
# expect(achievement.get 'achievedAmount').toBe 2
# expect(achievement.get 'previouslyAchievedAmount').toBeFalsy()
# done()
#
#
# it 'verify that the repeatable achievement with complex exp has been earned', (done) ->
# unittest.getNormalJoe (joe) ->
# EarnedAchievement.find {achievementName: diminishing.name}, (err, docs) ->
# expect(err).toBeNull()
# expect(docs.length).toBe 1
# achievement = docs[0]
#
# expect(achievement.get 'achievedAmount').toBe 2
# expect(achievement.get 'earnedPoints').toBe (Math.log(.5 * (2 + .5)) + 1) * diminishing.worth
#
# done()
#describe 'Recalculate Achievements', ->
# EarnedAchievementHandler = require '../../../server/achievements/earned_achievement_handler' describe 'Achieving Achievements', ->
# it 'wait for achievements to be loaded', (done) ->
# it 'remove earned achievements', (done) -> Achievement.loadAchievements (achievements) ->
# clearModels [EarnedAchievement], (err) -> expect(Object.keys(achievements).length).toBe(1)
# expect(err).toBeNull()
# EarnedAchievement.find {}, (err, earned) -> loadedAchievements = Achievement.getLoadedAchievements()
# expect(earned.length).toBe 0 expect(Object.keys(loadedAchievements).length).toBe(1)
# done()
# User.update {}, {$set: {points: 0}}, {multi:true}, (err) ->
# expect(err).toBeNull() it 'saving an object that should trigger a repeatable achievement', (done) ->
# done() unittest.getNormalJoe (joe) ->
# expect(joe.get 'simulatedBy').toBeFalsy()
# it 'can not be accessed by regular users', (done) -> joe.set('simulatedBy', 2)
# loginJoe -> request.post {uri:getURL '/admin/earned_achievement/recalculate'}, (err, res, body) -> joe.save (err, doc) ->
# expect(res.statusCode).toBe 403 expect(err).toBeNull()
# done() done()
#
# it 'can recalculate a selection of achievements', (done) -> it 'verify that a repeatable achievement has been earned', (done) ->
# loginAdmin -> unittest.getNormalJoe (joe) ->
# EarnedAchievementHandler.constructor.recalculate ['dungeon-arena-started'], -> EarnedAchievement.find {achievementName: repeatable.name}, (err, docs) ->
# EarnedAchievement.find {}, (err, earnedAchievements) -> expect(err).toBeNull()
# expect(earnedAchievements.length).toBe 1 expect(docs.length).toBe(1)
# achievement = docs[0]
# # Recalculate again, doesn't change a thing
# EarnedAchievementHandler.constructor.recalculate ['dungeon-arena-started'], -> expect(achievement.get 'achievement').toBe repeatable._id
# EarnedAchievement.find {}, (err, earnedAchievements) -> expect(achievement.get 'user').toBe joe._id.toHexString()
# expect(earnedAchievements.length).toBe 1 expect(achievement.get 'notified').toBeFalsy()
# expect(achievement.get 'earnedPoints').toBe 2 * repeatable.worth
# unittest.getNormalJoe (joe) -> expect(achievement.get 'achievedAmount').toBe 2
# User.findById joe.get('id'), (err, guy) -> expect(achievement.get 'previouslyAchievedAmount').toBeFalsy()
# expect(err).toBeNull() done()
# expect(guy.get 'points').toBe unlockable.worth
# done() it 'verify that the repeatable achievement with complex exp has been earned', (done) ->
# unittest.getNormalJoe (joe) ->
# it 'can recalculate all achievements', (done) -> EarnedAchievement.find {achievementName: diminishing.name}, (err, docs) ->
# loginAdmin -> expect(err).toBeNull()
# Achievement.count {}, (err, count) -> expect(docs.length).toBe 1
# expect(count).toBe 3 achievement = docs[0]
# EarnedAchievementHandler.constructor.recalculate ->
# EarnedAchievement.find {}, (err, earnedAchievements) -> expect(achievement.get 'achievedAmount').toBe 2
# expect(earnedAchievements.length).toBe 3 expect(achievement.get 'earnedPoints').toBe (Math.log(.5 * (2 + .5)) + 1) * diminishing.worth
# unittest.getNormalJoe (joe) ->
# User.findById joe.get('id'), (err, guy) -> done()
# expect(err).toBeNull()
# expect(guy.get 'points').toBe unlockable.worth + 2 * repeatable.worth + (Math.log(.5 * (2 + .5)) + 1) * diminishing.worth describe 'Recalculate Achievements', ->
# done() EarnedAchievementHandler = require '../../../server/achievements/earned_achievement_handler'
#
# it 'cleaning up test: deleting all Achievements and related', (done) -> it 'remove earned achievements', (done) ->
# clearModels [Achievement, EarnedAchievement, LevelSession], (err) -> clearModels [EarnedAchievement], (err) ->
# expect(err).toBeNull() expect(err).toBeNull()
# EarnedAchievement.find {}, (err, earned) ->
# # reset achievements in memory as well expect(earned.length).toBe 0
# Achievement.resetAchievements()
# loadedAchievements = Achievement.getLoadedAchievements() User.update {}, {$set: {points: 0}}, {multi:true}, (err) ->
# expect(Object.keys(loadedAchievements).length).toBe(0) expect(err).toBeNull()
# done()
# done()
it 'can not be accessed by regular users', (done) ->
loginJoe -> request.post {uri:getURL '/admin/earned_achievement/recalculate'}, (err, res, body) ->
expect(res.statusCode).toBe 403
done()
it 'can recalculate a selection of achievements', (done) ->
loginAdmin ->
EarnedAchievementHandler.constructor.recalculate ['dungeon-arena-started'], ->
EarnedAchievement.find {}, (err, earnedAchievements) ->
expect(earnedAchievements.length).toBe 1
# Recalculate again, doesn't change a thing
EarnedAchievementHandler.constructor.recalculate ['dungeon-arena-started'], ->
EarnedAchievement.find {}, (err, earnedAchievements) ->
expect(earnedAchievements.length).toBe 1
unittest.getNormalJoe (joe) ->
User.findById joe.get('id'), (err, guy) ->
expect(err).toBeNull()
expect(guy.get 'points').toBe unlockable.worth
done()
it 'can recalculate all achievements', (done) ->
loginAdmin ->
Achievement.count {}, (err, count) ->
expect(count).toBe 3
EarnedAchievementHandler.constructor.recalculate ->
EarnedAchievement.find {}, (err, earnedAchievements) ->
expect(earnedAchievements.length).toBe 3
unittest.getNormalJoe (joe) ->
User.findById joe.get('id'), (err, guy) ->
expect(err).toBeNull()
expect(guy.get 'points').toBe unlockable.worth + 2 * repeatable.worth + (Math.log(.5 * (2 + .5)) + 1) * diminishing.worth
done()
it 'cleaning up test: deleting all Achievements and related', (done) ->
clearModels [Achievement, EarnedAchievement, LevelSession], (err) ->
expect(err).toBeNull()
# reset achievements in memory as well
Achievement.resetAchievements()
loadedAchievements = Achievement.getLoadedAchievements()
expect(Object.keys(loadedAchievements).length).toBe(0)
done()

View file

@ -52,6 +52,12 @@ describe '/db/payment', ->
done() done()
) )
it 'prevents other users from reusing payment receipts', (done) ->
loginSam ->
request.post {uri: paymentURL, json: firstApplePayment}, (err, res, body) ->
expect(res.statusCode).toBe 403
done()
it 'processes only the transactionID that is given', (done) -> it 'processes only the transactionID that is given', (done) ->
loginJoe -> loginJoe ->
request.post {uri: paymentURL, json: secondApplePayment}, (err, res, body) -> request.post {uri: paymentURL, json: secondApplePayment}, (err, res, body) ->