Refactor client tests to use factories instead of fixtures

This commit is contained in:
Scott Erickson 2016-05-03 10:38:20 -07:00
parent b70e9bbcfe
commit 63a11a75f4
8 changed files with 270 additions and 83 deletions

View file

@ -32,3 +32,5 @@ module.exports = class CocoCollection extends Backbone.Collection
@jqxhr
setProjection: (@project) ->
stringify: -> return JSON.stringify(@toJSON())

View file

@ -451,4 +451,6 @@ class CocoModel extends Backbone.Model
patches.fetch(options)
return patches
stringify: -> return JSON.stringify(@toJSON())
module.exports = CocoModel

146
test/app/factories.coffee Normal file
View file

@ -0,0 +1,146 @@
Level = require 'models/Level'
Course = require 'models/Course'
Courses = require 'collections/Courses'
User = require 'models/User'
Classroom = require 'models/Classroom'
LevelSession = require 'models/LevelSession'
CourseInstance = require 'models/CourseInstance'
Achievement = require 'models/Achievement'
EarnedAchievement = require 'models/EarnedAchievement'
ThangType = require 'models/ThangType'
Users = require 'collections/Users'
module.exports = {
makeCourse: (attrs, sources={}) ->
_id = _.uniqueId('course_')
attrs = _.extend({}, {
_id: _id
name: _.string.humanize(_id)
}, attrs)
attrs.campaignID ?= sources.campaign?.id or _.uniqueId('campaign_')
return new Course(attrs)
makeLevel: (attrs) ->
_id = _.uniqueId('level_')
attrs = _.extend({}, {
_id: _id
name: _.string.humanize(_id)
original: _id+'_original'
version:
major: 0
minor: 0
isLatestMajor: true
isLatestMinor: true
}, attrs)
return new Level(attrs)
makeUser: (attrs) ->
_id = _.uniqueId('user_')
attrs = _.extend({
_id: _id
permissions: []
email: _id+'@email.com'
anonymous: false
name: _.string.humanize(_id)
}, attrs)
return new User(attrs)
makeClassroom: (attrs, sources={}) ->
levels = sources.levels or [] # array of Levels collections
courses = sources.courses or new Courses()
members = sources.members or new Users()
_id = _.uniqueId('classroom_')
attrs = _.extend({}, {
_id: _id,
name: _.string.humanize(_id)
aceConfig: { language: 'python' }
}, attrs)
# populate courses
if not attrs.courses
courses = sources.courses or new Courses()
attrs.courses = (course.pick('_id') for course in courses.models)
# populate levels
for [courseAttrs, levels] in _.zip(attrs.courses, levels)
break if not courseAttrs
course ?= @makeCourse()
levels ?= new Levels()
courseAttrs.levels = (level.pick('_id', 'slug', 'name', 'original', 'type') for level in levels.models)
# populate members
if not attrs.members
members = members or new Users()
attrs.members = (member.id for member in members.models)
return new Classroom(attrs)
makeLevelSession: (attrs, sources={}) ->
level = sources.level or @makeLevel()
creator = sources.creator or @makeUser()
attrs = _.extend({}, {
level:
original: level.get('original'),
creator: creator.id,
}, attrs)
return new LevelSession(attrs)
makeCourseInstance: (attrs, sources={}) ->
_id = _.uniqueId('course_instance_')
course = sources.course or @makeCourse()
classroom = sources.classroom or @makeClassroom()
owner = sources.owner or @makeUser()
members = sources.members or new Users()
attrs = _.extend({}, {
_id
courseID: course.id
classroomID: classroom.id
ownerID: owner.id
members: members.pluck('_id')
}, attrs)
return new CourseInstance(attrs)
makeLevelCompleteAchievement: (attrs, sources={}) ->
_id = _.uniqueId('achievement_')
level = sources.level or @makeLevel()
attrs = _.extend({}, {
_id
name: _.string.humanize(_id)
query: {
'state.complete': true,
'level.original': level.get('original')
}
rewards: { gems: 10 }
worth: 20
}, attrs)
return new Achievement(attrs)
makeEarnedAchievement: (attrs, sources={}) ->
_id = _.uniqueId('earned_achievement_')
achievement = sources.achievement or @makeLevelCompleteAchievement()
user = sources.user or @makeUser()
attrs = _.extend({}, {
_id,
"achievement": achievement.id,
"user": user.id,
"earnedRewards": _.clone(achievement.get('rewards')),
"earnedPoints": achievement.get('worth'),
"achievementName": achievement.get('name'),
"notified": true
}, attrs)
return new EarnedAchievement(attrs)
makeThangType: (attrs) ->
_id = _.uniqueId('thang_type_')
attrs = _.extend({}, {
_id
name: _.string.humanize(_id)
}, attrs)
return new ThangType(attrs)
}

