Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions common/test/acceptance/tests/lms/test_account_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def test_all_sections_and_fields_are_present(self):
'fields': [
'Username',
'Full Name',
'Email Address',
'Email Address (Sign In)',
'Password',
'Language',
'Country or Region of Residence',
Expand Down Expand Up @@ -289,7 +289,7 @@ def test_email_field(self):
self.visit_account_settings_page()
self._test_text_field(
u'email',
u'Email Address',
u'Email Address (Sign In)',
email,
u'test@example.com' + XSS_INJECTION,
[u'me@here.com', u'you@there.com'],
Expand Down
58 changes: 57 additions & 1 deletion lms/djangoapps/student_account/test/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from lms.djangoapps.commerce.tests.mocks import mock_get_orders
from openedx.core.djangoapps.oauth_dispatch.tests import factories as dot_factories
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme_context
from openedx.core.djangoapps.user_api.accounts.api import activate_account, create_account
Expand Down Expand Up @@ -727,8 +728,54 @@ def setUp(self):
MessageMiddleware().process_request(self.request)
messages.error(self.request, 'Facebook is already in use.', extra_tags='Auth facebook')

def test_context(self):
@mock.patch('student_account.views.get_enterprise_learner_data')
def test_context(self, mock_get_enterprise_learner_data):
self.request.site = SiteFactory.create()
mock_get_enterprise_learner_data.return_value = []
context = account_settings_context(self.request)

user_accounts_api_url = reverse("accounts_api", kwargs={'username': self.user.username})
self.assertEqual(context['user_accounts_api_url'], user_accounts_api_url)

user_preferences_api_url = reverse('preferences_api', kwargs={'username': self.user.username})
self.assertEqual(context['user_preferences_api_url'], user_preferences_api_url)

for attribute in self.FIELDS:
self.assertIn(attribute, context['fields'])

self.assertEqual(
context['user_accounts_api_url'], reverse("accounts_api", kwargs={'username': self.user.username})
)
self.assertEqual(
context['user_preferences_api_url'], reverse('preferences_api', kwargs={'username': self.user.username})
)

self.assertEqual(context['duplicate_provider'], 'facebook')
self.assertEqual(context['auth']['providers'][0]['name'], 'Facebook')
self.assertEqual(context['auth']['providers'][1]['name'], 'Google')

self.assertEqual(context['sync_learner_profile_data'], False)
self.assertEqual(context['edx_support_url'], settings.SUPPORT_SITE_LINK)
self.assertEqual(context['enterprise_name'], None)
self.assertEqual(
context['enterprise_readonly_account_fields'], {'fields': settings.ENTERPRISE_READONLY_ACCOUNT_FIELDS}
)

@mock.patch('student_account.views.get_enterprise_learner_data')
@mock.patch('student_account.views.third_party_auth.provider.Registry.get')
def test_context_for_enterprise_learner(
self, mock_get_auth_provider, mock_get_enterprise_learner_data
):
dummy_enterprise_customer = {
'uuid': 'real-ent-uuid',
'name': 'Dummy Enterprise',
'identity_provider': 'saml-ubc'
}
mock_get_enterprise_learner_data.return_value = [
{'enterprise_customer': dummy_enterprise_customer}
]
self.request.site = SiteFactory.create()
mock_get_auth_provider.return_value.sync_learner_profile_data = True
context = account_settings_context(self.request)

user_accounts_api_url = reverse("accounts_api", kwargs={'username': self.user.username})
Expand All @@ -751,6 +798,15 @@ def test_context(self):
self.assertEqual(context['auth']['providers'][0]['name'], 'Facebook')
self.assertEqual(context['auth']['providers'][1]['name'], 'Google')

self.assertEqual(
context['sync_learner_profile_data'], mock_get_auth_provider.return_value.sync_learner_profile_data
)
self.assertEqual(context['edx_support_url'], settings.SUPPORT_SITE_LINK)
self.assertEqual(context['enterprise_name'], dummy_enterprise_customer['name'])
self.assertEqual(
context['enterprise_readonly_account_fields'], {'fields': settings.ENTERPRISE_READONLY_ACCOUNT_FIELDS}
)

def test_view(self):
"""
Test that all fields are visible
Expand Down
18 changes: 17 additions & 1 deletion lms/djangoapps/student_account/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
)
from openedx.core.lib.edx_api_utils import get_edx_api_data
from openedx.core.lib.time_zone_utils import TIME_ZONE_CHOICES
from openedx.features.enterprise_support.api import enterprise_customer_for_request
from openedx.features.enterprise_support.api import enterprise_customer_for_request, get_enterprise_learner_data
from student.helpers import destroy_oauth_tokens, get_next_url_for_login_page
from student.models import UserProfile
from student.views import register_user as old_register_view
Expand Down Expand Up @@ -567,6 +567,22 @@ def account_settings_context(request):
'order_history': user_orders
}

enterprise_customer_name = None
sync_learner_profile_data = False
enterprise_learner_data = get_enterprise_learner_data(site=request.site, user=request.user)
if enterprise_learner_data:
enterprise_customer_name = enterprise_learner_data[0]['enterprise_customer']['name']
enterprise_idp = enterprise_learner_data[0]['enterprise_customer']['identity_provider']
identity_provider = third_party_auth.provider.Registry.get(provider_id=enterprise_idp)
sync_learner_profile_data = identity_provider.sync_learner_profile_data if identity_provider else False

context['sync_learner_profile_data'] = sync_learner_profile_data
context['edx_support_url'] = configuration_helpers.get_value('SUPPORT_SITE_LINK', settings.SUPPORT_SITE_LINK)
context['enterprise_name'] = enterprise_customer_name
context['enterprise_readonly_account_fields'] = {
'fields': settings.ENTERPRISE_READONLY_ACCOUNT_FIELDS
}

if third_party_auth.is_enabled():
# If the account on the third party provider is already connected with another edX account,
# we display a message to the user.
Expand Down
6 changes: 6 additions & 0 deletions lms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -3395,6 +3395,12 @@ def _make_locale_paths(settings):
'year_of_birth',
'mailing_address',
}
ENTERPRISE_READONLY_ACCOUNT_FIELDS = [
'username',
'name',
'email',
'country',
]
ENTERPRISE_CUSTOMER_COOKIE_NAME = 'enterprise_customer_uuid'
BASE_COOKIE_DOMAIN = 'localhost'

Expand Down
179 changes: 179 additions & 0 deletions lms/static/js/spec/student_account/account_settings_factory_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,183 @@ define(['backbone',
});
});
});

describe('edx.user.AccountSettingsFactory', function() {
var createEnterpriseLearnerAccountSettingsPage = function() {
var context = AccountSettingsPage(
Helpers.FIELDS_DATA,
[],
Helpers.AUTH_DATA,
Helpers.PASSWORD_RESET_SUPPORT_LINK,
Helpers.USER_ACCOUNTS_API_URL,
Helpers.USER_PREFERENCES_API_URL,
1,
Helpers.PLATFORM_NAME,
Helpers.CONTACT_EMAIL,
true,
'',

Helpers.SYNC_LEARNER_PROFILE_DATA,
Helpers.ENTERPRISE_NAME,
Helpers.ENTERPRISE_READ_ONLY_ACCOUNT_FIELDS,
Helpers.EDX_SUPPORT_URL
);
return context.accountSettingsView;
};

var requests;
var accountInfoTab = {
BASIC_ACCOUNT_INFORMATION: 0,
ADDITIONAL_INFORMATION: 1
};
var basicAccountInfoFields = {
USERNAME: 0,
FULL_NAME: 1,
EMAIL_ADDRESS: 2,
PASSWORD: 3,
LANGUAGE: 4,
COUNTRY: 5,
TIMEZONE: 6
};
var additionalInfoFields = {
EDUCATION: 0,
GENDER: 1,
YEAR_OF_BIRTH: 2,
PREFERRED_LANGUAGE: 3
};

beforeEach(function() {
setFixtures('<div class="wrapper-account-settings"></div>');
});

it('shows loading error when UserAccountModel fails to load for enterprise learners', function() {
var accountSettingsView, request;
requests = AjaxHelpers.requests(this);

accountSettingsView = createEnterpriseLearnerAccountSettingsPage();

Helpers.expectLoadingErrorIsVisible(accountSettingsView, false);

request = requests[0];
expect(request.method).toBe('GET');
expect(request.url).toBe(Helpers.USER_ACCOUNTS_API_URL);

AjaxHelpers.respondWithError(requests, 500);
Helpers.expectLoadingErrorIsVisible(accountSettingsView, true);
});

it('shows loading error when UserPreferencesModel fails to load for enterprise learners', function() {
var accountSettingsView, request;
requests = AjaxHelpers.requests(this);

accountSettingsView = createEnterpriseLearnerAccountSettingsPage();

Helpers.expectLoadingErrorIsVisible(accountSettingsView, false);

request = requests[0];
expect(request.method).toBe('GET');
expect(request.url).toBe(Helpers.USER_ACCOUNTS_API_URL);

AjaxHelpers.respondWithJson(requests, Helpers.createAccountSettingsData());
Helpers.expectLoadingErrorIsVisible(accountSettingsView, false);

request = requests[1];
expect(request.method).toBe('GET');
expect(request.url).toBe('/user_api/v1/preferences/time_zones/?country_code=1');
AjaxHelpers.respondWithJson(requests, Helpers.TIME_ZONE_RESPONSE);

request = requests[2];
expect(request.method).toBe('GET');
expect(request.url).toBe(Helpers.USER_PREFERENCES_API_URL);

AjaxHelpers.respondWithError(requests, 500);
Helpers.expectLoadingErrorIsVisible(accountSettingsView, true);
});

it('renders fields after the models are successfully fetched for enterprise learners', function() {
var accountSettingsView;
requests = AjaxHelpers.requests(this);

accountSettingsView = createEnterpriseLearnerAccountSettingsPage();

Helpers.expectLoadingErrorIsVisible(accountSettingsView, false);

AjaxHelpers.respondWithJson(requests, Helpers.createAccountSettingsData());
AjaxHelpers.respondWithJson(requests, Helpers.TIME_ZONE_RESPONSE);
AjaxHelpers.respondWithJson(requests, Helpers.createUserPreferencesData());

accountSettingsView.render();

Helpers.expectLoadingErrorIsVisible(accountSettingsView, false);
Helpers.expectSettingsSectionsAndFieldsToBeRenderedWithMessage(accountSettingsView);
});

it('expects all fields to behave correctly for enterprise learners', function() {
var accountSettingsView, i, view, sectionsData, textFields, dropdownFields;
requests = AjaxHelpers.requests(this);

accountSettingsView = createEnterpriseLearnerAccountSettingsPage();

AjaxHelpers.respondWithJson(requests, Helpers.createAccountSettingsData());
AjaxHelpers.respondWithJson(requests, Helpers.TIME_ZONE_RESPONSE);
AjaxHelpers.respondWithJson(requests, Helpers.createUserPreferencesData());
AjaxHelpers.respondWithJson(requests, {}); // Page viewed analytics event

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why analytics event required in that test?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These checks were implemented in existing code so I also move them in new tests.


sectionsData = accountSettingsView.options.tabSections.aboutTabSections;

expect(sectionsData[accountInfoTab.BASIC_ACCOUNT_INFORMATION].fields.length).toBe(7);

// Verify that username, name and email fields are readonly
textFields = [
sectionsData[accountInfoTab.BASIC_ACCOUNT_INFORMATION].fields[basicAccountInfoFields.USERNAME],
sectionsData[accountInfoTab.BASIC_ACCOUNT_INFORMATION].fields[basicAccountInfoFields.FULL_NAME],
sectionsData[accountInfoTab.BASIC_ACCOUNT_INFORMATION].fields[basicAccountInfoFields.EMAIL_ADDRESS]
];
for (i = 0; i < textFields.length; i++) {
view = textFields[i].view;

FieldViewsSpecHelpers.verifyReadonlyTextField(view, {
title: view.options.title,
valueAttribute: view.options.valueAttribute,
helpMessage: view.options.helpMessage,
validValue: 'My Name',
defaultValue: ''
}, requests);
}

// Verify un-editable country dropdown field
view = sectionsData[
accountInfoTab.BASIC_ACCOUNT_INFORMATION
].fields[basicAccountInfoFields.COUNTRY].view;

FieldViewsSpecHelpers.verifyReadonlyDropDownField(view, {
title: view.options.title,
valueAttribute: view.options.valueAttribute,
helpMessage: '',
validValue: Helpers.FIELD_OPTIONS[1][0],
editable: 'never',
defaultValue: null
});

expect(sectionsData[accountInfoTab.ADDITIONAL_INFORMATION].fields.length).toBe(4);
dropdownFields = [
sectionsData[accountInfoTab.ADDITIONAL_INFORMATION].fields[additionalInfoFields.EDUCATION],
sectionsData[accountInfoTab.ADDITIONAL_INFORMATION].fields[additionalInfoFields.GENDER],
sectionsData[accountInfoTab.ADDITIONAL_INFORMATION].fields[additionalInfoFields.YEAR_OF_BIRTH]
];
_.each(dropdownFields, function(field) {
view = field.view;
FieldViewsSpecHelpers.verifyDropDownField(view, {
title: view.options.title,
valueAttribute: view.options.valueAttribute,
helpMessage: '',
validValue: Helpers.FIELD_OPTIONS[1][0], // dummy option for dropdown field
invalidValue1: Helpers.FIELD_OPTIONS[2][0], // dummy option for dropdown field
invalidValue2: Helpers.FIELD_OPTIONS[3][0], // dummy option for dropdown field
validationError: 'Nope, this will not do!',
defaultValue: null
}, requests);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ define(['backbone',
var aboutSectionsData = [
{
title: 'Basic Account Information',
messageType: 'info',
message: 'Your profile settings are managed by Test Enterprise. ' +
'Contact your administrator or <a href="https://support.edx.org/">edX Support</a> for help.',
fields: [
{
view: new FieldViews.ReadonlyFieldView({
Expand Down
Loading