Commit e83bee65 by Mushtaq Ali

Fixed Backend: Move component directly to content experiment level (component…

Fixed Backend: Move component directly to content experiment level (component will now be the sibling of groups)

Fixed Move button is disabled when moving components inside conditional module

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