Commit d8884980 by Sofiya Semenova

EDUCATOR-699 Modify deleted group warning on the unit/container page

EDUCATOR-1001 Fix language for visibility editor and validation to include units
parent c7db5d89
define(['js/views/xblock_validation', 'js/models/xblock_validation'], define(['js/views/xblock_validation', 'js/models/xblock_validation'],
function(XBlockValidationView, XBlockValidationModel) { function(XBlockValidationView, XBlockValidationModel) {
'use strict'; 'use strict';
return function(validationMessages, hasEditingUrl, isRoot, validationEle) { return function(validationMessages, hasEditingUrl, isRoot, isUnit, validationEle) {
var model, response;
if (hasEditingUrl && !isRoot) { if (hasEditingUrl && !isRoot) {
validationMessages.showSummaryOnly = true; validationMessages.showSummaryOnly = true;
} }
response = validationMessages;
response.isUnit = isUnit;
var model = new XBlockValidationModel(validationMessages, {parse: true}); model = new XBlockValidationModel(response, {parse: true});
if (!model.get('empty')) { if (!model.get('empty')) {
new XBlockValidationView({el: validationEle, model: model, root: isRoot}).render(); new XBlockValidationView({el: validationEle, model: model, root: isRoot}).render();
......
...@@ -19,8 +19,12 @@ define(['backbone', 'gettext', 'underscore'], function(Backbone, gettext, _) { ...@@ -19,8 +19,12 @@ define(['backbone', 'gettext', 'underscore'], function(Backbone, gettext, _) {
var summary = 'summary' in response ? response.summary : {}; var summary = 'summary' in response ? response.summary : {};
var messages = 'messages' in response ? response.messages : []; var messages = 'messages' in response ? response.messages : [];
if (!summary.text) { if (!summary.text) {
if (response.isUnit) {
summary.text = gettext('This unit has validation issues.');
} else {
summary.text = gettext('This component has validation issues.'); summary.text = gettext('This component has validation issues.');
} }
}
if (!summary.type) { if (!summary.type) {
summary.type = this.WARNING; summary.type = this.WARNING;
// Possible types are ERROR, WARNING, and NOT_CONFIGURED. NOT_CONFIGURED is treated as a warning. // Possible types are ERROR, WARNING, and NOT_CONFIGURED. NOT_CONFIGURED is treated as a warning.
......
define(['jquery', 'js/factories/xblock_validation', 'common/js/spec_helpers/template_helpers'], define(['jquery', 'js/factories/xblock_validation', 'common/js/spec_helpers/template_helpers'],
function($, XBlockValidationFactory, TemplateHelpers) { function($, XBlockValidationFactory, TemplateHelpers) {
describe('XBlockValidationFactory', function() { describe('XBlockValidationFactory', function() {
var messageDiv; var $messageDiv;
beforeEach(function() { beforeEach(function() {
TemplateHelpers.installTemplate('xblock-validation-messages'); TemplateHelpers.installTemplate('xblock-validation-messages');
appendSetFixtures($('<div class="messages"></div>')); appendSetFixtures($('<div class="messages"></div>'));
messageDiv = $('.messages'); $messageDiv = $('.messages');
}); });
it('Does not attach a view if messages is empty', function() { it('Does not attach a view if messages is empty', function() {
XBlockValidationFactory({'empty': true}, false, false, messageDiv); XBlockValidationFactory({empty: true}, false, false, false, $messageDiv);
expect(messageDiv.children().length).toEqual(0); expect($messageDiv.children().length).toEqual(0);
}); });
it('Does attach a view if messages are not empty', function() { it('Does attach a view if messages are not empty', function() {
XBlockValidationFactory({'empty': false}, false, false, messageDiv); XBlockValidationFactory({empty: false}, false, false, false, $messageDiv);
expect(messageDiv.children().length).toEqual(1); expect($messageDiv.children().length).toEqual(1);
}); });
it('Passes through the root property to the view.', function() { it('Passes through the root property to the view.', function() {
...@@ -29,12 +29,12 @@ define(['jquery', 'js/factories/xblock_validation', 'common/js/spec_helpers/temp ...@@ -29,12 +29,12 @@ define(['jquery', 'js/factories/xblock_validation', 'common/js/spec_helpers/temp
'xblock_id': 'id' 'xblock_id': 'id'
}; };
// Root is false, will not add noContainerContent. // Root is false, will not add noContainerContent.
XBlockValidationFactory(notConfiguredMessages, true, false, messageDiv); XBlockValidationFactory(notConfiguredMessages, true, false, false, $messageDiv);
expect(messageDiv.find('.validation')).not.toHaveClass(noContainerContent); expect($messageDiv.find('.validation')).not.toHaveClass(noContainerContent);
// Root is true, will add noContainerContent. // Root is true, will add noContainerContent.
XBlockValidationFactory(notConfiguredMessages, true, true, messageDiv); XBlockValidationFactory(notConfiguredMessages, true, true, false, $messageDiv);
expect(messageDiv.find('.validation')).toHaveClass(noContainerContent); expect($messageDiv.find('.validation')).toHaveClass(noContainerContent);
}); });
describe('Controls display of detailed messages based on url and root property', function() { describe('Controls display of detailed messages based on url and root property', function() {
...@@ -50,25 +50,25 @@ define(['jquery', 'js/factories/xblock_validation', 'common/js/spec_helpers/temp ...@@ -50,25 +50,25 @@ define(['jquery', 'js/factories/xblock_validation', 'common/js/spec_helpers/temp
}); });
checkDetailedMessages = function(expectedDetailedMessages) { checkDetailedMessages = function(expectedDetailedMessages) {
expect(messageDiv.children().length).toEqual(1); expect($messageDiv.children().length).toEqual(1);
expect(messageDiv.find('.xblock-message-item').length).toBe(expectedDetailedMessages); expect($messageDiv.find('.xblock-message-item').length).toBe(expectedDetailedMessages);
}; };
it('Does not show details if xblock has an editing URL and it is not rendered as root', function() { it('Does not show details if xblock has an editing URL and it is not rendered as root', function() {
XBlockValidationFactory(messagesWithSummary, true, false, messageDiv); XBlockValidationFactory(messagesWithSummary, true, false, false, $messageDiv);
checkDetailedMessages(0); checkDetailedMessages(0);
}); });
it('Shows details if xblock does not have its own editing URL, regardless of root value', function() { it('Shows details if xblock does not have its own editing URL, regardless of root value', function() {
XBlockValidationFactory(messagesWithSummary, false, false, messageDiv); XBlockValidationFactory(messagesWithSummary, false, false, false, $messageDiv);
checkDetailedMessages(2); checkDetailedMessages(2);
XBlockValidationFactory(messagesWithSummary, false, true, messageDiv); XBlockValidationFactory(messagesWithSummary, false, true, false, $messageDiv);
checkDetailedMessages(2); checkDetailedMessages(2);
}); });
it('Shows details if xblock has its own editing URL and is rendered as root', function() { it('Shows details if xblock has its own editing URL and is rendered as root', function() {
XBlockValidationFactory(messagesWithSummary, true, true, messageDiv); XBlockValidationFactory(messagesWithSummary, true, true, false, $messageDiv);
checkDetailedMessages(2); checkDetailedMessages(2);
}); });
}); });
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from contentstore.views.helpers import xblock_studio_url from contentstore.views.helpers import xblock_studio_url
from contentstore.utils import is_visible_to_specific_partition_groups from contentstore.utils import is_visible_to_specific_partition_groups
from lms.lib.utils import is_unit
from openedx.core.djangolib.js_utils import ( from openedx.core.djangolib.js_utils import (
dump_js_escaped_json, js_escaped_string dump_js_escaped_json, js_escaped_string
) )
...@@ -14,6 +15,7 @@ section_class = "level-nesting" if show_inline else "level-element" ...@@ -14,6 +15,7 @@ section_class = "level-nesting" if show_inline else "level-element"
collapsible_class = "is-collapsible" if xblock.has_children else "" collapsible_class = "is-collapsible" if xblock.has_children else ""
label = xblock.display_name_with_default or xblock.scope_ids.block_type label = xblock.display_name_with_default or xblock.scope_ids.block_type
messages = xblock.validate().to_json() messages = xblock.validate().to_json()
block_is_unit = is_unit(xblock)
%> %>
<%namespace name='static' file='static_content.html'/> <%namespace name='static' file='static_content.html'/>
...@@ -30,6 +32,7 @@ messages = xblock.validate().to_json() ...@@ -30,6 +32,7 @@ messages = xblock.validate().to_json()
${messages | n, dump_js_escaped_json}, ${messages | n, dump_js_escaped_json},
${bool(xblock_url) | n, dump_js_escaped_json}, // xblock_url will be None or a string ${bool(xblock_url) | n, dump_js_escaped_json}, // xblock_url will be None or a string
${bool(is_root) | n, dump_js_escaped_json}, // is_root will be None or a boolean ${bool(is_root) | n, dump_js_escaped_json}, // is_root will be None or a boolean
${bool(block_is_unit) | n, dump_js_escaped_json}, // block_is_unit will be None or a boolean
$('div.xblock-validation-messages[data-locator="${xblock.location | n, js_escaped_string}"]') $('div.xblock-validation-messages[data-locator="${xblock.location | n, js_escaped_string}"]')
); );
}); });
......
...@@ -4,6 +4,7 @@ from django.conf import settings ...@@ -4,6 +4,7 @@ from django.conf import settings
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from contentstore.utils import ancestor_has_staff_lock, get_visibility_partition_info from contentstore.utils import ancestor_has_staff_lock, get_visibility_partition_info
from openedx.core.djangolib.markup import HTML, Text from openedx.core.djangolib.markup import HTML, Text
from lms.lib.utils import is_unit
partition_info = get_visibility_partition_info(xblock) partition_info = get_visibility_partition_info(xblock)
selectable_partitions = partition_info["selectable_partitions"] selectable_partitions = partition_info["selectable_partitions"]
...@@ -11,19 +12,31 @@ selected_partition_index = partition_info["selected_partition_index"] ...@@ -11,19 +12,31 @@ selected_partition_index = partition_info["selected_partition_index"]
selected_groups_label = partition_info["selected_groups_label"] selected_groups_label = partition_info["selected_groups_label"]
is_staff_locked = ancestor_has_staff_lock(xblock) is_staff_locked = ancestor_has_staff_lock(xblock)
block_is_unit = is_unit(xblock)
%> %>
<div class="modal-section visibility-summary"> <div class="modal-section visibility-summary">
% if len(selectable_partitions) == 0: % if len(selectable_partitions) == 0:
<div class="is-not-configured has-actions"> <div class="is-not-configured has-actions">
<h3 class="title">${_('Access is not restricted')}</h3> <h3 class="title">${_('Access is not restricted')}</h3>
<div class="copy"> <div class="copy">
% if block_is_unit:
<p>${_('Access to this unit is not restricted, but visibility might be affected by inherited settings.')}</p>
% else:
<p>${_('Access to this component is not restricted, but visibility might be affected by inherited settings.')}</p> <p>${_('Access to this component is not restricted, but visibility might be affected by inherited settings.')}</p>
%endif
% if settings.FEATURES.get('ENABLE_ENROLLMENT_TRACK_USER_PARTITION'): % if settings.FEATURES.get('ENABLE_ENROLLMENT_TRACK_USER_PARTITION'):
% if block_is_unit:
<p>${_('You can restrict access to this unit to learners in specific enrollment tracks or content groups.')}</p>
% else:
<p>${_('You can restrict access to this component to learners in specific enrollment tracks or content groups.')}</p> <p>${_('You can restrict access to this component to learners in specific enrollment tracks or content groups.')}</p>
% endif
% else:
% if block_is_unit:
<p>${_('You can restrict access to this unit to learners in specific content groups.')}</p>
% else: % else:
<p>${_('You can restrict access to this component to learners in specific content groups.')}</p> <p>${_('You can restrict access to this component to learners in specific content groups.')}</p>
% endif % endif
% endif
</div> </div>
<div class="actions"> <div class="actions">
...@@ -98,7 +111,9 @@ is_staff_locked = ancestor_has_staff_lock(xblock) ...@@ -98,7 +111,9 @@ is_staff_locked = ancestor_has_staff_lock(xblock)
% if group["deleted"]: % if group["deleted"]:
<label for="visibility-group-${partition["id"]}-${group["id"]}" class="label"> <label for="visibility-group-${partition["id"]}-${group["id"]}" class="label">
${_("Deleted Group")} ${_("Deleted Group")}
<span class="note">${_('This group no longer exists. Choose another group or do not restrict access to this component.')}</span> <span class="note">
${_('This group no longer exists. Choose another group or remove the access restriction.')}
</span>
</label> </label>
% else: % else:
<label for="visibility-group-${partition["id"]}-${group["id"]}" class="label">${group["name"]}</label> <label for="visibility-group-${partition["id"]}-${group["id"]}" class="label">${group["name"]}</label>
......
...@@ -319,7 +319,7 @@ class BaseGroupConfigurationsTest(ContainerBase): ...@@ -319,7 +319,7 @@ class BaseGroupConfigurationsTest(ContainerBase):
CHOOSE_ONE = "Select a group type" CHOOSE_ONE = "Select a group type"
CONTENT_GROUP_PARTITION = XBlockVisibilityEditorView.CONTENT_GROUP_PARTITION CONTENT_GROUP_PARTITION = XBlockVisibilityEditorView.CONTENT_GROUP_PARTITION
ENROLLMENT_TRACK_PARTITION = XBlockVisibilityEditorView.ENROLLMENT_TRACK_PARTITION ENROLLMENT_TRACK_PARTITION = XBlockVisibilityEditorView.ENROLLMENT_TRACK_PARTITION
MISSING_GROUP_LABEL = 'Deleted Group\nThis group no longer exists. Choose another group or do not restrict access to this component.' MISSING_GROUP_LABEL = 'Deleted Group\nThis group no longer exists. Choose another group or remove the access restriction.'
VALIDATION_ERROR_LABEL = 'This component has validation issues.' VALIDATION_ERROR_LABEL = 'This component has validation issues.'
VALIDATION_ERROR_MESSAGE = "Error:\nThis component's access settings refer to deleted or invalid groups." VALIDATION_ERROR_MESSAGE = "Error:\nThis component's access settings refer to deleted or invalid groups."
GROUP_VISIBILITY_MESSAGE = 'Access to some content in this unit is restricted to specific groups of learners.' GROUP_VISIBILITY_MESSAGE = 'Access to some content in this unit is restricted to specific groups of learners.'
......
...@@ -8,6 +8,7 @@ from xblock.core import XBlock ...@@ -8,6 +8,7 @@ from xblock.core import XBlock
from xblock.fields import Boolean, Dict, Scope, String, XBlockMixin from xblock.fields import Boolean, Dict, Scope, String, XBlockMixin
from xblock.validation import ValidationMessage from xblock.validation import ValidationMessage
from lms.lib.utils import is_unit
from xmodule.modulestore.inheritance import UserPartitionList from xmodule.modulestore.inheritance import UserPartitionList
from xmodule.partitions.partitions import NoSuchUserPartitionError, NoSuchUserPartitionGroupError from xmodule.partitions.partitions import NoSuchUserPartitionError, NoSuchUserPartitionGroupError
...@@ -15,8 +16,16 @@ from xmodule.partitions.partitions import NoSuchUserPartitionError, NoSuchUserPa ...@@ -15,8 +16,16 @@ from xmodule.partitions.partitions import NoSuchUserPartitionError, NoSuchUserPa
# more information can be found here: https://openedx.atlassian.net/browse/PLAT-902 # more information can be found here: https://openedx.atlassian.net/browse/PLAT-902
_ = lambda text: text _ = lambda text: text
INVALID_USER_PARTITION_VALIDATION = _(u"This component's access settings refer to deleted or invalid group configurations.") INVALID_USER_PARTITION_VALIDATION_COMPONENT = _(
INVALID_USER_PARTITION_GROUP_VALIDATION = _(u"This component's access settings refer to deleted or invalid groups.") u"This component's access settings refer to deleted or invalid group configurations."
)
INVALID_USER_PARTITION_VALIDATION_UNIT = _(
u"This unit's access settings refer to deleted or invalid group configurations."
)
INVALID_USER_PARTITION_GROUP_VALIDATION_COMPONENT = _(
u"This component's access settings refer to deleted or invalid groups."
)
INVALID_USER_PARTITION_GROUP_VALIDATION_UNIT = _(u"This unit's access settings refer to deleted or invalid groups.")
NONSENSICAL_ACCESS_RESTRICTION = _(u"This component's access settings contradict its parent's access settings.") NONSENSICAL_ACCESS_RESTRICTION = _(u"This component's access settings contradict its parent's access settings.")
...@@ -32,6 +41,7 @@ class GroupAccessDict(Dict): ...@@ -32,6 +41,7 @@ class GroupAccessDict(Dict):
@XBlock.needs('partitions') @XBlock.needs('partitions')
@XBlock.needs('i18n')
class LmsBlockMixin(XBlockMixin): class LmsBlockMixin(XBlockMixin):
""" """
Mixin that defines fields common to all blocks used in the LMS Mixin that defines fields common to all blocks used in the LMS
...@@ -185,6 +195,7 @@ class LmsBlockMixin(XBlockMixin): ...@@ -185,6 +195,7 @@ class LmsBlockMixin(XBlockMixin):
validation = super(LmsBlockMixin, self).validate() validation = super(LmsBlockMixin, self).validate()
has_invalid_user_partitions = False has_invalid_user_partitions = False
has_invalid_groups = False has_invalid_groups = False
block_is_unit = is_unit(self)
for user_partition_id, group_ids in self.group_access.iteritems(): for user_partition_id, group_ids in self.group_access.iteritems():
try: try:
...@@ -204,7 +215,9 @@ class LmsBlockMixin(XBlockMixin): ...@@ -204,7 +215,9 @@ class LmsBlockMixin(XBlockMixin):
validation.add( validation.add(
ValidationMessage( ValidationMessage(
ValidationMessage.ERROR, ValidationMessage.ERROR,
INVALID_USER_PARTITION_VALIDATION (INVALID_USER_PARTITION_VALIDATION_UNIT
if block_is_unit
else INVALID_USER_PARTITION_VALIDATION_COMPONENT)
) )
) )
...@@ -212,7 +225,9 @@ class LmsBlockMixin(XBlockMixin): ...@@ -212,7 +225,9 @@ class LmsBlockMixin(XBlockMixin):
validation.add( validation.add(
ValidationMessage( ValidationMessage(
ValidationMessage.ERROR, ValidationMessage.ERROR,
INVALID_USER_PARTITION_GROUP_VALIDATION (INVALID_USER_PARTITION_GROUP_VALIDATION_UNIT
if block_is_unit
else INVALID_USER_PARTITION_GROUP_VALIDATION_COMPONENT)
) )
) )
......
...@@ -57,3 +57,11 @@ class LmsUtilsTest(ModuleStoreTestCase): ...@@ -57,3 +57,11 @@ class LmsUtilsTest(ModuleStoreTestCase):
self.assertIsNone(utils.get_parent_unit(self.course)) self.assertIsNone(utils.get_parent_unit(self.course))
self.assertIsNone(utils.get_parent_unit(self.chapter)) self.assertIsNone(utils.get_parent_unit(self.chapter))
self.assertIsNone(utils.get_parent_unit(self.sequential)) self.assertIsNone(utils.get_parent_unit(self.sequential))
def test_is_unit(self):
"""
Tests `is_unit` method for the successful result.
"""
self.assertFalse(utils.is_unit(self.html_module_1))
self.assertFalse(utils.is_unit(self.child_vertical))
self.assertTrue(utils.is_unit(self.vertical))
...@@ -28,3 +28,19 @@ def get_parent_unit(xblock): ...@@ -28,3 +28,19 @@ def get_parent_unit(xblock):
if parent.category == "vertical" and grandparent.category == "sequential": if parent.category == "vertical" and grandparent.category == "sequential":
return parent return parent
xblock = parent xblock = parent
def is_unit(xblock):
"""
Checks whether the xblock is a unit.
Get_parent_unit() returns None if the current xblock either does
not have a parent unit or is itself a unit.
To make sure that get_parent_unit() isn't returning None because
the xblock is an orphan, we check that the xblock has a parent.
Returns:
True if the xblock is itself a unit, False otherwise.
"""
return get_parent_unit(xblock) is None and xblock.get_parent()
...@@ -5,7 +5,11 @@ import ddt ...@@ -5,7 +5,11 @@ import ddt
from nose.plugins.attrib import attr from nose.plugins.attrib import attr
from lms_xblock.mixin import ( from lms_xblock.mixin import (
INVALID_USER_PARTITION_VALIDATION, INVALID_USER_PARTITION_GROUP_VALIDATION, NONSENSICAL_ACCESS_RESTRICTION INVALID_USER_PARTITION_GROUP_VALIDATION_COMPONENT,
INVALID_USER_PARTITION_GROUP_VALIDATION_UNIT,
INVALID_USER_PARTITION_VALIDATION_COMPONENT,
INVALID_USER_PARTITION_VALIDATION_UNIT,
NONSENSICAL_ACCESS_RESTRICTION
) )
from xblock.validation import ValidationMessage from xblock.validation import ValidationMessage
from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore import ModuleStoreEnum
...@@ -92,14 +96,14 @@ class XBlockValidationTest(LmsXBlockMixinTestCase): ...@@ -92,14 +96,14 @@ class XBlockValidationTest(LmsXBlockMixinTestCase):
def test_validate_invalid_user_partitions(self): def test_validate_invalid_user_partitions(self):
""" """
Test the validation messages produced for an xblock referring to non-existent user partitions. Test the validation messages produced for a component referring to non-existent user partitions.
""" """
self.set_group_access(self.video_location, {999: [self.group1.id]}) self.set_group_access(self.video_location, {999: [self.group1.id]})
validation = self.store.get_item(self.video_location).validate() validation = self.store.get_item(self.video_location).validate()
self.assertEqual(len(validation.messages), 1) self.assertEqual(len(validation.messages), 1)
self.verify_validation_message( self.verify_validation_message(
validation.messages[0], validation.messages[0],
INVALID_USER_PARTITION_VALIDATION, INVALID_USER_PARTITION_VALIDATION_COMPONENT,
ValidationMessage.ERROR, ValidationMessage.ERROR,
) )
...@@ -111,7 +115,32 @@ class XBlockValidationTest(LmsXBlockMixinTestCase): ...@@ -111,7 +115,32 @@ class XBlockValidationTest(LmsXBlockMixinTestCase):
self.assertEqual(len(validation.messages), 1) self.assertEqual(len(validation.messages), 1)
self.verify_validation_message( self.verify_validation_message(
validation.messages[0], validation.messages[0],
INVALID_USER_PARTITION_VALIDATION, INVALID_USER_PARTITION_VALIDATION_COMPONENT,
ValidationMessage.ERROR,
)
def test_validate_invalid_user_partitions_unit(self):
"""
Test the validation messages produced for a unit referring to non-existent user partitions.
"""
self.set_group_access(self.vertical_location, {999: [self.group1.id]})
validation = self.store.get_item(self.vertical_location).validate()
self.assertEqual(len(validation.messages), 1)
self.verify_validation_message(
validation.messages[0],
INVALID_USER_PARTITION_VALIDATION_UNIT,
ValidationMessage.ERROR,
)
# Now add a second invalid user partition and validate again.
# Note that even though there are two invalid configurations,
# only a single error message will be returned.
self.set_group_access(self.vertical_location, {998: [self.group2.id]})
validation = self.store.get_item(self.vertical_location).validate()
self.assertEqual(len(validation.messages), 1)
self.verify_validation_message(
validation.messages[0],
INVALID_USER_PARTITION_VALIDATION_UNIT,
ValidationMessage.ERROR, ValidationMessage.ERROR,
) )
...@@ -124,7 +153,7 @@ class XBlockValidationTest(LmsXBlockMixinTestCase): ...@@ -124,7 +153,7 @@ class XBlockValidationTest(LmsXBlockMixinTestCase):
self.assertEqual(len(validation.messages), 1) self.assertEqual(len(validation.messages), 1)
self.verify_validation_message( self.verify_validation_message(
validation.messages[0], validation.messages[0],
INVALID_USER_PARTITION_GROUP_VALIDATION, INVALID_USER_PARTITION_GROUP_VALIDATION_COMPONENT,
ValidationMessage.ERROR, ValidationMessage.ERROR,
) )
...@@ -134,7 +163,7 @@ class XBlockValidationTest(LmsXBlockMixinTestCase): ...@@ -134,7 +163,7 @@ class XBlockValidationTest(LmsXBlockMixinTestCase):
self.assertEqual(len(validation.messages), 1) self.assertEqual(len(validation.messages), 1)
self.verify_validation_message( self.verify_validation_message(
validation.messages[0], validation.messages[0],
INVALID_USER_PARTITION_GROUP_VALIDATION, INVALID_USER_PARTITION_GROUP_VALIDATION_COMPONENT,
ValidationMessage.ERROR, ValidationMessage.ERROR,
) )
...@@ -165,6 +194,19 @@ class XBlockValidationTest(LmsXBlockMixinTestCase): ...@@ -165,6 +194,19 @@ class XBlockValidationTest(LmsXBlockMixinTestCase):
ValidationMessage.ERROR, ValidationMessage.ERROR,
) )
def test_validate_invalid_groups_for_unit(self):
"""
Test the validation messages produced for a unit-level xblock referring to non-existent groups.
"""
self.set_group_access(self.vertical_location, {self.user_partition.id: [self.group1.id, 999]})
validation = self.store.get_item(self.vertical_location).validate()
self.assertEqual(len(validation.messages), 1)
self.verify_validation_message(
validation.messages[0],
INVALID_USER_PARTITION_GROUP_VALIDATION_UNIT,
ValidationMessage.ERROR,
)
def test_validate_nonsensical_access_restriction(self): def test_validate_nonsensical_access_restriction(self):
""" """
Test the validation messages produced for a component whose Test the validation messages produced for a component whose
...@@ -221,7 +263,7 @@ class XBlockValidationTest(LmsXBlockMixinTestCase): ...@@ -221,7 +263,7 @@ class XBlockValidationTest(LmsXBlockMixinTestCase):
self.assertEqual(len(validation.messages), 2) self.assertEqual(len(validation.messages), 2)
self.verify_validation_message( self.verify_validation_message(
validation.messages[0], validation.messages[0],
INVALID_USER_PARTITION_GROUP_VALIDATION, INVALID_USER_PARTITION_GROUP_VALIDATION_COMPONENT,
ValidationMessage.ERROR, ValidationMessage.ERROR,
) )
self.verify_validation_message( self.verify_validation_message(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment