Update teacher trial request views, some related bug fixes

* Tweak wording, options
* Instead of storing changes in local storage, warn when users may lose changes by navigating away
* Fix CreateTeacherAccountView so that if you connect to an existing account, the redirect is triggered
* Fix users.coffee weird race condition
This commit is contained in:
Scott Erickson 2016-04-19 10:20:56 -07:00
parent 67686e8c71
commit a452f1ce47
13 changed files with 205 additions and 111 deletions

View file

@ -158,6 +158,12 @@ module.exports = class CocoRouter extends Backbone.Router
return @routeDirectly('teachers/RestrictedToTeachersView')
if options.studentsOnly and me.isTeacher()
return @routeDirectly('courses/RestrictedToStudentsView')
leavingMessage = _.result(window.currentView, 'onLeaveMessage')
if leavingMessage
if not confirm(leavingMessage)
return @navigate(this.path, {replace: true})
else
window.currentView.onLeaveMessage = _.noop # to stop repeat confirm calls
path = 'play/CampaignView' if window.serverConfig.picoCTF and not /^(views)?\/?play/.test(path)
path = "views/#{path}" if not _.string.startsWith(path, 'views/')
@ -185,6 +191,7 @@ module.exports = class CocoRouter extends Backbone.Router
@activateTab()
view.afterInsert()
view.didReappear()
@path = document.location.pathname + document.location.search
closeCurrentView: ->
if window.currentView?.reloadOnClose

View file

@ -159,4 +159,11 @@ window.serializeForIOS = serializeForIOS = (obj, depth=3) ->
seen = null if root
clone
window.onbeforeunload = (e) ->
leavingMessage = _.result(window.currentView, 'onLeaveMessage')
if leavingMessage
return leavingMessage
else
return
$ -> init()

View file

@ -773,14 +773,20 @@
email_exists: "User exists with this email."
phone_number: "Phone number"
phone_number_help: "Where can we reach you during the workday?"
role_label: "Your role"
role_help: "Select your primary role."
primary_role_label: "Your Primary Role"
role_default: "Select Role"
primary_role_default: "Select Primary Role"
purchaser_role_default: "Select Purchaser Role"
tech_coordinator: "Technology coordinator"
advisor: "Advisor"
principal: "Principal"
superintendent: "Superintendent"
parent: "Parent"
purchaser_role_label: "Your Purchaser Role"
influence_advocate: "Influence/Advocate"
evaluate_recommend: "Evaluate/Recommend"
approve_funds: "Approve Funds"
no_purchaser_role: "No role in purchase decisions"
organization_label: "Name of School/District"
city: "City"
state: "State"
@ -801,6 +807,7 @@
finish_signup: "Finish creating your teacher account:"
finish_signup_p: "Create an account to set up a class, add your students, and monitor their progress as they learn computer science."
signup_with: "Sign up with:"
connect_with: "Connect with:"
conversion_warning: "WARNING: Your current account is a <em>Student Account</em>. Once you submit this form, your account will be updated to a Teacher Account."
learn_more_modal: "Teacher accounts on CodeCombat have the ability to monitor student progress, assign enrollments and manage classrooms. Teacher accounts cannot be a part of a classroom - if you are currently enrolled in a class using this account, you will no longer be able to access it once you update to a Teacher Account."
create_account: "Create a Teacher Account"
@ -1021,7 +1028,8 @@
buy_course1: "Buy this course"
select_all_courses: "Select 'All Courses' for a 50% discount!"
all_courses: "All Courses"
number_students: "Number of students"
number_programming_students: "Number of Programming Students"
number_total_students: "Total Students in School/District"
enter_number_students: "Enter the number of students you need for this class."
name_class: "Name your class"
displayed_course_page: "This will be displayed on the course page for you and your students. It can be changed later."

View file

