Implement HeroSelectModal for demo flow

Add Campaign factory

First basic tests for HeroSelectModal in demo flow

Implement HeroSelectModal for demo flow

Improve tests

Disable empty test

Fix text inconsistency around 'me'

Just listen once

Add HeroSelectModal events test

Don't reuse destroyed modal

Fix inconsistent modal close behavior

Fix tests
This commit is contained in:
Phoenix Eliot 2016-08-15 12:43:28 -07:00
parent 7cd49b438b
commit fd45c9d473
8 changed files with 111 additions and 2 deletions

View file

@ -97,6 +97,7 @@ module.exports = TestView = class TestView extends RootView
jasmine.Ajax.install()
beforeEach ->
me.clear()
jasmine.Ajax.requests.reset()
Backbone.Mediator.init()
Backbone.Mediator.setValidationEnabled false

View file

@ -222,6 +222,7 @@ module.exports = class CocoView extends Backbone.View
window.currentModal = modalView
@getRootView().stopListeningToShortcuts(true)
Backbone.Mediator.publish 'modal:opened', {}
modalView
modalClosed: =>
visibleModal.willDisappear() if visibleModal

View file

@ -50,9 +50,13 @@ module.exports = class ModalView extends CocoView
$el = @$el.find('.modal-body') unless $el
super($el)
# TODO: Combine hide/onHidden such that backbone 'hide/hidden.bs.modal' events and our 'hide/hidden' events are more 1-to-1
# For example:
# pressing 'esc' or using `currentModal.hide()` triggers 'hide', 'hide.bs.modal', 'hidden', 'hidden.bs.modal'
# clicking outside the modal triggers 'hide.bs.modal', 'hidden', 'hidden.bs.modal' (but not 'hide')
hide: ->
@trigger 'hide'
@$el.removeClass('fade').modal 'hide'
@$el.removeClass('fade').modal 'hide' unless @destroyed
onHidden: ->
@trigger 'hidden'

View file

@ -9,6 +9,7 @@ User = require 'models/User'
CourseInstance = require 'models/CourseInstance'
RootView = require 'views/core/RootView'
template = require 'templates/courses/teacher-courses-view'
HeroSelectModal = require 'views/courses/HeroSelectModal'
module.exports = class TeacherCoursesView extends RootView
id: 'teacher-courses-view'
@ -66,4 +67,10 @@ module.exports = class TeacherCoursesView extends RootView
language = form.find('.language-select').val()
window.tracker?.trackEvent 'Classes Guides Play Level', category: 'Teachers', courseID: courseID, language: language, levelSlug: levelSlug, ['Mixpanel']
url = "/play/level/#{levelSlug}?course=#{courseID}&codeLanguage=#{language}"
application.router.navigate(url, { trigger: true })
firstLevelSlug = @campaigns.get(@courses.at(0).get('campaignID')).getLevels().at(0).get('slug')
if levelSlug is firstLevelSlug
@listenToOnce @openModalView(new HeroSelectModal()),
'hidden': ->
application.router.navigate(url, { trigger: true })
else
application.router.navigate(url, { trigger: true })

View file

