Show a select box when editing a library content block
This commit is contained in:
@@ -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')
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user