View file

@ -4,55 +4,64 @@ Users = require 'collections/Users'
Courses = require 'collections/Courses'
CourseInstances = require 'collections/CourseInstances'
Classrooms = require 'collections/Classrooms'
Levels = require 'collections/Levels'
LevelSessions = require 'collections/LevelSessions'
factories = require 'test/app/factories'
# These got broken by changes to fixtures :(
describe 'CoursesHelper', ->
describe 'calculateAllProgress', ->
beforeEach ->
# classrooms, courses, campaigns, courseInstances, students
@classroom = require 'test/app/fixtures/classrooms/active-classroom'
@course = factories.makeCourse()
@courses = new Courses([@course])
@members = new Users(_.times(2, -> factories.makeUser()))
@levels = new Levels(_.times(2, -> factories.makeLevel()))
@classroom = factories.makeClassroom({}, { @courses, @members, levels: [@levels] })
@classrooms = new Classrooms([ @classroom ])
@courses = require 'test/app/fixtures/courses'
@course = @courses.models[0]
@campaigns = require 'test/app/fixtures/campaigns'
@campaign = @campaigns.models[0]
@students = require 'test/app/fixtures/students'
courseInstance = factories.makeCourseInstance({}, { @course, @classroom, @members })
@courseInstances = new CourseInstances([courseInstance])
describe 'when all students have completed a course', ->
beforeEach ->
@classroom.sessions = require 'test/app/fixtures/level-sessions-completed'
@courseInstances = require 'test/app/fixtures/course-instances'
sessions = []
for level in @levels.models
for creator in @members.models
sessions.push(factories.makeLevelSession({state: {complete: true}}, { level, creator }))
@classroom.sessions = new LevelSessions(sessions)
describe 'progressData.get({classroom, course})', ->
it 'returns object with .completed=true and .started=true', ->
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @students)
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @members)
console.log 'progress data?', progressData
progress = progressData.get {@classroom, @course}
expect(progress.completed).toBe true
expect(progress.started).toBe true
describe 'progressData.get({classroom, course, level, user})', ->
it 'returns object with .completed=true and .started=true', ->
for student in @students.models
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @students)
for student in @members.models
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @members)
progress = progressData.get {@classroom, @course, user: student}
expect(progress.completed).toBe true
expect(progress.started).toBe true
describe 'progressData.get({classroom, course, level, user})', ->
it 'returns object with .completed=true and .started=true', ->
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @students)
for level in @campaign.getLevels().models
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @members)
for level in @levels.models
progress = progressData.get {@classroom, @course, level}
expect(progress.completed).toBe true
expect(progress.started).toBe true
describe 'progressData.get({classroom, course, level, user})', ->
it 'returns object with .completed=true and .started=true', ->
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @students)
for level in @campaign.getLevels().models
for user in @students.models
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @members)
for level in @levels.models
for user in @members.models
progress = progressData.get {@classroom, @course, level, user}
expect(progress.completed).toBe true
expect(progress.started).toBe true
@ -60,60 +69,66 @@ describe 'CoursesHelper', ->
describe 'when NOT all students have completed a course', ->
beforeEach ->
@classroom.sessions = require 'test/app/fixtures/level-sessions-partially-completed'
@courseInstances = require 'test/app/fixtures/course-instances'
sessions = []
@finishedMember = @members.first()
@unfinishedMember = @members.last()
for level in @levels.models
sessions.push(factories.makeLevelSession(
{state: {complete: true}},
{level, creator: @finishedMember})
)
sessions.push(factories.makeLevelSession(
{state: {complete: false}},
{level: @levels.first(), creator: @unfinishedMember})
)
@classroom.sessions = new LevelSessions(sessions)
it 'progressData.get({classroom, course}) returns object with .completed=false', ->
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @students)
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @members)
progress = progressData.get {@classroom, @course}
expect(progress.completed).toBe false
describe 'when NOT all students have completed a level', ->
it 'progressData.get({classroom, course, level}) returns object with .completed=false and .started=true', ->
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @students)
for level in @campaign.getLevels().models
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @members)
for level in @levels.models
progress = progressData.get {@classroom, @course, level}
expect(progress.completed).toBe false
describe 'when the student has completed the course', ->
it 'progressData.get({classroom, course, user}) returns object with .completed=true and .started=true', ->
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @students)
student = @students.get('student0')
progress = progressData.get {@classroom, @course, user: student}
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @members)
progress = progressData.get {@classroom, @course, user: @finishedMember}
expect(progress.completed).toBe true
expect(progress.started).toBe true
describe 'when the student has NOT completed the course', ->
it 'progressData.get({classroom, course, user}) returns object with .completed=false and .started=true', ->
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @students)
student = @students.get('student1')
progress = progressData.get {@classroom, @course, user: student}
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @members)
progress = progressData.get {@classroom, @course, user: @unfinishedMember}
expect(progress.completed).toBe false
expect(progress.started).toBe true
describe 'when the student has completed the level', ->
it 'progressData.get({classroom, course, level, user}) returns object with .completed=true and .started=true', ->
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @students)
student = @students.get('student0')
for level in @campaign.getLevels().models
progress = progressData.get {@classroom, @course, level, user: student}
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @members)
for level in @levels.models
progress = progressData.get {@classroom, @course, level, user: @finishedMember}
expect(progress.completed).toBe true
expect(progress.started).toBe true
describe 'when the student has NOT completed the level but has started', ->
it 'progressData.get({classroom, course, level, user}) returns object with .completed=true and .started=true', ->
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @students)
user = @students.get('student2')
level = @campaign.getLevels().get('level0_0')
progress = progressData.get {@classroom, @course, level, user}
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @members)
level = @levels.first()
progress = progressData.get {@classroom, @course, level, user: @unfinishedMember}
expect(progress.completed).toBe false
expect(progress.started).toBe true
describe 'when the student has NOT started the level', ->
it 'progressData.get({classroom, course, level, user}) returns object with .completed=false and .started=false', ->
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @students)
user = @students.get('student3')
level = @campaign.getLevels().get('level0_0')
progress = progressData.get {@classroom, @course, level, user}
progressData = helper.calculateAllProgress(@classrooms, @courses, @courseInstances, @members)
level = @levels.last()
progress = progressData.get {@classroom, @course, level, user: @unfinishedMember}
expect(progress.completed).toBe false
expect(progress.started).toBe false

