Refactor learner profile into openedx/features directory
LEARNER-1855
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
<div class="message-banner" aria-live="polite"></div>
|
||||
<div class="wrapper-profile">
|
||||
<div class="ui-loading-indicator">
|
||||
<p>
|
||||
<span class="spin">
|
||||
<span class="icon fa fa-refresh" aria-hidden="true"></span>
|
||||
</span>
|
||||
<span class="copy">
|
||||
Loading
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="ui-loading-error is-hidden">
|
||||
<span class="fa fa-exclamation-triangle message-error" aria-hidden="true"></span>
|
||||
<span class="copy">
|
||||
An error occurred. Please reload the page.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,92 +0,0 @@
|
||||
define([
|
||||
'backbone',
|
||||
'jquery',
|
||||
'underscore',
|
||||
'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'
|
||||
],
|
||||
function(Backbone, $, _, URI, AjaxHelpers, PagingCollection, LearnerProfileHelpers, BadgeListContainer) {
|
||||
'use strict';
|
||||
describe('edx.user.BadgeListContainer', function() {
|
||||
var view, requests;
|
||||
|
||||
var createView = function(requests, pageNum, badgeListObject) {
|
||||
var BadgeCollection = PagingCollection.extend({
|
||||
queryParams: {
|
||||
currentPage: 'current_page'
|
||||
}
|
||||
});
|
||||
var badgeCollection = new BadgeCollection();
|
||||
badgeCollection.url = '/api/badges/v1/assertions/user/staff/';
|
||||
var models = [];
|
||||
_.each(_.range(badgeListObject.count), function(idx) {
|
||||
models.push(LearnerProfileHelpers.makeBadge(idx));
|
||||
});
|
||||
badgeListObject.results = models;
|
||||
badgeCollection.setPage(pageNum);
|
||||
var request = AjaxHelpers.currentRequest(requests);
|
||||
var 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.render();
|
||||
return badgeListContainer;
|
||||
};
|
||||
|
||||
afterEach(function() {
|
||||
view.$el.remove();
|
||||
});
|
||||
|
||||
it('displays all badges', function() {
|
||||
requests = AjaxHelpers.requests(this);
|
||||
view = createView(requests, 1, {
|
||||
count: 30,
|
||||
previous: '/arbitrary/url',
|
||||
num_pages: 3,
|
||||
next: null,
|
||||
start: 20,
|
||||
current_page: 1,
|
||||
results: []
|
||||
});
|
||||
var badges = view.$el.find('div.badge-display');
|
||||
expect(badges.length).toBe(30);
|
||||
});
|
||||
|
||||
it('displays placeholder on last page', function() {
|
||||
requests = AjaxHelpers.requests(this);
|
||||
view = createView(requests, 3, {
|
||||
count: 30,
|
||||
previous: '/arbitrary/url',
|
||||
num_pages: 3,
|
||||
next: null,
|
||||
start: 20,
|
||||
current_page: 3,
|
||||
results: []
|
||||
});
|
||||
var 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);
|
||||
view = createView(requests, 1, {
|
||||
count: 30,
|
||||
previous: '/arbitrary/url',
|
||||
num_pages: 3,
|
||||
next: null,
|
||||
start: 0,
|
||||
current_page: 1,
|
||||
results: []
|
||||
});
|
||||
var placeholder = view.$el.find('span.accomplishment-placeholder');
|
||||
expect(placeholder.length).toBe(0);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,75 +0,0 @@
|
||||
define([
|
||||
'backbone',
|
||||
'jquery',
|
||||
'underscore',
|
||||
'edx-ui-toolkit/js/pagination/paging-collection',
|
||||
'js/spec/student_profile/helpers',
|
||||
'js/student_profile/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 = [];
|
||||
_.each(badges, function(element) {
|
||||
models.push(new Backbone.Model(element));
|
||||
});
|
||||
badgeCollection.models = models;
|
||||
badgeCollection.length = badges.length;
|
||||
badgeCollection.currentPage = page;
|
||||
badgeCollection.totalPages = pages;
|
||||
badgeCollection.hasNextPage = function() {
|
||||
return hasNextPage;
|
||||
};
|
||||
var badge_list = new BadgeListView({
|
||||
'collection': badgeCollection
|
||||
|
||||
});
|
||||
return badge_list;
|
||||
};
|
||||
|
||||
afterEach(function() {
|
||||
view.$el.remove();
|
||||
});
|
||||
|
||||
it('there is a single row if there is only one badge', function() {
|
||||
view = createView([LearnerProfileHelpers.makeBadge(1)], 1, 1, false);
|
||||
view.render();
|
||||
var rows = view.$el.find('div.row');
|
||||
expect(rows.length).toBe(1);
|
||||
});
|
||||
|
||||
it('accomplishments placeholder is visible on a last page', function() {
|
||||
view = createView([LearnerProfileHelpers.makeBadge(1)], 2, 2, false);
|
||||
view.render();
|
||||
var placeholder = view.$el.find('span.accomplishment-placeholder');
|
||||
expect(placeholder.length).toBe(1);
|
||||
});
|
||||
|
||||
it('accomplishments placeholder to be not visible on a first page', function() {
|
||||
view = createView([LearnerProfileHelpers.makeBadge(1)], 1, 2, true);
|
||||
view.render();
|
||||
var 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 = [];
|
||||
_.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');
|
||||
expect(placeholder.length).toBe(0);
|
||||
var rows = view.$el.find('div.row');
|
||||
expect(rows.length).toBe(2);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
define(['backbone', 'jquery', 'underscore',
|
||||
'js/spec/student_profile/helpers',
|
||||
'js/student_profile/views/badge_view'
|
||||
],
|
||||
function(Backbone, $, _, LearnerProfileHelpers, BadgeView) {
|
||||
'use strict';
|
||||
describe('edx.user.BadgeView', function() {
|
||||
var view, badge;
|
||||
|
||||
var createView = function(ownProfile) {
|
||||
badge = LearnerProfileHelpers.makeBadge(1);
|
||||
var 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;
|
||||
};
|
||||
|
||||
afterEach(function() {
|
||||
view.$el.remove();
|
||||
$('.badges-modal').remove();
|
||||
});
|
||||
|
||||
it('profile of other has no share button', function() {
|
||||
view = createView(false);
|
||||
expect(view.context.ownProfile).toBeFalsy();
|
||||
expect(view.$el.find('button.share-button').length).toBe(0);
|
||||
});
|
||||
|
||||
it('own profile has share button', function() {
|
||||
view = createView(true);
|
||||
expect(view.context.ownProfile).toBeTruthy();
|
||||
expect(view.$el.find('button.share-button').length).toBe(1);
|
||||
});
|
||||
|
||||
it('click on share button calls createModal function', function() {
|
||||
view = createView(true);
|
||||
spyOn(view, 'createModal');
|
||||
view.delegateEvents();
|
||||
expect(view.context.ownProfile).toBeTruthy();
|
||||
var shareButton = view.$el.find('button.share-button');
|
||||
expect(shareButton.length).toBe(1);
|
||||
expect(view.createModal).not.toHaveBeenCalled();
|
||||
shareButton.click();
|
||||
expect(view.createModal).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('click on share button calls shows the dialog', function(done) {
|
||||
view = createView(true);
|
||||
expect(view.context.ownProfile).toBeTruthy();
|
||||
var 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();
|
||||
shareButton.click();
|
||||
// Note: this element should have appeared in the dom during: shareButton.click();
|
||||
modalElement = $('.badges-modal');
|
||||
jasmine.waitUntil(function() {
|
||||
return modalElement.is(':visible');
|
||||
}).always(done);
|
||||
});
|
||||
|
||||
var testBadgeNameIsDisplayed = function(ownProfile) {
|
||||
view = createView(ownProfile);
|
||||
var 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();
|
||||
};
|
||||
|
||||
it('test badge name is displayed for own profile', function() {
|
||||
testBadgeNameIsDisplayed(true);
|
||||
});
|
||||
|
||||
it('test badge name is displayed for other profile', function() {
|
||||
testBadgeNameIsDisplayed(false);
|
||||
});
|
||||
|
||||
var testBadgeIconIsDisplayed = function(ownProfile) {
|
||||
view = createView(ownProfile);
|
||||
var badgeImg = view.$el.find('img.badge');
|
||||
expect(badgeImg.length).toBe(1);
|
||||
expect(badgeImg.attr('src')).toEqual(badge.image_url);
|
||||
};
|
||||
|
||||
it('test badge icon is displayed for own profile', function() {
|
||||
testBadgeIconIsDisplayed(true);
|
||||
});
|
||||
|
||||
it('test badge icon is displayed for other profile', function() {
|
||||
testBadgeIconIsDisplayed(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,244 +0,0 @@
|
||||
define(['underscore', 'URI', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'], function(_, URI, AjaxHelpers) {
|
||||
'use strict';
|
||||
|
||||
var expectProfileElementContainsField = function(element, view) {
|
||||
var $element = $(element);
|
||||
var fieldTitle = $element.find('.u-field-title').text().trim();
|
||||
|
||||
if (!_.isUndefined(view.options.title)) {
|
||||
expect(fieldTitle).toBe(view.options.title);
|
||||
}
|
||||
|
||||
if ('fieldValue' in view || 'imageUrl' in view) {
|
||||
if ('imageUrl' in view) {
|
||||
expect($($element.find('.image-frame')[0]).attr('src')).toBe(view.imageUrl());
|
||||
} 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()));
|
||||
} else {
|
||||
expect($($element.find('.u-field-value .u-field-value-readonly')[0]).text()).toBe(view.modelValue());
|
||||
}
|
||||
} else {
|
||||
throw new Error('Unexpected field type: ' + view.fieldType);
|
||||
}
|
||||
};
|
||||
|
||||
var expectProfilePrivacyFieldTobeRendered = function(learnerProfileView, othersProfile) {
|
||||
var accountPrivacyElement = learnerProfileView.$('.wrapper-profile-field-account-privacy');
|
||||
var privacyFieldElement = $(accountPrivacyElement).find('.u-field');
|
||||
|
||||
if (othersProfile) {
|
||||
expect(privacyFieldElement.length).toBe(0);
|
||||
} else {
|
||||
expect(privacyFieldElement.length).toBe(1);
|
||||
expectProfileElementContainsField(privacyFieldElement, learnerProfileView.options.accountPrivacyFieldView);
|
||||
}
|
||||
};
|
||||
|
||||
var expectSectionOneTobeRendered = function(learnerProfileView) {
|
||||
var sectionOneFieldElements = $(learnerProfileView.$('.wrapper-profile-section-one')).find('.u-field');
|
||||
|
||||
expect(sectionOneFieldElements.length).toBe(4);
|
||||
expectProfileElementContainsField(sectionOneFieldElements[0], learnerProfileView.options.profileImageFieldView);
|
||||
expectProfileElementContainsField(sectionOneFieldElements[1], learnerProfileView.options.usernameFieldView);
|
||||
|
||||
_.each(_.rest(sectionOneFieldElements, 2), function(sectionFieldElement, fieldIndex) {
|
||||
expectProfileElementContainsField(
|
||||
sectionFieldElement,
|
||||
learnerProfileView.options.sectionOneFieldViews[fieldIndex]
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
var expectSectionTwoTobeRendered = function(learnerProfileView) {
|
||||
var sectionTwoElement = learnerProfileView.$('.wrapper-profile-section-two');
|
||||
var sectionTwoFieldElements = $(sectionTwoElement).find('.u-field');
|
||||
|
||||
expect(sectionTwoFieldElements.length).toBe(learnerProfileView.options.sectionTwoFieldViews.length);
|
||||
|
||||
_.each(sectionTwoFieldElements, function(sectionFieldElement, fieldIndex) {
|
||||
expectProfileElementContainsField(
|
||||
sectionFieldElement,
|
||||
learnerProfileView.options.sectionTwoFieldViews[fieldIndex]
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
var expectProfileSectionsAndFieldsToBeRendered = function(learnerProfileView, othersProfile) {
|
||||
expectProfilePrivacyFieldTobeRendered(learnerProfileView, othersProfile);
|
||||
expectSectionOneTobeRendered(learnerProfileView);
|
||||
expectSectionTwoTobeRendered(learnerProfileView);
|
||||
};
|
||||
|
||||
var expectLimitedProfileSectionsAndFieldsToBeRendered = function(learnerProfileView, othersProfile) {
|
||||
expectProfilePrivacyFieldTobeRendered(learnerProfileView, othersProfile);
|
||||
|
||||
var sectionOneFieldElements = $(learnerProfileView.$('.wrapper-profile-section-one')).find('.u-field');
|
||||
|
||||
expect(sectionOneFieldElements.length).toBe(2);
|
||||
expectProfileElementContainsField(
|
||||
sectionOneFieldElements[0],
|
||||
learnerProfileView.options.profileImageFieldView
|
||||
);
|
||||
expectProfileElementContainsField(
|
||||
sectionOneFieldElements[1],
|
||||
learnerProfileView.options.usernameFieldView
|
||||
);
|
||||
|
||||
if (othersProfile) {
|
||||
expect($('.profile-private--message').text())
|
||||
.toBe('This learner is currently sharing a limited profile.');
|
||||
} else {
|
||||
expect($('.profile-private--message').text()).toBe('You are currently sharing a limited profile.');
|
||||
}
|
||||
};
|
||||
|
||||
var expectProfileSectionsNotToBeRendered = function(learnerProfileView) {
|
||||
expect(learnerProfileView.$('.wrapper-profile-field-account-privacy').length).toBe(0);
|
||||
expect(learnerProfileView.$('.wrapper-profile-section-one').length).toBe(0);
|
||||
expect(learnerProfileView.$('.wrapper-profile-section-two').length).toBe(0);
|
||||
};
|
||||
|
||||
var expectTabbedViewToBeUndefined = function(requests, tabbedViewView) {
|
||||
// Unrelated initial request, no badge request
|
||||
expect(requests.length).toBe(1);
|
||||
expect(tabbedViewView).toBe(undefined);
|
||||
};
|
||||
|
||||
var expectTabbedViewToBeShown = function(tabbedViewView) {
|
||||
expect(tabbedViewView.$el.find('.page-content-nav').is(':visible')).toBe(true);
|
||||
};
|
||||
|
||||
var expectBadgesDisplayed = function(learnerProfileView, length, lastPage) {
|
||||
var badgeListingView = learnerProfileView.$el.find('#tabpanel-accomplishments');
|
||||
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');
|
||||
expect(placeholder.length).toBe(1);
|
||||
expect(placeholder.attr('href')).toBe('/courses/');
|
||||
}
|
||||
expect(badgeListingView.find('.badge-display').length).toBe(length);
|
||||
};
|
||||
|
||||
var expectBadgesHidden = function(learnerProfileView) {
|
||||
var accomplishmentsTab = learnerProfileView.$el.find('#tabpanel-accomplishments');
|
||||
if (accomplishmentsTab.length) {
|
||||
// Nonexistence counts as hidden.
|
||||
expect(learnerProfileView.$el.find('#tabpanel-accomplishments').hasClass('is-hidden')).toBe(true);
|
||||
}
|
||||
expect(learnerProfileView.$el.find('#tabpanel-about_me').hasClass('is-hidden')).toBe(false);
|
||||
};
|
||||
|
||||
var expectPage = function(learnerProfileView, pageData) {
|
||||
var badgeListContainer = learnerProfileView.$el.find('#tabpanel-accomplishments');
|
||||
var index = badgeListContainer.find('span.search-count').text().trim();
|
||||
expect(index).toBe('Showing ' + (pageData.start + 1) + '-' + (pageData.start + pageData.results.length) +
|
||||
' out of ' + pageData.count + ' total');
|
||||
expect(badgeListContainer.find('.current-page').text()).toBe('' + pageData.current_page);
|
||||
_.each(pageData.results, function(badge) {
|
||||
expect($('.badge-display:contains(' + badge.badge_class.display_name + ')').length).toBe(1);
|
||||
});
|
||||
};
|
||||
|
||||
var expectBadgeLoadingErrorIsRendered = function(learnerProfileView) {
|
||||
var errorMessage = learnerProfileView.$el.find('.badge-set-display').text();
|
||||
expect(errorMessage).toBe(
|
||||
'Your request could not be completed. Reload the page and try again. If the issue persists, click the ' +
|
||||
'Help tab to report the problem.'
|
||||
);
|
||||
};
|
||||
|
||||
var breakBadgeLoading = function(learnerProfileView, requests) {
|
||||
var request = AjaxHelpers.currentRequest(requests);
|
||||
var path = new URI(request.url).path();
|
||||
expect(path).toBe('/api/badges/v1/assertions/user/student/');
|
||||
AjaxHelpers.respondWithError(requests, 500);
|
||||
};
|
||||
|
||||
var firstPageBadges = {
|
||||
count: 30,
|
||||
previous: null,
|
||||
next: '/arbitrary/url',
|
||||
num_pages: 3,
|
||||
start: 0,
|
||||
current_page: 1,
|
||||
results: []
|
||||
};
|
||||
|
||||
var secondPageBadges = {
|
||||
count: 30,
|
||||
previous: '/arbitrary/url',
|
||||
next: '/arbitrary/url',
|
||||
num_pages: 3,
|
||||
start: 10,
|
||||
current_page: 2,
|
||||
results: []
|
||||
};
|
||||
|
||||
var thirdPageBadges = {
|
||||
count: 30,
|
||||
previous: '/arbitrary/url',
|
||||
num_pages: 3,
|
||||
next: null,
|
||||
start: 20,
|
||||
current_page: 3,
|
||||
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'
|
||||
},
|
||||
'image_url': 'http://example.com/image.png',
|
||||
'assertion_url': 'http://example.com/example.json',
|
||||
'created_at': '2015-12-03T16:25:57.676113Z'
|
||||
};
|
||||
}
|
||||
|
||||
_.each(_.range(0, 10), function(i) {
|
||||
firstPageBadges.results.push(makeBadge(i));
|
||||
});
|
||||
|
||||
_.each(_.range(10, 20), function(i) {
|
||||
secondPageBadges.results.push(makeBadge(i));
|
||||
});
|
||||
|
||||
_.each(_.range(20, 30), function(i) {
|
||||
thirdPageBadges.results.push(makeBadge(i));
|
||||
});
|
||||
|
||||
var emptyBadges = {
|
||||
'count': 0,
|
||||
'previous': null,
|
||||
'num_pages': 1,
|
||||
'results': []
|
||||
};
|
||||
|
||||
return {
|
||||
expectLimitedProfileSectionsAndFieldsToBeRendered: expectLimitedProfileSectionsAndFieldsToBeRendered,
|
||||
expectProfileSectionsAndFieldsToBeRendered: expectProfileSectionsAndFieldsToBeRendered,
|
||||
expectProfileSectionsNotToBeRendered: expectProfileSectionsNotToBeRendered,
|
||||
expectTabbedViewToBeUndefined: expectTabbedViewToBeUndefined,
|
||||
expectTabbedViewToBeShown: expectTabbedViewToBeShown,
|
||||
expectBadgesDisplayed: expectBadgesDisplayed,
|
||||
expectBadgesHidden: expectBadgesHidden,
|
||||
expectBadgeLoadingErrorIsRendered: expectBadgeLoadingErrorIsRendered,
|
||||
breakBadgeLoading: breakBadgeLoading,
|
||||
firstPageBadges: firstPageBadges,
|
||||
secondPageBadges: secondPageBadges,
|
||||
thirdPageBadges: thirdPageBadges,
|
||||
emptyBadges: emptyBadges,
|
||||
expectPage: expectPage,
|
||||
makeBadge: makeBadge
|
||||
};
|
||||
});
|
||||
@@ -1,229 +0,0 @@
|
||||
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/spec/student_profile/helpers',
|
||||
'js/views/fields',
|
||||
'js/student_account/models/user_account_model',
|
||||
'js/student_account/models/user_preferences_model',
|
||||
'js/student_profile/views/learner_profile_view',
|
||||
'js/student_profile/views/learner_profile_fields',
|
||||
'js/student_profile/views/learner_profile_factory',
|
||||
'js/views/message_banner'
|
||||
],
|
||||
function(Backbone, $, _, AjaxHelpers, TemplateHelpers, Helpers, LearnerProfileHelpers, FieldViews,
|
||||
UserAccountModel, UserPreferencesModel, LearnerProfileView, LearnerProfileFields, LearnerProfilePage) {
|
||||
'use strict';
|
||||
|
||||
describe('edx.user.LearnerProfileFactory', function() {
|
||||
var requests;
|
||||
|
||||
beforeEach(function() {
|
||||
loadFixtures('js/fixtures/student_profile/student_profile.html');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
Backbone.history.stop();
|
||||
});
|
||||
|
||||
var createProfilePage = function(ownProfile, options) {
|
||||
return new LearnerProfilePage({
|
||||
'accounts_api_url': Helpers.USER_ACCOUNTS_API_URL,
|
||||
'preferences_api_url': Helpers.USER_PREFERENCES_API_URL,
|
||||
'badges_api_url': Helpers.BADGES_API_URL,
|
||||
'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,
|
||||
'profile_image_max_bytes': Helpers.IMAGE_MAX_BYTES,
|
||||
'profile_image_min_bytes': Helpers.IMAGE_MIN_BYTES,
|
||||
'profile_image_upload_url': Helpers.IMAGE_UPLOAD_API_URL,
|
||||
'profile_image_remove_url': Helpers.IMAGE_REMOVE_API_URL,
|
||||
'default_visibility': 'all_users',
|
||||
'platform_name': 'edX',
|
||||
'find_courses_url': '/courses/',
|
||||
'account_settings_data': Helpers.createAccountSettingsData(options),
|
||||
'preferences_data': Helpers.createUserPreferencesData()
|
||||
});
|
||||
};
|
||||
|
||||
it('renders the full profile for a user', function() {
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
var context = createProfilePage(true),
|
||||
learnerProfileView = context.learnerProfileView;
|
||||
|
||||
// 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() {
|
||||
var context = createProfilePage(true, {year_of_birth: '', requires_parental_consent: true}),
|
||||
learnerProfileView = context.learnerProfileView;
|
||||
|
||||
LearnerProfileHelpers.expectLimitedProfileSectionsAndFieldsToBeRendered(learnerProfileView);
|
||||
});
|
||||
|
||||
it("doesn't show the mode toggle if badges are disabled", function() {
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
var context = createProfilePage(true, {accomplishments_shared: false}),
|
||||
tabbedView = context.learnerProfileView.tabbedView,
|
||||
learnerProfileView = context.learnerProfileView;
|
||||
|
||||
LearnerProfileHelpers.expectTabbedViewToBeUndefined(requests, tabbedView);
|
||||
LearnerProfileHelpers.expectBadgesHidden(learnerProfileView);
|
||||
});
|
||||
|
||||
it("doesn't show the mode toggle if badges fail to fetch", function() {
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
var context = createProfilePage(true, {accomplishments_shared: false}),
|
||||
tabbedView = context.learnerProfileView.tabbedView,
|
||||
learnerProfileView = context.learnerProfileView;
|
||||
|
||||
LearnerProfileHelpers.expectTabbedViewToBeUndefined(requests, tabbedView);
|
||||
LearnerProfileHelpers.expectBadgesHidden(learnerProfileView);
|
||||
});
|
||||
|
||||
it('renders the mode toggle if there are badges', function() {
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
var context = createProfilePage(true, {accomplishments_shared: true}),
|
||||
tabbedView = context.learnerProfileView.tabbedView;
|
||||
|
||||
AjaxHelpers.expectRequest(requests, 'POST', '/event');
|
||||
AjaxHelpers.respondWithError(requests, 404);
|
||||
AjaxHelpers.respondWithJson(requests, LearnerProfileHelpers.firstPageBadges);
|
||||
|
||||
LearnerProfileHelpers.expectTabbedViewToBeShown(tabbedView);
|
||||
});
|
||||
|
||||
it('renders the mode toggle if badges enabled but none exist', function() {
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
var context = createProfilePage(true, {accomplishments_shared: true}),
|
||||
tabbedView = context.learnerProfileView.tabbedView;
|
||||
|
||||
AjaxHelpers.expectRequest(requests, 'POST', '/event');
|
||||
AjaxHelpers.respondWithError(requests, 404);
|
||||
AjaxHelpers.respondWithJson(requests, LearnerProfileHelpers.emptyBadges);
|
||||
|
||||
LearnerProfileHelpers.expectTabbedViewToBeShown(tabbedView);
|
||||
});
|
||||
|
||||
it('displays the badges when the accomplishments toggle is selected', function() {
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
var context = createProfilePage(true, {accomplishments_shared: true}),
|
||||
learnerProfileView = context.learnerProfileView,
|
||||
tabbedView = learnerProfileView.tabbedView;
|
||||
|
||||
AjaxHelpers.expectRequest(requests, 'POST', '/event');
|
||||
AjaxHelpers.respondWithError(requests, 404);
|
||||
AjaxHelpers.respondWithJson(requests, LearnerProfileHelpers.secondPageBadges);
|
||||
|
||||
LearnerProfileHelpers.expectBadgesHidden(learnerProfileView);
|
||||
tabbedView.$el.find('[data-url="accomplishments"]').click();
|
||||
LearnerProfileHelpers.expectBadgesDisplayed(learnerProfileView, 10, false);
|
||||
tabbedView.$el.find('[data-url="about_me"]').click();
|
||||
LearnerProfileHelpers.expectBadgesHidden(learnerProfileView);
|
||||
});
|
||||
|
||||
it('displays a placeholder on the last page of badges', function() {
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
var context = createProfilePage(true, {accomplishments_shared: true}),
|
||||
learnerProfileView = context.learnerProfileView,
|
||||
tabbedView = learnerProfileView.tabbedView;
|
||||
|
||||
AjaxHelpers.expectRequest(requests, 'POST', '/event');
|
||||
AjaxHelpers.respondWithError(requests, 404);
|
||||
AjaxHelpers.respondWithJson(requests, LearnerProfileHelpers.thirdPageBadges);
|
||||
|
||||
LearnerProfileHelpers.expectBadgesHidden(learnerProfileView);
|
||||
tabbedView.$el.find('[data-url="accomplishments"]').click();
|
||||
LearnerProfileHelpers.expectBadgesDisplayed(learnerProfileView, 10, true);
|
||||
tabbedView.$el.find('[data-url="about_me"]').click();
|
||||
LearnerProfileHelpers.expectBadgesHidden(learnerProfileView);
|
||||
});
|
||||
|
||||
it('displays a placeholder when the accomplishments toggle is selected and no badges exist', function() {
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
var context = createProfilePage(true, {accomplishments_shared: true}),
|
||||
learnerProfileView = context.learnerProfileView,
|
||||
tabbedView = learnerProfileView.tabbedView;
|
||||
|
||||
AjaxHelpers.expectRequest(requests, 'POST', '/event');
|
||||
AjaxHelpers.respondWithError(requests, 404);
|
||||
AjaxHelpers.respondWithJson(requests, LearnerProfileHelpers.emptyBadges);
|
||||
|
||||
LearnerProfileHelpers.expectBadgesHidden(learnerProfileView);
|
||||
tabbedView.$el.find('[data-url="accomplishments"]').click();
|
||||
LearnerProfileHelpers.expectBadgesDisplayed(learnerProfileView, 0, true);
|
||||
tabbedView.$el.find('[data-url="about_me"]').click();
|
||||
LearnerProfileHelpers.expectBadgesHidden(learnerProfileView);
|
||||
});
|
||||
|
||||
it('shows a paginated list of badges', function() {
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
var context = createProfilePage(true, {accomplishments_shared: true}),
|
||||
learnerProfileView = context.learnerProfileView,
|
||||
tabbedView = learnerProfileView.tabbedView;
|
||||
|
||||
AjaxHelpers.expectRequest(requests, 'POST', '/event');
|
||||
AjaxHelpers.respondWithError(requests, 404);
|
||||
AjaxHelpers.respondWithJson(requests, LearnerProfileHelpers.firstPageBadges);
|
||||
|
||||
tabbedView.$el.find('[data-url="accomplishments"]').click();
|
||||
LearnerProfileHelpers.expectBadgesDisplayed(learnerProfileView, 10, false);
|
||||
LearnerProfileHelpers.expectPage(learnerProfileView, LearnerProfileHelpers.firstPageBadges);
|
||||
});
|
||||
|
||||
it('allows forward and backward navigation of badges', function() {
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
var context = createProfilePage(true, {accomplishments_shared: true}),
|
||||
learnerProfileView = context.learnerProfileView,
|
||||
tabbedView = learnerProfileView.tabbedView,
|
||||
badgeListContainer = context.badgeListContainer;
|
||||
|
||||
AjaxHelpers.expectRequest(requests, 'POST', '/event');
|
||||
AjaxHelpers.respondWithError(requests, 404);
|
||||
AjaxHelpers.respondWithJson(requests, LearnerProfileHelpers.firstPageBadges);
|
||||
|
||||
tabbedView.$el.find('[data-url="accomplishments"]').click();
|
||||
|
||||
badgeListContainer.$el.find('.next-page-link').click();
|
||||
AjaxHelpers.respondWithJson(requests, LearnerProfileHelpers.secondPageBadges);
|
||||
LearnerProfileHelpers.expectPage(learnerProfileView, LearnerProfileHelpers.secondPageBadges);
|
||||
|
||||
badgeListContainer.$el.find('.next-page-link').click();
|
||||
AjaxHelpers.respondWithJson(requests, LearnerProfileHelpers.thirdPageBadges);
|
||||
LearnerProfileHelpers.expectBadgesDisplayed(learnerProfileView, 10, true);
|
||||
LearnerProfileHelpers.expectPage(learnerProfileView, LearnerProfileHelpers.thirdPageBadges);
|
||||
|
||||
badgeListContainer.$el.find('.previous-page-link').click();
|
||||
AjaxHelpers.respondWithJson(requests, LearnerProfileHelpers.secondPageBadges);
|
||||
LearnerProfileHelpers.expectPage(learnerProfileView, LearnerProfileHelpers.secondPageBadges);
|
||||
LearnerProfileHelpers.expectBadgesDisplayed(learnerProfileView, 10, false);
|
||||
|
||||
badgeListContainer.$el.find('.previous-page-link').click();
|
||||
AjaxHelpers.respondWithJson(requests, LearnerProfileHelpers.firstPageBadges);
|
||||
LearnerProfileHelpers.expectPage(learnerProfileView, LearnerProfileHelpers.firstPageBadges);
|
||||
});
|
||||
|
||||
|
||||
it('renders the limited profile for under 13 users', function() {
|
||||
var context = createProfilePage(
|
||||
true,
|
||||
{year_of_birth: new Date().getFullYear() - 10, requires_parental_consent: true}
|
||||
);
|
||||
var learnerProfileView = context.learnerProfileView;
|
||||
LearnerProfileHelpers.expectLimitedProfileSectionsAndFieldsToBeRendered(learnerProfileView);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,293 +0,0 @@
|
||||
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',
|
||||
'js/views/message_banner'
|
||||
],
|
||||
function(Backbone, $, _, AjaxHelpers, TemplateHelpers, Helpers, UserAccountModel, LearnerProfileFields,
|
||||
MessageBannerView) {
|
||||
'use strict';
|
||||
|
||||
describe('edx.user.LearnerProfileFields', function() {
|
||||
var MOCK_YEAR_OF_BIRTH = 1989;
|
||||
var MOCK_IMAGE_MAX_BYTES = 64;
|
||||
var MOCK_IMAGE_MIN_BYTES = 16;
|
||||
|
||||
var createImageView = function(options) {
|
||||
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 imageData = {
|
||||
image_url_large: '/media/profile-images/default.jpg',
|
||||
has_image: options.hasImage ? true : false
|
||||
};
|
||||
|
||||
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.url = Helpers.USER_ACCOUNTS_API_URL;
|
||||
|
||||
var messageView = new MessageBannerView({
|
||||
el: $('.message-banner')
|
||||
});
|
||||
|
||||
return new LearnerProfileFields.ProfileImageFieldView({
|
||||
model: accountSettingsModel,
|
||||
valueAttribute: 'profile_image',
|
||||
editable: options.ownProfile,
|
||||
messageView: messageView,
|
||||
imageMaxBytes: imageMaxBytes,
|
||||
imageMinBytes: imageMinBytes,
|
||||
imageUploadUrl: Helpers.IMAGE_UPLOAD_API_URL,
|
||||
imageRemoveUrl: Helpers.IMAGE_REMOVE_API_URL
|
||||
});
|
||||
};
|
||||
|
||||
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(
|
||||
[fileFakeData.substr(0, size)],
|
||||
{type: 'image/jpg'}
|
||||
);
|
||||
};
|
||||
|
||||
var initializeUploader = function(view) {
|
||||
view.$('.upload-button-input').fileupload({
|
||||
url: Helpers.IMAGE_UPLOAD_API_URL,
|
||||
type: 'POST',
|
||||
add: view.fileSelected,
|
||||
done: view.imageChangeSucceeded,
|
||||
fail: view.imageChangeFailed
|
||||
});
|
||||
};
|
||||
|
||||
describe('ProfileImageFieldView', function() {
|
||||
var verifyImageUploadButtonMessage = function(view, inProgress) {
|
||||
var iconName = inProgress ? 'fa-spinner' : 'fa-camera';
|
||||
var message = inProgress ? view.titleUploading : view.uploadButtonTitle();
|
||||
expect(view.$('.upload-button-icon span').attr('class')).toContain(iconName);
|
||||
expect(view.$('.upload-button-title').text().trim()).toBe(message);
|
||||
};
|
||||
|
||||
var verifyImageRemoveButtonMessage = function(view, inProgress) {
|
||||
var iconName = inProgress ? 'fa-spinner' : 'fa-remove';
|
||||
var message = inProgress ? view.titleRemoving : view.removeButtonTitle();
|
||||
expect(view.$('.remove-button-icon span').attr('class')).toContain(iconName);
|
||||
expect(view.$('.remove-button-title').text().trim()).toBe(message);
|
||||
};
|
||||
|
||||
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';
|
||||
|
||||
initializeUploader(imageView);
|
||||
|
||||
// Remove button should not be present for default image
|
||||
expect(imageView.$('.u-field-remove-button').css('display') === 'none').toBeTruthy();
|
||||
|
||||
// For default image, image title should be `Upload an image`
|
||||
verifyImageUploadButtonMessage(imageView, false);
|
||||
|
||||
// Add image to upload queue. Validate the image size and send POST request to upload image
|
||||
imageView.$('.upload-button-input').fileupload('add', {files: [createFakeImageFile(60)]});
|
||||
|
||||
// Verify image upload progress message
|
||||
verifyImageUploadButtonMessage(imageView, true);
|
||||
|
||||
// Verify if POST request received for image upload
|
||||
AjaxHelpers.expectRequest(requests, 'POST', Helpers.IMAGE_UPLOAD_API_URL, new FormData());
|
||||
|
||||
// Send 204 NO CONTENT to confirm the image upload success
|
||||
AjaxHelpers.respondWithNoContent(requests);
|
||||
|
||||
// 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: {
|
||||
image_url_large: '/media/profile-images/' + imageName,
|
||||
has_image: true
|
||||
}};
|
||||
AjaxHelpers.respondWithJson(requests, data);
|
||||
|
||||
// Verify uploaded image name
|
||||
expect(imageView.$('.image-frame').attr('src')).toContain(imageName);
|
||||
|
||||
// Remove button should be present after successful image upload
|
||||
expect(imageView.$('.u-field-remove-button').css('display') !== 'none').toBeTruthy();
|
||||
|
||||
// After image upload, image title should be `Change image`
|
||||
verifyImageUploadButtonMessage(imageView, false);
|
||||
});
|
||||
|
||||
it('can remove profile image', function() {
|
||||
var imageView = createImageView({ownProfile: true, hasImage: false});
|
||||
imageView.render();
|
||||
|
||||
var requests = AjaxHelpers.requests(this);
|
||||
|
||||
// Verify image remove title
|
||||
verifyImageRemoveButtonMessage(imageView, false);
|
||||
|
||||
imageView.$('.u-field-remove-button').click();
|
||||
|
||||
// Verify image remove progress message
|
||||
verifyImageRemoveButtonMessage(imageView, true);
|
||||
|
||||
// Verify if POST request received for image remove
|
||||
AjaxHelpers.expectRequest(requests, 'POST', Helpers.IMAGE_REMOVE_API_URL, null);
|
||||
|
||||
// Send 204 NO CONTENT to confirm the image removal success
|
||||
AjaxHelpers.respondWithNoContent(requests);
|
||||
|
||||
// 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: {
|
||||
image_url_large: '/media/profile-images/default.jpg',
|
||||
has_image: false
|
||||
}};
|
||||
AjaxHelpers.respondWithJson(requests, data);
|
||||
|
||||
// Remove button should not be present for default image
|
||||
expect(imageView.$('.u-field-remove-button').css('display') === 'none').toBeTruthy();
|
||||
});
|
||||
|
||||
it("can't remove default profile image", function() {
|
||||
var imageView = createImageView({ownProfile: true, hasImage: false});
|
||||
imageView.render();
|
||||
|
||||
spyOn(imageView, 'clickedRemoveButton');
|
||||
|
||||
// Remove button should not be present for default image
|
||||
expect(imageView.$('.u-field-remove-button').css('display') === 'none').toBeTruthy();
|
||||
|
||||
imageView.$('.u-field-remove-button').click();
|
||||
|
||||
// Remove button click handler should not be called
|
||||
expect(imageView.clickedRemoveButton).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("can't upload image having size greater than max size", function() {
|
||||
var imageView = createImageView({ownProfile: true, hasImage: false});
|
||||
imageView.render();
|
||||
|
||||
initializeUploader(imageView);
|
||||
|
||||
// Add image to upload queue, this will validate the image size
|
||||
imageView.$('.upload-button-input').fileupload('add', {files: [createFakeImageFile(70)]});
|
||||
|
||||
// Verify error message
|
||||
expect($('.message-banner').text().trim())
|
||||
.toBe('The file must be smaller than 64 bytes in size.');
|
||||
});
|
||||
|
||||
it("can't upload image having size less than min size", function() {
|
||||
var imageView = createImageView({ownProfile: true, hasImage: false});
|
||||
imageView.render();
|
||||
|
||||
initializeUploader(imageView);
|
||||
|
||||
// Add image to upload queue, this will validate the image size
|
||||
imageView.$('.upload-button-input').fileupload('add', {files: [createFakeImageFile(10)]});
|
||||
|
||||
// Verify error message
|
||||
expect($('.message-banner').text().trim()).toBe('The file must be at least 16 bytes in size.');
|
||||
});
|
||||
|
||||
it("can't upload and remove image if parental consent required", function() {
|
||||
var imageView = createImageView({ownProfile: true, hasImage: false, yearOfBirth: ''});
|
||||
imageView.render();
|
||||
|
||||
spyOn(imageView, 'clickedUploadButton');
|
||||
spyOn(imageView, 'clickedRemoveButton');
|
||||
|
||||
expect(imageView.$('.u-field-upload-button').css('display') === 'none').toBeTruthy();
|
||||
expect(imageView.$('.u-field-remove-button').css('display') === 'none').toBeTruthy();
|
||||
|
||||
imageView.$('.u-field-upload-button').click();
|
||||
imageView.$('.u-field-remove-button').click();
|
||||
|
||||
expect(imageView.clickedUploadButton).not.toHaveBeenCalled();
|
||||
expect(imageView.clickedRemoveButton).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("can't upload and remove image on others profile", function() {
|
||||
var imageView = createImageView({ownProfile: false});
|
||||
imageView.render();
|
||||
|
||||
spyOn(imageView, 'clickedUploadButton');
|
||||
spyOn(imageView, 'clickedRemoveButton');
|
||||
|
||||
expect(imageView.$('.u-field-upload-button').css('display') === 'none').toBeTruthy();
|
||||
expect(imageView.$('.u-field-remove-button').css('display') === 'none').toBeTruthy();
|
||||
|
||||
imageView.$('.u-field-upload-button').click();
|
||||
imageView.$('.u-field-remove-button').click();
|
||||
|
||||
expect(imageView.clickedUploadButton).not.toHaveBeenCalled();
|
||||
expect(imageView.clickedRemoveButton).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('shows message if we try to navigate away during image upload/remove', function() {
|
||||
var imageView = createImageView({ownProfile: true, hasImage: false});
|
||||
spyOn(imageView, 'onBeforeUnload');
|
||||
imageView.render();
|
||||
|
||||
initializeUploader(imageView);
|
||||
|
||||
// Add image to upload queue, this will validate image size and send POST request to upload image
|
||||
imageView.$('.upload-button-input').fileupload('add', {files: [createFakeImageFile(60)]});
|
||||
|
||||
// Verify image upload progress message
|
||||
verifyImageUploadButtonMessage(imageView, true);
|
||||
|
||||
window.onbeforeunload = null;
|
||||
$(window).trigger('beforeunload');
|
||||
expect(imageView.onBeforeUnload).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('shows error message for HTTP 500', function() {
|
||||
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
|
||||
imageView.$('.upload-button-input').fileupload('add', {files: [createFakeImageFile(60)]});
|
||||
|
||||
// Verify image upload progress message
|
||||
verifyImageUploadButtonMessage(imageView, true);
|
||||
|
||||
// Verify if POST request received for image upload
|
||||
AjaxHelpers.expectRequest(requests, 'POST', Helpers.IMAGE_UPLOAD_API_URL, new FormData());
|
||||
|
||||
// Send HTTP 500
|
||||
AjaxHelpers.respondWithError(requests);
|
||||
|
||||
expect($('.message-banner').text().trim()).toBe(imageView.errorMessage);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,217 +0,0 @@
|
||||
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',
|
||||
'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',
|
||||
'js/student_account/views/account_settings_fields',
|
||||
'js/views/message_banner'
|
||||
],
|
||||
function(Backbone, $, _, PagingCollection, AjaxHelpers, TemplateHelpers, Helpers, LearnerProfileHelpers,
|
||||
FieldViews, UserAccountModel, AccountPreferencesModel, LearnerProfileFields, LearnerProfileView,
|
||||
BadgeListContainer, AccountSettingsFieldViews, MessageBannerView) {
|
||||
'use strict';
|
||||
|
||||
describe('edx.user.LearnerProfileView', function() {
|
||||
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});
|
||||
|
||||
var accountPreferencesModel = new AccountPreferencesModel();
|
||||
accountPreferencesModel.set({account_privacy: accountPrivacy});
|
||||
|
||||
accountPreferencesModel.url = Helpers.USER_PREFERENCES_API_URL;
|
||||
|
||||
var editable = ownProfile ? 'toggle' : 'never';
|
||||
|
||||
var accountPrivacyFieldView = new LearnerProfileFields.AccountPrivacyFieldView({
|
||||
model: accountPreferencesModel,
|
||||
required: true,
|
||||
editable: 'always',
|
||||
showMessages: false,
|
||||
title: 'edX learners can see my:',
|
||||
valueAttribute: 'account_privacy',
|
||||
options: [
|
||||
['all_users', 'Full Profile'],
|
||||
['private', 'Limited Profile']
|
||||
],
|
||||
helpMessage: '',
|
||||
accountSettingsPageUrl: '/account/settings/'
|
||||
});
|
||||
|
||||
var messageView = new MessageBannerView({
|
||||
el: $('.message-banner')
|
||||
});
|
||||
|
||||
var profileImageFieldView = new LearnerProfileFields.ProfileImageFieldView({
|
||||
model: accountSettingsModel,
|
||||
valueAttribute: 'profile_image',
|
||||
editable: editable,
|
||||
messageView: messageView,
|
||||
imageMaxBytes: Helpers.IMAGE_MAX_BYTES,
|
||||
imageMinBytes: Helpers.IMAGE_MIN_BYTES,
|
||||
imageUploadUrl: Helpers.IMAGE_UPLOAD_API_URL,
|
||||
imageRemoveUrl: Helpers.IMAGE_REMOVE_API_URL
|
||||
});
|
||||
|
||||
var usernameFieldView = new FieldViews.ReadonlyFieldView({
|
||||
model: accountSettingsModel,
|
||||
valueAttribute: 'username',
|
||||
helpMessage: ''
|
||||
});
|
||||
|
||||
var sectionOneFieldViews = [
|
||||
new FieldViews.DropdownFieldView({
|
||||
model: accountSettingsModel,
|
||||
required: false,
|
||||
editable: editable,
|
||||
showMessages: false,
|
||||
iconName: 'fa-map-marker',
|
||||
placeholderValue: '',
|
||||
valueAttribute: 'country',
|
||||
options: Helpers.FIELD_OPTIONS,
|
||||
helpMessage: ''
|
||||
}),
|
||||
|
||||
new AccountSettingsFieldViews.LanguageProficienciesFieldView({
|
||||
model: accountSettingsModel,
|
||||
required: false,
|
||||
editable: editable,
|
||||
showMessages: false,
|
||||
iconName: 'fa-comment',
|
||||
placeholderValue: 'Add language',
|
||||
valueAttribute: 'language_proficiencies',
|
||||
options: Helpers.FIELD_OPTIONS,
|
||||
helpMessage: ''
|
||||
})
|
||||
];
|
||||
|
||||
var sectionTwoFieldViews = [
|
||||
new FieldViews.TextareaFieldView({
|
||||
model: accountSettingsModel,
|
||||
editable: editable,
|
||||
showMessages: false,
|
||||
title: 'About me',
|
||||
placeholderValue: 'Tell other edX learners a little about yourself: where you live, ' +
|
||||
"what your interests are, why you're taking courses on edX, or what you hope to learn.",
|
||||
valueAttribute: 'bio',
|
||||
helpMessage: '',
|
||||
messagePosition: 'header'
|
||||
})
|
||||
];
|
||||
|
||||
var badgeCollection = new PagingCollection();
|
||||
badgeCollection.url = Helpers.BADGES_API_URL;
|
||||
|
||||
var badgeListContainer = new BadgeListContainer({
|
||||
'attributes': {'class': 'badge-set-display'},
|
||||
'collection': badgeCollection,
|
||||
'find_courses_url': Helpers.FIND_COURSES_URL
|
||||
});
|
||||
|
||||
return new LearnerProfileView(
|
||||
{
|
||||
el: $('.wrapper-profile'),
|
||||
ownProfile: ownProfile,
|
||||
hasPreferencesAccess: true,
|
||||
accountSettingsModel: accountSettingsModel,
|
||||
preferencesModel: accountPreferencesModel,
|
||||
accountPrivacyFieldView: accountPrivacyFieldView,
|
||||
usernameFieldView: usernameFieldView,
|
||||
profileImageFieldView: profileImageFieldView,
|
||||
sectionOneFieldViews: sectionOneFieldViews,
|
||||
sectionTwoFieldViews: sectionTwoFieldViews,
|
||||
badgeListContainer: badgeListContainer
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
loadFixtures('js/fixtures/student_profile/student_profile.html');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
Backbone.history.stop();
|
||||
});
|
||||
|
||||
it('shows loading error correctly', function() {
|
||||
var learnerProfileView = createLearnerProfileView(false, 'all_users');
|
||||
|
||||
Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, true);
|
||||
Helpers.expectLoadingErrorIsVisible(learnerProfileView, false);
|
||||
|
||||
learnerProfileView.render();
|
||||
learnerProfileView.showLoadingError();
|
||||
|
||||
Helpers.expectLoadingErrorIsVisible(learnerProfileView, true);
|
||||
});
|
||||
|
||||
it('renders all fields as expected for self with full access', function() {
|
||||
var learnerProfileView = createLearnerProfileView(true, 'all_users', true);
|
||||
|
||||
Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, true);
|
||||
Helpers.expectLoadingErrorIsVisible(learnerProfileView, false);
|
||||
|
||||
learnerProfileView.render();
|
||||
|
||||
Helpers.expectLoadingErrorIsVisible(learnerProfileView, false);
|
||||
LearnerProfileHelpers.expectProfileSectionsAndFieldsToBeRendered(learnerProfileView);
|
||||
});
|
||||
|
||||
it('renders all fields as expected for self with limited access', function() {
|
||||
var learnerProfileView = createLearnerProfileView(true, 'private', false);
|
||||
|
||||
Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, true);
|
||||
Helpers.expectLoadingErrorIsVisible(learnerProfileView, false);
|
||||
|
||||
learnerProfileView.render();
|
||||
|
||||
Helpers.expectLoadingErrorIsVisible(learnerProfileView, false);
|
||||
LearnerProfileHelpers.expectLimitedProfileSectionsAndFieldsToBeRendered(learnerProfileView);
|
||||
});
|
||||
|
||||
it('renders the fields as expected for others with full access', function() {
|
||||
var learnerProfileView = createLearnerProfileView(false, 'all_users', true);
|
||||
|
||||
Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, true);
|
||||
Helpers.expectLoadingErrorIsVisible(learnerProfileView, false);
|
||||
|
||||
learnerProfileView.render();
|
||||
|
||||
Helpers.expectLoadingErrorIsVisible(learnerProfileView, false);
|
||||
LearnerProfileHelpers.expectProfileSectionsAndFieldsToBeRendered(learnerProfileView, true);
|
||||
});
|
||||
|
||||
it('renders the fields as expected for others with limited access', function() {
|
||||
var learnerProfileView = createLearnerProfileView(false, 'private', false);
|
||||
|
||||
Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, true);
|
||||
Helpers.expectLoadingErrorIsVisible(learnerProfileView, false);
|
||||
|
||||
learnerProfileView.render();
|
||||
|
||||
Helpers.expectLoadingErrorIsVisible(learnerProfileView, false);
|
||||
LearnerProfileHelpers.expectLimitedProfileSectionsAndFieldsToBeRendered(learnerProfileView, true);
|
||||
});
|
||||
|
||||
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});
|
||||
var requests = AjaxHelpers.requests(this);
|
||||
|
||||
learnerProfileView.render();
|
||||
|
||||
LearnerProfileHelpers.breakBadgeLoading(learnerProfileView, requests);
|
||||
LearnerProfileHelpers.expectBadgeLoadingErrorIsRendered(learnerProfileView);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,109 +0,0 @@
|
||||
define(['backbone', 'jquery', 'underscore',
|
||||
'js/spec/student_account/helpers',
|
||||
'js/student_profile/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});
|
||||
|
||||
var editable = ownProfile ? 'toggle' : 'never';
|
||||
|
||||
var sectionTwoFieldViews = [
|
||||
new FieldViews.TextareaFieldView({
|
||||
model: accountSettingsModel,
|
||||
editable: editable,
|
||||
showMessages: false,
|
||||
title: 'About me',
|
||||
placeholderValue: 'Tell other edX learners a little about yourself: where you live, ' +
|
||||
"what your interests are, why you're taking courses on edX, or what you hope to learn.",
|
||||
valueAttribute: 'bio',
|
||||
helpMessage: '',
|
||||
messagePosition: 'header'
|
||||
})
|
||||
];
|
||||
|
||||
return new SectionTwoTabView({
|
||||
viewList: sectionTwoFieldViews,
|
||||
showFullProfile: function() {
|
||||
return profileIsPublic;
|
||||
},
|
||||
ownProfile: ownProfile
|
||||
});
|
||||
};
|
||||
|
||||
it('full profile displayed for public profile', function() {
|
||||
var view = createSectionTwoView(false, true);
|
||||
view.render();
|
||||
var bio = view.$el.find('.u-field-bio');
|
||||
expect(bio.length).toBe(1);
|
||||
});
|
||||
|
||||
it('profile field parts are actually rendered for public profile', function() {
|
||||
var view = createSectionTwoView(false, true);
|
||||
_.each(view.options.viewList, function(fieldView) {
|
||||
spyOn(fieldView, 'render').and.callThrough();
|
||||
});
|
||||
view.render();
|
||||
_.each(view.options.viewList, function(fieldView) {
|
||||
expect(fieldView.render).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
var testPrivateProfile = function(ownProfile, msg_string) {
|
||||
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();
|
||||
};
|
||||
|
||||
it('no profile when profile is private for other people', function() {
|
||||
testPrivateProfile(false, 'This learner is currently sharing a limited profile');
|
||||
});
|
||||
|
||||
it('no profile when profile is private for the user herself', function() {
|
||||
testPrivateProfile(true, 'You are currently sharing a limited profile');
|
||||
});
|
||||
|
||||
var testProfilePrivatePartsDoNotRender = function(ownProfile) {
|
||||
var view = createSectionTwoView(ownProfile, false);
|
||||
_.each(view.options.viewList, function(fieldView) {
|
||||
spyOn(fieldView, 'render');
|
||||
});
|
||||
view.render();
|
||||
_.each(view.options.viewList, function(fieldView) {
|
||||
expect(fieldView.render).not.toHaveBeenCalled();
|
||||
});
|
||||
};
|
||||
|
||||
it('profile field parts are not rendered for private profile for owner', function() {
|
||||
testProfilePrivatePartsDoNotRender(true);
|
||||
});
|
||||
|
||||
it('profile field parts are not rendered for private profile for other people', function() {
|
||||
testProfilePrivatePartsDoNotRender(false);
|
||||
});
|
||||
|
||||
it('does not allow fields to be edited when visiting a profile for other people', function() {
|
||||
var view = createSectionTwoView(false, true);
|
||||
var bio = view.options.viewList[0];
|
||||
expect(bio.editable).toBe('never');
|
||||
});
|
||||
|
||||
it("allows fields to be edited when visiting one's own profile", function() {
|
||||
var view = createSectionTwoView(true, true);
|
||||
var bio = view.options.viewList[0];
|
||||
expect(bio.editable).toBe('toggle');
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,59 +0,0 @@
|
||||
define(['backbone', 'jquery', 'underscore', 'moment',
|
||||
'js/spec/student_account/helpers',
|
||||
'js/spec/student_profile/helpers',
|
||||
'js/student_profile/views/share_modal_view',
|
||||
'jquery.simulate'
|
||||
],
|
||||
function(Backbone, $, _, Moment, Helpers, LearnerProfileHelpers, ShareModalView) {
|
||||
'use strict';
|
||||
describe('edx.user.ShareModalView', function() {
|
||||
var keys = $.simulate.keyCode;
|
||||
|
||||
var view;
|
||||
|
||||
var createModalView = function() {
|
||||
var badge = LearnerProfileHelpers.makeBadge(1);
|
||||
var context = _.extend(badge, {
|
||||
'created': new Moment(badge.created),
|
||||
'ownProfile': true,
|
||||
'badgeMeta': {}
|
||||
});
|
||||
return new ShareModalView({
|
||||
model: new Backbone.Model(context),
|
||||
shareButton: $('<button/>')
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
view = createModalView();
|
||||
// Attach view to document, otherwise click won't work
|
||||
view.render();
|
||||
$('body').append(view.$el);
|
||||
view.$el.show();
|
||||
expect(view.$el.is(':visible')).toBe(true);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
view.$el.remove();
|
||||
});
|
||||
|
||||
it('modal view closes on escape', function() {
|
||||
spyOn(view, 'close');
|
||||
view.delegateEvents();
|
||||
expect(view.close).not.toHaveBeenCalled();
|
||||
$(view.$el).simulate('keydown', {keyCode: keys.ESCAPE});
|
||||
expect(view.close).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('modal view closes click on close', function() {
|
||||
spyOn(view, 'close');
|
||||
view.delegateEvents();
|
||||
var $closeButton = view.$el.find('button.close');
|
||||
expect($closeButton.length).toBe(1);
|
||||
expect(view.close).not.toHaveBeenCalled();
|
||||
$closeButton.trigger('click');
|
||||
expect(view.close).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,7 +0,0 @@
|
||||
(function(define) {
|
||||
'use strict';
|
||||
define(['backbone'], function(Backbone) {
|
||||
var BadgesModel = Backbone.Model.extend({});
|
||||
return BadgesModel;
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -1,31 +0,0 @@
|
||||
(function(define, undefined) {
|
||||
'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'],
|
||||
function(gettext, $, _, PaginatedView, BadgeView, BadgeListView, BadgeListTemplate) {
|
||||
var BadgeListContainer = PaginatedView.extend({
|
||||
type: 'badge',
|
||||
|
||||
itemViewClass: BadgeView,
|
||||
|
||||
listViewClass: BadgeListView,
|
||||
|
||||
viewTemplate: BadgeListTemplate,
|
||||
|
||||
isZeroIndexed: true,
|
||||
|
||||
paginationLabel: gettext('Accomplishments Pagination'),
|
||||
|
||||
initialize: function(options) {
|
||||
BadgeListContainer.__super__.initialize.call(this, options);
|
||||
this.listView.find_courses_url = options.find_courses_url;
|
||||
this.listView.badgeMeta = options.badgeMeta;
|
||||
this.listView.ownProfile = options.ownProfile;
|
||||
}
|
||||
});
|
||||
|
||||
return BadgeListContainer;
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -1,63 +0,0 @@
|
||||
(function(define, undefined) {
|
||||
'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'
|
||||
],
|
||||
function(gettext, $, _, HtmlUtils, ListView, BadgeView, badgePlaceholder) {
|
||||
var BadgeListView = ListView.extend({
|
||||
tagName: 'div',
|
||||
|
||||
template: HtmlUtils.template(badgePlaceholder),
|
||||
|
||||
renderCollection: function() {
|
||||
var self = this,
|
||||
$row;
|
||||
|
||||
this.$el.empty();
|
||||
|
||||
// Split into two columns.
|
||||
this.collection.each(function(badge, index) {
|
||||
if (index % 2 === 0) {
|
||||
$row = $('<div class="row">');
|
||||
this.$el.append($row);
|
||||
}
|
||||
var $item = new BadgeView({
|
||||
model: badge,
|
||||
badgeMeta: this.badgeMeta,
|
||||
ownProfile: this.ownProfile
|
||||
}).render().el;
|
||||
|
||||
if ($row) {
|
||||
$row.append($item);
|
||||
}
|
||||
|
||||
this.itemViews.push($item);
|
||||
}, this);
|
||||
// Placeholder must always be at the end, and may need a new row.
|
||||
if (!this.collection.hasNextPage()) {
|
||||
// find_courses_url set by BadgeListContainer during initialization.
|
||||
if (this.collection.length % 2 === 0) {
|
||||
$row = $('<div class="row">');
|
||||
this.$el.append($row);
|
||||
}
|
||||
|
||||
if ($row) {
|
||||
HtmlUtils.append(
|
||||
$row,
|
||||
this.template({find_courses_url: self.find_courses_url})
|
||||
);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
return BadgeListView;
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -1,42 +0,0 @@
|
||||
(function(define, undefined) {
|
||||
'use strict';
|
||||
define(['gettext', 'jquery', 'underscore', 'backbone', 'moment',
|
||||
'text!templates/student_profile/badge.underscore',
|
||||
'js/student_profile/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
|
||||
});
|
||||
},
|
||||
attributes: {
|
||||
'class': 'badge-display'
|
||||
},
|
||||
template: _.template(badgeTemplate),
|
||||
events: {
|
||||
'click .share-button': 'createModal'
|
||||
},
|
||||
createModal: function() {
|
||||
var modal = new ShareModalView({
|
||||
model: new Backbone.Model(this.context),
|
||||
shareButton: this.shareButton
|
||||
});
|
||||
modal.$el.hide();
|
||||
modal.render();
|
||||
$('body').append(modal.$el);
|
||||
modal.$el.fadeIn('short', 'swing', _.bind(modal.ready, modal));
|
||||
},
|
||||
render: function() {
|
||||
this.$el.html(this.template(this.context));
|
||||
this.shareButton = this.$el.find('.share-button');
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
return BadgeView;
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -1,199 +0,0 @@
|
||||
(function(define, undefined) {
|
||||
'use strict';
|
||||
define([
|
||||
'gettext',
|
||||
'jquery',
|
||||
'underscore',
|
||||
'backbone',
|
||||
'logger',
|
||||
'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',
|
||||
'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) {
|
||||
return function(options) {
|
||||
var learnerProfileElement = $('.wrapper-profile');
|
||||
|
||||
var accountSettingsModel = new AccountSettingsModel(
|
||||
_.extend(
|
||||
options.account_settings_data,
|
||||
{'default_public_account_fields': options.default_public_account_fields}
|
||||
),
|
||||
{parse: true}
|
||||
);
|
||||
var AccountPreferencesModelWithDefaults = AccountPreferencesModel.extend({
|
||||
defaults: {
|
||||
account_privacy: options.default_visibility
|
||||
}
|
||||
});
|
||||
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({
|
||||
model: accountPreferencesModel,
|
||||
required: true,
|
||||
editable: 'always',
|
||||
showMessages: false,
|
||||
title: interpolate_text(
|
||||
gettext('{platform_name} learners can see my:'), {platform_name: options.platform_name}
|
||||
),
|
||||
valueAttribute: 'account_privacy',
|
||||
options: [
|
||||
['private', gettext('Limited Profile')],
|
||||
['all_users', gettext('Full Profile')]
|
||||
],
|
||||
helpMessage: '',
|
||||
accountSettingsPageUrl: options.account_settings_page_url,
|
||||
persistChanges: true
|
||||
});
|
||||
|
||||
var 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']
|
||||
});
|
||||
|
||||
var usernameFieldView = new FieldsView.ReadonlyFieldView({
|
||||
model: accountSettingsModel,
|
||||
screenReaderTitle: gettext('Username'),
|
||||
valueAttribute: 'username',
|
||||
helpMessage: ''
|
||||
});
|
||||
|
||||
var sectionOneFieldViews = [
|
||||
new FieldsView.DropdownFieldView({
|
||||
model: accountSettingsModel,
|
||||
screenReaderTitle: gettext('Country'),
|
||||
titleVisible: false,
|
||||
required: true,
|
||||
editable: editable,
|
||||
showMessages: false,
|
||||
iconName: 'fa-map-marker',
|
||||
placeholderValue: gettext('Add Country'),
|
||||
valueAttribute: 'country',
|
||||
options: options.country_options,
|
||||
helpMessage: '',
|
||||
persistChanges: true
|
||||
}),
|
||||
new AccountSettingsFieldViews.LanguageProficienciesFieldView({
|
||||
model: accountSettingsModel,
|
||||
screenReaderTitle: gettext('Preferred Language'),
|
||||
titleVisible: false,
|
||||
required: false,
|
||||
editable: editable,
|
||||
showMessages: false,
|
||||
iconName: 'fa-comment',
|
||||
placeholderValue: gettext('Add language'),
|
||||
valueAttribute: 'language_proficiencies',
|
||||
options: options.language_options,
|
||||
helpMessage: '',
|
||||
persistChanges: true
|
||||
})
|
||||
];
|
||||
|
||||
var sectionTwoFieldViews = [
|
||||
new FieldsView.TextareaFieldView({
|
||||
model: accountSettingsModel,
|
||||
editable: editable,
|
||||
showMessages: false,
|
||||
title: gettext('About me'),
|
||||
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: '',
|
||||
persistChanges: true,
|
||||
messagePosition: 'header'
|
||||
})
|
||||
];
|
||||
|
||||
var BadgeCollection = PagingCollection.extend({
|
||||
queryParams: {
|
||||
currentPage: 'current_page'
|
||||
}
|
||||
});
|
||||
var 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
|
||||
}
|
||||
});
|
||||
|
||||
var learnerProfileView = new LearnerProfileView({
|
||||
el: learnerProfileElement,
|
||||
ownProfile: options.own_profile,
|
||||
has_preferences_access: options.has_preferences_access,
|
||||
accountSettingsModel: accountSettingsModel,
|
||||
preferencesModel: accountPreferencesModel,
|
||||
accountPrivacyFieldView: accountPrivacyFieldView,
|
||||
profileImageFieldView: profileImageFieldView,
|
||||
usernameFieldView: usernameFieldView,
|
||||
sectionOneFieldViews: sectionOneFieldViews,
|
||||
sectionTwoFieldViews: sectionTwoFieldViews,
|
||||
badgeListContainer: badgeListContainer
|
||||
});
|
||||
|
||||
var getProfileVisibility = function() {
|
||||
if (options.has_preferences_access) {
|
||||
return accountPreferencesModel.get('account_privacy');
|
||||
} else {
|
||||
return accountSettingsModel.get('profile_is_public') ? 'all_users' : 'private';
|
||||
}
|
||||
};
|
||||
|
||||
var showLearnerProfileView = function() {
|
||||
// Record that the profile page was viewed
|
||||
Logger.log('edx.user.settings.viewed', {
|
||||
page: 'profile',
|
||||
visibility: getProfileVisibility(),
|
||||
user_id: options.profile_user_id
|
||||
});
|
||||
|
||||
// Render the view for the first time
|
||||
learnerProfileView.render();
|
||||
};
|
||||
|
||||
if (options.has_preferences_access) {
|
||||
if (accountSettingsModel.get('requires_parental_consent')) {
|
||||
accountPreferencesModel.set('account_privacy', 'private');
|
||||
}
|
||||
}
|
||||
showLearnerProfileView();
|
||||
|
||||
return {
|
||||
accountSettingsModel: accountSettingsModel,
|
||||
accountPreferencesModel: accountPreferencesModel,
|
||||
learnerProfileView: learnerProfileView,
|
||||
badgeListContainer: badgeListContainer
|
||||
};
|
||||
};
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -1,124 +0,0 @@
|
||||
(function(define, undefined) {
|
||||
'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'
|
||||
], function(gettext, $, _, Backbone, StringUtils, HtmlUtils, FieldViews, ImageFieldView) {
|
||||
var LearnerProfileFieldViews = {};
|
||||
|
||||
LearnerProfileFieldViews.AccountPrivacyFieldView = FieldViews.DropdownFieldView.extend({
|
||||
|
||||
render: function() {
|
||||
this._super();
|
||||
this.showNotificationMessage();
|
||||
this.updateFieldValue();
|
||||
return this;
|
||||
},
|
||||
|
||||
showNotificationMessage: function() {
|
||||
var accountSettingsLink = HtmlUtils.joinHtml(
|
||||
HtmlUtils.interpolateHtml(
|
||||
HtmlUtils.HTML('<a href="{settings_url}">'), {settings_url: this.options.accountSettingsPageUrl}
|
||||
),
|
||||
gettext('Account Settings page.'),
|
||||
HtmlUtils.HTML('</a>')
|
||||
);
|
||||
if (this.profileIsPrivate) {
|
||||
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}
|
||||
)
|
||||
);
|
||||
} 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}
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
this._super('');
|
||||
}
|
||||
},
|
||||
|
||||
updateFieldValue: function() {
|
||||
if (!this.isAboveMinimumAge) {
|
||||
this.$('.u-field-value select').val('private');
|
||||
this.disableField(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
LearnerProfileFieldViews.ProfileImageFieldView = ImageFieldView.extend({
|
||||
|
||||
screenReaderTitle: gettext('Profile Image'),
|
||||
|
||||
imageUrl: function() {
|
||||
return this.model.profileImageUrl();
|
||||
},
|
||||
|
||||
imageAltText: function() {
|
||||
return interpolate_text(
|
||||
gettext('Profile image for {username}'), {username: this.model.get('username')}
|
||||
);
|
||||
},
|
||||
|
||||
imageChangeSucceeded: function(e, data) {
|
||||
var view = this;
|
||||
// Update model to get the latest urls of profile image.
|
||||
this.model.fetch().done(function() {
|
||||
view.setCurrentStatus('');
|
||||
view.render();
|
||||
view.$('.u-field-upload-button').focus();
|
||||
}).fail(function() {
|
||||
view.setCurrentStatus('');
|
||||
view.showErrorMessage(view.errorMessage);
|
||||
});
|
||||
},
|
||||
|
||||
imageChangeFailed: function(e, data) {
|
||||
this.setCurrentStatus('');
|
||||
this.showImageChangeFailedMessage(data.jqXHR.status, data.jqXHR.responseText);
|
||||
},
|
||||
|
||||
showImageChangeFailedMessage: function(status, responseText) {
|
||||
if (_.contains([400, 404], status)) {
|
||||
try {
|
||||
var errors = JSON.parse(responseText);
|
||||
this.showErrorMessage(errors.user_message);
|
||||
} catch (error) {
|
||||
this.showErrorMessage(this.errorMessage);
|
||||
}
|
||||
} else {
|
||||
this.showErrorMessage(this.errorMessage);
|
||||
}
|
||||
},
|
||||
|
||||
showErrorMessage: function(message) {
|
||||
this.options.messageView.showMessage(message);
|
||||
},
|
||||
|
||||
isEditingAllowed: function() {
|
||||
return this.model.isAboveMinimumAge();
|
||||
},
|
||||
|
||||
isShowingPlaceholder: function() {
|
||||
return !this.model.hasProfileImage();
|
||||
},
|
||||
|
||||
clickedRemoveButton: function(e, data) {
|
||||
this.options.messageView.hideMessage();
|
||||
this._super(e, data);
|
||||
},
|
||||
|
||||
fileSelected: function(e, data) {
|
||||
this.options.messageView.hideMessage();
|
||||
this._super(e, data);
|
||||
}
|
||||
});
|
||||
|
||||
return LearnerProfileFieldViews;
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -1,135 +0,0 @@
|
||||
(function(define, undefined) {
|
||||
'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'],
|
||||
function(gettext, $, _, Backbone, HtmlUtils, TabbedView, SectionTwoTab, learnerProfileTemplate) {
|
||||
var LearnerProfileView = Backbone.View.extend({
|
||||
|
||||
initialize: function(options) {
|
||||
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({
|
||||
routes: {':about_me': 'loadTab', ':accomplishments': 'loadTab'}
|
||||
});
|
||||
|
||||
this.router = new Router();
|
||||
this.firstRender = true;
|
||||
},
|
||||
|
||||
template: _.template(learnerProfileTemplate),
|
||||
|
||||
showFullProfile: function() {
|
||||
var isAboveMinimumAge = this.options.accountSettingsModel.isAboveMinimumAge();
|
||||
if (this.options.ownProfile) {
|
||||
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.
|
||||
if (this.tabbedView.getTabMeta(tab).tab) {
|
||||
this.tabbedView.setActiveTab(tab);
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var tabs,
|
||||
self = this;
|
||||
|
||||
this.sectionTwoView = new SectionTwoTab({
|
||||
viewList: this.options.sectionTwoFieldViews,
|
||||
showFullProfile: this.showFullProfile,
|
||||
ownProfile: this.options.ownProfile
|
||||
});
|
||||
|
||||
HtmlUtils.setHtml(this.$el, HtmlUtils.template(learnerProfileTemplate)({
|
||||
username: self.options.accountSettingsModel.get('username'),
|
||||
ownProfile: self.options.ownProfile,
|
||||
showFullProfile: self.showFullProfile()
|
||||
}));
|
||||
this.renderFields();
|
||||
|
||||
if (this.showFullProfile() && (this.options.accountSettingsModel.get('accomplishments_shared'))) {
|
||||
tabs = [
|
||||
{view: this.sectionTwoView, title: gettext('About Me'), url: 'about_me'},
|
||||
{
|
||||
view: this.options.badgeListContainer,
|
||||
title: gettext('Accomplishments'),
|
||||
url: 'accomplishments'
|
||||
}
|
||||
];
|
||||
|
||||
// Build the accomplishments Tab and fill with data
|
||||
this.options.badgeListContainer.collection.fetch().done(function() {
|
||||
self.options.badgeListContainer.render();
|
||||
}).error(function() {
|
||||
self.options.badgeListContainer.renderError();
|
||||
});
|
||||
|
||||
this.tabbedView = new TabbedView({
|
||||
tabs: tabs,
|
||||
router: this.router,
|
||||
viewLabel: gettext('Profile')
|
||||
});
|
||||
|
||||
this.tabbedView.render();
|
||||
this.$el.find('.account-settings-container').append(this.tabbedView.el);
|
||||
|
||||
if (this.firstRender) {
|
||||
this.router.on('route:loadTab', _.bind(this.setActiveTab, this));
|
||||
Backbone.history.start();
|
||||
this.firstRender = false;
|
||||
// Load from history.
|
||||
this.router.navigate((Backbone.history.getFragment() || 'about_me'), {trigger: true});
|
||||
} else {
|
||||
// Restart the router so the tab will be brought up anew.
|
||||
Backbone.history.stop();
|
||||
Backbone.history.start();
|
||||
}
|
||||
} else {
|
||||
this.$el.find('.account-settings-container').append(this.sectionTwoView.render().el);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
renderFields: function() {
|
||||
var view = this;
|
||||
|
||||
if (this.options.ownProfile) {
|
||||
var 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();
|
||||
fieldView.undelegateEvents();
|
||||
this.$('.wrapper-profile-field-account-privacy').append(fieldView.render().el);
|
||||
fieldView.delegateEvents();
|
||||
}
|
||||
|
||||
this.$('.profile-section-one-fields').append(this.options.usernameFieldView.render().el);
|
||||
|
||||
var 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);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
showLoadingError: function() {
|
||||
this.$('.ui-loading-indicator').addClass('is-hidden');
|
||||
this.$('.ui-loading-error').removeClass('is-hidden');
|
||||
}
|
||||
});
|
||||
|
||||
return LearnerProfileView;
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -1,32 +0,0 @@
|
||||
(function(define, undefined) {
|
||||
'use strict';
|
||||
define([
|
||||
'gettext', 'jquery', 'underscore', 'backbone', 'text!templates/student_profile/section_two.underscore'],
|
||||
function(gettext, $, _, Backbone, sectionTwoTemplate) {
|
||||
var SectionTwoTab = Backbone.View.extend({
|
||||
attributes: {
|
||||
'class': 'wrapper-profile-section-two'
|
||||
},
|
||||
template: _.template(sectionTwoTemplate),
|
||||
initialize: function(options) {
|
||||
this.options = _.extend({}, options);
|
||||
},
|
||||
render: function() {
|
||||
var self = this;
|
||||
var showFullProfile = this.options.showFullProfile();
|
||||
this.$el.html(this.template({
|
||||
ownProfile: self.options.ownProfile,
|
||||
showFullProfile: showFullProfile
|
||||
}));
|
||||
if (showFullProfile) {
|
||||
_.each(this.options.viewList, function(fieldView) {
|
||||
self.$el.find('.field-container').append(fieldView.render().el);
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
return SectionTwoTab;
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -1,51 +0,0 @@
|
||||
(function(define, undefined) {
|
||||
'use strict';
|
||||
define(['gettext', 'jquery', 'underscore', 'backbone', 'moment',
|
||||
'text!templates/student_profile/share_modal.underscore'],
|
||||
function(gettext, $, _, Backbone, Moment, badgeModalTemplate) {
|
||||
var ShareModalView = Backbone.View.extend({
|
||||
attributes: {
|
||||
'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',
|
||||
'focus .focusguard-start': 'focusGuardStart',
|
||||
'focus .focusguard-end': 'focusGuardEnd'
|
||||
},
|
||||
initialize: function(options) {
|
||||
this.options = _.extend({}, options);
|
||||
},
|
||||
focusGuardStart: function() {
|
||||
// Should only be selected directly if shift-tabbing from the start, so grab last item.
|
||||
this.$el.find('a').last().focus();
|
||||
},
|
||||
focusGuardEnd: function() {
|
||||
this.$el.find('.badges-modal').focus();
|
||||
},
|
||||
close: function() {
|
||||
this.$el.fadeOut('short', 'swing', _.bind(this.remove, this));
|
||||
this.options.shareButton.focus();
|
||||
},
|
||||
keyAction: function(event) {
|
||||
if (event.keyCode === $.ui.keyCode.ESCAPE) {
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
ready: function() {
|
||||
// Focusing on the modal background directly doesn't work, probably due
|
||||
// to its positioning.
|
||||
this.$el.find('.badges-modal').focus();
|
||||
},
|
||||
render: function() {
|
||||
this.$el.html(this.template(this.model.toJSON()));
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
return ShareModalView;
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
Reference in New Issue
Block a user