@ -59,7 +59,7 @@ block content
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.role_label")
label.control-label(data-i18n="teachers_quote.primary_role_label")
.help-block.small
em.text-info(data-i18n="teachers_quote.role_help")
select.form-control(name="role")
@ -98,7 +98,7 @@ block content
.row.m-y-2
.col-md-offset-2.col-md-4
.form-group
label.control-label(data-i18n="courses.number_students")
label.control-label(data-i18n="courses.number_programming_students")
.help-block.small
em.text-info(data-i18n="teachers_quote.num_students_help")
select.form-control(name="numStudents")
@ -111,6 +111,19 @@ block content
option 501-1000
option 1000+
.col-md-4.col-sm-6
.form-group
label.control-label
span(data-i18n="courses.number_total_students")
span.spl.text-muted(data-i18n="signup.optional")
select.form-control(name="numStudentsTotal")
option(data-i18n="teachers_quote.num_students_default", value='')
option 1-500
option 500-1,000
option 1,000-5,000
option 5,000-10,000
option 10,000+
.form-group
.row.m-y-2
.col-md-offset-2.col-md-10

View file

@ -12,12 +12,12 @@ block content
a.login-link(data-i18n="login.log_in")
#social-network-signups.m-y-2
button#facebook-signup-btn.btn.btn-facebook.btn-lg.m-x-1
span.spr(data-i18n="teachers_quote.signup_with")
span.spr(data-i18n="teachers_quote.connect_with")
| Facebook
img.m-l-1(src='/images/pages/community/logo_facebook.png')
button#gplus-signup-btn.btn.btn-gplus.btn-lg.spr
span.spr(data-i18n="teachers_quote.signup_with")
span.spr(data-i18n="teachers_quote.connect_with")
| G+
img.m-l-1(src='/images/pages/community/logo_g+.png')
@ -76,9 +76,7 @@ block content
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.role_label")
.help-block.small
em.text-info(data-i18n="teachers_quote.role_help")
label.control-label(data-i18n="teachers_quote.primary_role_label")
select.form-control(name="role")
option(data-i18n="teachers_quote.role_default", , value='')
option(data-i18n="courses.teacher", value="Teacher")
@ -115,7 +113,7 @@ block content
.row.m-y-2
.col-md-offset-2.col-md-4
.form-group
label.control-label(data-i18n="courses.number_students")
label.control-label(data-i18n="courses.number_programming_students")
.help-block.small
em.text-info(data-i18n="teachers_quote.num_students_help")
select.form-control(name="numStudents")
@ -128,6 +126,19 @@ block content
option 501-1000
option 1000+
.col-md-4.col-sm-6
.form-group
label.control-label
span(data-i18n="courses.number_total_students")
span.spl.text-muted(data-i18n="signup.optional")
select.form-control(name="numStudentsTotal")
option(data-i18n="teachers_quote.num_students_default", value='')
option 1-500
option 500-1,000
option 1,000-5,000
option 5,000-10,000
option 10,000+
.form-group
.row.m-y-2

View file

