Show a select box when editing a library content block

This commit is contained in:
Braden MacDonald
2015-02-27 02:03:12 -08:00
parent 616f2bb0df
commit 93e5a56d79
9 changed files with 144 additions and 317 deletions

View File

@@ -4,9 +4,8 @@ Library edit page in Studio
from bok_choy.javascript import js_defined, wait_for_js
from bok_choy.page_object import PageObject
from bok_choy.promise import EmptyPromise
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.select import Select
from .overview import CourseOutlineModal
from .component_editor import ComponentEditorView
from .container import XBlockWrapper
from ...pages.studio.pagination import PaginatedMixin
from ...tests.helpers import disable_animations
@@ -111,65 +110,41 @@ class LibraryPage(PageObject, PaginatedMixin):
)
class StudioLibraryContentXBlockEditModal(CourseOutlineModal, PageObject):
class StudioLibraryContentEditor(ComponentEditorView):
"""
Library Content XBlock Modal edit window
"""
url = None
MODAL_SELECTOR = ".wrapper-modal-window-edit-xblock"
# Labels used to identify the fields on the edit modal:
LIBRARY_LABEL = "Libraries"
LIBRARY_LABEL = "Library"
COUNT_LABEL = "Count"
SCORED_LABEL = "Scored"
PROBLEM_TYPE_LABEL = "Problem Type"
def is_browser_on_page(self):
"""
Check that we are on the right page in the browser.
"""
return self.is_shown()
@property
def library_key(self):
"""
Gets value of first library key input
"""
library_key_input = self.get_metadata_input(self.LIBRARY_LABEL)
if library_key_input is not None:
return library_key_input.get_attribute('value').strip(',')
return None
def library_name(self):
return self.get_selected_option_text(self.LIBRARY_LABEL)
@library_key.setter
def library_key(self, library_key):
@library_name.setter
def library_name(self, library_name):
"""
Sets value of first library key input, creating it if necessary
Select a library from the library select box
"""
library_key_input = self.get_metadata_input(self.LIBRARY_LABEL)
if library_key_input is None:
library_key_input = self._add_library_key()
if library_key is not None:
# can't use lib_text.clear() here as input get deleted by client side script
library_key_input.send_keys(Keys.HOME)
library_key_input.send_keys(Keys.SHIFT, Keys.END)
library_key_input.send_keys(library_key)
else:
library_key_input.clear()
EmptyPromise(lambda: self.library_key == library_key, "library_key is updated in modal.").fulfill()
self.set_select_value(self.LIBRARY_LABEL, library_name)
EmptyPromise(lambda: self.library_name == library_name, "library_name is updated in modal.").fulfill()
@property
def count(self):
"""
Gets value of children count input
"""
return int(self.get_metadata_input(self.COUNT_LABEL).get_attribute('value'))
return int(self.get_setting_element(self.COUNT_LABEL).get_attribute('value'))
@count.setter
def count(self, count):
"""
Sets value of children count input
"""
count_text = self.get_metadata_input(self.COUNT_LABEL)
count_text = self.get_setting_element(self.COUNT_LABEL)
count_text.clear()
count_text.send_keys(count)
EmptyPromise(lambda: self.count == count, "count is updated in modal.").fulfill()
@@ -179,7 +154,7 @@ class StudioLibraryContentXBlockEditModal(CourseOutlineModal, PageObject):
"""
Gets value of scored select
"""
value = self.get_metadata_input(self.SCORED_LABEL).get_attribute('value')
value = self.get_selected_option_text(self.SCORED_LABEL)
if value == 'True':
return True
elif value == 'False':
@@ -191,10 +166,7 @@ class StudioLibraryContentXBlockEditModal(CourseOutlineModal, PageObject):
"""
Sets value of scored select
"""
select_element = self.get_metadata_input(self.SCORED_LABEL)
select_element.click()
scored_select = Select(select_element)
scored_select.select_by_value(str(scored))
self.set_select_value(self.SCORED_LABEL, str(scored))
EmptyPromise(lambda: self.scored == scored, "scored is updated in modal.").fulfill()
@property
@@ -202,54 +174,23 @@ class StudioLibraryContentXBlockEditModal(CourseOutlineModal, PageObject):
"""
Gets value of CAPA type select
"""
return self.get_metadata_input(self.PROBLEM_TYPE_LABEL).get_attribute('value')
return self.get_setting_element(self.PROBLEM_TYPE_LABEL).get_attribute('value')
@capa_type.setter
def capa_type(self, value):
"""
Sets value of CAPA type select
"""
select_element = self.get_metadata_input(self.PROBLEM_TYPE_LABEL)
select_element.click()
problem_type_select = Select(select_element)
problem_type_select.select_by_value(value)
self.set_select_value(self.PROBLEM_TYPE_LABEL, value)
EmptyPromise(lambda: self.capa_type == value, "problem type is updated in modal.").fulfill()
def _add_library_key(self):
def set_select_value(self, label, value):
"""
Adds library key input
Sets the select with given label (display name) to the specified value
"""
wrapper = self._get_metadata_element(self.LIBRARY_LABEL)
add_button = wrapper.find_element_by_xpath(".//a[contains(@class, 'create-action')]")
add_button.click()
return self._get_list_inputs(wrapper)[0]
def _get_list_inputs(self, list_wrapper):
"""
Finds nested input elements (useful for List and Dict fields)
"""
return list_wrapper.find_elements_by_xpath(".//input[@type='text']")
def _get_metadata_element(self, metadata_key):
"""
Gets metadata input element (a wrapper div for List and Dict fields)
"""
metadata_inputs = self.find_css(".metadata_entry .wrapper-comp-setting label.setting-label")
target_label = [elem for elem in metadata_inputs if elem.text == metadata_key][0]
label_for = target_label.get_attribute('for')
return self.find_css("#" + label_for)[0]
def get_metadata_input(self, metadata_key):
"""
Gets input/select element for given field
"""
element = self._get_metadata_element(metadata_key)
if element.tag_name == 'div':
# List or Dict field - return first input
# TODO support multiple values
inputs = self._get_list_inputs(element)
element = inputs[0] if inputs else None
return element
elem = self.get_setting_element(label)
select = Select(elem)
select.select_by_value(value)
@js_defined('window.LibraryContentAuthorView')

