BLD-1104: List units that use group configuration.
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user