Commit d2a47908 by zubair-arbi

use the standard syntax to load JavaScript dependencies on logistration js files

ECOM-2044
parent 1ba4200a
......@@ -69,8 +69,8 @@ class IntegrationTestLTI(testutil.TestCase):
self.assertTrue(login_response['Location'].endswith(reverse('signin_user')))
register_response = self.client.get(login_response['Location'])
self.assertEqual(register_response.status_code, 200)
self.assertIn('currentProvider": "LTI Test Tool Consumer"', register_response.content)
self.assertIn('"errorMessage": null', register_response.content)
self.assertIn('"currentProvider": "LTI Test Tool Consumer"', register_response.content)
self.assertIn('"errorMessage": null', register_response.content)
# Now complete the form:
ajax_register_response = self.client.post(
......@@ -153,7 +153,7 @@ class IntegrationTestLTI(testutil.TestCase):
register_response = self.client.get(login_response['Location'])
self.assertEqual(register_response.status_code, 200)
self.assertIn(
'currentProvider": "Tool Consumer with Secret in Settings"',
'"currentProvider": "Tool Consumer with Secret in Settings"',
register_response.content
)
self.assertIn('"errorMessage": null', register_response.content)
self.assertIn('"errorMessage": null', register_response.content)
"""
Third_party_auth integration tests using a mock version of the TestShib provider
"""
from django.core.urlresolvers import reverse
import json
import unittest
import httpretty
from mock import patch
from django.core.urlresolvers import reverse
from openedx.core.lib.json_utils import EscapedEdxJSONEncoder
from student.tests.factories import UserFactory
from third_party_auth.tasks import fetch_saml_metadata
from third_party_auth.tests import testutil
import unittest
TESTSHIB_ENTITY_ID = 'https://idp.testshib.org/idp/shibboleth'
TESTSHIB_METADATA_URL = 'https://mock.testshib.org/metadata/testshib-providers.xml'
......@@ -81,11 +88,11 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
# We'd now like to see if the "You've successfully signed into TestShib" message is
# shown, but it's managed by a JavaScript runtime template, and we can't run JS in this
# type of test, so we just check for the variable that triggers that message.
self.assertIn('"currentProvider": "TestShib"', register_response.content)
self.assertIn('"errorMessage": null', register_response.content)
self.assertIn('"currentProvider": "TestShib"', register_response.content)
self.assertIn('"errorMessage": null', register_response.content)
# Now do a crude check that the data (e.g. email) from the provider is displayed in the form:
self.assertIn('"defaultValue": "myself@testshib.org"', register_response.content)
self.assertIn('"defaultValue": "Me Myself And I"', register_response.content)
self.assertIn('"defaultValue": "myself@testshib.org"', register_response.content)
self.assertIn('"defaultValue": "Me Myself And I"', register_response.content)
# Now complete the form:
ajax_register_response = self.client.post(
reverse('user_api_registration'),
......@@ -128,8 +135,8 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
# We'd now like to see if the "You've successfully signed into TestShib" message is
# shown, but it's managed by a JavaScript runtime template, and we can't run JS in this
# type of test, so we just check for the variable that triggers that message.
self.assertIn('"currentProvider": "TestShib"', login_response.content)
self.assertIn('"errorMessage": null', login_response.content)
self.assertIn('"currentProvider": "TestShib"', login_response.content)
self.assertIn('"errorMessage": null', login_response.content)
# Now the user enters their username and password.
# The AJAX on the page will log them in:
ajax_login_response = self.client.post(
......@@ -183,7 +190,7 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
response = self.client.get(self.login_page_url)
self.assertEqual(response.status_code, 200)
self.assertIn("TestShib", response.content)
self.assertIn(TPA_TESTSHIB_LOGIN_URL.replace('&', '&'), response.content)
self.assertIn(json.dumps(TPA_TESTSHIB_LOGIN_URL, cls=EscapedEdxJSONEncoder), response.content)
return response
def _check_register_page(self):
......@@ -191,7 +198,7 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
response = self.client.get(self.register_page_url)
self.assertEqual(response.status_code, 200)
self.assertIn("TestShib", response.content)
self.assertIn(TPA_TESTSHIB_REGISTER_URL.replace('&', '&'), response.content)
self.assertIn(json.dumps(TPA_TESTSHIB_REGISTER_URL, cls=EscapedEdxJSONEncoder), response.content)
return response
def _configure_testshib_provider(self, **kwargs):
......
describe('edx.utils.validate', function () {
;(function (define) {
'use strict';
var fixture = null,
field = null,
result = null,
MIN_LENGTH = 2,
MAX_LENGTH = 20,
VALID_STRING = 'xsy_is_awesome',
SHORT_STRING = 'x',
LONG_STRING = 'xsy_is_way_too_awesome',
EMAIL_ERROR_FRAGMENT = 'formatted',
MIN_ERROR_FRAGMENT = 'least',
MAX_ERROR_FRAGMENT = 'up to',
REQUIRED_ERROR_FRAGMENT = 'Please enter your',
CUSTOM_MESSAGE = 'custom message';
var createFixture = function( type, name, required, minlength, maxlength, value ) {
setFixtures('<input id="field" type=' + type + '>');
field = $('#field');
field.prop('required', required);
field.attr({
name: name,
minlength: minlength,
maxlength: maxlength,
value: value
define(['jquery', 'js/utils/edx.utils.validate'],
function($) {
var fixture = null,
field = null,
result = null,
MIN_LENGTH = 2,
MAX_LENGTH = 20,
VALID_STRING = 'xsy_is_awesome',
SHORT_STRING = 'x',
LONG_STRING = 'xsy_is_way_too_awesome',
EMAIL_ERROR_FRAGMENT = 'formatted',
MIN_ERROR_FRAGMENT = 'least',
MAX_ERROR_FRAGMENT = 'up to',
REQUIRED_ERROR_FRAGMENT = 'Please enter your',
CUSTOM_MESSAGE = 'custom message';
var createFixture = function( type, name, required, minlength, maxlength, value ) {
setFixtures('<input id="field" type=' + type + '>');
field = $('#field');
field.prop('required', required);
field.attr({
name: name,
minlength: minlength,
maxlength: maxlength,
value: value
});
};
var expectValid = function() {
result = edx.utils.validate(field);
expect(result.isValid).toBe(true);
};
var expectInvalid = function( errorFragment ) {
result = edx.utils.validate(field);
expect(result.isValid).toBe(false);
expect(result.message).toMatch(errorFragment);
};
it('succeeds if an optional field is left blank', function () {
createFixture('text', 'username', false, MIN_LENGTH, MAX_LENGTH, '');
expectValid();
});
};
var expectValid = function() {
result = edx.utils.validate(field);
expect(result.isValid).toBe(true);
};
var expectInvalid = function( errorFragment ) {
result = edx.utils.validate(field);
expect(result.isValid).toBe(false);
expect(result.message).toMatch(errorFragment);
};
it('succeeds if an optional field is left blank', function () {
createFixture('text', 'username', false, MIN_LENGTH, MAX_LENGTH, '');
expectValid();
});
it('succeeds if a required field is provided a valid value', function () {
createFixture('text', 'username', true, MIN_LENGTH, MAX_LENGTH, VALID_STRING);
expectValid();
});
it('succeeds if a required field is provided a valid value', function () {
createFixture('text', 'username', true, MIN_LENGTH, MAX_LENGTH, VALID_STRING);
expectValid();
});
it('fails if a required field is left blank', function () {
createFixture('text', 'username', true, MIN_LENGTH, MAX_LENGTH, '');
expectInvalid(REQUIRED_ERROR_FRAGMENT);
});
it('fails if a required field is left blank', function () {
createFixture('text', 'username', true, MIN_LENGTH, MAX_LENGTH, '');
expectInvalid(REQUIRED_ERROR_FRAGMENT);
});
it('fails if a field is provided a value below its minimum character limit', function () {
createFixture('text', 'username', false, MIN_LENGTH, MAX_LENGTH, SHORT_STRING);
it('fails if a field is provided a value below its minimum character limit', function () {
createFixture('text', 'username', false, MIN_LENGTH, MAX_LENGTH, SHORT_STRING);
// Verify optional field behavior
expectInvalid(MIN_ERROR_FRAGMENT);
// Verify optional field behavior
expectInvalid(MIN_ERROR_FRAGMENT);
// Verify required field behavior
field.prop('required', true);
expectInvalid(MIN_ERROR_FRAGMENT);
});
// Verify required field behavior
field.prop('required', true);
expectInvalid(MIN_ERROR_FRAGMENT);
});
it('succeeds if a field with no minimum character limit is provided a value below its maximum character limit', function () {
createFixture('text', 'username', false, null, MAX_LENGTH, SHORT_STRING);
it('succeeds if a field with no minimum character limit is provided a value below its maximum character limit', function () {
createFixture('text', 'username', false, null, MAX_LENGTH, SHORT_STRING);
// Verify optional field behavior
expectValid();
// Verify optional field behavior
expectValid();
// Verify required field behavior
field.prop('required', true);
expectValid();
});
// Verify required field behavior
field.prop('required', true);
expectValid();
});
it('fails if a required field with no minimum character limit is left blank', function () {
createFixture('text', 'username', true, null, MAX_LENGTH, '');
expectInvalid(REQUIRED_ERROR_FRAGMENT);
});
it('fails if a required field with no minimum character limit is left blank', function () {
createFixture('text', 'username', true, null, MAX_LENGTH, '');
expectInvalid(REQUIRED_ERROR_FRAGMENT);
});
it('fails if a field is provided a value above its maximum character limit', function () {
createFixture('text', 'username', false, MIN_LENGTH, MAX_LENGTH, LONG_STRING);
it('fails if a field is provided a value above its maximum character limit', function () {
createFixture('text', 'username', false, MIN_LENGTH, MAX_LENGTH, LONG_STRING);
// Verify optional field behavior
expectInvalid(MAX_ERROR_FRAGMENT);
// Verify optional field behavior
expectInvalid(MAX_ERROR_FRAGMENT);
// Verify required field behavior
field.prop('required', true);
expectInvalid(MAX_ERROR_FRAGMENT);
});
// Verify required field behavior
field.prop('required', true);
expectInvalid(MAX_ERROR_FRAGMENT);
});
it('succeeds if a field with no maximum character limit is provided a value above its minimum character limit', function () {
createFixture('text', 'username', false, MIN_LENGTH, null, LONG_STRING);
it('succeeds if a field with no maximum character limit is provided a value above its minimum character limit', function () {
createFixture('text', 'username', false, MIN_LENGTH, null, LONG_STRING);
// Verify optional field behavior
expectValid();
// Verify optional field behavior
expectValid();
// Verify required field behavior
field.prop('required', true);
expectValid();
});
// Verify required field behavior
field.prop('required', true);
expectValid();
});
it('succeeds if a field with no character limits is provided a value', function () {
createFixture('text', 'username', false, null, null, VALID_STRING);
it('succeeds if a field with no character limits is provided a value', function () {
createFixture('text', 'username', false, null, null, VALID_STRING);
// Verify optional field behavior
expectValid();
// Verify optional field behavior
expectValid();
// Verify required field behavior
field.prop('required', true);
expectValid();
});
// Verify required field behavior
field.prop('required', true);
expectValid();
});
it('fails if an email field is provided an invalid address', function () {
createFixture('email', 'email', false, MIN_LENGTH, MAX_LENGTH, 'localpart');
it('fails if an email field is provided an invalid address', function () {
createFixture('email', 'email', false, MIN_LENGTH, MAX_LENGTH, 'localpart');
// Verify optional field behavior
expectInvalid(EMAIL_ERROR_FRAGMENT);
// Verify optional field behavior
expectInvalid(EMAIL_ERROR_FRAGMENT);
// Verify required field behavior
field.prop('required', false);
expectInvalid(EMAIL_ERROR_FRAGMENT);
});
// Verify required field behavior
field.prop('required', false);
expectInvalid(EMAIL_ERROR_FRAGMENT);
});
it('succeeds if an email field is provided a valid address', function () {
createFixture('email', 'email', false, MIN_LENGTH, MAX_LENGTH, 'localpart@label.tld');
it('succeeds if an email field is provided a valid address', function () {
createFixture('email', 'email', false, MIN_LENGTH, MAX_LENGTH, 'localpart@label.tld');
// Verify optional field behavior
expectValid();
// Verify optional field behavior
expectValid();
// Verify required field behavior
field.prop('required', true);
expectValid();
});
// Verify required field behavior
field.prop('required', true);
expectValid();
});
it('succeeds if a checkbox is optional, or required and checked, but fails if a required checkbox is unchecked', function () {
createFixture('checkbox', 'checkbox', false, null, null, 'value');
it('succeeds if a checkbox is optional, or required and checked, but fails if a required checkbox is unchecked', function () {
createFixture('checkbox', 'checkbox', false, null, null, 'value');
// Optional, unchecked
expectValid();
// Optional, unchecked
expectValid();
// Optional, checked
field.prop('checked', true);
expectValid();
// Optional, checked
field.prop('checked', true);
expectValid();
// Required, checked
field.prop('required', true);
expectValid();
// Required, checked
field.prop('required', true);
expectValid();
// Required, unchecked
field.prop('checked', false);
expectInvalid(REQUIRED_ERROR_FRAGMENT);
});
// Required, unchecked
field.prop('checked', false);
expectInvalid(REQUIRED_ERROR_FRAGMENT);
});
it('succeeds if a select is optional, or required and default is selected, but fails if a required select has the default option selected', function () {
var select = [
'<select id="dropdown" name="country">',
'<option value="" data-isdefault="true">Please select a country</option>',
'<option value="BE">Belgium</option>',
'<option value="DE">Germany</option>',
'</select>'
].join('');
it('succeeds if a select is optional, or required and default is selected, but fails if a required select has the default option selected', function () {
var select = [
'<select id="dropdown" name="country">',
'<option value="" data-isdefault="true">Please select a country</option>',
'<option value="BE">Belgium</option>',
'<option value="DE">Germany</option>',
'</select>'
].join('');
setFixtures(select);
setFixtures(select);
field = $('#dropdown');
field = $('#dropdown');
// Optional
expectValid();
// Optional
expectValid();
// Required, default text selected
field.attr('required', true);
expectInvalid(REQUIRED_ERROR_FRAGMENT);
// Required, default text selected
field.attr('required', true);
expectInvalid(REQUIRED_ERROR_FRAGMENT);
// Required, country selected
field.val('BE');
expectValid();
});
// Required, country selected
field.val('BE');
expectValid();
});
it('returns a custom error message if an invalid field has one attached', function () {
// Create a blank required field
createFixture('text', 'username', true, MIN_LENGTH, MAX_LENGTH, '');
it('returns a custom error message if an invalid field has one attached', function () {
// Create a blank required field
createFixture('text', 'username', true, MIN_LENGTH, MAX_LENGTH, '');
// Attach a custom error message to the field
field.data('errormsg-required', CUSTOM_MESSAGE);
// Attach a custom error message to the field
field.data('errormsg-required', CUSTOM_MESSAGE);
expectInvalid(CUSTOM_MESSAGE);
expectInvalid(CUSTOM_MESSAGE);
});
});
});
}).call(this, define || RequireJS.define);
var edx = edx || {};
(function( $, _, _s, gettext ) {
;(function (define) {
'use strict';
define([
'jquery',
'underscore',
'underscore.string',
'gettext'
],
function($, _, _s, gettext) {
var utils;
/* Mix non-conflicting functions from underscore.string
* (all but include, contains, and reverse) into the
* Underscore namespace. In practice, this mixin is done
* by the access view, but doing it here helps keep the
* utility self-contained.
*/
if (_.isUndefined(_s)) {
_s = _.str;
}
_.mixin( _s.exports() );
utils = (function(){
var _fn = {
validate: {
msg: {
email: '<li><%- gettext("The email address you\'ve provided isn\'t formatted correctly.") %></li>',
min: '<li><%- _.sprintf( gettext("%(field)s must have at least %(count)d characters."), context ) %></li>',
max: '<li><%- _.sprintf( gettext("%(field)s can only contain up to %(count)d characters."), context ) %></li>',
required: '<li><%- _.sprintf( gettext("Please enter your %(field)s."), context ) %></li>',
custom: '<li><%= content %></li>'
},
/* Mix non-conflicting functions from underscore.string
* (all but include, contains, and reverse) into the
* Underscore namespace. In practice, this mixin is done
* by the access view, but doing it here helps keep the
* utility self-contained.
*/
_.mixin( _.str.exports() );
edx.utils = edx.utils || {};
var utils = (function(){
var _fn = {
validate: {
msg: {
email: '<li><%- gettext("The email address you\'ve provided isn\'t formatted correctly.") %></li>',
min: '<li><%- _.sprintf( gettext("%(field)s must have at least %(count)d characters."), context ) %></li>',
max: '<li><%- _.sprintf( gettext("%(field)s can only contain up to %(count)d characters."), context ) %></li>',
required: '<li><%- _.sprintf( gettext("Please enter your %(field)s."), context ) %></li>',
custom: '<li><%= content %></li>'
},
field: function( el ) {
var $el = $(el),
required = true,
min = true,
max = true,
email = true,
response = {},
isBlank = _fn.validate.isBlank( $el );
if ( _fn.validate.isRequired( $el ) ) {
if ( isBlank ) {
required = false;
} else {
field: function( el ) {
var $el = $(el),
required = true,
min = true,
max = true,
email = true,
response = {},
isBlank = _fn.validate.isBlank( $el );
if ( _fn.validate.isRequired( $el ) ) {
if ( isBlank ) {
required = false;
} else {
min = _fn.validate.str.minlength( $el );
max = _fn.validate.str.maxlength( $el );
email = _fn.validate.email.valid( $el );
}
} else if ( !isBlank ) {
min = _fn.validate.str.minlength( $el );
max = _fn.validate.str.maxlength( $el );
email = _fn.validate.email.valid( $el );
}
} else if ( !isBlank ) {
min = _fn.validate.str.minlength( $el );
max = _fn.validate.str.maxlength( $el );
email = _fn.validate.email.valid( $el );
}
response.isValid = required && min && max && email;
response.isValid = required && min && max && email;
if ( !response.isValid ) {
_fn.validate.removeDefault( $el );
if ( !response.isValid ) {
_fn.validate.removeDefault( $el );
response.message = _fn.validate.getMessage( $el, {
required: required,
min: min,
max: max,
email: email
});
}
response.message = _fn.validate.getMessage( $el, {
required: required,
min: min,
max: max,
email: email
});
}
return response;
},
return response;
},
str: {
minlength: function( $el ) {
var min = $el.attr('minlength') || 0;
str: {
minlength: function( $el ) {
var min = $el.attr('minlength') || 0;
return min <= $el.val().length;
},
return min <= $el.val().length;
maxlength: function( $el ) {
var max = $el.attr('maxlength') || false;
return ( !!max ) ? max >= $el.val().length : true;
}
},
maxlength: function( $el ) {
var max = $el.attr('maxlength') || false;
isRequired: function( $el ) {
return $el.attr('required');
},
return ( !!max ) ? max >= $el.val().length : true;
}
},
isRequired: function( $el ) {
return $el.attr('required');
},
isBlank: function( $el ) {
var type = $el.attr('type'),
isBlank;
if ( type === 'checkbox' ) {
isBlank = !$el.prop('checked');
} else if ( type === 'select' ) {
isBlank = ( $el.data('isdefault') === true );
} else {
isBlank = !$el.val();
}
isBlank: function( $el ) {
var type = $el.attr('type'),
isBlank;
return isBlank;
},
email: {
// This is the same regex used to validate email addresses in Django 1.4
regex: new RegExp(
[
'(^[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+(\\.[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+)*',
'|^"([\\001-\\010\\013\\014\\016-\\037!#-\\[\\]-\\177]|\\\\[\\001-\\011\\013\\014\\016-\\177])*"',
')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\\.)+[A-Z]{2,6}\\.?$)',
'|\\[(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\]$'
].join(''), 'i'
),
valid: function( $el ) {
return $el.attr('type') === 'email' ? _fn.validate.email.format( $el.val() ) : true;
if ( type === 'checkbox' ) {
isBlank = !$el.prop('checked');
} else if ( type === 'select' ) {
isBlank = ( $el.data('isdefault') === true );
} else {
isBlank = !$el.val();
}
return isBlank;
},
format: function( str ) {
return _fn.validate.email.regex.test( str );
}
},
getLabel: function( id ) {
// Extract the field label, remove the asterisk (if it appears) and any extra whitespace
return $("label[for=" + id + "]").text().split("*")[0].trim();
},
getMessage: function( $el, tests ) {
var txt = [],
tpl,
label,
obj,
customMsg;
_.each( tests, function( value, key ) {
if ( !value ) {
label = _fn.validate.getLabel( $el.attr('id') );
customMsg = $el.data('errormsg-' + key) || false;
// If the field has a custom error msg attached, use it
if ( customMsg ) {
tpl = _fn.validate.msg.custom;
obj = {
content: customMsg
};
} else {
tpl = _fn.validate.msg[key];
email: {
// This is the same regex used to validate email addresses in Django 1.4
regex: new RegExp(
[
'(^[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+(\\.[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+)*',
'|^"([\\001-\\010\\013\\014\\016-\\037!#-\\[\\]-\\177]|\\\\[\\001-\\011\\013\\014\\016-\\177])*"',
')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\\.)+[A-Z]{2,6}\\.?$)',
'|\\[(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\]$'
].join(''), 'i'
),
valid: function( $el ) {
return $el.attr('type') === 'email' ? _fn.validate.email.format( $el.val() ) : true;
},
format: function( str ) {
return _fn.validate.email.regex.test( str );
}
},
obj = {
// We pass the context object to the template so that
// we can perform variable interpolation using sprintf
context: {
field: label
}
};
getLabel: function( id ) {
// Extract the field label, remove the asterisk (if it appears) and any extra whitespace
return $("label[for=" + id + "]").text().split("*")[0].trim();
},
if ( key === 'min' ) {
obj.context.count = parseInt( $el.attr('minlength'), 10 );
} else if ( key === 'max' ) {
obj.context.count = parseInt( $el.attr('maxlength'), 10 );
getMessage: function( $el, tests ) {
var txt = [],
tpl,
label,
obj,
customMsg;
_.each( tests, function( value, key ) {
if ( !value ) {
label = _fn.validate.getLabel( $el.attr('id') );
customMsg = $el.data('errormsg-' + key) || false;
// If the field has a custom error msg attached, use it
if ( customMsg ) {
tpl = _fn.validate.msg.custom;
obj = {
content: customMsg
};
} else {
tpl = _fn.validate.msg[key];
obj = {
// We pass the context object to the template so that
// we can perform variable interpolation using sprintf
context: {
field: label
}
};
if ( key === 'min' ) {
obj.context.count = parseInt( $el.attr('minlength'), 10 );
} else if ( key === 'max' ) {
obj.context.count = parseInt( $el.attr('maxlength'), 10 );
}
}
}
txt.push( _.template( tpl, obj ) );
}
});
txt.push( _.template( tpl, obj ) );
}
});
return txt.join(' ');
},
return txt.join(' ');
},
// Removes the default HTML5 validation pop-up
removeDefault: function( $el ) {
if ( $el.setCustomValidity ) {
$el.setCustomValidity(' ');
// Removes the default HTML5 validation pop-up
removeDefault: function( $el ) {
if ( $el.setCustomValidity ) {
$el.setCustomValidity(' ');
}
}
}
}
};
return {
validate: _fn.validate.field
};
};
})();
return {
validate: _fn.validate.field
};
edx.utils.validate = utils.validate;
})();
})( jQuery, _, _.str, gettext );
return utils;
});
}).call(this, define || RequireJS.define);
......@@ -8,7 +8,6 @@ import json
import mock
import ddt
import markupsafe
from django.conf import settings
from django.core.urlresolvers import reverse
from django.core import mail
......@@ -20,6 +19,7 @@ from django.test.client import RequestFactory
from openedx.core.djangoapps.user_api.accounts.api import activate_account, create_account
from openedx.core.djangoapps.user_api.accounts import EMAIL_MAX_LENGTH
from openedx.core.lib.json_utils import EscapedEdxJSONEncoder
from student.tests.factories import UserFactory
from student_account.views import account_settings_context
from third_party_auth.tests.testutil import simulate_running_pipeline, ThirdPartyAuthTestMixin
......@@ -223,7 +223,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
@ddt.unpack
def test_login_and_registration_form(self, url_name, initial_mode):
response = self.client.get(reverse(url_name))
expected_data = u"data-initial-mode=\"{mode}\"".format(mode=initial_mode)
expected_data = '"initial_mode": "{mode}"'.format(mode=initial_mode)
self.assertContains(response, expected_data)
@ddt.data("signin_user", "register_user")
......@@ -255,6 +255,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
# that preserves the querystring params
with mock.patch.dict(settings.FEATURES, {'IS_EDX_DOMAIN': is_edx_domain}):
response = self.client.get(reverse(url_name), params)
expected_url = '/login?{}'.format(self._finish_auth_url_param(params + [('next', '/dashboard')]))
self.assertContains(response, expected_url)
......@@ -330,7 +331,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
def test_hinted_login(self):
params = [("next", "/courses/something/?tpa_hint=oa2-google-oauth2")]
response = self.client.get(reverse('signin_user'), params)
self.assertContains(response, "data-third-party-auth-hint='oa2-google-oauth2'")
self.assertContains(response, '"third_party_auth_hint": "oa2-google-oauth2"')
@override_settings(SITE_NAME=settings.MICROSITE_TEST_HOSTNAME)
def test_microsite_uses_old_login_page(self):
......@@ -358,17 +359,17 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
finish_auth_url = None
if current_backend:
finish_auth_url = reverse("social:complete", kwargs={"backend": current_backend}) + "?"
auth_info = markupsafe.escape(
json.dumps({
"currentProvider": current_provider,
"providers": providers,
"secondaryProviders": [],
"finishAuthUrl": finish_auth_url,
"errorMessage": None,
})
)
expected_data = u"data-third-party-auth='{auth_info}'".format(
auth_info = {
"currentProvider": current_provider,
"providers": providers,
"secondaryProviders": [],
"finishAuthUrl": finish_auth_url,
"errorMessage": None,
}
auth_info = json.dumps(auth_info, cls=EscapedEdxJSONEncoder)
expected_data = '"third_party_auth": {auth_info}'.format(
auth_info=auth_info
)
......
......@@ -95,22 +95,25 @@ def login_and_registration_form(request, initial_mode="login"):
# Otherwise, render the combined login/registration page
context = {
'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in the header
'disable_courseware_js': True,
'initial_mode': initial_mode,
'third_party_auth': json.dumps(_third_party_auth_context(request, redirect_to)),
'third_party_auth_hint': third_party_auth_hint or '',
'platform_name': settings.PLATFORM_NAME,
'data': {
'login_redirect_url': redirect_to,
'initial_mode': initial_mode,
'third_party_auth': _third_party_auth_context(request, redirect_to),
'third_party_auth_hint': third_party_auth_hint or '',
'platform_name': settings.PLATFORM_NAME,
# Include form descriptions retrieved from the user API.
# We could have the JS client make these requests directly,
# but we include them in the initial page load to avoid
# the additional round-trip to the server.
'login_form_desc': json.loads(form_descriptions['login']),
'registration_form_desc': json.loads(form_descriptions['registration']),
'password_reset_form_desc': json.loads(form_descriptions['password_reset']),
},
'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in header
'responsive': True,
'allow_iframing': True,
# Include form descriptions retrieved from the user API.
# We could have the JS client make these requests directly,
# but we include them in the initial page load to avoid
# the additional round-trip to the server.
'login_form_desc': form_descriptions['login'],
'registration_form_desc': form_descriptions['registration'],
'password_reset_form_desc': form_descriptions['password_reset'],
'disable_courseware_js': True,
}
return render_to_response('student_account/login_and_register.html', context)
......
......@@ -1303,26 +1303,6 @@ instructor_dash_js = (
sorted(rooted_glob(PROJECT_ROOT / 'static', 'js/instructor_dashboard/**/*.js'))
)
# JavaScript used by the student account and profile pages
# These are not courseware, so they do not need many of the courseware-specific
# JavaScript modules.
student_account_js = [
'js/utils/edx.utils.validate.js',
'js/sticky_filter.js',
'js/query-params.js',
'js/student_account/models/LoginModel.js',
'js/student_account/models/RegisterModel.js',
'js/student_account/models/PasswordResetModel.js',
'js/student_account/views/FormView.js',
'js/student_account/views/LoginView.js',
'js/student_account/views/HintedLoginView.js',
'js/student_account/views/RegisterView.js',
'js/student_account/views/PasswordResetView.js',
'js/student_account/views/AccessView.js',
'js/student_account/views/InstitutionLoginView.js',
'js/student_account/accessApp.js',
]
verify_student_js = [
'js/sticky_filter.js',
'js/query-params.js',
......@@ -1574,10 +1554,6 @@ PIPELINE_JS = {
'source_filenames': dashboard_js,
'output_filename': 'js/dashboard.js'
},
'student_account': {
'source_filenames': student_account_js,
'output_filename': 'js/student_account.js'
},
'verify_student': {
'source_filenames': verify_student_js,
'output_filename': 'js/verify_student.js'
......
......@@ -75,16 +75,6 @@
'js/views/file_uploader': 'js/views/file_uploader',
'js/views/notification': 'js/views/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/views/InstitutionLoginView': 'js/student_account/views/InstitutionLoginView',
'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_account/views/HintedLoginView': 'js/student_account/views/HintedLoginView',
'js/student_profile/views/learner_profile_fields': 'js/student_profile/views/learner_profile_fields',
'js/student_profile/views/learner_profile_factory': 'js/student_profile/views/learner_profile_factory',
'js/student_profile/views/learner_profile_view': 'js/student_profile/views/learner_profile_view',
......@@ -94,7 +84,10 @@
'DiscussionModuleView': 'xmodule_js/common_static/coffee/src/discussion/discussion_module_view',
// edxnotes
'annotator_1.2.9': 'xmodule_js/common_static/js/vendor/edxnotes/annotator-full.min'
'annotator_1.2.9': 'xmodule_js/common_static/js/vendor/edxnotes/annotator-full.min',
// Common edx utils
'js/utils/edx.utils.validate': 'xmodule_js/common_static/js/utils/edx.utils.validate'
},
shim: {
'gettext': {
......@@ -337,91 +330,6 @@
'js/models/notification', 'jquery.fileupload'
]
},
// Student account registration/login
// Loaded explicitly until these are converted to RequireJS
'js/student_account/views/FormView': {
exports: 'edx.student.account.FormView',
deps: ['jquery', 'underscore', 'backbone', 'gettext']
},
'js/student_account/models/LoginModel': {
exports: 'edx.student.account.LoginModel',
deps: ['jquery', 'jquery.cookie', 'backbone']
},
'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'
]
},
'js/student_account/views/InstitutionLoginView': {
exports: 'edx.student.account.InstitutionLoginView',
deps: [
'jquery',
'underscore',
'backbone'
]
},
'js/student_account/models/PasswordResetModel': {
exports: 'edx.student.account.PasswordResetModel',
deps: ['jquery', 'jquery.cookie', 'backbone']
},
'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: 'edx.student.account.RegisterModel',
deps: ['jquery', 'jquery.cookie', 'backbone']
},
'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'
]
},
'js/student_account/views/HintedLoginView': {
exports: 'edx.student.account.HintedLoginView',
deps: [
'jquery',
'underscore',
'backbone',
'gettext'
]
},
'js/student_account/views/AccessView': {
exports: 'edx.student.account.AccessView',
deps: [
'jquery',
'underscore',
'backbone',
'history',
'utility',
'js/student_account/views/LoginView',
'js/student_account/views/PasswordResetView',
'js/student_account/views/RegisterView',
'js/student_account/views/InstitutionLoginView',
'js/student_account/models/LoginModel',
'js/student_account/models/PasswordResetModel',
'js/student_account/models/RegisterModel',
'js/student_account/views/FormView'
]
},
'js/verify_student/models/verification_model': {
exports: 'edx.verify_student.VerificationModel',
deps: [ 'jquery', 'underscore', 'backbone', 'jquery.cookie' ]
......@@ -731,6 +639,7 @@
'lms/include/js/spec/instructor_dashboard/student_admin_spec.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/logistration_factory_spec.js',
'lms/include/js/spec/student_account/finish_auth_spec.js',
'lms/include/js/spec/student_account/hinted_login_spec.js',
'lms/include/js/spec/student_account/login_spec.js',
......
define([
'jquery',
'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/ajax_helpers',
'js/student_account/views/AccessView',
'js/student_account/views/FormView',
'js/student_account/enrollment',
'js/student_account/shoppingcart',
'js/student_account/emailoptin'
], function($, TemplateHelpers, AjaxHelpers, AccessView, FormView, EnrollmentInterface, ShoppingCartInterface) {
"use strict";
;(function (define) {
'use strict';
define([
'jquery',
'underscore',
'backbone',
'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/ajax_helpers',
'js/student_account/views/AccessView',
'js/student_account/views/FormView',
'js/student_account/enrollment',
'js/student_account/shoppingcart',
'js/student_account/emailoptin'
],
function($, _, Backbone, TemplateHelpers, AjaxHelpers, AccessView, FormView, EnrollmentInterface,
ShoppingCartInterface) {
describe('edx.student.account.AccessView', function() {
var requests = null,
view = null,
......@@ -24,7 +30,7 @@ define([
required: true,
placeholder: 'xsy@edx.org',
instructions: 'Enter your email here.',
restrictions: {},
restrictions: {}
},
{
name: 'username',
......@@ -49,24 +55,27 @@ define([
THIRD_PARTY_COMPLETE_URL = '/auth/complete/provider/';
var ajaxSpyAndInitialize = function(that, mode, nextUrl, finishAuthUrl) {
var options = {
initial_mode: mode,
third_party_auth: {
currentProvider: null,
providers: [],
secondaryProviders: [{name: "provider"}],
finishAuthUrl: finishAuthUrl
},
login_redirect_url: nextUrl, // undefined for default
platform_name: 'edX',
login_form_desc: FORM_DESCRIPTION,
registration_form_desc: FORM_DESCRIPTION,
password_reset_form_desc: FORM_DESCRIPTION
},
$logistrationElement = $('#login-and-registration-container');
// Spy on AJAX requests
requests = AjaxHelpers.requests(that);
// Initialize the access view
view = new AccessView({
mode: mode,
thirdPartyAuth: {
currentProvider: null,
providers: [],
secondaryProviders: [{name: "provider"}],
finishAuthUrl: finishAuthUrl
},
nextUrl: nextUrl, // undefined for default
platformName: 'edX',
loginFormDesc: FORM_DESCRIPTION,
registrationFormDesc: FORM_DESCRIPTION,
passwordResetFormDesc: FORM_DESCRIPTION
});
view = new AccessView(_.extend(options, {el: $logistrationElement}));
// Mock the redirect call
spyOn( view, 'redirect' ).andCallFake( function() {} );
......@@ -92,7 +101,7 @@ define([
};
beforeEach(function() {
setFixtures('<div id="login-and-registration-container"></div>');
setFixtures('<div id="login-and-registration-container" class="login-register" />');
TemplateHelpers.installTemplate('templates/student_account/access');
TemplateHelpers.installTemplate('templates/student_account/login');
TemplateHelpers.installTemplate('templates/student_account/register');
......@@ -105,6 +114,10 @@ define([
window.analytics = jasmine.createSpyObj('analytics', ['track', 'page', 'pageview', 'trackLink']);
});
afterEach(function() {
Backbone.history.stop();
});
it('can initially display the login form', function() {
ajaxSpyAndInitialize(this, 'login');
......@@ -217,5 +230,5 @@ define([
});
});
}
);
});
}).call(this, define || RequireJS.define);
define([
'jquery',
'utility',
'common/js/spec_helpers/ajax_helpers',
'js/student_account/views/FinishAuthView',
'js/student_account/enrollment',
'js/student_account/shoppingcart',
'js/student_account/emailoptin'
], function($, utility, AjaxHelpers, FinishAuthView, EnrollmentInterface, ShoppingCartInterface, EmailOptInInterface) {
'use strict';
;(function (define) {
'use strict';
define([
'jquery',
'jquery.url',
'utility',
'common/js/spec_helpers/ajax_helpers',
'js/student_account/views/FinishAuthView',
'js/student_account/enrollment',
'js/student_account/shoppingcart',
'js/student_account/emailoptin'
],
function($, url, utility, AjaxHelpers, FinishAuthView, EnrollmentInterface, ShoppingCartInterface,
EmailOptInInterface) {
describe('FinishAuthView', function() {
var requests = null,
view = null,
......@@ -167,5 +172,5 @@ define([
expect( view.redirect ).toHaveBeenCalledWith( "/dashboard" );
});
});
}
);
});
}).call(this, define || RequireJS.define);
define([
'jquery',
'underscore',
'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/ajax_helpers',
'js/student_account/views/HintedLoginView',
], function($, _, TemplateHelpers, AjaxHelpers, HintedLoginView) {
;(function (define) {
'use strict';
describe('edx.student.account.HintedLoginView', function() {
define([
'jquery',
'underscore',
'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/ajax_helpers',
'js/student_account/views/HintedLoginView'
],
function($, _, TemplateHelpers, AjaxHelpers, HintedLoginView) {
var view = null,
requests = null,
PLATFORM_NAME = 'edX',
THIRD_PARTY_AUTH = {
currentProvider: null,
providers: [
{
id: 'oa2-google-oauth2',
name: 'Google',
iconClass: 'fa-google-plus',
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
},
{
id: 'oa2-facebook',
name: 'Facebook',
iconClass: 'fa-facebook',
loginUrl: '/auth/login/facebook/?auth_entry=account_login',
registerUrl: '/auth/login/facebook/?auth_entry=account_register'
}
],
secondaryProviders: [
{
id: 'saml-harvard',
name: 'Harvard',
iconClass: 'fa-university',
loginUrl: '/auth/login/tpa-saml/?auth_entry=account_login&idp=harvard',
registerUrl: '/auth/login/tpa-saml/?auth_entry=account_register&idp=harvard'
}
]
};
describe('edx.student.account.HintedLoginView', function() {
var view = null,
requests = null,
PLATFORM_NAME = 'edX',
THIRD_PARTY_AUTH = {
currentProvider: null,
providers: [
{
id: 'oa2-google-oauth2',
name: 'Google',
iconClass: 'fa-google-plus',
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
},
{
id: 'oa2-facebook',
name: 'Facebook',
iconClass: 'fa-facebook',
loginUrl: '/auth/login/facebook/?auth_entry=account_login',
registerUrl: '/auth/login/facebook/?auth_entry=account_register'
}
],
secondaryProviders: [
{
id: 'saml-harvard',
name: 'Harvard',
iconClass: 'fa-university',
loginUrl: '/auth/login/tpa-saml/?auth_entry=account_login&idp=harvard',
registerUrl: '/auth/login/tpa-saml/?auth_entry=account_register&idp=harvard'
}
]
};
var createHintedLoginView = function(hintedProvider) {
// Initialize the login view
view = new HintedLoginView({
thirdPartyAuth: THIRD_PARTY_AUTH,
hintedProvider: hintedProvider,
platformName: PLATFORM_NAME
});
var createHintedLoginView = function(hintedProvider) {
// Initialize the login view
view = new HintedLoginView({
thirdPartyAuth: THIRD_PARTY_AUTH,
hintedProvider: hintedProvider,
platformName: PLATFORM_NAME
});
// Mock the redirect call
spyOn( view, 'redirect' ).andCallFake( function() {} );
// Mock the redirect call
spyOn( view, 'redirect' ).andCallFake( function() {} );
view.render();
};
view.render();
};
beforeEach(function() {
setFixtures('<div id="hinted-login-form"></div>');
TemplateHelpers.installTemplate('templates/student_account/hinted_login');
});
beforeEach(function() {
setFixtures('<div id="hinted-login-form"></div>');
TemplateHelpers.installTemplate('templates/student_account/hinted_login');
});
it('displays a choice as two buttons', function() {
createHintedLoginView("oa2-google-oauth2");
it('displays a choice as two buttons', function() {
createHintedLoginView("oa2-google-oauth2");
expect($('.proceed-button.button-oa2-google-oauth2')).toBeVisible();
expect($('.form-toggle')).toBeVisible();
expect($('.proceed-button.button-oa2-facebook')).not.toBeVisible();
});
expect($('.proceed-button.button-oa2-google-oauth2')).toBeVisible();
expect($('.form-toggle')).toBeVisible();
expect($('.proceed-button.button-oa2-facebook')).not.toBeVisible();
});
it('works with secondary providers as well', function() {
createHintedLoginView("saml-harvard");
it('works with secondary providers as well', function() {
createHintedLoginView("saml-harvard");
expect($('.proceed-button.button-saml-harvard')).toBeVisible();
expect($('.form-toggle')).toBeVisible();
expect($('.proceed-button.button-oa2-google-oauth2')).not.toBeVisible();
});
expect($('.proceed-button.button-saml-harvard')).toBeVisible();
expect($('.form-toggle')).toBeVisible();
expect($('.proceed-button.button-oa2-google-oauth2')).not.toBeVisible();
});
it('redirects the user to the hinted provider if the user clicks the proceed button', function() {
createHintedLoginView("oa2-google-oauth2");
it('redirects the user to the hinted provider if the user clicks the proceed button', function() {
createHintedLoginView('oa2-google-oauth2');
// Click the "Yes, proceed" button
$('.proceed-button').click();
// Click the "Yes, proceed" button
$('.proceed-button').click();
expect(view.redirect).toHaveBeenCalledWith( '/auth/login/google-oauth2/?auth_entry=account_login' );
expect(view.redirect).toHaveBeenCalledWith( '/auth/login/google-oauth2/?auth_entry=account_login' );
});
});
});
});
}).call(this, define || RequireJS.define);
define([
'jquery',
'underscore',
'common/js/spec_helpers/template_helpers',
'js/student_account/views/InstitutionLoginView',
], function($, _, TemplateHelpers, InstitutionLoginView) {
;(function (define) {
'use strict';
describe('edx.student.account.InstitutionLoginView', function() {
define([
'jquery',
'underscore',
'common/js/spec_helpers/template_helpers',
'js/student_account/views/InstitutionLoginView'
],
function($, _, TemplateHelpers, InstitutionLoginView) {
var view = null,
PLATFORM_NAME = 'edX',
THIRD_PARTY_AUTH = {
currentProvider: null,
providers: [],
secondaryProviders: [
{
id: 'oa2-google-oauth2',
name: 'Google',
iconClass: 'fa-google-plus',
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
},
{
id: 'oa2-facebook',
name: 'Facebook',
iconClass: 'fa-facebook',
loginUrl: '/auth/login/facebook/?auth_entry=account_login',
registerUrl: '/auth/login/facebook/?auth_entry=account_register'
}
]
describe('edx.student.account.InstitutionLoginView', function() {
var view = null,
PLATFORM_NAME = 'edX',
THIRD_PARTY_AUTH = {
currentProvider: null,
providers: [],
secondaryProviders: [
{
id: 'oa2-google-oauth2',
name: 'Google',
iconClass: 'fa-google-plus',
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
},
{
id: 'oa2-facebook',
name: 'Facebook',
iconClass: 'fa-facebook',
loginUrl: '/auth/login/facebook/?auth_entry=account_login',
registerUrl: '/auth/login/facebook/?auth_entry=account_register'
}
]
};
var createInstLoginView = function(mode) {
// Initialize the login view
view = new InstitutionLoginView({
mode: mode,
thirdPartyAuth: THIRD_PARTY_AUTH,
platformName: PLATFORM_NAME
});
view.render();
};
var createInstLoginView = function(mode) {
// Initialize the login view
view = new InstitutionLoginView({
mode: mode,
thirdPartyAuth: THIRD_PARTY_AUTH,
platformName: PLATFORM_NAME
beforeEach(function() {
setFixtures('<div id="institution_login-form"></div>');
TemplateHelpers.installTemplate('templates/student_account/institution_login');
TemplateHelpers.installTemplate('templates/student_account/institution_register');
});
view.render();
};
beforeEach(function() {
setFixtures('<div id="institution_login-form"></div>');
TemplateHelpers.installTemplate('templates/student_account/institution_login');
TemplateHelpers.installTemplate('templates/student_account/institution_register');
});
it('displays a list of providers', function() {
var $google, $facebook;
it('displays a list of providers', function() {
createInstLoginView('login');
expect($('#institution_login-form').html()).not.toBe("");
var $google = $('li a:contains("Google")');
expect($google).toBeVisible();
expect($google).toHaveAttr(
'href', '/auth/login/google-oauth2/?auth_entry=account_login'
);
var $facebook = $('li a:contains("Facebook")');
expect($facebook).toBeVisible();
expect($facebook).toHaveAttr(
'href', '/auth/login/facebook/?auth_entry=account_login'
);
});
createInstLoginView('login');
expect($('#institution_login-form').html()).not.toBe("");
$google = $('li a:contains("Google")');
expect($google).toBeVisible();
expect($google).toHaveAttr(
'href', '/auth/login/google-oauth2/?auth_entry=account_login'
);
$facebook = $('li a:contains("Facebook")');
expect($facebook).toBeVisible();
expect($facebook).toHaveAttr(
'href', '/auth/login/facebook/?auth_entry=account_login'
);
});
it('displays a list of providers', function() {
createInstLoginView('register');
expect($('#institution_login-form').html()).not.toBe("");
var $google = $('li a:contains("Google")');
expect($google).toBeVisible();
expect($google).toHaveAttr(
'href', '/auth/login/google-oauth2/?auth_entry=account_register'
);
var $facebook = $('li a:contains("Facebook")');
expect($facebook).toBeVisible();
expect($facebook).toHaveAttr(
'href', '/auth/login/facebook/?auth_entry=account_register'
);
});
it('displays a list of providers', function() {
var $google, $facebook;
createInstLoginView('register');
expect($('#institution_login-form').html()).not.toBe("");
$google = $('li a:contains("Google")');
expect($google).toBeVisible();
expect($google).toHaveAttr(
'href', '/auth/login/google-oauth2/?auth_entry=account_register'
);
$facebook = $('li a:contains("Facebook")');
expect($facebook).toBeVisible();
expect($facebook).toHaveAttr(
'href', '/auth/login/facebook/?auth_entry=account_register'
);
});
});
});
});
}).call(this, define || RequireJS.define);
define([
'jquery',
'underscore',
'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/ajax_helpers',
'js/student_account/models/LoginModel',
'js/student_account/views/LoginView',
'js/student_account/models/PasswordResetModel'
], function($, _, TemplateHelpers, AjaxHelpers, LoginModel, LoginView, PasswordResetModel) {
;(function (define) {
'use strict';
describe('edx.student.account.LoginView', function() {
var model = null,
resetModel = null,
view = null,
requests = null,
authComplete = false,
PLATFORM_NAME = 'edX',
USER_DATA = {
email: 'xsy@edx.org',
password: 'xsyisawesome',
remember: true
},
THIRD_PARTY_AUTH = {
currentProvider: null,
providers: [
{
id: 'oa2-google-oauth2',
name: 'Google',
iconClass: 'fa-google-plus',
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
},
{
id: 'oa2-facebook',
name: 'Facebook',
iconClass: 'fa-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: [
{
placeholder: 'username@domain.com',
name: 'email',
label: 'Email',
defaultValue: '',
type: 'email',
required: true,
instructions: 'Enter your email.',
restrictions: {}
},
{
placeholder: '',
name: 'password',
label: 'Password',
defaultValue: '',
type: 'password',
required: true,
instructions: 'Enter your password.',
restrictions: {}
},
{
placeholder: '',
name: 'remember',
label: 'Remember me',
defaultValue: '',
type: 'checkbox',
required: true,
instructions: "Agree to the terms of service.",
restrictions: {}
}
]
},
COURSE_ID = "edX/demoX/Fall";
var createLoginView = function(test) {
// Initialize the login model
model = new LoginModel({}, {
url: FORM_DESCRIPTION.submit_url,
method: FORM_DESCRIPTION.method
});
define([
'jquery',
'underscore',
'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/ajax_helpers',
'js/student_account/models/LoginModel',
'js/student_account/views/LoginView',
'js/student_account/models/PasswordResetModel'
],
function($, _, TemplateHelpers, AjaxHelpers, LoginModel, LoginView, PasswordResetModel) {
describe('edx.student.account.LoginView', function() {
var model = null,
resetModel = null,
view = null,
requests = null,
authComplete = false,
PLATFORM_NAME = 'edX',
USER_DATA = {
email: 'xsy@edx.org',
password: 'xsyisawesome',
remember: true
},
THIRD_PARTY_AUTH = {
currentProvider: null,
providers: [
{
id: 'oa2-google-oauth2',
name: 'Google',
iconClass: 'fa-google-plus',
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
},
{
id: 'oa2-facebook',
name: 'Facebook',
iconClass: 'fa-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: [
{
placeholder: 'username@domain.com',
name: 'email',
label: 'Email',
defaultValue: '',
type: 'email',
required: true,
instructions: 'Enter your email.',
restrictions: {}
},
{
placeholder: '',
name: 'password',
label: 'Password',
defaultValue: '',
type: 'password',
required: true,
instructions: 'Enter your password.',
restrictions: {}
},
{
placeholder: '',
name: 'remember',
label: 'Remember me',
defaultValue: '',
type: 'checkbox',
required: true,
instructions: 'Agree to the terms of service.',
restrictions: {}
}
]
},
COURSE_ID = 'edX/demoX/Fall';
var createLoginView = function(test) {
// Initialize the login model
model = new LoginModel({}, {
url: FORM_DESCRIPTION.submit_url,
method: FORM_DESCRIPTION.method
});
// Initialize the passwordReset model
resetModel = new PasswordResetModel({}, {
method: 'GET',
url: '#'
});
// Initialize the passwordReset model
resetModel = new PasswordResetModel({}, {
method: 'GET',
url: '#'
});
// Initialize the login view
view = new LoginView({
fields: FORM_DESCRIPTION.fields,
model: model,
resetModel: resetModel,
thirdPartyAuth: THIRD_PARTY_AUTH,
platformName: PLATFORM_NAME
});
// Initialize the login view
view = new LoginView({
fields: FORM_DESCRIPTION.fields,
model: model,
resetModel: resetModel,
thirdPartyAuth: THIRD_PARTY_AUTH,
platformName: PLATFORM_NAME
});
// Spy on AJAX requests
requests = AjaxHelpers.requests(test);
// Spy on AJAX requests
requests = AjaxHelpers.requests(test);
// Intercept events from the view
authComplete = false;
view.on("auth-complete", function() {
authComplete = 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.'
// Intercept events from the view
authComplete = false;
view.on("auth-complete", function() {
authComplete = true;
});
}
};
var submitForm = function(validationSuccess) {
// Create a fake click event
var clickEvent = $.Event('click');
// 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);
// 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);
};
// Submit the email address
view.submitForm(clickEvent);
};
beforeEach(function() {
setFixtures('<div id="login-form"></div>');
TemplateHelpers.installTemplate('templates/student_account/login');
TemplateHelpers.installTemplate('templates/student_account/form_field');
});
beforeEach(function() {
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() {
createLoginView(this);
it('logs the user in', function() {
createLoginView(this);
// Submit the form, with successful validation
submitForm(true);
// Submit the form, with successful validation
submitForm(true);
// Form button should be disabled on success.
expect(view.$submitButton).toHaveAttr('disabled');
// Form button should be disabled on success.
expect(view.$submitButton).toHaveAttr('disabled');
// Verify that the client contacts the server with the expected data
AjaxHelpers.expectRequest(
requests, 'POST',
FORM_DESCRIPTION.submit_url,
$.param(USER_DATA)
);
// Verify that the client contacts the server with the expected data
AjaxHelpers.expectRequest(
requests, 'POST',
FORM_DESCRIPTION.submit_url,
$.param(USER_DATA)
);
// Respond with status code 200
AjaxHelpers.respondWithJson(requests, {});
// Respond with status code 200
AjaxHelpers.respondWithJson(requests, {});
// Verify that auth-complete is triggered
expect(authComplete).toBe(true);
});
// Verify that auth-complete is triggered
expect(authComplete).toBe(true);
});
it('sends analytics info containing the enrolled course ID', function() {
createLoginView(this);
it('sends analytics info containing the enrolled course ID', function() {
var expectedData;
// Simulate that the user is attempting to enroll in a course
// by setting the course_id query string param.
spyOn($, 'url').andCallFake(function( param ) {
if (param === "?course_id") {
return encodeURIComponent( COURSE_ID );
}
});
createLoginView(this);
// Attempt to login
submitForm( true );
// Simulate that the user is attempting to enroll in a course
// by setting the course_id query string param.
spyOn($, 'url').andCallFake(function( param ) {
if (param === '?course_id') {
return encodeURIComponent( COURSE_ID );
}
});
// Verify that the client sent the course ID for analytics
var expectedData = {};
$.extend(expectedData, USER_DATA, {
analytics: JSON.stringify({
enroll_course_id: COURSE_ID
})
});
// Attempt to login
submitForm( true );
AjaxHelpers.expectRequest(
requests, 'POST',
FORM_DESCRIPTION.submit_url,
$.param( expectedData )
);
});
// Verify that the client sent the course ID for analytics
expectedData = {};
$.extend(expectedData, USER_DATA, {
analytics: JSON.stringify({
enroll_course_id: COURSE_ID
})
});
AjaxHelpers.expectRequest(
requests, 'POST',
FORM_DESCRIPTION.submit_url,
$.param( expectedData )
);
});
it('displays third-party auth login buttons', function() {
createLoginView(this);
it('displays third-party auth login buttons', function() {
createLoginView(this);
// Verify that Google and Facebook registration buttons are displayed
expect($('.button-oa2-google-oauth2')).toBeVisible();
expect($('.button-oa2-facebook')).toBeVisible();
});
// Verify that Google and Facebook registration buttons are displayed
expect($('.button-oa2-google-oauth2')).toBeVisible();
expect($('.button-oa2-facebook')).toBeVisible();
});
it('displays a link to the password reset form', function() {
createLoginView(this);
it('displays a link to the password reset form', function() {
createLoginView(this);
// Verify that the password reset link is displayed
expect($('.forgot-password')).toBeVisible();
});
// Verify that the password reset link is displayed
expect($('.forgot-password')).toBeVisible();
});
it('validates login form fields', function() {
createLoginView(this);
it('validates login form fields', function() {
createLoginView(this);
submitForm(true);
submitForm(true);
// Verify that validation of form fields occurred
expect(view.validate).toHaveBeenCalledWith($('#login-email')[0]);
expect(view.validate).toHaveBeenCalledWith($('#login-password')[0]);
});
// Verify that validation of form fields occurred
expect(view.validate).toHaveBeenCalledWith($('#login-email')[0]);
expect(view.validate).toHaveBeenCalledWith($('#login-password')[0]);
});
it('displays login form validation errors', function() {
createLoginView(this);
it('displays login form validation errors', function() {
createLoginView(this);
// Submit the form, with failed validation
submitForm(false);
// Submit the form, with failed validation
submitForm(false);
// Verify that submission errors are visible
expect(view.$errors).not.toHaveClass('hidden');
// Verify that submission errors are visible
expect(view.$errors).not.toHaveClass('hidden');
// Expect auth complete NOT to have been triggered
expect(authComplete).toBe(false);
// Form button should be re-enabled when errors occur
expect(view.$submitButton).not.toHaveAttr('disabled');
});
// Expect auth complete NOT to have been triggered
expect(authComplete).toBe(false);
// Form button should be re-enabled when errors occur
expect(view.$submitButton).not.toHaveAttr('disabled');
});
it('displays an error if the server returns an error while logging in', function() {
createLoginView(this);
it('displays an error if the server returns an error while logging in', function() {
createLoginView(this);
// Submit the form, with successful validation
submitForm(true);
// Submit the form, with successful validation
submitForm(true);
// Simulate an error from the LMS servers
AjaxHelpers.respondWithError(requests);
// Simulate an error from the LMS servers
AjaxHelpers.respondWithError(requests);
// Expect that an error is displayed and that auth complete is not triggered
expect(view.$errors).not.toHaveClass('hidden');
expect(authComplete).toBe(false);
// Form button should be re-enabled on server failure.
expect(view.$submitButton).not.toHaveAttr('disabled');
// Expect that an error is displayed and that auth complete is not triggered
expect(view.$errors).not.toHaveClass('hidden');
expect(authComplete).toBe(false);
// Form button should be re-enabled on server failure.
expect(view.$submitButton).not.toHaveAttr('disabled');
// If we try again and succeed, the error should go away
submitForm();
// If we try again and succeed, the error should go away
submitForm();
// Form button should be disabled on success.
expect(view.$submitButton).toHaveAttr('disabled');
// Form button should be disabled on success.
expect(view.$submitButton).toHaveAttr('disabled');
// This time, respond with status code 200
AjaxHelpers.respondWithJson(requests, {});
// This time, respond with status code 200
AjaxHelpers.respondWithJson(requests, {});
// Expect that the error is hidden and auth complete is triggered
expect(view.$errors).toHaveClass('hidden');
expect(authComplete).toBe(true);
// Expect that the error is hidden and auth complete is triggered
expect(view.$errors).toHaveClass('hidden');
expect(authComplete).toBe(true);
});
});
});
});
}).call(this, define || RequireJS.define);
;(function (define) {
'use strict';
define([
'jquery',
'underscore',
'backbone',
'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/ajax_helpers',
'js/student_account/logistration_factory'
],
function($, _, Backbone, TemplateHelpers, AjaxHelpers, LogistrationFactory) {
describe('Logistration Factory', function() {
var FORM_DESCRIPTION = {
method: 'post',
submit_url: '/submit',
fields: [
{
name: 'email',
label: 'Email',
defaultValue: '',
type: 'text',
required: true,
placeholder: 'xsy@edx.org',
instructions: 'Enter your email here.',
restrictions: {}
},
{
name: 'username',
label: 'Username',
defaultValue: '',
type: 'text',
required: true,
placeholder: 'Xsy',
instructions: 'Enter your username here.',
restrictions: {
max_length: 200
}
}
]
};
var initializeLogistrationFactory = function(that, mode, nextUrl, finishAuthUrl) {
var options = {
initial_mode: mode,
third_party_auth: {
currentProvider: null,
providers: [],
secondaryProviders: [{name: 'provider'}],
finishAuthUrl: finishAuthUrl
},
login_redirect_url: nextUrl, // undefined for default
platform_name: 'edX',
login_form_desc: FORM_DESCRIPTION,
registration_form_desc: FORM_DESCRIPTION,
password_reset_form_desc: FORM_DESCRIPTION
};
// Initialize the logistration Factory
LogistrationFactory(options);
};
var assertForms = function(visibleForm, hiddenFormsList) {
expect($(visibleForm)).not.toHaveClass('hidden');
_.each(hiddenFormsList, function (hiddenForm) {
expect($(hiddenForm)).toHaveClass('hidden');
}, this);
};
beforeEach(function() {
setFixtures('<div id="login-and-registration-container" class="login-register" />');
TemplateHelpers.installTemplate('templates/student_account/access');
TemplateHelpers.installTemplate('templates/student_account/form_field');
TemplateHelpers.installTemplate('templates/student_account/login');
TemplateHelpers.installTemplate('templates/student_account/register');
TemplateHelpers.installTemplate('templates/student_account/password_reset')
});
afterEach(function() {
Backbone.history.stop();
});
it('can initially render the login form', function() {
var hiddenFormsList;
initializeLogistrationFactory(this, 'login');
/* Verify that only login form is expanded, and that the
/* all other logistration forms are collapsed.
*/
hiddenFormsList = [
'#register-form',
'#password-reset-form'
];
assertForms('#login-form', hiddenFormsList);
});
it('can initially render the registration form', function() {
var hiddenFormsList;
initializeLogistrationFactory(this, 'register');
/* Verify that only registration form is expanded, and that the
/* all other logistration forms are collapsed.
*/
hiddenFormsList = [
'#login-form',
'#password-reset-form'
];
assertForms('#register-form', hiddenFormsList);
});
it('can initially render the password reset form', function() {
var hiddenFormsList;
initializeLogistrationFactory(this, 'reset');
/* Verify that only password reset form is expanded, and that the
/* all other logistration forms are collapsed.
*/
hiddenFormsList = [
'#login-form',
'#register-form'
];
assertForms('#password-reset-form', hiddenFormsList);
});
});
});
}).call(this, define || RequireJS.define);
define([
'jquery',
'underscore',
'common/js/spec_helpers/template_helpers',
'common/js/spec_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';
;(function (define) {
'use strict';
define([
'jquery',
'underscore',
'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/ajax_helpers',
'js/student_account/models/PasswordResetModel',
'js/student_account/views/PasswordResetView'
],
function($, _, TemplateHelpers, AjaxHelpers, PasswordResetModel, PasswordResetView) {
describe('edx.student.account.PasswordResetView', function() {
var model = null,
view = null,
requests = null,
......@@ -46,12 +48,12 @@ define([
};
var submitEmail = function(validationSuccess) {
// Simulate manual entry of an email address
$('#password-reset-email').val(EMAIL);
// Create a fake click event
var clickEvent = $.Event('click');
// Simulate manual entry of an email address
$('#password-reset-email').val(EMAIL);
// If validationSuccess isn't passed, we avoid
// spying on `view.validate` twice
if ( !_.isUndefined(validationSuccess) ) {
......@@ -141,5 +143,5 @@ define([
expect(view.$errors).toHaveClass('hidden');
});
});
}
);
});
}).call(this, define || RequireJS.define);
define([
'jquery',
'underscore',
'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/ajax_helpers',
'js/student_account/models/RegisterModel',
'js/student_account/views/RegisterView'
], function($, _, TemplateHelpers, AjaxHelpers, RegisterModel, RegisterView) {
;(function (define) {
'use strict';
define([
'jquery',
'underscore',
'common/js/spec_helpers/template_helpers',
'common/js/spec_helpers/ajax_helpers',
'js/student_account/models/RegisterModel',
'js/student_account/views/RegisterView'
],
function($, _, TemplateHelpers, AjaxHelpers, RegisterModel, RegisterView) {
describe('edx.student.account.RegisterView', function() {
var model = null,
view = null,
requests = null,
authComplete = false,
PLATFORM_NAME = 'edX',
COURSE_ID = 'edX/DemoX/Fall',
USER_DATA = {
email: 'xsy@edx.org',
name: 'Xsy M. Education',
username: 'Xsy',
password: 'xsyisawesome',
level_of_education: 'p',
gender: 'm',
year_of_birth: 2014,
mailing_address: '141 Portland',
goals: 'To boldly learn what no letter of the alphabet has learned before',
honor_code: true
},
THIRD_PARTY_AUTH = {
currentProvider: null,
providers: [
{
id: 'oa2-google-oauth2',
name: 'Google',
iconClass: 'fa-google-plus',
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
},
{
id: 'oa2-facebook',
name: 'Facebook',
iconClass: 'fa-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/registration/',
fields: [
{
placeholder: 'username@domain.com',
name: 'email',
label: 'Email',
defaultValue: '',
type: 'email',
required: true,
instructions: 'Enter your email.',
restrictions: {}
},
{
placeholder: 'Jane Doe',
name: 'name',
label: 'Full Name',
defaultValue: '',
type: 'text',
required: true,
instructions: 'Enter your username.',
restrictions: {}
},
{
placeholder: 'JaneDoe',
name: 'username',
label: 'Username',
defaultValue: '',
type: 'text',
required: true,
instructions: 'Enter your username.',
restrictions: {}
},
{
placeholder: '',
name: 'password',
label: 'Password',
defaultValue: '',
type: 'password',
required: true,
instructions: 'Enter your password.',
restrictions: {}
},
{
placeholder: '',
name: 'level_of_education',
label: 'Highest Level of Education Completed',
defaultValue: '',
type: 'select',
options: [
{value: "", name: "--"},
{value: "p", name: "Doctorate"},
{value: "m", name: "Master's or professional degree"},
{value: "b", name: "Bachelor's degree"}
],
required: false,
instructions: 'Select your education level.',
restrictions: {}
},
{
placeholder: '',
name: 'gender',
label: 'Gender',
defaultValue: '',
type: 'select',
options: [
{value: "", name: "--"},
{value: "m", name: "Male"},
{value: "f", name: "Female"},
{value: "o", name: "Other"}
],
required: false,
instructions: 'Select your gender.',
restrictions: {}
},
{
placeholder: '',
name: 'year_of_birth',
label: 'Year of Birth',
defaultValue: '',
type: 'select',
options: [
{value: "", name: "--"},
{value: 1900, name: "1900"},
{value: 1950, name: "1950"},
{value: 2014, name: "2014"}
],
required: false,
instructions: 'Select your year of birth.',
restrictions: {}
},
{
placeholder: '',
name: 'mailing_address',
label: 'Mailing Address',
defaultValue: '',
type: 'textarea',
required: false,
instructions: 'Enter your mailing address.',
restrictions: {}
},
{
placeholder: '',
name: 'goals',
label: 'Goals',
defaultValue: '',
type: 'textarea',
required: false,
instructions: "If you'd like, tell us why you're interested in edX.",
restrictions: {}
},
{
placeholder: '',
name: 'honor_code',
label: 'I agree to the <a href="/honor">Terms of Service and Honor Code</a>',
defaultValue: '',
type: 'checkbox',
required: true,
instructions: '',
restrictions: {}
}
]
};
var createRegisterView = function(that) {
// Initialize the register model
model = new RegisterModel({}, {
url: FORM_DESCRIPTION.submit_url,
method: FORM_DESCRIPTION.method
});
describe('edx.student.account.RegisterView', function() {
var model = null,
view = null,
requests = null,
authComplete = false,
PLATFORM_NAME = 'edX',
COURSE_ID = "edX/DemoX/Fall",
USER_DATA = {
email: 'xsy@edx.org',
name: 'Xsy M. Education',
username: 'Xsy',
password: 'xsyisawesome',
level_of_education: 'p',
gender: 'm',
year_of_birth: 2014,
mailing_address: '141 Portland',
goals: 'To boldly learn what no letter of the alphabet has learned before',
honor_code: true
},
THIRD_PARTY_AUTH = {
currentProvider: null,
providers: [
{
id: 'oa2-google-oauth2',
name: 'Google',
iconClass: 'fa-google-plus',
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
},
{
id: 'oa2-facebook',
name: 'Facebook',
iconClass: 'fa-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/registration/',
fields: [
{
placeholder: 'username@domain.com',
name: 'email',
label: 'Email',
defaultValue: '',
type: 'email',
required: true,
instructions: 'Enter your email.',
restrictions: {}
},
{
placeholder: 'Jane Doe',
name: 'name',
label: 'Full Name',
defaultValue: '',
type: 'text',
required: true,
instructions: 'Enter your username.',
restrictions: {}
},
{
placeholder: 'JaneDoe',
name: 'username',
label: 'Username',
defaultValue: '',
type: 'text',
required: true,
instructions: 'Enter your username.',
restrictions: {}
},
{
placeholder: '',
name: 'password',
label: 'Password',
defaultValue: '',
type: 'password',
required: true,
instructions: 'Enter your password.',
restrictions: {}
},
{
placeholder: '',
name: 'level_of_education',
label: 'Highest Level of Education Completed',
defaultValue: '',
type: 'select',
options: [
{value: "", name: "--"},
{value: "p", name: "Doctorate"},
{value: "m", name: "Master's or professional degree"},
{value: "b", name: "Bachelor's degree"}
],
required: false,
instructions: 'Select your education level.',
restrictions: {}
},
{
placeholder: '',
name: 'gender',
label: 'Gender',
defaultValue: '',
type: 'select',
options: [
{value: "", name: "--"},
{value: "m", name: "Male"},
{value: "f", name: "Female"},
{value: "o", name: "Other"}
],
required: false,
instructions: 'Select your gender.',
restrictions: {}
},
{
placeholder: '',
name: 'year_of_birth',
label: 'Year of Birth',
defaultValue: '',
type: 'select',
options: [
{value: "", name: "--"},
{value: 1900, name: "1900"},
{value: 1950, name: "1950"},
{value: 2014, name: "2014"}
],
required: false,
instructions: 'Select your year of birth.',
restrictions: {}
},
{
placeholder: '',
name: 'mailing_address',
label: 'Mailing Address',
defaultValue: '',
type: 'textarea',
required: false,
instructions: 'Enter your mailing address.',
restrictions: {}
},
{
placeholder: '',
name: 'goals',
label: 'Goals',
defaultValue: '',
type: 'textarea',
required: false,
instructions: "If you'd like, tell us why you're interested in edX.",
restrictions: {}
},
{
placeholder: '',
name: 'honor_code',
label: 'I agree to the <a href="/honor">Terms of Service and Honor Code</a>',
defaultValue: '',
type: 'checkbox',
required: true,
instructions: '',
restrictions: {}
}
]
};
// Initialize the register view
view = new RegisterView({
fields: FORM_DESCRIPTION.fields,
model: model,
thirdPartyAuth: THIRD_PARTY_AUTH,
platformName: PLATFORM_NAME
});
var createRegisterView = function(that) {
// Initialize the register model
model = new RegisterModel({}, {
url: FORM_DESCRIPTION.submit_url,
method: FORM_DESCRIPTION.method
});
// Spy on AJAX requests
requests = AjaxHelpers.requests(that);
// Initialize the register view
view = new RegisterView({
fields: FORM_DESCRIPTION.fields,
model: model,
thirdPartyAuth: THIRD_PARTY_AUTH,
platformName: PLATFORM_NAME
});
// Intercept events from the view
authComplete = false;
view.on("auth-complete", function() {
authComplete = true;
});
};
// Spy on AJAX requests
requests = AjaxHelpers.requests(that);
var submitForm = function(validationSuccess) {
// Create a fake click event
var clickEvent = $.Event('click');
// Simulate manual entry of registration form data
$('#register-email').val(USER_DATA.email);
$('#register-name').val(USER_DATA.name);
$('#register-username').val(USER_DATA.username);
$('#register-password').val(USER_DATA.password);
$('#register-level_of_education').val(USER_DATA.level_of_education);
$('#register-gender').val(USER_DATA.gender);
$('#register-year_of_birth').val(USER_DATA.year_of_birth);
$('#register-mailing_address').val(USER_DATA.mailing_address);
$('#register-goals').val(USER_DATA.goals);
// Check the honor code checkbox
$('#register-honor_code').prop('checked', USER_DATA.honor_code);
// 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.'
});
}
// Intercept events from the view
authComplete = false;
view.on("auth-complete", function() {
authComplete = true;
});
};
var submitForm = function(validationSuccess) {
// Simulate manual entry of registration form data
$('#register-email').val(USER_DATA.email);
$('#register-name').val(USER_DATA.name);
$('#register-username').val(USER_DATA.username);
$('#register-password').val(USER_DATA.password);
$('#register-level_of_education').val(USER_DATA.level_of_education);
$('#register-gender').val(USER_DATA.gender);
$('#register-year_of_birth').val(USER_DATA.year_of_birth);
$('#register-mailing_address').val(USER_DATA.mailing_address);
$('#register-goals').val(USER_DATA.goals);
// Check the honor code checkbox
$('#register-honor_code').prop('checked', USER_DATA.honor_code);
// 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);
};
// Submit the email address
view.submitForm(clickEvent);
};
beforeEach(function() {
setFixtures('<div id="register-form"></div>');
TemplateHelpers.installTemplate('templates/student_account/register');
TemplateHelpers.installTemplate('templates/student_account/form_field');
});
beforeEach(function() {
setFixtures('<div id="register-form"></div>');
TemplateHelpers.installTemplate('templates/student_account/register');
TemplateHelpers.installTemplate('templates/student_account/form_field');
});
it('registers a new user', function() {
createRegisterView(this);
it('registers a new user', function() {
createRegisterView(this);
// Submit the form, with successful validation
submitForm(true);
// Submit the form, with successful validation
submitForm(true);
// Verify that the client contacts the server with the expected data
AjaxHelpers.expectRequest(
requests, 'POST',
FORM_DESCRIPTION.submit_url,
$.param( USER_DATA )
);
// Verify that the client contacts the server with the expected data
AjaxHelpers.expectRequest(
requests, 'POST',
FORM_DESCRIPTION.submit_url,
$.param( USER_DATA )
);
// Respond with status code 200
AjaxHelpers.respondWithJson(requests, {});
// Respond with status code 200
AjaxHelpers.respondWithJson(requests, {});
// Verify that auth complete is triggered
expect(authComplete).toBe(true);
// Form button should be disabled on success.
expect(view.$submitButton).toHaveAttr('disabled');
});
// Verify that auth complete is triggered
expect(authComplete).toBe(true);
// Form button should be disabled on success.
expect(view.$submitButton).toHaveAttr('disabled');
});
it('sends analytics info containing the enrolled course ID', function() {
var expectedData;
it('sends analytics info containing the enrolled course ID', function() {
createRegisterView(this);
createRegisterView(this);
// Simulate that the user is attempting to enroll in a course
// by setting the course_id query string param.
spyOn($, 'url').andCallFake(function( param ) {
if (param === "?course_id") {
return encodeURIComponent( COURSE_ID );
}
});
// Simulate that the user is attempting to enroll in a course
// by setting the course_id query string param.
spyOn($, 'url').andCallFake(function( param ) {
if (param === '?course_id') {
return encodeURIComponent( COURSE_ID );
}
});
// Attempt to register
submitForm( true );
// Attempt to register
submitForm( true );
// Verify that the client sent the course ID for analytics
var expectedData = {course_id: COURSE_ID};
$.extend(expectedData, USER_DATA);
// Verify that the client sent the course ID for analytics
expectedData = {course_id: COURSE_ID};
$.extend(expectedData, USER_DATA);
AjaxHelpers.expectRequest(
requests, 'POST',
FORM_DESCRIPTION.submit_url,
$.param( expectedData )
);
});
AjaxHelpers.expectRequest(
requests, 'POST',
FORM_DESCRIPTION.submit_url,
$.param( expectedData )
);
});
it('displays third-party auth registration buttons', function() {
createRegisterView(this);
it('displays third-party auth registration buttons', function() {
createRegisterView(this);
// Verify that Google and Facebook registration buttons are displayed
expect($('.button-oa2-google-oauth2')).toBeVisible();
expect($('.button-oa2-facebook')).toBeVisible();
});
// Verify that Google and Facebook registration buttons are displayed
expect($('.button-oa2-google-oauth2')).toBeVisible();
expect($('.button-oa2-facebook')).toBeVisible();
});
it('validates registration form fields', function() {
createRegisterView(this);
it('validates registration form fields', function() {
createRegisterView(this);
// Submit the form, with successful validation
submitForm(true);
// Submit the form, with successful validation
submitForm(true);
// Verify that validation of form fields occurred
expect(view.validate).toHaveBeenCalledWith($('#register-email')[0]);
expect(view.validate).toHaveBeenCalledWith($('#register-name')[0]);
expect(view.validate).toHaveBeenCalledWith($('#register-username')[0]);
expect(view.validate).toHaveBeenCalledWith($('#register-password')[0]);
// Verify that validation of form fields occurred
expect(view.validate).toHaveBeenCalledWith($('#register-email')[0]);
expect(view.validate).toHaveBeenCalledWith($('#register-name')[0]);
expect(view.validate).toHaveBeenCalledWith($('#register-username')[0]);
expect(view.validate).toHaveBeenCalledWith($('#register-password')[0]);
// Verify that no submission errors are visible
expect(view.$errors).toHaveClass('hidden');
// Form button should be disabled on success.
expect(view.$submitButton).toHaveAttr('disabled');
});
// Verify that no submission errors are visible
expect(view.$errors).toHaveClass('hidden');
// Form button should be disabled on success.
expect(view.$submitButton).toHaveAttr('disabled');
});
it('displays registration form validation errors', function() {
createRegisterView(this);
it('displays registration form validation errors', function() {
createRegisterView(this);
// Submit the form, with failed validation
submitForm(false);
// Submit the form, with failed validation
submitForm(false);
// Verify that submission errors are visible
expect(view.$errors).not.toHaveClass('hidden');
// Verify that submission errors are visible
expect(view.$errors).not.toHaveClass('hidden');
// Expect that auth complete is NOT triggered
expect(authComplete).toBe(false);
// Form button should be re-enabled on error.
expect(view.$submitButton).not.toHaveAttr('disabled');
});
// Expect that auth complete is NOT triggered
expect(authComplete).toBe(false);
// Form button should be re-enabled on error.
expect(view.$submitButton).not.toHaveAttr('disabled');
});
it('displays an error if the server returns an error while registering', function() {
createRegisterView(this);
it('displays an error if the server returns an error while registering', function() {
createRegisterView(this);
// Submit the form, with successful validation
submitForm(true);
// Submit the form, with successful validation
submitForm(true);
// Simulate an error from the LMS servers
AjaxHelpers.respondWithError(requests);
// Simulate an error from the LMS servers
AjaxHelpers.respondWithError(requests);
// Expect that an error is displayed and that auth complete is NOT triggered
expect(view.$errors).not.toHaveClass('hidden');
expect(authComplete).toBe(false);
// Expect that an error is displayed and that auth complete is NOT triggered
expect(view.$errors).not.toHaveClass('hidden');
expect(authComplete).toBe(false);
// If we try again and succeed, the error should go away
submitForm();
// If we try again and succeed, the error should go away
submitForm();
// This time, respond with status code 200
AjaxHelpers.respondWithJson(requests, {});
// This time, respond with status code 200
AjaxHelpers.respondWithJson(requests, {});
// Expect that the error is hidden and that auth complete is triggered
expect(view.$errors).toHaveClass('hidden');
expect(authComplete).toBe(true);
// Form button should be disabled on success.
expect(view.$submitButton).toHaveAttr('disabled');
// Expect that the error is hidden and that auth complete is triggered
expect(view.$errors).toHaveClass('hidden');
expect(authComplete).toBe(true);
// Form button should be disabled on success.
expect(view.$submitButton).toHaveAttr('disabled');
});
});
});
});
}).call(this, define || RequireJS.define);
var edx = edx || {};
(function($) {
'use strict';
edx.student = edx.student || {};
edx.student.account = edx.student.account || {};
var container = $('#login-and-registration-container');
return new edx.student.account.AccessView({
mode: container.data('initial-mode'),
thirdPartyAuth: container.data('third-party-auth'),
thirdPartyAuthHint: container.data('third-party-auth-hint'),
nextUrl: container.data('next-url'),
platformName: container.data('platform-name'),
loginFormDesc: container.data('login-form-desc'),
registrationFormDesc: container.data('registration-form-desc'),
passwordResetFormDesc: container.data('password-reset-form-desc')
});
})(jQuery);
;(function (define) {
'use strict';
define([
'jquery',
'js/student_account/views/AccessView'
],
function($, AccessView) {
return function(options) {
var $logistrationElement = $('#login-and-registration-container');
new AccessView(_.extend(options, {el: $logistrationElement}));
};
}
);
}).call(this, define || RequireJS.define);
var edx = edx || {};
(function($, Backbone) {
;(function (define) {
'use strict';
define([
'jquery',
'backbone',
'jquery.url'
], function ($, Backbone) {
return Backbone.Model.extend({
defaults: {
email: '',
password: '',
remember: false
},
ajaxType: '',
urlRoot: '',
initialize: function (attributes, options) {
this.ajaxType = options.method;
this.urlRoot = options.url;
},
sync: function (method, model) {
var headers = {'X-CSRFToken': $.cookie('csrftoken')},
data = {},
analytics,
courseId = $.url('?course_id');
// If there is a course ID in the query string param,
// send that to the server as well so it can be included
// in analytics events.
if (courseId) {
analytics = JSON.stringify({
enroll_course_id: decodeURIComponent(courseId)
});
}
edx.student = edx.student || {};
edx.student.account = edx.student.account || {};
edx.student.account.LoginModel = Backbone.Model.extend({
defaults: {
email: '',
password: '',
remember: false
},
ajaxType: '',
urlRoot: '',
initialize: function( attributes, options ) {
this.ajaxType = options.method;
this.urlRoot = options.url;
},
sync: function(method, model) {
var headers = { 'X-CSRFToken': $.cookie('csrftoken') },
data = {},
analytics,
courseId = $.url( '?course_id' );
// If there is a course ID in the query string param,
// send that to the server as well so it can be included
// in analytics events.
if ( courseId ) {
analytics = JSON.stringify({
enroll_course_id: decodeURIComponent( courseId )
// Include all form fields and analytics info in the data sent to the server
$.extend(data, model.attributes, {analytics: analytics});
$.ajax({
url: model.urlRoot,
type: model.ajaxType,
data: data,
headers: headers,
success: function () {
model.trigger('sync');
},
error: function (error) {
model.trigger('error', error);
}
});
}
// Include all form fields and analytics info in the data sent to the server
$.extend( data, model.attributes, { analytics: analytics });
$.ajax({
url: model.urlRoot,
type: model.ajaxType,
data: data,
headers: headers,
success: function() {
model.trigger('sync');
},
error: function( error ) {
model.trigger('error', error);
}
});
}
});
});
})(jQuery, Backbone);
}).call(this, define || RequireJS.define);
var edx = edx || {};
(function($, Backbone) {
;(function (define) {
'use strict';
edx.student = edx.student || {};
edx.student.account = edx.student.account || {};
edx.student.account.PasswordResetModel = Backbone.Model.extend({
defaults: {
email: ''
},
ajaxType: '',
urlRoot: '',
initialize: function( attributes, options ) {
this.ajaxType = options.method;
this.urlRoot = options.url;
},
sync: function( method, model ) {
var headers = {
'X-CSRFToken': $.cookie('csrftoken')
};
// Only expects an email address.
$.ajax({
url: model.urlRoot,
type: model.ajaxType,
data: model.attributes,
headers: headers,
success: function() {
model.trigger('sync');
},
error: function( error ) {
model.trigger('error', error);
}
});
}
define(['jquery', 'backbone'],
function($, Backbone) {
return Backbone.Model.extend({
defaults: {
email: ''
},
ajaxType: '',
urlRoot: '',
initialize: function( attributes, options ) {
this.ajaxType = options.method;
this.urlRoot = options.url;
},
sync: function( method, model ) {
var headers = {
'X-CSRFToken': $.cookie('csrftoken')
};
// Only expects an email address.
$.ajax({
url: model.urlRoot,
type: model.ajaxType,
data: model.attributes,
headers: headers,
success: function() {
model.trigger('sync');
},
error: function( error ) {
model.trigger('error', error);
}
});
}
});
});
})(jQuery, Backbone);
}).call(this, define || RequireJS.define);
var edx = edx || {};
(function($, Backbone) {
;(function (define) {
'use strict';
define(['jquery', 'backbone', 'jquery.url'],
function($, Backbone) {
return Backbone.Model.extend({
defaults: {
email: '',
name: '',
username: '',
password: '',
level_of_education: '',
gender: '',
year_of_birth: '',
mailing_address: '',
goals: ''
},
ajaxType: '',
urlRoot: '',
initialize: function( attributes, options ) {
this.ajaxType = options.method;
this.urlRoot = options.url;
},
sync: function(method, model) {
var headers = { 'X-CSRFToken': $.cookie('csrftoken') },
data = {},
courseId = $.url( '?course_id' );
// If there is a course ID in the query string param,
// send that to the server as well so it can be included
// in analytics events.
if ( courseId ) {
data.course_id = decodeURIComponent(courseId);
}
edx.student = edx.student || {};
edx.student.account = edx.student.account || {};
edx.student.account.RegisterModel = Backbone.Model.extend({
defaults: {
email: '',
name: '',
username: '',
password: '',
level_of_education: '',
gender: '',
year_of_birth: '',
mailing_address: '',
goals: '',
},
ajaxType: '',
urlRoot: '',
initialize: function( attributes, options ) {
this.ajaxType = options.method;
this.urlRoot = options.url;
},
sync: function(method, model) {
var headers = { 'X-CSRFToken': $.cookie('csrftoken') },
data = {},
courseId = $.url( '?course_id' );
// If there is a course ID in the query string param,
// send that to the server as well so it can be included
// in analytics events.
if ( courseId ) {
data.course_id = decodeURIComponent(courseId);
// Include all form fields and analytics info in the data sent to the server
$.extend( data, model.attributes);
$.ajax({
url: model.urlRoot,
type: model.ajaxType,
data: data,
headers: headers,
success: function() {
model.trigger('sync');
},
error: function( error ) {
model.trigger('error', error);
}
});
}
// Include all form fields and analytics info in the data sent to the server
$.extend( data, model.attributes);
$.ajax({
url: model.urlRoot,
type: model.ajaxType,
data: data,
headers: headers,
success: function() {
model.trigger('sync');
},
error: function( error ) {
model.trigger('error', error);
}
});
}
});
});
})(jQuery, Backbone);
}).call(this, define || RequireJS.define);
var edx = edx || {};
(function($, _, _s, Backbone, History) {
;(function (define) {
'use strict';
define([
'jquery',
'utility',
'underscore',
'underscore.string',
'backbone',
'js/student_account/models/LoginModel',
'js/student_account/models/PasswordResetModel',
'js/student_account/models/RegisterModel',
'js/student_account/views/LoginView',
'js/student_account/views/PasswordResetView',
'js/student_account/views/RegisterView',
'js/student_account/views/InstitutionLoginView',
'js/student_account/views/HintedLoginView',
'js/vendor/history'
],
function($, utility, _, _s, Backbone, LoginModel, PasswordResetModel, RegisterModel, LoginView,
PasswordResetView, RegisterView, InstitutionLoginView, HintedLoginView) {
if (_.isUndefined(_s)) {
_s = _.str;
}
edx.student = edx.student || {};
edx.student.account = edx.student.account || {};
edx.student.account.AccessView = Backbone.View.extend({
el: '#login-and-registration-container',
tpl: '#access-tpl',
events: {
'click .form-toggle': 'toggleForm'
},
subview: {
login: {},
register: {},
passwordHelp: {},
institutionLogin: {},
hintedLogin: {}
},
nextUrl: '/dashboard',
return Backbone.View.extend({
tpl: '#access-tpl',
events: {
'click .form-toggle': 'toggleForm'
},
subview: {
login: {},
register: {},
passwordHelp: {},
institutionLogin: {},
hintedLogin: {}
},
nextUrl: '/dashboard',
// The form currently loaded
activeForm: '',
// The form currently loaded
activeForm: '',
initialize: function( options ) {
initialize: function( obj ) {
/* Mix non-conflicting functions from underscore.string
* (all but include, contains, and reverse) into the
* Underscore namespace
*/
_.mixin( _s.exports() );
/* Mix non-conflicting functions from underscore.string
* (all but include, contains, and reverse) into the
* Underscore namespace
*/
_.mixin( _s.exports() );
this.tpl = $(this.tpl).html();
this.tpl = $(this.tpl).html();
this.activeForm = obj.mode || 'login';
this.activeForm = options.initial_mode || 'login';
this.thirdPartyAuth = obj.thirdPartyAuth || {
currentProvider: null,
providers: []
};
this.thirdPartyAuth = options.third_party_auth || {
currentProvider: null,
providers: []
};
this.thirdPartyAuthHint = obj.thirdPartyAuthHint || null;
this.thirdPartyAuthHint = options.third_party_auth_hint || null;
if (obj.nextUrl) {
// Ensure that the next URL is internal for security reasons
if ( ! window.isExternal( obj.nextUrl ) ) {
this.nextUrl = obj.nextUrl;
if (options.login_redirect_url) {
// Ensure that the next URL is internal for security reasons
if ( ! window.isExternal( options.login_redirect_url ) ) {
this.nextUrl = options.login_redirect_url;
}
}
}
this.formDescriptions = {
login: obj.loginFormDesc,
register: obj.registrationFormDesc,
reset: obj.passwordResetFormDesc,
institution_login: null,
hinted_login: null
};
this.platformName = obj.platformName;
// The login view listens for 'sync' events from the reset model
this.resetModel = new edx.student.account.PasswordResetModel({}, {
method: 'GET',
url: '#'
});
this.render();
// Once the third party error message has been shown once,
// there is no need to show it again, if the user changes mode:
this.thirdPartyAuth.errorMessage = null;
},
render: function() {
$(this.el).html( _.template( this.tpl, {
mode: this.activeForm
}));
this.postRender();
return this;
},
postRender: function() {
//get & check current url hash part & load form accordingly
if (Backbone.history.getHash() === "forgot-password-modal") {
this.resetPassword();
} else {
this.loadForm(this.activeForm);
}
},
loadForm: function( type ) {
var loadFunc = _.bind( this.load[type], this );
loadFunc( this.formDescriptions[type] );
},
load: {
login: function( data ) {
var model = new edx.student.account.LoginModel({}, {
method: data.method,
url: data.submit_url
});
this.formDescriptions = {
login: options.login_form_desc,
register: options.registration_form_desc,
reset: options.password_reset_form_desc,
institution_login: null,
hinted_login: null
};
this.subview.login = new edx.student.account.LoginView({
fields: data.fields,
model: model,
resetModel: this.resetModel,
thirdPartyAuth: this.thirdPartyAuth,
platformName: this.platformName
});
this.platformName = options.platform_name;
// Listen for 'password-help' event to toggle sub-views
this.listenTo( this.subview.login, 'password-help', this.resetPassword );
// The login view listens for 'sync' events from the reset model
this.resetModel = new PasswordResetModel({}, {
method: 'GET',
url: '#'
});
// Listen for 'auth-complete' event so we can enroll/redirect the user appropriately.
this.listenTo( this.subview.login, 'auth-complete', this.authComplete );
this.render();
// Once the third party error message has been shown once,
// there is no need to show it again, if the user changes mode:
this.thirdPartyAuth.errorMessage = null;
},
reset: function( data ) {
this.resetModel.ajaxType = data.method;
this.resetModel.urlRoot = data.submit_url;
render: function() {
$(this.el).html( _.template( this.tpl, {
mode: this.activeForm
}));
this.subview.passwordHelp = new edx.student.account.PasswordResetView({
fields: data.fields,
model: this.resetModel
});
this.postRender();
// Listen for 'password-email-sent' event to toggle sub-views
this.listenTo( this.subview.passwordHelp, 'password-email-sent', this.passwordEmailSent );
return this;
},
// Focus on the form
$('.password-reset-form').focus();
postRender: function() {
//get & check current url hash part & load form accordingly
if (Backbone.history.getHash() === 'forgot-password-modal') {
this.resetPassword();
} else {
this.loadForm(this.activeForm);
}
},
register: function( data ) {
var model = new edx.student.account.RegisterModel({}, {
method: data.method,
url: data.submit_url
});
loadForm: function( type ) {
var loadFunc = _.bind( this.load[type], this );
loadFunc( this.formDescriptions[type] );
},
this.subview.register = new edx.student.account.RegisterView({
fields: data.fields,
model: model,
thirdPartyAuth: this.thirdPartyAuth,
platformName: this.platformName
});
load: {
login: function( data ) {
var model = new LoginModel({}, {
method: data.method,
url: data.submit_url
});
this.subview.login = new LoginView({
fields: data.fields,
model: model,
resetModel: this.resetModel,
thirdPartyAuth: this.thirdPartyAuth,
platformName: this.platformName
});
// Listen for 'password-help' event to toggle sub-views
this.listenTo( this.subview.login, 'password-help', this.resetPassword );
// Listen for 'auth-complete' event so we can enroll/redirect the user appropriately.
this.listenTo( this.subview.login, 'auth-complete', this.authComplete );
},
reset: function( data ) {
this.resetModel.ajaxType = data.method;
this.resetModel.urlRoot = data.submit_url;
this.subview.passwordHelp = new PasswordResetView({
fields: data.fields,
model: this.resetModel
});
// Listen for 'password-email-sent' event to toggle sub-views
this.listenTo( this.subview.passwordHelp, 'password-email-sent', this.passwordEmailSent );
// Focus on the form
$('.password-reset-form').focus();
},
register: function( data ) {
var model = new RegisterModel({}, {
method: data.method,
url: data.submit_url
});
this.subview.register = new RegisterView({
fields: data.fields,
model: model,
thirdPartyAuth: this.thirdPartyAuth,
platformName: this.platformName
});
// Listen for 'auth-complete' event so we can enroll/redirect the user appropriately.
this.listenTo( this.subview.register, 'auth-complete', this.authComplete );
},
institution_login: function ( unused ) {
this.subview.institutionLogin = new InstitutionLoginView({
thirdPartyAuth: this.thirdPartyAuth,
platformName: this.platformName,
mode: this.activeForm
});
this.subview.institutionLogin.render();
},
hinted_login: function ( unused ) {
this.subview.hintedLogin = new HintedLoginView({
thirdPartyAuth: this.thirdPartyAuth,
hintedProvider: this.thirdPartyAuthHint,
platformName: this.platformName
});
this.subview.hintedLogin.render();
}
},
// Listen for 'auth-complete' event so we can enroll/redirect the user appropriately.
this.listenTo( this.subview.register, 'auth-complete', this.authComplete );
passwordEmailSent: function() {
var $loginAnchorElement = $('#login-anchor');
this.element.hide( $(this.el).find('#password-reset-anchor') );
this.element.show( $loginAnchorElement );
this.element.scrollTop( $loginAnchorElement );
},
institution_login: function ( unused ) {
this.subview.institutionLogin = new edx.student.account.InstitutionLoginView({
thirdPartyAuth: this.thirdPartyAuth,
platformName: this.platformName,
mode: this.activeForm
resetPassword: function() {
window.analytics.track('edx.bi.password_reset_form.viewed', {
category: 'user-engagement'
});
this.subview.institutionLogin.render();
this.element.hide( $(this.el).find('#login-anchor') );
this.loadForm('reset');
this.element.scrollTop( $('#password-reset-anchor') );
},
hinted_login: function ( unused ) {
this.subview.hintedLogin = new edx.student.account.HintedLoginView({
thirdPartyAuth: this.thirdPartyAuth,
hintedProvider: this.thirdPartyAuthHint,
platformName: this.platformName
toggleForm: function( e ) {
var type = $(e.currentTarget).data('type'),
$form = $('#' + type + '-form'),
$anchor = $('#' + type + '-anchor'),
queryParams = url('?'),
queryStr = queryParams.length > 0 ? '?' + queryParams : '';
e.preventDefault();
window.analytics.track('edx.bi.' + type + '_form.toggled', {
category: 'user-engagement'
});
this.subview.hintedLogin.render();
}
},
passwordEmailSent: function() {
this.element.hide( $(this.el).find('#password-reset-anchor') );
this.element.show( $('#login-anchor') );
this.element.scrollTop( $('#login-anchor') );
},
resetPassword: function() {
window.analytics.track('edx.bi.password_reset_form.viewed', {
category: 'user-engagement'
});
this.element.hide( $(this.el).find('#login-anchor') );
this.loadForm('reset');
this.element.scrollTop( $('#password-reset-anchor') );
},
toggleForm: function( e ) {
var type = $(e.currentTarget).data('type'),
$form = $('#' + type + '-form'),
$anchor = $('#' + type + '-anchor'),
queryParams = url('?'),
queryStr = queryParams.length > 0 ? '?' + queryParams : '';
e.preventDefault();
window.analytics.track('edx.bi.' + type + '_form.toggled', {
category: 'user-engagement'
});
// Load the form. Institution login is always refreshed since it changes based on the previous form.
if ( !this.form.isLoaded( $form ) || type == "institution_login") {
this.loadForm( type );
}
this.activeForm = type;
// Load the form. Institution login is always refreshed since it changes based on the previous form.
if ( !this.form.isLoaded( $form ) || type == 'institution_login') {
this.loadForm( type );
}
this.activeForm = type;
this.element.hide( $(this.el).find('.submission-success') );
this.element.hide( $(this.el).find('.form-wrapper') );
this.element.show( $form );
this.element.scrollTop( $anchor );
this.element.hide( $(this.el).find('.submission-success') );
this.element.hide( $(this.el).find('.form-wrapper') );
this.element.show( $form );
this.element.scrollTop( $anchor );
// Update url without reloading page
if (type != "institution_login") {
History.pushState( null, document.title, '/' + type + queryStr );
}
analytics.page( 'login_and_registration', type );
// Focus on the form
$("#" + type).focus();
},
/**
* Once authentication has completed successfully:
*
* If we're in a third party auth pipeline, we must complete the pipeline.
* Otherwise, redirect to the specified next step.
*
*/
authComplete: function() {
if (this.thirdPartyAuth && this.thirdPartyAuth.finishAuthUrl) {
this.redirect(this.thirdPartyAuth.finishAuthUrl);
// Note: the third party auth URL likely contains another redirect URL embedded inside
} else {
this.redirect(this.nextUrl);
}
},
/**
* Redirect to a URL. Mainly useful for mocking out in tests.
* @param {string} url The URL to redirect to.
*/
redirect: function( url ) {
window.location.replace(url);
},
form: {
isLoaded: function( $form ) {
return $form.html().length > 0;
}
},
/* Helper method to toggle display
* including accessibility considerations
*/
element: {
hide: function( $el ) {
$el.addClass('hidden');
// Update url without reloading page
if (type != 'institution_login') {
History.pushState( null, document.title, '/' + type + queryStr );
}
analytics.page( 'login_and_registration', type );
// Focus on the form
$('#' + type).focus();
},
/**
* Once authentication has completed successfully:
*
* If we're in a third party auth pipeline, we must complete the pipeline.
* Otherwise, redirect to the specified next step.
*
*/
authComplete: function() {
if (this.thirdPartyAuth && this.thirdPartyAuth.finishAuthUrl) {
this.redirect(this.thirdPartyAuth.finishAuthUrl);
// Note: the third party auth URL likely contains another redirect URL embedded inside
} else {
this.redirect(this.nextUrl);
}
},
/**
* Redirect to a URL. Mainly useful for mocking out in tests.
* @param {string} url The URL to redirect to.
*/
redirect: function( url ) {
window.location.replace(url);
},
scrollTop: function( $el ) {
// Scroll to top of selected element
$('html,body').animate({
scrollTop: $el.offset().top
},'slow');
form: {
isLoaded: function( $form ) {
return $form.html().length > 0;
}
},
show: function( $el ) {
$el.removeClass('hidden');
/* Helper method to toggle display
* including accessibility considerations
*/
element: {
hide: function( $el ) {
$el.addClass('hidden');
},
scrollTop: function( $el ) {
// Scroll to top of selected element
$('html,body').animate({
scrollTop: $el.offset().top
},'slow');
},
show: function( $el ) {
$el.removeClass('hidden');
}
}
}
});
});
})(jQuery, _, _.str, Backbone, History);
}).call(this, define || RequireJS.define);
var edx = edx || {};
(function($, _, Backbone, gettext) {
;(function (define) {
'use strict';
define([
'jquery',
'underscore',
'backbone',
'js/utils/edx.utils.validate'
],
function($, _, Backbone, EdxUtilsValidate) {
edx.student = edx.student || {};
edx.student.account = edx.student.account || {};
edx.student.account.FormView = Backbone.View.extend({
tagName: 'form',
return Backbone.View.extend({
tagName: 'form',
el: '',
el: '',
tpl: '',
tpl: '',
fieldTpl: '#form_field-tpl',
fieldTpl: '#form_field-tpl',
events: {},
events: {},
errors: [],
errors: [],
formType: '',
formType: '',
$form: {},
$form: {},
fields: [],
fields: [],
// String to append to required label fields
requiredStr: '*',
// String to append to required label fields
requiredStr: '*',
submitButton: '',
submitButton: '',
initialize: function( data ) {
this.model = data.model;
this.preRender( data );
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.tpl = $(this.tpl).html();
this.fieldTpl = $(this.fieldTpl).html();
this.buildForm( data.fields );
this.listenTo( this.model, 'error', this.saveError );
},
this.listenTo( this.model, 'error', this.saveError );
},
/* Allows extended views to add custom
* init steps without needing to repeat
* default init steps
*/
preRender: function( data ) {
/* Custom code goes here */
return data;
},
/* Allows extended views to add custom
* init steps without needing to repeat
* default init steps
*/
preRender: function( data ) {
/* Custom code goes here */
return data;
},
render: function( html ) {
var fields = html || '';
render: function( html ) {
var fields = html || '';
$(this.el).html( _.template( this.tpl, {
fields: fields
}));
$(this.el).html( _.template( this.tpl, {
fields: fields
}));
this.postRender();
this.postRender();
return this;
},
return this;
},
postRender: function() {
var $container = $(this.el);
postRender: function() {
var $container = $(this.el);
this.$form = $container.find('form');
this.$errors = $container.find('.submission-error');
this.$submitButton = $container.find(this.submitButton);
},
this.$form = $container.find('form');
this.$errors = $container.find('.submission-error');
this.$submitButton = $container.find(this.submitButton);
},
buildForm: function( data ) {
var html = [],
i,
len = data.length,
fieldTpl = this.fieldTpl;
buildForm: function( data ) {
var html = [],
i,
len = data.length,
fieldTpl = this.fieldTpl;
this.fields = data;
this.fields = data;
for ( i=0; i<len; i++ ) {
if ( data[i].errorMessages ) {
data[i].errorMessages = this.escapeStrings( data[i].errorMessages );
}
for ( i=0; i<len; i++ ) {
if ( data[i].errorMessages ) {
data[i].errorMessages = this.escapeStrings( data[i].errorMessages );
html.push( _.template( fieldTpl, $.extend( data[i], {
form: this.formType,
requiredStr: this.requiredStr
}) ) );
}
html.push( _.template( fieldTpl, $.extend( data[i], {
form: this.formType,
requiredStr: this.requiredStr
}) ) );
}
this.render( html.join('') );
},
this.render( html.join('') );
},
/* Helper method to toggle display
* including accessibility considerations
*/
element: {
hide: function( $el ) {
if ( $el ) {
$el.addClass('hidden');
/* Helper method to toggle display
* including accessibility considerations
*/
element: {
hide: function( $el ) {
if ( $el ) {
$el.addClass('hidden');
}
},
scrollTop: function( $el ) {
// Scroll to top of selected element
$('html,body').animate({
scrollTop: $el.offset().top
},'slow');
},
show: function( $el ) {
if ( $el ) {
$el.removeClass('hidden');
}
}
},
scrollTop: function( $el ) {
// Scroll to top of selected element
$('html,body').animate({
scrollTop: $el.offset().top
},'slow');
escapeStrings: function( obj ) {
_.each( obj, function( val, key ) {
obj[key] = _.escape( val );
});
return obj;
},
show: function( $el ) {
if ( $el ) {
$el.removeClass('hidden');
focusFirstError: function() {
var $error = this.$form.find('.error').first(),
$field = {},
$parent = {};
if ( $error.is('label') ) {
$parent = $error.parent('.form-field');
$error = $parent.find('input') || $parent.find('select');
} else {
$field = $error;
}
}
},
escapeStrings: function( obj ) {
_.each( obj, function( val, key ) {
obj[key] = _.escape( val );
});
return obj;
},
focusFirstError: function() {
var $error = this.$form.find('.error').first(),
$field = {},
$parent = {};
if ( $error.is('label') ) {
$parent = $error.parent('.form-field');
$error = $parent.find('input') || $parent.find('select');
} else {
$field = $error;
}
$error.focus();
},
forgotPassword: function( event ) {
event.preventDefault();
this.trigger('password-help');
},
getFormData: function() {
var obj = {},
$form = this.$form,
elements = $form[0].elements,
i,
len = elements.length,
$el,
$label,
key = '',
errors = [],
test = {};
for ( i=0; i<len; i++ ) {
$el = $( elements[i] );
$label = $form.find('label[for=' + $el.attr('id') + ']');
key = $el.attr('name') || false;
if ( key ) {
test = this.validate( elements[i] );
if ( test.isValid ) {
obj[key] = $el.attr('type') === 'checkbox' ? $el.is(':checked') : $el.val();
$el.removeClass('error');
$label.removeClass('error');
} else {
errors.push( test.message );
$el.addClass('error');
$label.addClass('error');
$error.focus();
},
forgotPassword: function( event ) {
event.preventDefault();
this.trigger('password-help');
},
getFormData: function() {
var obj = {},
$form = this.$form,
elements = $form[0].elements,
i,
len = elements.length,
$el,
$label,
key = '',
errors = [],
test = {};
for ( i=0; i<len; i++ ) {
$el = $( elements[i] );
$label = $form.find('label[for=' + $el.attr('id') + ']');
key = $el.attr('name') || false;
if ( key ) {
test = this.validate( elements[i] );
if ( test.isValid ) {
obj[key] = $el.attr('type') === 'checkbox' ? $el.is(':checked') : $el.val();
$el.removeClass('error');
$label.removeClass('error');
} else {
errors.push( test.message );
$el.addClass('error');
$label.addClass('error');
}
}
}
}
this.errors = _.uniq( errors );
this.errors = _.uniq( errors );
return obj;
},
return obj;
},
saveError: function( error ) {
this.errors = ['<li>' + error.responseText + '</li>'];
this.setErrors();
this.toggleDisableButton(false);
},
saveError: function( error ) {
this.errors = ['<li>' + error.responseText + '</li>'];
this.setErrors();
this.toggleDisableButton(false);
},
setErrors: function() {
var $msg = this.$errors.find('.message-copy'),
html = [],
errors = this.errors,
i,
len = errors.length;
setErrors: function() {
var $msg = this.$errors.find('.message-copy'),
html = [],
errors = this.errors,
i,
len = errors.length;
for ( i=0; i<len; i++ ) {
html.push( errors[i] );
}
for ( i=0; i<len; i++ ) {
html.push( errors[i] );
}
$msg.html( html.join('') );
$msg.html( html.join('') );
this.element.show( this.$errors );
this.element.show( this.$errors );
// Scroll to error messages
$('html,body').animate({
scrollTop: this.$errors.offset().top
},'slow');
// Focus on first error field
this.focusFirstError();
},
// Scroll to error messages
$('html,body').animate({
scrollTop: this.$errors.offset().top
},'slow');
submitForm: function( event ) {
var data = this.getFormData();
// Focus on first error field
this.focusFirstError();
},
if (!_.isUndefined(event)) {
event.preventDefault();
}
submitForm: function( event ) {
var data = this.getFormData();
this.toggleDisableButton(true);
if (!_.isUndefined(event)) {
event.preventDefault();
}
if ( !_.compact(this.errors).length ) {
this.model.set( data );
this.model.save();
this.toggleErrorMsg( false );
} else {
this.toggleErrorMsg( true );
}
this.toggleDisableButton(true);
this.postFormSubmission();
},
if ( !_.compact(this.errors).length ) {
this.model.set( data );
this.model.save();
this.toggleErrorMsg( false );
} else {
this.toggleErrorMsg( true );
}
/* Allows extended views to add custom
* code after form submission
*/
postFormSubmission: function() {
return true;
},
this.postFormSubmission();
},
toggleErrorMsg: function( show ) {
if ( show ) {
this.setErrors();
this.toggleDisableButton(false);
} else {
this.element.hide( this.$errors );
}
},
/* Allows extended views to add custom
* code after form submission
*/
postFormSubmission: function() {
return true;
},
/**
* If a form button is defined for this form, this will disable the button on
* submit, and re-enable the button if an error occurs.
*
* Args:
* disabled (boolean): If set to TRUE, disable the button.
*
*/
toggleDisableButton: function ( disabled ) {
if (this.$submitButton) {
this.$submitButton.attr('disabled', disabled);
}
},
toggleErrorMsg: function( show ) {
if ( show ) {
this.setErrors();
this.toggleDisableButton(false);
} else {
this.element.hide( this.$errors );
validate: function( $el ) {
return EdxUtilsValidate.validate( $el );
}
},
/**
* If a form button is defined for this form, this will disable the button on
* submit, and re-enable the button if an error occurs.
*
* Args:
* disabled (boolean): If set to TRUE, disable the button.
*
*/
toggleDisableButton: function ( disabled ) {
if (this.$submitButton) {
this.$submitButton.attr('disabled', disabled);
}
},
validate: function( $el ) {
return edx.utils.validate( $el );
}
});
});
})(jQuery, _, Backbone, gettext);
}).call(this, define || RequireJS.define);
var edx = edx || {};
(function($, _, gettext) {
;(function (define) {
'use strict';
edx.student = edx.student || {};
edx.student.account = edx.student.account || {};
edx.student.account.HintedLoginView = Backbone.View.extend({
el: '#hinted-login-form',
tpl: '#hinted_login-tpl',
events: {
'click .proceed-button': 'proceedWithHintedAuth'
},
formType: 'hinted-login',
initialize: function( data ) {
this.tpl = $(this.tpl).html();
this.hintedProvider = (
_.findWhere(data.thirdPartyAuth.providers, {id: data.hintedProvider}) ||
_.findWhere(data.thirdPartyAuth.secondaryProviders, {id: data.hintedProvider})
);
},
render: function() {
$(this.el).html( _.template( this.tpl, {
hintedProvider: this.hintedProvider
}));
return this;
},
proceedWithHintedAuth: function( event ) {
this.redirect(this.hintedProvider.loginUrl);
},
/**
* Redirect to a URL. Mainly useful for mocking out in tests.
* @param {string} url The URL to redirect to.
*/
redirect: function( url ) {
window.location.href = url;
}
define(['jquery', 'underscore', 'backbone'],
function($, _, Backbone) {
return Backbone.View.extend({
el: '#hinted-login-form',
tpl: '#hinted_login-tpl',
events: {
'click .proceed-button': 'proceedWithHintedAuth'
},
formType: 'hinted-login',
initialize: function( data ) {
this.tpl = $(this.tpl).html();
this.hintedProvider = (
_.findWhere(data.thirdPartyAuth.providers, {id: data.hintedProvider}) ||
_.findWhere(data.thirdPartyAuth.secondaryProviders, {id: data.hintedProvider})
);
},
render: function() {
$(this.el).html( _.template( this.tpl, {
hintedProvider: this.hintedProvider
}));
return this;
},
proceedWithHintedAuth: function( event ) {
this.redirect(this.hintedProvider.loginUrl);
},
/**
* Redirect to a URL. Mainly useful for mocking out in tests.
* @param {string} url The URL to redirect to.
*/
redirect: function( url ) {
window.location.href = url;
}
});
});
})(jQuery, _, gettext);
}).call(this, define || RequireJS.define);
var edx = edx || {};
(function($, _, Backbone) {
;(function (define) {
'use strict';
define(['jquery', 'underscore', 'backbone'],
function($, _, Backbone) {
edx.student = edx.student || {};
edx.student.account = edx.student.account || {};
edx.student.account.InstitutionLoginView = Backbone.View.extend({
el: '#institution_login-form',
return Backbone.View.extend({
el: '#institution_login-form',
initialize: function( data ) {
var tpl = data.mode == "register" ? '#institution_register-tpl' : '#institution_login-tpl';
this.tpl = $(tpl).html();
this.providers = data.thirdPartyAuth.secondaryProviders || [];
this.platformName = data.platformName;
},
initialize: function( data ) {
var tpl = data.mode == "register" ? '#institution_register-tpl' : '#institution_login-tpl';
this.tpl = $(tpl).html();
this.providers = data.thirdPartyAuth.secondaryProviders || [];
this.platformName = data.platformName;
},
render: function() {
$(this.el).html( _.template( this.tpl, {
// We pass the context object to the template so that
// we can perform variable interpolation using sprintf
providers: this.providers,
platformName: this.platformName
}));
render: function() {
$(this.el).html( _.template( this.tpl, {
// We pass the context object to the template so that
// we can perform variable interpolation using sprintf
providers: this.providers,
platformName: this.platformName
}));
return this;
}
return this;
}
});
});
})(jQuery, _, Backbone);
}).call(this, define || RequireJS.define);
var edx = edx || {};
(function($, _, gettext) {
;(function (define) {
'use strict';
edx.student = edx.student || {};
edx.student.account = edx.student.account || {};
edx.student.account.LoginView = edx.student.account.FormView.extend({
el: '#login-form',
tpl: '#login-tpl',
events: {
'click .js-login': 'submitForm',
'click .forgot-password': 'forgotPassword',
'click .login-provider': 'thirdPartyAuth'
},
formType: 'login',
requiredStr: '',
submitButton: '.js-login',
preRender: function( data ) {
this.providers = data.thirdPartyAuth.providers || [];
this.hasSecondaryProviders = (
data.thirdPartyAuth.secondaryProviders && data.thirdPartyAuth.secondaryProviders.length
);
this.currentProvider = data.thirdPartyAuth.currentProvider || '';
this.errorMessage = data.thirdPartyAuth.errorMessage || '';
this.platformName = data.platformName;
this.resetModel = data.resetModel;
this.listenTo( this.model, 'sync', this.saveSuccess );
this.listenTo( this.resetModel, 'sync', this.resetEmail );
},
render: function( html ) {
var fields = html || '';
$(this.el).html( _.template( this.tpl, {
// We pass the context object to the template so that
// we can perform variable interpolation using sprintf
context: {
fields: fields,
currentProvider: this.currentProvider,
errorMessage: this.errorMessage,
providers: this.providers,
hasSecondaryProviders: this.hasSecondaryProviders,
platformName: this.platformName
define([
'jquery',
'underscore',
'js/student_account/views/FormView'
],
function($, _, FormView) {
return FormView.extend({
el: '#login-form',
tpl: '#login-tpl',
events: {
'click .js-login': 'submitForm',
'click .forgot-password': 'forgotPassword',
'click .login-provider': 'thirdPartyAuth'
},
formType: 'login',
requiredStr: '',
submitButton: '.js-login',
preRender: function( data ) {
this.providers = data.thirdPartyAuth.providers || [];
this.hasSecondaryProviders = (
data.thirdPartyAuth.secondaryProviders && data.thirdPartyAuth.secondaryProviders.length
);
this.currentProvider = data.thirdPartyAuth.currentProvider || '';
this.errorMessage = data.thirdPartyAuth.errorMessage || '';
this.platformName = data.platformName;
this.resetModel = data.resetModel;
this.listenTo( this.model, 'sync', this.saveSuccess );
this.listenTo( this.resetModel, 'sync', this.resetEmail );
},
render: function( html ) {
var fields = html || '';
$(this.el).html( _.template( this.tpl, {
// We pass the context object to the template so that
// we can perform variable interpolation using sprintf
context: {
fields: fields,
currentProvider: this.currentProvider,
errorMessage: this.errorMessage,
providers: this.providers,
hasSecondaryProviders: this.hasSecondaryProviders,
platformName: this.platformName
}
}));
this.postRender();
return this;
},
postRender: function() {
this.$container = $(this.el);
this.$form = this.$container.find('form');
this.$errors = this.$container.find('.submission-error');
this.$resetSuccess = this.$container.find('.js-reset-success');
this.$authError = this.$container.find('.already-authenticated-msg');
this.$submitButton = this.$container.find(this.submitButton);
/* If we're already authenticated with a third-party
* provider, try logging in. The easiest way to do this
* is to simply submit the form.
*/
if (this.currentProvider) {
this.model.save();
}
}));
this.postRender();
return this;
},
postRender: function() {
this.$container = $(this.el);
this.$form = this.$container.find('form');
this.$errors = this.$container.find('.submission-error');
this.$resetSuccess = this.$container.find('.js-reset-success');
this.$authError = this.$container.find('.already-authenticated-msg');
this.$submitButton = this.$container.find(this.submitButton);
/* If we're already authenticated with a third-party
* provider, try logging in. The easiest way to do this
* is to simply submit the form.
*/
if (this.currentProvider) {
this.model.save();
}
},
},
forgotPassword: function( event ) {
event.preventDefault();
forgotPassword: function( event ) {
event.preventDefault();
this.trigger('password-help');
this.element.hide( this.$resetSuccess );
},
this.trigger('password-help');
this.element.hide( this.$resetSuccess );
},
postFormSubmission: function() {
this.element.hide( this.$resetSuccess );
},
postFormSubmission: function() {
this.element.hide( this.$resetSuccess );
},
resetEmail: function() {
this.element.hide( this.$errors );
this.element.show( this.$resetSuccess );
},
resetEmail: function() {
this.element.hide( this.$errors );
this.element.show( this.$resetSuccess );
},
thirdPartyAuth: function( event ) {
var providerUrl = $(event.currentTarget).data('provider-url') || '';
thirdPartyAuth: function( event ) {
var providerUrl = $(event.currentTarget).data('provider-url') || '';
if (providerUrl) {
window.location.href = providerUrl;
}
},
saveSuccess: function() {
this.trigger('auth-complete');
this.element.hide( this.$resetSuccess );
},
saveError: function( error ) {
this.errors = ['<li>' + error.responseText + '</li>'];
this.setErrors();
this.element.hide( this.$resetSuccess );
/* If we've gotten a 403 error, it means that we've successfully
* authenticated with a third-party provider, but we haven't
* linked the account to an EdX account. In this case,
* we need to prompt the user to enter a little more information
* to complete the registration process.
*/
if ( error.status === 403 &&
error.responseText === 'third-party-auth' &&
this.currentProvider ) {
this.element.show( this.$authError );
this.element.hide( this.$errors );
} else {
this.element.hide( this.$authError );
this.element.show( this.$errors );
if (providerUrl) {
window.location.href = providerUrl;
}
},
saveSuccess: function() {
this.trigger('auth-complete');
this.element.hide( this.$resetSuccess );
},
saveError: function( error ) {
this.errors = ['<li>' + error.responseText + '</li>'];
this.setErrors();
this.element.hide( this.$resetSuccess );
/* If we've gotten a 403 error, it means that we've successfully
* authenticated with a third-party provider, but we haven't
* linked the account to an EdX account. In this case,
* we need to prompt the user to enter a little more information
* to complete the registration process.
*/
if ( error.status === 403 &&
error.responseText === 'third-party-auth' &&
this.currentProvider ) {
this.element.show( this.$authError );
this.element.hide( this.$errors );
} else {
this.element.hide( this.$authError );
this.element.show( this.$errors );
}
this.toggleDisableButton(false);
}
this.toggleDisableButton(false);
}
});
});
})(jQuery, _, gettext);
}).call(this, define || RequireJS.define);
var edx = edx || {};
(function($, gettext) {
;(function (define) {
'use strict';
define([
'jquery',
'js/student_account/views/FormView'
],
function($, FormView) {
edx.student = edx.student || {};
edx.student.account = edx.student.account || {};
edx.student.account.PasswordResetView = edx.student.account.FormView.extend({
el: '#password-reset-form',
return FormView.extend({
el: '#password-reset-form',
tpl: '#password_reset-tpl',
tpl: '#password_reset-tpl',
events: {
'click .js-reset': 'submitForm'
},
events: {
'click .js-reset': 'submitForm'
},
formType: 'password-reset',
formType: 'password-reset',
requiredStr: '',
requiredStr: '',
submitButton: '.js-reset',
submitButton: '.js-reset',
preRender: function() {
this.element.show( $( this.el ) );
this.element.show( $( this.el ).parent() );
this.listenTo( this.model, 'sync', this.saveSuccess );
},
preRender: function() {
this.element.show( $( this.el ) );
this.element.show( $( this.el ).parent() );
this.listenTo( this.model, 'sync', this.saveSuccess );
},
toggleErrorMsg: function( show ) {
if ( show ) {
this.setErrors();
this.toggleDisableButton(false);
} else {
this.element.hide( this.$errors );
}
},
toggleErrorMsg: function( show ) {
if ( show ) {
this.setErrors();
this.toggleDisableButton(false);
} else {
this.element.hide( this.$errors );
}
},
saveSuccess: function() {
this.trigger('password-email-sent');
saveSuccess: function() {
this.trigger('password-email-sent');
// Destroy the view (but not el) and unbind events
this.$el.empty().off();
this.stopListening();
}
// Destroy the view (but not el) and unbind events
this.$el.empty().off();
this.stopListening();
}
});
});
})(jQuery, gettext);
}).call(this, define || RequireJS.define);
var edx = edx || {};
(function($, _, gettext) {
;(function (define) {
'use strict';
define([
'jquery',
'underscore',
'js/student_account/views/FormView'
],
function($, _, FormView) {
return FormView.extend({
el: '#register-form',
tpl: '#register-tpl',
events: {
'click .js-register': 'submitForm',
'click .login-provider': 'thirdPartyAuth'
},
formType: 'register',
submitButton: '.js-register',
preRender: function( data ) {
this.providers = data.thirdPartyAuth.providers || [];
this.hasSecondaryProviders = (
data.thirdPartyAuth.secondaryProviders && data.thirdPartyAuth.secondaryProviders.length
);
this.currentProvider = data.thirdPartyAuth.currentProvider || '';
this.errorMessage = data.thirdPartyAuth.errorMessage || '';
this.platformName = data.platformName;
this.autoSubmit = data.thirdPartyAuth.autoSubmitRegForm;
this.listenTo( this.model, 'sync', this.saveSuccess );
},
render: function( html ) {
var fields = html || '';
$(this.el).html( _.template( this.tpl, {
/* We pass the context object to the template so that
* we can perform variable interpolation using sprintf
*/
context: {
fields: fields,
currentProvider: this.currentProvider,
errorMessage: this.errorMessage,
providers: this.providers,
hasSecondaryProviders: this.hasSecondaryProviders,
platformName: this.platformName
}
}));
edx.student = edx.student || {};
edx.student.account = edx.student.account || {};
edx.student.account.RegisterView = edx.student.account.FormView.extend({
el: '#register-form',
tpl: '#register-tpl',
events: {
'click .js-register': 'submitForm',
'click .login-provider': 'thirdPartyAuth'
},
formType: 'register',
submitButton: '.js-register',
preRender: function( data ) {
this.providers = data.thirdPartyAuth.providers || [];
this.hasSecondaryProviders = (
data.thirdPartyAuth.secondaryProviders && data.thirdPartyAuth.secondaryProviders.length
);
this.currentProvider = data.thirdPartyAuth.currentProvider || '';
this.errorMessage = data.thirdPartyAuth.errorMessage || '';
this.platformName = data.platformName;
this.autoSubmit = data.thirdPartyAuth.autoSubmitRegForm;
this.listenTo( this.model, 'sync', this.saveSuccess );
},
render: function( html ) {
var fields = html || '';
$(this.el).html( _.template( this.tpl, {
/* We pass the context object to the template so that
* we can perform variable interpolation using sprintf
*/
context: {
fields: fields,
currentProvider: this.currentProvider,
errorMessage: this.errorMessage,
providers: this.providers,
hasSecondaryProviders: this.hasSecondaryProviders,
platformName: this.platformName
this.postRender();
if (this.autoSubmit) {
$(this.el).hide();
$('#register-honor_code').prop('checked', true);
this.submitForm();
}
}));
this.postRender();
return this;
},
if (this.autoSubmit) {
$(this.el).hide();
$('#register-honor_code').prop('checked', true);
this.submitForm();
}
thirdPartyAuth: function( event ) {
var providerUrl = $(event.currentTarget).data('provider-url') || '';
return this;
},
if ( providerUrl ) {
window.location.href = providerUrl;
}
},
thirdPartyAuth: function( event ) {
var providerUrl = $(event.currentTarget).data('provider-url') || '';
saveSuccess: function() {
this.trigger('auth-complete');
},
if ( providerUrl ) {
window.location.href = providerUrl;
}
},
saveSuccess: function() {
this.trigger('auth-complete');
},
saveError: function( error ) {
$(this.el).show(); // Show in case the form was hidden for auto-submission
this.errors = _.flatten(
_.map(
JSON.parse(error.responseText),
function(error_list) {
return _.map(
error_list,
function(error) { return "<li>" + error.user_message + "</li>"; }
);
}
)
);
this.setErrors();
this.toggleDisableButton(false);
},
postFormSubmission: function() {
if (_.compact(this.errors).length) {
// The form did not get submitted due to validation errors.
saveError: function( error ) {
$(this.el).show(); // Show in case the form was hidden for auto-submission
this.errors = _.flatten(
_.map(
JSON.parse(error.responseText),
function(error_list) {
return _.map(
error_list,
function(error) { return '<li>' + error.user_message + '</li>'; }
);
}
)
);
this.setErrors();
this.toggleDisableButton(false);
},
postFormSubmission: function() {
if (_.compact(this.errors).length) {
// The form did not get submitted due to validation errors.
$(this.el).show(); // Show in case the form was hidden for auto-submission
}
}
},
});
});
})(jQuery, _, gettext);
}).call(this, define || RequireJS.define);
......@@ -63,6 +63,7 @@ lib_paths:
- xmodule_js/common_static/js/test/i18n.js
- xmodule_js/common_static/js/vendor/date.js
- xmodule_js/common_static/js/vendor/moment.min.js
- xmodule_js/common_static/js/utils/edx.utils.validate.js
# Paths to source JavaScript files
src_paths:
......
......@@ -24,6 +24,7 @@
'js/groups/views/cohorts_dashboard_factory',
'js/search/course/course_search_factory',
'js/search/dashboard/dashboard_search_factory',
'js/student_account/logistration_factory',
'js/student_account/views/account_settings_factory',
'js/student_account/views/finish_auth_factory',
'js/student_profile/views/learner_profile_factory',
......
<%! from django.utils.translation import ugettext as _ %>
<%!
import json
from django.utils.translation import ugettext as _
from openedx.core.lib.json_utils import EscapedEdxJSONEncoder
%>
<%namespace name='static' file='/static_content.html'/>
<%inherit file="../main.html" />
......@@ -6,9 +10,10 @@
<%block name="pagetitle">${_("Sign in or Register")}</%block>
<%block name="js_extra">
<script src="${static.url('js/vendor/underscore.string.min.js')}"></script>
<script src="${static.url('js/vendor/history.js')}"></script>
<%static:js group='student_account'/>
<%static:require_module module_name="js/student_account/logistration_factory" class_name="LogistrationFactory">
var options = ${ json.dumps(data, cls=EscapedEdxJSONEncoder) };
LogistrationFactory(options);
</%static:require_module>
</%block>
<%block name="header_extras">
......@@ -20,17 +25,7 @@
</%block>
<div class="section-bkg-wrapper">
<div id="login-and-registration-container"
class="login-register"
data-initial-mode="${initial_mode}"
data-third-party-auth='${third_party_auth|h}'
data-third-party-auth-hint='${third_party_auth_hint}'
data-next-url='${login_redirect_url|h}'
data-platform-name='${platform_name}'
data-login-form-desc='${login_form_desc|h}'
data-registration-form-desc='${registration_form_desc|h}'
data-password-reset-form-desc='${password_reset_form_desc|h}'
/>
<div id="login-and-registration-container" class="login-register" />
</div>
% if settings.FEATURES.get('ENABLE_COMBINED_LOGIN_REGISTRATION'):
......
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