Refactor learner profile into openedx/features directory

LEARNER-1855
This commit is contained in:
Andy Armstrong
2017-07-17 17:04:55 -04:00
parent 583719cce1
commit 33bee3f70b
39 changed files with 411 additions and 311 deletions

View File

@@ -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>

View File

@@ -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);
});
});
}
);

View File

@@ -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);
});
});
}
);

View File

@@ -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);
});
});
}
);

View File

@@ -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
};
});

View File

@@ -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);
});
});
});

View File

@@ -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);
});
});
});
});

View File

@@ -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);
});
});
});

View File

@@ -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');
});
});
}
);

View File

@@ -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();
});
});
}
);

View File

@@ -1,7 +0,0 @@
(function(define) {
'use strict';
define(['backbone'], function(Backbone) {
var BadgesModel = Backbone.Model.extend({});
return BadgesModel;
});
}).call(this, define || RequireJS.define);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);