@ -36,13 +36,15 @@ block content
.row
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="general.username")
label.control-label
span(data-i18n="general.username")
span.spl.text-muted(data-i18n="signup.optional")
- var name = me.get('name') || '';
input.form-control(name="name" value=name, disabled=!!name)
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="general.email")
label.control-label(data-i18n="share_progress_modal.form_label")
- var email = me.get('email') || '';
input.form-control(name="email" value=email, disabled=!!email)
@ -59,6 +61,16 @@ block content
- var lastName = me.get('lastName') || '';
input.form-control(name="lastName" value=lastName, disabled=!!lastName)
if !me.isAnonymous()
.row
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label
span(data-i18n="teachers_quote.phone_number")
.help-block.small
em.text-info(data-i18n="teachers_quote.phone_number_help")
input.form-control(name="phoneNumber")
if me.isAnonymous()
.row
.col-md-offset-2.col-md-4.col-sm-6
@ -67,30 +79,38 @@ block content
- var email = me.get('email') || '';
input.form-control(name="email" type="email", value=email, disabled=!!email)
.col-md-4.col-sm-6
.form-group
label.control-label
span(data-i18n="teachers_quote.phone_number")
.help-block.small
em.text-info(data-i18n="teachers_quote.phone_number_help")
input.form-control(name="phoneNumber")
.row
.col-md-offset-2.col-md-4.col-sm-6
.form-group
label.control-label
span(data-i18n="teachers_quote.phone_number")
span.spl.text-muted(data-i18n="signup.optional")
.help-block.small
em.text-info(data-i18n="teachers_quote.phone_number_help")
input.form-control(name="phoneNumber")
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.role_label")
.help-block.small
em.text-info(data-i18n="teachers_quote.role_help")
label.control-label(data-i18n="teachers_quote.primary_role_label")
select.form-control(name="role")
option(data-i18n="teachers_quote.role_default", , value='')
option(data-i18n="teachers_quote.primary_role_default", , value='')
option(data-i18n="courses.teacher", value="Teacher")
option(data-i18n="teachers_quote.tech_coordinator", value="Technology coordinator")
option(data-i18n="teachers_quote.advisor", value="Advisor")
option(data-i18n="teachers_quote.principal", value="Principal")
option(data-i18n="teachers_quote.superintendent", value="Superintendent")
option(data-i18n="teachers_quote.parent", value="Parent")
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="teachers_quote.purchaser_role_label")
select.form-control(name="purchaserRole")
option(data-i18n="teachers_quote.purchaser_role_default", , value='')
option(data-i18n="teachers_quote.influence_advocate", value="Influence/Advocate")
option(data-i18n="teachers_quote.evaluate_recommend", value="Evaluate/Recommend")
option(data-i18n="teachers_quote.approve_funds", value="Approve Funds")
option(data-i18n="teachers_quote.no_purchaser_role", value="No role in purchase decisions")
#form-school-info
.row
.col-md-offset-2.col-md-4.col-sm-6
@ -118,7 +138,7 @@ block content
.row
.col-md-offset-2.col-md-4
.form-group
label.control-label(data-i18n="courses.number_students")
label.control-label(data-i18n="courses.number_programming_students")
.help-block.small
em.text-info(data-i18n="teachers_quote.num_students_help")
select.form-control(name="numStudents")
@ -130,6 +150,17 @@ block content
option 201-500
option 501-1000
option 1000+
.col-md-4.col-sm-6
.form-group
label.control-label(data-i18n="courses.number_total_students")
select.form-control(name="numStudentsTotal")
option(data-i18n="teachers_quote.num_students_default", value='')
option 1-500
option 500-1,000
option 1,000-5,000
option 5,000-10,000
option 10,000+
.form-group

View file

