Backbone version of the course outline page

STUD-1726
This commit is contained in:
Andy Armstrong
2014-06-25 20:54:58 -04:00
committed by cahrens
parent 49e0c642e2
commit f061bbc08b
74 changed files with 2814 additions and 1583 deletions

View File

@@ -6,12 +6,54 @@ from bok_choy.promise import EmptyPromise
from .course_page import CoursePage
from .container import ContainerPage
from .utils import set_input_value_and_save
class CourseOutlineContainer(object):
class CourseOutlineItem(object):
"""
A mixin class for any :class:`PageObject` shown in a course outline.
"""
BODY_SELECTOR = None
NAME_SELECTOR = '.xblock-title .xblock-field-value'
NAME_INPUT_SELECTOR = '.xblock-title .xblock-field-input'
def __repr__(self):
return "{}(<browser>, {!r})".format(self.__class__.__name__, self.locator)
def _bounded_selector(self, selector):
"""
Returns `selector`, but limited to this particular `CourseOutlineItem` context
"""
return '{}[data-locator="{}"] {}'.format(
self.BODY_SELECTOR,
self.locator,
selector
)
@property
def name(self):
"""
Returns the display name of this object.
"""
name_element = self.q(css=self._bounded_selector(self.NAME_SELECTOR)).first
if name_element:
return name_element.text[0]
else:
return None
def change_name(self, new_name):
"""
Changes the container's name.
"""
self.q(css=self._bounded_selector(self.NAME_SELECTOR)).first.click()
set_input_value_and_save(self, self._bounded_selector(self.NAME_INPUT_SELECTOR), new_name)
self.wait_for_ajax()
class CourseOutlineContainer(CourseOutlineItem):
"""
A mixin to a CourseOutline page object that adds the ability to load
a child page object by title.
a child page object by title or by index.
CHILD_CLASS must be a :class:`CourseOutlineChild` subclass.
"""
@@ -33,15 +75,24 @@ class CourseOutlineContainer(object):
).attrs('data-locator')[0]
)
def child_at(self, index, child_class=None):
"""
Returns the child at the specified index.
:type self: object
"""
if not child_class:
child_class = self.CHILD_CLASS
class CourseOutlineChild(PageObject):
"""
A mixin to a CourseOutline page object that will be used as a child of
:class:`CourseOutlineContainer`.
"""
NAME_SELECTOR = None
BODY_SELECTOR = None
return child_class(
self.browser,
self.q(css=child_class.BODY_SELECTOR).attrs('data-locator')[index]
)
class CourseOutlineChild(PageObject, CourseOutlineItem):
"""
A page object that will be used as a child of :class:`CourseOutlineContainer`.
"""
def __init__(self, browser, locator):
super(CourseOutlineChild, self).__init__(browser)
self.locator = locator
@@ -49,38 +100,14 @@ class CourseOutlineChild(PageObject):
def is_browser_on_page(self):
return self.q(css='{}[data-locator="{}"]'.format(self.BODY_SELECTOR, self.locator)).present
@property
def name(self):
"""
Return the display name of this object.
"""
titles = self.q(css=self._bounded_selector(self.NAME_SELECTOR)).text
if titles:
return titles[0]
else:
return None
def __repr__(self):
return "{}(<browser>, {!r})".format(self.__class__.__name__, self.locator)
def _bounded_selector(self, selector):
"""
Return `selector`, but limited to this particular `CourseOutlineChild` context
"""
return '{}[data-locator="{}"] {}'.format(
self.BODY_SELECTOR,
self.locator,
selector
)
class CourseOutlineUnit(CourseOutlineChild):
"""
PageObject that wraps a unit link on the Studio Course Overview page.
"""
url = None
BODY_SELECTOR = '.courseware-unit'
NAME_SELECTOR = '.unit-name'
BODY_SELECTOR = '.outline-item-unit'
NAME_SELECTOR = '.xblock-title a'
def go_to(self):
"""
@@ -88,7 +115,7 @@ class CourseOutlineUnit(CourseOutlineChild):
an initialized :class:`.ContainerPage` for that unit.
"""
return ContainerPage(self.browser, self.locator).visit()
def is_browser_on_page(self):
return self.q(css=self.BODY_SELECTOR).present
@@ -99,8 +126,7 @@ class CourseOutlineSubsection(CourseOutlineChild, CourseOutlineContainer):
"""
url = None
BODY_SELECTOR = '.courseware-subsection'
NAME_SELECTOR = '.subsection-name-value'
BODY_SELECTOR = '.outline-item-subsection'
CHILD_CLASS = CourseOutlineUnit
def unit(self, title):
@@ -116,15 +142,12 @@ class CourseOutlineSubsection(CourseOutlineChild, CourseOutlineContainer):
self.browser.execute_script("jQuery.fx.off = true;")
def subsection_expanded():
return all(
self.q(css=self._bounded_selector('.new-unit-item'))
.map(lambda el: el.is_displayed())
.results
)
add_button = self.q(css=self._bounded_selector('.add-button')).first.results
return add_button and add_button[0].is_displayed()
currently_expanded = subsection_expanded()
self.q(css=self._bounded_selector('.expand-collapse')).first.click()
self.q(css=self._bounded_selector('.ui-toggle-expansion')).first.click()
EmptyPromise(
lambda: subsection_expanded() != currently_expanded,
@@ -139,8 +162,7 @@ class CourseOutlineSection(CourseOutlineChild, CourseOutlineContainer):
:class`.PageObject` that wraps a section block on the Studio Course Overview page.
"""
url = None
BODY_SELECTOR = '.courseware-section'
NAME_SELECTOR = '.section-name-span'
BODY_SELECTOR = '.outline-item-section'
CHILD_CLASS = CourseOutlineSubsection
def subsection(self, title):
@@ -165,6 +187,12 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer):
Return the :class:`.CourseOutlineSection` with the title `title`.
"""
return self.child(title)
def section_at(self, index):
"""
Returns the :class:`.CourseOutlineSection` at the specified index.
"""
return self.child_at(index)
def click_section_name(self, parent_css=''):
"""

