Commit b6a23306 by zubair-arbi

add tests for drag and drop unit into collapsed subsection on course outline page

parent a0e69723
......@@ -11,7 +11,8 @@ define(["js/utils/drag_and_drop", "js/views/feedback_notification", "js/spec_hel
handleClass: '.unit-drag-handle',
droppableClass: 'ol.sortable-unit-list',
parentLocationSelector: 'li.courseware-subsection',
refresh: jasmine.createSpy('Spy on Unit')
refresh: jasmine.createSpy('Spy on Unit'),
ensureChildrenRendered: jasmine.createSpy('Spy on Unit')
});
}
);
......@@ -23,7 +24,8 @@ define(["js/utils/drag_and_drop", "js/views/feedback_notification", "js/spec_hel
handleClass: '.subsection-drag-handle',
droppableClass: '.sortable-subsection-list',
parentLocationSelector: 'section',
refresh: jasmine.createSpy('Spy on Subsection')
refresh: jasmine.createSpy('Spy on Subsection'),
ensureChildrenRendered: jasmine.createSpy('Spy on Subsection')
});
}
);
......@@ -277,6 +279,10 @@ define(["js/utils/drag_and_drop", "js/views/feedback_notification", "js/spec_hel
expect($('#subsection-1')).not.toHaveClass('expand-on-drop');
});
it("expands a collapsed element when something is dropped in it", function () {
expandElementSpy = spyOn(ContentDragger, 'expandElement').andCallThrough();
expect(expandElementSpy).not.toHaveBeenCalled();
expect($('#subsection-2').data('ensureChildrenRendered')).not.toHaveBeenCalled();
$('#subsection-2').addClass('is-collapsed');
ContentDragger.dragState.dropDestination = $('#list-2');
ContentDragger.dragState.attachMethod = "prepend";
......@@ -286,6 +292,10 @@ define(["js/utils/drag_and_drop", "js/views/feedback_notification", "js/spec_hel
}, null, {
clientX: $('#unit-1').offset().left
});
// verify collapsed element expands while ensuring its children are properly rendered
expect(expandElementSpy).toHaveBeenCalled();
expect($('#subsection-2').data('ensureChildrenRendered')).toHaveBeenCalled();
expect($('#subsection-2')).not.toHaveClass('is-collapsed');
});
});
......
......@@ -8,10 +8,11 @@ from bok_choy.promise import EmptyPromise
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from .course_page import CoursePage
from .container import ContainerPage
from .utils import set_input_value_and_save, set_input_value, click_css, confirm_prompt
from .utils import set_input_value_and_save, set_input_value, click_css, confirm_prompt, wait_for_notification
class CourseOutlineItem(object):
......@@ -510,6 +511,86 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer):
if subsection.is_collapsed:
subsection.toggle_expand()
@property
def outline_items(self):
"""
Return a list of xblocks loaded on the outline page.
"""
return self._get_outline_items()
def _get_outline_items(self, prefix=""):
return self.q(css=prefix + OutlineWrapper.BODY_SELECTOR).map(
lambda el: OutlineWrapper(self.browser, el.get_attribute('data-locator'))).results
def drag(self, source_index, target_index):
"""
Gets the drag handle with index source_index (relative to the vertical layout of the page)
and drags it to the location of the drag handle with target_index.
This should drag the element with the source_index drag handle BEFORE the
one with the target_index drag handle.
"""
draggables = self.q(css='.drag-handle')
source = draggables[source_index]
target = draggables[target_index]
action = ActionChains(self.browser)
# When dragging before the target element, must take into account that the placeholder
# will appear in the place where the target used to be.
placeholder_height = 40
action.click_and_hold(source).move_to_element_with_offset(
target, 0, placeholder_height
).release().perform()
wait_for_notification(self)
class OutlineWrapper(PageObject):
"""
A PageObject representing a wrapper around course outline items shown on the Course Outline page.
"""
url = None
BODY_SELECTOR = '.outline-item'
NAME_SELECTOR = '.item-title'
def __init__(self, browser, locator):
super(OutlineWrapper, self).__init__(browser)
self.locator = locator
def is_browser_on_page(self):
return self.q(css='{}[data-locator="{}"]'.format(self.BODY_SELECTOR, self.locator)).present
def _bounded_selector(self, selector):
"""
Return `selector`, but limited to this particular `CourseOutlineChild` context
"""
return '{}[data-locator="{}"] {}'.format(
self.BODY_SELECTOR,
self.locator,
selector
)
@property
def name(self):
titles = self.q(css=self._bounded_selector(self.NAME_SELECTOR)).text
if titles:
return titles[0]
else:
return None
@property
def children(self):
"""
Will return any first-generation descendant items of this item.
"""
descendants = self.q(css=self._bounded_selector(self.BODY_SELECTOR)).map(
lambda el: OutlineWrapper(self.browser, el.get_attribute('data-locator'))).results
# Now remove any non-direct descendants.
grandkids = []
for descendant in descendants:
grandkids.extend(descendant.children)
grand_locators = [grandkid.locator for grandkid in grandkids]
return [descendant for descendant in descendants if not descendant.locator in grand_locators]
class CourseOutlineModal(object):
MODAL_SELECTOR = ".wrapper-modal-window"
......
......@@ -49,10 +49,107 @@ class CourseOutlineTest(StudioCourseTest):
XBlockFixtureDesc('html', 'Test HTML Component'),
XBlockFixtureDesc('discussion', 'Test Discussion Component')
)
),
XBlockFixtureDesc('sequential', "DropS").add_children(
XBlockFixtureDesc('vertical', "DropV").add_children(
XBlockFixtureDesc('problem', 'Drop Problem 1', data=load_data_str('multiple_choice.xml')),
)
)
)
)
def verify_ordering(self, outline_page, expected_orderings):
"""
Verifies the expected ordering of xblocks on the page.
"""
xblocks = outline_page.outline_items
blocks_checked = set()
for expected_ordering in expected_orderings:
for xblock in xblocks:
parent = expected_ordering.keys()[0]
if xblock.name == parent:
blocks_checked.add(parent)
children = xblock.children
expected_length = len(expected_ordering.get(parent))
self.assertEqual(
expected_length, len(children),
"Number of children incorrect for group {0}. Expected {1} but got {2}.".format(parent, expected_length, len(children)))
for idx, expected in enumerate(expected_ordering.get(parent)):
self.assertEqual(expected, children[idx].name)
blocks_checked.add(expected)
break
self.assertEqual(len(blocks_checked), len(xblocks))
def do_action_and_verify(self, outline_page, action, expected_ordering):
"""
Perform the supplied action and then verify the resulting ordering.
"""
if outline_page is None:
outline_page = self.course_outline_page.visit()
action(outline_page)
self.verify_ordering(outline_page, expected_ordering)
# Reload the page and expand all subsections to see that the change was persisted.
course_outline_page = self.course_outline_page.visit()
course_outline_page.q(css='.outline-item.outline-subsection.is-collapsed .ui-toggle-expansion').click()
self.verify_ordering(course_outline_page, expected_ordering)
@attr('shard_2')
class CourseOutlineDragAndDropTest(CourseOutlineTest):
"""
Tests of drag and drop within the outline page.
"""
__test__ = True
def populate_course_fixture(self, course_fixture):
"""
Create a course with one section, two subsections, and four units
"""
# with collapsed outline
self.chap_1_handle = 0
self.chap_1_seq_1_handle = 1
# with first sequential expanded
self.seq_1_vert_1_handle = 2
self.seq_1_vert_2_handle = 3
self.chap_1_seq_2_handle = 4
course_fixture.add_children(
XBlockFixtureDesc('chapter', "1").add_children(
XBlockFixtureDesc('sequential', '1.1').add_children(
XBlockFixtureDesc('vertical', '1.1.1'),
XBlockFixtureDesc('vertical', '1.1.2')
),
XBlockFixtureDesc('sequential', '1.2').add_children(
XBlockFixtureDesc('vertical', '1.2.1'),
XBlockFixtureDesc('vertical', '1.2.2')
)
)
)
def drag_and_verify(self, source, target, expected_ordering, outline_page=None):
self.do_action_and_verify(
outline_page,
lambda (outline): outline.drag(source, target),
expected_ordering
)
def test_drop_unit_in_collapsed_subsection(self):
"""
Drag vertical "1.1.2" from subsection "1.1" into collapsed subsection "1.2" which already
have its own verticals.
"""
course_outline_page = self.course_outline_page.visit()
# expand first subsection
course_outline_page.q(css='.outline-item.outline-subsection.is-collapsed .ui-toggle-expansion').first.click()
expected_ordering = [{"1": ["1.1", "1.2"]},
{"1.1": ["1.1.1"]},
{"1.2": ["1.1.2", "1.2.1", "1.2.2"]}]
self.drag_and_verify(self.seq_1_vert_2_handle, self.chap_1_seq_2_handle, expected_ordering, course_outline_page)
@attr('shard_2')
class WarningMessagesTest(CourseOutlineTest):
......
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