@ -3,7 +3,6 @@ forms = require 'core/forms'
TrialRequest = require 'models/TrialRequest'
TrialRequests = require 'collections/TrialRequests'
AuthModal = require 'views/core/AuthModal'
storage = require 'core/storage'
errors = require 'core/errors'
User = require 'models/User'
ConfirmModal = require 'views/editor/modal/ConfirmModal'
@ -35,6 +34,10 @@ module.exports = class ConvertToTeacherAccountView extends RootView
@trialRequests.fetchOwn()
@supermodel.trackCollection(@trialRequests)
onLeaveMessage: ->
if @formChanged
return 'Your account has not been updated! If you continue, your changes will be lost.'
invalidateNCES: ->
for key in NCES_KEYS
@$('input[name="nces_' + key + '"]').val ''
@ -58,13 +61,6 @@ module.exports = class ConvertToTeacherAccountView extends RootView
@$('#other-education-level-checkbox').attr('checked', !!otherLevel)
@$('#other-education-level-input').val(otherLevel)
# apply changes from local storage
obj = storage.load(FORM_KEY)
if obj
@$('#other-education-level-checkbox').attr('checked', obj.otherChecked)
@$('#other-education-level-input').val(obj.otherInput)
forms.objectToForm(@$('form'), obj, { overwriteExisting: true })
$("#organization-control").autocomplete({hint: false}, [
source: (query, callback) ->
algolia.schoolsIndex.search(query, { hitsPerPage: 5, aroundLatLngViaIP: false }).then (answer) ->
@ -88,14 +84,10 @@ module.exports = class ConvertToTeacherAccountView extends RootView
for key in NCES_KEYS
@$('input[name="nces_' + key + '"]').val suggestion[key]
@onChangeRequestForm()
@onChangeForm()
onChangeRequestForm: ->
# save changes to local storage
obj = forms.formToObject(@$('form'))
obj.otherChecked = @$('#other-education-level-checkbox').is(':checked')
obj.otherInput = @$('#other-education-level-input').val()
storage.save(FORM_KEY, obj, 10)
onChangeForm: ->
@formChanged = true
onSubmitForm: (e) ->
e.preventDefault()
@ -149,13 +141,15 @@ module.exports = class ConvertToTeacherAccountView extends RootView
errors.showNotyNetworkError(arguments...)
onTrialRequestSubmit: ->
@formChanged = false
me.setRole @trialRequest.get('properties').role.toLowerCase(), true
storage.remove(FORM_KEY)
application.router.navigate('/teachers/classes', {trigger: true})
formSchema = {
type: 'object'
required: ['firstName', 'lastName', 'organization', 'role', 'numStudents']
required: [
'firstName', 'lastName', 'organization', 'role', 'numStudents', 'city', 'state', 'country'
]
properties:
firstName: { type: 'string' }
lastName: { type: 'string' }
@ -166,6 +160,7 @@ formSchema = {
state: { type: 'string' }
country: { type: 'string' }
numStudents: { type: 'string' }
numStudentsTotal: { type: 'string' }
educationLevel: {
type: 'array'
items: { type: 'string' }

View file

@ -3,7 +3,6 @@ forms = require 'core/forms'
TrialRequest = require 'models/TrialRequest'
TrialRequests = require 'collections/TrialRequests'
AuthModal = require 'views/core/AuthModal'
storage = require 'core/storage'
errors = require 'core/errors'
User = require 'models/User'
algolia = require 'core/services/algolia'
@ -33,6 +32,10 @@ module.exports = class CreateTeacherAccountView extends RootView
@trialRequests.fetchOwn()
@supermodel.trackCollection(@trialRequests)
onLeaveMessage: ->
if @formChanged
return 'Your account has not been created! If you continue, your changes will be lost.'
onLoaded: ->
if @trialRequests.size()
@trialRequest = @trialRequests.first()
@ -57,13 +60,6 @@ module.exports = class CreateTeacherAccountView extends RootView
@$('#other-education-level-checkbox').attr('checked', !!otherLevel)
@$('#other-education-level-input').val(otherLevel)
# apply changes from local storage
obj = storage.load(FORM_KEY)
if obj
@$('#other-education-level-checkbox').attr('checked', obj.otherChecked)
@$('#other-education-level-input').val(obj.otherInput)
forms.objectToForm(@$('form'), obj, { overwriteExisting: true })
$("#organization-control").autocomplete({hint: false}, [
source: (query, callback) ->
algolia.schoolsIndex.search(query, { hitsPerPage: 5, aroundLatLngViaIP: false }).then (answer) ->
@ -87,20 +83,14 @@ module.exports = class CreateTeacherAccountView extends RootView
for key in NCES_KEYS
@$('input[name="nces_' + key + '"]').val suggestion[key]
@onChangeRequestForm()
@onChangeForm()
onClickLoginLink: ->
modal = new AuthModal({ initialValues: { email: @trialRequest.get('properties')?.email } })
@openModalView(modal)
onChangeRequestForm: ->
# Local storage is being used to store the contents of the form whenever it changes,
# and filling in the stored values if the page is reloaded or navigated away from and returned to.
# save changes to local storage
obj = forms.formToObject(@$('form'))
obj.otherChecked = @$('#other-education-level-checkbox').is(':checked')
obj.otherInput = @$('#other-education-level-input').val()
storage.save(FORM_KEY, obj, 10)
onChangeForm: ->
@formChanged = true
onSubmitForm: (e) ->
e.preventDefault()
@ -168,7 +158,7 @@ module.exports = class CreateTeacherAccountView extends RootView
@openModalView(modal)
onTrialRequestSubmit: ->
storage.remove(FORM_KEY)
@formChanged = false
attrs = _.pick(forms.formToObject(@$('form')), 'name', 'email', 'role')
attrs.role = attrs.role.toLowerCase()
options = {}
@ -215,7 +205,7 @@ module.exports = class CreateTeacherAccountView extends RootView
success: =>
me.loginGPlusUser(@gplusAttrs.gplusID, {
success: ->
application.router.navigate('/teachers/update-account')
application.router.navigate('/teachers/update-account', {trigger: true})
error: errors.showNotyNetworkError
})
})
@ -224,6 +214,7 @@ module.exports = class CreateTeacherAccountView extends RootView
})
onGPlusConnected: ->
@formChanged = true
forms.objectToForm(@$('form'), @gplusAttrs)
for field in ['email', 'firstName', 'lastName']
input = @$("input[name='#{field}']")
@ -257,7 +248,7 @@ module.exports = class CreateTeacherAccountView extends RootView
success: =>
me.loginFacebookUser(@facebookAttrs.facebookID, {
success: ->
application.router.navigate('/teachers/update-account')
application.router.navigate('/teachers/update-account', {trigger: true})
error: errors.showNotyNetworkError
})
})
@ -266,6 +257,7 @@ module.exports = class CreateTeacherAccountView extends RootView
})
onFacebookConnected: ->
@formChanged = true
forms.objectToForm(@$('form'), @facebookAttrs)
for field in ['email', 'firstName', 'lastName']
input = @$("input[name='#{field}']")
@ -278,7 +270,10 @@ module.exports = class CreateTeacherAccountView extends RootView
formSchema = {
type: 'object'
required: ['firstName', 'lastName', 'email', 'organization', 'role', 'numStudents']
required: [
'firstName', 'lastName', 'email', 'organization', 'role', 'numStudents', 'city'
'state', 'country'
]
properties:
password1: { type: 'string' }
password2: { type: 'string' }
@ -293,6 +288,7 @@ formSchema = {
state: { type: 'string' }
country: { type: 'string' }
numStudents: { type: 'string' }
numStudentsTotal: { type: 'string' }
educationLevel: {
type: 'array'
items: { type: 'string' }

View file

@ -3,12 +3,10 @@ forms = require 'core/forms'
TrialRequest = require 'models/TrialRequest'
TrialRequests = require 'collections/TrialRequests'
AuthModal = require 'views/core/AuthModal'
storage = require 'core/storage'
errors = require 'core/errors'
ConfirmModal = require 'views/editor/modal/ConfirmModal'
algolia = require 'core/services/algolia'
FORM_KEY = 'request-quote-form'
SIGNUP_REDIRECT = '/teachers'
NCES_KEYS = ['id', 'name', 'district', 'district_id', 'district_schools', 'district_students', 'students', 'phone']
@ -35,6 +33,11 @@ module.exports = class RequestQuoteView extends RootView
@trialRequests = new TrialRequests()
@trialRequests.fetchOwn()
@supermodel.trackCollection(@trialRequests)
@formChanged = false
onLeaveMessage: ->
if @formChanged
return 'Your request has not been submitted! If you continue, your changes will be lost.'
onLoaded: ->
if @trialRequests.size()
@ -60,13 +63,6 @@ module.exports = class RequestQuoteView extends RootView
@$('#other-education-level-checkbox').attr('checked', !!otherLevel)
@$('#other-education-level-input').val(otherLevel)
# apply changes from local storage
obj = storage.load(FORM_KEY)
if obj
@$('#other-education-level-checkbox').attr('checked', obj.otherChecked)
@$('#other-education-level-input').val(obj.otherInput)
forms.objectToForm(@$('#request-form'), obj, { overwriteExisting: true })
$("#organization-control").autocomplete({hint: false}, [
source: (query, callback) ->
algolia.schoolsIndex.search(query, { hitsPerPage: 5, aroundLatLngViaIP: false }).then (answer) ->
@ -93,18 +89,14 @@ module.exports = class RequestQuoteView extends RootView
@onChangeRequestForm()
onChangeRequestForm: ->
# save changes to local storage
obj = forms.formToObject(@$('form'))
obj.otherChecked = @$('#other-education-level-checkbox').is(':checked')
obj.otherInput = @$('#other-education-level-input').val()
storage.save(FORM_KEY, obj, 10)
@formChanged = true
onSubmitRequestForm: (e) ->
e.preventDefault()
form = @$('#request-form')
attrs = forms.formToObject(form)
# custom other input logic (also used in form local storage save/restore)
# custom other input logic
if @$('#other-education-level-checkbox').is(':checked')
val = @$('#other-education-level-input').val()
attrs.educationLevel.push(val) if val
@ -138,7 +130,10 @@ module.exports = class RequestQuoteView extends RootView
decline: $.i18n.t('common.cancel')
})
@openModalView(modal)
modal.once 'confirm', @saveTrialRequest, @
modal.once('confirm', (->
modal.hide()
@saveTrialRequest()
), @)
else
@saveTrialRequest()
@ -166,10 +161,10 @@ module.exports = class RequestQuoteView extends RootView
@openModalView(modal)
onTrialRequestSubmit: ->
@formChanged = false
me.setRole @trialRequest.get('properties').role.toLowerCase(), true
defaultName = [@trialRequest.get('firstName'), @trialRequest.get('lastName')].join(' ')
@$('input[name="name"]').val(defaultName)
storage.remove(FORM_KEY)
@$('#request-form, #form-submit-success').toggleClass('hide')
@scrollToTop(0)
$('#flying-focus').css({top: 0, left: 0}) # Hack copied from Router.coffee#187. Ideally we'd swap out the view and have view-swapping logic handle this
@ -264,19 +259,23 @@ module.exports = class RequestQuoteView extends RootView
requestFormSchemaAnonymous = {
type: 'object'
required: ['firstName', 'lastName', 'email', 'organization', 'role', 'numStudents']
required: [
'firstName', 'lastName', 'email', 'organization', 'role', 'purchaserRole', 'numStudents',
'numStudentsTotal', 'phoneNumber', 'city', 'state', 'country']
properties:
firstName: { type: 'string' }
lastName: { type: 'string' }
name: { type: 'string', minLength: 1 }
name: { type: 'string' }
email: { type: 'string', format: 'email' }
phoneNumber: { type: 'string' }
role: { type: 'string' }
purchaserRole: { type: 'string' }
organization: { type: 'string' }
city: { type: 'string' }
state: { type: 'string' }
country: { type: 'string' }
numStudents: { type: 'string' }
numStudentsTotal: { type: 'string' }
educationLevel: {
type: 'array'
items: { type: 'string' }

View file

@ -14,7 +14,7 @@ module.exports =
fetchByGPlusID: wrap (req, res, next) ->
gpID = req.query.gplusID
gpAT = req.query.gplusAccessToken
next() unless gpID and gpAT
return next() unless gpID and gpAT
dbq = User.find()
dbq.select(parse.getProjectFromReq(req))
@ -29,7 +29,7 @@ module.exports =
fetchByFacebookID: wrap (req, res, next) ->
fbID = req.query.facebookID
fbAT = req.query.facebookAccessToken
next() unless fbID and fbAT
return next() unless fbID and fbAT
dbq = User.find()
dbq.select(parse.getProjectFromReq(req))

View file

@ -59,7 +59,18 @@ describe 'ConvertToTeacherAccountView (/teachers/update-account)', ->
afterEach (done) ->
_.defer(done) # let everything finish loading, keep navigate spied on
describe 'when the form is unchanged', ->
it 'does not prevent navigating away', ->
expect(_.result(view, 'onLeaveMessage')).toBeFalsy()
describe 'when the form has changed but is not submitted', ->
beforeEach ->
view.$el.find('form').trigger('change')
it 'prevents navigating away', ->
expect(_.result(view, 'onLeaveMessage')).toBeTruthy()
describe 'when the user already has a TrialRequest and is a teacher', ->
beforeEach (done) ->
spyOn(me, 'isTeacher').and.returnValue(true)
@ -121,11 +132,15 @@ describe 'ConvertToTeacherAccountView (/teachers/update-account)', ->
describe 'submitting the form', ->
beforeEach ->
view.$el.find('#request-form').trigger('change') # to confirm navigating away isn't prevented
_.last(view.trialRequests.fakeRequests).respondWith({ status: 200, responseText: JSON.stringify('[]') })
form = view.$('form')
forms.objectToForm(form, successForm, {overwriteExisting: true})
form.submit()
it 'does not prevent navigating away', ->
expect(_.result(view, 'onLeaveMessage')).toBeFalsy()
it 'creates a new TrialRequest with the information', ->
request = _.last(view.trialRequest.fakeRequests)
expect(request).toBeTruthy()

View file

@ -1,5 +1,4 @@
CreateTeacherAccountView = require 'views/teachers/CreateTeacherAccountView'
storage = require 'core/storage'
forms = require 'core/forms'
describe '/teachers/signup', ->
@ -55,8 +54,6 @@ describe 'CreateTeacherAccountView', ->
view.render()
jasmine.demoEl(view.$el)
spyOn(storage, 'load').and.returnValue({ lastName: 'Saved Changes' })
request = jasmine.Ajax.requests.mostRecent()
request.respondWith({
status: 200
@ -70,6 +67,17 @@ describe 'CreateTeacherAccountView', ->
})
_.defer done # Let SuperModel finish
describe 'when the form is unchanged', ->
it 'does not prevent navigating away', ->
expect(_.result(view, 'onLeaveMessage')).toBeFalsy()
describe 'when the form has changed but is not submitted', ->
beforeEach ->
view.$el.find('form').trigger('change')
it 'prevents navigating away', ->
expect(_.result(view, 'onLeaveMessage')).toBeTruthy()
describe '"Log in" link', ->
it 'opens the log in modal', ->
@ -100,7 +108,7 @@ describe 'CreateTeacherAccountView', ->
request = jasmine.Ajax.requests.mostRecent()
expect(request.url).toBe('/auth/login-facebook')
describe 'when the user\s info is loaded', ->
describe 'when the user connects with Facebook and there isn\'t already an associated account', ->
beforeEach ->
request = jasmine.Ajax.requests.mostRecent()
request.respondWith({ status: 404, responseText: '{}' })
@ -156,7 +164,7 @@ describe 'CreateTeacherAccountView', ->
request = jasmine.Ajax.requests.mostRecent()
expect(request.url).toBe('/auth/login-gplus')
describe 'when the user\s info is loaded', ->
describe 'when the user connects with F+ and there isn\'t already an associated account', ->
beforeEach ->
request = jasmine.Ajax.requests.mostRecent()
request.respondWith({ status: 404, responseText: '{}' })
@ -194,9 +202,13 @@ describe 'CreateTeacherAccountView', ->
describe 'submitting the form successfully', ->
beforeEach ->
view.$el.find('#request-form').trigger('change') # to confirm navigating away isn't prevented
form = view.$('form')
forms.objectToForm(form, successForm)
form.submit()
it 'does not prevent navigating away', ->
expect(_.result(view, 'onLeaveMessage')).toBeFalsy()
it 'submits a trial request, which does not include "account" settings', ->
request = jasmine.Ajax.requests.mostRecent()

View file

@ -1,5 +1,4 @@
RequestQuoteView = require 'views/teachers/RequestQuoteView'
storage = require 'core/storage'
forms = require 'core/forms'
describe 'RequestQuoteView', ->
@ -17,11 +16,13 @@ describe 'RequestQuoteView', ->
state: 'AA'
country: 'asdf'
numStudents: '1-10'
numStudentsTotal: '10,000+'
purchaserRole: 'Approve Funds'
educationLevel: ['Middle']
}
isSubmitRequest = (r) -> _.string.startsWith(r.url, '/db/trial.request') and r.method is 'POST'
describe 'when user is anonymous and has an existing trial request', ->
beforeEach (done) ->
me.clear()
@ -60,8 +61,6 @@ describe 'RequestQuoteView', ->
view.render()
jasmine.demoEl(view.$el)
spyOn(storage, 'load').and.returnValue({ lastName: 'Saved Changes' })
request = jasmine.Ajax.requests.mostRecent()
request.respondWith({
status: 200
@ -78,18 +77,6 @@ describe 'RequestQuoteView', ->
it 'shows form with data from the most recent request', ->
expect(view.$('input[name="firstName"]').val()).toBe('First')
it 'prioritizes showing local, unsaved changes', ->
expect(view.$('input[name="lastName"]').val()).toBe('Saved Changes')
describe 'when the form changes', ->
it 'stores local, unsaved changes', ->
spyOn(storage, 'save')
view.$('input[name="firstName"]').val('Just Changed').change()
expect(storage.save).toHaveBeenCalled()
args = storage.save.calls.argsFor(0)
expect(args[1].firstName).toBe('Just Changed')
describe 'when a user is anonymous and does NOT have an existing trial request', ->
beforeEach (done) ->
me.clear()
@ -100,8 +87,6 @@ describe 'RequestQuoteView', ->
view.render()
jasmine.demoEl(view.$el)
spyOn(storage, 'load').and.returnValue({ lastName: 'Saved Changes' })
request = jasmine.Ajax.requests.mostRecent()
request.respondWith({
status: 200
@ -109,8 +94,20 @@ describe 'RequestQuoteView', ->
})
_.defer done # Let SuperModel finish
describe 'when the form is unchanged', ->
it 'does not prevent navigating away', ->
expect(_.result(view, 'onLeaveMessage')).toBeFalsy()
describe 'when the form has changed but is not submitted', ->
beforeEach ->
view.$el.find('#request-form').trigger('change')
it 'prevents navigating away', ->
expect(_.result(view, 'onLeaveMessage')).toBeTruthy()
describe 'on successful form submit', ->
beforeEach ->
view.$el.find('#request-form').trigger('change') # to confirm navigating away isn't prevented
forms.objectToForm(view.$el, successFormValues)
view.$('#request-form').submit()
@submitRequest = _.last(jasmine.Ajax.requests.filter(isSubmitRequest))
@ -119,6 +116,9 @@ describe 'RequestQuoteView', ->
responseText: JSON.stringify(_.extend({_id: 'a'}, successFormValues))
})
it 'does not prevent navigating away', ->
expect(_.result(view, 'onLeaveMessage')).toBeFalsy()
it 'creates a new trial request', ->
expect(@submitRequest).toBeTruthy()
expect(@submitRequest.method).toBe('POST')