View File

@@ -9,7 +9,7 @@ from nose.plugins.attrib import attr
from ..helpers import UniqueCourseTest
from ...pages.studio.auto_auth import AutoAuthPage
from ...pages.studio.overview import CourseOutlinePage
from ...pages.studio.library import StudioLibraryContentXBlockEditModal, StudioLibraryContainerXBlockWrapper
from ...pages.studio.library import StudioLibraryContentEditor, StudioLibraryContainerXBlockWrapper
from ...pages.lms.courseware import CoursewarePage
from ...pages.lms.library import LibraryContentXBlockWrapper
from ...pages.common.logout import LogoutPage
@@ -65,7 +65,7 @@ class LibraryContentTestBase(UniqueCourseTest):
)
library_content_metadata = {
'source_libraries': [self.library_key],
'source_library_id': unicode(self.library_key),
'mode': 'random',
'max_count': 1,
'has_score': False
@@ -90,12 +90,13 @@ class LibraryContentTestBase(UniqueCourseTest):
Performs library block refresh in Studio, configuring it to show {count} children
"""
unit_page = self._go_to_unit_page(True)
library_container_block = StudioLibraryContainerXBlockWrapper.from_xblock_wrapper(unit_page.xblocks[0])
modal = StudioLibraryContentXBlockEditModal(library_container_block.edit())
modal.count = count
library_container_block = StudioLibraryContainerXBlockWrapper.from_xblock_wrapper(unit_page.xblocks[1])
library_container_block.edit()
editor = StudioLibraryContentEditor(self.browser, library_container_block.locator)
editor.count = count
if capa_type is not None:
modal.capa_type = capa_type
library_container_block.save_settings()
editor.capa_type = capa_type
editor.save()
self._go_to_unit_page(change_login=False)
unit_page.wait_for_page()
unit_page.publish_action.click()

View File

@@ -4,12 +4,11 @@ Acceptance tests for Library Content in LMS
import ddt
from flaky import flaky
import textwrap
from unittest import skip
from .base_studio_test import StudioLibraryTest
from ...fixtures.course import CourseFixture
from ..helpers import UniqueCourseTest
from ...pages.studio.library import StudioLibraryContentXBlockEditModal, StudioLibraryContainerXBlockWrapper
from ...pages.studio.library import StudioLibraryContentEditor, StudioLibraryContainerXBlockWrapper
from ...pages.studio.overview import CourseOutlinePage
from ...fixtures.course import XBlockFixtureDesc
@@ -56,7 +55,7 @@ class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest):
def populate_course_fixture(self, course_fixture):
""" Install a course with sections/problems, tabs, updates, and handouts """
library_content_metadata = {
'source_libraries': [self.library_key],
'source_library_id': unicode(self.library_key),
'mode': 'random',
'max_count': 1,
'has_score': False
@@ -79,29 +78,32 @@ class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest):
return StudioLibraryContainerXBlockWrapper.from_xblock_wrapper(xblock)
@ddt.data(
('library-v1:111+111', 1, True),
('library-v1:edX+L104', 2, False),
('library-v1:OtherX+IDDQD', 3, True),
(1, True),
(2, False),
(3, True),
)
@ddt.unpack
def test_can_edit_metadata(self, library_key, max_count, scored):
def test_can_edit_metadata(self, max_count, scored):
"""
Scenario: Given I have a library, a course and library content xblock in a course
When I go to studio unit page for library content block
And I edit library content metadata and save it
Then I can ensure that data is persisted
"""
library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[0])
edit_modal = StudioLibraryContentXBlockEditModal(library_container.edit())
edit_modal.library_key = library_key
library_name = self.library_info['display_name']
library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[1])
library_container.edit()
edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator)
edit_modal.library_name = library_name
edit_modal.count = max_count
edit_modal.scored = scored
library_container.save_settings() # saving settings
# open edit window again to verify changes are persistent
edit_modal = StudioLibraryContentXBlockEditModal(library_container.edit())
self.assertEqual(edit_modal.library_key, library_key)
library_container.edit()
edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator)
self.assertEqual(edit_modal.library_name, library_name)
self.assertEqual(edit_modal.count, max_count)
self.assertEqual(edit_modal.scored, scored)
@@ -109,47 +111,25 @@ class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest):
"""
Scenario: Given I have a library, a course and library content xblock in a course
When I go to studio unit page for library content block
And I edit set library key to none
And I edit to select "No Library"
Then I can see that library content block is misconfigured
"""
expected_text = 'A library has not yet been selected.'
expected_action = 'Select a Library'
library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[0])
library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[1])
# precondition check - the library block should be configured before we remove the library setting
self.assertFalse(library_container.has_validation_not_configured_warning)
edit_modal = StudioLibraryContentXBlockEditModal(library_container.edit())
edit_modal.library_key = None
library_container.edit()
edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator)
edit_modal.library_name = "No Library Selected"
library_container.save_settings()
self.assertTrue(library_container.has_validation_not_configured_warning)
self.assertIn(expected_text, library_container.validation_not_configured_warning_text)
self.assertIn(expected_action, library_container.validation_not_configured_warning_text)
def test_set_missing_library_shows_correct_label(self):
"""
Scenario: Given I have a library, a course and library content xblock in a course
When I go to studio unit page for library content block
And I edit set library key to non-existent library
Then I can see that library content block is misconfigured
"""
nonexistent_lib_key = 'library-v1:111+111'
expected_text = "Library is invalid, corrupt, or has been deleted."
library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[0])
# precondition check - assert library is configured before we remove it
self.assertFalse(library_container.has_validation_error)
edit_modal = StudioLibraryContentXBlockEditModal(library_container.edit())
edit_modal.library_key = nonexistent_lib_key
library_container.save_settings()
self.assertTrue(library_container.has_validation_error)
self.assertIn(expected_text, library_container.validation_error_text)
@flaky # TODO fix this, see TE-745
def test_out_of_date_message(self):
"""
@@ -162,7 +142,7 @@ class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest):
Then I can see that the content no longer needs to be updated
"""
expected_text = "This component is out of date. The library has new content."
library_block = self._get_library_xblock_wrapper(self.unit_page.xblocks[0])
library_block = self._get_library_xblock_wrapper(self.unit_page.xblocks[1])
self.assertFalse(library_block.has_validation_warning)
# Removed this assert until a summary message is added back to the author view (SOL-192)
@@ -178,7 +158,7 @@ class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest):
library_block.refresh_children()
self.unit_page.wait_for_page() # Wait for the page to reload
library_block = self._get_library_xblock_wrapper(self.unit_page.xblocks[0])
library_block = self._get_library_xblock_wrapper(self.unit_page.xblocks[1])
self.assertFalse(library_block.has_validation_message)
# Removed this assert until a summary message is added back to the author view (SOL-192)
@@ -206,13 +186,14 @@ class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest):
expected_text = 'There are no matching problem types in the specified libraries. Select another problem type'
library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[0])
library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[1])
# precondition check - assert library has children matching filter criteria
self.assertFalse(library_container.has_validation_error)
self.assertFalse(library_container.has_validation_warning)
edit_modal = StudioLibraryContentXBlockEditModal(library_container.edit())
library_container.edit()
edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator)
self.assertEqual(edit_modal.capa_type, "Any Type") # precondition check
edit_modal.capa_type = "Custom Evaluated Script"
@@ -221,7 +202,8 @@ class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest):
self.assertTrue(library_container.has_validation_warning)
self.assertIn(expected_text, library_container.validation_warning_text)
edit_modal = StudioLibraryContentXBlockEditModal(library_container.edit())
library_container.edit()
edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator)
self.assertEqual(edit_modal.capa_type, "Custom Evaluated Script") # precondition check
edit_modal.capa_type = "Dropdown"
library_container.save_settings()
@@ -240,13 +222,14 @@ class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest):
expected_tpl = "The specified libraries are configured to fetch {count} problems, " \
"but there are only {actual} matching problems."
library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[0])
library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[1])
# precondition check - assert block is configured fine
self.assertFalse(library_container.has_validation_error)
self.assertFalse(library_container.has_validation_warning)
edit_modal = StudioLibraryContentXBlockEditModal(library_container.edit())
library_container.edit()
edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator)
edit_modal.count = 50
library_container.save_settings()