Merge pull request #4606 from edx/anton/remove-group-configurations
Add possibility to remove group configurations.
This commit is contained in:
@@ -1,6 +1,3 @@
|
||||
$ ->
|
||||
new TooltipManager
|
||||
|
||||
class @TooltipManager
|
||||
constructor: () ->
|
||||
@$body = $('body')
|
||||
@@ -45,3 +42,8 @@ class @TooltipManager
|
||||
hideTooltip: (e) =>
|
||||
@$tooltip.hide().css('opacity', 0)
|
||||
clearTimeout(@tooltipTimer)
|
||||
|
||||
# Move initialization at the bottom to make sure that TooltipManager is already
|
||||
# assigned to the Global object.
|
||||
$ ->
|
||||
new TooltipManager
|
||||
|
||||
@@ -499,7 +499,6 @@ 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))
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ Course Group Configurations page.
|
||||
"""
|
||||
|
||||
from .course_page import CoursePage
|
||||
from .utils import confirm_prompt
|
||||
|
||||
|
||||
class GroupConfigurationsPage(CoursePage):
|
||||
@@ -53,15 +54,13 @@ class GroupConfiguration(object):
|
||||
"""
|
||||
Expand/collapse group configuration.
|
||||
"""
|
||||
css = 'a.group-toggle'
|
||||
self.find_css(css).first.click()
|
||||
self.find_css('a.group-toggle').first.click()
|
||||
|
||||
def add_group(self):
|
||||
"""
|
||||
Add new group.
|
||||
"""
|
||||
css = 'button.action-add-group'
|
||||
self.find_css(css).first.click()
|
||||
self.find_css('button.action-add-group').first.click()
|
||||
|
||||
def get_text(self, css):
|
||||
"""
|
||||
@@ -73,37 +72,47 @@ class GroupConfiguration(object):
|
||||
"""
|
||||
Click on the `Course Outline` link.
|
||||
"""
|
||||
css = 'p.group-configuration-usage-text a'
|
||||
self.find_css(css).first.click()
|
||||
self.find_css('p.group-configuration-usage-text a').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()
|
||||
self.find_css('li.group-configuration-usage-unit a').nth(index).click()
|
||||
|
||||
def edit(self):
|
||||
"""
|
||||
Open editing view for the group configuration.
|
||||
"""
|
||||
css = '.action-edit .edit'
|
||||
self.find_css(css).first.click()
|
||||
self.find_css('.action-edit .edit').first.click()
|
||||
|
||||
@property
|
||||
def delete_button_is_disabled(self):
|
||||
return self.find_css('.actions .delete.is-disabled').present
|
||||
|
||||
@property
|
||||
def delete_button_is_absent(self):
|
||||
return not self.find_css('.actions .delete').present
|
||||
|
||||
def delete(self):
|
||||
"""
|
||||
Delete the group configuration.
|
||||
"""
|
||||
self.find_css('.actions .delete').first.click()
|
||||
confirm_prompt(self.page)
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Save group configuration.
|
||||
"""
|
||||
css = '.action-primary'
|
||||
self.find_css(css).first.click()
|
||||
self.find_css('.action-primary').first.click()
|
||||
self.page.wait_for_ajax()
|
||||
|
||||
def cancel(self):
|
||||
"""
|
||||
Cancel group configuration.
|
||||
"""
|
||||
css = '.action-secondary'
|
||||
self.find_css(css).first.click()
|
||||
self.find_css('.action-secondary').first.click()
|
||||
|
||||
@property
|
||||
def mode(self):
|
||||
@@ -149,8 +158,7 @@ class GroupConfiguration(object):
|
||||
"""
|
||||
Set group configuration name.
|
||||
"""
|
||||
css = '.group-configuration-name-input'
|
||||
self.find_css(css).first.fill(value)
|
||||
self.find_css('.group-configuration-name-input').first.fill(value)
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
@@ -164,20 +172,24 @@ class GroupConfiguration(object):
|
||||
"""
|
||||
Set group configuration description.
|
||||
"""
|
||||
css = '.group-configuration-description-input'
|
||||
self.find_css(css).first.fill(value)
|
||||
self.find_css('.group-configuration-description-input').first.fill(value)
|
||||
|
||||
@property
|
||||
def groups(self):
|
||||
"""
|
||||
Return list of groups.
|
||||
"""
|
||||
css = '.group'
|
||||
|
||||
def group_selector(group_index):
|
||||
return self.get_selector('.group-{} '.format(group_index))
|
||||
|
||||
return [Group(self.page, group_selector(index)) for index, element in enumerate(self.find_css(css))]
|
||||
return [Group(self.page, group_selector(index)) for index, element in enumerate(self.find_css('.group'))]
|
||||
|
||||
@property
|
||||
def delete_note(self):
|
||||
"""
|
||||
Return delete note for the group configuration.
|
||||
"""
|
||||
return self.find_css('.wrapper-delete-button').first.attrs('data-tooltip')[0]
|
||||
|
||||
def __repr__(self):
|
||||
return "<{}:{}>".format(self.__class__.__name__, self.name)
|
||||
|
||||
@@ -111,3 +111,14 @@ def get_codemirror_value(page, index=0, find_prefix="$"):
|
||||
return {find_prefix}('div.CodeMirror:eq({index})').get(0).CodeMirror.getValue();
|
||||
""".format(index=index, find_prefix=find_prefix)
|
||||
)
|
||||
|
||||
|
||||
def confirm_prompt(page, cancel=False):
|
||||
"""
|
||||
Ensures that a modal prompt and confirmation button are visible, then clicks the button. The prompt is canceled iff
|
||||
cancel is True.
|
||||
"""
|
||||
page.wait_for_element_visibility('.prompt', 'Prompt is visible')
|
||||
confirmation_button_css = '.prompt .action-' + ('secondary' if cancel else 'primary')
|
||||
page.wait_for_element_visibility(confirmation_button_css, 'Confirmation button is visible')
|
||||
click_css(page, confirmation_button_css, require_notification=(not cancel))
|
||||
|
||||
@@ -375,6 +375,7 @@ class GroupConfigurationsTest(ContainerBase, SplitTestMixin):
|
||||
|
||||
# Save the configuration
|
||||
self.assertEqual(config.get_text('.action-primary'), "CREATE")
|
||||
self.assertTrue(config.delete_button_is_absent)
|
||||
config.save()
|
||||
|
||||
self._assert_fields(
|
||||
@@ -652,6 +653,7 @@ class GroupConfigurationsTest(ContainerBase, SplitTestMixin):
|
||||
usage = config.usages[0]
|
||||
config.click_unit_anchor()
|
||||
|
||||
unit = UnitPage(self.browser, vertical.locator)
|
||||
# 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),
|
||||
@@ -659,3 +661,71 @@ class GroupConfigurationsTest(ContainerBase, SplitTestMixin):
|
||||
).fulfill()
|
||||
|
||||
self.assertIn(unit.name, usage)
|
||||
|
||||
def test_can_delete_unused_group_configuration(self):
|
||||
"""
|
||||
Scenario: Ensure that the user can delete unused group configuration.
|
||||
Given I have a course with 2 group configurations
|
||||
And I go to the Group Configuration page
|
||||
When I delete the Group Configuration with name "Configuration 1"
|
||||
Then I see that there is one Group Configuration
|
||||
When I edit the Group Configuration with name "Configuration 2"
|
||||
And I delete the Group Configuration with name "Configuration 2"
|
||||
Then I see that the are no Group Configurations
|
||||
"""
|
||||
self.course_fixture._update_xblock(self.course_fixture._course_location, {
|
||||
"metadata": {
|
||||
u"user_partitions": [
|
||||
UserPartition(0, 'Configuration 1', 'Description of the group configuration.', [Group("0", 'Group 0'), Group("1", 'Group 1')]).to_json(),
|
||||
UserPartition(1, 'Configuration 2', 'Second group configuration.', [Group("0", 'Alpha'), Group("1", 'Beta'), Group("2", 'Gamma')]).to_json()
|
||||
],
|
||||
},
|
||||
})
|
||||
self.page.visit()
|
||||
|
||||
self.assertEqual(len(self.page.group_configurations), 2)
|
||||
config = self.page.group_configurations[1]
|
||||
# Delete first group configuration via detail view
|
||||
config.delete()
|
||||
self.assertEqual(len(self.page.group_configurations), 1)
|
||||
|
||||
config = self.page.group_configurations[0]
|
||||
config.edit()
|
||||
self.assertFalse(config.delete_button_is_disabled)
|
||||
# Delete first group configuration via edit view
|
||||
config.delete()
|
||||
self.assertEqual(len(self.page.group_configurations), 0)
|
||||
|
||||
def test_cannot_delete_used_group_configuration(self):
|
||||
"""
|
||||
Scenario: Ensure that the user cannot delete unused group configuration.
|
||||
Given I have a course with group configuration that is used in the Content Experiment
|
||||
When I go to the Group Configuration page
|
||||
Then I do not see delete button and I see a note about that
|
||||
When I edit the Group Configuration
|
||||
Then I do not see delete button and I see the note about that
|
||||
"""
|
||||
# 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()
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
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})
|
||||
)
|
||||
# Go to the Group Configuration Page and click unit anchor
|
||||
self.page.visit()
|
||||
|
||||
config = self.page.group_configurations[0]
|
||||
self.assertTrue(config.delete_button_is_disabled)
|
||||
self.assertIn('Cannot delete when in use by an experiment', config.delete_note)
|
||||
|
||||
config.edit()
|
||||
self.assertTrue(config.delete_button_is_disabled)
|
||||
self.assertIn('Cannot delete when in use by an experiment', config.delete_note)
|
||||
|
||||
Reference in New Issue
Block a user