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', 'js/views/pages/container', 'edx-ui-toolkit/js/utils/html-utils',
    'edx-ui-toolkit/js/utils/string-utils', 'js/models/xblock_info'],
    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, getMovedAlertNotification,
                verifyConfirmationFeedbackTitleText, verifyConfirmationFeedbackRedirectLinkText,
                verifyUndoConfirmationFeedbackTitleText, verifyConfirmationFeedbackUndoMoveActionText,
                sourceParentXBlockInfo, mockContainerPage, createContainerPage, containerPage,
                sourceDisplayName = 'component_display_name_0',
                sourceLocator = 'component_ID_0',
                sourceParentLocator = 'unit_ID_0';

            parentChildMap = {
                course: 'section',
                section: 'subsection',
                subsection: 'unit',
                unit: 'component'
            };

            categoryMap = {
                section: 'chapter',
                subsection: 'sequential',
                unit: 'vertical',
                component: 'component'
            };

            courseOutlineOptions = {
                section: 2,
                subsection: 2,
                unit: 2,
                component: 2
            };

            xblockAncestorInfo = {
                ancestors: [
                    {
                        category: 'vertical',
                        display_name: 'unit_display_name_0',
                        id: 'unit_ID_0'
                    },
                    {
                        category: 'sequential',
                        display_name: 'subsection_display_name_0',
                        id: 'subsection_ID_0'
                    },
                    {
                        category: 'chapter',
                        display_name: 'section_display_name_0',
                        id: 'section_ID_0'
                    },
                    {
                        category: 'course',
                        display_name: 'Demo Course',
                        id: 'COURSE_ID_101'
                    }
                ]
            };

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

            afterEach(function() {
                modal.hide();
                courseOutline = null;
                containerPage.remove();
            });

            showModal = function() {
                modal = new MoveXBlockModal({
                    sourceXBlockInfo: new XBlockInfo({
                        id: sourceLocator,
                        display_name: sourceDisplayName,
                        category: 'component'
                    }),
                    sourceParentXBlockInfo: sourceParentXBlockInfo,
                    XBlockUrlRoot: '/xblock'
                });
                modal.show();
            };

            /**
             * Create child XBlock info.
             *
             * @param {String} category         XBlock category
             * @param {Object} outlineOptions   options according to which outline was created
             * @param {Object} xblockIndex      XBlock Index
             * @returns
             */
            createChildXBlockInfo = function(category, outlineOptions, xblockIndex) {
                var childInfo = {
                    category: categoryMap[category],
                    display_name: category + '_display_name_' + xblockIndex,
                    id: category + '_ID_' + xblockIndex
                };
                return createXBlockInfo(parentChildMap[category], outlineOptions, childInfo);
            };

            /**
             * Create parent XBlock info.
             *
             * @param {String} category         XBlock category
             * @param {Object} outlineOptions   options according to which outline was created
             * @param {Object} outline          ouline info being constructed
             * @returns {Object}
             */
            createXBlockInfo = function(category, outlineOptions, outline) {
                var childInfo = {
                        category: categoryMap[category],
                        display_name: category,
                        children: []
                    },
                    xblocks;

                xblocks = outlineOptions[category];
                if (!xblocks) {
                    return outline;
                }

                outline.child_info = childInfo; // eslint-disable-line no-param-reassign
                _.each(_.range(xblocks), function(xblockIndex) {
                    childInfo.children.push(
                        createChildXBlockInfo(category, outlineOptions, xblockIndex)
                    );
                });
                return outline;
            };

            /**
             * Create course outline.
             *
             * @param {Object} outlineOptions   options according to which outline was created
             * @returns {Object}
             */
            createCourseOutline = function(outlineOptions) {
                var courseXBlockInfo = {
                    category: 'course',
                    display_name: 'Demo Course',
                    id: 'COURSE_ID_101'
                };
                return createXBlockInfo('section', outlineOptions, courseXBlockInfo);
            };

            /**
             * Render breadcrumb and XBlock list view.
             *
             * @param {any} courseOutlineInfo      course outline info
             * @param {any} ancestorInfo           ancestors info
             */
            renderViews = function(courseOutlineInfo, ancestorInfo) {
                var ancestorInfo = ancestorInfo || {ancestors: []};  // eslint-disable-line no-redeclare
                modal.renderViews(courseOutlineInfo, ancestorInfo);
            };

            /**
             * Extract displayed XBlock list info.
             *
             * @returns {Object}
             */
            getDisplayedInfo = function() {
                var viewEl = modal.moveXBlockListView.$el;
                return {
                    categoryText: viewEl.find('.category-text').text().trim(),
                    currentLocationText: viewEl.find('.current-location').text().trim(),
                    xblockCount: viewEl.find('.xblock-item').length,
                    xblockDisplayNames: viewEl.find('.xblock-item .xblock-displayname').map(
                        function() { return $(this).text().trim(); }
                    ).get(),
                    forwardButtonSRTexts: viewEl.find('.xblock-item .forward-sr-text').map(
                        function() { return $(this).text().trim(); }
                    ).get(),
                    forwardButtonCount: viewEl.find('.fa-arrow-right.forward-sr-icon').length
                };
            };

            /**
             * Verify displayed XBlock list info.
             *
             * @param {String} category                 XBlock category
             * @param {Integer} expectedXBlocksCount    number of XBlock childs displayed
             * @param {Boolean} hasCurrentLocation      do we need to check current location
             */
            verifyListViewInfo = function(category, expectedXBlocksCount, hasCurrentLocation) {
                var displayedInfo = getDisplayedInfo();
                expect(displayedInfo.categoryText).toEqual(modal.moveXBlockListView.categoriesText[category] + ':');
                expect(displayedInfo.xblockCount).toEqual(expectedXBlocksCount);
                expect(displayedInfo.xblockDisplayNames).toEqual(
                    _.map(_.range(expectedXBlocksCount), function(xblockIndex) {
                        return category + '_display_name_' + xblockIndex;
                    })
                );
                if (category === 'component') {
                    if (hasCurrentLocation) {
                        expect(displayedInfo.currentLocationText).toEqual('(Currently selected)');
                    }
                } else {
                    if (hasCurrentLocation) {
                        expect(displayedInfo.currentLocationText).toEqual('(Current location)');
                    }
                    expect(displayedInfo.forwardButtonSRTexts).toEqual(
                        _.map(_.range(expectedXBlocksCount), function() {
                            return 'View child items';
                        })
                    );
                    expect(displayedInfo.forwardButtonCount).toEqual(expectedXBlocksCount);
                }
            };

            /**
             * Verify rendered breadcrumb info.
             *
             * @param {any} category        XBlock category
             * @param {any} xblockIndex     XBlock index
             */
            verifyBreadcrumbViewInfo = function(category, xblockIndex) {
                var displayedBreadcrumbs = modal.moveXBlockBreadcrumbView.$el.find('.breadcrumbs .bc-container').map(
                    function() { return $(this).text().trim(); }
                ).get(),
                    categories = _.keys(parentChildMap).concat(['component']),
                    visitedCategories = categories.slice(0, _.indexOf(categories, category));

                expect(displayedBreadcrumbs).toEqual(
                    _.map(visitedCategories, function(visitedCategory) {
                        return visitedCategory === 'course' ?
                            'Course Outline' : visitedCategory + '_display_name_' + xblockIndex;
                    })
                );
            };

            /**
             * Click forward button in the list of displayed XBlocks.
             *
             * @param {any} buttonIndex     forward button index
             */
            clickForwardButton = function(buttonIndex) {
                buttonIndex = buttonIndex || 0;  // eslint-disable-line no-param-reassign
                modal.moveXBlockListView.$el.find('[data-item-index="' + buttonIndex + '"] button').click();
            };

            /**
             * Click on last clickable breadcrumb button.
             */
            clickBreadcrumbButton = function() {
                modal.moveXBlockBreadcrumbView.$el.find('.bc-container button').last().click();
            };

            /**
             * Returns the parent or child category of current XBlock.
             *
             * @param {String} direction    `forward` or `backward`
             * @param {String} category     XBlock category
             * @returns {String}
             */
            nextCategory = function(direction, category) {
                return direction === 'forward' ? parentChildMap[category] : _.invert(parentChildMap)[category];
            };

            /**
             * Verify renderd info of breadcrumbs and XBlock list.
             *
             * @param {Object} outlineOptions       options according to which outline was created
             * @param {String} category             XBlock category
             * @param {Integer} buttonIndex         forward button index
             * @param {String} direction            `forward` or `backward`
             * @param {String} hasCurrentLocation   do we need to check current location
             * @returns
             */
            verifyXBlockInfo = function(outlineOptions, category, buttonIndex, direction, hasCurrentLocation) {
                var expectedXBlocksCount = outlineOptions[category];

                verifyListViewInfo(category, expectedXBlocksCount, hasCurrentLocation);
                verifyBreadcrumbViewInfo(category, buttonIndex);
                verifyMoveEnabled(category, hasCurrentLocation);

                if (direction === 'forward') {
                    if (category === 'component') {
                        return;
                    }
                    clickForwardButton(buttonIndex);
                } else if (direction === 'backward') {
                    if (category === 'section') {
                        return;
                    }
                    clickBreadcrumbButton();
                }
                category = nextCategory(direction, category);  // eslint-disable-line no-param-reassign

                verifyXBlockInfo(outlineOptions, category, buttonIndex, direction, hasCurrentLocation);
            };

            /**
             * Verify move button is enabled.
             *
             * @param {String} category             XBlock category
             * @param {String} hasCurrentLocation   do we need to check current location
             */
            verifyMoveEnabled = function(category, hasCurrentLocation) {
                var isMoveEnabled = !modal.$el.find('.modal-actions .action-move').hasClass('is-disabled');
                if (category === 'component' && !hasCurrentLocation) {
                    expect(isMoveEnabled).toBeTruthy();
                } else {
                    expect(isMoveEnabled).toBeFalsy();
                }
            };

            /**
             * Verify notification status.
             *
             * @param {Object} requests             requests object
             * @param {Object} notificationSpy      notification spy
             * @param {String} notificationText     notification text to be verified
             * @param {Integer} sourceIndex         source index of the xblock
             */
            verifyNotificationStatus = function(requests, notificationSpy, notificationText, sourceIndex) {
                var sourceIndex = sourceIndex || 0;  // eslint-disable-line no-redeclare
                ViewHelpers.verifyNotificationShowing(notificationSpy, notificationText);
                AjaxHelpers.respondWithJson(requests, {
                    move_source_locator: sourceLocator,
                    parent_locator: sourceParentLocator,
                    target_index: sourceIndex
                });
                ViewHelpers.verifyNotificationHidden(notificationSpy);
            };

            /**
             * Get move alert confirmation message HTML
             */
            getMovedAlertNotification = function() {
                return $('#page-alert');
            };

            /**
             * Send move xblock request.
             *
             * @param {Object} requests             requests object
             * @param {Object} xblockLocator        Xblock id location
             * @param {Integer} targetIndex         target index of the xblock
             * @param {Integer} sourceIndex         source index of the xblock
             */
            sendMoveXBlockRequest = function(requests, xblockLocator, targetIndex, sourceIndex) {
                var responseData,
                    expectedData,
                    sourceIndex = sourceIndex || 0; // eslint-disable-line no-redeclare

                responseData = expectedData = {
                    move_source_locator: xblockLocator,
                    parent_locator: modal.targetParentXBlockInfo.id
                };

                if (targetIndex !== undefined) {
                    expectedData = _.extend(expectedData, {
                        targetIndex: targetIndex
                    });
                }

                // verify content of request
                AjaxHelpers.expectJsonRequest(requests, 'PATCH', '/xblock/', expectedData);

                // send the response
                AjaxHelpers.respondWithJson(requests, _.extend(responseData, {
                    source_index: sourceIndex
                }));
            };

            /**
             * Move xblock with success.
             *
             * @param {Object} requests             requests object
             */
            moveXBlockWithSuccess = function(requests) {
                // select a target item and click
                renderViews(courseOutline);
                _.each(_.range(3), function() {
                    clickForwardButton(1);
                });
                modal.$el.find('.modal-actions .action-move').click();
                sendMoveXBlockRequest(requests, sourceLocator);
                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 text.
             *
             * @param {String} displayName             XBlock display name
             */
            verifyConfirmationFeedbackTitleText = function(displayName) {
                expect(getMovedAlertNotification().find('.title').html()
                    .trim())
                    .toEqual(StringUtils.interpolate('Success! "{displayName}" has been moved.',
                        {
                            displayName: displayName
                        })
                    );
            };

            /**
             * Verify undo success banner message html has correct title text.
             *
             * @param {String} displayName             XBlock display name
             */
            verifyUndoConfirmationFeedbackTitleText = function(displayName) {
                expect(getMovedAlertNotification().find('.title').html()).toEqual(
                    StringUtils.interpolate(
                        'Move cancelled. "{sourceDisplayName}" has been moved back to its original location.',
                        {
                            sourceDisplayName: displayName
                        }
                    )
                );
            };

            /**
             * Verify success banner message html has correct redirect link text.
             */
            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 text.
             */
            verifyConfirmationFeedbackUndoMoveActionText = function() {
                expect(getMovedAlertNotification().find('.nav-actions .action-primary').html()).toEqual('Undo move');
            };

            /**
             * Get sent requests.
             *
             * @returns {Object}
             */
            getSentRequests = function() {
                return jasmine.Ajax.requests.filter(function(request) {
                    return request.readyState > 0;
                });
            };

            it('renders views with correct information', function() {
                var outlineOptions = {section: 1, subsection: 1, unit: 1, component: 1},
                    outline = createCourseOutline(outlineOptions);

                renderViews(outline, xblockAncestorInfo);
                verifyXBlockInfo(outlineOptions, 'section', 0, 'forward', true);
                verifyXBlockInfo(outlineOptions, 'component', 0, 'backward', true);
            });

            it('shows correct behavior on breadcrumb navigation', function() {
                var outline = createCourseOutline({section: 1, subsection: 1, unit: 1, component: 1});

                renderViews(outline);
                _.each(_.range(3), function() {
                    clickForwardButton();
                });

                _.each(['component', 'unit', 'subsection', 'section'], function(category) {
                    verifyListViewInfo(category, 1);
                    if (category !== 'section') {
                        modal.moveXBlockBreadcrumbView.$el.find('.bc-container button').last().click();
                    }
                });
            });

            it('shows the correct current location', function() {
                var outlineOptions = {section: 2, subsection: 2, unit: 2, component: 2},
                    outline = createCourseOutline(outlineOptions);
                renderViews(outline, xblockAncestorInfo);
                verifyXBlockInfo(outlineOptions, 'section', 0, 'forward', true);
                // click the outline breadcrumb to render sections
                modal.moveXBlockBreadcrumbView.$el.find('.bc-container button').first().click();
                verifyXBlockInfo(outlineOptions, 'section', 1, 'forward', false);
            });

            it('shows correct message when parent has no children', function() {
                var outlinesInfo = [
                    {
                        outline: createCourseOutline({}),
                        message: 'This course has no sections'
                    },
                    {
                        outline: createCourseOutline({section: 1}),
                        message: 'This section has no subsections',
                        forwardClicks: 1
                    },
                    {
                        outline: createCourseOutline({section: 1, subsection: 1}),
                        message: 'This subsection has no units',
                        forwardClicks: 2
                    },
                    {
                        outline: createCourseOutline({section: 1, subsection: 1, unit: 1}),
                        message: 'This unit has no components',
                        forwardClicks: 3
                    }
                ];

                _.each(outlinesInfo, function(info) {
                    renderViews(info.outline);
                    _.each(_.range(info.forwardClicks), function() {
                        clickForwardButton();
                    });
                    expect(modal.moveXBlockListView.$el.find('.xblock-no-child-message').text().trim())
                        .toEqual(info.message);
                    modal.moveXBlockListView.undelegateEvents();
                    modal.moveXBlockBreadcrumbView.undelegateEvents();
                });
            });

            describe('Move button', function() {
                it('is disabled when navigating to same parent', function() {
                    // select a target parent as the same as source parent and click
                    renderViews(courseOutline);
                    _.each(_.range(3), function() {
                        clickForwardButton(0);
                    });
                    verifyMoveEnabled('component', true);
                });

                it('is enabled when navigating to different parent', function() {
                    // select a target parent as the different as source parent and click
                    renderViews(courseOutline);
                    _.each(_.range(3), function() {
                        clickForwardButton(1);
                    });
                    verifyMoveEnabled('component', false);
                });

                it('verify move state while navigating', function() {
                    renderViews(courseOutline, xblockAncestorInfo);
                    verifyXBlockInfo(courseOutlineOptions, 'section', 0, 'forward', true);
                    // start from course outline again
                    modal.moveXBlockBreadcrumbView.$el.find('.bc-container button').first().click();
                    verifyXBlockInfo(courseOutlineOptions, 'section', 1, 'forward', false);
                });

                it('is disbabled when navigating to same source xblock', function() {
                    var outline,
                        libraryContentXBlockInfo = {
                            category: 'library_content',
                            display_name: 'Library Content',
                            has_children: true,
                            id: 'LIBRARY_CONTENT_ID'
                        },
                        outlineOptions = {library_content: 1, component: 1};

                    // make above xblock source xblock.
                    modal.sourceXBlockInfo = libraryContentXBlockInfo;
                    outline = createXBlockInfo('component', outlineOptions, libraryContentXBlockInfo);
                    renderViews(outline);
                    expect(modal.$el.find('.modal-actions .action-move').hasClass('is-disabled')).toBeTruthy();

                    // select a target parent
                    clickForwardButton(0);
                    expect(modal.$el.find('.modal-actions .action-move').hasClass('is-disabled')).toBeTruthy();
                });

                it('is disabled when navigating inside source content experiment', function() {
                    var outline,
                        splitTestXBlockInfo = {
                            category: 'split_test',
                            display_name: 'Content Experiment',
                            has_children: true,
                            id: 'SPLIT_TEST_ID'
                        },
                        outlineOptions = {split_test: 1, unit: 2, component: 1};

                    // make above xblock source xblock.
                    modal.sourceXBlockInfo = splitTestXBlockInfo;
                    outline = createXBlockInfo('unit', outlineOptions, splitTestXBlockInfo);
                    renderViews(outline);
                    expect(modal.$el.find('.modal-actions .action-move').hasClass('is-disabled')).toBeTruthy();

                    // navigate to groups level
                    clickForwardButton(0);
                    expect(modal.$el.find('.modal-actions .action-move').hasClass('is-disabled')).toBeTruthy();

                    // navigate to component level inside a group
                    clickForwardButton(0);

                    // move should be disabled because we are navigating inside source xblock
                    expect(modal.$el.find('.modal-actions .action-move').hasClass('is-disabled')).toBeTruthy();
                });

                it('is disabled when navigating to any content experiment groups', function() {
                    var outline,
                        splitTestXBlockInfo = {
                            category: 'split_test',
                            display_name: 'Content Experiment',
                            has_children: true,
                            id: 'SPLIT_TEST_ID'
                        },
                        outlineOptions = {split_test: 1, unit: 2, component: 1};

                    // group level should be disabled but component level inside groups should be movable
                    outline = createXBlockInfo('unit', outlineOptions, splitTestXBlockInfo);
                    renderViews(outline);

                    // move is disabled on groups level
                    expect(modal.$el.find('.modal-actions .action-move').hasClass('is-disabled')).toBeTruthy();

                    // navigate to component level inside a group
                    clickForwardButton(1);
                    expect(modal.$el.find('.modal-actions .action-move').hasClass('is-disabled')).toBeFalsy();
                });

                it('is enabled when navigating to any parentable component', function() {
                    var parentableXBlockInfo = {
                        category: 'vertical',
                        display_name: 'Parentable Component',
                        has_children: true,
                        id: 'PARENTABLE_ID'
                    };
                    renderViews(parentableXBlockInfo);

                    // move is enabled on parentable xblocks.
                    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',
                        display_name: 'Non Parentable Component',
                        has_children: false,
                        id: 'NON_PARENTABLE_ID'
                    };
                    renderViews(nonParentableXBlockInfo);

                    // move is disabled on non-parent xblocks.
                    expect(modal.$el.find('.modal-actions .action-move').hasClass('is-disabled')).toBeTruthy();
                });
            });

            describe('Move an xblock', function() {
                it('can not move in a disabled state', function() {
                    verifyMoveEnabled(false);
                    modal.$el.find('.modal-actions .action-move').click();
                    expect(getMovedAlertNotification().html().length).toEqual(0);
                    expect(getSentRequests().length).toEqual(0);
                });

                it('move an xblock when move button is clicked', function() {
                    var requests = AjaxHelpers.requests(this);
                    moveXBlockWithSuccess(requests);
                });

                it('do not move an xblock when cancel button is clicked', function() {
                    modal.$el.find('.modal-actions .action-cancel').click();
                    expect(getMovedAlertNotification().html().length).toEqual(0);
                    expect(getSentRequests().length).toEqual(0);
                });

                it('undo move an xblock when undo move link is clicked', function() {
                    var sourceIndex = 0,
                        requests = AjaxHelpers.requests(this);
                    moveXBlockWithSuccess(requests);
                    getMovedAlertNotification().find('.action-save').click();
                    AjaxHelpers.respondWithJson(requests, {
                        move_source_locator: sourceLocator,
                        parent_locator: sourceParentLocator,
                        target_index: sourceIndex
                    });
                    verifyUndoConfirmationFeedbackTitleText(sourceDisplayName);
                });
            });

            describe('shows a notification', function() {
                it('mini operation message when moving an xblock', function() {
                    var requests = AjaxHelpers.requests(this),
                        notificationSpy = ViewHelpers.createNotificationSpy();
                    // navigate to a target parent and click
                    renderViews(courseOutline);
                    _.each(_.range(3), function() {
                        clickForwardButton(1);
                    });
                    modal.$el.find('.modal-actions .action-move').click();
                    verifyNotificationStatus(requests, notificationSpy, 'Moving');
                });

                it('mini operation message when undo moving an xblock', function() {
                    var notificationSpy,
                        requests = AjaxHelpers.requests(this);
                    moveXBlockWithSuccess(requests);
                    notificationSpy = ViewHelpers.createNotificationSpy();
                    getMovedAlertNotification().find('.action-save').click();
                    verifyNotificationStatus(requests, notificationSpy, 'Undo moving');
                });

                it('error message when move request fails', function() {
                    var requests = AjaxHelpers.requests(this),
                        notificationSpy = ViewHelpers.createNotificationSpy('Error');
                    // select a target item and click
                    renderViews(courseOutline);
                    _.each(_.range(3), function() {
                        clickForwardButton(1);
                    });
                    modal.$el.find('.modal-actions .action-move').click();
                    AjaxHelpers.respondWithError(requests);
                    ViewHelpers.verifyNotificationShowing(notificationSpy, "Studio's having trouble saving your work");
                });

                it('error message when undo move request fails', function() {
                    var requests = AjaxHelpers.requests(this),
                        notificationSpy = ViewHelpers.createNotificationSpy('Error');
                    moveXBlockWithSuccess(requests);
                    getMovedAlertNotification().find('.action-save').click();
                    AjaxHelpers.respondWithError(requests);
                    ViewHelpers.verifyNotificationShowing(notificationSpy, "Studio's having trouble saving your work");
                });
            });
        });
    });
