BLD-1104: List units that use group configuration.

This commit is contained in:
jmclaus
2014-07-08 17:46:30 +02:00
parent df71afd4c0
commit 69ee9d92d6
14 changed files with 660 additions and 144 deletions

View File

@@ -99,6 +99,7 @@ class XBlockFixtureDesc(object):
self.grader_type = grader_type
self.publish = publish
self.children = []
self.locator = None
def add_children(self, *args):
"""
@@ -137,11 +138,12 @@ class XBlockFixtureDesc(object):
metadata={2},
grader_type={3},
publish={4},
children={5}
children={5},
locator={6},
>
""").strip().format(
self.category, self.data, self.metadata,
self.grader_type, self.publish, self.children
self.grader_type, self.publish, self.children, self.locator
)
@@ -199,7 +201,7 @@ class CourseFixture(StudioApiFixture):
self._updates = []
self._handouts = []
self._children = []
self.children = []
self._assets = []
self._advanced_settings = {}
@@ -216,7 +218,7 @@ class CourseFixture(StudioApiFixture):
Returns the course fixture to allow chaining.
"""
self._children.extend(args)
self.children.extend(args)
return self
def add_update(self, update):
@@ -257,7 +259,7 @@ class CourseFixture(StudioApiFixture):
self._configure_course()
self._upload_assets()
self._add_advanced_settings()
self._create_xblock_children(self._course_location, self._children)
self._create_xblock_children(self._course_location, self.children)
return self
@@ -362,7 +364,7 @@ class CourseFixture(StudioApiFixture):
# Construct HTML with each of the handout links
handouts_li = [
'<li><a href="/static/{handout}">Example Handout</a></li>'.format(handout=handout)
for handout in self._handouts
for handout in self._handouts
]
handouts_html = '<ol class="treeview-handoutsnav">{}</ol>'.format("".join(handouts_li))
@@ -446,12 +448,31 @@ class CourseFixture(StudioApiFixture):
Recursively create XBlock children.
"""
for desc in xblock_descriptions:
loc = self._create_xblock(parent_loc, desc)
loc = self.create_xblock(parent_loc, desc)
self._create_xblock_children(loc, desc.children)
self._publish_xblock(parent_loc)
def _create_xblock(self, parent_loc, xblock_desc):
def get_nested_xblocks(self, category=None):
"""
Return a list of nested XBlocks for the course that can be filtered by
category.
"""
xblocks = self._get_nested_xblocks(self)
if category:
xblocks = filter(lambda x: x.category == category, xblocks)
return xblocks
def _get_nested_xblocks(self, xblock_descriptor):
"""
Return a list of nested XBlocks for the course.
"""
xblocks = list(xblock_descriptor.children)
for child in xblock_descriptor.children:
xblocks.extend(self._get_nested_xblocks(child))
return xblocks
def create_xblock(self, parent_loc, xblock_desc):
"""
Create an XBlock with `parent_loc` (the location of the parent block)
and `xblock_desc` (an `XBlockFixtureDesc` instance).
@@ -477,6 +498,7 @@ class CourseFixture(StudioApiFixture):
try:
loc = response.json().get('locator')
xblock_desc.locator = loc
except ValueError:
raise CourseFixtureError("Could not decode JSON from '{0}'".format(response.content))

View File

@@ -89,6 +89,9 @@ class CourseOutlineUnit(CourseOutlineChild):
"""
return UnitPage(self.browser, self.locator).visit()
def is_browser_on_page(self):
return self.q(css=self.BODY_SELECTOR).present
class CourseOutlineSubsection(CourseOutlineChild, CourseOutlineContainer):
"""
@@ -197,4 +200,3 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer):
Open release date edit modal of first section in course outline
"""
self.q(css='div.section-published-date a.edit-release-date').first.click()

View File

@@ -15,6 +15,7 @@ class GroupConfigurationsPage(CoursePage):
def is_browser_on_page(self):
return self.q(css='body.view-group-configurations').present
@property
def group_configurations(self):
"""
Return list of the group configurations for the course.
@@ -68,6 +69,20 @@ class GroupConfiguration(object):
"""
return self.find_css(css).first.text[0]
def click_outline_anchor(self):
"""
Click on the `Course Outline` link.
"""
css = 'p.group-configuration-usage-text a'
self.find_css(css).first.click()
def click_unit_anchor(self, index=0):
"""
Click on the link to the unit.
"""
css = 'li.group-configuration-usage-unit a'
self.find_css(css).nth(index).click()
def edit(self):
"""
Open editing view for the group configuration.
@@ -114,6 +129,14 @@ class GroupConfiguration(object):
"""
return self.get_text('.message-status.error')
@property
def usages(self):
"""
Return list of usages.
"""
css = '.group-configuration-usage-unit'
return self.find_css(css).text
@property
def name(self):
"""

View File

@@ -14,6 +14,8 @@ class UnitPage(PageObject):
Unit page in Studio
"""
NAME_SELECTOR = '#unit-display-name-input'
def __init__(self, browser, unit_locator):
super(UnitPage, self).__init__(browser)
self.unit_locator = unit_locator
@@ -38,6 +40,10 @@ class UnitPage(PageObject):
Promise(_is_finished_loading, 'Finished rendering the xblocks in the unit.').fulfill()
)
@property
def name(self):
return self.q(css=self.NAME_SELECTOR).attrs('value')[0]
@property
def components(self):
"""
@@ -87,6 +93,7 @@ COMPONENT_BUTTONS = {
'save_settings': '.action-save',
}
class Component(PageObject):
"""
A PageObject representing an XBlock child on the Studio UnitPage (including

View File

@@ -8,13 +8,15 @@ import math
from unittest import skip, skipUnless
from xmodule.partitions.partitions import Group, UserPartition
from bok_choy.promise import Promise
from bok_choy.promise import Promise, EmptyPromise
from ..fixtures.course import XBlockFixtureDesc
from ..pages.studio.component_editor import ComponentEditorView
from ..pages.studio.overview import CourseOutlinePage
from ..pages.studio.settings_advanced import AdvancedSettingsPage
from ..pages.studio.settings_group_configurations import GroupConfigurationsPage
from ..pages.studio.utils import add_advanced_component
from ..pages.studio.unit import UnitPage
from ..pages.xblock.utils import wait_for_xblock_initialization
from acceptance.tests.base_studio_test import StudioCourseTest
@@ -238,6 +240,13 @@ class GroupConfigurationsTest(ContainerBase, SplitTestMixin):
self.course_info['run']
)
self.outline_page = CourseOutlinePage(
self.browser,
self.course_info['org'],
self.course_info['number'],
self.course_info['run']
)
def _assert_fields(self, config, cid=None, name='', description='', groups=None):
self.assertEqual(config.mode, 'details')
@@ -317,7 +326,7 @@ class GroupConfigurationsTest(ContainerBase, SplitTestMixin):
})
self.page.visit()
config = self.page.group_configurations()[0]
config = self.page.group_configurations[0]
# no groups when the the configuration is collapsed
self.assertEqual(len(config.groups), 0)
self._assert_fields(
@@ -327,7 +336,7 @@ class GroupConfigurationsTest(ContainerBase, SplitTestMixin):
groups=["Group 0", "Group 1"]
)
config = self.page.group_configurations()[1]
config = self.page.group_configurations[1]
self._assert_fields(
config,
@@ -350,10 +359,10 @@ class GroupConfigurationsTest(ContainerBase, SplitTestMixin):
Then I see the group configuration is saved successfully and has the new data
"""
self.page.visit()
self.assertEqual(len(self.page.group_configurations()), 0)
self.assertEqual(len(self.page.group_configurations), 0)
# Create new group configuration
self.page.create()
config = self.page.group_configurations()[0]
config = self.page.group_configurations[0]
config.name = "New Group Configuration Name"
config.description = "New Description of the group configuration."
config.groups[1].name = "New Group Name"
@@ -418,7 +427,7 @@ class GroupConfigurationsTest(ContainerBase, SplitTestMixin):
self.page.visit()
# Create new group configuration
self.page.create()
config = self.page.group_configurations()[0]
config = self.page.group_configurations[0]
config.name = "New Group Configuration Name"
# Add new group
config.add_group()
@@ -435,7 +444,7 @@ class GroupConfigurationsTest(ContainerBase, SplitTestMixin):
self.verify_groups(container, ['Group A', 'Group B', 'New group'], [])
self.page.visit()
config = self.page.group_configurations()[0]
config = self.page.group_configurations[0]
config.edit()
config.name = "Second Group Configuration Name"
# Add new group
@@ -476,11 +485,11 @@ class GroupConfigurationsTest(ContainerBase, SplitTestMixin):
"""
self.page.visit()
self.assertEqual(len(self.page.group_configurations()), 0)
self.assertEqual(len(self.page.group_configurations), 0)
# Create new group configuration
self.page.create()
config = self.page.group_configurations()[0]
config = self.page.group_configurations[0]
config.name = "Name of the Group Configuration"
config.description = "Description of the group configuration."
# Add new group
@@ -488,7 +497,7 @@ class GroupConfigurationsTest(ContainerBase, SplitTestMixin):
# Cancel the configuration
config.cancel()
self.assertEqual(len(self.page.group_configurations()), 0)
self.assertEqual(len(self.page.group_configurations), 0)
def test_can_cancel_editing_of_group_configuration(self):
"""
@@ -508,8 +517,7 @@ class GroupConfigurationsTest(ContainerBase, SplitTestMixin):
},
})
self.page.visit()
config = self.page.group_configurations()[0]
config = self.page.group_configurations[0]
config.name = "New Group Configuration Name"
config.description = "New Description of the group configuration."
# Add 2 new groups
@@ -552,7 +560,7 @@ class GroupConfigurationsTest(ContainerBase, SplitTestMixin):
# Create new group configuration
self.page.create()
# Leave empty required field
config = self.page.group_configurations()[0]
config = self.page.group_configurations[0]
config.description = "Description of the group configuration."
try_to_save_and_verify_error_message("Group Configuration name is required")
@@ -574,3 +582,76 @@ class GroupConfigurationsTest(ContainerBase, SplitTestMixin):
description="Description of the group configuration.",
groups=["Group A", "Group B"]
)
def test_group_configuration_empty_usage(self):
"""
Scenario: When group configuration is not used, ensure that the link to outline page works correctly.
Given I have a course without group configurations
And I create new group configuration with 2 default groups
Then I see a link to the outline page
When I click on the outline link
Then I see the outline page
"""
# Create a new group configurations
self.course_fixture._update_xblock(self.course_fixture._course_location, {
"metadata": {
u"user_partitions": [
UserPartition(0, "Name", "Description.", [Group("0", "Group A"), Group("1", "Group B")]).to_json(),
],
},
})
# Go to the Group Configuration Page and click on outline anchor
self.page.visit()
config = self.page.group_configurations[0]
config.toggle()
config.click_outline_anchor()
# Waiting for the page load and verify that we've landed on course outline page
EmptyPromise(
lambda: self.outline_page.is_browser_on_page(), "loaded page {!r}".format(self.outline_page),
timeout=30
).fulfill()
def test_group_configuration_non_empty_usage(self):
"""
Scenario: When group configuration is used, ensure that the links to units using a group configuration work correctly.
Given I have a course without group configurations
And I create new group configuration with 2 default groups
And I create a unit and assign the newly created group configuration
And open the Group Configuration page
Then I see a link to the newly created unit
When I click on the unit link
Then I see correct unit page
"""
# Create a new group configurations
self.course_fixture._update_xblock(self.course_fixture._course_location, {
"metadata": {
u"user_partitions": [
UserPartition(0, "Name", "Description.", [Group("0", "Group A"), Group("1", "Group B")]).to_json(),
],
},
})
# Assign newly created group configuration to unit
vertical = self.course_fixture.get_nested_xblocks(category="vertical")[0]
self.course_fixture.create_xblock(
vertical.locator,
XBlockFixtureDesc('split_test', 'Test Content Experiment', metadata={'user_partition_id': 0})
)
unit = UnitPage(self.browser, vertical.locator)
# Go to the Group Configuration Page and click unit anchor
self.page.visit()
config = self.page.group_configurations[0]
config.toggle()
usage = config.usages[0]
config.click_unit_anchor()
# Waiting for the page load and verify that we've landed on the unit page
EmptyPromise(
lambda: unit.is_browser_on_page(), "loaded page {!r}".format(unit),
timeout=30
).fulfill()
self.assertIn(unit.name, usage)