mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-02-17 00:40:56 -05:00
Add primer level support to classroom Ux
Exclude levels if classroom.aceConfig.language == level.primerLanguage Closes #3856
This commit is contained in:
parent
efa4b2b158
commit
84e3ee270a
12 changed files with 429 additions and 231 deletions
|
@ -47,9 +47,11 @@ module.exports = class Classroom extends CocoModel
|
|||
getLevelNumber: (levelID, defaultNumber) ->
|
||||
unless @levelNumberMap
|
||||
@levelNumberMap = {}
|
||||
language = @get('aceConfig')?.language
|
||||
for course in @get('courses') ? []
|
||||
levels = []
|
||||
for level in course.levels when level.original
|
||||
continue if language? and level.primerLanguage is language
|
||||
levels.push({key: level.original, practice: level.practice ? false})
|
||||
_.assign(@levelNumberMap, utils.createLevelNumberMap(levels))
|
||||
@levelNumberMap[levelID] ? defaultNumber
|
||||
|
@ -84,6 +86,8 @@ module.exports = class Classroom extends CocoModel
|
|||
continue
|
||||
levelObjects.push(course.levels)
|
||||
levels = new Levels(_.flatten(levelObjects))
|
||||
language = @get('aceConfig')?.language
|
||||
levels.remove(levels.filter((level) => level.get('primerLanguage') is language)) if language
|
||||
if options.withoutLadderLevels
|
||||
levels.remove(levels.filter((level) -> level.isLadder()))
|
||||
if options.projectLevels
|
||||
|
|
|
@ -37,11 +37,19 @@ block content
|
|||
span(data-i18n="courses.select_language")
|
||||
| :
|
||||
select.language-select.form-control
|
||||
// TODO: Automate this list @scott
|
||||
option(value="python")
|
||||
| Python
|
||||
option(value="javascript")
|
||||
| JavaScript
|
||||
//- TODO: Automate this list @scott
|
||||
//- Web dev courses use HTML and JavaScript, except web-dev-1 which doesn't have scripting
|
||||
if course.get('slug') === 'web-dev-1'
|
||||
option(value="javascript")
|
||||
| HTML
|
||||
else if course.get('slug').indexOf('web-dev') >= 0
|
||||
option(value="javascript")
|
||||
| HTML / JavaScript
|
||||
else
|
||||
option(value="python")
|
||||
| Python
|
||||
option(value="javascript")
|
||||
| JavaScript
|
||||
//- option(value="coffeescript")
|
||||
//- | CoffeeScript (Experimental)
|
||||
//- option(value="clojure")
|
||||
|
@ -86,11 +94,19 @@ mixin course-info(course)
|
|||
span.spr ,
|
||||
|
||||
if me.isTeacher() || view.ownedClassrooms.size() || me.isAdmin()
|
||||
p
|
||||
a.guide-btn.btn.btn-primary(href=("/teachers/course-solution/" + course.id + "/python") data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide Python" class=(me.isTeacher() || me.isAdmin() ? '': 'disabled'))
|
||||
//- Web dev courses use HTML and JavaScript, except web-dev-1 which doesn't have scripting
|
||||
if course.get('slug') === 'web-dev-1'
|
||||
a.guide-btn.btn.btn-primary(href=("/teachers/course-solution/" + course.id + "/javascript") data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide JavaScript" class=(me.isTeacher() || me.isAdmin() ? '': 'disabled'))
|
||||
span(data-i18n="courses.view_guide_online")
|
||||
| — Python
|
||||
p
|
||||
| — HTML
|
||||
else if course.get('slug').indexOf('web-dev') >= 0
|
||||
a.guide-btn.btn.btn-primary(href=("/teachers/course-solution/" + course.id + "/javascript") data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide JavaScript" class=(me.isTeacher() || me.isAdmin() ? '': 'disabled'))
|
||||
span(data-i18n="courses.view_guide_online")
|
||||
| — HTML / JavaScript
|
||||
else
|
||||
a.guide-btn.btn.btn-primary(href=("/teachers/course-solution/" + course.id + "/javascript") data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide JavaScript" class=(me.isTeacher() || me.isAdmin() ? '': 'disabled'))
|
||||
span(data-i18n="courses.view_guide_online")
|
||||
| — JavaScript
|
||||
a.guide-btn.btn.btn-primary(href=("/teachers/course-solution/" + course.id + "/python") data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide Python" class=(me.isTeacher() || me.isAdmin() ? '': 'disabled'))
|
||||
span(data-i18n="courses.view_guide_online")
|
||||
| — Python
|
||||
|
|
|
@ -47,7 +47,7 @@ module.exports = class CourseDetailsView extends RootView
|
|||
@supermodel.trackRequest(@classroom.fetch())
|
||||
|
||||
levelsLoaded = @supermodel.trackRequest(@levels.fetchForClassroomAndCourse(classroomID, @courseID, {
|
||||
data: { project: 'concepts,practice,type,slug,name,original,description,shareable,i18n' }
|
||||
data: { project: 'concepts,practice,primerLanguage,type,slug,name,original,description,shareable,i18n' }
|
||||
}))
|
||||
|
||||
@supermodel.trackRequest($.when(levelsLoaded, sessionsLoaded).then(=>
|
||||
|
|
|
@ -120,7 +120,7 @@ module.exports = class TeacherClassView extends RootView
|
|||
@supermodel.trackRequest @courseInstances.fetchForClassroom(classroomID)
|
||||
|
||||
@levels = new Levels()
|
||||
@supermodel.trackRequest @levels.fetchForClassroom(classroomID, {data: {project: 'original,concepts,practice,shareable,i18n'}})
|
||||
@supermodel.trackRequest @levels.fetchForClassroom(classroomID, {data: {project: 'original,concepts,primerLanguage,practice,shareable,i18n'}})
|
||||
|
||||
@attachMediatorEvents()
|
||||
window.tracker?.trackEvent 'Teachers Class Loaded', category: 'Teachers', classroomID: @classroom.id, ['Mixpanel']
|
||||
|
@ -329,8 +329,10 @@ module.exports = class TeacherClassView extends RootView
|
|||
courseOrder.push(course.id)
|
||||
csvContent = "data:text/csv;charset=utf-8,Username,Email,Total Playtime,#{courseLabels}Concepts\n"
|
||||
levelCourseMap = {}
|
||||
language = @classroom.get('aceConfig')?.language
|
||||
for trimCourse in @classroom.get('courses')
|
||||
for trimLevel in trimCourse.levels
|
||||
continue if language and trimLevel.primerLanguage is language
|
||||
levelCourseMap[trimLevel.original] = @courses.get(trimCourse._id)
|
||||
for student in @students.models
|
||||
concepts = []
|
||||
|
@ -448,9 +450,11 @@ module.exports = class TeacherClassView extends RootView
|
|||
stats.totalPlaytime = if playtime then moment.duration(playtime, "seconds").humanize() else 0
|
||||
# TODO: Humanize differently ('1 hour' instead of 'an hour')
|
||||
|
||||
levelPracticeMap = {}
|
||||
levelPracticeMap[level.id] = level.get('practice') ? false for level in @levels.models
|
||||
completeSessions = @classroom.sessions.filter (s) -> s.get('state')?.complete and not levelPracticeMap[s.get('levelID')]
|
||||
levelIncludeMap = {}
|
||||
language = @classroom.get('aceConfig')?.language
|
||||
for level in @levels.models
|
||||
levelIncludeMap[level.get('original')] = not level.get('practice') and (not language? or level.get('primerLanguage') isnt language)
|
||||
completeSessions = @classroom.sessions.filter (s) -> s.get('state')?.complete and levelIncludeMap[s.get('level')?.original]
|
||||
stats.averageLevelsComplete = if @students.size() then (_.size(completeSessions) / @students.size()).toFixed(1) else 'N/A' # '
|
||||
stats.totalLevelsComplete = _.size(completeSessions)
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ module.exports = class TeacherCoursesView extends RootView
|
|||
form = $(e.currentTarget).closest('.play-level-form')
|
||||
levelSlug = form.find('.level-select').val()
|
||||
courseID = form.data('course-id')
|
||||
language = form.find('.language-select').val()
|
||||
language = form.find('.language-select').val() or 'javascript'
|
||||
window.tracker?.trackEvent 'Classes Guides Play Level', category: 'Teachers', courseID: courseID, language: language, levelSlug: levelSlug, ['Mixpanel']
|
||||
url = "/play/level/#{levelSlug}?course=#{courseID}&codeLanguage=#{language}"
|
||||
firstLevelSlug = @campaigns.get(@courses.at(0).get('campaignID')).getLevels().at(0).get('slug')
|
||||
|
|
|
@ -31,7 +31,7 @@ module.exports = class TeacherCourseSolutionView extends RootView
|
|||
|
||||
onLoaded: ->
|
||||
for level in @levels?.models
|
||||
articles = level.get('documentation').specificArticles
|
||||
articles = level.get('documentation')?.specificArticles
|
||||
if articles
|
||||
guide = articles.filter((x) => x.name == "Overview").pop()
|
||||
level.set 'guide', marked(@hideWrongLanguage(guide.body)) if guide
|
||||
|
|
|
@ -55,13 +55,18 @@ module.exports =
|
|||
classroom = yield database.getDocFromHandle(req, Classroom)
|
||||
if not classroom
|
||||
throw new errors.NotFound('Classroom not found.')
|
||||
|
||||
|
||||
levelOriginals = []
|
||||
for course in classroom.get('courses') or []
|
||||
for level in course.levels
|
||||
levelOriginals.push(level.original)
|
||||
|
||||
levels = yield Level.find({ original: { $in: levelOriginals }, slug: { $exists: true }}).select(parse.getProjectFromReq(req))
|
||||
|
||||
query = {$and: [
|
||||
{original: { $in: levelOriginals }}
|
||||
{$or: [{primerLanguage: {$exists: false}}, {primerLanguage: { $ne: classroom.get('aceConfig')?.language }}]}
|
||||
{slug: { $exists: true }}
|
||||
]}
|
||||
levels = yield Level.find(query).select(parse.getProjectFromReq(req))
|
||||
levels = (level.toObject({ req: req }) for level in levels)
|
||||
|
||||
# maintain course order
|
||||
|
@ -76,7 +81,7 @@ module.exports =
|
|||
classroom = yield database.getDocFromHandle(req, Classroom)
|
||||
if not classroom
|
||||
throw new errors.NotFound('Classroom not found.')
|
||||
|
||||
|
||||
levelOriginals = []
|
||||
for course in classroom.get('courses') or []
|
||||
if course._id.toString() isnt req.params.courseID
|
||||
|
@ -84,15 +89,20 @@ module.exports =
|
|||
for level in course.levels
|
||||
levelOriginals.push(level.original)
|
||||
|
||||
levels = yield Level.find({ original: { $in: levelOriginals }, slug: { $exists: true }}).select(parse.getProjectFromReq(req))
|
||||
query = {$and: [
|
||||
{original: { $in: levelOriginals }}
|
||||
{$or: [{primerLanguage: {$exists: false}}, {primerLanguage: { $ne: classroom.get('aceConfig')?.language }}]}
|
||||
{slug: { $exists: true }}
|
||||
]}
|
||||
levels = yield Level.find(query).select(parse.getProjectFromReq(req))
|
||||
levels = (level.toObject({ req: req }) for level in levels)
|
||||
|
||||
|
||||
# maintain course order
|
||||
levelMap = {}
|
||||
for level in levels
|
||||
levelMap[level.original] = level
|
||||
levels = (levelMap[levelOriginal.toString()] for levelOriginal in levelOriginals)
|
||||
|
||||
levels = (levelMap[levelOriginal.toString()] for levelOriginal in levelOriginals when levelMap[levelOriginal.toString()])
|
||||
|
||||
res.status(200).send(levels)
|
||||
|
||||
fetchMemberSessions: wrap (req, res, next) ->
|
||||
|
@ -143,14 +153,14 @@ module.exports =
|
|||
database.assignBody(req, classroom)
|
||||
|
||||
# Copy over data from how courses are right now
|
||||
coursesData = yield module.exports.generateCoursesData(req)
|
||||
coursesData = yield module.exports.generateCoursesData(classroom.get('aceConfig')?.language, req.user?.isAdmin())
|
||||
classroom.set('courses', coursesData)
|
||||
|
||||
# finish
|
||||
database.validateDoc(classroom)
|
||||
classroom = yield classroom.save()
|
||||
res.status(201).send(classroom.toObject({req: req}))
|
||||
|
||||
|
||||
updateCourses: wrap (req, res) ->
|
||||
throw new errors.Unauthorized() unless req.user and not req.user.isAnonymous()
|
||||
classroom = yield database.getDocFromHandle(req, Classroom)
|
||||
|
@ -159,15 +169,15 @@ module.exports =
|
|||
unless req.user._id.equals(classroom.get('ownerID'))
|
||||
throw new errors.Forbidden('Only the owner may update their classroom content')
|
||||
|
||||
coursesData = yield module.exports.generateCoursesData(req)
|
||||
coursesData = yield module.exports.generateCoursesData(classroom.get('aceConfig')?.language, req.user?.isAdmin())
|
||||
classroom.set('courses', coursesData)
|
||||
classroom = yield classroom.save()
|
||||
res.status(200).send(classroom.toObject({req: req}))
|
||||
|
||||
generateCoursesData: co.wrap (req) ->
|
||||
|
||||
generateCoursesData: co.wrap (classLanguage, isAdmin) ->
|
||||
# helper function for generating the latest version of courses
|
||||
query = {}
|
||||
query = {releasePhase: 'released'} unless req.user?.isAdmin()
|
||||
query = {releasePhase: 'released'} unless isAdmin
|
||||
courses = yield Course.find(query)
|
||||
courses = Course.sortCourses courses
|
||||
campaigns = yield Campaign.find({_id: {$in: (course.get('campaignID') for course in courses)}})
|
||||
|
@ -180,8 +190,9 @@ module.exports =
|
|||
levels = _.values(campaign.get('levels'))
|
||||
levels = _.sortBy(levels, 'campaignIndex')
|
||||
for level in levels
|
||||
continue if classLanguage and level.primerLanguage is classLanguage
|
||||
levelData = { original: mongoose.Types.ObjectId(level.original) }
|
||||
_.extend(levelData, _.pick(level, 'type', 'slug', 'name', 'practice', 'practiceThresholdMinutes', 'shareable'))
|
||||
_.extend(levelData, _.pick(level, 'type', 'slug', 'name', 'practice', 'practiceThresholdMinutes', 'primerLanguage', 'shareable'))
|
||||
courseData.levels.push(levelData)
|
||||
coursesData.push(courseData)
|
||||
return coursesData
|
||||
|
|
|
@ -87,6 +87,8 @@ module.exports =
|
|||
courseID = courseInstance.get('courseID')
|
||||
courseLevels = []
|
||||
courseLevels = course.levels for course in classroom.get('courses') or [] when courseID.equals(course._id)
|
||||
classLanguage = classroom.get('aceConfig')?.language
|
||||
_.remove(courseLevels, (level) -> level.primerLanguage is classLanguage) if classLanguage
|
||||
|
||||
# Get level completions and playtime
|
||||
currentLevelSession = null
|
||||
|
|
|
@ -90,8 +90,15 @@ describe 'POST /db/classroom', ->
|
|||
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSONC})
|
||||
expect(res.statusCode).toBe(200)
|
||||
@levelC = yield Level.findById(res.body._id)
|
||||
levelJSONJSPrimer1 = { name: 'JS Primer 1', permissions: [{access: 'owner', target: admin.id}], type: 'hero', primerLanguage: 'javascript' }
|
||||
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSONJSPrimer1})
|
||||
expect(res.statusCode).toBe(200)
|
||||
@levelJSPrimer1 = yield Level.findById(res.body._id)
|
||||
|
||||
campaignJSON = { name: 'Campaign', levels: {} }
|
||||
paredLevelJSPrimer1 = _.pick(@levelJSPrimer1.toObject(), 'name', 'original', 'type', 'slug', 'primerLanguage')
|
||||
paredLevelJSPrimer1.campaignIndex = 3
|
||||
campaignJSON.levels[@levelJSPrimer1.get('original').toString()] = paredLevelJSPrimer1
|
||||
paredLevelC = _.pick(@levelC.toObject(), 'name', 'original', 'type', 'slug', 'practice')
|
||||
paredLevelC.campaignIndex = 2
|
||||
campaignJSON.levels[@levelC.get('original').toString()] = paredLevelC
|
||||
|
@ -134,17 +141,42 @@ describe 'POST /db/classroom', ->
|
|||
expect(res.statusCode).toBe(403)
|
||||
done()
|
||||
|
||||
it 'makes a copy of the list of all levels in all courses', utils.wrap (done) ->
|
||||
teacher = yield utils.initUser({role: 'teacher'})
|
||||
yield utils.loginUser(teacher)
|
||||
data = { name: 'Classroom 2' }
|
||||
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||
classroom = yield Classroom.findById(res.body._id)
|
||||
expect(classroom.get('courses')[0].levels[0].original.toString()).toBe(@levelA.get('original').toString())
|
||||
expect(classroom.get('courses')[0].levels[0].type).toBe('course')
|
||||
expect(classroom.get('courses')[0].levels[0].slug).toBe('level-a')
|
||||
expect(classroom.get('courses')[0].levels[0].name).toBe('Level A')
|
||||
done()
|
||||
describe 'when javascript classroom', ->
|
||||
|
||||
beforeEach utils.wrap (done) ->
|
||||
teacher = yield utils.initUser({role: 'teacher'})
|
||||
yield utils.loginUser(teacher)
|
||||
data = { name: 'Classroom 2', aceConfig: { language: 'javascript' } }
|
||||
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||
@classroom = yield Classroom.findById(res.body._id)
|
||||
done()
|
||||
|
||||
it 'makes a copy of the list of all levels in all courses', utils.wrap (done) ->
|
||||
expect(@classroom.get('courses')[0].levels.length).toEqual(3)
|
||||
expect(@classroom.get('courses')[0].levels[0].original.toString()).toBe(@levelA.get('original').toString())
|
||||
expect(@classroom.get('courses')[0].levels[0].type).toBe('course')
|
||||
expect(@classroom.get('courses')[0].levels[0].slug).toBe('level-a')
|
||||
expect(@classroom.get('courses')[0].levels[0].name).toBe('Level A')
|
||||
done()
|
||||
|
||||
describe 'when python classroom', ->
|
||||
|
||||
beforeEach utils.wrap (done) ->
|
||||
teacher = yield utils.initUser({role: 'teacher'})
|
||||
yield utils.loginUser(teacher)
|
||||
data = { name: 'Classroom 2', aceConfig: { language: 'python' } }
|
||||
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||
@classroom = yield Classroom.findById(res.body._id)
|
||||
done()
|
||||
|
||||
it 'makes a copy all levels in all courses', utils.wrap (done) ->
|
||||
expect(@classroom.get('courses')[0].levels.length).toEqual(4)
|
||||
expect(@classroom.get('courses')[0].levels[0].original.toString()).toBe(@levelA.get('original').toString())
|
||||
expect(@classroom.get('courses')[0].levels[0].type).toBe('course')
|
||||
expect(@classroom.get('courses')[0].levels[0].slug).toBe('level-a')
|
||||
expect(@classroom.get('courses')[0].levels[0].name).toBe('Level A')
|
||||
done()
|
||||
|
||||
|
||||
describe 'when there are unreleased courses', ->
|
||||
beforeEach utils.wrap (done) ->
|
||||
|
@ -203,40 +235,7 @@ describe 'GET /db/classroom/:handle/levels', ->
|
|||
yield utils.clearModels [User, Classroom, Course, Level, Campaign]
|
||||
admin = yield utils.initAdmin()
|
||||
yield utils.loginUser(admin)
|
||||
levelJSON = { name: 'King\'s Peak 3', permissions: [{access: 'owner', target: admin.id}], type: 'course' }
|
||||
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
|
||||
expect(res.statusCode).toBe(200)
|
||||
@level = yield Level.findById(res.body._id)
|
||||
campaignJSON = { name: 'Campaign', levels: {} }
|
||||
paredLevel = _.pick(res.body, 'name', 'original', 'type')
|
||||
campaignJSON.levels[res.body.original] = paredLevel
|
||||
[res, body] = yield request.postAsync({uri: getURL('/db/campaign'), json: campaignJSON})
|
||||
@campaign = yield Campaign.findById(res.body._id)
|
||||
@course = Course({name: 'Course', campaignID: @campaign._id, releasePhase: 'released'})
|
||||
yield @course.save()
|
||||
teacher = yield utils.initUser({role: 'teacher'})
|
||||
yield utils.loginUser(teacher)
|
||||
data = { name: 'Classroom 1' }
|
||||
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||
expect(res.statusCode).toBe(201)
|
||||
@classroom = yield Classroom.findById(res.body._id)
|
||||
done()
|
||||
|
||||
it 'returns all levels referenced in in the classroom\'s copy of course levels', utils.wrap (done) ->
|
||||
[res, body] = yield request.getAsync { uri: getURL("/db/classroom/#{@classroom.id}/levels"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
levels = res.body
|
||||
expect(levels.length).toBe(1)
|
||||
expect(levels[0].name).toBe("King's Peak 3")
|
||||
done()
|
||||
|
||||
describe 'GET /db/classroom/:handle/levels', ->
|
||||
|
||||
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)
|
||||
|
@ -248,7 +247,13 @@ describe 'GET /db/classroom/:handle/levels', ->
|
|||
expect(res.statusCode).toBe(200)
|
||||
@levelB = yield Level.findById(res.body._id)
|
||||
paredLevelB = _.pick(res.body, 'name', 'original', 'type')
|
||||
|
||||
|
||||
levelJSON = { name: 'JS Primer 1', permissions: [{access: 'owner', target: admin.id}], type: 'course', primerLanguage: 'javascript' }
|
||||
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
|
||||
expect(res.statusCode).toBe(200)
|
||||
@levelJSPrimer1 = yield Level.findById(res.body._id)
|
||||
paredLevelJSPrimer1 = _.pick(res.body, 'name', 'original', 'type')
|
||||
|
||||
campaignJSONA = { name: 'Campaign A', levels: {} }
|
||||
campaignJSONA.levels[paredLevelA.original] = paredLevelA
|
||||
[res, body] = yield request.postAsync({uri: getURL('/db/campaign'), json: campaignJSONA})
|
||||
|
@ -256,6 +261,7 @@ describe 'GET /db/classroom/:handle/levels', ->
|
|||
|
||||
campaignJSONB = { name: 'Campaign B', levels: {} }
|
||||
campaignJSONB.levels[paredLevelB.original] = paredLevelB
|
||||
campaignJSONB.levels[paredLevelJSPrimer1.original] = paredLevelJSPrimer1
|
||||
[res, body] = yield request.postAsync({uri: getURL('/db/campaign'), json: campaignJSONB})
|
||||
@campaignB = yield Campaign.findById(res.body._id)
|
||||
|
||||
|
@ -265,34 +271,70 @@ describe 'GET /db/classroom/:handle/levels', ->
|
|||
@courseB = Course({name: 'Course B', campaignID: @campaignB._id, releasePhase: 'released'})
|
||||
yield @courseB.save()
|
||||
|
||||
teacher = yield utils.initUser({role: 'teacher'})
|
||||
yield utils.loginUser(teacher)
|
||||
data = { name: 'Classroom 1' }
|
||||
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||
expect(res.statusCode).toBe(201)
|
||||
@classroom = yield Classroom.findById(res.body._id)
|
||||
done()
|
||||
|
||||
it 'returns all levels referenced in in the classroom\'s copy of course levels', utils.wrap (done) ->
|
||||
[res, body] = yield request.getAsync { uri: getURL("/db/classroom/#{@classroom.id}/levels"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
levels = res.body
|
||||
expect(levels.length).toBe(2)
|
||||
describe 'when javascript classroom', ->
|
||||
|
||||
[res, body] = yield request.getAsync { uri: getURL("/db/classroom/#{@classroom.id}/courses/#{@courseA.id}/levels"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
levels = res.body
|
||||
expect(levels.length).toBe(1)
|
||||
expect(levels[0].original).toBe(@levelA.get('original').toString())
|
||||
beforeEach utils.wrap (done) ->
|
||||
teacher = yield utils.initUser({role: 'teacher'})
|
||||
yield utils.loginUser(teacher)
|
||||
data = { name: 'Classroom 1', aceConfig: { language: 'javascript' } }
|
||||
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||
expect(res.statusCode).toBe(201)
|
||||
@classroom = yield Classroom.findById(res.body._id)
|
||||
done()
|
||||
|
||||
[res, body] = yield request.getAsync { uri: getURL("/db/classroom/#{@classroom.id}/courses/#{@courseB.id}/levels"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
levels = res.body
|
||||
expect(levels.length).toBe(1)
|
||||
expect(levels[0].original).toBe(@levelB.get('original').toString())
|
||||
|
||||
done()
|
||||
it 'returns all levels referenced in in the classroom\'s copy of course levels', utils.wrap (done) ->
|
||||
[res, body] = yield request.getAsync { uri: getURL("/db/classroom/#{@classroom.id}/levels"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
levels = res.body
|
||||
expect(levels.length).toBe(2)
|
||||
|
||||
[res, body] = yield request.getAsync { uri: getURL("/db/classroom/#{@classroom.id}/courses/#{@courseA.id}/levels"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
levels = res.body
|
||||
expect(levels.length).toBe(1)
|
||||
expect(levels[0].original).toBe(@levelA.get('original').toString())
|
||||
|
||||
[res, body] = yield request.getAsync { uri: getURL("/db/classroom/#{@classroom.id}/courses/#{@courseB.id}/levels"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
levels = res.body
|
||||
expect(levels.length).toBe(1)
|
||||
expect(levels[0].original).toBe(@levelB.get('original').toString())
|
||||
|
||||
done()
|
||||
|
||||
describe 'when python classroom', ->
|
||||
|
||||
beforeEach utils.wrap (done) ->
|
||||
teacher = yield utils.initUser({role: 'teacher'})
|
||||
yield utils.loginUser(teacher)
|
||||
data = { name: 'Classroom 1', aceConfig: { language: 'python' } }
|
||||
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||
expect(res.statusCode).toBe(201)
|
||||
@classroom = yield Classroom.findById(res.body._id)
|
||||
done()
|
||||
|
||||
it 'returns all levels referenced in in the classroom\'s copy of course levels', utils.wrap (done) ->
|
||||
[res, body] = yield request.getAsync { uri: getURL("/db/classroom/#{@classroom.id}/levels"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
levels = res.body
|
||||
expect(levels.length).toBe(3)
|
||||
|
||||
[res, body] = yield request.getAsync { uri: getURL("/db/classroom/#{@classroom.id}/courses/#{@courseA.id}/levels"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
levels = res.body
|
||||
expect(levels.length).toBe(1)
|
||||
expect(levels[0].original).toBe(@levelA.get('original').toString())
|
||||
|
||||
[res, body] = yield request.getAsync { uri: getURL("/db/classroom/#{@classroom.id}/courses/#{@courseB.id}/levels"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
levels = res.body
|
||||
expect(levels.length).toBe(2)
|
||||
expect(levels[0].original).toBe(@levelB.get('original').toString())
|
||||
expect(levels[1].original).toBe(@levelJSPrimer1.get('original').toString())
|
||||
|
||||
done()
|
||||
|
||||
describe 'PUT /db/classroom', ->
|
||||
|
||||
|
|
|
@ -246,7 +246,7 @@ describe 'GET /db/course_instance/:handle/levels/:levelOriginal/next', ->
|
|||
yield utils.clearModels [User, Classroom, Course, Level, Campaign]
|
||||
admin = yield utils.initAdmin()
|
||||
yield utils.loginUser(admin)
|
||||
teacher = yield utils.initUser({role: 'teacher'})
|
||||
@teacher = yield utils.initUser({role: 'teacher'})
|
||||
|
||||
levelJSON = { name: 'A', permissions: [{access: 'owner', target: admin.id}], type: 'course' }
|
||||
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
|
||||
|
@ -255,7 +255,7 @@ describe 'GET /db/course_instance/:handle/levels/:levelOriginal/next', ->
|
|||
paredLevelA = _.pick(res.body, 'name', 'original', 'type')
|
||||
|
||||
@sessionA = new LevelSession
|
||||
creator: teacher.id
|
||||
creator: @teacher.id
|
||||
level: original: @levelA.get('original').toString()
|
||||
permissions: simplePermissions
|
||||
state: complete: true
|
||||
|
@ -268,7 +268,7 @@ describe 'GET /db/course_instance/:handle/levels/:levelOriginal/next', ->
|
|||
paredLevelB = _.pick(res.body, 'name', 'original', 'type')
|
||||
|
||||
@sessionB = new LevelSession
|
||||
creator: teacher.id
|
||||
creator: @teacher.id
|
||||
level: original: @levelB.get('original').toString()
|
||||
permissions: simplePermissions
|
||||
yield @sessionB.save()
|
||||
|
@ -279,9 +279,22 @@ describe 'GET /db/course_instance/:handle/levels/:levelOriginal/next', ->
|
|||
@levelC = yield Level.findById(res.body._id)
|
||||
paredLevelC = _.pick(res.body, 'name', 'original', 'type')
|
||||
|
||||
levelJSON = { name: 'JS Primer 1', permissions: [{access: 'owner', target: admin.id}], type: 'course', primerLanguage: 'javascript' }
|
||||
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
|
||||
expect(res.statusCode).toBe(200)
|
||||
@levelJSPrimer1 = yield Level.findById(res.body._id)
|
||||
paredLevelJSPrimer1 = _.pick(res.body, 'name', 'original', 'primerLanguage', 'type')
|
||||
|
||||
@sessionJSPrimer1 = new LevelSession
|
||||
creator: @teacher.id
|
||||
level: original: @levelJSPrimer1.get('original').toString()
|
||||
permissions: simplePermissions
|
||||
yield @sessionJSPrimer1.save()
|
||||
|
||||
campaignJSONA = { name: 'Campaign A', levels: {} }
|
||||
campaignJSONA.levels[paredLevelA.original] = paredLevelA
|
||||
campaignJSONA.levels[paredLevelB.original] = paredLevelB
|
||||
campaignJSONA.levels[paredLevelJSPrimer1.original] = paredLevelJSPrimer1
|
||||
[res, body] = yield request.postAsync({uri: getURL('/db/campaign'), json: campaignJSONA})
|
||||
@campaignA = yield Campaign.findById(res.body._id)
|
||||
|
||||
|
@ -296,44 +309,87 @@ describe 'GET /db/course_instance/:handle/levels/:levelOriginal/next', ->
|
|||
@courseB = Course({name: 'Course B', campaignID: @campaignB._id, releasePhase: 'released'})
|
||||
yield @courseB.save()
|
||||
|
||||
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}/sessions/#{@sessionA.id}/next"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(res.body.original).toBe(@levelB.original.toString())
|
||||
done()
|
||||
describe 'when javascript classroom', ->
|
||||
beforeEach utils.wrap (done) ->
|
||||
yield utils.loginUser(@teacher)
|
||||
data = { name: 'Classroom 1', aceConfig: { language: 'javascript' } }
|
||||
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)
|
||||
|
||||
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}/sessions/#{@sessionB.id}/next"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(res.body).toEqual({})
|
||||
done()
|
||||
url = getURL('/db/course_instance')
|
||||
|
||||
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}/sessions/#{@sessionA.id}/next"), json: true }
|
||||
expect(res.statusCode).toBe(404)
|
||||
done()
|
||||
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}/sessions/#{@sessionA.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}/sessions/#{@sessionB.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}/sessions/#{@sessionA.id}/next"), json: true }
|
||||
expect(res.statusCode).toBe(404)
|
||||
done()
|
||||
|
||||
it 'returns 404 if the given level is no applicable primer level', utils.wrap (done) ->
|
||||
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course_instance/#{@courseInstanceA.id}/levels/#{@levelJSPrimer1.id}/sessions/#{@sessionJSPrimer1.id}/next"), json: true }
|
||||
expect(res.statusCode).toBe(404)
|
||||
done()
|
||||
|
||||
describe 'when python classroom', ->
|
||||
beforeEach utils.wrap (done) ->
|
||||
yield utils.loginUser(@teacher)
|
||||
data = { name: 'Classroom 1', aceConfig: { language: 'python' } }
|
||||
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/#{@levelB.id}/sessions/#{@sessionB.id}/next"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(res.body.original).toBe(@levelJSPrimer1.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/#{@levelJSPrimer1.id}/sessions/#{@sessionJSPrimer1.id}/next"), json: true }
|
||||
expect(res.statusCode).toBe(200)
|
||||
expect(res.body).toEqual({})
|
||||
done()
|
||||
|
||||
describe 'GET /db/course_instance/:handle/classroom', ->
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ module.exports = {
|
|||
break if not courseAttrs
|
||||
course ?= @makeCourse()
|
||||
levels ?= new Levels()
|
||||
courseAttrs.levels = (level.pick('_id', 'slug', 'name', 'original', 'type') for level in levels.models)
|
||||
courseAttrs.levels = (level.pick('_id', 'slug', 'name', 'original', 'primerLanguage', 'type') for level in levels.models)
|
||||
|
||||
# populate members
|
||||
if not attrs.members
|
||||
|
@ -111,6 +111,7 @@ module.exports = {
|
|||
original: level.get('original'),
|
||||
creator: creator.id,
|
||||
}, attrs)
|
||||
attrs.level.primerLanguage = level.get('primerLanguage') if level.get('primerLanguage')
|
||||
return new LevelSession(attrs)
|
||||
|
||||
makeCourseInstance: (attrs, sources={}) ->
|
||||
|
|
|
@ -41,107 +41,169 @@ describe 'TeacherClassView', ->
|
|||
factories.makeUser({name: 'Ebner'}, {prepaid: expired})
|
||||
])
|
||||
@levels = new Levels(_.times(2, -> factories.makeLevel({ concepts: ['basic_syntax', 'arguments', 'functions'] })))
|
||||
@classroom = factories.makeClassroom({}, { courses: @releasedCourses, members: @students, levels: [@levels, new Levels()] })
|
||||
@courseInstances = new CourseInstances([
|
||||
factories.makeCourseInstance({}, { course: @releasedCourses.first(), @classroom, members: @students })
|
||||
factories.makeCourseInstance({}, { course: @releasedCourses.last(), @classroom, members: @students })
|
||||
])
|
||||
@levels.push(factories.makeLevel({ concepts: ['basic_syntax', 'arguments', 'functions'], primerLanguage: 'javascript' }))
|
||||
|
||||
sessions = []
|
||||
@finishedStudent = @students.first()
|
||||
@unfinishedStudent = @students.last()
|
||||
for level in @levels.models
|
||||
_.defer done
|
||||
|
||||
describe 'when python classroom', ->
|
||||
beforeEach (done) ->
|
||||
@classroom = factories.makeClassroom({ aceConfig: { language: 'python' }}, { courses: @releasedCourses, members: @students, levels: [@levels, new Levels()] })
|
||||
@courseInstances = new CourseInstances([
|
||||
factories.makeCourseInstance({}, { course: @releasedCourses.first(), @classroom, members: @students })
|
||||
factories.makeCourseInstance({}, { course: @releasedCourses.last(), @classroom, members: @students })
|
||||
])
|
||||
|
||||
sessions = []
|
||||
@finishedStudent = @students.first()
|
||||
@unfinishedStudent = @students.last()
|
||||
for level in @levels.models
|
||||
sessions.push(factories.makeLevelSession(
|
||||
{state: {complete: true}, playtime: 60},
|
||||
{level, creator: @finishedStudent})
|
||||
)
|
||||
sessions.push(factories.makeLevelSession(
|
||||
{state: {complete: true}, playtime: 60},
|
||||
{level, creator: @finishedStudent})
|
||||
{level: @levels.first(), creator: @unfinishedStudent})
|
||||
)
|
||||
sessions.push(factories.makeLevelSession(
|
||||
{state: {complete: true}, playtime: 60},
|
||||
{level: @levels.first(), creator: @unfinishedStudent})
|
||||
)
|
||||
@levelSessions = new LevelSessions(sessions)
|
||||
|
||||
@view = new TeacherClassView({}, @courseInstances.first().id)
|
||||
@view.classroom.fakeRequests[0].respondWith({ status: 200, responseText: @classroom.stringify() })
|
||||
@view.courses.fakeRequests[0].respondWith({ status: 200, responseText: @courses.stringify() })
|
||||
@view.courseInstances.fakeRequests[0].respondWith({ status: 200, responseText: @courseInstances.stringify() })
|
||||
@view.students.fakeRequests[0].respondWith({ status: 200, responseText: @students.stringify() })
|
||||
@view.classroom.sessions.fakeRequests[0].respondWith({ status: 200, responseText: @levelSessions.stringify() })
|
||||
@view.levels.fakeRequests[0].respondWith({ status: 200, responseText: @levels.stringify() })
|
||||
|
||||
jasmine.demoEl(@view.$el)
|
||||
_.defer done
|
||||
|
||||
it 'has contents', ->
|
||||
expect(@view.$el.children().length).toBeGreaterThan(0)
|
||||
@levelSessions = new LevelSessions(sessions)
|
||||
|
||||
@view = new TeacherClassView({}, @courseInstances.first().id)
|
||||
@view.classroom.fakeRequests[0].respondWith({ status: 200, responseText: @classroom.stringify() })
|
||||
@view.courses.fakeRequests[0].respondWith({ status: 200, responseText: @courses.stringify() })
|
||||
@view.courseInstances.fakeRequests[0].respondWith({ status: 200, responseText: @courseInstances.stringify() })
|
||||
@view.students.fakeRequests[0].respondWith({ status: 200, responseText: @students.stringify() })
|
||||
@view.classroom.sessions.fakeRequests[0].respondWith({ status: 200, responseText: @levelSessions.stringify() })
|
||||
@view.levels.fakeRequests[0].respondWith({ status: 200, responseText: @levels.stringify() })
|
||||
|
||||
jasmine.demoEl(@view.$el)
|
||||
_.defer done
|
||||
|
||||
it 'has contents', ->
|
||||
expect(@view.$el.children().length).toBeGreaterThan(0)
|
||||
|
||||
# it "shows the classroom's name and description"
|
||||
# it "shows the classroom's join code"
|
||||
|
||||
# it "shows the classroom's name and description"
|
||||
# it "shows the classroom's join code"
|
||||
|
||||
describe 'the Students tab', ->
|
||||
describe 'the Students tab', ->
|
||||
beforeEach (done) ->
|
||||
@view.state.set('activeTab', '#students-tab')
|
||||
_.defer(done)
|
||||
|
||||
# it 'shows all of the students'
|
||||
# it 'sorts correctly by Name'
|
||||
# it 'sorts correctly by Progress'
|
||||
|
||||
describe 'bulk-assign controls', ->
|
||||
it 'shows alert when assigning course 2 to unenrolled students', (done) ->
|
||||
expect(@view.$('.cant-assign-to-unenrolled').hasClass('visible')).toBe(false)
|
||||
@view.$('.student-row .checkbox-flat').click()
|
||||
@view.$('.assign-to-selected-students').click()
|
||||
_.defer =>
|
||||
expect(@view.$('.cant-assign-to-unenrolled').hasClass('visible')).toBe(true)
|
||||
done()
|
||||
|
||||
it 'shows alert when assigning but no students are selected', (done) ->
|
||||
expect(@view.$('.no-students-selected').hasClass('visible')).toBe(false)
|
||||
@view.$('.assign-to-selected-students').click()
|
||||
_.defer =>
|
||||
expect(@view.$('.no-students-selected').hasClass('visible')).toBe(true)
|
||||
done()
|
||||
|
||||
# describe 'the Course Progress tab', ->
|
||||
# it 'shows the correct Course Overview progress'
|
||||
#
|
||||
# describe 'when viewing another course'
|
||||
# it 'still shows the correct Course Overview progress'
|
||||
#
|
||||
|
||||
describe 'the Enrollment Status tab', ->
|
||||
beforeEach ->
|
||||
@view.state.set('activeTab', '#enrollment-status-tab')
|
||||
|
||||
describe 'Enroll button', ->
|
||||
it 'calls enrollStudents with that user when clicked', ->
|
||||
spyOn(@view, 'enrollStudents')
|
||||
@view.$('.enroll-student-button:first').click()
|
||||
expect(@view.enrollStudents).toHaveBeenCalled()
|
||||
users = @view.enrollStudents.calls.argsFor(0)[0]
|
||||
expect(users.size()).toBe(1)
|
||||
expect(users.first().id).toBe(@view.students.first().id)
|
||||
|
||||
describe 'Export Student Progress (CSV) button', ->
|
||||
it 'downloads a CSV file', ->
|
||||
spyOn(window, 'open').and.callFake (encodedCSV) =>
|
||||
progressData = decodeURI(encodedCSV)
|
||||
CSVHeader = 'data:text\/csv;charset=utf-8,'
|
||||
expect(progressData).toMatch new RegExp('^' + CSVHeader)
|
||||
lines = progressData.slice(CSVHeader.length).split('\n')
|
||||
expect(lines.length).toBe(@students.length + 1)
|
||||
for line in lines
|
||||
simplerLine = line.replace(/"[^"]+"/g, '""')
|
||||
# Username,Email,Total Playtime, [CS1-? Playtime], Concepts
|
||||
expect(simplerLine.match(/[^,]+/g).length).toBe(3 + @releasedCourses.length + 1)
|
||||
if simplerLine.match new RegExp(@finishedStudent.get('email'))
|
||||
expect(simplerLine).toMatch /3 minutes,3 minutes,0/
|
||||
else if simplerLine.match new RegExp(@unfinishedStudent.get('email'))
|
||||
expect(simplerLine).toMatch /a minute,a minute,0/
|
||||
else if simplerLine.match /@/
|
||||
expect(simplerLine).toMatch /0,0,0/
|
||||
return true
|
||||
@view.$('.export-student-progress-btn').click()
|
||||
expect(window.open).toHaveBeenCalled()
|
||||
|
||||
describe 'when javascript classroom', ->
|
||||
beforeEach (done) ->
|
||||
@view.state.set('activeTab', '#students-tab')
|
||||
_.defer(done)
|
||||
@classroom = factories.makeClassroom({ aceConfig: { language: 'javascript' }}, { courses: @releasedCourses, members: @students, levels: [@levels, new Levels()]})
|
||||
@courseInstances = new CourseInstances([
|
||||
factories.makeCourseInstance({}, { course: @releasedCourses.first(), @classroom, members: @students })
|
||||
factories.makeCourseInstance({}, { course: @releasedCourses.last(), @classroom, members: @students })
|
||||
])
|
||||
|
||||
# it 'shows all of the students'
|
||||
# it 'sorts correctly by Name'
|
||||
# it 'sorts correctly by Progress'
|
||||
|
||||
describe 'bulk-assign controls', ->
|
||||
it 'shows alert when assigning course 2 to unenrolled students', (done) ->
|
||||
expect(@view.$('.cant-assign-to-unenrolled').hasClass('visible')).toBe(false)
|
||||
@view.$('.student-row .checkbox-flat').click()
|
||||
@view.$('.assign-to-selected-students').click()
|
||||
_.defer =>
|
||||
expect(@view.$('.cant-assign-to-unenrolled').hasClass('visible')).toBe(true)
|
||||
done()
|
||||
|
||||
it 'shows alert when assigning but no students are selected', (done) ->
|
||||
expect(@view.$('.no-students-selected').hasClass('visible')).toBe(false)
|
||||
@view.$('.assign-to-selected-students').click()
|
||||
_.defer =>
|
||||
expect(@view.$('.no-students-selected').hasClass('visible')).toBe(true)
|
||||
done()
|
||||
|
||||
# describe 'the Course Progress tab', ->
|
||||
# it 'shows the correct Course Overview progress'
|
||||
#
|
||||
# describe 'when viewing another course'
|
||||
# it 'still shows the correct Course Overview progress'
|
||||
#
|
||||
|
||||
describe 'the Enrollment Status tab', ->
|
||||
beforeEach ->
|
||||
@view.state.set('activeTab', '#enrollment-status-tab')
|
||||
|
||||
describe 'Enroll button', ->
|
||||
it 'calls enrollStudents with that user when clicked', ->
|
||||
spyOn(@view, 'enrollStudents')
|
||||
@view.$('.enroll-student-button:first').click()
|
||||
expect(@view.enrollStudents).toHaveBeenCalled()
|
||||
users = @view.enrollStudents.calls.argsFor(0)[0]
|
||||
expect(users.size()).toBe(1)
|
||||
expect(users.first().id).toBe(@view.students.first().id)
|
||||
sessions = []
|
||||
@finishedStudent = @students.first()
|
||||
@unfinishedStudent = @students.last()
|
||||
classLanguage = @classroom.get('aceConfig')?.language
|
||||
for level in @levels.models
|
||||
continue if classLanguage and classLanguage is level.get('primerLanguage')
|
||||
sessions.push(factories.makeLevelSession(
|
||||
{state: {complete: true}, playtime: 60},
|
||||
{level, creator: @finishedStudent})
|
||||
)
|
||||
sessions.push(factories.makeLevelSession(
|
||||
{state: {complete: true}, playtime: 60},
|
||||
{level: @levels.first(), creator: @unfinishedStudent})
|
||||
)
|
||||
@levelSessions = new LevelSessions(sessions)
|
||||
|
||||
describe 'Export Student Progress (CSV) button', ->
|
||||
it 'downloads a CSV file', ->
|
||||
spyOn(window, 'open').and.callFake (encodedCSV) =>
|
||||
progressData = decodeURI(encodedCSV)
|
||||
CSVHeader = 'data:text\/csv;charset=utf-8,'
|
||||
expect(progressData).toMatch new RegExp('^' + CSVHeader)
|
||||
lines = progressData.slice(CSVHeader.length).split('\n')
|
||||
expect(lines.length).toBe(@students.length + 1)
|
||||
for line in lines
|
||||
simplerLine = line.replace(/"[^"]+"/g, '""')
|
||||
# Username,Email,Total Playtime, [CS1-? Playtime], Concepts
|
||||
expect(simplerLine.match(/[^,]+/g).length).toBe(3 + @releasedCourses.length + 1)
|
||||
if simplerLine.match new RegExp(@finishedStudent.get('email'))
|
||||
expect(simplerLine).toMatch /2 minutes,2 minutes,0/
|
||||
else if simplerLine.match new RegExp(@unfinishedStudent.get('email'))
|
||||
expect(simplerLine).toMatch /a minute,a minute,0/
|
||||
else if simplerLine.match /@/
|
||||
expect(simplerLine).toMatch /0,0,0/
|
||||
return true
|
||||
@view.$('.export-student-progress-btn').click()
|
||||
expect(window.open).toHaveBeenCalled()
|
||||
@view = new TeacherClassView({}, @courseInstances.first().id)
|
||||
@view.classroom.fakeRequests[0].respondWith({ status: 200, responseText: @classroom.stringify() })
|
||||
@view.courses.fakeRequests[0].respondWith({ status: 200, responseText: @courses.stringify() })
|
||||
@view.courseInstances.fakeRequests[0].respondWith({ status: 200, responseText: @courseInstances.stringify() })
|
||||
@view.students.fakeRequests[0].respondWith({ status: 200, responseText: @students.stringify() })
|
||||
@view.classroom.sessions.fakeRequests[0].respondWith({ status: 200, responseText: @levelSessions.stringify() })
|
||||
@view.levels.fakeRequests[0].respondWith({ status: 200, responseText: @levels.stringify() })
|
||||
|
||||
jasmine.demoEl(@view.$el)
|
||||
_.defer done
|
||||
|
||||
describe 'Export Student Progress (CSV) button', ->
|
||||
it 'downloads a CSV file', ->
|
||||
spyOn(window, 'open').and.callFake (encodedCSV) =>
|
||||
progressData = decodeURI(encodedCSV)
|
||||
CSVHeader = 'data:text\/csv;charset=utf-8,'
|
||||
expect(progressData).toMatch new RegExp('^' + CSVHeader)
|
||||
lines = progressData.slice(CSVHeader.length).split('\n')
|
||||
expect(lines.length).toBe(@students.length + 1)
|
||||
for line in lines
|
||||
simplerLine = line.replace(/"[^"]+"/g, '""')
|
||||
# Username,Email,Total Playtime, [CS1-? Playtime], Concepts
|
||||
expect(simplerLine.match(/[^,]+/g).length).toBe(3 + @releasedCourses.length + 1)
|
||||
if simplerLine.match new RegExp(@finishedStudent.get('email'))
|
||||
expect(simplerLine).toMatch /2 minutes,2 minutes,0/
|
||||
else if simplerLine.match new RegExp(@unfinishedStudent.get('email'))
|
||||
expect(simplerLine).toMatch /a minute,a minute,0/
|
||||
else if simplerLine.match /@/
|
||||
expect(simplerLine).toMatch /0,0,0/
|
||||
return true
|
||||
@view.$('.export-student-progress-btn').click()
|
||||
expect(window.open).toHaveBeenCalled()
|
||||
|
|
Loading…
Reference in a new issue