mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-05-02 17:03:42 -04:00
Merge branch 'clans'
This commit is contained in:
commit
a77e9cafe9
21 changed files with 1087 additions and 6 deletions
app
core
models
schemas/models
styles/clans
templates
views
server
test/server
|
@ -44,6 +44,10 @@ module.exports = class CocoRouter extends Backbone.Router
|
||||||
'beta': go('HomeView')
|
'beta': go('HomeView')
|
||||||
|
|
||||||
'cla': go('CLAView')
|
'cla': go('CLAView')
|
||||||
|
|
||||||
|
'clans': go('clans/ClansView')
|
||||||
|
'clans/:clanID': go('clans/ClanDetailsView')
|
||||||
|
|
||||||
'community': go('CommunityView')
|
'community': go('CommunityView')
|
||||||
|
|
||||||
'contribute': go('contribute/MainContributeView')
|
'contribute': go('contribute/MainContributeView')
|
||||||
|
|
7
app/models/Clan.coffee
Normal file
7
app/models/Clan.coffee
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
CocoModel = require './CocoModel'
|
||||||
|
schema = require 'schemas/models/clan.schema'
|
||||||
|
|
||||||
|
module.exports = class Clan extends CocoModel
|
||||||
|
@className: 'Clan'
|
||||||
|
@schema: schema
|
||||||
|
urlRoot: '/db/clan'
|
18
app/schemas/models/clan.schema.coffee
Normal file
18
app/schemas/models/clan.schema.coffee
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
c = require './../schemas'
|
||||||
|
|
||||||
|
ClanSchema = c.object {title: 'Clan', required: ['name', 'type']}
|
||||||
|
c.extendNamedProperties ClanSchema # name first
|
||||||
|
|
||||||
|
_.extend ClanSchema.properties,
|
||||||
|
description: {type: 'string'}
|
||||||
|
members: c.array {title: 'Members'}, c.objectId()
|
||||||
|
ownerID: c.objectId()
|
||||||
|
type: {type: 'string', 'enum': ['public']}
|
||||||
|
|
||||||
|
c.extendBasicProperties ClanSchema, 'Clan'
|
||||||
|
|
||||||
|
# Do we need these?
|
||||||
|
# c.extendSearchableProperties ClanSchema
|
||||||
|
# c.extendPermissionsProperties ClanSchema
|
||||||
|
|
||||||
|
module.exports = ClanSchema
|
|
@ -306,6 +306,8 @@ _.extend UserSchema.properties,
|
||||||
referrer: { type: 'string' }
|
referrer: { type: 'string' }
|
||||||
chinaVersion: { type: 'boolean' }
|
chinaVersion: { type: 'boolean' }
|
||||||
|
|
||||||
|
clans: c.array {}, c.objectId()
|
||||||
|
|
||||||
c.extendBasicProperties UserSchema, 'user'
|
c.extendBasicProperties UserSchema, 'user'
|
||||||
|
|
||||||
UserSchema.definitions =
|
UserSchema.definitions =
|
||||||
|
|
56
app/styles/clans/clan-details.sass
Normal file
56
app/styles/clans/clan-details.sass
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#clan-details-view
|
||||||
|
|
||||||
|
.join-clan-link
|
||||||
|
width: 390px
|
||||||
|
|
||||||
|
.join-link-prompt
|
||||||
|
font-weight: bold
|
||||||
|
|
||||||
|
.stats-table
|
||||||
|
width: 240px
|
||||||
|
background: rgba(0, 0, 0, 0.0)
|
||||||
|
|
||||||
|
$spriteSheetSize: 30px
|
||||||
|
|
||||||
|
td.hero-icon-cell
|
||||||
|
width: 30px
|
||||||
|
|
||||||
|
.player-hero-icon
|
||||||
|
background: transparent url(/images/pages/play/play-spritesheet.png)
|
||||||
|
background-size: cover
|
||||||
|
background-position: (-2 * $spriteSheetSize) 0
|
||||||
|
display: inline-block
|
||||||
|
width: 30px
|
||||||
|
height: 30px
|
||||||
|
margin: 0px 2px
|
||||||
|
|
||||||
|
.player-hero-icon
|
||||||
|
background-position: (-4 * $spriteSheetSize) 0
|
||||||
|
|
||||||
|
&.knight
|
||||||
|
background-position: (-5 * $spriteSheetSize) 0
|
||||||
|
&.librarian
|
||||||
|
background-position: (-6 * $spriteSheetSize) 0
|
||||||
|
&.ninja
|
||||||
|
background-position: (-7 * $spriteSheetSize) 0
|
||||||
|
&.potion-master
|
||||||
|
background-position: (-8 * $spriteSheetSize) 0
|
||||||
|
&.samurai
|
||||||
|
background-position: (-9 * $spriteSheetSize) 0
|
||||||
|
&.trapper
|
||||||
|
background-position: (-10 * $spriteSheetSize) 0
|
||||||
|
&.forest-archer
|
||||||
|
background-position: (-11 * $spriteSheetSize) 0
|
||||||
|
&.sorcerer
|
||||||
|
background-position: (-12 * $spriteSheetSize) 0
|
||||||
|
|
||||||
|
td.code-language-cell
|
||||||
|
width: 30px
|
||||||
|
|
||||||
|
span.code-language-cell
|
||||||
|
background: transparent url(/images/common/code_languages/javascript_small.png)
|
||||||
|
background-size: cover
|
||||||
|
display: inline-block
|
||||||
|
width: 30px
|
||||||
|
height: 30px
|
||||||
|
margin: 0px 2px
|
8
app/styles/clans/clans.sass
Normal file
8
app/styles/clans/clans.sass
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#clans-view
|
||||||
|
color: black
|
||||||
|
|
||||||
|
.clan-title
|
||||||
|
cursor: pointer
|
||||||
|
|
||||||
|
.create-clan-description
|
||||||
|
width: 50%
|
|
@ -8,11 +8,12 @@ block header
|
||||||
a(href="/")
|
a(href="/")
|
||||||
span.glyphicon.glyphicon-home
|
span.glyphicon.glyphicon-home
|
||||||
a(href="/about", data-i18n="nav.about")
|
a(href="/about", data-i18n="nav.about")
|
||||||
|
a(href='/clans') Clans
|
||||||
//a(href='/play/ladder', data-i18n="home.multiplayer").multiplayer-nav-link
|
//a(href='/play/ladder', data-i18n="home.multiplayer").multiplayer-nav-link
|
||||||
a(href='/community', data-i18n="nav.community")
|
a(href='/community', data-i18n="nav.community")
|
||||||
a(href='http://blog.codecombat.com/', data-i18n="nav.blog")
|
a(href='http://blog.codecombat.com/', data-i18n="nav.blog")
|
||||||
a(href='http://discourse.codecombat.com/', data-i18n="nav.forum")
|
a(href='http://discourse.codecombat.com/', data-i18n="nav.forum")
|
||||||
|
|
||||||
if me.get('anonymous') === false
|
if me.get('anonymous') === false
|
||||||
span.dropdown
|
span.dropdown
|
||||||
button.btn.btn-sm.header-font.dropdown-toggle(href="#", data-toggle="dropdown")
|
button.btn.btn-sm.header-font.dropdown-toggle(href="#", data-toggle="dropdown")
|
||||||
|
|
74
app/templates/clans/clan-details.jade
Normal file
74
app/templates/clans/clan-details.jade
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
extends /templates/base
|
||||||
|
|
||||||
|
block content
|
||||||
|
|
||||||
|
if clan
|
||||||
|
h1= clan.get('name')
|
||||||
|
if clan.get('description')
|
||||||
|
.clan-description
|
||||||
|
each line in clan.get('description').split('\n')
|
||||||
|
p= line
|
||||||
|
|
||||||
|
h5 Summary
|
||||||
|
table.table.table-condensed.stats-table
|
||||||
|
if owner
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
span.spr Chieftain
|
||||||
|
td
|
||||||
|
span.spr.player-hero-icon(data-memberid="#{clan.get('ownerID')}")
|
||||||
|
a(href="/user/#{clan.get('ownerID')}")= owner.get('name')
|
||||||
|
if stats.averageLevel
|
||||||
|
tr
|
||||||
|
td Average Level
|
||||||
|
td= stats.averageLevel
|
||||||
|
if stats.totalAchievements
|
||||||
|
tr
|
||||||
|
td Total Achievements
|
||||||
|
td= stats.totalAchievements
|
||||||
|
|
||||||
|
p
|
||||||
|
if isOwner
|
||||||
|
button.btn.btn-xs.btn-warning.delete-clan-btn Delete Clan
|
||||||
|
else if isMember
|
||||||
|
button.btn.btn-xs.btn-warning.leave-clan-btn Leave Clan
|
||||||
|
else
|
||||||
|
button.btn.btn-lg.btn-success.join-clan-btn Join Clan
|
||||||
|
|
||||||
|
div
|
||||||
|
span.spl.spr.join-link-prompt Invite:
|
||||||
|
input.join-clan-link(type="text", readonly, value="#{joinClanLink}")
|
||||||
|
.small *Invite players to this Clan by sending them this link.
|
||||||
|
|
||||||
|
if members
|
||||||
|
h3 Heroes (#{members.length})
|
||||||
|
table.table.table-striped.table-condensed
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
th
|
||||||
|
th
|
||||||
|
td Name
|
||||||
|
th Level
|
||||||
|
th Achievements
|
||||||
|
th Latest Achievement
|
||||||
|
th
|
||||||
|
tbody
|
||||||
|
each member in members
|
||||||
|
tr
|
||||||
|
td.hero-icon-cell
|
||||||
|
span.spr.player-hero-icon(data-memberid="#{member.id}")
|
||||||
|
td.code-language-cell
|
||||||
|
if memberLanguageMap && memberLanguageMap[member.id]
|
||||||
|
span.code-language-cell(style="background-image: url(/images/common/code_languages/#{memberLanguageMap[member.id]}_small.png)", title=memberLanguageMap[member.id])
|
||||||
|
td
|
||||||
|
a(href="/user/#{member.id}")= member.get('name') || 'Anoner'
|
||||||
|
td= member.level()
|
||||||
|
td
|
||||||
|
if memberAchievementsMap && memberAchievementsMap[member.id]
|
||||||
|
| #{memberAchievementsMap[member.id].length}
|
||||||
|
td
|
||||||
|
if memberAchievementsMap && memberAchievementsMap[member.id] && memberAchievementsMap[member.id].length
|
||||||
|
span= memberAchievementsMap[member.id][0].get('achievementName')
|
||||||
|
td
|
||||||
|
if isOwner && member.id !== clan.get('ownerID')
|
||||||
|
button.btn.btn-xs.btn-warning.remove-member-btn(data-id="#{member.id}") Remove Hero
|
74
app/templates/clans/clans.jade
Normal file
74
app/templates/clans/clans.jade
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
extends /templates/base
|
||||||
|
|
||||||
|
block content
|
||||||
|
|
||||||
|
p
|
||||||
|
input.create-clan-name(type='text' placeholder='New clan name')
|
||||||
|
p
|
||||||
|
textarea.create-clan-description(rows=2, placeholder='New clan description')
|
||||||
|
p
|
||||||
|
button.btn.btn-success.create-clan-btn Create New Clan
|
||||||
|
|
||||||
|
div(role='tabpanel')
|
||||||
|
ul.nav.nav-tabs(role='tablist')
|
||||||
|
li.active(role='presentation')
|
||||||
|
a(href='#public-clans' aria-controls='public-clans', role='tab', data-toggle='tab') Public Clans
|
||||||
|
li(role='presentation')
|
||||||
|
a(href='#my-clans' aria-controls='my-clans', role='tab', data-toggle='tab') My Clans
|
||||||
|
|
||||||
|
.tab-content
|
||||||
|
.tab-pane.active#public-clans(role='tabpanel')
|
||||||
|
table.table.table-striped.table-condensed
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
th Clan Name
|
||||||
|
th Heroes
|
||||||
|
th Chieftain
|
||||||
|
th
|
||||||
|
tbody
|
||||||
|
if publicClans.length
|
||||||
|
each clan in publicClans
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
if clan.get('ownerID') === me.id
|
||||||
|
a(href="/clans/#{clan.id}", style='font-weight:bold')= clan.get('name')
|
||||||
|
else
|
||||||
|
a(href="/clans/#{clan.id}")= clan.get('name')
|
||||||
|
td= clan.get('members').length
|
||||||
|
td
|
||||||
|
if idNameMap && idNameMap[clan.get('ownerID')]
|
||||||
|
a(href="/user/#{clan.get('ownerID')}")= idNameMap[clan.get('ownerID')]
|
||||||
|
else
|
||||||
|
a(href="/user/#{clan.get('ownerID')}") Anoner
|
||||||
|
td
|
||||||
|
if myClanIDs.indexOf(clan.id) < 0
|
||||||
|
button.btn.btn-success.join-clan-btn(data-id="#{clan.id}") Join Clan
|
||||||
|
else if clan.get('ownerID') !== me.id
|
||||||
|
button.btn.btn-xs.btn-warning.leave-clan-btn(data-id="#{clan.id}") Leave Clan
|
||||||
|
|
||||||
|
.tab-pane#my-clans(role='tabpanel')
|
||||||
|
table.table.table-striped.table-condensed
|
||||||
|
thead
|
||||||
|
tr
|
||||||
|
th Clan Name
|
||||||
|
th Heroes
|
||||||
|
th Chieftain
|
||||||
|
th
|
||||||
|
tbody
|
||||||
|
if myClans.length
|
||||||
|
each clan in myClans
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
if clan.get('ownerID') === me.id
|
||||||
|
a(href="/clans/#{clan.id}", style='font-weight:bold')= clan.get('name')
|
||||||
|
else
|
||||||
|
a(href="/clans/#{clan.id}")= clan.get('name')
|
||||||
|
td= clan.get('members').length
|
||||||
|
td
|
||||||
|
if idNameMap && idNameMap[clan.get('ownerID')]
|
||||||
|
a(href="/user/#{clan.get('ownerID')}")= idNameMap[clan.get('ownerID')]
|
||||||
|
else
|
||||||
|
a(href="/user/#{clan.get('ownerID')}") Anoner
|
||||||
|
td
|
||||||
|
if clan.get('ownerID') !== me.id
|
||||||
|
button.btn.btn-xs.btn-warning.leave-clan-btn(data-id="#{clan.id}") Leave Clan
|
|
@ -112,6 +112,8 @@ else
|
||||||
button#logout-button.btn.btn-illustrated.btn-warning(data-i18n="login.log_out") Log Out
|
button#logout-button.btn.btn-illustrated.btn-warning(data-i18n="login.log_out") Log Out
|
||||||
if me.isPremium()
|
if me.isPremium()
|
||||||
button.btn.btn-illustrated.btn-primary(data-i18n="nav.contact", data-toggle="coco-modal", data-target="core/ContactModal") Contact
|
button.btn.btn-illustrated.btn-primary(data-i18n="nav.contact", data-toggle="coco-modal", data-target="core/ContactModal") Contact
|
||||||
|
span.spl
|
||||||
|
a.btn.btn-illustrated(href='/clans') Clans
|
||||||
|
|
||||||
|
|
||||||
button.btn.btn-lg.btn-inverse.campaign-control-button#volume-button(data-i18n="[title]play.adjust_volume", title="Adjust volume")
|
button.btn.btn-lg.btn-inverse.campaign-control-button#volume-button(data-i18n="[title]play.adjust_volume", title="Adjust volume")
|
||||||
|
|
|
@ -56,6 +56,33 @@ block append content
|
||||||
a(href="/contribute#scribe" data-i18n="classes.scribe_title") Scribe
|
a(href="/contribute#scribe" data-i18n="classes.scribe_title") Scribe
|
||||||
|
|
||||||
.right-column
|
.right-column
|
||||||
|
|
||||||
|
.panel.panel-default
|
||||||
|
.panel-heading
|
||||||
|
h3.panel-title Clans
|
||||||
|
if (!clans)
|
||||||
|
.panel-body
|
||||||
|
p(data-i18n="common.loading")
|
||||||
|
else if (clans.length)
|
||||||
|
table.table
|
||||||
|
tr
|
||||||
|
th.col-xs-4 Name
|
||||||
|
th.col-xs-4 Chieftain
|
||||||
|
th.col-xs-4 Heroes
|
||||||
|
each clan in clans
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
a(href="/clans/#{clan.id}")= clan.get('name')
|
||||||
|
td
|
||||||
|
if idNameMap && idNameMap[clan.get('ownerID')]
|
||||||
|
a(href="/user/#{clan.get('ownerID')}")= idNameMap[clan.get('ownerID')]
|
||||||
|
else
|
||||||
|
a(href="/user/#{clan.get('ownerID')}") Anoner
|
||||||
|
td= clan.get('members').length
|
||||||
|
else
|
||||||
|
.panel-body
|
||||||
|
p Not a member of any clans yet.
|
||||||
|
|
||||||
.panel.panel-default
|
.panel.panel-default
|
||||||
.panel-heading
|
.panel-heading
|
||||||
h3.panel-title(data-i18n="user.singleplayer_title") Singleplayer Levels
|
h3.panel-title(data-i18n="user.singleplayer_title") Singleplayer Levels
|
||||||
|
@ -141,4 +168,3 @@ block append content
|
||||||
if earnedAchievements.length > 4
|
if earnedAchievements.length > 4
|
||||||
.panel-footer
|
.panel-footer
|
||||||
button.btn.btn-info.btn-sm.more-button(data-i18n="editor.more")
|
button.btn.btn-info.btn-sm.more-button(data-i18n="editor.more")
|
||||||
|
|
||||||
|
|
165
app/views/clans/ClanDetailsView.coffee
Normal file
165
app/views/clans/ClanDetailsView.coffee
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
app = require 'core/application'
|
||||||
|
AuthModal = require 'views/core/AuthModal'
|
||||||
|
RootView = require 'views/core/RootView'
|
||||||
|
template = require 'templates/clans/clan-details'
|
||||||
|
CocoCollection = require 'collections/CocoCollection'
|
||||||
|
Clan = require 'models/Clan'
|
||||||
|
EarnedAchievement = require 'models/EarnedAchievement'
|
||||||
|
LevelSession = require 'models/LevelSession'
|
||||||
|
ThangType = require 'models/ThangType'
|
||||||
|
User = require 'models/User'
|
||||||
|
|
||||||
|
# TODO: Message for clan not found
|
||||||
|
# TODO: join/leave mostly duped from clans view
|
||||||
|
|
||||||
|
module.exports = class ClanDetailsView extends RootView
|
||||||
|
id: 'clan-details-view'
|
||||||
|
template: template
|
||||||
|
|
||||||
|
events:
|
||||||
|
'click .delete-clan-btn': 'onDeleteClan'
|
||||||
|
'click .join-clan-btn': 'onJoinClan'
|
||||||
|
'click .leave-clan-btn': 'onLeaveClan'
|
||||||
|
'click .remove-member-btn': 'onRemoveMember'
|
||||||
|
|
||||||
|
constructor: (options, @clanID) ->
|
||||||
|
super options
|
||||||
|
@initData()
|
||||||
|
|
||||||
|
destroy: ->
|
||||||
|
@stopListening?()
|
||||||
|
|
||||||
|
initData: ->
|
||||||
|
@stats = {}
|
||||||
|
|
||||||
|
@clan = new Clan _id: @clanID
|
||||||
|
@members = new CocoCollection([], { url: "/db/clan/#{@clanID}/members", model: User, comparator:'slug' })
|
||||||
|
@memberAchievements = new CocoCollection([], { url: "/db/clan/#{@clanID}/member_achievements", model: EarnedAchievement, comparator:'_id' })
|
||||||
|
@memberSessions = new CocoCollection([], { url: "/db/clan/#{@clanID}/member_sessions", model: LevelSession, comparator:'_id' })
|
||||||
|
|
||||||
|
@listenTo me, 'sync', => @render?()
|
||||||
|
@listenTo @clan, 'sync', @onClanSync
|
||||||
|
@listenTo @members, 'sync', @onMembersSync
|
||||||
|
@listenTo @memberAchievements, 'sync', @onMemberAchievementsSync
|
||||||
|
@listenTo @memberSessions, 'sync', @onMemberSessionsSync
|
||||||
|
|
||||||
|
@supermodel.loadModel @clan, 'clan', cache: false
|
||||||
|
@supermodel.loadCollection(@members, 'members', {cache: false})
|
||||||
|
@supermodel.loadCollection(@memberAchievements, 'member_achievements', {cache: false})
|
||||||
|
@supermodel.loadCollection(@memberSessions, 'member_sessions', {cache: false})
|
||||||
|
|
||||||
|
getRenderData: ->
|
||||||
|
context = super()
|
||||||
|
context.clan = @clan
|
||||||
|
if application.isProduction()
|
||||||
|
context.joinClanLink = "https://codecombat.com/clans/#{@clanID}"
|
||||||
|
else
|
||||||
|
context.joinClanLink = "http://localhost:3000/clans/#{@clanID}"
|
||||||
|
context.owner = @owner
|
||||||
|
context.memberAchievementsMap = @memberAchievementsMap
|
||||||
|
context.memberLanguageMap = @memberLanguageMap
|
||||||
|
context.members = @members?.models
|
||||||
|
context.isOwner = @clan.get('ownerID') is me.id
|
||||||
|
context.isMember = @clanID in (me.get('clans') ? [])
|
||||||
|
context.stats = @stats
|
||||||
|
context
|
||||||
|
|
||||||
|
afterRender: ->
|
||||||
|
super()
|
||||||
|
@updateHeroIcons()
|
||||||
|
|
||||||
|
refreshData: ->
|
||||||
|
me.fetch cache: false
|
||||||
|
@members.fetch cache: false
|
||||||
|
@memberAchievements.fetch cache: false
|
||||||
|
|
||||||
|
updateHeroIcons: ->
|
||||||
|
return unless @members?.models?
|
||||||
|
for member in @members.models
|
||||||
|
continue unless hero = member.get('heroConfig')?.thangType
|
||||||
|
for slug, original of ThangType.heroes when original is hero
|
||||||
|
@$el.find(".player-hero-icon[data-memberID=#{member.id}]").removeClass('.player-hero-icon').addClass('player-hero-icon ' + slug)
|
||||||
|
|
||||||
|
onClanSync: ->
|
||||||
|
unless @owner?
|
||||||
|
@owner = new User _id: @clan.get('ownerID')
|
||||||
|
@listenTo @owner, 'sync', => @render?()
|
||||||
|
@supermodel.loadModel @owner, 'owner', cache: false
|
||||||
|
@render?()
|
||||||
|
|
||||||
|
onMembersSync: ->
|
||||||
|
@stats.averageLevel = Math.round(@members.reduce(((sum, member) -> sum + member.level()), 0) / @members.length)
|
||||||
|
@render?()
|
||||||
|
|
||||||
|
onMemberAchievementsSync: ->
|
||||||
|
@stats.totalAchievements = @memberAchievements.models.length
|
||||||
|
@memberAchievementsMap = {}
|
||||||
|
for achievement in @memberAchievements.models
|
||||||
|
user = achievement.get('user')
|
||||||
|
@memberAchievementsMap[user] ?= []
|
||||||
|
@memberAchievementsMap[user].push achievement
|
||||||
|
for user of @memberAchievementsMap
|
||||||
|
@memberAchievementsMap[user].sort (a, b) -> b.id.localeCompare(a.id)
|
||||||
|
@render?()
|
||||||
|
|
||||||
|
onMemberSessionsSync: ->
|
||||||
|
@memberSessionMap = {}
|
||||||
|
for levelSession in @memberSessions.models
|
||||||
|
user = levelSession.get('creator')
|
||||||
|
@memberSessionMap[user] ?= []
|
||||||
|
@memberSessionMap[user].push levelSession
|
||||||
|
@memberLanguageMap = {}
|
||||||
|
for user of @memberSessionMap
|
||||||
|
languageCounts = {}
|
||||||
|
for levelSession in @memberSessionMap[user]
|
||||||
|
language = levelSession.get('codeLanguage') or levelSession.get('submittedCodeLanguage')
|
||||||
|
languageCounts[language] = (languageCounts[language] or 0) + 1 if language
|
||||||
|
mostUsedCount = 0
|
||||||
|
for language, count of languageCounts
|
||||||
|
if count > mostUsedCount
|
||||||
|
mostUsedCount = count
|
||||||
|
@memberLanguageMap[user] = language
|
||||||
|
@render?()
|
||||||
|
|
||||||
|
onDeleteClan: (e) ->
|
||||||
|
return @openModalView(new AuthModal()) if me.isAnonymous()
|
||||||
|
options =
|
||||||
|
url: "/db/clan/#{@clanID}"
|
||||||
|
method: 'DELETE'
|
||||||
|
error: (model, response, options) =>
|
||||||
|
console.error 'Error joining clan', response
|
||||||
|
success: (model, response, options) =>
|
||||||
|
app.router.navigate "/clans"
|
||||||
|
window.location.reload()
|
||||||
|
@supermodel.addRequestResource( 'delete_clan', options).load()
|
||||||
|
|
||||||
|
onJoinClan: (e) ->
|
||||||
|
return @openModalView(new AuthModal()) if me.isAnonymous()
|
||||||
|
options =
|
||||||
|
url: "/db/clan/#{@clanID}/join"
|
||||||
|
method: 'PUT'
|
||||||
|
error: (model, response, options) =>
|
||||||
|
console.error 'Error joining clan', response
|
||||||
|
success: (model, response, options) => @refreshData()
|
||||||
|
@supermodel.addRequestResource( 'join_clan', options).load()
|
||||||
|
|
||||||
|
onLeaveClan: (e) ->
|
||||||
|
options =
|
||||||
|
url: "/db/clan/#{@clanID}/leave"
|
||||||
|
method: 'PUT'
|
||||||
|
error: (model, response, options) =>
|
||||||
|
console.error 'Error leaving clan', response
|
||||||
|
success: (model, response, options) => @refreshData()
|
||||||
|
@supermodel.addRequestResource( 'leave_clan', options).load()
|
||||||
|
|
||||||
|
onRemoveMember: (e) ->
|
||||||
|
if memberID = $(e.target).data('id')
|
||||||
|
options =
|
||||||
|
url: "/db/clan/#{@clanID}/remove/#{memberID}"
|
||||||
|
method: 'PUT'
|
||||||
|
error: (model, response, options) =>
|
||||||
|
console.error 'Error removing clan member', response
|
||||||
|
success: (model, response, options) => @refreshData()
|
||||||
|
@supermodel.addRequestResource( 'remove_member', options).load()
|
||||||
|
else
|
||||||
|
console.error "No member ID attached to remove button."
|
110
app/views/clans/ClansView.coffee
Normal file
110
app/views/clans/ClansView.coffee
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
app = require 'core/application'
|
||||||
|
AuthModal = require 'views/core/AuthModal'
|
||||||
|
RootView = require 'views/core/RootView'
|
||||||
|
template = require 'templates/clans/clans'
|
||||||
|
CocoCollection = require 'collections/CocoCollection'
|
||||||
|
Clan = require 'models/Clan'
|
||||||
|
|
||||||
|
# TODO: Waiting for async messages
|
||||||
|
# TODO: Invalid clan name message
|
||||||
|
# TODO: Refresh data instead of page
|
||||||
|
|
||||||
|
module.exports = class MainAdminView extends RootView
|
||||||
|
id: 'clans-view'
|
||||||
|
template: template
|
||||||
|
|
||||||
|
events:
|
||||||
|
'click .create-clan-btn': 'onClickCreateClan'
|
||||||
|
'click .join-clan-btn': 'onJoinClan'
|
||||||
|
'click .leave-clan-btn': 'onLeaveClan'
|
||||||
|
|
||||||
|
constructor: (options) ->
|
||||||
|
super options
|
||||||
|
@initData()
|
||||||
|
|
||||||
|
destroy: ->
|
||||||
|
@stopListening?()
|
||||||
|
|
||||||
|
getRenderData: ->
|
||||||
|
context = super()
|
||||||
|
context.idNameMap = @idNameMap
|
||||||
|
context.publicClans = @publicClans.models
|
||||||
|
context.myClans = @myClans.models
|
||||||
|
context.myClanIDs = me.get('clans') ? []
|
||||||
|
context
|
||||||
|
|
||||||
|
initData: ->
|
||||||
|
@idNameMap = {}
|
||||||
|
|
||||||
|
sortClanList = (a, b) ->
|
||||||
|
if a.get('members').length isnt b.get('members').length
|
||||||
|
if a.get('members').length < b.get('members').length then 1 else -1
|
||||||
|
else
|
||||||
|
b.id.localeCompare(a.id)
|
||||||
|
@publicClans = new CocoCollection([], { url: '/db/clan/-/public', model: Clan, comparator: sortClanList })
|
||||||
|
@listenTo @publicClans, 'sync', =>
|
||||||
|
@refreshNames @publicClans.models
|
||||||
|
@render?()
|
||||||
|
@supermodel.loadCollection(@publicClans, 'public_clans', {cache: false})
|
||||||
|
@myClans = new CocoCollection([], { url: "/db/user/#{me.id}/clans", model: Clan, comparator: sortClanList })
|
||||||
|
@listenTo @myClans, 'sync', =>
|
||||||
|
@refreshNames @myClans.models
|
||||||
|
@render?()
|
||||||
|
@supermodel.loadCollection(@myClans, 'my_clans', {cache: false})
|
||||||
|
@listenTo me, 'sync', => @render?()
|
||||||
|
|
||||||
|
refreshNames: (clans) ->
|
||||||
|
options =
|
||||||
|
url: '/db/user/-/names'
|
||||||
|
method: 'POST'
|
||||||
|
data: {ids: _.map(clans, (clan) -> clan.get('ownerID'))}
|
||||||
|
success: (models, response, options) =>
|
||||||
|
@idNameMap[userID] = models[userID].name for userID of models
|
||||||
|
@render?()
|
||||||
|
@supermodel.addRequestResource('user_names', options, 0).load()
|
||||||
|
|
||||||
|
onClickCreateClan: (e) ->
|
||||||
|
return @openModalView(new AuthModal()) if me.isAnonymous()
|
||||||
|
if name = $('.create-clan-name').val()
|
||||||
|
clan = new Clan()
|
||||||
|
clan.set 'type', 'public'
|
||||||
|
clan.set 'name', name
|
||||||
|
clan.set 'description', description if description = $('.create-clan-description').val()
|
||||||
|
clan.save {},
|
||||||
|
error: (model, response, options) =>
|
||||||
|
console.error 'Error saving clan', response.status
|
||||||
|
success: (model, response, options) =>
|
||||||
|
app.router.navigate "/clans/#{model.id}"
|
||||||
|
window.location.reload()
|
||||||
|
else
|
||||||
|
console.log 'Invalid name'
|
||||||
|
|
||||||
|
onJoinClan: (e) ->
|
||||||
|
return @openModalView(new AuthModal()) if me.isAnonymous()
|
||||||
|
if clanID = $(e.target).data('id')
|
||||||
|
options =
|
||||||
|
url: "/db/clan/#{clanID}/join"
|
||||||
|
method: 'PUT'
|
||||||
|
error: (model, response, options) =>
|
||||||
|
console.error 'Error joining clan', response
|
||||||
|
success: (model, response, options) =>
|
||||||
|
app.router.navigate "/clans/#{clanID}"
|
||||||
|
window.location.reload()
|
||||||
|
@supermodel.addRequestResource( 'join_clan', options).load()
|
||||||
|
else
|
||||||
|
console.error "No clan ID attached to join button."
|
||||||
|
|
||||||
|
onLeaveClan: (e) ->
|
||||||
|
if clanID = $(e.target).data('id')
|
||||||
|
options =
|
||||||
|
url: "/db/clan/#{clanID}/leave"
|
||||||
|
method: 'PUT'
|
||||||
|
error: (model, response, options) =>
|
||||||
|
console.error 'Error leaving clan', response
|
||||||
|
success: (model, response, options) =>
|
||||||
|
me.fetch cache: false
|
||||||
|
@publicClans.fetch cache: false
|
||||||
|
@myClans.fetch cache: false
|
||||||
|
@supermodel.addRequestResource( 'leave_clan', options).load()
|
||||||
|
else
|
||||||
|
console.error "No clan ID attached to leave button."
|
|
@ -3,6 +3,7 @@ CocoCollection = require 'collections/CocoCollection'
|
||||||
LevelSession = require 'models/LevelSession'
|
LevelSession = require 'models/LevelSession'
|
||||||
template = require 'templates/user/main-user-view'
|
template = require 'templates/user/main-user-view'
|
||||||
{me} = require 'core/auth'
|
{me} = require 'core/auth'
|
||||||
|
Clan = require 'models/Clan'
|
||||||
EarnedAchievementCollection = require 'collections/EarnedAchievementCollection'
|
EarnedAchievementCollection = require 'collections/EarnedAchievementCollection'
|
||||||
|
|
||||||
class LevelSessionsCollection extends CocoCollection
|
class LevelSessionsCollection extends CocoCollection
|
||||||
|
@ -22,6 +23,9 @@ module.exports = class MainUserView extends UserView
|
||||||
constructor: (userID, options) ->
|
constructor: (userID, options) ->
|
||||||
super options
|
super options
|
||||||
|
|
||||||
|
destroy: ->
|
||||||
|
@stopListening?()
|
||||||
|
|
||||||
getRenderData: ->
|
getRenderData: ->
|
||||||
context = super()
|
context = super()
|
||||||
if @levelSessions and @levelSessions.loaded
|
if @levelSessions and @levelSessions.loaded
|
||||||
|
@ -47,6 +51,9 @@ module.exports = class MainUserView extends UserView
|
||||||
context.favoriteLanguage = favoriteLanguage
|
context.favoriteLanguage = favoriteLanguage
|
||||||
if @earnedAchievements and @earnedAchievements.loaded
|
if @earnedAchievements and @earnedAchievements.loaded
|
||||||
context.earnedAchievements = @earnedAchievements
|
context.earnedAchievements = @earnedAchievements
|
||||||
|
if @clans and @clans.loaded
|
||||||
|
context.clans = @clans.models
|
||||||
|
context.idNameMap = @idNameMap
|
||||||
context
|
context
|
||||||
|
|
||||||
onLoaded: ->
|
onLoaded: ->
|
||||||
|
@ -56,8 +63,30 @@ module.exports = class MainUserView extends UserView
|
||||||
@earnedAchievements = new EarnedAchievementCollection @user.getSlugOrID()
|
@earnedAchievements = new EarnedAchievementCollection @user.getSlugOrID()
|
||||||
@supermodel.loadCollection @levelSessions, 'levelSessions', {cache: false}
|
@supermodel.loadCollection @levelSessions, 'levelSessions', {cache: false}
|
||||||
@supermodel.loadCollection @earnedAchievements, 'earnedAchievements', {cache: false}
|
@supermodel.loadCollection @earnedAchievements, 'earnedAchievements', {cache: false}
|
||||||
|
sortClanList = (a, b) ->
|
||||||
|
if a.get('members').length isnt b.get('members').length
|
||||||
|
if a.get('members').length < b.get('members').length then 1 else -1
|
||||||
|
else
|
||||||
|
b.id.localeCompare(a.id)
|
||||||
|
@idNameMap = {}
|
||||||
|
@clans = new CocoCollection([], { url: "/db/user/#{@userID}/clans", model: Clan, comparator: sortClanList })
|
||||||
|
@listenTo @clans, 'sync', =>
|
||||||
|
@refreshNameMap @clans?.models
|
||||||
|
@render?()
|
||||||
|
@supermodel.loadCollection(@clans, 'clans', {cache: false})
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
refreshNameMap: (clans) ->
|
||||||
|
return unless clans?
|
||||||
|
options =
|
||||||
|
url: '/db/user/-/names'
|
||||||
|
method: 'POST'
|
||||||
|
data: {ids: _.map(clans, (clan) -> clan.get('ownerID'))}
|
||||||
|
success: (models, response, options) =>
|
||||||
|
@idNameMap[userID] = models[userID].name for userID of models
|
||||||
|
@render?()
|
||||||
|
@supermodel.addRequestResource('user_names', options, 0).load()
|
||||||
|
|
||||||
onClickMoreButton: (e) ->
|
onClickMoreButton: (e) ->
|
||||||
panel = $(e.target).closest('.panel')
|
panel = $(e.target).closest('.panel')
|
||||||
panel.find('tr.hide').removeClass('hide')
|
panel.find('tr.hide').removeClass('hide')
|
||||||
|
|
32
server/clans/Clan.coffee
Normal file
32
server/clans/Clan.coffee
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
mongoose = require 'mongoose'
|
||||||
|
log = require 'winston'
|
||||||
|
config = require '../../server_config'
|
||||||
|
plugins = require '../plugins/plugins'
|
||||||
|
User = require '../users/User'
|
||||||
|
jsonSchema = require '../../app/schemas/models/clan.schema'
|
||||||
|
|
||||||
|
ClanSchema = new mongoose.Schema {}, {strict: false, minimize: false, read:config.mongo.readpref}
|
||||||
|
|
||||||
|
ClanSchema.pre 'save', (next) ->
|
||||||
|
User.update {_id: @get('ownerID')}, {$addToSet: {clans: @get('_id')}}, (err) =>
|
||||||
|
if err
|
||||||
|
log.error err
|
||||||
|
return next(err)
|
||||||
|
next()
|
||||||
|
|
||||||
|
ClanSchema.statics.privateProperties = []
|
||||||
|
ClanSchema.statics.editableProperties = [
|
||||||
|
'description'
|
||||||
|
'members'
|
||||||
|
'name'
|
||||||
|
'type'
|
||||||
|
]
|
||||||
|
|
||||||
|
ClanSchema.plugin plugins.NamedPlugin
|
||||||
|
|
||||||
|
# TODO: Do we need this?
|
||||||
|
# ClanSchema.plugin plugins.SearchablePlugin, {searchable: ['name']}
|
||||||
|
|
||||||
|
ClanSchema.statics.jsonSchema = jsonSchema
|
||||||
|
|
||||||
|
module.exports = Clan = mongoose.model 'clan', ClanSchema, 'clans'
|
148
server/clans/clan_handler.coffee
Normal file
148
server/clans/clan_handler.coffee
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
async = require 'async'
|
||||||
|
mongoose = require 'mongoose'
|
||||||
|
Handler = require '../commons/Handler'
|
||||||
|
Clan = require './Clan'
|
||||||
|
EarnedAchievement = require '../achievements/EarnedAchievement'
|
||||||
|
EarnedAchievementHandler = require '../achievements/earned_achievement_handler'
|
||||||
|
LevelSession = require '../levels/sessions/LevelSession'
|
||||||
|
LevelSessionHandler = require '../levels/sessions/level_session_handler'
|
||||||
|
User = require '../users/User'
|
||||||
|
UserHandler = require '../users/user_handler'
|
||||||
|
|
||||||
|
ClanHandler = class ClanHandler extends Handler
|
||||||
|
modelClass: Clan
|
||||||
|
jsonSchema: require '../../app/schemas/models/Clan.schema'
|
||||||
|
allowedMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']
|
||||||
|
|
||||||
|
hasAccess: (req) ->
|
||||||
|
return true if req.method in ['GET']
|
||||||
|
return true if req.user? and not req.user.isAnonymous()
|
||||||
|
false
|
||||||
|
|
||||||
|
hasAccessToDocument: (req, document, method=null) ->
|
||||||
|
return false unless document?
|
||||||
|
method = (method or req.method).toLowerCase()
|
||||||
|
return true if req.user?.isAdmin()
|
||||||
|
return true if method is 'get'
|
||||||
|
return true if document.get('ownerID')?.equals req.user._id
|
||||||
|
false
|
||||||
|
|
||||||
|
makeNewInstance: (req) ->
|
||||||
|
userName = req.user.get('name') ? 'Anoner'
|
||||||
|
instance = super(req)
|
||||||
|
instance.set 'ownerID', req.user._id
|
||||||
|
instance.set 'members', [req.user._id]
|
||||||
|
instance
|
||||||
|
|
||||||
|
delete: (req, res, clanID) ->
|
||||||
|
@getDocumentForIdOrSlug clanID, (err, clan) =>
|
||||||
|
return @sendDatabaseError res, err if err
|
||||||
|
return @sendNotFoundError res unless clan
|
||||||
|
return @sendForbiddenError res unless @hasAccessToDocument(req, clan)
|
||||||
|
memberIDs = clan.get('members')
|
||||||
|
Clan.remove {_id: clan.get('_id')}, (err) =>
|
||||||
|
return @sendDatabaseError res, err if err
|
||||||
|
User.update {_id: {$in: memberIDs}}, {$pull: {clans: clan.get('_id')}}, {multi: true}, (err) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
@sendNoContent(res)
|
||||||
|
|
||||||
|
getByRelationship: (req, res, args...) ->
|
||||||
|
return @joinClan(req, res, args[0]) if args[1] is 'join'
|
||||||
|
return @leaveClan(req, res, args[0]) if args[1] is 'leave'
|
||||||
|
return @getMemberAchievements(req, res, args[0]) if args[1] is 'member_achievements'
|
||||||
|
return @getMembers(req, res, args[0]) if args[1] is 'members'
|
||||||
|
return @getMemberSessions(req, res, args[0]) if args[1] is 'member_sessions'
|
||||||
|
return @getPublicClans(req, res) if args[1] is 'public'
|
||||||
|
return @removeMember(req, res, args[0], args[2]) if args.length is 3 and args[1] is 'remove'
|
||||||
|
super(arguments...)
|
||||||
|
|
||||||
|
joinClan: (req, res, clanID) ->
|
||||||
|
return @sendForbiddenError(res) unless req.user? and not req.user.isAnonymous()
|
||||||
|
try
|
||||||
|
clanID = mongoose.Types.ObjectId(clanID)
|
||||||
|
catch err
|
||||||
|
return @sendNotFoundError(res, err)
|
||||||
|
Clan.update {_id: clanID}, {$addToSet: {members: req.user._id}}, (err) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
User.update {_id: req.user._id}, {$addToSet: {clans: clanID}}, (err) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
@sendSuccess(res)
|
||||||
|
|
||||||
|
leaveClan: (req, res, clanID) ->
|
||||||
|
return @sendForbiddenError(res) unless req.user? and not req.user.isAnonymous()
|
||||||
|
try
|
||||||
|
clanID = mongoose.Types.ObjectId(clanID)
|
||||||
|
catch err
|
||||||
|
return @sendNotFoundError(res, err)
|
||||||
|
Clan.findById clanID, (err, clan) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
return @sendDatabaseError(res, err) unless clan
|
||||||
|
return @sendForbiddenError(res) if clan.get('ownerID')?.equals req.user._id
|
||||||
|
Clan.update {_id: clanID}, {$pull: {members: req.user._id}}, (err) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
User.update {_id: req.user._id}, {$pull: {clans: clanID}}, (err) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
@sendSuccess(res)
|
||||||
|
|
||||||
|
getMemberAchievements: (req, res, clanID) ->
|
||||||
|
# TODO: add tests
|
||||||
|
Clan.findById clanID, (err, clan) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
return @sendDatabaseError(res, err) unless clan
|
||||||
|
memberIDs = _.map clan.get('members') ? [], (memberID) -> memberID.toHexString()
|
||||||
|
EarnedAchievement.find {user: {$in: memberIDs}}, (err, documents) =>
|
||||||
|
return @sendDatabaseError(res, err) if err?
|
||||||
|
cleandocs = (EarnedAchievementHandler.formatEntity(req, doc) for doc in documents)
|
||||||
|
@sendSuccess(res, cleandocs)
|
||||||
|
|
||||||
|
getMembers: (req, res, clanID) ->
|
||||||
|
# TODO: add tests
|
||||||
|
Clan.findById clanID, (err, clan) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
return @sendDatabaseError(res, err) unless clan
|
||||||
|
memberIDs = clan.get('members') ? []
|
||||||
|
User.find {_id: {$in: memberIDs}}, (err, users) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
cleandocs = (UserHandler.formatEntity(req, doc) for doc in users)
|
||||||
|
@sendSuccess(res, cleandocs)
|
||||||
|
|
||||||
|
getMemberSessions: (req, res, clanID) ->
|
||||||
|
# TODO: add tests
|
||||||
|
Clan.findById clanID, (err, clan) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
return @sendDatabaseError(res, err) unless clan
|
||||||
|
memberIDs = _.map clan.get('members') ? [], (memberID) -> memberID.toHexString()
|
||||||
|
LevelSession.find {creator: {$in: memberIDs}}, (err, documents) =>
|
||||||
|
return @sendDatabaseError(res, err) if err?
|
||||||
|
cleandocs = (LevelSessionHandler.formatEntity(req, doc) for doc in documents)
|
||||||
|
@sendSuccess(res, cleandocs)
|
||||||
|
|
||||||
|
getPublicClans: (req, res) ->
|
||||||
|
# Return 100 public clans, sorted by member count, created date
|
||||||
|
query = [{ $match : {type : 'public'} }]
|
||||||
|
query.push {$project : {_id: 1, name: 1, slug: 1, type: 1, description: 1, members: 1, memberCount: {$size: "$members"}, ownerID: 1}}
|
||||||
|
query.push {$sort: { memberCount: -1, _id: -1 }}
|
||||||
|
query.push {$limit: 100}
|
||||||
|
Clan.aggregate(query).exec (err, documents) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
@sendSuccess(res, documents)
|
||||||
|
|
||||||
|
removeMember: (req, res, clanID, memberID) ->
|
||||||
|
return @sendForbiddenError(res) unless req.user? and not req.user.isAnonymous()
|
||||||
|
try
|
||||||
|
clanID = mongoose.Types.ObjectId(clanID)
|
||||||
|
memberID = mongoose.Types.ObjectId(memberID)
|
||||||
|
catch err
|
||||||
|
return @sendNotFoundError(res, err)
|
||||||
|
Clan.findById clanID, (err, clan) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
return @sendDatabaseError(res, err) unless clan
|
||||||
|
return @sendForbiddenError res unless @hasAccessToDocument(req, clan)
|
||||||
|
return @sendForbiddenError(res) if clan.get('ownerID').equals memberID
|
||||||
|
Clan.update {_id: clanID}, {$pull: {members: memberID}}, (err) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
User.update {_id: memberID}, {$pull: {clans: clanID}}, (err) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
@sendSuccess(res)
|
||||||
|
|
||||||
|
module.exports = new ClanHandler()
|
|
@ -6,6 +6,7 @@ module.exports.handlers =
|
||||||
# 'analytics_users_active': 'analytics/analytics_users_active_handler'
|
# 'analytics_users_active': 'analytics/analytics_users_active_handler'
|
||||||
'article': 'articles/article_handler'
|
'article': 'articles/article_handler'
|
||||||
'campaign': 'campaigns/campaign_handler'
|
'campaign': 'campaigns/campaign_handler'
|
||||||
|
'clan': 'clans/clan_handler'
|
||||||
'level': 'levels/level_handler'
|
'level': 'levels/level_handler'
|
||||||
'level_component': 'levels/components/level_component_handler'
|
'level_component': 'levels/components/level_component_handler'
|
||||||
'level_feedback': 'levels/feedbacks/level_feedback_handler'
|
'level_feedback': 'levels/feedbacks/level_feedback_handler'
|
||||||
|
@ -22,9 +23,9 @@ module.exports.handlers =
|
||||||
'achievement': 'achievements/achievement_handler'
|
'achievement': 'achievements/achievement_handler'
|
||||||
'earned_achievement': 'achievements/earned_achievement_handler'
|
'earned_achievement': 'achievements/earned_achievement_handler'
|
||||||
'poll': 'polls/poll_handler'
|
'poll': 'polls/poll_handler'
|
||||||
'user_polls_record': 'polls/user_polls_record_handler'
|
|
||||||
'prepaid': 'prepaids/prepaid_handler'
|
'prepaid': 'prepaids/prepaid_handler'
|
||||||
'subscription': 'payments/subscription_handler'
|
'subscription': 'payments/subscription_handler'
|
||||||
|
'user_polls_record': 'polls/user_polls_record_handler'
|
||||||
|
|
||||||
module.exports.routes =
|
module.exports.routes =
|
||||||
[
|
[
|
||||||
|
|
|
@ -10,6 +10,7 @@ async = require 'async'
|
||||||
log = require 'winston'
|
log = require 'winston'
|
||||||
moment = require 'moment'
|
moment = require 'moment'
|
||||||
AnalyticsLogEvent = require '../analytics/AnalyticsLogEvent'
|
AnalyticsLogEvent = require '../analytics/AnalyticsLogEvent'
|
||||||
|
Clan = require '../clans/Clan'
|
||||||
LevelSession = require '../levels/sessions/LevelSession'
|
LevelSession = require '../levels/sessions/LevelSession'
|
||||||
LevelSessionHandler = require '../levels/sessions/level_session_handler'
|
LevelSessionHandler = require '../levels/sessions/level_session_handler'
|
||||||
SubscriptionHandler = require '../payments/subscription_handler'
|
SubscriptionHandler = require '../payments/subscription_handler'
|
||||||
|
@ -262,6 +263,7 @@ UserHandler = class UserHandler extends Handler
|
||||||
return @getLevelSessionsForEmployer(req, res, args[0]) if args[1] is 'level.sessions' and args[2] is 'employer'
|
return @getLevelSessionsForEmployer(req, res, args[0]) if args[1] is 'level.sessions' and args[2] is 'employer'
|
||||||
return @getLevelSessions(req, res, args[0]) if args[1] is 'level.sessions'
|
return @getLevelSessions(req, res, args[0]) if args[1] is 'level.sessions'
|
||||||
return @getCandidates(req, res) if args[1] is 'candidates'
|
return @getCandidates(req, res) if args[1] is 'candidates'
|
||||||
|
return @getClans(req, res, args[0]) if args[1] is 'clans'
|
||||||
return @getEmployers(req, res) if args[1] is 'employers'
|
return @getEmployers(req, res) if args[1] is 'employers'
|
||||||
return @getSimulatorLeaderboard(req, res, args[0]) if args[1] is 'simulatorLeaderboard'
|
return @getSimulatorLeaderboard(req, res, args[0]) if args[1] is 'simulatorLeaderboard'
|
||||||
return @getMySimulatorLeaderboardRank(req, res, args[0]) if args[1] is 'simulator_leaderboard_rank'
|
return @getMySimulatorLeaderboardRank(req, res, args[0]) if args[1] is 'simulator_leaderboard_rank'
|
||||||
|
@ -539,6 +541,14 @@ UserHandler = class UserHandler extends Handler
|
||||||
candidates = (@formatCandidate(authorized, candidate) for candidate in candidates)
|
candidates = (@formatCandidate(authorized, candidate) for candidate in candidates)
|
||||||
@sendSuccess(res, candidates)
|
@sendSuccess(res, candidates)
|
||||||
|
|
||||||
|
getClans: (req, res, userIDOrSlug) ->
|
||||||
|
@getDocumentForIdOrSlug userIDOrSlug, (err, user) =>
|
||||||
|
return @sendNotFoundError(res) if not user
|
||||||
|
clanIDs = user.get('clans') ? []
|
||||||
|
Clan.find {_id: {$in: clanIDs}}, (err, documents) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
@sendSuccess(res, documents)
|
||||||
|
|
||||||
formatCandidate: (authorized, document) ->
|
formatCandidate: (authorized, document) ->
|
||||||
fields = if authorized then ['name', 'jobProfile', 'jobProfileApproved', 'photoURL', '_id'] else ['_id','jobProfile', 'jobProfileApproved']
|
fields = if authorized then ['name', 'jobProfile', 'jobProfileApproved', 'photoURL', '_id'] else ['_id','jobProfile', 'jobProfileApproved']
|
||||||
obj = _.pick document.toObject(), fields
|
obj = _.pick document.toObject(), fields
|
||||||
|
|
|
@ -27,6 +27,7 @@ models_path = [
|
||||||
'../../server/analytics/AnalyticsUsersActive'
|
'../../server/analytics/AnalyticsUsersActive'
|
||||||
'../../server/articles/Article'
|
'../../server/articles/Article'
|
||||||
'../../server/campaigns/Campaign'
|
'../../server/campaigns/Campaign'
|
||||||
|
'../../server/clans/Clan'
|
||||||
'../../server/levels/Level'
|
'../../server/levels/Level'
|
||||||
'../../server/levels/components/LevelComponent'
|
'../../server/levels/components/LevelComponent'
|
||||||
'../../server/levels/systems/LevelSystem'
|
'../../server/levels/systems/LevelSystem'
|
||||||
|
@ -182,6 +183,10 @@ GLOBAL.loginUser = (user, done) ->
|
||||||
form.append('username', user.get('email'))
|
form.append('username', user.get('email'))
|
||||||
form.append('password', user.get('name'))
|
form.append('password', user.get('name'))
|
||||||
|
|
||||||
|
GLOBAL.logoutUser = (done) ->
|
||||||
|
request.post getURL('/auth/logout'), ->
|
||||||
|
done()
|
||||||
|
|
||||||
GLOBAL.dropGridFS = (done) ->
|
GLOBAL.dropGridFS = (done) ->
|
||||||
if mongoose.connection.readyState is 2
|
if mongoose.connection.readyState is 2
|
||||||
mongoose.connection.once 'open', ->
|
mongoose.connection.once 'open', ->
|
||||||
|
|
309
test/server/functional/clan.spec.coffee
Normal file
309
test/server/functional/clan.spec.coffee
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
config = require '../../../server_config'
|
||||||
|
require '../common'
|
||||||
|
utils = require '../../../app/core/utils' # Must come after require /common
|
||||||
|
mongoose = require 'mongoose'
|
||||||
|
|
||||||
|
describe 'Clans', ->
|
||||||
|
stripe = require('stripe')(config.stripe.secretKey)
|
||||||
|
clanURL = getURL('/db/clan')
|
||||||
|
|
||||||
|
clanCount = 0
|
||||||
|
createClanName = (name) -> name + clanCount++
|
||||||
|
|
||||||
|
createClan = (user, type, description, done) ->
|
||||||
|
name = createClanName 'myclan'
|
||||||
|
requestBody =
|
||||||
|
type: type
|
||||||
|
name: name
|
||||||
|
requestBody.description = description if description?
|
||||||
|
request.post {uri: clanURL, json: requestBody }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
expect(body.type).toEqual(type)
|
||||||
|
expect(body.name).toEqual(name)
|
||||||
|
expect(body.description).toEqual(description) if description?
|
||||||
|
expect(body.members?.length).toEqual(1)
|
||||||
|
expect(body.members?[0]).toEqual(user.id)
|
||||||
|
Clan.findById body._id, (err, clan) ->
|
||||||
|
expect(clan.get('type')).toEqual(type)
|
||||||
|
expect(clan.get('name')).toEqual(name)
|
||||||
|
expect(clan.get('description')).toEqual(description) if description?
|
||||||
|
expect(clan.get('members')?.length).toEqual(1)
|
||||||
|
expect(clan.get('members')?[0]).toEqual(user._id)
|
||||||
|
User.findById user.id, (err, user) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(user.get('clans')?.length).toBeGreaterThan(0)
|
||||||
|
expect(_.find user.get('clans'), (clanID) -> clan._id.equals clanID).toBeDefined()
|
||||||
|
done(clan)
|
||||||
|
|
||||||
|
it 'Clear database users and clans', (done) ->
|
||||||
|
clearModels [User, Clan], (err) ->
|
||||||
|
throw err if err
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Create clan', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', 'test description', (clan) ->
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Anonymous create clan 401', (done) ->
|
||||||
|
logoutUser ->
|
||||||
|
requestBody =
|
||||||
|
type: 'public'
|
||||||
|
name: createClanName 'myclan'
|
||||||
|
request.post {uri: clanURL, json: requestBody }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(401)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Create clan missing type 422', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
requestBody =
|
||||||
|
name: createClanName 'myclan'
|
||||||
|
request.post {uri: clanURL, json: requestBody }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(422)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Create clan missing name 422', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
requestBody =
|
||||||
|
type: 'public'
|
||||||
|
request.post {uri: clanURL, json: requestBody }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(422)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Get public clans', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan1) ->
|
||||||
|
createClan user1, 'public', 'the second clan', (clan2) ->
|
||||||
|
request.get {uri: "#{clanURL}/-/public" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
expect(body.length).toBeGreaterThan(1)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Get public clans anonymous', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan1) ->
|
||||||
|
createClan user1, 'public', null, (clan2) ->
|
||||||
|
logoutUser ->
|
||||||
|
request.get {uri: "#{clanURL}/-/public" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
expect(body.length).toBeGreaterThan(1)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Join clan', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan1) ->
|
||||||
|
loginNewUser (user2) ->
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
Clan.findById clan1.id, (err, clan1) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(clan1.get('members')?.length).toEqual(2)
|
||||||
|
expect(_.find clan1.get('members'), (memberID) -> user2._id.equals memberID).toBeDefined()
|
||||||
|
User.findById user2.id, (err, user2) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(user2.get('clans')?.length).toBeGreaterThan(0)
|
||||||
|
expect(_.find user2.get('clans'), (clanID) -> clan1._id.equals clanID).toBeDefined()
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Join invalid clan 404', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan1) ->
|
||||||
|
loginNewUser (user2) ->
|
||||||
|
request.put {uri: "#{clanURL}/1234/join" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(404)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Join clan anonymous 401', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan1) ->
|
||||||
|
logoutUser ->
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(401)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Join clan twice 200', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan1) ->
|
||||||
|
loginNewUser (user2) ->
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
Clan.findById clan1.id, (err, clan1) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(_.find clan1.get('members'), (memberID) -> memberID.equals user2.id).toBeDefined()
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Leave clan', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', 'do not stay too long', (clan1) ->
|
||||||
|
loginNewUser (user2) ->
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/leave" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
Clan.findById clan1.id, (err, clan1) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(_.find clan1.get('members'), (memberID) -> memberID.equals user2.id).toBeUndefined()
|
||||||
|
User.findById user2.id, (err, user2) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(user2.get('clans').length).toEqual(0)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Leave clan not member 200', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan1) ->
|
||||||
|
loginNewUser (user2) ->
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/leave" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
Clan.findById clan1.id, (err, clan1) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(_.find clan1.get('members'), (memberID) -> memberID.equals user2.id).toBeUndefined()
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Leave owned clan 403', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan1) ->
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/leave" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(403)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Remove member', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan1) ->
|
||||||
|
loginNewUser (user2) ->
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
loginUser user1, (user1) ->
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/remove/#{user2.id}" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
Clan.findById clan1.id, (err, clan1) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(clan1.get('members').length).toEqual(1)
|
||||||
|
expect(clan1.get('members')[0]).toEqual(user1.get('_id'))
|
||||||
|
User.findById user2.id, (err, user2) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(user2.get('clans').length).toEqual(0)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Remove non-member 200', (done) ->
|
||||||
|
loginNewUser (user2) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan1) ->
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/remove/#{user2.id}" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
Clan.findById clan1.id, (err, clan1) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(clan1.get('members').length).toEqual(1)
|
||||||
|
expect(clan1.get('members')[0]).toEqual(user1.get('_id'))
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Remove invalid memberID 404', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan1) ->
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/remove/123" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(404)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Remove member, not in clan 403', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan1) ->
|
||||||
|
loginNewUser (user2) ->
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
loginNewUser (user3) ->
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/remove/#{user2.id}" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(403)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Remove member, not the owner 403', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan1) ->
|
||||||
|
loginNewUser (user2) ->
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
loginNewUser (user3) ->
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/join" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/remove/#{user2.id}" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(403)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Remove member from owned clan 403', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan1) ->
|
||||||
|
request.put {uri: "#{clanURL}/#{clan1.id}/remove/#{user1.id}" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(403)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Delete clan', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan) ->
|
||||||
|
request.del {uri: "#{clanURL}/#{clan.id}" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(204)
|
||||||
|
User.findById user1.id, (err, user1) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(user1.get('clans').length).toEqual(0)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Delete clan anonymous 401', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan) ->
|
||||||
|
logoutUser ->
|
||||||
|
request.del {uri: "#{clanURL}/#{clan.id}" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(401)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Delete clan not owner 403', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan) ->
|
||||||
|
loginNewUser (user2) ->
|
||||||
|
request.del {uri: "#{clanURL}/#{clan.id}" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(403)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Delete clan no longer exists 404', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
createClan user1, 'public', null, (clan) ->
|
||||||
|
request.del {uri: "#{clanURL}/#{clan.id}" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(204)
|
||||||
|
request.del {uri: "#{clanURL}/#{clan.id}" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(404)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'Delete clan invalid ID 404', (done) ->
|
||||||
|
loginNewUser (user1) ->
|
||||||
|
request.del {uri: "#{clanURL}/1234" }, (err, res, body) ->
|
||||||
|
expect(err).toBeNull()
|
||||||
|
expect(res.statusCode).toBe(404)
|
||||||
|
done()
|
|
@ -46,7 +46,7 @@ describe '/db/payment', ->
|
||||||
paymentCreated = body?._id
|
paymentCreated = body?._id
|
||||||
expect(res.statusCode).toBe 201
|
expect(res.statusCode).toBe 201
|
||||||
User.findOne({name:'Joe'}).exec(err, (err, user) ->
|
User.findOne({name:'Joe'}).exec(err, (err, user) ->
|
||||||
expect(user.get('purchased').gems).toBe(5000)
|
expect(user.get('purchased')?.gems).toBe(5000)
|
||||||
done()
|
done()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ describe '/db/payment', ->
|
||||||
expect(body._id is paymentCreated).toBe(true)
|
expect(body._id is paymentCreated).toBe(true)
|
||||||
expect(res.statusCode).toBe 200
|
expect(res.statusCode).toBe 200
|
||||||
User.findOne({name:'Joe'}).exec(err, (err, user) ->
|
User.findOne({name:'Joe'}).exec(err, (err, user) ->
|
||||||
expect(user.get('purchased').gems).toBe(5000)
|
expect(user.get('purchased')?.gems).toBe(5000)
|
||||||
done()
|
done()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ describe '/db/payment', ->
|
||||||
expect(body._id is paymentCreated).toBe(false)
|
expect(body._id is paymentCreated).toBe(false)
|
||||||
expect(res.statusCode).toBe 201
|
expect(res.statusCode).toBe 201
|
||||||
User.findOne({name:'Joe'}).exec(err, (err, user) ->
|
User.findOne({name:'Joe'}).exec(err, (err, user) ->
|
||||||
expect(user.get('purchased').gems).toBe(16000)
|
expect(user.get('purchased')?.gems).toBe(16000)
|
||||||
done()
|
done()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue