instead of , but would need to adjust colors
diff --git a/app/views/play/level/tome/spell_palette_view.coffee b/app/views/play/level/tome/SpellPaletteView.coffee
similarity index 89%
rename from app/views/play/level/tome/spell_palette_view.coffee
rename to app/views/play/level/tome/SpellPaletteView.coffee
index 89914fb3e..769cd8f92 100644
--- a/app/views/play/level/tome/spell_palette_view.coffee
+++ b/app/views/play/level/tome/SpellPaletteView.coffee
@@ -2,9 +2,9 @@ CocoView = require 'views/kinds/CocoView'
template = require 'templates/play/level/tome/spell_palette'
{me} = require 'lib/auth'
filters = require 'lib/image_filter'
-SpellPaletteEntryView = require './spell_palette_entry_view'
+SpellPaletteEntryView = require './SpellPaletteEntryView'
LevelComponent = require 'models/LevelComponent'
-EditorConfigModal = require '../modal/editor_config_modal'
+EditorConfigModal = require '../modal/EditorConfigModal'
N_ROWS = 4
@@ -84,13 +84,16 @@ module.exports = class SpellPaletteView extends CocoView
snippets: 'programmableSnippets'
else
propStorage =
- 'this': 'apiProperties'
+ 'this': ['apiProperties', 'apiMethods']
count = 0
propGroups = {}
- for owner, storage of propStorage
- props = _.reject @thang[storage] ? [], (prop) -> prop[0] is '_' # no private properties
- added = propGroups[owner] = _.sortBy(props).slice()
- count += added.length
+ for owner, storages of propStorage
+ storages = [storages] if _.isString storages
+ for storage in storages
+ props = _.reject @thang[storage] ? [], (prop) -> prop[0] is '_' # no private properties
+ added = _.sortBy(props).slice()
+ propGroups[owner] = (propGroups[owner] ? []).concat added
+ count += added.length
shortenize = count > 6
tabbify = count >= 10
@@ -150,11 +153,11 @@ module.exports = class SpellPaletteView extends CocoView
toggleBackground: =>
# TODO: make the palette background an actual background and do the CSS trick
# used in spell_list_entry.sass for disabling
- background = @$el.find('.code-palette-background')[0]
+ background = @$el.find('img.code-palette-background')[0]
if background.naturalWidth is 0 # not loaded yet
return _.delay @toggleBackground, 100
- filters.revertImage background if @controlsEnabled
- filters.darkenImage background, 0.8 unless @controlsEnabled
+ filters.revertImage background, 'span.code-palette-background' if @controlsEnabled
+ filters.darkenImage background, 'span.code-palette-background', 0.8 unless @controlsEnabled
onFrameChanged: (e) ->
return unless e.selectedThang?.id is @thang.id
diff --git a/app/views/play/level/tome/spell_toolbar_view.coffee b/app/views/play/level/tome/SpellToolbarView.coffee
similarity index 100%
rename from app/views/play/level/tome/spell_toolbar_view.coffee
rename to app/views/play/level/tome/SpellToolbarView.coffee
diff --git a/app/views/play/level/tome/spell_view.coffee b/app/views/play/level/tome/SpellView.coffee
similarity index 98%
rename from app/views/play/level/tome/spell_view.coffee
rename to app/views/play/level/tome/SpellView.coffee
index 46fb62e4e..ee979cc38 100644
--- a/app/views/play/level/tome/spell_view.coffee
+++ b/app/views/play/level/tome/SpellView.coffee
@@ -3,9 +3,9 @@ template = require 'templates/play/level/tome/spell'
{me} = require 'lib/auth'
filters = require 'lib/image_filter'
Range = ace.require('ace/range').Range
-Problem = require './problem'
-SpellDebugView = require './spell_debug_view'
-SpellToolbarView = require './spell_toolbar_view'
+Problem = require './Problem'
+SpellDebugView = require './SpellDebugView'
+SpellToolbarView = require './SpellToolbarView'
LevelComponent = require 'models/LevelComponent'
module.exports = class SpellView extends CocoView
@@ -167,6 +167,10 @@ module.exports = class SpellView extends CocoView
bindKey: {win: 'Ctrl-L', mac: 'Command-L'}
passEvent: true
exec: -> # just prevent default ACE go-to-line alert
+ addCommand
+ name: 'open-fullscreen-editor'
+ bindKey: {win: 'Alt-Shift-F', mac: 'Ctrl-Shift-F'}
+ exec: -> Backbone.Mediator.publish 'tome:fullscreen-view'
fillACE: ->
@ace.setValue @spell.source
@@ -630,11 +634,11 @@ module.exports = class SpellView extends CocoView
toggleBackground: =>
# TODO: make the background an actual background and do the CSS trick
# used in spell_list_entry.sass for disabling
- background = @$el.find('.code-background')[0]
+ background = @$el.find('img.code-background')[0]
if background.naturalWidth is 0 # not loaded yet
return _.delay @toggleBackground, 100
- filters.revertImage background if @controlsEnabled
- filters.darkenImage background, 0.8 unless @controlsEnabled
+ filters.revertImage background, 'span.code-background' if @controlsEnabled
+ filters.darkenImage background, 'span.code-background', 0.8 unless @controlsEnabled
onSpellBeautify: (e) ->
return unless @spellThang and (@ace.isFocused() or e.spell is @spell)
diff --git a/app/views/play/level/tome/thang_list_entry_view.coffee b/app/views/play/level/tome/ThangListEntryView.coffee
similarity index 98%
rename from app/views/play/level/tome/thang_list_entry_view.coffee
rename to app/views/play/level/tome/ThangListEntryView.coffee
index 06756c898..442dbe0e9 100644
--- a/app/views/play/level/tome/thang_list_entry_view.coffee
+++ b/app/views/play/level/tome/ThangListEntryView.coffee
@@ -2,7 +2,7 @@
# TODO: reordering based on errors isn't working yet
CocoView = require 'views/kinds/CocoView'
-ThangAvatarView = require 'views/play/level/thang_avatar_view'
+ThangAvatarView = require 'views/play/level/ThangAvatarView'
template = require 'templates/play/level/tome/thang_list_entry'
spellsPopoverTemplate = require 'templates/play/level/tome/thang_list_entry_spells'
{me} = require 'lib/auth'
diff --git a/app/views/play/level/tome/thang_list_view.coffee b/app/views/play/level/tome/ThangListView.coffee
similarity index 98%
rename from app/views/play/level/tome/thang_list_view.coffee
rename to app/views/play/level/tome/ThangListView.coffee
index 9f34e2977..03ab371c1 100644
--- a/app/views/play/level/tome/thang_list_view.coffee
+++ b/app/views/play/level/tome/ThangListView.coffee
@@ -4,7 +4,7 @@
CocoView = require 'views/kinds/CocoView'
template = require 'templates/play/level/tome/thang_list'
{me} = require 'lib/auth'
-ThangListEntryView = require './thang_list_entry_view'
+ThangListEntryView = require './ThangListEntryView'
module.exports = class ThangListView extends CocoView
className: 'thang-list-view'
diff --git a/app/views/play/level/tome/tome_view.coffee b/app/views/play/level/tome/TomeView.coffee
similarity index 97%
rename from app/views/play/level/tome/tome_view.coffee
rename to app/views/play/level/tome/TomeView.coffee
index 3ef45258c..e0335cd70 100644
--- a/app/views/play/level/tome/tome_view.coffee
+++ b/app/views/play/level/tome/TomeView.coffee
@@ -30,11 +30,11 @@
CocoView = require 'views/kinds/CocoView'
template = require 'templates/play/level/tome/tome'
{me} = require 'lib/auth'
-Spell = require './spell'
-SpellListView = require './spell_list_view'
-ThangListView = require './thang_list_view'
-SpellPaletteView = require './spell_palette_view'
-CastButtonView = require './cast_button_view'
+Spell = require './Spell'
+SpellListView = require './SpellListView'
+ThangListView = require './ThangListView'
+SpellPaletteView = require './SpellPaletteView'
+CastButtonView = require './CastButtonView'
window.SHIM_WORKER_PATH = '/javascripts/workers/catiline_worker_shim.js'
diff --git a/bower.json b/bower.json
index e979e2f49..32c815408 100644
--- a/bower.json
+++ b/bower.json
@@ -44,7 +44,7 @@
"bootstrap": "~3.1.1",
"validated-backbone-mediator": "~0.1.3",
"jquery.browser": "~0.0.6",
- "zatanna": "~0.0.2",
+ "zatanna": "~0.0.4",
"modernizr": "~2.8.3"
},
"overrides": {
diff --git a/server/commons/Handler.coffee b/server/commons/Handler.coffee
index 9dfca705b..55b45884f 100644
--- a/server/commons/Handler.coffee
+++ b/server/commons/Handler.coffee
@@ -13,12 +13,20 @@ FETCH_LIMIT = 200
module.exports = class Handler
# subclasses should override these properties
modelClass: null
+ privateProperties: []
editableProperties: []
postEditableProperties: []
jsonSchema: {}
waterfallFunctions: []
allowedMethods: ['GET', 'POST', 'PUT', 'PATCH']
+ constructor: ->
+ # TODO The second 'or' is for backward compatibility only is for backward compatibility only
+ @privateProperties = @modelClass.privateProperties or @privateProperties or []
+ @editableProperties = @modelClass.editableProperties or @editableProperties or []
+ @postEditableProperties = @modelClass.postEditableProperties or @postEditableProperties or []
+ @jsonSchema = @modelClass.jsonSchema or @jsonSchema or {}
+
# subclasses should override these methods
hasAccess: (req) -> true
hasAccessToDocument: (req, document, method=null) ->
@@ -47,7 +55,7 @@ module.exports = class Handler
# sending functions
sendUnauthorizedError: (res) -> errors.forbidden(res) #TODO: rename sendUnauthorizedError to sendForbiddenError
- sendNotFoundError: (res) -> errors.notFound(res)
+ sendNotFoundError: (res, message) -> errors.notFound(res, message)
sendMethodNotAllowed: (res) -> errors.badMethod(res)
sendBadInputError: (res, message) -> errors.badInput(res, message)
sendDatabaseError: (res, err) ->
@@ -158,7 +166,7 @@ module.exports = class Handler
ids = ids.split(',') if _.isString ids
ids = _.uniq ids
- project = {name:1, original:1}
+ project = {name:1, original:1, kind:1}
sort = {'version.major':-1, 'version.minor':-1}
makeFunc = (id) =>
@@ -435,3 +443,10 @@ module.exports = class Handler
delete: (req, res) -> @sendMethodNotAllowed res, @allowedMethods, 'DELETE not allowed.'
head: (req, res) -> @sendMethodNotAllowed res, @allowedMethods, 'HEAD not allowed.'
+
+ # This is not a Mongoose user
+ projectionForUser: (req, model, ownerID) ->
+ return {} if 'privateProperties' not of model or req.user._id + '' is ownerID + '' or req.user.isAdmin()
+ projection = {}
+ projection[field] = 0 for field in model.privateProperties
+ projection
diff --git a/server/levels/sessions/LevelSession.coffee b/server/levels/sessions/LevelSession.coffee
index 4873e40a5..b8f0a58dc 100644
--- a/server/levels/sessions/LevelSession.coffee
+++ b/server/levels/sessions/LevelSession.coffee
@@ -24,4 +24,10 @@ LevelSessionSchema.pre 'save', (next) ->
@set('changed', new Date())
next()
+LevelSessionSchema.statics.privateProperties = ['code', 'submittedCode', 'unsubscribed']
+LevelSessionSchema.statics.editableProperties = ['multiplayer', 'players', 'code', 'codeLanguage', 'completed', 'state',
+ 'levelName', 'creatorName', 'levelID', 'screenshot',
+ 'chat', 'teamSpells', 'submitted', 'submittedCodeLanguage', 'unsubscribed', 'playtime']
+LevelSessionSchema.statics.jsonSchema = jsonschema
+
module.exports = LevelSession = mongoose.model('level.session', LevelSessionSchema)
diff --git a/server/levels/sessions/level_session_handler.coffee b/server/levels/sessions/level_session_handler.coffee
index b1179acf8..678b33e80 100644
--- a/server/levels/sessions/level_session_handler.coffee
+++ b/server/levels/sessions/level_session_handler.coffee
@@ -6,11 +6,6 @@ TIMEOUT = 1000 * 30 # no activity for 30 seconds means it's not active
class LevelSessionHandler extends Handler
modelClass: LevelSession
- editableProperties: ['multiplayer', 'players', 'code', 'codeLanguage', 'completed', 'state',
- 'levelName', 'creatorName', 'levelID', 'screenshot',
- 'chat', 'teamSpells', 'submitted', 'submittedCodeLanguage', 'unsubscribed', 'playtime']
- privateProperties: ['code', 'submittedCode', 'unsubscribed']
- jsonSchema: require '../../../app/schemas/models/level_session'
getByRelationship: (req, res, args...) ->
return @getActiveSessions req, res if args.length is 2 and args[1] is 'active'
diff --git a/server/plugins/plugins.coffee b/server/plugins/plugins.coffee
index 2fdce3bef..7d7911ae2 100644
--- a/server/plugins/plugins.coffee
+++ b/server/plugins/plugins.coffee
@@ -30,6 +30,9 @@ module.exports.NamedPlugin = (schema) ->
schema.add({name: String, slug: String})
schema.index({'slug': 1}, {unique: true, sparse: true, name: 'slug index'})
+ schema.statics.getBySlug = (slug, done) ->
+ @findOne {slug: slug}, done
+
schema.pre('save', (next) ->
if schema.uses_coco_versions
v = @get('version')
diff --git a/server/queues/scoring.coffee b/server/queues/scoring.coffee
index d35ce871a..d6aa6f3cb 100644
--- a/server/queues/scoring.coffee
+++ b/server/queues/scoring.coffee
@@ -124,7 +124,7 @@ module.exports.getTwoGames = (req, res) ->
#if userIsAnonymous req then return errors.unauthorized(res, 'You need to be logged in to get games.')
humansGameID = req.body.humansGameID
ogresGameID = req.body.ogresGameID
- ladderGameIDs = ['greed', 'criss-cross', 'brawlwood', 'dungeon-arena', 'gold-rush']
+ ladderGameIDs = ['greed', 'criss-cross', 'brawlwood', 'dungeon-arena', 'gold-rush', 'sky-span']
levelID = _.sample ladderGameIDs
unless ogresGameID and humansGameID
#fetch random games here
diff --git a/server/users/User.coffee b/server/users/User.coffee
index 13f4c11fb..0372d073c 100644
--- a/server/users/User.coffee
+++ b/server/users/User.coffee
@@ -158,6 +158,18 @@ UserSchema.statics.hashPassword = (password) ->
shasum.update(salt + password)
shasum.digest('hex')
+UserSchema.statics.privateProperties = [
+ 'permissions', 'email', 'firstName', 'lastName', 'gender', 'facebookID',
+ 'gplusID', 'music', 'volume', 'aceConfig', 'employerAt', 'signedEmployerAgreement'
+]
+UserSchema.statics.jsonSchema = jsonschema
+UserSchema.statics.editableProperties = [
+ 'name', 'photoURL', 'password', 'anonymous', 'wizardColor1', 'volume',
+ 'firstName', 'lastName', 'gender', 'facebookID', 'gplusID', 'emails',
+ 'testGroupNumber', 'music', 'hourOfCode', 'hourOfCodeComplete', 'preferredLanguage',
+ 'wizard', 'aceConfig', 'autocastDelay', 'lastLevel', 'jobProfile', 'savedEmployerFilterAlerts'
+]
+
UserSchema.plugin plugins.NamedPlugin
module.exports = User = mongoose.model('User', UserSchema)
diff --git a/server/users/user_handler.coffee b/server/users/user_handler.coffee
index 4a6e85320..a450951b0 100644
--- a/server/users/user_handler.coffee
+++ b/server/users/user_handler.coffee
@@ -14,37 +14,26 @@ EarnedAchievement = require '../achievements/EarnedAchievement'
UserRemark = require './remarks/UserRemark'
serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset']
-privateProperties = [
- 'permissions', 'email', 'firstName', 'lastName', 'gender', 'facebookID',
- 'gplusID', 'music', 'volume', 'aceConfig', 'employerAt', 'signedEmployerAgreement'
-]
candidateProperties = [
'jobProfile', 'jobProfileApproved', 'jobProfileNotes'
]
UserHandler = class UserHandler extends Handler
modelClass: User
- jsonSchema: schema
- editableProperties: [
- 'name', 'photoURL', 'password', 'anonymous', 'wizardColor1', 'volume',
- 'firstName', 'lastName', 'gender', 'facebookID', 'gplusID', 'emails',
- 'testGroupNumber', 'music', 'hourOfCode', 'hourOfCodeComplete', 'preferredLanguage',
- 'wizard', 'aceConfig', 'autocastDelay', 'lastLevel', 'jobProfile', 'savedEmployerFilterAlerts'
- ]
getEditableProperties: (req, document) ->
props = super req, document
props.push 'permissions' unless config.isProduction
props.push 'jobProfileApproved', 'jobProfileNotes','jobProfileApprovedDate' if req.user.isAdmin() # Admins naturally edit these
- props.push privateProperties... if req.user.isAdmin() # Admins are mad with power
+ props.push @privateProperties... if req.user.isAdmin() # Admins are mad with power
props
- formatEntity: (req, document) ->
+ formatEntity: (req, document) =>
return null unless document?
obj = document.toObject()
delete obj[prop] for prop in serverProperties
includePrivates = req.user and (req.user.isAdmin() or req.user._id.equals(document._id))
- delete obj[prop] for prop in privateProperties unless includePrivates
+ delete obj[prop] for prop in @privateProperties unless includePrivates
includeCandidate = includePrivates or (obj.jobProfile?.active and req.user and ('employer' in (req.user.get('permissions') ? [])) and @employerCanViewCandidate req.user, obj)
delete obj[prop] for prop in candidateProperties unless includeCandidate
return obj
@@ -239,31 +228,40 @@ UserHandler = class UserHandler extends Handler
documents = (LevelSessionHandler.formatEntity(req, doc) for doc in documents)
@sendSuccess(res, documents)
- getLevelSessions: (req, res, userID) ->
- query = creator: userID
- isAuthorized = req.user._id+'' is userID or req.user.isAdmin()
- projection = {}
- if req.query.project
- projection[field] = 1 for field in req.query.project.split(',') when isAuthorized or not (field in LevelSessionHandler.privateProperties)
- else unless isAuthorized
- projection[field] = 0 for field in LevelSessionHandler.privateProperties
+ IDify: (idOrSlug, done) ->
+ return done null, idOrSlug if Handler.isID idOrSlug
+ User.getBySlug idOrSlug, (err, user) -> done err, user?.get '_id'
- LevelSession.find(query).select(projection).exec (err, documents) =>
- return @sendDatabaseError(res, err) if err
- documents = (LevelSessionHandler.formatEntity(req, doc) for doc in documents)
- @sendSuccess(res, documents)
+ getLevelSessions: (req, res, userIDOrSlug) ->
+ @IDify userIDOrSlug, (err, userID) =>
+ return @sendDatabaseError res, err if err
+ return @sendNotFoundError res unless userID?
+ query = creator: userID + ''
+ isAuthorized = req.user._id+'' is userID or req.user.isAdmin()
+ projection = {}
+ if req.query.project
+ projection[field] = 1 for field in req.query.project.split(',') when isAuthorized or not (field in LevelSessionHandler.privateProperties)
+ else unless isAuthorized
+ projection[field] = 0 for field in LevelSessionHandler.privateProperties
- getEarnedAchievements: (req, res, userID) ->
- queryObject = {$query: {user: userID}, $orderby: {changed: -1}}
- queryObject.$query.notified = false if req.query.notified is 'false'
- query = EarnedAchievement.find(queryObject)
- query.exec (err, documents) =>
- return @sendDatabaseError(res, err) if err?
- cleandocs = (@formatEntity(req, doc) for doc in documents)
- for doc in documents # Maybe move this logic elsewhere
- doc.set('notified', true)
- doc.save()
- @sendSuccess(res, cleandocs)
+ LevelSession.find(query).select(projection).exec (err, documents) =>
+ return @sendDatabaseError(res, err) if err
+ documents = (LevelSessionHandler.formatEntity(req, doc) for doc in documents)
+ @sendSuccess(res, documents)
+
+ getEarnedAchievements: (req, res, userIDOrSlug) ->
+ @IDify userIDOrSlug, (err, userID) =>
+ return @sendDatabaseError res, err if err
+ return @sendNotFoundError res unless userID?
+ query = user: userID + ''
+ query.notified = false if req.query.notified is 'false'
+ EarnedAchievement.find(query).sort(changed: -1).exec (err, documents) =>
+ return @sendDatabaseError(res, err) if err?
+ cleandocs = (@formatEntity(req, doc) for doc in documents)
+ for doc in documents # TODO Ruben Maybe move this logic elsewhere
+ doc.set('notified', true)
+ doc.save()
+ @sendSuccess(res, cleandocs)
trackActivity: (req, res, userID, activityName, increment=1) ->
return @sendMethodNotAllowed res unless req.method is 'POST'
diff --git a/test/app/models/Level.spec.coffee b/test/app/models/Level.spec.coffee
new file mode 100644
index 000000000..db90afd8d
--- /dev/null
+++ b/test/app/models/Level.spec.coffee
@@ -0,0 +1,52 @@
+SuperModel = require 'models/SuperModel'
+Level = require 'models/Level'
+ThangType = require 'models/ThangType'
+
+describe 'Level', ->
+ describe 'denormalize', ->
+ level = new Level({
+ thangs: [
+ {
+ "thangType": "A"
+ "id": "Tharin"
+ "components": [
+ {"original": "a", "majorVersion": 0}
+ {"original": "b", "majorVersion": 0, "config": {i: 2}}
+ {"original": "c", "majorVersion": 0, "config": {i: 1, ii: 2, nest: {iii: 3}}}
+ # should add one more
+ ]
+ }
+ ]
+ })
+
+ thangType = new ThangType({
+ original: 'A'
+ version: {major: 0, minor: 0}
+ components: [
+ {"original": "a", "majorVersion": 0, "config": {i: 1}}
+ {"original": "c", "majorVersion": 0, "config": {i: 3, nest: {iv: 4}}}
+ {"original": "d", "majorVersion": 0, "config": {i: 1}}
+ ]
+ })
+
+ supermodel = new SuperModel()
+ supermodel.registerModel(thangType)
+
+ result = level.denormalize(supermodel)
+ tharinThangComponents = result.thangs[0].components
+
+ it 'adds default configs to thangs without any config', ->
+ aComp = _.find tharinThangComponents, {original:'a'}
+ expect(_.isEqual(aComp.config, {i:1})).toBeTruthy()
+
+ it 'leaves alone configs for components the level thang has but the thang type does not', ->
+ bComp = _.find tharinThangComponents, {original:'b'}
+ expect(_.isEqual(bComp.config, {i:2})).toBeTruthy()
+
+ it 'merges configs where both the level thang and thang type have one, giving priority to the level thang', ->
+ cComp = _.find tharinThangComponents, {original:'c'}
+ expect(_.isEqual(cComp.config, {i: 1, ii: 2, nest: {iii: 3, iv: 4}})).toBeTruthy()
+
+ it 'adds components from the thang type that do not exist in the level thang', ->
+ dComp = _.find tharinThangComponents, {original:'d'}
+ expect(_.isEqual(dComp.config, {i: 1})).toBeTruthy()
\ No newline at end of file
diff --git a/test/app/require.spec.coffee b/test/app/require.spec.coffee
new file mode 100644
index 000000000..33ebcf846
--- /dev/null
+++ b/test/app/require.spec.coffee
@@ -0,0 +1,9 @@
+describe 'require', ->
+ it 'has no modules that error when you import them', ->
+ modules = window.require.list()
+ for module in modules
+ try
+ require(module)
+ catch
+ console.error 'Could not load', module
+ expect(false).toBe(true)
\ No newline at end of file
diff --git a/test/app/vendor/lodash.spec.coffee b/test/app/vendor/lodash.spec.coffee
new file mode 100644
index 000000000..cbc5b6766
--- /dev/null
+++ b/test/app/vendor/lodash.spec.coffee
@@ -0,0 +1,23 @@
+describe 'merge', ->
+ it 'combines nested objects recursively', ->
+ a = { i: 0, nest: { iii: 0 }}
+ b = { ii: 0, nest: { iv: 0 }}
+ res = _.merge(a, b)
+ expect(_.isEqual(res, { i: 0, ii: 0, nest: {iii:0, iv:0}})).toBeTruthy()
+
+ it 'overwrites values from source to object', ->
+ a = { i: 0 }
+ b = { i: 1 }
+ res = _.merge(a, b)
+ expect(_.isEqual(res, b)).toBeTruthy()
+
+ it 'treats arrays as atomic', ->
+ a = { i: 0 }
+ b = { i: [1,2,3] }
+ res = _.merge(a, b)
+ expect(_.isEqual(res, b)).toBeTruthy()
+
+ a = { i: [5,4,3] }
+ b = { i: [1,2,3] }
+ res = _.merge(a, b)
+ expect(_.isEqual(res, b)).toBeTruthy()
\ No newline at end of file
diff --git a/test/app/views/editor/level/EditorLevelView.spec.coffee b/test/app/views/editor/level/EditorLevelView.spec.coffee
index 47b32700f..7a9064993 100644
--- a/test/app/views/editor/level/EditorLevelView.spec.coffee
+++ b/test/app/views/editor/level/EditorLevelView.spec.coffee
@@ -1,11 +1,11 @@
-EditorLevelView = require 'views/editor/level/edit'
+LevelEditView = require 'views/editor/level/LevelEditView'
emptyLevel = {'_id': '53a0a1e2d9048dbc3a793c81', 'name': 'Emptiness', 'description': 'Tis nothing..', 'documentation': {'generalArticles': [], 'specificArticles': []}, 'scripts': [], 'thangs': [], 'systems': [], 'victory': {}, 'version': {'minor': 0, 'major': 0, 'isLatestMajor': true, 'isLatestMinor': true}, 'index': '5388f9ac9a904d0000d94f87', 'slug': 'emptiness', 'creator': '5388f9ac9a904d0000d94f87', 'original': '53a0a1e2d9048dbc3a793c81', 'watchers': ['5388f9ac9a904d0000d94f87'], '__v': 0, 'created': '2014-06-17T20:15:30.207Z', 'permissions': [{'access': 'owner', 'target': '5388f9ac9a904d0000d94f87'}]}
-describe 'EditorLevelView', ->
+describe 'LevelEditView', ->
describe 'revert button', ->
it 'opens just one modal when you click it', ->
- view = new EditorLevelView({}, 'something')
+ view = new LevelEditView({}, 'something')
request = jasmine.Ajax.requests.first()
request.response {status: 200, responseText: JSON.stringify(emptyLevel)}
view.render()
diff --git a/test/app/views/home_view.spec.coffee b/test/app/views/home_view.spec.coffee
deleted file mode 100644
index e69de29bb..000000000
diff --git a/test/app/views/modal/AuthModalView.spec.coffee b/test/app/views/modal/AuthModal.spec.coffee
similarity index 53%
rename from test/app/views/modal/AuthModalView.spec.coffee
rename to test/app/views/modal/AuthModal.spec.coffee
index 01bd1b98e..199b265f4 100644
--- a/test/app/views/modal/AuthModalView.spec.coffee
+++ b/test/app/views/modal/AuthModal.spec.coffee
@@ -1,12 +1,12 @@
-AuthModalView = require 'views/modal/auth_modal'
-RecoverModalView = require 'views/modal/recover_modal'
+AuthModal = require 'views/modal/AuthModal'
+RecoverModal = require 'views/modal/RecoverModal'
-describe 'AuthModalView', ->
+describe 'AuthModal', ->
it 'opens the recover modal when you click the recover link', ->
- m = new AuthModalView()
+ m = new AuthModal()
m.render()
spyOn(m, 'openModalView')
m.$el.find('#link-to-recover').click()
expect(m.openModalView.calls.count()).toEqual(1)
args = m.openModalView.calls.argsFor(0)
- expect(args[0] instanceof RecoverModalView).toBeTruthy()
+ expect(args[0] instanceof RecoverModal).toBeTruthy()
diff --git a/test/app/views/play/ladder/ladder_tab.spec.coffee b/test/app/views/play/ladder/LadderTabView.coffee
similarity index 91%
rename from test/app/views/play/ladder/ladder_tab.spec.coffee
rename to test/app/views/play/ladder/LadderTabView.coffee
index 0f74b341b..e23fc26be 100644
--- a/test/app/views/play/ladder/ladder_tab.spec.coffee
+++ b/test/app/views/play/ladder/LadderTabView.coffee
@@ -1,4 +1,4 @@
-LadderTabView = require 'views/play/ladder/ladder_tab'
+LadderTabView = require 'views/play/ladder/LadderTabView'
Level = require 'models/Level'
fixtures = require 'test/app/fixtures/levels'
diff --git a/test/demo/views/user/JobProfileView.demo.coffee b/test/demo/views/user/JobProfileView.demo.coffee
index 17ac8767b..5ab643f87 100644
--- a/test/demo/views/user/JobProfileView.demo.coffee
+++ b/test/demo/views/user/JobProfileView.demo.coffee
@@ -1,4 +1,4 @@
-ProfileView = require 'views/account/profile_view'
+JobProfileView = require 'views/account/JobProfileView'
responses =
'/db/user/joe/nameToID':'512ef4805a67a8c507000001'
@@ -564,7 +564,7 @@ responses =
module.exports = ->
me.isAdmin = -> false
me.set('permissions', ['employer'])
- v = new ProfileView({}, 'joe')
+ v = new JobProfileView({}, 'joe')
for url, responseBody of responses
requests = jasmine.Ajax.requests.filter(url)
if not requests.length
diff --git a/test/server/functional/level_session.spec.coffee b/test/server/functional/level_session.spec.coffee
index ba0265cf0..eddb3407c 100644
--- a/test/server/functional/level_session.spec.coffee
+++ b/test/server/functional/level_session.spec.coffee
@@ -1,12 +1,64 @@
require '../common'
-describe 'LevelFeedback', ->
+describe '/db/level.session', ->
- url = getURL('/db/level.session')
+ url = getURL('/db/level.session/')
+ session =
+ permissions: simplePermissions
it 'get schema', (done) ->
- request.get {uri: url+'/schema'}, (err, res, body) ->
+ request.get {uri: url+'schema'}, (err, res, body) ->
expect(res.statusCode).toBe(200)
body = JSON.parse(body)
expect(body.type).toBeDefined()
done()
+
+ it 'clears things first', (done) ->
+ clearModels [LevelSession], (err) ->
+ expect(err).toBeNull()
+ done()
+
+ # TODO Tried to mimic what happens on the site. Why is this even so hard to do.
+ # Right now it's even possible to create ownerless sessions through POST
+ xit 'allows users to create level sessions through PATCH', (done) ->
+ loginJoe (joe) ->
+ console.log url + mongoose.Types.ObjectId()
+ request {method: 'patch', uri: url + mongoose.Types.ObjectId(), json: session}, (err, res, body) ->
+ expect(err).toBeNull()
+ expect(res.statusCode).toBe 200
+ console.log body
+ expect(body.creator).toEqual joe.get('_id').toHexString()
+ done()
+
+ # Should remove this as soon as the PATCH test case above works
+ it 'create a level session', (done) ->
+ unittest.getNormalJoe (joe) ->
+ session.creator = joe.get('_id').toHexString()
+ session = new LevelSession session
+ session.save (err) ->
+ expect(err).toBeNull()
+ done()
+
+ it 'GET /db/user//level.sessions gets a user\'s level sessions', (done) ->
+ unittest.getNormalJoe (joe) ->
+ request.get {uri: getURL "/db/user/#{joe.get '_id'}/level.sessions"}, (err, res, body) ->
+ expect(err).toBeNull()
+ expect(res.statusCode).toBe 200
+ sessions = JSON.parse body
+ expect(sessions.length).toBe 1
+ done()
+
+ it 'GET /db/user//level.sessions gets a user\'s level sessions', (done) ->
+ unittest.getNormalJoe (joe) ->
+ request.get {uri: getURL "/db/user/#{joe.get 'slug'}/level.sessions"}, (err, res, body) ->
+ expect(err).toBeNull()
+ expect(res.statusCode).toBe 200
+ sessions = JSON.parse body
+ expect(sessions.length).toBe 1
+ done()
+
+ it 'GET /db/user//level.sessions returns 404 if user not found', (done) ->
+ request.get {uri: getURL "/db/user/misterschtroumpf/level.sessions"}, (err, res) ->
+ expect(err).toBeNull()
+ expect(res.statusCode).toBe 404
+ done()
diff --git a/test/vendor/example.coffee b/test/vendor/example.coffee
deleted file mode 100644
index d08284dc1..000000000
--- a/test/vendor/example.coffee
+++ /dev/null
@@ -1 +0,0 @@
-massivelyUsefulTestExample = 'test...'