Commit 3a9dc5de by Mushtaq Ali Committed by GitHub

Merge pull request #14625 from edx/mushtaq/bug-bash-fixes

Move Bug fixes
parents de29ef94 e83bee65
......@@ -739,6 +739,8 @@ def _move_item(source_usage_key, target_parent_usage_key, user, target_index=Non
error = 'You can not move an item into itself.'
elif is_source_item_in_target_parents(source_item, target_parent):
error = 'You can not move an item into it\'s child.'
elif target_parent_type == 'split_test':
error = 'You can not move an item directly into content experiment.'
elif source_index is None:
error = '{source_usage_key} not found in {parent_usage_key}.'.format(
source_usage_key=unicode(source_usage_key),
......@@ -1119,7 +1121,8 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
xblock_info = {
'id': unicode(xblock.location),
'display_name': xblock.display_name_with_default,
'category': xblock.category
'category': xblock.category,
'has_children': xblock.has_children
}
if is_concise:
if child_info and len(child_info.get('children', [])) > 0:
......@@ -1127,7 +1130,6 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
# Groups are labelled with their internal ids, rather than with the group name. Replace id with display name.
group_display_name = get_group_display_name(user_partitions, xblock_info['display_name'])
xblock_info['display_name'] = group_display_name if group_display_name else xblock_info['display_name']
xblock_info['has_children'] = xblock.has_children
else:
xblock_info.update({
'edited_on': get_default_time_display(xblock.subtree_edited_on) if xblock.subtree_edited_on else None,
......
......@@ -948,17 +948,17 @@ class TestMoveItem(ItemTest):
"""
Test invalid move.
"""
parent_loc = self.store.get_parent_location(self.chapter_usage_key)
response = self._move_component(self.chapter_usage_key, self.usage_key)
parent_loc = self.store.get_parent_location(self.html_usage_key)
response = self._move_component(self.html_usage_key, self.seq_usage_key)
self.assertEqual(response.status_code, 400)
response = json.loads(response.content)
expected_error = 'You can not move {source_type} into {target_type}.'.format(
source_type=self.chapter_usage_key.block_type,
target_type=self.usage_key.block_type
source_type=self.html_usage_key.block_type,
target_type=self.seq_usage_key.block_type
)
self.assertEqual(expected_error, response['error'])
new_parent_loc = self.store.get_parent_location(self.chapter_usage_key)
new_parent_loc = self.store.get_parent_location(self.html_usage_key)
self.assertEqual(new_parent_loc, parent_loc)
def test_move_current_parent(self):
......@@ -1039,11 +1039,23 @@ class TestMoveItem(ItemTest):
def test_move_into_content_experiment_groups(self):
"""
Test that a component can be moved to content experiment.
Test that a component can be moved to content experiment groups.
"""
split_test = self.setup_and_verify_content_experiment(0)
self.assert_move_item(self.html_usage_key, split_test.children[0])
def test_can_not_move_into_content_experiment_level(self):
"""
Test that a component can not be moved directly to content experiment level.
"""
self.setup_and_verify_content_experiment(0)
response = self._move_component(self.html_usage_key, self.split_test_usage_key)
self.assertEqual(response.status_code, 400)
response = json.loads(response.content)
self.assertEqual(response['error'], 'You can not move an item directly into content experiment.')
self.assertEqual(self.store.get_parent_location(self.html_usage_key), self.vert_usage_key)
def test_can_not_move_content_experiment_into_its_children(self):
"""
Test that a content experiment can not be moved inside any of it's children.
......
......@@ -50,6 +50,10 @@ function(Backbone, _, str, ModuleUtils) {
*/
'published_by': null,
/**
* True if the xblock is a parentable xblock.
*/
has_children: null,
/**
* True if the xblock has changes.
* Note: this is not always provided as a performance optimization. It is only provided for
* verticals functioning as units.
......
define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'js/spec_helpers/edit_helpers',
'common/js/spec_helpers/template_helpers', 'common/js/spec_helpers/view_helpers',
'js/views/modals/move_xblock_modal', 'edx-ui-toolkit/js/utils/html-utils',
'js/views/modals/move_xblock_modal', 'js/views/pages/container', 'edx-ui-toolkit/js/utils/html-utils',
'edx-ui-toolkit/js/utils/string-utils', 'js/models/xblock_info'],
function($, _, AjaxHelpers, TemplateHelpers, ViewHelpers, MoveXBlockModal, HtmlUtils, StringUtils, XBlockInfo) {
function($, _, AjaxHelpers, EditHelpers, TemplateHelpers, ViewHelpers, MoveXBlockModal, ContainerPage, HtmlUtils,
StringUtils, XBlockInfo) {
'use strict';
describe('MoveXBlock', function() {
var modal, showModal, renderViews, createXBlockInfo, createCourseOutline, courseOutlineOptions,
parentChildMap, categoryMap, createChildXBlockInfo, xblockAncestorInfo, courseOutline,
verifyBreadcrumbViewInfo, verifyListViewInfo, getDisplayedInfo, clickForwardButton,
clickBreadcrumbButton, verifyXBlockInfo, nextCategory, verifyMoveEnabled, getSentRequests,
verifyNotificationStatus, sendMoveXBlockRequest, moveXBlockWithSuccess,
verifyConfirmationFeedbackTitleHtml, verifyConfirmationFeedbackRedirectLinkHtml,
verifyUndoConfirmationFeedbackTitleHtml, verifyConfirmationFeedbackUndoMoveActionHtml,
verifyNotificationStatus, sendMoveXBlockRequest, moveXBlockWithSuccess, getMovedAlertNotification,
verifyConfirmationFeedbackTitleText, verifyConfirmationFeedbackRedirectLinkText,
verifyUndoConfirmationFeedbackTitleText, verifyConfirmationFeedbackUndoMoveActionText,
sourceParentXBlockInfo, mockContainerPage, createContainerPage, containerPage,
sourceDisplayName = 'component_display_name_0',
sourceLocator = 'component_ID_0',
sourceParentLocator = 'unit_ID_0';
......@@ -62,13 +64,31 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
]
};
sourceParentXBlockInfo = new XBlockInfo({
id: sourceParentLocator,
display_name: 'unit_display_name_0',
category: 'vertical'
});
createContainerPage = function() {
containerPage = new ContainerPage({
model: sourceParentXBlockInfo,
templates: EditHelpers.mockComponentTemplates,
el: $('#content'),
isUnitPage: true
});
};
beforeEach(function() {
setFixtures("<div id='page-alert'></div>");
mockContainerPage = readFixtures('mock/mock-container-page.underscore');
TemplateHelpers.installTemplates([
'basic-modal',
'modal-button',
'move-xblock-modal'
]);
appendSetFixtures(mockContainerPage);
createContainerPage();
courseOutline = createCourseOutline(courseOutlineOptions);
showModal();
});
......@@ -76,6 +96,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
afterEach(function() {
modal.hide();
courseOutline = null;
containerPage.remove();
});
showModal = function() {
......@@ -85,11 +106,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
display_name: sourceDisplayName,
category: 'component'
}),
sourceParentXBlockInfo: new XBlockInfo({
id: sourceParentLocator,
display_name: 'unit_display_name_0',
category: 'vertical'
}),
sourceParentXBlockInfo: sourceParentXBlockInfo,
XBlockUrlRoot: '/xblock'
});
modal.show();
......@@ -339,6 +356,13 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
};
/**
* Get move alert confirmation message HTML
*/
getMovedAlertNotification = function() {
return $('#page-alert');
};
/**
* Send move xblock request.
*
* @param {Object} requests requests object
......@@ -384,19 +408,22 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
});
modal.$el.find('.modal-actions .action-move').click();
sendMoveXBlockRequest(requests, sourceLocator);
expect(modal.movedAlertView).toBeDefined();
verifyConfirmationFeedbackTitleHtml(sourceDisplayName);
verifyConfirmationFeedbackRedirectLinkHtml();
verifyConfirmationFeedbackUndoMoveActionHtml();
AjaxHelpers.expectJsonRequest(requests, 'GET', '/xblock/' + sourceParentLocator);
AjaxHelpers.respondWithJson(requests, sourceParentXBlockInfo);
expect(getMovedAlertNotification().html().length).not.toEqual(0);
verifyConfirmationFeedbackTitleText(sourceDisplayName);
verifyConfirmationFeedbackRedirectLinkText();
verifyConfirmationFeedbackUndoMoveActionText();
};
/**
* Verify success banner message html has correct title html.
* Verify success banner message html has correct title text.
*
* @param {String} displayName XBlock display name
*/
verifyConfirmationFeedbackTitleHtml = function(displayName) {
expect(modal.movedAlertView.$el.find('.title').html().trim())
verifyConfirmationFeedbackTitleText = function(displayName) {
expect(getMovedAlertNotification().find('.title').html()
.trim())
.toEqual(StringUtils.interpolate('Success! "{displayName}" has been moved.',
{
displayName: displayName
......@@ -405,12 +432,12 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
};
/**
* Verify undo success banner message html has correct title html.
* Verify undo success banner message html has correct title text.
*
* @param {String} displayName XBlock display name
*/
verifyUndoConfirmationFeedbackTitleHtml = function(displayName) {
expect(modal.movedAlertView.$el.find('.title').html()).toEqual(
verifyUndoConfirmationFeedbackTitleText = function(displayName) {
expect(getMovedAlertNotification().find('.title').html()).toEqual(
StringUtils.interpolate(
'Move cancelled. "{sourceDisplayName}" has been moved back to its original location.',
{
......@@ -421,25 +448,18 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
};
/**
* Verify success banner message html has correct redirect link html.
* Verify success banner message html has correct redirect link text.
*/
verifyConfirmationFeedbackRedirectLinkHtml = function() {
expect(modal.movedAlertView.$el.find('.copy').html().indexOf(
HtmlUtils.HTML(
'<button class="action-secondary action-cancel">Take me to the new location</button>'
) !== -1
)).toBeTruthy();
verifyConfirmationFeedbackRedirectLinkText = function() {
expect(getMovedAlertNotification().find('.nav-actions .action-secondary').html())
.toEqual('Take me to the new location');
};
/**
* Verify success banner message html has correct undo move button html.
* Verify success banner message html has correct undo move text.
*/
verifyConfirmationFeedbackUndoMoveActionHtml = function() {
expect(modal.movedAlertView.$el.find('.copy').html().indexOf(
HtmlUtils.HTML(
'<button class="action-primary action-save">Undo Move</button>'
) !== -1
)).toBeTruthy();
verifyConfirmationFeedbackUndoMoveActionText = function() {
expect(getMovedAlertNotification().find('.nav-actions .action-primary').html()).toEqual('Undo move');
};
/**
......@@ -633,6 +653,24 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
expect(modal.$el.find('.modal-actions .action-move').hasClass('is-disabled')).toBeFalsy();
});
it('is enabled when moving a component inside a parentable component', function() {
// create a source parent with has_childern set true
modal.sourceParentXBlockInfo = new XBlockInfo({
category: 'conditional',
display_name: 'Parentable Component',
has_children: true,
id: 'PARENTABLE_ID'
});
// navigate and verify move button is enabled
renderViews(courseOutline);
_.each(_.range(3), function() {
clickForwardButton(0);
});
// move is enabled when moving a component.
expect(modal.$el.find('.modal-actions .action-move').hasClass('is-disabled')).toBeFalsy();
});
it('is disabled when navigating to any non-parentable component', function() {
var nonParentableXBlockInfo = {
category: 'html',
......@@ -651,7 +689,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
it('can not move in a disabled state', function() {
verifyMoveEnabled(false);
modal.$el.find('.modal-actions .action-move').click();
expect(modal.movedAlertView).toBeNull();
expect(getMovedAlertNotification().html().length).toEqual(0);
expect(getSentRequests().length).toEqual(0);
});
......@@ -662,7 +700,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
it('do not move an xblock when cancel button is clicked', function() {
modal.$el.find('.modal-actions .action-cancel').click();
expect(modal.movedAlertView).toBeNull();
expect(getMovedAlertNotification().html().length).toEqual(0);
expect(getSentRequests().length).toEqual(0);
});
......@@ -670,13 +708,13 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
var sourceIndex = 0,
requests = AjaxHelpers.requests(this);
moveXBlockWithSuccess(requests);
modal.movedAlertView.$el.find('.action-save').click();
getMovedAlertNotification().find('.action-save').click();
AjaxHelpers.respondWithJson(requests, {
move_source_locator: sourceLocator,
parent_locator: sourceParentLocator,
target_index: sourceIndex
});
verifyUndoConfirmationFeedbackTitleHtml(sourceDisplayName);
verifyUndoConfirmationFeedbackTitleText(sourceDisplayName);
});
});
......@@ -698,7 +736,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
requests = AjaxHelpers.requests(this);
moveXBlockWithSuccess(requests);
notificationSpy = ViewHelpers.createNotificationSpy();
modal.movedAlertView.$el.find('.action-save').click();
getMovedAlertNotification().find('.action-save').click();
verifyNotificationStatus(requests, notificationSpy, 'Undo moving');
});
......@@ -719,7 +757,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
var requests = AjaxHelpers.requests(this),
notificationSpy = ViewHelpers.createNotificationSpy('Error');
moveXBlockWithSuccess(requests);
modal.movedAlertView.$el.find('.action-save').click();
getMovedAlertNotification().find('.action-save').click();
AjaxHelpers.respondWithError(requests);
ViewHelpers.verifyNotificationShowing(notificationSpy, "Studio's having trouble saving your work");
});
......
......@@ -49,6 +49,9 @@ define(['jquery', 'underscore', 'underscore.string', 'edx-ui-toolkit/js/utils/sp
afterEach(function() {
EditHelpers.uninstallMockXBlock();
if (containerPage !== undefined) {
containerPage.remove();
}
});
respondWithHtml = function(html) {
......
......@@ -35,6 +35,9 @@ define(['jquery', 'underscore', 'underscore.string', 'edx-ui-toolkit/js/utils/sp
afterEach(function() {
delete window.course;
if (containerPage !== undefined) {
containerPage.remove();
}
});
defaultXBlockInfo = {
......
......@@ -41,7 +41,6 @@ function($, Backbone, _, gettext, BaseView, XBlockViewUtils, MoveXBlockUtils, Ht
initialize: function() {
var self = this;
BaseModal.prototype.initialize.call(this);
this.listenTo(Backbone, 'move:breadcrumbRendered', this.focusModal);
this.sourceXBlockInfo = this.options.sourceXBlockInfo;
this.sourceParentXBlockInfo = this.options.sourceParentXBlockInfo;
this.targetParentXBlockInfo = null;
......@@ -57,9 +56,9 @@ function($, Backbone, _, gettext, BaseView, XBlockViewUtils, MoveXBlockUtils, Ht
$('.breadcrumb-container').removeClass('is-hidden');
self.renderViews(courseOutlineInfo, ancestorInfo);
});
this.movedAlertView = null;
this.isValidMove = false;
this.listenTo(Backbone, 'move:breadcrumbRendered', this.focusModal);
this.listenTo(Backbone, 'move:enableMoveOperation', this.enableMoveOperation);
this.listenTo(Backbone, 'move:hideMoveModal', this.hide);
},
getTitle: function() {
......@@ -137,15 +136,22 @@ function($, Backbone, _, gettext, BaseView, XBlockViewUtils, MoveXBlockUtils, Ht
}
},
isValidCategory: function(sourceParentType, targetParentType, targetHasChildren) {
var basicBlockTypes = ['course', 'chapter', 'sequential', 'vertical'];
isValidCategory: function(targetParentXBlockInfo) {
var basicBlockTypes = ['course', 'chapter', 'sequential', 'vertical'],
sourceParentType = this.sourceParentXBlockInfo.get('category'),
targetParentType = targetParentXBlockInfo.get('category'),
sourceParentHasChildren = this.sourceParentXBlockInfo.get('has_children'),
targetParentHasChildren = targetParentXBlockInfo.get('has_children');
// Treat source parent component as vertical to support move child components under content experiment
// and other similar xblocks.
// eslint-disable-next-line no-param-reassign
sourceParentType = sourceParentType === 'split_test' ? 'vertical' : sourceParentType;
if (sourceParentHasChildren && !_.contains(basicBlockTypes, sourceParentType)) {
sourceParentType = 'vertical'; // eslint-disable-line no-param-reassign
}
// Treat target parent component as a vertical to support move to parentable target parent components.
// Also, moving a component directly to content experiment is not allowed, we need to visit to group level.
if (targetHasChildren && !_.contains(basicBlockTypes, targetParentType) &&
if (targetParentHasChildren && !_.contains(basicBlockTypes, targetParentType) &&
targetParentType !== 'split_test') {
targetParentType = 'vertical'; // eslint-disable-line no-param-reassign
}
......@@ -153,44 +159,28 @@ function($, Backbone, _, gettext, BaseView, XBlockViewUtils, MoveXBlockUtils, Ht
},
enableMoveOperation: function(targetParentXBlockInfo) {
var isValidMove = false,
sourceParentType = this.sourceParentXBlockInfo.get('category'),
targetParentType = targetParentXBlockInfo.get('category'),
targetHasChildren = targetParentXBlockInfo.get('has_children');
var isValidMove = false;
if (this.isValidCategory(sourceParentType, targetParentType, targetHasChildren) &&
// update target parent on navigation
this.targetParentXBlockInfo = targetParentXBlockInfo;
if (this.isValidCategory(targetParentXBlockInfo) &&
this.sourceParentXBlockInfo.id !== targetParentXBlockInfo.id && // same parent case
this.sourceXBlockInfo.id !== targetParentXBlockInfo.id) { // same source item case
isValidMove = true;
this.targetParentXBlockInfo = targetParentXBlockInfo;
}
this.updateMoveState(isValidMove);
},
moveXBlock: function() {
var self = this;
XBlockViewUtils.moveXBlock(self.sourceXBlockInfo.id, self.targetParentXBlockInfo.id)
.done(function(response) {
// hide modal
self.hide();
// hide xblock element
$("li.studio-xblock-wrapper[data-locator='" + self.sourceXBlockInfo.id + "']").hide();
self.movedAlertView = MoveXBlockUtils.showMovedNotification(
StringUtils.interpolate(
gettext('Success! "{displayName}" has been moved.'),
MoveXBlockUtils.moveXBlock(
{
displayName: self.sourceXBlockInfo.get('display_name')
}
),
{
sourceDisplayName: self.sourceXBlockInfo.get('display_name'),
sourceLocator: self.sourceXBlockInfo.id,
sourceParentLocator: self.sourceParentXBlockInfo.id,
targetParentLocator: response.parent_locator,
targetIndex: response.source_index
sourceXBlockElement: $("li.studio-xblock-wrapper[data-locator='" + this.sourceXBlockInfo.id + "']"),
sourceDisplayName: this.sourceXBlockInfo.get('display_name'),
sourceLocator: this.sourceXBlockInfo.id,
sourceParentLocator: this.sourceParentXBlockInfo.id,
targetParentLocator: this.targetParentXBlockInfo.id
}
);
});
}
});
......
......@@ -2,11 +2,12 @@
* XBlockContainerPage is used to display Studio's container page for an xblock which has children.
* This page allows the user to understand and manipulate the xblock and its children.
*/
define(['jquery', 'underscore', 'gettext', 'js/views/pages/base_page', 'common/js/components/utils/view_utils',
'js/views/container', 'js/views/xblock', 'js/views/components/add_xblock', 'js/views/modals/edit_xblock',
'js/views/modals/move_xblock_modal', 'js/models/xblock_info', 'js/views/xblock_string_field_editor',
'js/views/pages/container_subviews', 'js/views/unit_outline', 'js/views/utils/xblock_utils'],
function($, _, gettext, BasePage, ViewUtils, ContainerView, XBlockView, AddXBlockComponent,
define(['jquery', 'underscore', 'backbone', 'gettext', 'js/views/pages/base_page',
'common/js/components/utils/view_utils', 'js/views/container', 'js/views/xblock',
'js/views/components/add_xblock', 'js/views/modals/edit_xblock', 'js/views/modals/move_xblock_modal',
'js/models/xblock_info', 'js/views/xblock_string_field_editor', 'js/views/pages/container_subviews',
'js/views/unit_outline', 'js/views/utils/xblock_utils'],
function($, _, Backbone, gettext, BasePage, ViewUtils, ContainerView, XBlockView, AddXBlockComponent,
EditXBlockModal, MoveXBlockModal, XBlockInfo, XBlockStringFieldEditor, ContainerSubviews,
UnitOutlineView, XBlockUtils) {
'use strict';
......@@ -81,6 +82,8 @@ define(['jquery', 'underscore', 'gettext', 'js/views/pages/base_page', 'common/j
});
this.unitOutlineView.render();
}
this.listenTo(Backbone, 'move:onXBlockMoved', this.onXBlockMoved);
},
getViewParameters: function() {
......@@ -283,6 +286,13 @@ define(['jquery', 'underscore', 'gettext', 'js/views/pages/base_page', 'common/j
this.model.fetch();
},
/*
After move operation is complete, updates the xblock information from server .
*/
onXBlockMoved: function() {
this.model.fetch();
},
onNewXBlock: function(xblockElement, scrollOffset, is_duplicate, data) {
ViewUtils.setScrollOffset(xblockElement, scrollOffset);
xblockElement.data('locator', data.locator);
......
......@@ -4,25 +4,53 @@
define([
'jquery',
'underscore',
'backbone',
'common/js/components/views/feedback',
'common/js/components/views/feedback_alert',
'js/views/utils/xblock_utils',
'js/views/utils/move_xblock_utils',
'edx-ui-toolkit/js/utils/string-utils'
],
function($, _, Feedback, AlertView, XBlockViewUtils, MoveXBlockUtils, StringUtils) {
function($, _, Backbone, Feedback, AlertView, XBlockViewUtils, MoveXBlockUtils, StringUtils) {
'use strict';
var redirectLink, undoMoveXBlock, showMovedNotification, hideMovedNotification;
var redirectLink, moveXBlock, undoMoveXBlock, showMovedNotification, hideMovedNotification;
redirectLink = function(link) {
window.location.href = link;
};
moveXBlock = function(data) {
XBlockViewUtils.moveXBlock(data.sourceLocator, data.targetParentLocator)
.done(function(response) {
// hide modal
Backbone.trigger('move:hideMoveModal');
// hide xblock element
data.sourceXBlockElement.hide();
showMovedNotification(
StringUtils.interpolate(
gettext('Success! "{displayName}" has been moved.'),
{
displayName: data.sourceDisplayName
}
),
{
sourceXBlockElement: data.sourceXBlockElement,
sourceDisplayName: data.sourceDisplayName,
sourceLocator: data.sourceLocator,
sourceParentLocator: data.sourceParentLocator,
targetParentLocator: data.targetParentLocator,
targetIndex: response.source_index
}
);
Backbone.trigger('move:onXBlockMoved');
});
};
undoMoveXBlock = function(data) {
XBlockViewUtils.moveXBlock(data.sourceLocator, data.sourceParentLocator, data.targetIndex)
.done(function(response) {
.done(function() {
// show XBlock element
$('.studio-xblock-wrapper[data-locator="' + response.move_source_locator + '"]').show();
data.sourceXBlockElement.show();
showMovedNotification(
StringUtils.interpolate(
gettext('Move cancelled. "{sourceDisplayName}" has been moved back to its original location.'),
......@@ -31,6 +59,7 @@ function($, _, Feedback, AlertView, XBlockViewUtils, MoveXBlockUtils, StringUtil
}
)
);
Backbone.trigger('move:onXBlockMoved');
});
};
......@@ -44,15 +73,10 @@ function($, _, Feedback, AlertView, XBlockViewUtils, MoveXBlockUtils, StringUtil
primary: {
text: gettext('Undo move'),
class: 'action-save',
data: JSON.stringify({
sourceDisplayName: data.sourceDisplayName,
sourceLocator: data.sourceLocator,
sourceParentLocator: data.sourceParentLocator,
targetIndex: data.targetIndex
}),
click: function() {
undoMoveXBlock(
{
sourceXBlockElement: data.sourceXBlockElement,
sourceDisplayName: data.sourceDisplayName,
sourceLocator: data.sourceLocator,
sourceParentLocator: data.sourceParentLocator,
......@@ -65,9 +89,6 @@ function($, _, Feedback, AlertView, XBlockViewUtils, MoveXBlockUtils, StringUtil
{
text: gettext('Take me to the new location'),
class: 'action-cancel',
data: JSON.stringify({
targetParentLocator: data.targetParentLocator
}),
click: function() {
redirectLink('/container/' + data.targetParentLocator);
}
......@@ -100,6 +121,8 @@ function($, _, Feedback, AlertView, XBlockViewUtils, MoveXBlockUtils, StringUtil
return {
redirectLink: redirectLink,
moveXBlock: moveXBlock,
undoMoveXBlock: undoMoveXBlock,
showMovedNotification: showMovedNotification,
hideMovedNotification: hideMovedNotification
};
......
......@@ -272,7 +272,8 @@ define(['jquery', 'underscore', 'gettext', 'common/js/components/utils/view_util
findXBlockInfo = function(xblockWrapperElement, defaultXBlockInfo) {
var xblockInfo = defaultXBlockInfo,
xblockElement,
displayName;
displayName,
hasChildren;
if (xblockWrapperElement.length > 0) {
xblockElement = xblockWrapperElement.find('.xblock');
displayName = xblockWrapperElement.find(
......@@ -283,11 +284,13 @@ define(['jquery', 'underscore', 'gettext', 'common/js/components/utils/view_util
if (!displayName) {
displayName = xblockElement.find('.component-header').text().trim();
}
hasChildren = defaultXBlockInfo ? defaultXBlockInfo.get('has_children') : false;
xblockInfo = new XBlockInfo({
id: xblockWrapperElement.data('locator'),
courseKey: xblockWrapperElement.data('course-key'),
category: xblockElement.data('block-type'),
display_name: displayName
display_name: displayName,
has_children: hasChildren
});
}
return xblockInfo;
......
......@@ -229,6 +229,18 @@ class ContainerPage(PageObject, HelpMixin):
self.q(css='.button-view').first.click()
self._switch_to_lms()
def verify_publish_title(self, expected_title):
"""
Waits for the publish title to change to the expected value.
"""
def wait_for_title_change():
"""
Promise function to check publish title.
"""
return (self.publish_title == expected_title, self.publish_title)
Promise(wait_for_title_change, "Publish title incorrect. Found '" + self.publish_title + "'").fulfill()
def preview(self):
"""
Clicks "Preview", which will open the draft version of the unit page in the LMS.
......
......@@ -664,7 +664,7 @@ class UnitPublishingTest(ContainerBase):
And the last saved text contains "Last published"
"""
unit = self.go_to_unit_page()
self._verify_publish_title(unit, self.PUBLISHED_LIVE_STATUS)
unit.verify_publish_title(self.PUBLISHED_LIVE_STATUS)
# Start date set in course fixture to 1970.
self._verify_release_date_info(
unit, self.RELEASE_TITLE_RELEASED, 'Jan 01, 1970 at 00:00 UTC\nwith Section "Test Section"'
......@@ -675,11 +675,11 @@ class UnitPublishingTest(ContainerBase):
# Add a component to the page so it will have unpublished changes.
add_discussion(unit)
self._verify_publish_title(unit, self.DRAFT_STATUS)
unit.verify_publish_title(self.DRAFT_STATUS)
self._verify_last_published_and_saved(unit, self.LAST_PUBLISHED, self.LAST_SAVED)
unit.publish_action.click()
unit.wait_for_ajax()
self._verify_publish_title(unit, self.PUBLISHED_LIVE_STATUS)
unit.verify_publish_title(self.PUBLISHED_LIVE_STATUS)
self._verify_last_published_and_saved(unit, self.LAST_PUBLISHED, self.LAST_PUBLISHED)
def test_discard_changes(self):
......@@ -696,9 +696,9 @@ class UnitPublishingTest(ContainerBase):
"""
unit = self.go_to_unit_page()
add_discussion(unit)
self._verify_publish_title(unit, self.DRAFT_STATUS)
unit.verify_publish_title(self.DRAFT_STATUS)
unit.discard_changes()
self._verify_publish_title(unit, self.PUBLISHED_LIVE_STATUS)
unit.verify_publish_title(self.PUBLISHED_LIVE_STATUS)
def test_view_live_no_changes(self):
"""
......@@ -757,7 +757,7 @@ class UnitPublishingTest(ContainerBase):
Then I see the content in the unit
"""
unit = self.go_to_unit_page("Unlocked Section", "Unlocked Subsection", "Unlocked Unit")
self._verify_publish_title(unit, self.PUBLISHED_LIVE_STATUS)
unit.verify_publish_title(self.PUBLISHED_LIVE_STATUS)
self.assertTrue(unit.currently_visible_to_students)
self._verify_release_date_info(
unit, self.RELEASE_TITLE_RELEASED, self.past_start_date_text + '\n' + 'with Section "Unlocked Section"'
......@@ -783,7 +783,7 @@ class UnitPublishingTest(ContainerBase):
self.assertTrue(checked)
self.assertFalse(unit.currently_visible_to_students)
self.assertFalse(unit.shows_inherited_staff_lock())
self._verify_publish_title(unit, self.LOCKED_STATUS)
unit.verify_publish_title(self.LOCKED_STATUS)
self._view_published_version(unit)
# Will initially be in staff view, locked component should be visible.
self._verify_components_visible(['problem'])
......@@ -802,7 +802,7 @@ class UnitPublishingTest(ContainerBase):
Then I do not see any content in the unit
"""
unit = self.go_to_unit_page("Section With Locked Unit", "Subsection With Locked Unit", "Locked Unit")
self._verify_publish_title(unit, self.LOCKED_STATUS)
unit.verify_publish_title(self.LOCKED_STATUS)
self.assertFalse(unit.currently_visible_to_students)
self._verify_release_date_info(
unit, self.RELEASE_TITLE_RELEASE,
......@@ -826,7 +826,7 @@ class UnitPublishingTest(ContainerBase):
unit = self.go_to_unit_page("Section With Locked Unit", "Subsection With Locked Unit", "Locked Unit")
checked = unit.toggle_staff_lock()
self.assertFalse(checked)
self._verify_publish_title(unit, self.PUBLISHED_LIVE_STATUS)
unit.verify_publish_title(self.PUBLISHED_LIVE_STATUS)
self.assertTrue(unit.currently_visible_to_students)
self._view_published_version(unit)
# Will initially be in staff view, components always visible.
......@@ -894,10 +894,10 @@ class UnitPublishingTest(ContainerBase):
component.edit()
HtmlComponentEditorView(self.browser, component.locator).set_content_and_save(modified_content)
self.assertEqual(component.student_content, modified_content)
self._verify_publish_title(unit, self.DRAFT_STATUS)
unit.verify_publish_title(self.DRAFT_STATUS)
unit.publish_action.click()
unit.wait_for_ajax()
self._verify_publish_title(unit, self.PUBLISHED_LIVE_STATUS)
unit.verify_publish_title(self.PUBLISHED_LIVE_STATUS)
self._view_published_version(unit)
self.assertIn(modified_content, self.courseware.xblock_component_html_content(0))
......@@ -917,10 +917,10 @@ class UnitPublishingTest(ContainerBase):
component.edit()
HtmlComponentEditorView(self.browser, component.locator).set_content_and_cancel("modified content")
self.assertEqual(component.student_content, "Body of HTML Unit.")
self._verify_publish_title(unit, self.PUBLISHED_LIVE_STATUS)
unit.verify_publish_title(self.PUBLISHED_LIVE_STATUS)
self.browser.refresh()
unit.wait_for_page()
self._verify_publish_title(unit, self.PUBLISHED_LIVE_STATUS)
unit.verify_publish_title(self.PUBLISHED_LIVE_STATUS)
def test_delete_child_in_published_unit(self):
"""
......@@ -936,10 +936,10 @@ class UnitPublishingTest(ContainerBase):
"""
unit = self.go_to_unit_page()
unit.delete(0)
self._verify_publish_title(unit, self.DRAFT_STATUS)
unit.verify_publish_title(self.DRAFT_STATUS)
unit.publish_action.click()
unit.wait_for_ajax()
self._verify_publish_title(unit, self.PUBLISHED_LIVE_STATUS)
unit.verify_publish_title(self.PUBLISHED_LIVE_STATUS)
self._view_published_version(unit)
self.assertEqual(0, self.courseware.num_xblock_components)
......@@ -955,12 +955,12 @@ class UnitPublishingTest(ContainerBase):
Then the title in the Publish information box is "Published (not yet released)"
"""
unit = self.go_to_unit_page('Unreleased Section', 'Unreleased Subsection', 'Unreleased Unit')
self._verify_publish_title(unit, self.PUBLISHED_STATUS)
unit.verify_publish_title(self.PUBLISHED_STATUS)
add_discussion(unit)
self._verify_publish_title(unit, self.DRAFT_STATUS)
unit.verify_publish_title(self.DRAFT_STATUS)
unit.publish_action.click()
unit.wait_for_ajax()
self._verify_publish_title(unit, self.PUBLISHED_STATUS)
unit.verify_publish_title(self.PUBLISHED_STATUS)
def _view_published_version(self, unit):
"""
......@@ -1007,15 +1007,6 @@ class UnitPublishingTest(ContainerBase):
self.assertEqual(expected_title, unit.release_title)
self.assertEqual(expected_date, unit.release_date)
def _verify_publish_title(self, unit, expected_title):
"""
Waits for the publish title to change to the expected value.
"""
def wait_for_title_change():
return (unit.publish_title == expected_title, unit.publish_title)
Promise(wait_for_title_change, "Publish title incorrect. Found '" + unit.publish_title + "'").fulfill()
def _verify_last_published_and_saved(self, unit, expected_published_prefix, expected_saved_prefix):
"""
Verifies that last published and last saved messages respectively contain the given strings.
......@@ -1144,6 +1135,9 @@ class MoveComponentTest(ContainerBase):
"""
Tests of moving an XBlock to another XBlock.
"""
PUBLISHED_LIVE_STATUS = "Publishing Status\nPublished and Live"
DRAFT_STATUS = "Publishing Status\nDraft (Unpublished changes)"
def setUp(self, is_staff=True):
super(MoveComponentTest, self).setUp(is_staff=is_staff)
self.container = ContainerPage(self.browser, None)
......@@ -1181,26 +1175,36 @@ class MoveComponentTest(ContainerBase):
)
)
def verify_move_opertions(self, unit_page, source_component, operation, component_display_names_after_operation):
def verify_move_opertions(self, unit_page, source_component, operation, component_display_names_after_operation,
should_verify_publish_title=True):
"""
Verify move operations.
Arguments:
unit_page (Object) Unit container page.
source_component (Object) source XBlock object to be moved.
source_component (Object) Source XBlock object to be moved.
operation (str), `move` or `undo move` operation.
component_display_names_after_operation (dict) display names of components after operation in source/dest
component_display_names_after_operation (dict) Display names of components after operation in source/dest
should_verify_publish_title (Boolean) Should verify publish title ot not. Default is True.
"""
source_component.open_move_modal()
self.move_modal_view.navigate_to_category(self.source_xblock_category, self.navigation_options)
self.assertEqual(self.move_modal_view.is_move_button_enabled, True)
# Verify unit is in published state before move operation
if should_verify_publish_title:
self.container.verify_publish_title(self.PUBLISHED_LIVE_STATUS)
self.move_modal_view.click_move_button()
self.container.verify_confirmation_message(
self.message_move.format(display_name=self.source_component_display_name)
)
self.assertEqual(len(unit_page.displayed_children), 1)
# Verify unit in draft state now
if should_verify_publish_title:
self.container.verify_publish_title(self.DRAFT_STATUS)
if operation == 'move':
self.container.click_take_me_there_link()
elif operation == 'undo_move':
......@@ -1333,7 +1337,8 @@ class MoveComponentTest(ContainerBase):
unit_page=group_container_page,
source_component=components[0],
operation='undo_move',
component_display_names_after_operation=['HTML 311', 'HTML 312']
component_display_names_after_operation=['HTML 311', 'HTML 312'],
should_verify_publish_title=False
)
# Verify move operation for content experiment.
......@@ -1341,7 +1346,8 @@ class MoveComponentTest(ContainerBase):
unit_page=group_container_page,
source_component=components[0],
operation='move',
component_display_names_after_operation=['HTML 21', 'HTML 22', 'HTML 311']
component_display_names_after_operation=['HTML 21', 'HTML 22', 'HTML 311'],
should_verify_publish_title=False
)
def test_a11y(self):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment