BLD-1117: Add read-only list of Group Configurations.

This commit is contained in:
polesye
2014-06-20 12:46:24 +03:00
committed by Tim Babych
parent 3515212196
commit d38b51cb4a
30 changed files with 1465 additions and 17 deletions

View File

@@ -3,6 +3,10 @@ Course Advanced Settings page
"""
from .course_page import CoursePage
from .utils import press_the_notification_button, type_in_codemirror, get_codemirror_value
KEY_CSS = '.key h3.title'
class AdvancedSettingsPage(CoursePage):
@@ -14,3 +18,27 @@ class AdvancedSettingsPage(CoursePage):
def is_browser_on_page(self):
return self.q(css='body.advanced').present
def _get_index_of(self, expected_key):
for i, element in enumerate(self.q(css=KEY_CSS)):
# Sometimes get stale reference if I hold on to the array of elements
key = self.q(css=KEY_CSS).nth(i).text[0]
if key == expected_key:
return i
return -1
def save(self):
press_the_notification_button(self, "Save")
def cancel(self):
press_the_notification_button(self, "Cancel")
def set(self, key, new_value):
index = self._get_index_of(key)
type_in_codemirror(self, index, new_value)
self.save()
def get(self, key):
index = self._get_index_of(key)
return get_codemirror_value(self, index)

View File

@@ -0,0 +1,123 @@
"""
Course Group Configurations page.
"""
from .course_page import CoursePage
class GroupConfigurationsPage(CoursePage):
"""
Course Group Configurations page.
"""
url_path = "group_configurations"
def is_browser_on_page(self):
return self.q(css='body.view-group-configurations').present
def group_configurations(self):
"""
Returns list of the group configurations for the course.
"""
css = '.wrapper-group-configuration'
return [GroupConfiguration(self, index) for index in xrange(len(self.q(css=css)))]
class GroupConfiguration(object):
"""
Group Configuration wrapper.
"""
def __init__(self, page, index):
self.page = page
self.SELECTOR = '.view-group-configuration-{}'.format(index)
self.index = index
def get_selector(self, css=''):
return ' '.join([self.SELECTOR, css])
def find_css(self, selector):
"""
Find elements as defined by css locator.
"""
return self.page.q(css=self.get_selector(css=selector))
def toggle(self):
"""
Expand/collapse group configuration.
"""
css = 'a.group-toggle'
self.find_css(css).first.click()
@property
def id(self):
"""
Returns group configuration id.
"""
css = '.group-configuration-id .group-configuration-value'
return self.find_css(css).first.text[0]
@property
def name(self):
"""
Returns group configuration name.
"""
css = '.group-configuration-title'
return self.find_css(css).first.text[0]
@property
def description(self):
"""
Returns group configuration description.
"""
css = '.group-configuration-description'
return self.find_css(css).first.text[0]
@property
def groups(self):
"""
Returns list of groups.
"""
css = '.group'
def group_selector(config_index, group_index):
return self.get_selector('.groups-{} .group-{} '.format(config_index, group_index))
return [Group(self.page, group_selector(self.index, index)) for index, element in enumerate(self.find_css(css))]
def __repr__(self):
return "<{}:{}>".format(self.__class__.__name__, self.name)
class Group(object):
"""
Group wrapper.
"""
def __init__(self, page, prefix_selector):
self.page = page
self.prefix = prefix_selector
def find_css(self, selector):
"""
Find elements as defined by css locator.
"""
return self.page.q(css=self.prefix + selector)
@property
def name(self):
"""
Returns group name.
"""
css = '.group-name'
return self.find_css(css).first.text[0]
@property
def allocation(self):
"""
Returns allocation for the group.
"""
css = '.group-allocation'
return self.find_css(css).first.text[0]
def __repr__(self):
return "<{}:{}>".format(self.__class__.__name__, self.name)

View File

@@ -37,6 +37,18 @@ def wait_for_notification(page):
Promise(_is_saving_done, 'Notification should have been hidden.', timeout=60).fulfill()
def press_the_notification_button(page, name):
# Because the notification uses a CSS transition,
# Selenium will always report it as being visible.
# This makes it very difficult to successfully click
# the "Save" button at the UI level.
# Instead, we use JavaScript to reliably click
# the button.
btn_css = 'div#page-notification a.action-%s' % name.lower()
page.browser.execute_script("$('{}').focus().click()".format(btn_css))
page.wait_for_ajax()
def add_discussion(page, menu_index):
"""
Add a new instance of the discussion category.
@@ -66,3 +78,20 @@ def add_advanced_component(page, menu_index, name):
Promise(is_advanced_components_showing, "Advanced component menu not showing").fulfill()
click_css(page, 'a[data-category={}]'.format(name))
def type_in_codemirror(page, index, text, find_prefix="$"):
script = """
var cm = {find_prefix}('div.CodeMirror:eq({index})').get(0).CodeMirror;
CodeMirror.signal(cm, "focus", cm);
cm.setValue(arguments[0]);
CodeMirror.signal(cm, "blur", cm);""".format(index=index, find_prefix=find_prefix)
page.browser.execute_script(script, str(text))
def get_codemirror_value(page, index=0, find_prefix="$"):
return page.browser.execute_script(
"""
return {find_prefix}('div.CodeMirror:eq({index})').get(0).CodeMirror.getValue();
""".format(index=index, find_prefix=find_prefix)
)

View File

@@ -2,16 +2,22 @@
Acceptance tests for Studio related to the split_test module.
"""
import json
from unittest import skip
from ..fixtures.course import CourseFixture, XBlockFixtureDesc
from ..pages.studio.component_editor import ComponentEditorView
from ..pages.studio.settings_advanced import AdvancedSettingsPage
from ..pages.studio.settings_group_configurations import GroupConfigurationsPage
from ..pages.studio.auto_auth import AutoAuthPage
from test_studio_container import ContainerBase
from ..pages.studio.utils import add_advanced_component
from xmodule.partitions.partitions import Group, UserPartition
from bok_choy.promise import Promise
from .helpers import UniqueCourseTest
class SplitTest(ContainerBase):
"""
@@ -154,3 +160,147 @@ class SplitTest(ContainerBase):
container = self.create_poorly_configured_split_instance()
container.delete(0)
self.verify_groups(container, ['alpha'], [], verify_missing_groups_not_present=False)
class SettingsMenuTest(UniqueCourseTest):
"""
Tests that Setting menu is rendered correctly in Studio
"""
def setUp(self):
super(SettingsMenuTest, self).setUp()
course_fix = CourseFixture(**self.course_info)
course_fix.install()
self.auth_page = AutoAuthPage(
self.browser,
staff=False,
username=course_fix.user.get('username'),
email=course_fix.user.get('email'),
password=course_fix.user.get('password')
)
self.auth_page.visit()
self.advanced_settings = AdvancedSettingsPage(
self.browser,
self.course_info['org'],
self.course_info['number'],
self.course_info['run']
)
self.advanced_settings.visit()
def test_link_exist_if_split_test_enabled(self):
"""
Ensure that the link to the "Group Configurations" page is shown in the
Settings menu.
"""
link_css = 'li.nav-course-settings-group-configurations a'
self.assertFalse(self.advanced_settings.q(css=link_css).present)
self.advanced_settings.set('Advanced Module List', '["split_test"]')
self.browser.refresh()
self.advanced_settings.wait_for_page()
self.assertIn(
"split_test",
json.loads(self.advanced_settings.get('Advanced Module List')),
)
self.assertTrue(self.advanced_settings.q(css=link_css).present)
def test_link_does_not_exist_if_split_test_disabled(self):
"""
Ensure that the link to the "Group Configurations" page does not exist
in the Settings menu.
"""
link_css = 'li.nav-course-settings-group-configurations a'
self.advanced_settings.set('Advanced Module List', '[]')
self.browser.refresh()
self.advanced_settings.wait_for_page()
self.assertFalse(self.advanced_settings.q(css=link_css).present)
class GroupConfigurationsTest(UniqueCourseTest):
"""
Tests that Group Configurations page works correctly with previously
added configurations in Studio
"""
def setUp(self):
super(GroupConfigurationsTest, self).setUp()
course_fix = CourseFixture(**self.course_info)
course_fix.add_advanced_settings({
u"advanced_modules": {"value": ["split_test"]},
})
course_fix.install()
self.course_fix = course_fix
self.user = course_fix.user
self.auth_page = AutoAuthPage(
self.browser,
staff=False,
username=course_fix.user.get('username'),
email=course_fix.user.get('email'),
password=course_fix.user.get('password')
)
self.auth_page.visit()
self.page = GroupConfigurationsPage(
self.browser,
self.course_info['org'],
self.course_info['number'],
self.course_info['run']
)
def test_no_group_configurations_added(self):
"""
Ensure that message telling me to create a new group configuration is
shown when group configurations were not added.
"""
self.page.visit()
css = ".wrapper-content .no-group-configurations-content"
self.assertTrue(self.page.q(css=css).present)
self.assertIn(
"You haven't created any group configurations yet.",
self.page.q(css=css).text[0]
)
def test_group_configurations_have_correct_data(self):
"""
Ensure that the group configuration is rendered correctly in
expanded/collapsed mode.
"""
self.course_fix.add_advanced_settings({
u"user_partitions": {
"value": [
UserPartition(0, 'Name of the Group Configuration', 'Description of the group configuration.', [Group("0", 'Group 0'), Group("1", 'Group 1')]).to_json(),
UserPartition(1, 'Name of second Group Configuration', 'Second group configuration.', [Group("0", 'Alpha'), Group("1", 'Beta'), Group("2", 'Gamma')]).to_json()
],
},
})
self.course_fix._add_advanced_settings()
self.page.visit()
config = self.page.group_configurations()[0]
self.assertIn("Name of the Group Configuration", config.name)
self.assertEqual(config.id, '0')
config.toggle()
self.assertIn("Description of the group configuration.", config.description)
self.assertEqual(len(config.groups), 2)
self.assertEqual("Group 0", config.groups[0].name)
self.assertEqual("50%", config.groups[0].allocation)
config = self.page.group_configurations()[1]
self.assertIn("Name of second Group Configuration", config.name)
self.assertEqual(len(config.groups), 0) # no groups when the partition is collapsed
config.toggle()
self.assertEqual(len(config.groups), 3)
self.assertEqual("Beta", config.groups[1].name)
self.assertEqual("33%", config.groups[1].allocation)