@ -1,6 +1,7 @@
Level = require 'models/Level'
Course = require 'models/Course'
Courses = require 'collections/Courses'
Campaign = require 'models/Campaign'
User = require 'models/User'
Classroom = require 'models/Classroom'
LevelSession = require 'models/LevelSession'
@ -19,16 +20,34 @@ module.exports = {
_id: _id
name: _.string.humanize(_id)
releasePhase: 'released'
concepts: []
}, attrs)
attrs.campaignID ?= sources.campaign?.id or _.uniqueId('campaign_')
return new Course(attrs)
makeCampaign: (attrs, sources={}) ->
_id = _.uniqueId('campaign_')
attrs = _.extend({}, {
_id
name: _.string.humanize(_id)
levels: [@makeLevel(), @makeLevel()]
}, attrs)
if sources.levels
levelsMap = {}
sources.levels.each (level) ->
levelsMap[level.id] = level
attrs.levels = levelsMap
return new Campaign(attrs)
makeLevel: (attrs) ->
_id = _.uniqueId('level_')
attrs = _.extend({}, {
_id: _id
name: _.string.humanize(_id)
slug: _.string.dasherize(_id)
original: _id+'_original'
version:
major: 0

View file

@ -13,6 +13,7 @@ describe 'CoursesView', ->
describe 'Change Hero button', ->
beforeEach (done) ->
me.set(factories.makeUser({ role: 'student' }).attributes)
view = new CoursesView()
classrooms = new Classrooms([factories.makeClassroom()])
courseInstances = new CourseInstances([factories.makeCourseInstance()])

View file

@ -35,3 +35,16 @@ describe 'HeroSelectModal', ->
expect(request.method).toBe("PUT")
expect(JSON.parse(request.params).heroConfig?.thangType).toBe(hero2.get('original'))
done()
it 'triggers its events properly', (done) ->
spyOn(modal, 'trigger')
modal.render()
modal.$('.hero-option:nth-child(2)').click()
request = jasmine.Ajax.requests.mostRecent()
request.respondWith({ status: 200, responseText: me.attributes })
expect(modal.trigger).toHaveBeenCalled()
expect(modal.trigger.calls.argsFor(0)[0]).toBe('hero-select:success')
expect(modal.trigger).not.toHaveBeenCalledWith('hide')
modal.$('.select-hero-btn').click()
expect(modal.trigger).toHaveBeenCalledWith('hide')
done()

View file

@ -0,0 +1,63 @@
TeacherCoursesView = require 'views/courses/TeacherCoursesView'
HeroSelectModal = require 'views/courses/HeroSelectModal'
Classrooms = require 'collections/Classrooms'
Courses = require 'collections/Courses'
Campaigns = require 'collections/Campaigns'
Levels = require 'collections/Levels'
auth = require 'core/auth'
factories = require 'test/app/factories'
describe 'TeacherCoursesView', ->
modal = null
view = null
describe 'Play Level form', ->
beforeEach (done) ->
me.set(factories.makeUser({ role: 'teacher' }).attributes)
view = new TeacherCoursesView()
classrooms = new Classrooms([factories.makeClassroom()])
levels1 = new Levels([ factories.makeLevel({ name: 'Dungeons of Kithgard' }), factories.makeLevel(), factories.makeLevel() ])
levels2 = new Levels([ factories.makeLevel(), factories.makeLevel(), factories.makeLevel() ])
campaigns = new Campaigns([factories.makeCampaign({}, { levels: levels1 }), factories.makeCampaign({}, { levels: levels2 })])
courses = new Courses([factories.makeCourse({}, {campaign: campaigns.at(0)}), factories.makeCourse({}, {campaign: campaigns.at(1)})])
view.ownedClassrooms.fakeRequests[0].respondWith({ status: 200, responseText: classrooms.stringify() })
view.campaigns.fakeRequests[0].respondWith({ status: 200, responseText: campaigns.stringify() })
view.courses.fakeRequests[0].respondWith({ status: 200, responseText: courses.stringify() })
view.render()
done()
it 'opens HeroSelectModal for the first level of the first course', (done) ->
spyOn(view, 'openModalView').and.callFake (modal) -> modal
spyOn(application.router, 'navigate')
view.$('.play-level-button').first().click()
expect(view.openModalView).toHaveBeenCalled()
expect(application.router.navigate).not.toHaveBeenCalled()
args = view.openModalView.calls.argsFor(0)
modalView = args[0]
expect(modalView instanceof HeroSelectModal).toBe(true)
modalView.trigger('hero-select:success')
expect(application.router.navigate).not.toHaveBeenCalled()
modalView.trigger('hide')
modalView.trigger('hidden')
_.defer ->
expect(application.router.navigate).toHaveBeenCalled()
done()
it "doesn't open HeroSelectModal for other levels", ->
spyOn(view, 'openModalView')
spyOn(application.router, 'navigate')
secondLevelSlug = view.$('.level-select:first option:nth-child(2)').val()
view.$('.level-select').first().val(secondLevelSlug)
view.$('.play-level-button').first().click()
expect(view.openModalView).not.toHaveBeenCalled()
expect(application.router.navigate).toHaveBeenCalled()
it "doesn't open HeroSelectModal for other courses", ->
spyOn(view, 'openModalView')
spyOn(application.router, 'navigate')
view.$('.play-level-button').get(1).click()
expect(view.openModalView).not.toHaveBeenCalled()
expect(application.router.navigate).toHaveBeenCalled()
it "remembers the selected hero" # TODO