View file

@ -1,10 +1,10 @@
LadderTabView = require 'views/ladder/LadderTabView'
Level = require 'models/Level'
fixtures = require 'test/app/fixtures/levels'
factories = require 'test/app/factories'
describe 'LeaderboardData', ->
it 'triggers "sync" when its request is finished', ->
level = new Level(fixtures.LadderLevel)
level = factories.makeLevel()
leaderboard = new LadderTabView.LeaderboardData(level, 'humans', null, 4)
leaderboard.fetch()

View file

@ -1,12 +1,11 @@
Course = require 'models/Course'
Level = require 'models/Level'
LevelSession = require 'models/LevelSession'
Achievements = require 'collections/Achievements'
CourseVictoryModal = require 'views/play/level/modal/CourseVictoryModal'
fixtures = require './CourseVictoryModal.fixtures'
NewItemView = require 'views/play/level/modal/NewItemView'
ProgressView = require 'views/play/level/modal/ProgressView'
factories = require 'test/app/factories'
describe 'CourseVictoryModal', ->
beforeEach ->
@ -15,34 +14,33 @@ describe 'CourseVictoryModal', ->
it 'will eventually be the only victory modal'
makeViewOptions = ->
level = factories.makeLevel()
course = factories.makeCourse()
courseInstance = factories.makeCourseInstance()
{
course: new Course(fixtures.course)
level: new Level(fixtures.level)
session: new LevelSession(fixtures.session)
achievements: new Achievements(fixtures.achievements)
nextLevel: new Level(fixtures.nextLevel)
courseInstanceID: '56414c3868785b5f152424f1'
courseID: '560f1a9f22961295f9427742'
course: factories.makeCourse()
level: level
session: factories.makeLevelSession({ state: { complete: true } }, { level })
achievements: new Achievements([factories.makeLevelCompleteAchievement({}, {level: level})])
nextLevel: factories.makeLevel()
courseInstanceID: courseInstance.id
courseID: course.id
}
nextLevelRequest = null
handleRequests = ->
handleRequests = (modal) ->
requests = jasmine.Ajax.requests.all()
thangRequest = _.find(requests, (r) -> _.string.startsWith(r.url, '/db/thang.type'))
thangRequest?.respondWith({status: 200, responseText: JSON.stringify(fixtures.thangType)})
earnedAchievementRequests = _.where(requests, {url: '/db/earned_achievement'})
for [request, response] in _.zip(earnedAchievementRequests, fixtures.earnedAchievements)
request.respondWith({status: 200, responseText: JSON.stringify(response)})
sessionsRequest = _.findWhere(requests, {url: '/db/course_instance/56414c3868785b5f152424f1/my-course-level-sessions'})
sessionsRequest.respondWith({status: 200, responseText: JSON.stringify(fixtures.courseInstanceSessions)})
classroomRequest = _.findWhere(requests, {url: '/db/course_instance/56414c3868785b5f152424f1/classroom'})
classroomRequest.respondWith({status: 200, responseText: JSON.stringify(fixtures.campaign)}) # TODO: Fix this...
nextLevelRequest = _.findWhere(requests, {url: '/db/course_instance/56414c3868785b5f152424f1/levels/54173c90844506ae0195a0b4/next'})
thangRequest?.respondWith({status: 200, responseText: factories.makeThangType().stringify()})
modal.newEarnedAchievements[0].fakeRequests[0].respondWith({
status: 200, responseText: factories.makeEarnedAchievement().stringify()
})
modal.levelSessions.fakeRequests[0].respondWith({ status: 200, responseText: '[]' })
modal.classroom.fakeRequests[0].respondWith({
status: 200, responseText: factories.makeClassroom().stringify()
})
nextLevelRequest = modal.nextLevel.fakeRequests[0]
describe 'given a course level with a next level and no item or hero rewards', ->
modal = null
@ -50,8 +48,8 @@ describe 'CourseVictoryModal', ->
beforeEach (done) ->
options = makeViewOptions()
modal = new CourseVictoryModal(options)
handleRequests()
nextLevelRequest.respondWith({status: 200, responseText: JSON.stringify(fixtures.nextLevel)})
handleRequests(modal)
nextLevelRequest.respondWith({status: 200, responseText: factories.makeLevel().stringify()})
_.defer done
it 'only shows the ProgressView', ->
@ -84,7 +82,7 @@ describe 'CourseVictoryModal', ->
level.unset('nextLevel')
delete options.nextLevel
modal = new CourseVictoryModal(options)
handleRequests()
handleRequests(modal)
nextLevelRequest.respondWith({status: 404, responseText: '{}'})
_.defer done
@ -117,8 +115,8 @@ describe 'CourseVictoryModal', ->
achievement.set('rewards', rewards)
modal = new CourseVictoryModal(options)
handleRequests()
nextLevelRequest.respondWith({status: 200, responseText: JSON.stringify(fixtures.nextLevel)})
handleRequests(modal)
nextLevelRequest.respondWith({status: 200, responseText: factories.makeLevel().stringify()})
_.defer done
it 'includes a NewItemView when the level rewards a new item', ->

