diff --git a/common/test/acceptance/pages/lms/learner_profile.py b/common/test/acceptance/pages/lms/learner_profile.py index a27c7b8144..50d1a9dcb0 100644 --- a/common/test/acceptance/pages/lms/learner_profile.py +++ b/common/test/acceptance/pages/lms/learner_profile.py @@ -149,3 +149,19 @@ class LearnerProfilePage(FieldsMixin, PageObject): EmptyPromise(lambda: self.field_is_visible('country'), 'Country field is visible').fulfill() EmptyPromise(lambda: self.field_is_visible('language_proficiencies'), 'Language field is visible').fulfill() EmptyPromise(lambda: self.field_is_visible('bio'), 'About Me field is visible').fulfill() + + @property + def profile_forced_private_message(self): + """ + Returns age limit message. + """ + self.wait_for_ajax() + return self.q(css='#u-field-message-account_privacy').text[0] + + @property + def age_limit_message_present(self): + """ + Check if age limit message is present. + """ + self.wait_for_ajax() + return self.q(css='#u-field-message-account_privacy').visible diff --git a/common/test/acceptance/tests/lms/test_account_settings.py b/common/test/acceptance/tests/lms/test_account_settings.py index 4558171360..dd73ebb876 100644 --- a/common/test/acceptance/tests/lms/test_account_settings.py +++ b/common/test/acceptance/tests/lms/test_account_settings.py @@ -226,6 +226,7 @@ class AccountSettingsPageTest(WebAppTest): """ Test behaviour of "Year of Birth" field. """ + self.assertEqual(self.account_settings_page.value_for_dropdown_field('year_of_birth', ''), '') self._test_dropdown_field( u'year_of_birth', u'Year of Birth', diff --git a/common/test/acceptance/tests/lms/test_learner_profile.py b/common/test/acceptance/tests/lms/test_learner_profile.py index 6fb13b689f..6e6a9dd837 100644 --- a/common/test/acceptance/tests/lms/test_learner_profile.py +++ b/common/test/acceptance/tests/lms/test_learner_profile.py @@ -52,6 +52,9 @@ class LearnerProfilePageTest(WebAppTest): self.other_profile_page = LearnerProfilePage(self.browser, self.USER_2_NAME) + self.set_birth_year(self.MY_USER, birth_year='1990') + self.set_birth_year(self.OTHER_USER, birth_year='1990') + def authenticate_as_user(self, user): """ Auto authenticate a user. @@ -69,11 +72,13 @@ class LearnerProfilePageTest(WebAppTest): profile_page.value_for_dropdown_field('country', 'United Kingdom') profile_page.value_for_textarea_field('bio', 'Nothing Special') - def visit_my_profile_page(self, user, privacy=None): + def visit_my_profile_page(self, user, privacy=None, authenticate=True): """ Visits a users profile page. """ - self.authenticate_as_user(user) + if authenticate: + self.authenticate_as_user(user) + self.my_profile_page.visit() self.my_profile_page.wait_for_page() @@ -92,10 +97,6 @@ class LearnerProfilePageTest(WebAppTest): self.other_profile_page.wait_for_page() if user is self.OTHER_USER and privacy is not None: - self.account_settings_page.visit() - self.account_settings_page.wait_for_page() - self.assertEqual(self.account_settings_page.value_for_dropdown_field('year_of_birth', '1980'), '1980') - self.other_profile_page.visit() self.other_profile_page.wait_for_page() self.other_profile_page.privacy = privacy @@ -103,6 +104,25 @@ class LearnerProfilePageTest(WebAppTest): if privacy == self.PRIVACY_PUBLIC: self.set_pubilc_profile_fields_data(self.other_profile_page) + def set_birth_year(self, user, birth_year): + """ + Set birth year for `user` to the specified value. + """ + self.authenticate_as_user(user) + self.account_settings_page.visit() + self.account_settings_page.wait_for_page() + self.assertEqual(self.account_settings_page.value_for_dropdown_field('year_of_birth', birth_year), birth_year) + + def verify_profile_forced_private_message(self, birth_year, message=None): + """ + Verify age limit messages for a user. + """ + self.set_birth_year(self.MY_USER, birth_year=birth_year) + self.visit_my_profile_page(self.MY_USER, authenticate=False) + self.assertTrue(self.my_profile_page.privacy_field_visible) + self.assertEqual(self.my_profile_page.age_limit_message_present, message is not None) + self.assertIn(message, self.my_profile_page.profile_forced_private_message) + def test_dashboard_learner_profile_link(self): """ Scenario: Verify that my profile link is present on dashboard page and we can navigate to correct page. @@ -212,7 +232,7 @@ class LearnerProfilePageTest(WebAppTest): """ Test behaviour of a textarea field. """ - self.visit_my_profile_page(self.MY_USER, privacy=self.PRIVACY_PUBLIC) + self.visit_my_profile_page(self.MY_USER, privacy=self.PRIVACY_PUBLIC, ) self.my_profile_page.value_for_textarea_field(field_id, new_value) self.assertEqual(self.my_profile_page.get_non_editable_mode_value(field_id), displayed_value) @@ -305,3 +325,26 @@ class LearnerProfilePageTest(WebAppTest): self.my_profile_page.make_field_editable('bio') self.assertTrue(self.my_profile_page.mode_for_field('bio'), 'edit') + + def test_birth_year_not_set(self): + """ + Verify message if birth year is not set. + + Given that I am a registered user. + And birth year is not set for the user. + And i visit my profile page. + Then i should see message `Your profile is disabled because you haven't filled in your Year of Birth.` + """ + message = "You must specify your birth year before you can share your full profile." + self.verify_profile_forced_private_message('', message=message) + + def test_user_is_under_age(self): + """ + Verify message if user is under age. + + Given that I am a registered user. + And birth year is set so that age is less than 13. + And i visit my profile page. + Then i should see message `You must be over 13 to share a full profile.` + """ + self.verify_profile_forced_private_message('2010', message='You must be over 13 to share a full profile.') diff --git a/lms/static/js/spec/student_account/helpers.js b/lms/static/js/spec/student_account/helpers.js index f110bcfcce..12defc5f7b 100644 --- a/lms/static/js/spec/student_account/helpers.js +++ b/lms/static/js/spec/student_account/helpers.js @@ -15,7 +15,8 @@ define(['underscore'], function(_) { country: '0', language: '0', bio: "About the student", - language_proficiencies: [{code: '1'}] + language_proficiencies: [{code: '1'}], + requires_parental_consent: true }; var USER_PREFERENCES_DATA = { diff --git a/lms/static/js/spec/student_profile/helpers.js b/lms/static/js/spec/student_profile/helpers.js index 777061d69c..543e972dbd 100644 --- a/lms/static/js/spec/student_profile/helpers.js +++ b/lms/static/js/spec/student_profile/helpers.js @@ -35,7 +35,7 @@ define(['underscore'], function(_) { expect(privacyFieldElement.length).toBe(0); } else { expect(privacyFieldElement.length).toBe(1); - expectProfileElementContainsField(privacyFieldElement, learnerProfileView.options.accountPrivacyFieldView) + expectProfileElementContainsField(privacyFieldElement, learnerProfileView.options.accountPrivacyFieldView); } }; diff --git a/lms/static/js/spec/student_profile/learner_profile_factory_spec.js b/lms/static/js/spec/student_profile/learner_profile_factory_spec.js index 79807d5ea3..b67a83dc4b 100644 --- a/lms/static/js/spec/student_profile/learner_profile_factory_spec.js +++ b/lms/static/js/spec/student_profile/learner_profile_factory_spec.js @@ -24,26 +24,25 @@ define(['backbone', 'jquery', 'underscore', 'js/common_helpers/ajax_helpers', 'j TemplateHelpers.installTemplate('templates/student_profile/learner_profile'); }); - it("show loading error when UserAccountModel fails to load", function() { - - requests = AjaxHelpers.requests(this); - - var context = LearnerProfilePage({ + var createProfilePage = function(ownProfile) { + return LearnerProfilePage({ 'accounts_api_url': Helpers.USER_ACCOUNTS_API_URL, 'preferences_api_url': Helpers.USER_PREFERENCES_API_URL, - 'own_profile': true, + 'own_profile': ownProfile, 'account_settings_page_url': Helpers.USER_ACCOUNTS_API_URL, 'country_options': Helpers.FIELD_OPTIONS, 'language_options': Helpers.FIELD_OPTIONS, 'has_preferences_access': true - }), + }) + }; + + it("show loading error when UserAccountModel fails to load", function() { + + requests = AjaxHelpers.requests(this); + + var context = createProfilePage(true), learnerProfileView = context.learnerProfileView; - Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, true); - Helpers.expectLoadingErrorIsVisible(learnerProfileView, false); - LearnerProfileHelpers.expectProfileSectionsNotToBeRendered(learnerProfileView); - - var userAccountRequest = requests[0]; expect(userAccountRequest.method).toBe('GET'); expect(userAccountRequest.url).toBe(Helpers.USER_ACCOUNTS_API_URL); @@ -59,21 +58,9 @@ define(['backbone', 'jquery', 'underscore', 'js/common_helpers/ajax_helpers', 'j requests = AjaxHelpers.requests(this); - var context = LearnerProfilePage({ - 'accounts_api_url': Helpers.USER_ACCOUNTS_API_URL, - 'preferences_api_url': Helpers.USER_PREFERENCES_API_URL, - 'own_profile': true, - 'account_settings_page_url': Helpers.USER_ACCOUNTS_API_URL, - 'country_options': Helpers.FIELD_OPTIONS, - 'language_options': Helpers.FIELD_OPTIONS, - 'has_preferences_access': true - }), + var context = createProfilePage(true), learnerProfileView = context.learnerProfileView; - Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, true); - Helpers.expectLoadingErrorIsVisible(learnerProfileView, false); - LearnerProfileHelpers.expectProfileSectionsNotToBeRendered(learnerProfileView); - var userAccountRequest = requests[0]; expect(userAccountRequest.method).toBe('GET'); expect(userAccountRequest.url).toBe(Helpers.USER_ACCOUNTS_API_URL); @@ -97,21 +84,8 @@ define(['backbone', 'jquery', 'underscore', 'js/common_helpers/ajax_helpers', 'j requests = AjaxHelpers.requests(this); - var context = LearnerProfilePage({ - 'accounts_api_url': Helpers.USER_ACCOUNTS_API_URL, - 'preferences_api_url': Helpers.USER_PREFERENCES_API_URL, - 'own_profile': true, - 'account_settings_page_url': Helpers.USER_ACCOUNTS_API_URL, - 'country_options': Helpers.FIELD_OPTIONS, - 'language_options': Helpers.FIELD_OPTIONS, - 'has_preferences_access': true - }); - - var learnerProfileView = context.learnerProfileView; - - Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, true); - Helpers.expectLoadingErrorIsVisible(learnerProfileView, false); - LearnerProfileHelpers.expectProfileSectionsNotToBeRendered(learnerProfileView); + var context = createProfilePage(true), + learnerProfileView = context.learnerProfileView; AjaxHelpers.respondWithJson(requests, Helpers.USER_ACCOUNTS_DATA); AjaxHelpers.respondWithJson(requests, Helpers.USER_PREFERENCES_DATA); @@ -124,27 +98,48 @@ define(['backbone', 'jquery', 'underscore', 'js/common_helpers/ajax_helpers', 'j requests = AjaxHelpers.requests(this); - var context = LearnerProfilePage({ - 'accounts_api_url': Helpers.USER_ACCOUNTS_API_URL, - 'preferences_api_url': Helpers.USER_PREFERENCES_API_URL, - 'own_profile': true, - 'account_settings_page_url': Helpers.USER_ACCOUNTS_API_URL, - 'country_options': Helpers.FIELD_OPTIONS, - 'language_options': Helpers.FIELD_OPTIONS, - 'has_preferences_access': true - }), + var context = createProfilePage(true), learnerProfileView = context.learnerProfileView; - Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, true); - Helpers.expectLoadingErrorIsVisible(learnerProfileView, false); - LearnerProfileHelpers.expectProfileSectionsNotToBeRendered(learnerProfileView); + var accountSettingsData = Helpers.USER_ACCOUNTS_DATA; + accountSettingsData['year_of_birth'] = 1989; + accountSettingsData['requires_parental_consent'] = false; - AjaxHelpers.respondWithJson(requests, Helpers.USER_ACCOUNTS_DATA); + AjaxHelpers.respondWithJson(requests, accountSettingsData); AjaxHelpers.respondWithJson(requests, Helpers.USER_PREFERENCES_DATA); // sets the profile for full view. context.accountPreferencesModel.set({account_privacy: 'all_users'}); LearnerProfileHelpers.expectProfileSectionsAndFieldsToBeRendered(learnerProfileView, false) }); + + it("renders the limited profile for undefined 'year_of_birth'", function() { + + requests = AjaxHelpers.requests(this); + + var context = createProfilePage(true), + learnerProfileView = context.learnerProfileView; + + AjaxHelpers.respondWithJson(requests, Helpers.USER_ACCOUNTS_DATA); + AjaxHelpers.respondWithJson(requests, Helpers.USER_PREFERENCES_DATA); + + LearnerProfileHelpers.expectLimitedProfileSectionsAndFieldsToBeRendered(learnerProfileView) + }); + + it("renders the limited profile for under 13 users", function() { + + requests = AjaxHelpers.requests(this); + + var context = createProfilePage(true), + learnerProfileView = context.learnerProfileView; + + var accountSettingsData = Helpers.USER_ACCOUNTS_DATA; + accountSettingsData['requires_parental_consent'] = true; + + AjaxHelpers.respondWithJson(requests, accountSettingsData); + AjaxHelpers.respondWithJson(requests, Helpers.USER_PREFERENCES_DATA); + + LearnerProfileHelpers.expectLimitedProfileSectionsAndFieldsToBeRendered(learnerProfileView) + }); }); }); diff --git a/lms/static/js/spec/student_profile/learner_profile_view_spec.js b/lms/static/js/spec/student_profile/learner_profile_view_spec.js index 484501d9b2..e7f9c7b0fb 100644 --- a/lms/static/js/spec/student_profile/learner_profile_view_spec.js +++ b/lms/static/js/spec/student_profile/learner_profile_view_spec.js @@ -12,12 +12,15 @@ define(['backbone', 'jquery', 'underscore', 'js/common_helpers/ajax_helpers', 'j AccountPreferencesModel, LearnerProfileFields, LearnerProfileView, AccountSettingsFieldViews) { 'use strict'; - describe("edx.user.LearnerProfileView", function (options) { + describe("edx.user.LearnerProfileView", function () { var createLearnerProfileView = function (ownProfile, accountPrivacy, profileIsPublic) { var accountSettingsModel = new UserAccountModel(); - accountSettingsModel.set(Helpers.USER_ACCOUNTS_DATA); + var accountSettingsData = Helpers.USER_ACCOUNTS_DATA; + accountSettingsData['year_of_birth'] = 1989; + accountSettingsData['requires_parental_consent'] = false; + accountSettingsModel.set(accountSettingsData); accountSettingsModel.set({'profile_is_public': profileIsPublic}); var accountPreferencesModel = new AccountPreferencesModel(); diff --git a/lms/static/js/student_account/models/user_account_model.js b/lms/static/js/student_account/models/user_account_model.js index e7afa5b194..868b275846 100644 --- a/lms/static/js/student_account/models/user_account_model.js +++ b/lms/static/js/student_account/models/user_account_model.js @@ -37,9 +37,13 @@ this.set({'profile_is_public': profileIsPublic}, { silent: true }); return response; + }, + + isAboveMinimumAge: function() { + var isBirthDefined = !(_.isUndefined(this.get('year_of_birth')) || _.isNull(this.get('year_of_birth'))); + return isBirthDefined && !(this.get("requires_parental_consent")); } }); - return UserAccountModel; }) }).call(this, define || RequireJS.define); diff --git a/lms/static/js/student_profile/views/learner_profile_fields.js b/lms/static/js/student_profile/views/learner_profile_fields.js index 8bdc66fd0b..ef3dfb0a3f 100644 --- a/lms/static/js/student_profile/views/learner_profile_fields.js +++ b/lms/static/js/student_profile/views/learner_profile_fields.js @@ -11,6 +11,7 @@ render: function () { this._super(); this.message(); + this.updateFieldValue(); return this; }, @@ -30,6 +31,13 @@ this._super(''); } return this._super(); + }, + + updateFieldValue: function() { + if (!this.isAboveMinimumAge) { + this.$('.u-field-value select').val('private'); + this.disableField(true); + } } }); diff --git a/lms/static/js/student_profile/views/learner_profile_view.js b/lms/static/js/student_profile/views/learner_profile_view.js index 0dea1e1066..2c93f25f56 100644 --- a/lms/static/js/student_profile/views/learner_profile_view.js +++ b/lms/static/js/student_profile/views/learner_profile_view.js @@ -13,8 +13,9 @@ }, showFullProfile: function () { + var isAboveMinimumAge = this.options.accountSettingsModel.isAboveMinimumAge(); if (this.options.own_profile) { - return this.options.preferencesModel.get('account_privacy') === 'all_users'; + return isAboveMinimumAge && this.options.preferencesModel.get('account_privacy') === 'all_users'; } else { return this.options.accountSettingsModel.get('profile_is_public'); } @@ -38,6 +39,7 @@ var fieldView = this.options.accountPrivacyFieldView; fieldView.profileIsPrivate = (!this.options.accountSettingsModel.get('year_of_birth')); fieldView.requiresParentalConsent = (this.options.accountSettingsModel.get('requires_parental_consent')); + fieldView.isAboveMinimumAge = this.options.accountSettingsModel.isAboveMinimumAge(); fieldView.undelegateEvents(); this.$('.wrapper-profile-field-account-privacy').append(fieldView.render().el); fieldView.delegateEvents(); diff --git a/lms/static/js/views/fields.js b/lms/static/js/views/fields.js index 2b154de08a..bc6fd57b31 100644 --- a/lms/static/js/views/fields.js +++ b/lms/static/js/views/fields.js @@ -370,6 +370,10 @@ if (this.editable === 'toggle') { this.showDisplayMode(true); } + }, + + disableField: function(disable) { + this.$('.u-field-value select').prop('disabled', disable); } });