Allow editing of container xblocks/xmodules

STUD-1312
This commit is contained in:
Andy Armstrong
2014-05-20 15:28:24 -04:00
parent 7cb241b8cc
commit 6b3e100cc1
44 changed files with 1016 additions and 514 deletions

View File

@@ -0,0 +1,66 @@
from bok_choy.page_object import PageObject
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from utils import click_css
class ComponentEditorView(PageObject):
"""
A :class:`.PageObject` representing the rendered view of a component editor.
This class assumes that the editor is our default editor as displayed for xmodules.
"""
BODY_SELECTOR = '.xblock-editor'
def __init__(self, browser, locator):
"""
Args:
browser (selenium.webdriver): The Selenium-controlled browser that this page is loaded in.
locator (str): The locator that identifies which xblock this :class:`.xblock-editor` relates to.
"""
super(ComponentEditorView, 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 `ComponentEditorView` context
"""
return '{}[data-locator="{}"] {}'.format(
self.BODY_SELECTOR,
self.locator,
selector
)
def url(self):
"""
Returns None because this is not directly accessible via URL.
"""
return None
def get_setting_entry_index(self, label):
"""
Returns the index of the setting entry with given label (display name) within the Settings modal.
"""
# TODO: will need to handle tabbed "Settings" in future (current usage is in vertical, only shows Settings.
setting_labels = self.q(css=self._bounded_selector('.metadata_edit .wrapper-comp-setting .setting-label'))
for index, setting in enumerate(setting_labels):
if setting.text == label:
return index
return None
def set_field_value_and_save(self, label, value):
"""
Set the field with given label (display name) to the specified value, and presses Save.
"""
index = self.get_setting_entry_index(label)
elem = self.q(css=self._bounded_selector('.metadata_edit div.wrapper-comp-setting input.setting-input'))[index]
# Click in the field, delete the value there.
action = ActionChains(self.browser).click(elem)
for _x in range(0, len(elem.get_attribute('value'))):
action = action.send_keys(Keys.BACKSPACE)
# Send the new text, then Tab to move to the next field (so change event is triggered).
action.send_keys(value).send_keys(Keys.TAB).perform()
click_css(self, 'a.action-save')

View File

@@ -3,7 +3,7 @@ Container page in Studio
"""
from bok_choy.page_object import PageObject
from bok_choy.promise import Promise
from bok_choy.promise import Promise, EmptyPromise
from . import BASE_URL
from selenium.webdriver.common.action_chains import ActionChains
@@ -15,15 +15,24 @@ class ContainerPage(PageObject):
"""
Container page in Studio
"""
NAME_SELECTOR = 'a.navigation-current'
def __init__(self, browser, unit_locator):
def __init__(self, browser, locator):
super(ContainerPage, self).__init__(browser)
self.unit_locator = unit_locator
self.locator = locator
@property
def url(self):
"""URL to the container page for an xblock."""
return "{}/container/{}".format(BASE_URL, self.unit_locator)
return "{}/container/{}".format(BASE_URL, self.locator)
@property
def name(self):
titles = self.q(css=self.NAME_SELECTOR).text
if titles:
return titles[0]
else:
return None
def is_browser_on_page(self):
@@ -91,6 +100,14 @@ class ContainerPage(PageObject):
# Click the confirmation dialog button
click_css(self, 'a.button.action-primary', 0)
def edit(self):
self.q(css='.edit-button').first.click()
EmptyPromise(
lambda: self.q(css='.xblock-studio_view').present,
'Wait for the Studio editor to be present'
).fulfill()
return self
class XBlockWrapper(PageObject):

View File

@@ -5,7 +5,7 @@ from bok_choy.promise import Promise
from selenium.webdriver.common.action_chains import ActionChains
def click_css(page, css, source_index, require_notification=True):
def click_css(page, css, source_index=0, require_notification=True):
"""
Click the button/link with the given css and index on the specified page (subclass of PageObject).

View File

@@ -1,11 +1,15 @@
"""
Acceptance tests for Studio related to the container page.
"""
from ..pages.studio.auto_auth import AutoAuthPage
from ..pages.studio.overview import CourseOutlinePage
from ..fixtures.course import CourseFixture, XBlockFixtureDesc
from .helpers import UniqueCourseTest
from ..pages.studio.component_editor import ComponentEditorView
from unittest import skip
class ContainerBase(UniqueCourseTest):
@@ -85,13 +89,17 @@ class ContainerBase(UniqueCourseTest):
).install()
def go_to_container_page(self, make_draft=False):
unit = self.go_to_unit_page(make_draft)
container = unit.components[0].go_to_container()
return container
def go_to_unit_page(self, make_draft=False):
self.outline.visit()
subsection = self.outline.section('Test Section').subsection('Test Subsection')
unit = subsection.toggle_expand().unit('Test Unit').go_to()
if make_draft:
unit.edit_draft()
container = unit.components[0].go_to_container()
return container
return unit
def verify_ordering(self, container, expected_orderings):
xblocks = container.xblocks
@@ -131,6 +139,7 @@ class DragAndDropTest(ContainerBase):
expected_ordering
)
@skip("Sporadically drags outside of the Group.")
def test_reorder_in_group(self):
"""
Drag Group A Item 2 before Group A Item 1.
@@ -303,3 +312,36 @@ class DeleteComponentTest(ContainerBase):
{self.group_b: [self.group_b_item_1, self.group_b_item_2]},
{self.group_empty: []}]
self.delete_and_verify(self.group_a_item_1_action_index, expected_ordering)
class EditContainerTest(ContainerBase):
"""
Tests of editing a container.
"""
__test__ = True
def modify_display_name_and_verify(self, component):
"""
Helper method for changing a display name.
"""
modified_name = 'modified'
self.assertNotEqual(component.name, modified_name)
component.edit()
component_editor = ComponentEditorView(self.browser, component.locator)
component_editor.set_field_value_and_save('Display Name', modified_name)
self.assertEqual(component.name, modified_name)
def test_edit_container_on_unit_page(self):
"""
Test the "edit" button on a container appearing on the unit page.
"""
unit = self.go_to_unit_page(make_draft=True)
component = unit.components[0]
self.modify_display_name_and_verify(component)
def test_edit_container_on_container_page(self):
"""
Test the "edit" button on a container appearing on the container page.
"""
container = self.go_to_container_page(make_draft=True)
self.modify_display_name_and_verify(container)