View file

@ -3,7 +3,6 @@ Course = require 'models/Course'
Level = require 'models/Level'
LevelSession = require 'models/LevelSession'
Achievements = require 'collections/Achievements'
fixtures = require './CourseVictoryModal.fixtures'
describe 'ShareProgressModal', ->
beforeEach ->

View file

@ -1,6 +1,12 @@
TeacherClassView = require 'views/courses/TeacherClassView'
storage = require 'core/storage'
forms = require 'core/forms'
factories = require 'test/app/factories'
Users = require 'collections/Users'
Courses = require 'collections/Courses'
Levels = require 'collections/Levels'
LevelSessions = require 'collections/LevelSessions'
CourseInstances = require 'collections/CourseInstances'
describe '/teachers/classes/:handle', ->
@ -16,19 +22,38 @@ describe 'TeacherClassView', ->
describe 'when logged in', ->
beforeEach (done) ->
me = require 'test/app/fixtures/teacher'
@classroom = require 'test/app/fixtures/classrooms/active-classroom'
@students = require 'test/app/fixtures/students'
@courses = require 'test/app/fixtures/courses'
@courseInstances = require 'test/app/fixtures/course-instances'
@levelSessions = require 'test/app/fixtures/level-sessions-partially-completed'
me = factories.makeUser({})
@courses = new Courses([factories.makeCourse({name: 'First Course'}), factories.makeCourse({name: 'Second Course'})])
@students = new Users(_.times(2, -> factories.makeUser()))
@levels = new Levels(_.times(2, -> factories.makeLevel()))
@classroom = factories.makeClassroom({}, { @courses, members: @students, levels: [@levels, new Levels()] })
@courseInstances = new CourseInstances([
factories.makeCourseInstance({}, { course: @courses.first(), @classroom, members: @students })
factories.makeCourseInstance({}, { course: @courses.last(), @classroom, members: @students })
])
sessions = []
@finishedStudent = @students.first()
@unfinishedStudent = @students.last()
for level in @levels.models
sessions.push(factories.makeLevelSession(
{state: {complete: true}},
{level, creator: @finishedStudent})
)
sessions.push(factories.makeLevelSession(
{state: {complete: false}},
{level: @levels.first(), creator: @unfinishedStudent})
)
@levelSessions = new LevelSessions(sessions)
@view = new TeacherClassView({}, @courseInstances.first().id)
@view.classroom.fakeRequests.forEach (r, index) => r.respondWith({ status: 200, responseText: JSON.stringify(@classroom) })
@view.courses.fakeRequests.forEach (r, index) => r.respondWith({ status: 200, responseText: JSON.stringify(@courses) })
@view.courseInstances.fakeRequests.forEach (r, index) => r.respondWith({ status: 200, responseText: JSON.stringify(@courseInstances) })
@view.students.fakeRequests.forEach (r, index) => r.respondWith({ status: 200, responseText: JSON.stringify(@students) })
@view.classroom.sessions.fakeRequests.forEach (r, index) => r.respondWith({ status: 200, responseText: JSON.stringify(@levelSessions) })
@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