diff --git a/app/core/Router.coffee b/app/core/Router.coffee index 560009438..0fa7425f6 100644 --- a/app/core/Router.coffee +++ b/app/core/Router.coffee @@ -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 diff --git a/app/core/initialize.coffee b/app/core/initialize.coffee index 0ca95d784..7acbc0cdc 100644 --- a/app/core/initialize.coffee +++ b/app/core/initialize.coffee @@ -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() diff --git a/app/locale/en.coffee b/app/locale/en.coffee index 2803f8649..23693bd5c 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -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 Student Account. 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." diff --git a/app/templates/teachers/convert-to-teacher-account-view.jade b/app/templates/teachers/convert-to-teacher-account-view.jade index f38042c7b..3feab53ac 100644 --- a/app/templates/teachers/convert-to-teacher-account-view.jade +++ b/app/templates/teachers/convert-to-teacher-account-view.jade @@ -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 diff --git a/app/templates/teachers/create-teacher-account-view.jade b/app/templates/teachers/create-teacher-account-view.jade index 0eac0c6b7..e7a4e940e 100644 --- a/app/templates/teachers/create-teacher-account-view.jade +++ b/app/templates/teachers/create-teacher-account-view.jade @@ -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 diff --git a/app/templates/teachers/request-quote-view.jade b/app/templates/teachers/request-quote-view.jade index 252eb51ab..255b67e67 100644 --- a/app/templates/teachers/request-quote-view.jade +++ b/app/templates/teachers/request-quote-view.jade @@ -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 diff --git a/app/views/teachers/ConvertToTeacherAccountView.coffee b/app/views/teachers/ConvertToTeacherAccountView.coffee index 69aff6459..a82be6956 100644 --- a/app/views/teachers/ConvertToTeacherAccountView.coffee +++ b/app/views/teachers/ConvertToTeacherAccountView.coffee @@ -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' } diff --git a/app/views/teachers/CreateTeacherAccountView.coffee b/app/views/teachers/CreateTeacherAccountView.coffee index b32f4d07d..0b80fe9b2 100644 --- a/app/views/teachers/CreateTeacherAccountView.coffee +++ b/app/views/teachers/CreateTeacherAccountView.coffee @@ -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' } diff --git a/app/views/teachers/RequestQuoteView.coffee b/app/views/teachers/RequestQuoteView.coffee index 14065b9f7..915694d22 100644 --- a/app/views/teachers/RequestQuoteView.coffee +++ b/app/views/teachers/RequestQuoteView.coffee @@ -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' } diff --git a/server/middleware/users.coffee b/server/middleware/users.coffee index c1add7f00..5de9ca422 100644 --- a/server/middleware/users.coffee +++ b/server/middleware/users.coffee @@ -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)) diff --git a/test/app/views/teachers/ConvertToTeacherAccountView.spec.coffee b/test/app/views/teachers/ConvertToTeacherAccountView.spec.coffee index 3a5647d9a..d853030c7 100644 --- a/test/app/views/teachers/ConvertToTeacherAccountView.spec.coffee +++ b/test/app/views/teachers/ConvertToTeacherAccountView.spec.coffee @@ -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() diff --git a/test/app/views/teachers/CreateTeacherAccountView.spec.coffee b/test/app/views/teachers/CreateTeacherAccountView.spec.coffee index 3607bd54f..ee8903535 100644 --- a/test/app/views/teachers/CreateTeacherAccountView.spec.coffee +++ b/test/app/views/teachers/CreateTeacherAccountView.spec.coffee @@ -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() diff --git a/test/app/views/teachers/RequestQuoteView.spec.coffee b/test/app/views/teachers/RequestQuoteView.spec.coffee index bca50dc4b..73d2c3386 100644 --- a/test/app/views/teachers/RequestQuoteView.spec.coffee +++ b/test/app/views/teachers/RequestQuoteView.spec.coffee @@ -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')