View File

@@ -1,7 +1,10 @@
"""
Utility methods useful for Studio page tests.
"""
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from bok_choy.promise import EmptyPromise
from ...tests.helpers import disable_animations
@@ -122,3 +125,17 @@ def confirm_prompt(page, cancel=False):
confirmation_button_css = '.prompt .action-' + ('secondary' if cancel else 'primary')
page.wait_for_element_visibility(confirmation_button_css, 'Confirmation button is visible')
click_css(page, confirmation_button_css, require_notification=(not cancel))
def set_input_value_and_save(page, css, value):
"""
Sets the text field with given label (display name) to the specified value, and presses Save.
"""
input_element = page.q(css=css).results[0]
# Click in the input to give it the focus
action = ActionChains(page.browser).click(input_element)
# Delete all of the characters that are currently there
for _x in range(0, len(input_element.get_attribute('value'))):
action = action.send_keys(Keys.BACKSPACE)
# Send the new text, then hit the enter key so that the change event is triggered).
action.send_keys(value).send_keys(Keys.ENTER).perform()

View File

@@ -142,23 +142,25 @@ class CourseSectionTest(StudioCourseTest):
"""
Check that section name is editable on course outline page.
"""
section_name = self.course_outline_page.get_section_name()[0]
self.assertEqual(section_name, "Test Section")
self.course_outline_page.change_section_name("Test Section New")
section_name = self.course_outline_page.get_section_name(page_refresh=True)[0]
self.assertEqual(section_name, "Test Section New")
new_name = u"Test Section New"
section = self.course_outline_page.section_at(0)
self.assertEqual(section.name, u"Test Section")
section.change_name(new_name)
self.browser.refresh()
self.assertEqual(section.name, new_name)
def test_section_name_not_editable_inside_modal(self):
"""
Check that section name is not editable inside "Section Release Date" modal on course outline page.
"""
parent_css='div.modal-window'
self.course_outline_page.click_release_date()
section_name = self.course_outline_page.get_section_name(parent_css)[0]
self.assertEqual(section_name, '"Test Section"')
self.course_outline_page.click_section_name(parent_css)
section_name_edit_form = self.course_outline_page.section_name_edit_form_present(parent_css)
self.assertFalse(section_name_edit_form)
# TODO: re-enable when release date support is added back
# def test_section_name_not_editable_inside_modal(self):
# """
# Check that section name is not editable inside "Section Release Date" modal on course outline page.
# """
# parent_css='div.modal-window'
# self.course_outline_page.click_release_date()
# section_name = self.course_outline_page.get_section_name(parent_css)[0]
# self.assertEqual(section_name, '"Test Section"')
# self.course_outline_page.click_section_name(parent_css)
# section_name_edit_form = self.course_outline_page.section_name_edit_form_present(parent_css)
# self.assertFalse(section_name_edit_form)
@attr('shard_1')