mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-12-04 21:01:06 -05:00
8dbc86ca04
Hide TeachersContactModal after sending message Fix GET /db/level/:handle/session, more extensively test Fix EnrollmentView number of students input to stop losing focus on input Fix EnrollmentsView syntax Fix ActivateLicensesModal "Get Enrollments" button when already in the enrollments page Update EnrollmentsView with new credit numbers when ActivateLicensesModal closes Hide search box in TeacherClassView "Enrollment Status" tab Tweak EnrollmentsView styling Fix EnrollmentsView tests Fix AnalyticsView Make EnrollmentsView more explicitly handle undefined and empty array prepaid groups Remove log CoursesView handles JoinClassModal cancel Re-enable EditStudentModal set password button when the form changes Fix course instance tests, next level endpoint bug Fix EditStudentModal tests
415 lines
17 KiB
CoffeeScript
415 lines
17 KiB
CoffeeScript
async = require 'async'
|
|
config = require '../../../server_config'
|
|
require '../common'
|
|
stripe = require('stripe')(config.stripe.secretKey)
|
|
utils = require '../utils'
|
|
CourseInstance = require '../../../server/models/CourseInstance'
|
|
Course = require '../../../server/models/Course'
|
|
User = require '../../../server/models/User'
|
|
Classroom = require '../../../server/models/Classroom'
|
|
Campaign = require '../../../server/models/Campaign'
|
|
Level = require '../../../server/models/Level'
|
|
Prepaid = require '../../../server/models/Prepaid'
|
|
request = require '../request'
|
|
moment = require 'moment'
|
|
|
|
courseFixture = {
|
|
name: 'Unnamed course'
|
|
campaignID: ObjectId("55b29efd1cd6abe8ce07db0d")
|
|
concepts: ['basic_syntax', 'arguments', 'while_loops', 'strings', 'variables']
|
|
description: "Learn basic syntax, while loops, and the CodeCombat environment."
|
|
screenshot: "/images/pages/courses/101_info.png"
|
|
}
|
|
|
|
classroomFixture = {
|
|
name: 'Unnamed classroom'
|
|
members: []
|
|
}
|
|
|
|
describe 'POST /db/course_instance', ->
|
|
url = getURL('/db/course_instance')
|
|
|
|
beforeEach utils.wrap (done) ->
|
|
yield utils.clearModels([CourseInstance, Course, User, Classroom])
|
|
@teacher = yield utils.initUser({role: 'teacher'})
|
|
yield utils.loginUser(@teacher)
|
|
@course = yield new Course(courseFixture).save()
|
|
classroomData = _.extend({ownerID: @teacher._id}, classroomFixture)
|
|
@classroom = yield new Classroom(classroomData).save()
|
|
done()
|
|
|
|
it 'creates a CourseInstance', utils.wrap (done) ->
|
|
data = {
|
|
name: 'Some Name'
|
|
courseID: @course.id
|
|
classroomID: @classroom.id
|
|
}
|
|
[res, body] = yield request.postAsync {uri: url, json: data}
|
|
expect(res.statusCode).toBe(200)
|
|
expect(body.classroomID).toBeDefined()
|
|
done()
|
|
|
|
it 'returns the same CourseInstance if you POST twice', utils.wrap (done) ->
|
|
data = {
|
|
name: 'Some Name'
|
|
courseID: @course.id
|
|
classroomID: @classroom.id
|
|
}
|
|
[res, body] = yield request.postAsync {uri: url, json: data}
|
|
expect(res.statusCode).toBe(200)
|
|
expect(body.classroomID).toBeDefined()
|
|
firstID = body._id
|
|
[res, body] = yield request.postAsync {uri: url, json: data}
|
|
expect(res.statusCode).toBe(200)
|
|
expect(body.classroomID).toBeDefined()
|
|
secondID = body._id
|
|
expect(firstID).toBe(secondID)
|
|
done()
|
|
|
|
it 'returns 404 if the Course does not exist', utils.wrap (done) ->
|
|
data = {
|
|
name: 'Some Name'
|
|
courseID: '123456789012345678901234'
|
|
classroomID: @classroom.id
|
|
}
|
|
[res, body] = yield request.postAsync {uri: url, json: data}
|
|
expect(res.statusCode).toBe(404)
|
|
done()
|
|
|
|
it 'returns 404 if the Classroom does not exist', utils.wrap (done) ->
|
|
data = {
|
|
name: 'Some Name'
|
|
courseID: @course.id
|
|
classroomID: '123456789012345678901234'
|
|
}
|
|
[res, body] = yield request.postAsync {uri: url, json: data}
|
|
expect(res.statusCode).toBe(404)
|
|
done()
|
|
|
|
it 'return 403 if the logged in user does not own the Classroom', utils.wrap (done) ->
|
|
user = yield utils.initUser()
|
|
yield utils.loginUser(user)
|
|
data = {
|
|
name: 'Some Name'
|
|
courseID: @course.id
|
|
classroomID: @classroom.id
|
|
}
|
|
[res, body] = yield request.postAsync {uri: url, json: data}
|
|
expect(res.statusCode).toBe(403)
|
|
done()
|
|
|
|
|
|
describe 'POST /db/course_instance/:id/members', ->
|
|
|
|
beforeEach utils.wrap (done) ->
|
|
yield utils.clearModels([CourseInstance, Course, User, Classroom, Prepaid, Campaign, Level])
|
|
@teacher = yield utils.initUser({role: 'teacher'})
|
|
admin = yield utils.initAdmin()
|
|
yield utils.loginUser(admin)
|
|
@level = yield utils.makeLevel({type: 'course'})
|
|
@campaign = yield utils.makeCampaign({}, {levels: [@level]})
|
|
@course = yield utils.makeCourse({free: true}, {campaign: @campaign})
|
|
@student = yield utils.initUser({role: 'student'})
|
|
@prepaid = yield utils.makePrepaid({creator: @teacher.id})
|
|
members = [@student]
|
|
yield utils.loginUser(@teacher)
|
|
@classroom = yield utils.makeClassroom({aceConfig: { language: 'javascript' }}, { members })
|
|
@courseInstance = yield utils.makeCourseInstance({}, { @course, @classroom })
|
|
done()
|
|
|
|
it 'adds an array of members to the given CourseInstance', utils.wrap (done) ->
|
|
@classroom.set('members', [@student._id])
|
|
yield @classroom.save()
|
|
url = getURL("/db/course_instance/#{@courseInstance.id}/members")
|
|
[res, body] = yield request.postAsync {uri: url, json: {userIDs: [@student.id]}}
|
|
expect(res.statusCode).toBe(200)
|
|
expect(body.members.length).toBe(1)
|
|
expect(body.members[0]).toBe(@student.id)
|
|
done()
|
|
|
|
it 'adds a member to the given CourseInstance', utils.wrap (done) ->
|
|
url = getURL("/db/course_instance/#{@courseInstance.id}/members")
|
|
[res, body] = yield request.postAsync {uri: url, json: {userID: @student.id}}
|
|
expect(res.statusCode).toBe(200)
|
|
expect(res.body.members.length).toBe(1)
|
|
expect(res.body.members[0]).toBe(@student.id)
|
|
done()
|
|
|
|
it 'adds the CourseInstance id to the user', utils.wrap (done) ->
|
|
url = getURL("/db/course_instance/#{@courseInstance.id}/members")
|
|
[res, body] = yield request.postAsync {uri: url, json: {userID: @student.id}}
|
|
user = yield User.findById(@student.id)
|
|
expect(_.size(user.get('courseInstances'))).toBe(1)
|
|
done()
|
|
|
|
it 'return 403 if the member is not in the classroom', utils.wrap (done) ->
|
|
@classroom.set('members', [])
|
|
yield @classroom.save()
|
|
url = getURL("/db/course_instance/#{@courseInstance.id}/members")
|
|
[res, body] = yield request.postAsync {uri: url, json: {userID: @student.id}}
|
|
expect(res.statusCode).toBe(403)
|
|
done()
|
|
|
|
it 'returns 403 if the user does not own the course instance and is not adding self', utils.wrap (done) ->
|
|
otherUser = yield utils.initUser()
|
|
yield utils.loginUser(otherUser)
|
|
url = getURL("/db/course_instance/#{@courseInstance.id}/members")
|
|
[res, body] = yield request.postAsync {uri: url, json: {userID: @student.id}}
|
|
expect(res.statusCode).toBe(403)
|
|
done()
|
|
|
|
it 'returns 200 if the user is a member of the classroom and is adding self', ->
|
|
|
|
it 'return 402 if the course is not free and the user is not enrolled', utils.wrap (done) ->
|
|
@course.set('free', false)
|
|
yield @course.save()
|
|
url = getURL("/db/course_instance/#{@courseInstance.id}/members")
|
|
[res, body] = yield request.postAsync {uri: url, json: {userID: @student.id}}
|
|
expect(res.statusCode).toBe(402)
|
|
done()
|
|
|
|
it 'works if the course is not free and the user is enrolled', utils.wrap (done) ->
|
|
@course.set('free', false)
|
|
yield @course.save()
|
|
@student.set('coursePrepaid', _.pick(@prepaid.toObject(), '_id', 'startDate', 'endDate'))
|
|
yield @student.save()
|
|
url = getURL("/db/course_instance/#{@courseInstance.id}/members")
|
|
[res, body] = yield request.postAsync {uri: url, json: {userID: @student.id}}
|
|
expect(res.statusCode).toBe(200)
|
|
done()
|
|
|
|
it 'works if the course is not free and the user is enrolled but is not migrated', utils.wrap (done) ->
|
|
@course.set('free', false)
|
|
yield @course.save()
|
|
@student.set('coursePrepaidID', @prepaid._id)
|
|
yield @student.save()
|
|
url = getURL("/db/course_instance/#{@courseInstance.id}/members")
|
|
[res, body] = yield request.postAsync {uri: url, json: {userID: @student.id}}
|
|
expect(res.statusCode).toBe(200)
|
|
done()
|
|
|
|
describe 'DELETE /db/course_instance/:id/members', ->
|
|
|
|
beforeEach utils.wrap (done) ->
|
|
utils.clearModels([CourseInstance, Course, User, Classroom, Prepaid])
|
|
|
|
# create, login user
|
|
@teacher = yield utils.initUser({role: 'teacher'})
|
|
yield utils.loginUser(@teacher)
|
|
|
|
# create student, course, classroom and course instance
|
|
@student = yield utils.initUser()
|
|
courseData = _.extend({free: true}, courseFixture)
|
|
@course = yield new Course(courseData).save()
|
|
classroomData = _.extend({}, classroomFixture, {ownerID: @teacher._id, members: [@student._id]})
|
|
@classroom = yield new Classroom(classroomData).save()
|
|
url = getURL('/db/course_instance')
|
|
data = {
|
|
name: 'Some Name'
|
|
courseID: @course.id
|
|
classroomID: @classroom.id
|
|
}
|
|
[res, body] = yield request.postAsync {uri: url, json: data}
|
|
@courseInstance = yield CourseInstance.findById res.body._id
|
|
|
|
# add user to course instance
|
|
url = getURL("/db/course_instance/#{@courseInstance.id}/members")
|
|
[res, body] = yield request.postAsync {uri: url, json: {userID: @student.id}}
|
|
@prepaid = yield new Prepaid({
|
|
type: 'course'
|
|
maxRedeemers: 10
|
|
redeemers: []
|
|
}).save()
|
|
done()
|
|
|
|
it 'removes a member to the given CourseInstance', utils.wrap (done) ->
|
|
url = getURL("/db/course_instance/#{@courseInstance.id}/members")
|
|
[res, body] = yield request.delAsync {uri: url, json: {userID: @student.id}}
|
|
expect(res.statusCode).toBe(200)
|
|
expect(res.body.members.length).toBe(0)
|
|
done()
|
|
|
|
it 'removes the CourseInstance from the User.courseInstances', utils.wrap (done) ->
|
|
url = getURL("/db/course_instance/#{@courseInstance.id}/members")
|
|
user = yield User.findById(@student.id)
|
|
expect(_.size(user.get('courseInstances'))).toBe(1)
|
|
[res, body] = yield request.delAsync {uri: url, json: {userID: @student.id}}
|
|
expect(res.statusCode).toBe(200)
|
|
expect(res.body.members.length).toBe(0)
|
|
user = yield User.findById(@student.id)
|
|
expect(_.size(user.get('courseInstances'))).toBe(0)
|
|
done()
|
|
|
|
describe 'GET /db/course_instance/:handle/levels/:levelOriginal/next', ->
|
|
|
|
beforeEach utils.wrap (done) ->
|
|
yield utils.clearModels [User, Classroom, Course, Level, Campaign]
|
|
admin = yield utils.initAdmin()
|
|
yield utils.loginUser(admin)
|
|
|
|
levelJSON = { name: 'A', permissions: [{access: 'owner', target: admin.id}], type: 'course' }
|
|
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
|
|
expect(res.statusCode).toBe(200)
|
|
@levelA = yield Level.findById(res.body._id)
|
|
paredLevelA = _.pick(res.body, 'name', 'original', 'type')
|
|
|
|
levelJSON = { name: 'B', permissions: [{access: 'owner', target: admin.id}], type: 'course' }
|
|
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
|
|
expect(res.statusCode).toBe(200)
|
|
@levelB = yield Level.findById(res.body._id)
|
|
paredLevelB = _.pick(res.body, 'name', 'original', 'type')
|
|
|
|
levelJSON = { name: 'C', permissions: [{access: 'owner', target: admin.id}], type: 'course' }
|
|
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
|
|
expect(res.statusCode).toBe(200)
|
|
@levelC = yield Level.findById(res.body._id)
|
|
paredLevelC = _.pick(res.body, 'name', 'original', 'type')
|
|
|
|
campaignJSONA = { name: 'Campaign A', levels: {} }
|
|
campaignJSONA.levels[paredLevelA.original] = paredLevelA
|
|
campaignJSONA.levels[paredLevelB.original] = paredLevelB
|
|
[res, body] = yield request.postAsync({uri: getURL('/db/campaign'), json: campaignJSONA})
|
|
@campaignA = yield Campaign.findById(res.body._id)
|
|
|
|
campaignJSONB = { name: 'Campaign B', levels: {} }
|
|
campaignJSONB.levels[paredLevelC.original] = paredLevelC
|
|
[res, body] = yield request.postAsync({uri: getURL('/db/campaign'), json: campaignJSONB})
|
|
@campaignB = yield Campaign.findById(res.body._id)
|
|
|
|
@courseA = Course({name: 'Course A', campaignID: @campaignA._id})
|
|
yield @courseA.save()
|
|
|
|
@courseB = Course({name: 'Course B', campaignID: @campaignB._id})
|
|
yield @courseB.save()
|
|
|
|
teacher = yield utils.initUser({role: 'teacher'})
|
|
yield utils.loginUser(teacher)
|
|
data = { name: 'Classroom 1' }
|
|
classroomsURL = getURL('/db/classroom')
|
|
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
|
expect(res.statusCode).toBe(201)
|
|
@classroom = yield Classroom.findById(res.body._id)
|
|
|
|
url = getURL('/db/course_instance')
|
|
|
|
dataA = { name: 'Some Name', courseID: @courseA.id, classroomID: @classroom.id }
|
|
[res, body] = yield request.postAsync {uri: url, json: dataA}
|
|
expect(res.statusCode).toBe(200)
|
|
@courseInstanceA = yield CourseInstance.findById(res.body._id)
|
|
|
|
dataB = { name: 'Some Other Name', courseID: @courseB.id, classroomID: @classroom.id }
|
|
[res, body] = yield request.postAsync {uri: url, json: dataB}
|
|
expect(res.statusCode).toBe(200)
|
|
@courseInstanceB = yield CourseInstance.findById(res.body._id)
|
|
|
|
done()
|
|
|
|
it 'returns the next level for the course in the linked classroom', utils.wrap (done) ->
|
|
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course_instance/#{@courseInstanceA.id}/levels/#{@levelA.id}/next"), json: true }
|
|
expect(res.statusCode).toBe(200)
|
|
expect(res.body.original).toBe(@levelB.original.toString())
|
|
done()
|
|
|
|
it 'returns empty object if the given level is the last level in its course', utils.wrap (done) ->
|
|
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course_instance/#{@courseInstanceA.id}/levels/#{@levelB.id}/next"), json: true }
|
|
expect(res.statusCode).toBe(200)
|
|
expect(res.body).toEqual({})
|
|
done()
|
|
|
|
it 'returns 404 if the given level is not in the course instance\'s course', utils.wrap (done) ->
|
|
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course_instance/#{@courseInstanceB.id}/levels/#{@levelA.id}/next"), json: true }
|
|
expect(res.statusCode).toBe(404)
|
|
done()
|
|
|
|
|
|
describe 'GET /db/course_instance/:handle/classroom', ->
|
|
|
|
beforeEach utils.wrap (done) ->
|
|
yield utils.clearModels [User, CourseInstance, Classroom]
|
|
@owner = yield utils.initUser()
|
|
yield @owner.save()
|
|
@member = yield utils.initUser()
|
|
yield @member.save()
|
|
@classroom = new Classroom({
|
|
ownerID: @owner._id
|
|
members: [@member._id]
|
|
})
|
|
yield @classroom.save()
|
|
@courseInstance = new CourseInstance({classroomID: @classroom._id})
|
|
yield @courseInstance.save()
|
|
@url = getURL("/db/course_instance/#{@courseInstance.id}/classroom")
|
|
done()
|
|
|
|
it 'returns the course instance\'s referenced classroom', utils.wrap (done) ->
|
|
yield utils.loginUser @owner
|
|
[res, body] = yield request.getAsync(@url, {json: true})
|
|
expect(res.statusCode).toBe(200)
|
|
expect(body.code).toBeDefined()
|
|
done()
|
|
|
|
it 'works if you are the owner or member', utils.wrap (done) ->
|
|
yield utils.loginUser @member
|
|
[res, body] = yield request.getAsync(@url, {json: true})
|
|
expect(res.statusCode).toBe(200)
|
|
expect(body.code).toBeUndefined()
|
|
done()
|
|
|
|
it 'does not work if you are not the owner or a member', utils.wrap (done) ->
|
|
@user = yield utils.initUser()
|
|
yield utils.loginUser @user
|
|
[res, body] = yield request.getAsync(@url, {json: true})
|
|
expect(res.statusCode).toBe(403)
|
|
done()
|
|
|
|
describe 'POST /db/course_instance/-/recent', ->
|
|
|
|
url = getURL('/db/course_instance/-/recent')
|
|
|
|
beforeEach utils.wrap (done) ->
|
|
yield utils.clearModels([CourseInstance, Course, User, Classroom, Prepaid, Campaign, Level])
|
|
@teacher = yield utils.initUser({role: 'teacher'})
|
|
@admin = yield utils.initAdmin()
|
|
yield utils.loginUser(@admin)
|
|
@campaign = yield utils.makeCampaign()
|
|
@course = yield utils.makeCourse({free: true}, {campaign: @campaign})
|
|
@student = yield utils.initUser({role: 'student'})
|
|
@prepaid = yield utils.makePrepaid({creator: @teacher.id})
|
|
members = [@student]
|
|
yield utils.loginUser(@teacher)
|
|
@classroom = yield utils.makeClassroom({aceConfig: { language: 'javascript' }}, { members })
|
|
@courseInstance = yield utils.makeCourseInstance({}, { @course, @classroom, members })
|
|
[res, body] = yield request.postAsync({url: getURL("/db/prepaid/#{@prepaid.id}/redeemers"), json: { userID: @student.id} })
|
|
yield utils.loginUser(@admin)
|
|
done()
|
|
|
|
it 'returns all non-HoC course instances and their related users and prepaids', utils.wrap (done) ->
|
|
[res, body] = yield request.postAsync(url, { json: true })
|
|
expect(res.statusCode).toBe(200)
|
|
expect(res.body.courseInstances[0]._id).toBe(@courseInstance.id)
|
|
expect(res.body.students[0]._id).toBe(@student.id)
|
|
expect(res.body.prepaids[0]._id).toBe(@prepaid.id)
|
|
done()
|
|
|
|
it 'returns course instances within a specified range', utils.wrap (done) ->
|
|
startDay = moment().subtract(1, 'day').format('YYYY-MM-DD')
|
|
endDay = moment().add(1, 'day').format('YYYY-MM-DD')
|
|
[res, body] = yield request.postAsync(url, { json: { startDay, endDay } })
|
|
expect(res.body.courseInstances.length).toBe(1)
|
|
|
|
startDay = moment().add(1, 'day').format('YYYY-MM-DD')
|
|
endDay = moment().add(2, 'day').format('YYYY-MM-DD')
|
|
[res, body] = yield request.postAsync(url, { json: { startDay, endDay } })
|
|
expect(res.body.courseInstances.length).toBe(0)
|
|
|
|
startDay = moment().subtract(2, 'day').format('YYYY-MM-DD')
|
|
endDay = moment().subtract(1, 'day').format('YYYY-MM-DD')
|
|
[res, body] = yield request.postAsync(url, { json: { startDay, endDay } })
|
|
expect(res.body.courseInstances.length).toBe(0)
|
|
|
|
done()
|
|
|
|
it 'returns 403 if not an admin', utils.wrap (done) ->
|
|
yield utils.loginUser(@teacher)
|
|
[res, body] = yield request.postAsync(url, { json: true })
|
|
expect(res.statusCode).toBe(403)
|
|
done()
|