Commit a86c80bb by Renzo Lucioni

Merge pull request #5857 from edx/renzo/add-js-tests

Fix logistration Jasmine tests
parents d512fe86 f9ecd884
......@@ -728,10 +728,6 @@ class LoginSessionViewTest(ApiTestCase):
class PasswordResetViewTest(ApiTestCase):
"""Tests of the user API's password reset endpoint. """
USERNAME = "bob"
EMAIL = "bob@example.com"
PASSWORD = "password"
def setUp(self):
super(PasswordResetViewTest, self).setUp()
self.url = reverse("user_api_password_reset")
......
/*! url - v1.8.4 - 2013-08-14 */window.url=function(){function a(a){return!isNaN(parseFloat(a))&&isFinite(a)}return function(b,c){var d=c||window.location.toString();if(!b)return d;b=b.toString(),"//"===d.substring(0,2)?d="http:"+d:1===d.split("://").length&&(d="http://"+d),c=d.split("/");var e={auth:""},f=c[2].split("@");1===f.length?f=f[0].split(":"):(e.auth=f[0],f=f[1].split(":")),e.protocol=c[0],e.hostname=f[0],e.port=f[1]||"80",e.pathname=(c.length>3?"/":"")+c.slice(3,c.length).join("/").split("?")[0].split("#")[0];var g=e.pathname;"/"===g.charAt(g.length-1)&&(g=g.substring(0,g.length-1));var h=e.hostname,i=h.split("."),j=g.split("/");if("hostname"===b)return h;if("domain"===b)return i.slice(-2).join(".");if("sub"===b)return i.slice(0,i.length-2).join(".");if("port"===b)return e.port||"80";if("protocol"===b)return e.protocol.split(":")[0];if("auth"===b)return e.auth;if("user"===b)return e.auth.split(":")[0];if("pass"===b)return e.auth.split(":")[1]||"";if("path"===b)return e.pathname;if("."===b.charAt(0)){if(b=b.substring(1),a(b))return b=parseInt(b,10),i[0>b?i.length+b:b-1]||""}else{if(a(b))return b=parseInt(b,10),j[0>b?j.length+b:b]||"";if("file"===b)return j.slice(-1)[0];if("filename"===b)return j.slice(-1)[0].split(".")[0];if("fileext"===b)return j.slice(-1)[0].split(".")[1]||"";if("?"===b.charAt(0)||"#"===b.charAt(0)){var k=d,l=null;if("?"===b.charAt(0)?k=(k.split("?")[1]||"").split("#")[0]:"#"===b.charAt(0)&&(k=k.split("#")[1]||""),!b.charAt(1))return k;b=b.substring(1),k=k.split("&");for(var m=0,n=k.length;n>m;m++)if(l=k[m].split("="),l[0]===b)return l[1]||"";return null}}return""}}(),"undefined"!=typeof jQuery&&jQuery.extend({url:function(a,b){return window.url(a,b)}});
\ No newline at end of file
......@@ -23,6 +23,7 @@
'jquery.inputnumber': 'xmodule_js/common_static/js/vendor/html5-input-polyfills/number-polyfill',
'jquery.immediateDescendents': 'xmodule_js/common_static/coffee/src/jquery.immediateDescendents',
'jquery.simulate': 'xmodule_js/common_static/js/vendor/jquery.simulate',
'jquery.url': 'xmodule_js/common_static/js/vendor/url.min',
'datepair': 'xmodule_js/common_static/js/vendor/timepicker/datepair',
'date': 'xmodule_js/common_static/js/vendor/date',
'underscore': 'xmodule_js/common_static/js/vendor/underscore-min',
......@@ -43,7 +44,6 @@
'jasmine.async': 'xmodule_js/common_static/js/vendor/jasmine.async',
'draggabilly': 'xmodule_js/common_static/js/vendor/draggabilly.pkgd',
'domReady': 'xmodule_js/common_static/js/vendor/domReady',
'URI': 'xmodule_js/common_static/js/vendor/URI.min',
'mathjax': '//edx-static.s3.amazonaws.com/mathjax-MathJax-727332c/MathJax.js?config=TeX-MML-AM_HTMLorMML-full&delayStartupUntil=configured',
'youtube': '//www.youtube.com/player_api?noext',
'tender': '//edxedge.tenderapp.com/tender_widget',
......@@ -65,7 +65,17 @@
'js/views/cohort_editor': 'js/views/cohort_editor',
'js/views/cohorts': 'js/views/cohorts',
'js/views/notification': 'js/views/notification',
'js/models/notification': 'js/models/notification'
'js/models/notification': 'js/models/notification',
'js/student_account/account': 'js/student_account/account',
'js/student_account/views/FormView': 'js/student_account/views/FormView',
'js/student_account/models/LoginModel': 'js/student_account/models/LoginModel',
'js/student_account/views/LoginView': 'js/student_account/views/LoginView',
'js/student_account/models/PasswordResetModel': 'js/student_account/models/PasswordResetModel',
'js/student_account/views/PasswordResetView': 'js/student_account/views/PasswordResetView',
'js/student_account/models/RegisterModel': 'js/student_account/models/RegisterModel',
'js/student_account/views/RegisterView': 'js/student_account/views/RegisterView',
'js/student_account/views/AccessView': 'js/student_account/views/AccessView',
'js/student_profile/profile': 'js/student_profile/profile'
},
shim: {
'gettext': {
......@@ -133,11 +143,31 @@
deps: ['jquery', 'tinymce'],
exports: 'jQuery.fn.tinymce'
},
'jquery.url': {
deps: ['jquery'],
exports: 'jQuery.fn.url'
},
'datepair': {
deps: ['jquery.ui', 'jquery.timepicker']
},
'underscore': {
exports: '_'
deps: ['underscore.string'],
exports: '_',
init: function(UnderscoreString) {
/* Mix non-conflicting functions from underscore.string
* (all but include, contains, and reverse) into the
* Underscore namespace. This allows the login, register,
* and password reset templates to render independent of the
* access view.
*/
_.mixin(UnderscoreString.exports());
/* Since the access view is not using RequireJS, we also
* expose underscore.string at _.str, so that the access
* view can perform the mixin on its own.
*/
_.str = UnderscoreString;
}
},
'backbone': {
deps: ['underscore', 'jquery'],
......@@ -216,7 +246,6 @@
exports: 'js/student_account/account',
deps: ['jquery', 'underscore', 'backbone', 'gettext', 'jquery.cookie']
},
'js/student_profile/profile': {
exports: 'js/student_profile/profile',
deps: ['jquery', 'underscore', 'backbone', 'gettext', 'jquery.cookie']
......@@ -261,60 +290,76 @@
deps: ['backbone', 'jquery', 'underscore']
},
'js/student_account/enrollment_interface': {
exports: 'js/student_account/enrollment_interface',
deps: ['jquery', 'underscore', 'gettext']
exports: 'edx.student.account.EnrollmentInterface',
deps: ['jquery', 'jquery.cookie', 'underscore', 'gettext']
},
// Student account registration/login
// Loaded explicitly until these are converted to RequireJS
'js/student_account/views/FormView': {
exports: 'js/student_account/views/FormView',
exports: 'edx.student.account.FormView',
deps: ['jquery', 'underscore', 'backbone', 'gettext']
},
'js/student_account/models/LoginModel': {
exports: 'js/student_account/models/LoginModel',
deps: ['jquery', 'underscore', 'backbone', 'gettext', 'jquery.cookie']
exports: 'edx.student.account.LoginModel',
deps: ['jquery', 'jquery.cookie', 'backbone']
},
'js/student_account/views/LoginView': {
exports: 'js/student_account/views/LoginView',
exports: 'edx.student.account.LoginView',
deps: [
'jquery',
'jquery.url',
'underscore',
'gettext',
'js/student_account/models/LoginModel',
'js/student_account/views/FormView',
'underscore.string'
'js/student_account/views/FormView'
]
},
'js/student_account/models/PasswordResetModel': {
exports: 'js/student_account/models/PasswordResetModel',
deps: ['jquery', 'underscore', 'backbone', 'gettext', 'jquery.cookie']
exports: 'edx.student.account.PasswordResetModel',
deps: ['jquery', 'jquery.cookie', 'backbone']
},
'js/student_account/views/PasswordResetView': {
exports: 'js/student_account/views/PasswordResetView',
exports: 'edx.student.account.PasswordResetView',
deps: [
'jquery',
'underscore',
'gettext',
'js/student_account/models/PasswordResetModel',
'js/student_account/views/FormView'
]
},
'js/student_account/models/RegisterModel': {
exports: 'js/student_account/models/RegisterModel',
deps: ['jquery', 'underscore', 'backbone', 'gettext', 'jquery.cookie']
exports: 'edx.student.account.RegisterModel',
deps: ['jquery', 'jquery.cookie', 'backbone']
},
'js/student_account/views/RegisterView': {
exports: 'js/student_account/views/RegisterView',
exports: 'edx.student.account.RegisterView',
deps: [
'jquery',
'jquery.url',
'underscore',
'gettext',
'js/student_account/models/RegisterModel',
'js/student_account/views/FormView',
'underscore.string'
'js/student_account/views/FormView'
]
},
'js/student_account/views/AccessView': {
exports: 'js/student_account/views/AccessView',
exports: 'edx.student.account.AccessView',
deps: [
'jquery',
'underscore',
'backbone',
'gettext',
'js/student_account/views/LoginView',
'js/student_account/views/PasswordResetView',
'js/student_account/views/RegisterView',
'underscore.string'
'js/student_account/models/LoginModel',
'js/student_account/models/PasswordResetModel',
'js/student_account/models/RegisterModel',
'js/student_account/views/FormView'
]
}
},
}
});
// TODO: why do these need 'lms/include' at the front but the CMS equivalent logic doesn't?
......@@ -325,13 +370,13 @@
'lms/include/js/spec/staff_debug_actions_spec.js',
'lms/include/js/spec/views/notification_spec.js',
'lms/include/js/spec/dashboard/donation.js',
'lms/include/js/spec/student_account/account.js',
'lms/include/js/spec/student_account/account_spec.js',
'lms/include/js/spec/student_account/access_spec.js',
'lms/include/js/spec/student_account/login_spec.js',
'lms/include/js/spec/student_account/register_spec.js',
'lms/include/js/spec/student_account/password_reset_spec.js',
'lms/include/js/spec/student_account/enrollment_interface_spec.js',
'lms/include/js/spec/student_profile/profile.js',
'lms/include/js/spec/student_profile/profile_spec.js'
]);
}).call(this, requirejs, define);
define(['js/common_helpers/template_helpers', 'js/student_account/views/AccessView'],
function(TemplateHelpers, AccessView) {
define([
'jquery',
'js/common_helpers/template_helpers',
'js/common_helpers/ajax_helpers',
'js/student_account/views/AccessView',
'js/student_account/views/FormView'
], function($, TemplateHelpers, AjaxHelpers, AccessView) {
describe('edx.student.account.AccessView', function() {
'use strict';
var view = null,
ajaxSuccess = true;
var assertForms = function(visible, hidden) {
expect($(visible)).not.toHaveClass('hidden');
expect($(hidden)).toHaveClass('hidden');
expect($('#password-reset-wrapper')).toBeEmpty();
};
beforeEach(function() {
setFixtures("<div id='login-and-registration-container'></div>");
TemplateHelpers.installTemplate('templates/student_account/access');
TemplateHelpers.installTemplate('templates/student_account/login');
TemplateHelpers.installTemplate('templates/student_account/register');
TemplateHelpers.installTemplate('templates/student_account/password_reset');
TemplateHelpers.installTemplate('templates/student_account/form_field');
// Used to populate forms
var form_description = {
"method": "post",
"submit_url": "/submit",
"fields": [
var requests = null,
view = null,
AJAX_INFO = {
register: {
url: '/user_api/v1/account/registration/',
requestIndex: 1
},
login: {
url: '/user_api/v1/account/login_session/',
requestIndex: 0
},
password_reset: {
url: '/user_api/v1/account/password_reset/',
requestIndex: 1
}
},
FORM_DESCRIPTION = {
method: 'post',
submit_url: '/submit',
fields: [
{
"name": "email",
"label": "Email",
"default": "",
"type": "text",
"required": true,
"placeholder": "xsy@edx.org",
"instructions": "Enter your email here.",
"restrictions": {},
name: 'email',
label: 'Email',
defaultValue: '',
type: 'text',
required: true,
placeholder: 'xsy@edx.org',
instructions: 'Enter your email here.',
restrictions: {},
},
{
"name": "username",
"label": "Username",
"default": "",
"type": "text",
"required": true,
"placeholder": "Xsy",
"instructions": "Enter your username here.",
"restrictions": {
"max_length": 200
name: 'username',
label: 'Username',
defaultValue: '',
type: 'text',
required: true,
placeholder: 'Xsy',
instructions: 'Enter your username here.',
restrictions: {
max_length: 200
}
}
]
};
// Stub AJAX calls and force them to return a form description
spyOn($, 'ajax').andCallFake(function() {
return $.Deferred(function(defer) {
if (ajaxSuccess) {
defer.resolveWith(this, [form_description]);
} else {
defer.reject();
}
}).promise();
});
var ajaxAssertAndRespond = function(url, requestIndex) {
// Verify that the client contacts the server as expected
AjaxHelpers.expectJsonRequest(requests, 'GET', url, null, requestIndex);
/* Simulate a response from the server containing
/* a dummy form description
*/
AjaxHelpers.respondWithJson(requests, FORM_DESCRIPTION);
};
view = new edx.student.account.AccessView({
mode: 'login',
var ajaxSpyAndInitialize = function(that, mode) {
// Spy on AJAX requests
requests = AjaxHelpers.requests(that);
// Initialize the access view
view = new AccessView({
mode: mode,
thirdPartyAuth: {
currentProvider: null,
providers: []
}
},
platformName: 'edX'
});
ajaxAssertAndRespond(AJAX_INFO[mode].url);
};
var assertForms = function(visibleType, hiddenType) {
expect($(visibleType)).not.toHaveClass('hidden');
expect($(hiddenType)).toHaveClass('hidden');
expect($('#password-reset-wrapper')).toBeEmpty();
};
var selectForm = function(type) {
// Create a fake change event to control form toggling
var changeEvent = $.Event('change');
changeEvent.currentTarget = $('#' + type + '-option');
// Load form corresponding to the change event
view.toggleForm(changeEvent);
ajaxAssertAndRespond(AJAX_INFO[type].url, AJAX_INFO[type].requestIndex);
};
beforeEach(function() {
setFixtures('<div id="login-and-registration-container"></div>');
TemplateHelpers.installTemplate('templates/student_account/access');
TemplateHelpers.installTemplate('templates/student_account/login');
TemplateHelpers.installTemplate('templates/student_account/register');
TemplateHelpers.installTemplate('templates/student_account/password_reset');
TemplateHelpers.installTemplate('templates/student_account/form_field');
});
it('can initially display the login form', function() {
ajaxSpyAndInitialize(this, 'login');
/* Verify that the login form is expanded, and that the
/* registration form is collapsed.
*/
assertForms('#login-form', '#register-form');
});
it("initially displays the correct form", function() {
assertForms($('#login-form'), $('#register-form'));
it('can initially display the registration form', function() {
ajaxSpyAndInitialize(this, 'register');
/* Verify that the registration form is expanded, and that the
/* login form is collapsed.
*/
assertForms('#register-form', '#login-form');
});
it("toggles between the login and registration forms", function() {
var registerChangeEvent = $.Event('change', {currentTarget: $('#register-option')}),
loginChangeEvent = $.Event('change', {currentTarget: $('#login-option')});
it('toggles between the login and registration forms', function() {
ajaxSpyAndInitialize(this, 'login');
// Simulate selection of the registration form
view.toggleForm(registerChangeEvent)
assertForms($('#register-form'), $('#login-form'));
selectForm('register');
assertForms('#register-form', '#login-form');
// Simulate selection of the login form
view.toggleForm(loginChangeEvent)
assertForms($('#login-form'), $('#register-form'));
selectForm('login');
assertForms('#login-form', '#register-form');
});
it("displays the reset password form", function() {
it('displays the reset password form', function() {
ajaxSpyAndInitialize(this, 'login');
// Simulate a click on the reset password link
view.resetPassword();
ajaxAssertAndRespond(
AJAX_INFO['password_reset'].url,
AJAX_INFO['password_reset'].requestIndex
);
// Verify that the password reset wrapper is populated
expect($('#password-reset-wrapper')).not.toBeEmpty();
});
it('displays an error if a form definition could not be loaded', function() {
/* TODO: Not yet implemeted in the access view; currently, it only
* logs to the console.
*/
});
});
}
);
define(['js/common_helpers/template_helpers', 'js/student_account/enrollment_interface'],
function(TemplateHelpers, EnrollmentInterface) {
define(['js/student_account/enrollment_interface'],
function(EnrollmentInterface) {
describe("edx.student.account.EnrollmentInterface", function() {
'use strict';
it("find course modes using modeInArray ", function() {
var course_modes = [
{
slug: 'honor'
},
{
slug: 'professional'
}
],
expect(EnrollmentInterface.modeInArray('professional')).toBe(true);
expect(EnrollmentInterface.modeInArray('audit')).toBe(false);
it('checks if a given course mode slug exists in an array of mode objects', function() {
var courseModes = [ { slug: 'honor' }, { slug: 'professional' } ]
expect( EnrollmentInterface.modeInArray( courseModes, 'professional' ) ).toBe(true);
expect( EnrollmentInterface.modeInArray( courseModes, 'audit' ) ).toBe(false);
});
});
}
......
define(['js/common_helpers/template_helpers', 'js/student_account/views/LoginView'],
function(TemplateHelpers) {
describe("edx.student.account.LoginView", function() {
define([
'jquery',
'underscore',
'js/common_helpers/template_helpers',
'js/common_helpers/ajax_helpers',
'js/student_account/models/LoginModel',
'js/student_account/views/LoginView'
], function($, _, TemplateHelpers, AjaxHelpers, LoginModel, LoginView) {
describe('edx.student.account.LoginView', function() {
'use strict';
var model = null,
view = null,
requests = null,
PLATFORM_NAME = 'edX',
USER_DATA = {
email: 'xsy@edx.org',
password: 'xsyisawesome',
remember: true
},
THIRD_PARTY_AUTH = {
currentProvider: null,
providers: [
{
name: 'Google',
iconClass: 'icon-google-plus',
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
},
{
name: 'Facebook',
iconClass: 'icon-facebook',
loginUrl: '/auth/login/facebook/?auth_entry=account_login',
registerUrl: '/auth/login/facebook/?auth_entry=account_register'
}
]
},
FORM_DESCRIPTION = {
method: 'post',
submit_url: '/user_api/v1/account/login_session/',
fields: [
{
name: 'email',
label: 'Email',
defaultValue: '',
type: 'email',
required: true,
placeholder: 'place@holder.org',
instructions: 'Enter your email.',
restrictions: {}
},
{
name: 'password',
label: 'Password',
defaultValue: '',
type: 'password',
required: true,
instructions: 'Enter your password.',
restrictions: {}
},
{
name: 'remember',
label: 'Remember me',
defaultValue: '',
type: 'checkbox',
required: true,
instructions: "Agree to the terms of service.",
restrictions: {}
}
]
};
var createLoginView = function(test) {
// Initialize the login model
model = new LoginModel({ url: FORM_DESCRIPTION.submit_url });
// Initialize the login view
view = new LoginView({
fields: FORM_DESCRIPTION.fields,
model: model,
thirdPartyAuth: THIRD_PARTY_AUTH,
platformName: PLATFORM_NAME
});
// Spy on AJAX requests
requests = AjaxHelpers.requests(test);
// Mock out redirection logic
spyOn(view, 'redirect').andCallFake(function() {
return true;
});
};
var submitForm = function(validationSuccess) {
// Simulate manual entry of login form data
$('#login-email').val(USER_DATA.email);
$('#login-password').val(USER_DATA.password);
// Check the "Remember me" checkbox
$('#login-remember').prop('checked', USER_DATA.remember);
// Create a fake click event
var clickEvent = $.Event('click');
// If validationSuccess isn't passed, we avoid
// spying on `view.validate` twice
if ( !_.isUndefined(validationSuccess) ) {
// Force validation to return as expected
spyOn(view, 'validate').andReturn({
isValid: validationSuccess,
message: 'Submission was validated.'
});
}
// Submit the email address
view.submitForm(clickEvent);
};
beforeEach(function() {
setFixtures("<div></div>");
TemplateHelpers.installTemplate("templates/student_account/login");
setFixtures('<div id="login-form"></div>');
TemplateHelpers.installTemplate('templates/student_account/login');
TemplateHelpers.installTemplate('templates/student_account/form_field');
});
it("logs the user in", function() {
// TODO
});
it('logs the user in', function() {
createLoginView(this);
it("displays third party auth login buttons", function() {
// TODO
});
// Submit the form, with successful validation
submitForm(true);
it("validates the email field", function() {
// TODO
});
// Verify that the client contacts the server with the expected data
AjaxHelpers.expectRequest(
requests, 'POST', FORM_DESCRIPTION.submit_url, $.param(
$.extend({url: FORM_DESCRIPTION.submit_url}, USER_DATA)
)
);
it("validates the password field", function() {
// TODO
// Respond with status code 200
AjaxHelpers.respondWithJson(requests, {});
// Verify that the user is redirected to the dashboard
expect(view.redirect).toHaveBeenCalledWith('/dashboard');
});
it("displays login errors", function() {
// TODO
it('displays third-party auth login buttons', function() {
createLoginView(this);
// Verify that Google and Facebook registration buttons are displayed
expect($('.button-Google')).toBeVisible();
expect($('.button-Facebook')).toBeVisible();
});
it("displays an error if the form definition could not be loaded", function() {
// TODO
it('displays a link to the password reset form', function() {
createLoginView(this);
// Verify that the password reset link is displayed
expect($('.forgot-password')).toBeVisible();
});
it("displays an error if the server could not be contacted while logging in", function() {
// TODO
it('validates login form fields', function() {
createLoginView(this);
submitForm(true);
// Verify that validation of form fields occurred
expect(view.validate).toHaveBeenCalledWith($('#login-email')[0]);
expect(view.validate).toHaveBeenCalledWith($('#login-password')[0]);
});
it("allows the user to navigate to the password assistance form", function() {
// TODO
it('displays login form validation errors', function() {
createLoginView(this);
// Submit the form, with failed validation
submitForm(false);
// Verify that submission errors are visible
expect(view.$errors).not.toHaveClass('hidden');
});
it("enrolls the student into the right location and forwards them properly", function() {
// TODO
it('displays an error if the server returns an error while logging in', function() {
createLoginView(this);
// Submit the form, with successful validation
submitForm(true);
// Simulate an error from the LMS servers
AjaxHelpers.respondWithError(requests);
// Expect that an error is displayed, and that we haven't been redirected
expect(view.$errors).not.toHaveClass('hidden');
expect(view.redirect).not.toHaveBeenCalled();
// If we try again and succeed, the error should go away
submitForm();
// This time, respond with status code 200
AjaxHelpers.respondWithJson(requests, {});
// Expect that the error is hidden
expect(view.$errors).toHaveClass('hidden');
});
});
}
......
define(['js/common_helpers/template_helpers', 'js/student_account/views/PasswordResetView'],
function(TemplateHelpers) {
define([
'jquery',
'underscore',
'js/common_helpers/template_helpers',
'js/common_helpers/ajax_helpers',
'js/student_account/models/PasswordResetModel',
'js/student_account/views/PasswordResetView',
], function($, _, TemplateHelpers, AjaxHelpers, PasswordResetModel, PasswordResetView) {
describe('edx.student.account.PasswordResetView', function() {
'use strict';
var view = null,
ajaxSuccess = true,
model = new edx.student.account.PasswordResetModel(),
data = [{
label: 'E-mail',
instructions: 'This is the e-mail address you used to register with edX',
name: 'email',
required: true,
type: 'email',
restrictions: [],
defaultValue: ''
}];
var model = null,
view = null,
requests = null,
EMAIL = 'xsy@edx.org',
FORM_DESCRIPTION = {
method: 'post',
submit_url: '/account/password',
fields: [{
name: 'email',
label: 'Email',
defaultValue: '',
type: 'text',
required: true,
placeholder: 'place@holder.org',
instructions: 'Enter your email.',
restrictions: {}
}]
};
var createPasswordResetView = function(that) {
// Initialize the password reset model
model = new PasswordResetModel({ url: FORM_DESCRIPTION.submit_url });
// Initialize the password reset view
view = new PasswordResetView({
fields: FORM_DESCRIPTION.fields,
model: model
});
// Spy on AJAX requests
requests = AjaxHelpers.requests(that);
};
var submitEmail = function(validationSuccess) {
// Simulate manual entry of an email address
$('#password-reset-email').val('foo@bar.baz');
$('#password-reset-email').val(EMAIL);
// Create a fake click event
var clickEvent = $.Event('click');
// Used to avoid spying on view.validate twice
if (typeof validationSuccess !== 'undefined') {
// If validationSuccess isn't passed, we avoid
// spying on `view.validate` twice
if ( !_.isUndefined(validationSuccess) ) {
// Force validation to return as expected
spyOn(view, 'validate').andReturn({
isValid: validationSuccess,
message: "We're all good."
message: 'Submission was validated.'
});
}
......@@ -36,65 +63,74 @@ define(['js/common_helpers/template_helpers', 'js/student_account/views/Password
view.submitForm(clickEvent);
};
var assertAjax = function(url, method, data) {
expect($.ajax).toHaveBeenCalled();
var ajaxArgs = $.ajax.mostRecentCall.args[0];
expect(ajaxArgs.url).toEqual(url);
expect(ajaxArgs.type).toEqual(method);
expect(ajaxArgs.data).toEqual(data)
expect(ajaxArgs.headers.hasOwnProperty("X-CSRFToken")).toBe(true);
};
beforeEach(function() {
setFixtures("<div id='password-reset-wrapper'></div>");
setFixtures('<div id="password-reset-wrapper"></div>');
TemplateHelpers.installTemplate('templates/student_account/password_reset');
TemplateHelpers.installTemplate('templates/student_account/form_field');
// Stub AJAX calls
spyOn($, 'ajax').andCallFake(function() {
return $.Deferred(function(defer) {
if (ajaxSuccess) {
defer.resolve();
} else {
defer.rejectWith(this, ["The server could not be contacted."]);
}
}).promise();
});
view = new edx.student.account.PasswordResetView({
fields: data,
model: model
});
});
it("allows the user to request a new password", function() {
it('allows the user to request a new password', function() {
createPasswordResetView(this);
// Submit the form, with successful validation
submitEmail(true);
assertAjax('/account/password', 'POST', {email: 'foo@bar.baz'});
// Verify that the client contacts the server with the expected data
AjaxHelpers.expectRequest(
requests, 'POST', FORM_DESCRIPTION.submit_url, $.param({
url: FORM_DESCRIPTION.submit_url,
email: EMAIL
})
);
// Respond with status code 200
AjaxHelpers.respondWithJson(requests, {});
// Verify that the success message is visible
expect($('.js-reset-success')).not.toHaveClass('hidden');
});
it("validates the email field", function() {
it('validates the email field', function() {
createPasswordResetView(this);
// Submit the form, with successful validation
submitEmail(true);
expect(view.validate).toHaveBeenCalled()
// Verify that validation of the email field occurred
expect(view.validate).toHaveBeenCalledWith($('#password-reset-email')[0]);
// Verify that no submission errors are visible
expect(view.$errors).toHaveClass('hidden');
});
it("displays password reset validation errors", function() {
it('displays password reset validation errors', function() {
createPasswordResetView(this);
// Submit the form, with failed validation
submitEmail(false);
// Verify that submission errors are visible
expect(view.$errors).not.toHaveClass('hidden');
});
it("displays an error if the server could not be contacted", function() {
// If we get an error status on the AJAX request, display an error
ajaxSuccess = false;
it('displays an error if the server returns an error while sending a password reset email', function() {
createPasswordResetView(this);
submitEmail(true);
expect(view.$'#submission-error').not.toHaveClass('hidden');
// Simulate an error from the LMS servers
AjaxHelpers.respondWithError(requests);
// Expect that an error is displayed
expect(view.$errors).not.toHaveClass('hidden');
// If we try again and succeed, the error should go away
ajaxSuccess = true;
// No argument means we won't spy on view.validate again
submitEmail();
expect(view.$'#submission-error').toHaveClass('hidden');
// This time, respond with status code 200
AjaxHelpers.respondWithJson(requests, {});
// Expect that the error is hidden
expect(view.$errors).toHaveClass('hidden');
});
});
}
......
......@@ -4,7 +4,7 @@ var edx = edx || {};
'use strict';
edx.student = edx.student || {};
edx.student.account = {};
edx.student.account = edx.student.account || {};
edx.student.account.AccountModel = Backbone.Model.extend({
// These should be the same length limits enforced by the server
......
......@@ -8,71 +8,80 @@ var edx = edx || {};
edx.student.account.EnrollmentInterface = {
courseUrl: '/enrollment/v0/course/',
studentUrl: '/enrollment/v0/student',
trackSelectionUrl: '/course_modes/choose/',
headers: {
'X-CSRFToken': $.cookie('csrftoken')
},
studentInformation: function(course_key) {
studentInformation: function(courseKey) {
// retrieve student enrollment information
},
courseInformation: function(course_key) {
courseInformation: function(courseKey) {
// retrieve course information from the enrollment API
},
modeInArray: function(mode_slug, course_modes) {
// finds whether or not a particular course mode slug exists
// in an array of course modes
var result = _.find(course_modes, function(mode){ return mode.slug === mode_slug; });
return result != undefined;
modeInArray: function(modeObjects, targetMode) {
// Check if a given course mode slug exists in an array of mode objects
var result = _.find(modeObjects, function(mode) {
return mode.slug === targetMode;
});
/* _.find returns the first value which passes the provided truth test,
/* or undefined if no values pass the test
*/
return !_.isUndefined(result);
},
enroll: function(course_key, forward_url){
enroll: function(courseKey, forwardUrl){
var me = this;
// attempt to enroll a student in a course
$.ajax({
url: this.courseUrl + course_key,
url: this.courseUrl + courseKey,
type: 'POST',
data: {},
headers: this.headers
}).done(function(data){
me.postEnrollmentHandler(course_key, data, forward_url);
me.postEnrollmentHandler(courseKey, data, forwardUrl);
}
).fail(function(data, textStatus) {
me.enrollmentFailureHandler(course_key, data, forward_url);
me.enrollmentFailureHandler(courseKey, data, forwardUrl);
});
},
enrollmentFailureHandler: function(course_key, data, forward_url) {
enrollmentFailureHandler: function(courseKey, data, forwardUrl) {
// handle failures to enroll via the API
if(data.status == 400) {
// This status code probably means we don't have permissions to register for this course.
// look at the contents of the response
/* This status code probably means we don't have permissions to register
/* for this course; look at the contents of the response
*/
var course = $.parseJSON(data.responseText);
// see if it's a professional ed course
if('course_modes' in course && this.modeInArray('professional', course.course_modes)) {
if( 'course_modes' in course && this.modeInArray(course.course_modes, 'professional') ) {
// forward appropriately
forward_url = this.trackSelectionUrl + course_key;
forwardUrl = this.trackSelectionUrl + courseKey;
}
}
// TODO: if we have a paid registration mode, add item to the cart and send them along
// TODO: we should figure out how to handle errors here eventually
window.location.href = forward_url;
// TODO: we should figure out how to handle errors here
window.location.href = forwardUrl;
},
postEnrollmentHandler: function(course_key, data, forward_url) {
postEnrollmentHandler: function(courseKey, data, forwardUrl) {
// Determine whether or not the course needs to be redirected to
// a particular page.
var course = data.course,
course_modes = course.course_modes;
// send the user to the track selection page, because it will do the right thing
forward_url = this.trackSelectionUrl + course_key;
forwardUrl = this.trackSelectionUrl + courseKey;
window.location.href = forward_url;
window.location.href = forwardUrl;
}
};
})(jQuery, _, gettext);
var edx = edx || {};
(function($, _, Backbone, gettext) {
(function($, Backbone) {
'use strict';
edx.student = edx.student || {};
edx.student.account = edx.student.account || {};
edx.student.account.LoginModel = Backbone.Model.extend({
defaults: {
......@@ -33,35 +32,11 @@ var edx = edx || {};
headers: headers
})
.done(function() {
var enrollment = edx.student.account.EnrollmentInterface,
query = new URI(window.location.search),
url = '/dashboard',
query_map = query.search(true),
next = '';
// check for forwarding url
if("next" in query_map) {
next = query_map['next'];
if(!window.isExternal(next)){
url = next;
}
}
model.trigger('sync');
// if we need to enroll in the course, mark as enrolled
if('enrollment_action' in query_map && query_map['enrollment_action'] === 'enroll'){
enrollment.enroll(query_map['course_id'], url);
}
else {
window.location.href = url;
}
})
.fail( function( error ) {
model.trigger('error', error);
});
}
});
})(jQuery, _, Backbone, gettext);
})(jQuery, Backbone);
var edx = edx || {};
(function($, _, Backbone, gettext) {
(function($, Backbone) {
'use strict';
edx.student = edx.student || {};
......@@ -31,11 +31,11 @@ var edx = edx || {};
headers: headers
})
.done(function() {
model.trigger('success');
model.trigger('sync');
})
.fail( function( error ) {
model.trigger( 'error', error );
model.trigger('error', error);
});
}
});
})(jQuery, _, Backbone, gettext);
})(jQuery, Backbone);
var edx = edx || {};
(function($, _, Backbone, gettext) {
(function($, Backbone) {
'use strict';
edx.student = edx.student || {};
......@@ -18,7 +18,7 @@ var edx = edx || {};
year_of_birth: '',
mailing_address: '',
goals: '',
termsofservice: false
terms_of_service: false
},
urlRoot: '',
......@@ -39,33 +39,11 @@ var edx = edx || {};
headers: headers
})
.done(function() {
var enrollment = edx.student.account.EnrollmentInterface,
query = new URI(window.location.search),
url = '/dashboard',
query_map = query.search(true),
next = '';
// check for forwarding url
if("next" in query_map) {
next = query_map['next'];
if(!window.isExternal(next)){
url = next;
}
}
model.trigger('sync');
// if we need to enroll in the course, mark as enrolled
if('enrollment_action' in query_map && query_map['enrollment_action'] === 'enroll'){
enrollment.enroll(query_map['course_id'], url);
}
else {
window.location.href = url;
}
})
.fail( function( error ) {
model.trigger('error', error);
});
}
});
})(jQuery, _, Backbone, gettext);
})(jQuery, Backbone);
var edx = edx || {};
(function($, _, Backbone, gettext, analytics) {
(function($, _, _s, Backbone, gettext) {
'use strict';
edx.student = edx.student || {};
......@@ -29,7 +29,7 @@ var edx = edx || {};
* (all but include, contains, and reverse) into the
* Underscore namespace
*/
_.mixin( _.str.exports() );
_.mixin( _s.exports() );
this.tpl = $(this.tpl).html();
this.activeForm = obj.mode || 'login';
......@@ -112,20 +112,20 @@ var edx = edx || {};
};
$.ajax({
type: 'GET',
dataType: 'json',
url: '/user_api/v1/account/' + urls[type] + '/',
success: function( data ) {
callback( data, context );
},
error: function( jqXHR, textStatus, errorThrown ) {
console.log('fail ', errorThrown);
}
type: 'GET',
dataType: 'json'
})
.done(function( data ) {
callback( data, context );
})
.fail(function( jqXHR, textStatus, errorThrown ) {
console.log('fail ', errorThrown);
});
},
resetPassword: function() {
analytics.track('edx.bi.password_reset_form.viewed', {
window.analytics.track('edx.bi.password_reset_form.viewed', {
category: 'user-engagement'
});
......@@ -139,7 +139,7 @@ var edx = edx || {};
$form = $('#' + type + '-form'),
$anchor = $('#' + type + '-anchor');
analytics.track('edx.bi.' + type + '_form.toggled', {
window.analytics.track('edx.bi.' + type + '_form.toggled', {
category: 'user-engagement'
});
......@@ -177,5 +177,4 @@ var edx = edx || {};
}
}
});
})(jQuery, _, Backbone, gettext, analytics);
})(jQuery, _, _.str, Backbone, gettext);
......@@ -29,12 +29,12 @@ var edx = edx || {};
requiredStr: '*',
initialize: function( data ) {
this.model = data.model;
this.preRender( data );
this.tpl = $(this.tpl).html();
this.fieldTpl = $(this.fieldTpl).html();
this.buildForm( data.fields );
this.model = data.model;
this.listenTo( this.model, 'error', this.saveError );
},
......
......@@ -25,6 +25,8 @@ var edx = edx || {};
this.providers = data.thirdPartyAuth.providers || [];
this.currentProvider = data.thirdPartyAuth.currentProvider || '';
this.platformName = data.platformName;
this.listenTo( this.model, 'sync', this.saveSuccess );
},
render: function( html ) {
......@@ -76,6 +78,32 @@ var edx = edx || {};
}
},
saveSuccess: function () {
var enrollment = edx.student.account.EnrollmentInterface,
redirectUrl = '/dashboard',
next = null;
// Check for forwarding url
if ( !_.isNull( $.url('?next') ) ) {
next = decodeURIComponent( $.url('?next') );
if ( !window.isExternal(next) ) {
redirectUrl = next;
}
}
// If we need to enroll in a course, mark as enrolled
if ( $.url('?enrollment_action') === 'enroll' ) {
enrollment.enroll( decodeURIComponent( $.url('?course_id') ), redirectUrl );
} else {
this.redirect(redirectUrl);
}
},
redirect: function( url ) {
window.location.href = url;
},
saveError: function( error ) {
this.errors = ['<li>' + error.responseText + '</li>'];
this.setErrors();
......
......@@ -19,15 +19,8 @@ var edx = edx || {};
requiredStr: '',
postRender: function() {
var $container = $(this.el);
this.$form = $container.find('form');
this.$errors = $container.find('.submission-error');
this.listenTo( this.model, 'success', this.resetComplete );
this.listenTo( this.model, 'error', this.saveError );
preRender: function( data ) {
this.listenTo( this.model, 'sync', this.saveSuccess );
},
toggleErrorMsg: function( show ) {
......@@ -38,7 +31,7 @@ var edx = edx || {};
}
},
resetComplete: function() {
saveSuccess: function() {
var $el = $(this.el);
this.element.hide( $el.find('#password-reset-form') );
......
......@@ -22,6 +22,8 @@ var edx = edx || {};
this.providers = data.thirdPartyAuth.providers || [];
this.currentProvider = data.thirdPartyAuth.currentProvider || '';
this.platformName = data.platformName;
this.listenTo( this.model, 'sync', this.saveSuccess );
},
render: function( html ) {
......@@ -50,7 +52,32 @@ var edx = edx || {};
if ( providerUrl ) {
window.location.href = providerUrl;
}
},
saveSuccess: function() {
var enrollment = edx.student.account.EnrollmentInterface,
redirectUrl = '/dashboard',
next = null;
// Check for forwarding url
if ( !_.isNull( $.url('?next') ) ) {
next = decodeURIComponent( $.url('?next') );
if ( !window.isExternal(next) ) {
redirectUrl = next;
}
}
// If we need to enroll in a course, mark as enrolled
if ( $.url('?enrollment_action') === 'enroll' ) {
enrollment.enroll( decodeURIComponent( $.url('?course_id') ), redirectUrl );
} else {
this.redirect(redirectUrl);
}
},
redirect: function( url ) {
window.location.href = url;
}
});
})(jQuery, _, gettext);
......@@ -4,7 +4,7 @@ var edx = edx || {};
'use strict';
edx.student = edx.student || {};
edx.student.profile = {};
edx.student.profile = edx.student.profile || {};
var syncErrorMessage = gettext("The data could not be saved.");
......
......@@ -41,6 +41,7 @@ lib_paths:
- xmodule_js/common_static/js/vendor/flot/jquery.flot.js
- xmodule_js/common_static/js/vendor/CodeMirror/codemirror.js
- xmodule_js/common_static/js/vendor/URI.min.js
- xmodule_js/common_static/js/vendor/url.min.js
- xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js
- xmodule_js/common_static/coffee/src/xblock
- xmodule_js/common_static/js/vendor/sinon-1.7.1.js
......@@ -49,6 +50,7 @@ lib_paths:
- xmodule_js/src/xmodule.js
- xmodule_js/common_static/js/src/
- xmodule_js/common_static/js/vendor/underscore-min.js
- xmodule_js/common_static/js/vendor/underscore.string.min.js
- xmodule_js/common_static/js/vendor/backbone-min.js
# Paths to source JavaScript files
......
......@@ -6,9 +6,10 @@
<%block name="pagetitle">${_("Log in or Register")}</%block>
<%block name="js_extra">
<script type="text/javascript" src="${static.url('js/vendor/backbone-min.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/underscore.string.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/URI.min.js')}"></script>
<script src="${static.url('js/vendor/underscore-min.js')}"></script>
<script src="${static.url('js/vendor/underscore.string.min.js')}"></script>
<script src="${static.url('js/vendor/backbone-min.js')}"></script>
<script src="${static.url('js/vendor/url.min.js')}"></script>
<%static:js group='student_account'/>
</%block>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment