Commit 33bee3f7 by Andy Armstrong

Refactor learner profile into openedx/features directory

LEARNER-1855
parent 583719cc
......@@ -1782,7 +1782,6 @@ REQUIRE_JS_PATH_OVERRIDES = {
'js/courseware/link_clicked_events': 'js/courseware/link_clicked_events.js',
'js/courseware/toggle_element_visibility': 'js/courseware/toggle_element_visibility.js',
'js/student_account/logistration_factory': 'js/student_account/logistration_factory.js',
'js/student_profile/views/learner_profile_factory': 'js/student_profile/views/learner_profile_factory.js',
'js/courseware/courseware_factory': 'js/courseware/courseware_factory.js',
'js/groups/views/cohorts_dashboard_factory': 'js/groups/views/cohorts_dashboard_factory.js',
'js/groups/discussions_management/discussions_dashboard_factory': 'js/discussions_management/views/discussions_dashboard_factory.js',
......@@ -2252,7 +2251,9 @@ INSTALLED_APPS = (
'openedx.features.course_experience',
'openedx.features.course_search',
'openedx.features.enterprise_support',
'openedx.features.learner_profile',
# Experiments
'experiments',
# DRF filters
......
......@@ -31,14 +31,18 @@ var options = {
{pattern: 'course_search/**/!(*spec).js'},
{pattern: 'discussion/js/**/!(*spec).js'},
{pattern: 'js/**/!(*spec|djangojs).js'},
{pattern: 'learner_profile/**/!(*spec).js'},
{pattern: 'lms/js/**/!(*spec).js'},
{pattern: 'support/js/**/!(*spec).js'},
{pattern: 'teams/js/**/!(*spec).js'}
],
specFiles: [
{pattern: '../**/*spec.js'},
{pattern: 'course_experience/js/**/*_spec.js', webpack: true}
// Define the Webpack-built spec files first
{pattern: 'course_experience/js/**/*_spec.js', webpack: true},
// Add all remaining spec files to be used without Webpack
{pattern: '../**/*spec.js'}
],
fixtureFiles: [
......
../../openedx/features/learner_profile/static/learner_profile
\ No newline at end of file
......@@ -37,8 +37,8 @@
'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',
'js/views/message_banner',
'learner_profile/js/learner_profile_factory',
'lms/js/preview/preview_factory',
'support/js/certificates_factory',
'support/js/enrollment_factory',
......
......@@ -88,9 +88,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_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',
'js/ccx/schedule': 'js/ccx/schedule',
'js/views/message_banner': 'js/views/message_banner',
......@@ -784,14 +781,6 @@
'js/spec/student_account/password_reset_spec.js',
'js/spec/student_account/register_spec.js',
'js/spec/student_account/shoppingcart_spec.js',
'js/spec/student_profile/badge_list_container_spec.js',
'js/spec/student_profile/badge_list_view_spec.js',
'js/spec/student_profile/badge_view_spec.js',
'js/spec/student_profile/learner_profile_factory_spec.js',
'js/spec/student_profile/learner_profile_fields_spec.js',
'js/spec/student_profile/learner_profile_view_spec.js',
'js/spec/student_profile/section_two_tab_spec.js',
'js/spec/student_profile/share_modal_view_spec.js',
'js/spec/verify_student/image_input_spec.js',
'js/spec/verify_student/make_payment_step_view_ab_testing_spec.js',
'js/spec/verify_student/make_payment_step_view_spec.js',
......@@ -803,6 +792,14 @@
'js/spec/views/file_uploader_spec.js',
'js/spec/views/message_banner_spec.js',
'js/spec/views/notification_spec.js',
'learner_profile/js/spec/learner_profile_factory_spec.js',
'learner_profile/js/spec/views/badge_list_container_spec.js',
'learner_profile/js/spec/views/badge_list_view_spec.js',
'learner_profile/js/spec/views/badge_view_spec.js',
'learner_profile/js/spec/views/learner_profile_fields_spec.js',
'learner_profile/js/spec/views/learner_profile_view_spec.js',
'learner_profile/js/spec/views/section_two_tab_spec.js',
'learner_profile/js/spec/views/share_modal_view_spec.js',
'support/js/spec/collections/enrollment_spec.js',
'support/js/spec/models/enrollment_spec.js',
'support/js/spec/views/certificates_spec.js',
......
......@@ -604,15 +604,6 @@ urlpatterns += (
include('student_account.urls')
),
# Student profile
url(
r'^u/{username_pattern}$'.format(
username_pattern=settings.USERNAME_PATTERN,
),
'student_profile.views.learner_profile',
name='learner_profile',
),
# Student Notes
url(
r'^courses/{}/edxnotes'.format(
......@@ -651,6 +642,12 @@ urlpatterns += (
),
include('openedx.features.course_search.urls'),
),
# Learner profile
url(
r'^u/',
include('openedx.features.learner_profile.urls'),
),
)
if settings.FEATURES["ENABLE_TEAMS"]:
......
(function(define, undefined) {
(function(define) {
'use strict';
define([
'gettext',
'jquery',
'underscore',
'backbone',
'logger',
'edx-ui-toolkit/js/utils/string-utils',
'edx-ui-toolkit/js/pagination/paging-collection',
'js/student_account/models/user_account_model',
'js/student_account/models/user_preferences_model',
'js/views/fields',
'js/student_profile/views/learner_profile_fields',
'js/student_profile/views/learner_profile_view',
'js/student_profile/models/badges_model',
'js/student_profile/views/badge_list_container',
'learner_profile/js/views/learner_profile_fields',
'learner_profile/js/views/learner_profile_view',
'learner_profile/js/models/badges_model',
'learner_profile/js/views/badge_list_container',
'js/student_account/views/account_settings_fields',
'js/views/message_banner',
'string_utils'
], function(gettext, $, _, Backbone, Logger, PagingCollection, AccountSettingsModel, AccountPreferencesModel,
FieldsView, LearnerProfileFieldsView, LearnerProfileView, BadgeModel, BadgeListContainer,
AccountSettingsFieldViews, MessageBannerView) {
], function(gettext, $, _, Backbone, Logger, StringUtils, PagingCollection, AccountSettingsModel,
AccountPreferencesModel, FieldsView, LearnerProfileFieldsView, LearnerProfileView, BadgeModel,
BadgeListContainer, AccountSettingsFieldViews, MessageBannerView) {
return function(options) {
var learnerProfileElement = $('.wrapper-profile');
var $learnerProfileElement = $('.wrapper-profile');
var accountSettingsModel = new AccountSettingsModel(
_.extend(
options.account_settings_data,
{'default_public_account_fields': options.default_public_account_fields}
{default_public_account_fields: options.default_public_account_fields}
),
{parse: true}
);
......@@ -37,22 +39,35 @@
});
var accountPreferencesModel = new AccountPreferencesModelWithDefaults(options.preferences_data);
accountSettingsModel.url = options.accounts_api_url;
accountPreferencesModel.url = options.preferences_api_url;
var editable = options.own_profile ? 'toggle' : 'never';
var messageView = new MessageBannerView({
el: $('.message-banner')
});
var accountPrivacyFieldView = new LearnerProfileFieldsView.AccountPrivacyFieldView({
var accountPrivacyFieldView,
profileImageFieldView,
usernameFieldView,
sectionOneFieldViews,
sectionTwoFieldViews,
BadgeCollection,
badgeCollection,
badgeListContainer,
learnerProfileView,
getProfileVisibility,
showLearnerProfileView;
accountSettingsModel.url = options.accounts_api_url;
accountPreferencesModel.url = options.preferences_api_url;
accountPrivacyFieldView = new LearnerProfileFieldsView.AccountPrivacyFieldView({
model: accountPreferencesModel,
required: true,
editable: 'always',
showMessages: false,
title: interpolate_text(
gettext('{platform_name} learners can see my:'), {platform_name: options.platform_name}
title: StringUtils.interpolate(
gettext('{platform_name} learners can see my:'),
{platform_name: options.platform_name}
),
valueAttribute: 'account_privacy',
options: [
......@@ -64,25 +79,25 @@
persistChanges: true
});
var profileImageFieldView = new LearnerProfileFieldsView.ProfileImageFieldView({
profileImageFieldView = new LearnerProfileFieldsView.ProfileImageFieldView({
model: accountSettingsModel,
valueAttribute: 'profile_image',
editable: editable === 'toggle',
messageView: messageView,
imageMaxBytes: options['profile_image_max_bytes'],
imageMinBytes: options['profile_image_min_bytes'],
imageUploadUrl: options['profile_image_upload_url'],
imageRemoveUrl: options['profile_image_remove_url']
imageMaxBytes: options.profile_image_max_bytes,
imageMinBytes: options.profile_image_min_bytes,
imageUploadUrl: options.profile_image_upload_url,
imageRemoveUrl: options.profile_image_remove_url
});
var usernameFieldView = new FieldsView.ReadonlyFieldView({
usernameFieldView = new FieldsView.ReadonlyFieldView({
model: accountSettingsModel,
screenReaderTitle: gettext('Username'),
valueAttribute: 'username',
helpMessage: ''
});
var sectionOneFieldViews = [
sectionOneFieldViews = [
new FieldsView.DropdownFieldView({
model: accountSettingsModel,
screenReaderTitle: gettext('Country'),
......@@ -113,12 +128,13 @@
})
];
var sectionTwoFieldViews = [
sectionTwoFieldViews = [
new FieldsView.TextareaFieldView({
model: accountSettingsModel,
editable: editable,
showMessages: false,
title: gettext('About me'),
// eslint-disable-next-line max-len
placeholderValue: gettext("Tell other learners a little about yourself: where you live, what your interests are, why you're taking courses, or what you hope to learn."),
valueAttribute: 'bio',
helpMessage: '',
......@@ -127,28 +143,28 @@
})
];
var BadgeCollection = PagingCollection.extend({
BadgeCollection = PagingCollection.extend({
queryParams: {
currentPage: 'current_page'
}
});
var badgeCollection = new BadgeCollection();
badgeCollection = new BadgeCollection();
badgeCollection.url = options.badges_api_url;
var badgeListContainer = new BadgeListContainer({
'attributes': {'class': 'badge-set-display'},
'collection': badgeCollection,
'find_courses_url': options.find_courses_url,
'ownProfile': options.own_profile,
'badgeMeta': {
'badges_logo': options.badges_logo,
'backpack_ui_img': options.backpack_ui_img,
'badges_icon': options.badges_icon
badgeListContainer = new BadgeListContainer({
attributes: {class: 'badge-set-display'},
collection: badgeCollection,
find_courses_url: options.find_courses_url,
ownProfile: options.own_profile,
badgeMeta: {
badges_logo: options.badges_logo,
backpack_ui_img: options.backpack_ui_img,
badges_icon: options.badges_icon
}
});
var learnerProfileView = new LearnerProfileView({
el: learnerProfileElement,
learnerProfileView = new LearnerProfileView({
el: $learnerProfileElement,
ownProfile: options.own_profile,
has_preferences_access: options.has_preferences_access,
accountSettingsModel: accountSettingsModel,
......@@ -161,7 +177,7 @@
badgeListContainer: badgeListContainer
});
var getProfileVisibility = function() {
getProfileVisibility = function() {
if (options.has_preferences_access) {
return accountPreferencesModel.get('account_privacy');
} else {
......@@ -169,7 +185,7 @@
}
};
var showLearnerProfileView = function() {
showLearnerProfileView = function() {
// Record that the profile page was viewed
Logger.log('edx.user.settings.viewed', {
page: 'profile',
......
(function(define) {
'use strict';
define(['backbone'], function(Backbone) {
var BadgesModel = Backbone.Model.extend({});
return BadgesModel;
......
......@@ -5,13 +5,14 @@ define([
'URI',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'edx-ui-toolkit/js/pagination/paging-collection',
'js/spec/student_profile/helpers',
'js/student_profile/views/badge_list_container'
'learner_profile/js/spec_helpers/helpers',
'learner_profile/js/views/badge_list_container'
],
function(Backbone, $, _, URI, AjaxHelpers, PagingCollection, LearnerProfileHelpers, BadgeListContainer) {
'use strict';
describe('edx.user.BadgeListContainer', function() {
var view, requests;
var view;
var createView = function(requests, pageNum, badgeListObject) {
var BadgeCollection = PagingCollection.extend({
......@@ -20,19 +21,22 @@ define([
}
});
var badgeCollection = new BadgeCollection();
badgeCollection.url = '/api/badges/v1/assertions/user/staff/';
var models = [];
var badgeListContainer;
var request;
var path;
badgeCollection.url = '/api/badges/v1/assertions/user/staff/';
_.each(_.range(badgeListObject.count), function(idx) {
models.push(LearnerProfileHelpers.makeBadge(idx));
});
badgeListObject.results = models;
badgeListObject.results = models; // eslint-disable-line no-param-reassign
badgeCollection.setPage(pageNum);
var request = AjaxHelpers.currentRequest(requests);
var path = new URI(request.url).path();
request = AjaxHelpers.currentRequest(requests);
path = new URI(request.url).path();
expect(path).toBe('/api/badges/v1/assertions/user/staff/');
AjaxHelpers.respondWithJson(requests, badgeListObject);
var badgeListContainer = new BadgeListContainer({
'collection': badgeCollection
badgeListContainer = new BadgeListContainer({
collection: badgeCollection
});
badgeListContainer.render();
......@@ -44,7 +48,8 @@ define([
});
it('displays all badges', function() {
requests = AjaxHelpers.requests(this);
var requests = AjaxHelpers.requests(this),
badges;
view = createView(requests, 1, {
count: 30,
previous: '/arbitrary/url',
......@@ -54,12 +59,13 @@ define([
current_page: 1,
results: []
});
var badges = view.$el.find('div.badge-display');
badges = view.$el.find('div.badge-display');
expect(badges.length).toBe(30);
});
it('displays placeholder on last page', function() {
requests = AjaxHelpers.requests(this);
var requests = AjaxHelpers.requests(this),
placeholder;
view = createView(requests, 3, {
count: 30,
previous: '/arbitrary/url',
......@@ -69,12 +75,13 @@ define([
current_page: 3,
results: []
});
var placeholder = view.$el.find('span.accomplishment-placeholder');
placeholder = view.$el.find('span.accomplishment-placeholder');
expect(placeholder.length).toBe(1);
});
it('does not display placeholder on first page', function() {
requests = AjaxHelpers.requests(this);
var requests = AjaxHelpers.requests(this),
placeholder;
view = createView(requests, 1, {
count: 30,
previous: '/arbitrary/url',
......@@ -84,7 +91,7 @@ define([
current_page: 1,
results: []
});
var placeholder = view.$el.find('span.accomplishment-placeholder');
placeholder = view.$el.find('span.accomplishment-placeholder');
expect(placeholder.length).toBe(0);
});
});
......
......@@ -3,18 +3,20 @@ define([
'jquery',
'underscore',
'edx-ui-toolkit/js/pagination/paging-collection',
'js/spec/student_profile/helpers',
'js/student_profile/views/badge_list_view'
'learner_profile/js/spec_helpers/helpers',
'learner_profile/js/views/badge_list_view'
],
function(Backbone, $, _, PagingCollection, LearnerProfileHelpers, BadgeListView) {
'use strict';
describe('edx.user.BadgeListView', function() {
var view;
var createView = function(badges, pages, page, hasNextPage) {
var badgeCollection = new PagingCollection();
badgeCollection.url = '/api/badges/v1/assertions/user/staff/';
var models = [];
var badgeList;
badgeCollection.url = '/api/badges/v1/assertions/user/staff/';
_.each(badges, function(element) {
models.push(new Backbone.Model(element));
});
......@@ -25,11 +27,11 @@ define([
badgeCollection.hasNextPage = function() {
return hasNextPage;
};
var badge_list = new BadgeListView({
'collection': badgeCollection
badgeList = new BadgeListView({
collection: badgeCollection
});
return badge_list;
return badgeList;
};
afterEach(function() {
......@@ -37,36 +39,41 @@ define([
});
it('there is a single row if there is only one badge', function() {
var rows;
view = createView([LearnerProfileHelpers.makeBadge(1)], 1, 1, false);
view.render();
var rows = view.$el.find('div.row');
rows = view.$el.find('div.row');
expect(rows.length).toBe(1);
});
it('accomplishments placeholder is visible on a last page', function() {
var placeholder;
view = createView([LearnerProfileHelpers.makeBadge(1)], 2, 2, false);
view.render();
var placeholder = view.$el.find('span.accomplishment-placeholder');
placeholder = view.$el.find('span.accomplishment-placeholder');
expect(placeholder.length).toBe(1);
});
it('accomplishments placeholder to be not visible on a first page', function() {
var placeholder;
view = createView([LearnerProfileHelpers.makeBadge(1)], 1, 2, true);
view.render();
var placeholder = view.$el.find('span.accomplishment-placeholder');
placeholder = view.$el.find('span.accomplishment-placeholder');
expect(placeholder.length).toBe(0);
});
it('badges are in two columns (checked by counting rows for a known number of badges)', function() {
var badges = [];
var placeholder;
var rows;
_.each(_.range(4), function(item) {
badges.push(LearnerProfileHelpers.makeBadge(item));
});
view = createView(badges, 1, 2, true);
view.render();
var placeholder = view.$el.find('span.accomplishment-placeholder');
placeholder = view.$el.find('span.accomplishment-placeholder');
expect(placeholder.length).toBe(0);
var rows = view.$el.find('div.row');
rows = view.$el.find('div.row');
expect(rows.length).toBe(2);
});
});
......
define(['backbone', 'jquery', 'underscore',
'js/spec/student_profile/helpers',
'js/student_profile/views/badge_view'
],
define([
'backbone', 'jquery', 'underscore',
'learner_profile/js/spec_helpers/helpers',
'learner_profile/js/views/badge_view'
],
function(Backbone, $, _, LearnerProfileHelpers, BadgeView) {
'use strict';
describe('edx.user.BadgeView', function() {
var view, badge;
var view,
badge,
testBadgeNameIsDisplayed,
testBadgeIconIsDisplayed;
var createView = function(ownProfile) {
var options,
testView;
badge = LearnerProfileHelpers.makeBadge(1);
var options = {
'model': new Backbone.Model(badge),
'ownProfile': ownProfile,
'badgeMeta': {}
options = {
model: new Backbone.Model(badge),
ownProfile: ownProfile,
badgeMeta: {}
};
var view = new BadgeView(options);
view.render();
$('body').append(view.$el);
view.$el.show();
expect(view.$el.is(':visible')).toBe(true);
return view;
testView = new BadgeView(options);
testView.render();
$('body').append(testView.$el);
testView.$el.show();
expect(testView.$el.is(':visible')).toBe(true);
return testView;
};
afterEach(function() {
......@@ -40,11 +47,12 @@ define(['backbone', 'jquery', 'underscore',
});
it('click on share button calls createModal function', function() {
var shareButton;
view = createView(true);
spyOn(view, 'createModal');
view.delegateEvents();
expect(view.context.ownProfile).toBeTruthy();
var shareButton = view.$el.find('button.share-button');
shareButton = view.$el.find('button.share-button');
expect(shareButton.length).toBe(1);
expect(view.createModal).not.toHaveBeenCalled();
shareButton.click();
......@@ -52,24 +60,27 @@ define(['backbone', 'jquery', 'underscore',
});
it('click on share button calls shows the dialog', function(done) {
var shareButton,
$modalElement;
view = createView(true);
expect(view.context.ownProfile).toBeTruthy();
var shareButton = view.$el.find('button.share-button');
shareButton = view.$el.find('button.share-button');
expect(shareButton.length).toBe(1);
var modalElement = $('.badges-modal');
expect(modalElement.length).toBe(0);
expect(modalElement.is(':visible')).toBeFalsy();
$modalElement = $('.badges-modal');
expect($modalElement.length).toBe(0);
expect($modalElement.is(':visible')).toBeFalsy();
shareButton.click();
// Note: this element should have appeared in the dom during: shareButton.click();
modalElement = $('.badges-modal');
$modalElement = $('.badges-modal');
jasmine.waitUntil(function() {
return modalElement.is(':visible');
return $modalElement.is(':visible');
}).always(done);
});
var testBadgeNameIsDisplayed = function(ownProfile) {
testBadgeNameIsDisplayed = function(ownProfile) {
var badgeDiv;
view = createView(ownProfile);
var badgeDiv = view.$el.find('.badge-name');
badgeDiv = view.$el.find('.badge-name');
expect(badgeDiv.length).toBeTruthy();
expect(badgeDiv.is(':visible')).toBe(true);
expect(_.count(badgeDiv.html(), badge.badge_class.display_name)).toBeTruthy();
......@@ -83,9 +94,10 @@ define(['backbone', 'jquery', 'underscore',
testBadgeNameIsDisplayed(false);
});
var testBadgeIconIsDisplayed = function(ownProfile) {
testBadgeIconIsDisplayed = function(ownProfile) {
var badgeImg;
view = createView(ownProfile);
var badgeImg = view.$el.find('img.badge');
badgeImg = view.$el.find('img.badge');
expect(badgeImg.length).toBe(1);
expect(badgeImg.attr('src')).toEqual(badge.image_url);
};
......
define(['backbone',
define(
[
'backbone',
'jquery',
'underscore',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/template_helpers',
'js/spec/student_account/helpers',
'js/student_account/models/user_account_model',
'js/student_profile/views/learner_profile_fields',
'learner_profile/js/views/learner_profile_fields',
'js/views/message_banner'
],
],
function(Backbone, $, _, AjaxHelpers, TemplateHelpers, Helpers, UserAccountModel, LearnerProfileFields,
MessageBannerView) {
MessageBannerView) {
'use strict';
describe('edx.user.LearnerProfileFields', function() {
......@@ -21,20 +23,21 @@ define(['backbone',
var yearOfBirth = _.isUndefined(options.yearOfBirth) ? MOCK_YEAR_OF_BIRTH : options.yearOfBirth;
var imageMaxBytes = _.isUndefined(options.imageMaxBytes) ? MOCK_IMAGE_MAX_BYTES : options.imageMaxBytes;
var imageMinBytes = _.isUndefined(options.imageMinBytes) ? MOCK_IMAGE_MIN_BYTES : options.imageMinBytes;
var messageView;
var imageData = {
image_url_large: '/media/profile-images/default.jpg',
has_image: options.hasImage ? true : false
has_image: !!options.hasImage
};
var accountSettingsModel = new UserAccountModel();
accountSettingsModel.set({'profile_image': imageData});
accountSettingsModel.set({'year_of_birth': yearOfBirth});
accountSettingsModel.set({'requires_parental_consent': _.isEmpty(yearOfBirth) ? true : false});
accountSettingsModel.set({profile_image: imageData});
accountSettingsModel.set({year_of_birth: yearOfBirth});
accountSettingsModel.set({requires_parental_consent: !!_.isEmpty(yearOfBirth)});
accountSettingsModel.url = Helpers.USER_ACCOUNTS_API_URL;
var messageView = new MessageBannerView({
messageView = new MessageBannerView({
el: $('.message-banner')
});
......@@ -50,18 +53,6 @@ define(['backbone',
});
};
beforeEach(function() {
loadFixtures('js/fixtures/student_profile/student_profile.html');
TemplateHelpers.installTemplate('templates/student_profile/learner_profile');
TemplateHelpers.installTemplate('templates/fields/field_image');
TemplateHelpers.installTemplate('templates/fields/message_banner');
});
afterEach(function() {
// image_field.js's window.onBeforeUnload breaks Karma in Chrome, clean it up after each test
$(window).off('beforeunload');
});
var createFakeImageFile = function(size) {
var fileFakeData = 'i63ljc6giwoskyb9x5sw0169bdcmcxr3cdz8boqv0lik971972cmd6yknvcxr5sw0nvc169bdcmcxsdf';
return new Blob(
......@@ -80,6 +71,17 @@ define(['backbone',
});
};
beforeEach(function() {
loadFixtures('learner_profile/fixtures/learner_profile.html');
TemplateHelpers.installTemplate('templates/fields/field_image');
TemplateHelpers.installTemplate('templates/fields/message_banner');
});
afterEach(function() {
// image_field.js's window.onBeforeUnload breaks Karma in Chrome, clean it up after each test
$(window).off('beforeunload');
});
describe('ProfileImageFieldView', function() {
var verifyImageUploadButtonMessage = function(view, inProgress) {
var iconName = inProgress ? 'fa-spinner' : 'fa-camera';
......@@ -96,11 +98,11 @@ define(['backbone',
};
it('can upload profile image', function() {
var imageView = createImageView({ownProfile: true, hasImage: false});
imageView.render();
var requests = AjaxHelpers.requests(this);
var imageName = 'profile_image.jpg';
var imageView = createImageView({ownProfile: true, hasImage: false});
var data;
imageView.render();
initializeUploader(imageView);
......@@ -124,7 +126,7 @@ define(['backbone',
// Upon successful image upload, account settings model will be fetched to
// get the url for newly uploaded image, So we need to send the response for that GET
var data = {profile_image: {
data = {profile_image: {
image_url_large: '/media/profile-images/' + imageName,
has_image: true
}};
......@@ -141,10 +143,11 @@ define(['backbone',
});
it('can remove profile image', function() {
var requests = AjaxHelpers.requests(this);
var imageView = createImageView({ownProfile: true, hasImage: false});
var data;
imageView.render();
var requests = AjaxHelpers.requests(this);
// Verify image remove title
verifyImageRemoveButtonMessage(imageView, false);
......@@ -162,7 +165,7 @@ define(['backbone',
// Upon successful image removal, account settings model will be fetched to get default image url
// So we need to send the response for that GET
var data = {profile_image: {
data = {profile_image: {
image_url_large: '/media/profile-images/default.jpg',
has_image: false
}};
......@@ -267,11 +270,10 @@ define(['backbone',
});
it('shows error message for HTTP 500', function() {
var requests = AjaxHelpers.requests(this);
var imageView = createImageView({ownProfile: true, hasImage: false});
imageView.render();
var requests = AjaxHelpers.requests(this);
initializeUploader(imageView);
// Add image to upload queue. Validate the image size and send POST request to upload image
......
define(['backbone',
/* eslint-disable vars-on-top */
define(
[
'backbone',
'jquery',
'underscore',
'edx-ui-toolkit/js/pagination/paging-collection',
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/template_helpers',
'js/spec/student_account/helpers',
'js/spec/student_profile/helpers',
'learner_profile/js/spec_helpers/helpers',
'js/views/fields',
'js/student_account/models/user_account_model',
'js/student_account/models/user_preferences_model',
'js/student_profile/views/learner_profile_fields',
'js/student_profile/views/learner_profile_view',
'js/student_profile/views/badge_list_container',
'learner_profile/js/views/learner_profile_fields',
'learner_profile/js/views/learner_profile_view',
'learner_profile/js/views/badge_list_container',
'js/student_account/views/account_settings_fields',
'js/views/message_banner'
],
......@@ -24,8 +27,8 @@ define(['backbone',
var createLearnerProfileView = function(ownProfile, accountPrivacy, profileIsPublic) {
var accountSettingsModel = new UserAccountModel();
accountSettingsModel.set(Helpers.createAccountSettingsData());
accountSettingsModel.set({'profile_is_public': profileIsPublic});
accountSettingsModel.set({'profile_image': Helpers.PROFILE_IMAGE});
accountSettingsModel.set({profile_is_public: profileIsPublic});
accountSettingsModel.set({profile_image: Helpers.PROFILE_IMAGE});
var accountPreferencesModel = new AccountPreferencesModel();
accountPreferencesModel.set({account_privacy: accountPrivacy});
......@@ -114,9 +117,9 @@ define(['backbone',
badgeCollection.url = Helpers.BADGES_API_URL;
var badgeListContainer = new BadgeListContainer({
'attributes': {'class': 'badge-set-display'},
'collection': badgeCollection,
'find_courses_url': Helpers.FIND_COURSES_URL
attributes: {class: 'badge-set-display'},
collection: badgeCollection,
find_courses_url: Helpers.FIND_COURSES_URL
});
return new LearnerProfileView(
......@@ -136,7 +139,7 @@ define(['backbone',
};
beforeEach(function() {
loadFixtures('js/fixtures/student_profile/student_profile.html');
loadFixtures('learner_profile/fixtures/learner_profile.html');
});
afterEach(function() {
......@@ -205,7 +208,7 @@ define(['backbone',
it("renders an error if the badges can't be fetched", function() {
var learnerProfileView = createLearnerProfileView(false, 'all_users', true);
learnerProfileView.options.accountSettingsModel.set({'accomplishments_shared': true});
learnerProfileView.options.accountSettingsModel.set({accomplishments_shared: true});
var requests = AjaxHelpers.requests(this);
learnerProfileView.render();
......
define(['backbone', 'jquery', 'underscore',
/* eslint-disable vars-on-top */
define(
[
'backbone', 'jquery', 'underscore',
'js/spec/student_account/helpers',
'js/student_profile/views/section_two_tab',
'learner_profile/js/views/section_two_tab',
'js/views/fields',
'js/student_account/models/user_account_model'
],
function(Backbone, $, _, Helpers, SectionTwoTabView, FieldViews, UserAccountModel) {
'use strict';
describe('edx.user.SectionTwoTab', function() {
var createSectionTwoView = function(ownProfile, profileIsPublic) {
var accountSettingsModel = new UserAccountModel();
accountSettingsModel.set(Helpers.createAccountSettingsData());
accountSettingsModel.set({'profile_is_public': profileIsPublic});
accountSettingsModel.set({'profile_image': Helpers.PROFILE_IMAGE});
accountSettingsModel.set({profile_is_public: profileIsPublic});
accountSettingsModel.set({profile_image: Helpers.PROFILE_IMAGE});
var editable = ownProfile ? 'toggle' : 'never';
......@@ -56,14 +60,14 @@ define(['backbone', 'jquery', 'underscore',
});
});
var testPrivateProfile = function(ownProfile, msg_string) {
var testPrivateProfile = function(ownProfile, messageString) {
var view = createSectionTwoView(ownProfile, false);
view.render();
var bio = view.$el.find('.u-field-bio');
expect(bio.length).toBe(0);
var msg = view.$el.find('span.profile-private--message');
expect(msg.length).toBe(1);
expect(_.count(msg.html(), msg_string)).toBeTruthy();
expect(_.count(msg.html(), messageString)).toBeTruthy();
};
it('no profile when profile is private for other people', function() {
......
define(['backbone', 'jquery', 'underscore', 'moment',
define(
[
'backbone', 'jquery', 'underscore', 'moment',
'js/spec/student_account/helpers',
'js/spec/student_profile/helpers',
'js/student_profile/views/share_modal_view',
'learner_profile/js/spec_helpers/helpers',
'learner_profile/js/views/share_modal_view',
'jquery.simulate'
],
function(Backbone, $, _, Moment, Helpers, LearnerProfileHelpers, ShareModalView) {
'use strict';
describe('edx.user.ShareModalView', function() {
var keys = $.simulate.keyCode;
......@@ -14,9 +17,9 @@ define(['backbone', 'jquery', 'underscore', 'moment',
var createModalView = function() {
var badge = LearnerProfileHelpers.makeBadge(1);
var context = _.extend(badge, {
'created': new Moment(badge.created),
'ownProfile': true,
'badgeMeta': {}
created: new Moment(badge.created),
ownProfile: true,
badgeMeta: {}
});
return new ShareModalView({
model: new Backbone.Model(context),
......@@ -46,9 +49,10 @@ define(['backbone', 'jquery', 'underscore', 'moment',
});
it('modal view closes click on close', function() {
var $closeButton;
spyOn(view, 'close');
view.delegateEvents();
var $closeButton = view.$el.find('button.close');
$closeButton = view.$el.find('button.close');
expect($closeButton.length).toBe(1);
expect(view.close).not.toHaveBeenCalled();
$closeButton.trigger('click');
......
......@@ -15,7 +15,9 @@ define(['underscore', 'URI', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
} else if (view.fieldValue()) {
expect(view.fieldValue()).toBe(view.modelValue());
} else if ('optionForValue' in view) {
expect($($element.find('.u-field-value .u-field-value-readonly')[0]).text()).toBe(view.displayValue(view.modelValue()));
expect($($element.find('.u-field-value .u-field-value-readonly')[0]).text()).toBe(
view.displayValue(view.modelValue())
);
} else {
expect($($element.find('.u-field-value .u-field-value-readonly')[0]).text()).toBe(view.modelValue());
}
......@@ -72,10 +74,10 @@ define(['underscore', 'URI', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
};
var expectLimitedProfileSectionsAndFieldsToBeRendered = function(learnerProfileView, othersProfile) {
expectProfilePrivacyFieldTobeRendered(learnerProfileView, othersProfile);
var sectionOneFieldElements = $(learnerProfileView.$('.wrapper-profile-section-one')).find('.u-field');
expectProfilePrivacyFieldTobeRendered(learnerProfileView, othersProfile);
expect(sectionOneFieldElements.length).toBe(2);
expectProfileElementContainsField(
sectionOneFieldElements[0],
......@@ -111,16 +113,18 @@ define(['underscore', 'URI', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
};
var expectBadgesDisplayed = function(learnerProfileView, length, lastPage) {
var badgeListingView = learnerProfileView.$el.find('#tabpanel-accomplishments');
var badgeListingView = learnerProfileView.$el.find('#tabpanel-accomplishments'),
updatedLength = length,
placeholder;
expect(learnerProfileView.$el.find('#tabpanel-about_me').hasClass('is-hidden')).toBe(true);
expect(badgeListingView.hasClass('is-hidden')).toBe(false);
if (lastPage) {
length += 1;
var placeholder = badgeListingView.find('.find-course');
updatedLength += 1;
placeholder = badgeListingView.find('.find-course');
expect(placeholder.length).toBe(1);
expect(placeholder.attr('href')).toBe('/courses/');
}
expect(badgeListingView.find('.badge-display').length).toBe(length);
expect(badgeListingView.find('.badge-display').length).toBe(updatedLength);
};
var expectBadgesHidden = function(learnerProfileView) {
......@@ -188,20 +192,27 @@ define(['underscore', 'URI', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
results: []
};
var emptyBadges = {
count: 0,
previous: null,
num_pages: 1,
results: []
};
function makeBadge(num) {
return {
'badge_class': {
'slug': 'test_slug_' + num,
'issuing_component': 'test_component',
'display_name': 'Test Badge ' + num,
'course_id': null,
'description': "Yay! It's a test badge.",
'criteria': 'https://example.com/syllabus',
'image_url': 'http://localhost:8000/media/badge_classes/test_lMB9bRw.png'
badge_class: {
slug: 'test_slug_' + num,
issuing_component: 'test_component',
display_name: 'Test Badge ' + num,
course_id: null,
description: "Yay! It's a test badge.",
criteria: 'https://example.com/syllabus',
image_url: 'http://localhost:8000/media/badge_classes/test_lMB9bRw.png'
},
'image_url': 'http://example.com/image.png',
'assertion_url': 'http://example.com/example.json',
'created_at': '2015-12-03T16:25:57.676113Z'
image_url: 'http://example.com/image.png',
assertion_url: 'http://example.com/example.json',
created_at: '2015-12-03T16:25:57.676113Z'
};
}
......@@ -217,13 +228,6 @@ define(['underscore', 'URI', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'
thirdPageBadges.results.push(makeBadge(i));
});
var emptyBadges = {
'count': 0,
'previous': null,
'num_pages': 1,
'results': []
};
return {
expectLimitedProfileSectionsAndFieldsToBeRendered: expectLimitedProfileSectionsAndFieldsToBeRendered,
expectProfileSectionsAndFieldsToBeRendered: expectProfileSectionsAndFieldsToBeRendered,
......
(function(define, undefined) {
/* eslint-disable no-underscore-dangle */
(function(define) {
'use strict';
define([
'gettext', 'jquery', 'underscore', 'common/js/components/views/paginated_view',
'js/student_profile/views/badge_view', 'js/student_profile/views/badge_list_view',
'text!templates/student_profile/badge_list.underscore'],
define(
[
'gettext', 'jquery', 'underscore', 'common/js/components/views/paginated_view',
'learner_profile/js/views/badge_view', 'learner_profile/js/views/badge_list_view',
'text!learner_profile/templates/badge_list.underscore'
],
function(gettext, $, _, PaginatedView, BadgeView, BadgeListView, BadgeListTemplate) {
var BadgeListContainer = PaginatedView.extend({
type: 'badge',
......
(function(define, undefined) {
(function(define) {
'use strict';
define([
'gettext',
'jquery',
'underscore',
'edx-ui-toolkit/js/utils/html-utils',
'common/js/components/views/list',
'js/student_profile/views/badge_view',
'text!templates/student_profile/badge_placeholder.underscore'
'learner_profile/js/views/badge_view',
'text!learner_profile/templates/badge_placeholder.underscore'
],
function(gettext, $, _, HtmlUtils, ListView, BadgeView, badgePlaceholder) {
var BadgeListView = ListView.extend({
......@@ -23,11 +24,12 @@
// Split into two columns.
this.collection.each(function(badge, index) {
var $item;
if (index % 2 === 0) {
$row = $('<div class="row">');
this.$el.append($row);
}
var $item = new BadgeView({
$item = new BadgeView({
model: badge,
badgeMeta: this.badgeMeta,
ownProfile: this.ownProfile
......
(function(define, undefined) {
(function(define) {
'use strict';
define(['gettext', 'jquery', 'underscore', 'backbone', 'moment',
'text!templates/student_profile/badge.underscore',
'js/student_profile/views/share_modal_view'],
define(
[
'gettext', 'jquery', 'underscore', 'backbone', 'moment',
'text!learner_profile/templates/badge.underscore',
'learner_profile/js/views/share_modal_view'
],
function(gettext, $, _, Backbone, Moment, badgeTemplate, ShareModalView) {
var BadgeView = Backbone.View.extend({
initialize: function(options) {
this.options = _.extend({}, options);
this.context = _.extend(this.options.model.toJSON(), {
'created': new Moment(this.options.model.toJSON().created),
'ownProfile': options.ownProfile,
'badgeMeta': options.badgeMeta
created: new Moment(this.options.model.toJSON().created),
ownProfile: options.ownProfile,
badgeMeta: options.badgeMeta
});
},
attributes: {
'class': 'badge-display'
class: 'badge-display'
},
template: _.template(badgeTemplate),
events: {
......
(function(define, undefined) {
/* eslint-disable no-underscore-dangle */
(function(define) {
'use strict';
define([
'gettext', 'jquery', 'underscore', 'backbone', 'edx-ui-toolkit/js/utils/string-utils',
'edx-ui-toolkit/js/utils/html-utils', 'js/views/fields', 'js/views/image_field', 'backbone-super'
......@@ -27,18 +29,17 @@
this._super(
HtmlUtils.interpolateHtml(
gettext('You must specify your birth year before you can share your full profile. To specify your birth year, go to the {account_settings_page_link}'), // eslint-disable-line max-len
{'account_settings_page_link': accountSettingsLink}
{account_settings_page_link: accountSettingsLink}
)
);
} else if (this.requiresParentalConsent) {
this._super(
HtmlUtils.interpolateHtml(
gettext('You must be over 13 to share a full profile. If you are over 13, make sure that you have specified a birth year on the {account_settings_page_link}'), // eslint-disable-line max-len
{'account_settings_page_link': accountSettingsLink}
{account_settings_page_link: accountSettingsLink}
)
);
}
else {
} else {
this._super('');
}
},
......@@ -60,12 +61,13 @@
},
imageAltText: function() {
return interpolate_text(
gettext('Profile image for {username}'), {username: this.model.get('username')}
return StringUtils.interpolate(
gettext('Profile image for {username}'),
{username: this.model.get('username')}
);
},
imageChangeSucceeded: function(e, data) {
imageChangeSucceeded: function() {
var view = this;
// Update model to get the latest urls of profile image.
this.model.fetch().done(function() {
......@@ -84,9 +86,10 @@
},
showImageChangeFailedMessage: function(status, responseText) {
var errors;
if (_.contains([400, 404], status)) {
try {
var errors = JSON.parse(responseText);
errors = JSON.parse(responseText);
this.showErrorMessage(errors.user_message);
} catch (error) {
this.showErrorMessage(this.errorMessage);
......
(function(define, undefined) {
(function(define) {
'use strict';
define([
'gettext', 'jquery', 'underscore', 'backbone', 'edx-ui-toolkit/js/utils/html-utils',
'common/js/components/views/tabbed_view',
'js/student_profile/views/section_two_tab',
'text!templates/student_profile/learner_profile.underscore'],
define(
[
'gettext', 'jquery', 'underscore', 'backbone', 'edx-ui-toolkit/js/utils/html-utils',
'common/js/components/views/tabbed_view',
'learner_profile/js/views/section_two_tab',
'text!learner_profile/templates/learner_profile.underscore'
],
function(gettext, $, _, Backbone, HtmlUtils, TabbedView, SectionTwoTab, learnerProfileTemplate) {
var LearnerProfileView = Backbone.View.extend({
initialize: function(options) {
var Router;
this.options = _.extend({}, options);
_.bindAll(this, 'showFullProfile', 'render', 'renderFields', 'showLoadingError');
this.listenTo(this.options.preferencesModel, 'change:' + 'account_privacy', this.render);
var Router = Backbone.Router.extend({
this.listenTo(this.options.preferencesModel, 'change:account_privacy', this.render);
Router = Backbone.Router.extend({
routes: {':about_me': 'loadTab', ':accomplishments': 'loadTab'}
});
......@@ -25,14 +29,15 @@
showFullProfile: function() {
var isAboveMinimumAge = this.options.accountSettingsModel.isAboveMinimumAge();
if (this.options.ownProfile) {
return isAboveMinimumAge && 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');
}
},
setActiveTab: function(tab) {
// This tab may not actually exist.
// This tab may not actually exist.
if (this.tabbedView.getTabMeta(tab).tab) {
this.tabbedView.setActiveTab(tab);
}
......@@ -99,11 +104,14 @@
},
renderFields: function() {
var view = this;
var view = this,
fieldView,
imageView,
settings;
if (this.options.ownProfile) {
var fieldView = this.options.accountPrivacyFieldView,
settings = this.options.accountSettingsModel;
fieldView = this.options.accountPrivacyFieldView;
settings = this.options.accountSettingsModel;
fieldView.profileIsPrivate = !settings.get('year_of_birth');
fieldView.requiresParentalConsent = settings.get('requires_parental_consent');
fieldView.isAboveMinimumAge = settings.isAboveMinimumAge();
......@@ -114,12 +122,12 @@
this.$('.profile-section-one-fields').append(this.options.usernameFieldView.render().el);
var imageView = this.options.profileImageFieldView;
imageView = this.options.profileImageFieldView;
this.$('.profile-image-field').append(imageView.render().el);
if (this.showFullProfile()) {
_.each(this.options.sectionOneFieldViews, function(fieldView) {
view.$('.profile-section-one-fields').append(fieldView.render().el);
_.each(this.options.sectionOneFieldViews, function(childFieldView) {
view.$('.profile-section-one-fields').append(childFieldView.render().el);
});
}
},
......
(function(define, undefined) {
(function(define) {
'use strict';
define([
'gettext', 'jquery', 'underscore', 'backbone', 'text!templates/student_profile/section_two.underscore'],
define(
[
'gettext', 'jquery', 'underscore', 'backbone', 'text!learner_profile/templates/section_two.underscore'
],
function(gettext, $, _, Backbone, sectionTwoTemplate) {
var SectionTwoTab = Backbone.View.extend({
attributes: {
'class': 'wrapper-profile-section-two'
class: 'wrapper-profile-section-two'
},
template: _.template(sectionTwoTemplate),
initialize: function(options) {
......
(function(define, undefined) {
(function(define) {
'use strict';
define(['gettext', 'jquery', 'underscore', 'backbone', 'moment',
'text!templates/student_profile/share_modal.underscore'],
define(
[
'gettext', 'jquery', 'underscore', 'backbone', 'moment',
'text!learner_profile/templates/share_modal.underscore'
],
function(gettext, $, _, Backbone, Moment, badgeModalTemplate) {
var ShareModalView = Backbone.View.extend({
attributes: {
'class': 'badges-overlay'
class: 'badges-overlay'
},
template: _.template(badgeModalTemplate),
events: {
'click .badges-modal': function(event) { event.stopPropagation(); },
'click .badges-modal .close': 'close',
'click .badges-overlay': 'close',
'keydown': 'keyAction',
keydown: 'keyAction',
'focus .focusguard-start': 'focusGuardStart',
'focus .focusguard-end': 'focusGuardEnd'
},
......
## mako
<%page expression_filter="h"/>
<%inherit file="/main.html" />
<%def name="online_help_token()"><% return "profile" %></%def>
......@@ -26,7 +28,7 @@ from openedx.core.djangolib.js_utils import dump_js_escaped_json
</%block>
<%block name="js_extra">
<%static:require_module module_name="js/student_profile/views/learner_profile_factory" class_name="LearnerProfileFactory">
<%static:require_module module_name="learner_profile/js/learner_profile_factory" class_name="LearnerProfileFactory">
var options = ${data | n, dump_js_escaped_json};
LearnerProfileFactory(options);
</%static:require_module>
......
......@@ -5,11 +5,11 @@ from django.conf import settings
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.test.client import RequestFactory
from student.tests.factories import UserFactory
from student_profile.views import learner_profile_context
from util.testing import UrlResetMixin
from ..views import learner_profile_context
class LearnerProfileViewTest(UrlResetMixin, TestCase):
""" Tests for the student profile view. """
......
"""
Defines URLs for the learner profile.
"""
from django.conf import settings
from django.conf.urls import url
urlpatterns = [
url(
r'^{username_pattern}$'.format(
username_pattern=settings.USERNAME_PATTERN,
),
'openedx.features.learner_profile.views.learner_profile',
name='learner_profile',
),
]
""" Views for a student's profile information. """
from badges.utils import badges_enabled
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.staticfiles.storage import staticfiles_storage
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.http import Http404
from django.shortcuts import render_to_response
from django.views.decorators.http import require_http_methods
from django_countries import countries
from badges.utils import badges_enabled
from edxmako.shortcuts import marketing_link, render_to_response
from edxmako.shortcuts import marketing_link
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.user_api.accounts.api import get_account_settings
from openedx.core.djangoapps.user_api.errors import UserNotAuthorized, UserNotFound
......@@ -39,7 +39,7 @@ def learner_profile(request, username):
"""
try:
return render_to_response(
'student_profile/learner_profile.html',
'learner_profile/learner_profile.html',
learner_profile_context(request, username, request.user.is_staff)
)
except (UserNotAuthorized, UserNotFound, ObjectDoesNotExist):
......
......@@ -12,7 +12,7 @@ set -e
# Violations thresholds for failing the build
export PYLINT_THRESHOLD=3600
export ESLINT_THRESHOLD=10122
export ESLINT_THRESHOLD=9190
XSSLINT_THRESHOLDS=`cat scripts/xsslint_thresholds.json`
export XSSLINT_THRESHOLDS=${XSSLINT_THRESHOLDS//[[:space:]]/}
......
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