Merge pull request #15037 from edx/christina/ed11-simplification
Feature branch: dividing discussions by enrollment tracks
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import openedx.core.djangoapps.xmodule_django.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('django_comment_common', '0004_auto_20161117_1209'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CourseDiscussionSettings',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('course_id', openedx.core.djangoapps.xmodule_django.models.CourseKeyField(help_text=b'Which course are these settings associated with?', unique=True, max_length=255, db_index=True)),
|
||||
('always_divide_inline_discussions', models.BooleanField(default=False)),
|
||||
('_divided_discussions', models.TextField(null=True, db_column=b'divided_discussions', blank=True)),
|
||||
('division_scheme', models.CharField(default=b'none', max_length=20, choices=[(b'none', b'None'), (b'cohort', b'Cohort'), (b'enrollment_track', b'Enrollment Track')])),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -1,3 +1,4 @@
|
||||
import json
|
||||
import logging
|
||||
|
||||
from config_models.models import ConfigurationModel
|
||||
@@ -162,3 +163,30 @@ class ForumsConfig(ConfigurationModel):
|
||||
def __unicode__(self):
|
||||
"""Simple representation so the admin screen looks less ugly."""
|
||||
return u"ForumsConfig: timeout={}".format(self.connection_timeout)
|
||||
|
||||
|
||||
class CourseDiscussionSettings(models.Model):
|
||||
course_id = CourseKeyField(
|
||||
unique=True,
|
||||
max_length=255,
|
||||
db_index=True,
|
||||
help_text="Which course are these settings associated with?",
|
||||
)
|
||||
always_divide_inline_discussions = models.BooleanField(default=False)
|
||||
_divided_discussions = models.TextField(db_column='divided_discussions', null=True, blank=True) # JSON list
|
||||
|
||||
COHORT = 'cohort'
|
||||
ENROLLMENT_TRACK = 'enrollment_track'
|
||||
NONE = 'none'
|
||||
ASSIGNMENT_TYPE_CHOICES = ((NONE, 'None'), (COHORT, 'Cohort'), (ENROLLMENT_TRACK, 'Enrollment Track'))
|
||||
division_scheme = models.CharField(max_length=20, choices=ASSIGNMENT_TYPE_CHOICES, default=NONE)
|
||||
|
||||
@property
|
||||
def divided_discussions(self):
|
||||
"""Jsonify the divided_discussions"""
|
||||
return json.loads(self._divided_discussions)
|
||||
|
||||
@divided_discussions.setter
|
||||
def divided_discussions(self, value):
|
||||
"""Un-Jsonify the divided_discussions"""
|
||||
self._divided_discussions = json.dumps(value)
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
from nose.plugins.attrib import attr
|
||||
|
||||
from django.test import TestCase
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
|
||||
from django_comment_common.models import Role
|
||||
from models import CourseDiscussionSettings
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from openedx.core.djangoapps.course_groups.cohorts import CourseCohortsSettings
|
||||
from student.models import CourseEnrollment, User
|
||||
from utils import get_course_discussion_settings, set_course_discussion_settings
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
|
||||
@attr(shard=1)
|
||||
class RoleAssignmentTest(TestCase):
|
||||
"""
|
||||
Basic checks to make sure our Roles get assigned and unassigned as students
|
||||
@@ -55,3 +64,73 @@ class RoleAssignmentTest(TestCase):
|
||||
# )
|
||||
# self.assertNotIn(student_role, self.student_user.roles.all())
|
||||
# self.assertIn(student_role, another_student.roles.all())
|
||||
|
||||
|
||||
@attr(shard=1)
|
||||
class CourseDiscussionSettingsTest(ModuleStoreTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(CourseDiscussionSettingsTest, self).setUp()
|
||||
self.course = CourseFactory.create()
|
||||
|
||||
def test_get_course_discussion_settings(self):
|
||||
discussion_settings = get_course_discussion_settings(self.course.id)
|
||||
self.assertEqual(CourseDiscussionSettings.NONE, discussion_settings.division_scheme)
|
||||
self.assertEqual([], discussion_settings.divided_discussions)
|
||||
self.assertFalse(discussion_settings.always_divide_inline_discussions)
|
||||
|
||||
def test_get_course_discussion_settings_legacy_settings(self):
|
||||
self.course.cohort_config = {
|
||||
'cohorted': True,
|
||||
'always_cohort_inline_discussions': True,
|
||||
'cohorted_discussions': ['foo']
|
||||
}
|
||||
modulestore().update_item(self.course, ModuleStoreEnum.UserID.system)
|
||||
discussion_settings = get_course_discussion_settings(self.course.id)
|
||||
self.assertEqual(CourseDiscussionSettings.COHORT, discussion_settings.division_scheme)
|
||||
self.assertEqual(['foo'], discussion_settings.divided_discussions)
|
||||
self.assertTrue(discussion_settings.always_divide_inline_discussions)
|
||||
|
||||
def test_get_course_discussion_settings_cohort_settings(self):
|
||||
CourseCohortsSettings.objects.get_or_create(
|
||||
course_id=self.course.id,
|
||||
defaults={
|
||||
'is_cohorted': True,
|
||||
'always_cohort_inline_discussions': True,
|
||||
'cohorted_discussions': ['foo', 'bar']
|
||||
}
|
||||
)
|
||||
discussion_settings = get_course_discussion_settings(self.course.id)
|
||||
self.assertEqual(CourseDiscussionSettings.COHORT, discussion_settings.division_scheme)
|
||||
self.assertEqual(['foo', 'bar'], discussion_settings.divided_discussions)
|
||||
self.assertTrue(discussion_settings.always_divide_inline_discussions)
|
||||
|
||||
def test_set_course_discussion_settings(self):
|
||||
set_course_discussion_settings(
|
||||
course_key=self.course.id,
|
||||
divided_discussions=['cohorted_topic'],
|
||||
division_scheme=CourseDiscussionSettings.ENROLLMENT_TRACK,
|
||||
always_divide_inline_discussions=True,
|
||||
)
|
||||
discussion_settings = get_course_discussion_settings(self.course.id)
|
||||
self.assertEqual(CourseDiscussionSettings.ENROLLMENT_TRACK, discussion_settings.division_scheme)
|
||||
self.assertEqual(['cohorted_topic'], discussion_settings.divided_discussions)
|
||||
self.assertTrue(discussion_settings.always_divide_inline_discussions)
|
||||
|
||||
def test_invalid_data_types(self):
|
||||
exception_msg_template = "Incorrect field type for `{}`. Type must be `{}`"
|
||||
fields = [
|
||||
{'name': 'division_scheme', 'type': basestring},
|
||||
{'name': 'always_divide_inline_discussions', 'type': bool},
|
||||
{'name': 'divided_discussions', 'type': list}
|
||||
]
|
||||
invalid_value = 3.14
|
||||
|
||||
for field in fields:
|
||||
with self.assertRaises(ValueError) as value_error:
|
||||
set_course_discussion_settings(self.course.id, **{field['name']: invalid_value})
|
||||
|
||||
self.assertEqual(
|
||||
value_error.exception.message,
|
||||
exception_msg_template.format(field['name'], field['type'].__name__)
|
||||
)
|
||||
|
||||
@@ -9,6 +9,10 @@ from django_comment_common.models import (
|
||||
FORUM_ROLE_STUDENT,
|
||||
Role
|
||||
)
|
||||
from openedx.core.djangoapps.course_groups.cohorts import get_legacy_discussion_settings
|
||||
from request_cache.middleware import request_cached
|
||||
|
||||
from .models import CourseDiscussionSettings
|
||||
|
||||
|
||||
class ThreadContext(object):
|
||||
@@ -91,3 +95,48 @@ def are_permissions_roles_seeded(course_id):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@request_cached
|
||||
def get_course_discussion_settings(course_key):
|
||||
try:
|
||||
course_discussion_settings = CourseDiscussionSettings.objects.get(course_id=course_key)
|
||||
except CourseDiscussionSettings.DoesNotExist:
|
||||
legacy_discussion_settings = get_legacy_discussion_settings(course_key)
|
||||
course_discussion_settings, _ = CourseDiscussionSettings.objects.get_or_create(
|
||||
course_id=course_key,
|
||||
defaults={
|
||||
'always_divide_inline_discussions': legacy_discussion_settings['always_cohort_inline_discussions'],
|
||||
'divided_discussions': legacy_discussion_settings['cohorted_discussions'],
|
||||
'division_scheme': CourseDiscussionSettings.COHORT if legacy_discussion_settings['is_cohorted']
|
||||
else CourseDiscussionSettings.NONE
|
||||
}
|
||||
)
|
||||
|
||||
return course_discussion_settings
|
||||
|
||||
|
||||
def set_course_discussion_settings(course_key, **kwargs):
|
||||
"""
|
||||
Set discussion settings for a course.
|
||||
|
||||
Arguments:
|
||||
course_key: CourseKey
|
||||
always_divide_inline_discussions (bool): If inline discussions should always be divided.
|
||||
divided_discussions (list): List of discussion ids.
|
||||
division_scheme (str): `CourseDiscussionSettings.NONE`, `CourseDiscussionSettings.COHORT`,
|
||||
or `CourseDiscussionSettings.ENROLLMENT_TRACK`
|
||||
|
||||
Returns:
|
||||
A CourseDiscussionSettings object.
|
||||
"""
|
||||
fields = {'division_scheme': basestring, 'always_divide_inline_discussions': bool, 'divided_discussions': list}
|
||||
course_discussion_settings = get_course_discussion_settings(course_key)
|
||||
for field, field_type in fields.items():
|
||||
if field in kwargs:
|
||||
if not isinstance(kwargs[field], field_type):
|
||||
raise ValueError("Incorrect field type for `{}`. Type must be `{}`".format(field, field_type.__name__))
|
||||
setattr(course_discussion_settings, field, kwargs[field])
|
||||
|
||||
course_discussion_settings.save()
|
||||
return course_discussion_settings
|
||||
|
||||
@@ -133,7 +133,7 @@ class PartitionService(object):
|
||||
if self._cache and (cache_key in self._cache):
|
||||
return self._cache[cache_key]
|
||||
|
||||
user_partition = self._get_user_partition(user_partition_id)
|
||||
user_partition = self.get_user_partition(user_partition_id)
|
||||
if user_partition is None:
|
||||
raise ValueError(
|
||||
"Configuration problem! No user_partition with id {0} "
|
||||
@@ -148,7 +148,7 @@ class PartitionService(object):
|
||||
|
||||
return group_id
|
||||
|
||||
def _get_user_partition(self, user_partition_id):
|
||||
def get_user_partition(self, user_partition_id):
|
||||
"""
|
||||
Look for a user partition with a matching id in the course's partitions.
|
||||
Note that this method can return an inactive user partition.
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
this.retrieveFollowed = function() {
|
||||
return DiscussionThreadListView.prototype.retrieveFollowed.apply(self, arguments);
|
||||
};
|
||||
this.chooseCohort = function() {
|
||||
return DiscussionThreadListView.prototype.chooseCohort.apply(self, arguments);
|
||||
this.chooseGroup = function() {
|
||||
return DiscussionThreadListView.prototype.chooseGroup.apply(self, arguments);
|
||||
};
|
||||
this.chooseFilter = function() {
|
||||
return DiscussionThreadListView.prototype.chooseFilter.apply(self, arguments);
|
||||
@@ -85,7 +85,7 @@
|
||||
'click .forum-nav-thread-link': 'threadSelected',
|
||||
'click .forum-nav-load-more-link': 'loadMorePages',
|
||||
'change .forum-nav-filter-main-control': 'chooseFilter',
|
||||
'change .forum-nav-filter-cohort-control': 'chooseCohort'
|
||||
'change .forum-nav-filter-cohort-control': 'chooseGroup'
|
||||
};
|
||||
|
||||
DiscussionThreadListView.prototype.initialize = function(options) {
|
||||
@@ -194,7 +194,7 @@
|
||||
edx.HtmlUtils.append(
|
||||
this.$el,
|
||||
this.template({
|
||||
isCohorted: this.courseSettings.get('is_cohorted'),
|
||||
isDiscussionDivisionEnabled: this.courseSettings.get('is_discussion_division_enabled'),
|
||||
isPrivilegedUser: DiscussionUtil.isPrivilegedUser()
|
||||
})
|
||||
);
|
||||
@@ -404,7 +404,7 @@
|
||||
return $(elem).data('discussion-id');
|
||||
}).get();
|
||||
this.retrieveDiscussions(discussionIds);
|
||||
return this.$('.forum-nav-filter-cohort').toggle($item.data('cohorted') === true);
|
||||
return this.$('.forum-nav-filter-cohort').toggle($item.data('divided') === true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -413,7 +413,7 @@
|
||||
return this.retrieveFirstPage();
|
||||
};
|
||||
|
||||
DiscussionThreadListView.prototype.chooseCohort = function() {
|
||||
DiscussionThreadListView.prototype.chooseGroup = function() {
|
||||
this.group_id = this.$('.forum-nav-filter-cohort-control :selected').val();
|
||||
return this.retrieveFirstPage();
|
||||
};
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
'[data-discussion-id="' + this.getCurrentTopicId() + '"]'
|
||||
));
|
||||
} else if ($general.length > 0) {
|
||||
this.setTopic($general);
|
||||
this.setTopic($general.first());
|
||||
} else {
|
||||
this.setTopic(this.$('.post-topic option').first());
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
threadTypeTemplate;
|
||||
context = _.clone(this.course_settings.attributes);
|
||||
_.extend(context, {
|
||||
cohort_options: this.getCohortOptions(),
|
||||
group_options: this.getGroupOptions(),
|
||||
is_commentable_divided: this.is_commentable_divided,
|
||||
mode: this.mode,
|
||||
startHeader: this.startHeader,
|
||||
@@ -84,15 +84,15 @@
|
||||
return this.mode === 'tab';
|
||||
};
|
||||
|
||||
NewPostView.prototype.getCohortOptions = function() {
|
||||
NewPostView.prototype.getGroupOptions = function() {
|
||||
var userGroupId;
|
||||
if (this.course_settings.get('is_cohorted') && DiscussionUtil.isPrivilegedUser()) {
|
||||
if (this.course_settings.get('is_discussion_division_enabled') && DiscussionUtil.isPrivilegedUser()) {
|
||||
userGroupId = $('#discussion-container').data('user-group-id');
|
||||
return _.map(this.course_settings.get('cohorts'), function(cohort) {
|
||||
return _.map(this.course_settings.get('groups'), function(group) {
|
||||
return {
|
||||
value: cohort.id,
|
||||
text: cohort.name,
|
||||
selected: cohort.id === userGroupId
|
||||
value: group.id,
|
||||
text: group.name,
|
||||
selected: group.id === userGroupId
|
||||
};
|
||||
});
|
||||
} else {
|
||||
@@ -112,7 +112,7 @@
|
||||
};
|
||||
|
||||
NewPostView.prototype.toggleGroupDropdown = function($target) {
|
||||
if ($target.data('cohorted')) {
|
||||
if ($target.data('divided')) {
|
||||
$('.js-group-select').prop('disabled', false);
|
||||
return $('.group-selector-wrapper').removeClass('disabled');
|
||||
} else {
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
' <li' +
|
||||
' class="forum-nav-browse-menu-item"' +
|
||||
' data-discussion-id="child"' +
|
||||
' data-cohorted="false"' +
|
||||
' data-divided="false"' +
|
||||
' >' +
|
||||
' <a href="#" class="forum-nav-browse-title">Child</a>' +
|
||||
' </li>' +
|
||||
@@ -70,7 +70,7 @@
|
||||
' <li' +
|
||||
' class="forum-nav-browse-menu-item"' +
|
||||
' data-discussion-id="sibling"' +
|
||||
' data-cohorted="false"' +
|
||||
' data-divided="false"' +
|
||||
' >' +
|
||||
' <a href="#" class="forum-nav-browse-title">Sibling</a>' +
|
||||
' </li>' +
|
||||
@@ -79,7 +79,7 @@
|
||||
' <li' +
|
||||
' class="forum-nav-browse-menu-item"' +
|
||||
' data-discussion-id="other"' +
|
||||
' data-cohorted="true"' +
|
||||
' data-divided="true"' +
|
||||
' >' +
|
||||
' <a href="#" class="forum-nav-browse-title">Other Category</a>' +
|
||||
' </li>' +
|
||||
@@ -95,11 +95,11 @@
|
||||
' <option value="flagged">Flagged</option>' +
|
||||
' </select>' +
|
||||
' </label>' +
|
||||
' <% if (isCohorted && isPrivilegedUser) { %>' +
|
||||
' <% if (isDiscussionDivisionEnabled && isPrivilegedUser) { %>' +
|
||||
' <label class="forum-nav-filter-cohort">' +
|
||||
' <span class="sr">Cohort:</span>' +
|
||||
' <span class="sr">Group:</span>' +
|
||||
' <select class="forum-nav-filter-cohort-control">' +
|
||||
' <option value="">in all cohorts</option>' +
|
||||
' <option value="">in all groups</option>' +
|
||||
' <option value="1">Cohort1</option>' +
|
||||
' <option value="2">Cohort2</option>' +
|
||||
' </select>' +
|
||||
@@ -164,7 +164,7 @@
|
||||
collection: this.discussion,
|
||||
el: $('#fixture-element'),
|
||||
courseSettings: new DiscussionCourseSettings({
|
||||
is_cohorted: true
|
||||
is_discussion_division_enabled: true
|
||||
})
|
||||
});
|
||||
return this.view.render();
|
||||
@@ -199,7 +199,7 @@
|
||||
collection: discussion,
|
||||
showThreadPreview: true,
|
||||
courseSettings: new DiscussionCourseSettings({
|
||||
is_cohorted: true
|
||||
is_discussion_division_enabled: true
|
||||
})
|
||||
},
|
||||
props
|
||||
@@ -233,7 +233,7 @@
|
||||
});
|
||||
});
|
||||
|
||||
describe('cohort selector', function() {
|
||||
describe('group selector', function() {
|
||||
it('should not be visible to students', function() {
|
||||
return expect(this.view.$('.forum-nav-filter-cohort-control:visible')).not.toExist();
|
||||
});
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
return expect(group_disabled).toEqual(true);
|
||||
}
|
||||
};
|
||||
describe('cohort selector', function() {
|
||||
describe('group selector', function() {
|
||||
beforeEach(function() {
|
||||
this.course_settings = new DiscussionCourseSettings({
|
||||
category_map: {
|
||||
@@ -53,8 +53,8 @@
|
||||
},
|
||||
allow_anonymous: false,
|
||||
allow_anonymous_to_peers: false,
|
||||
is_cohorted: true,
|
||||
cohorts: [
|
||||
is_discussion_division_enabled: true,
|
||||
groups: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Cohort1'
|
||||
@@ -75,15 +75,15 @@
|
||||
it('is not visible to students', function() {
|
||||
return checkVisibility(this.view, false, false, true);
|
||||
});
|
||||
it('allows TAs to see the cohort selector when the topic is cohorted', function() {
|
||||
it('allows TAs to see the group selector when the topic is divided', function() {
|
||||
DiscussionSpecHelper.makeTA();
|
||||
return checkVisibility(this.view, true, false, true);
|
||||
});
|
||||
it('allows moderators to see the cohort selector when the topic is cohorted', function() {
|
||||
it('allows moderators to see the group selector when the topic is divided', function() {
|
||||
DiscussionSpecHelper.makeModerator();
|
||||
return checkVisibility(this.view, true, false, true);
|
||||
});
|
||||
it('only enables the cohort selector when applicable', function() {
|
||||
it('only enables the group selector when applicable', function() {
|
||||
DiscussionSpecHelper.makeModerator();
|
||||
checkVisibility(this.view, true, false, true);
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
$('.post-topic').trigger('change');
|
||||
return checkVisibility(this.view, true, false, false);
|
||||
});
|
||||
it('allows the user to make a cohort selection', function() {
|
||||
it('allows the user to make a group selection', function() {
|
||||
var expectedGroupId,
|
||||
self = this;
|
||||
DiscussionSpecHelper.makeModerator();
|
||||
@@ -116,23 +116,23 @@
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('always cohort inline discussions ', function() {
|
||||
describe('always divide inline discussions ', function() {
|
||||
beforeEach(function() {
|
||||
this.course_settings = new DiscussionCourseSettings({
|
||||
'category_map': {
|
||||
'children': [],
|
||||
'entries': {}
|
||||
category_map: {
|
||||
children: [],
|
||||
entries: {}
|
||||
},
|
||||
'allow_anonymous': false,
|
||||
'allow_anonymous_to_peers': false,
|
||||
'is_cohorted': true,
|
||||
'cohorts': [
|
||||
allow_anonymous: false,
|
||||
allow_anonymous_to_peers: false,
|
||||
is_discussion_division_enabled: true,
|
||||
groups: [
|
||||
{
|
||||
'id': 1,
|
||||
'name': 'Cohort1'
|
||||
id: 1,
|
||||
name: 'Cohort1'
|
||||
}, {
|
||||
'id': 2,
|
||||
'name': 'Cohort2'
|
||||
id: 2,
|
||||
name: 'Cohort2'
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -143,12 +143,12 @@
|
||||
mode: 'tab'
|
||||
});
|
||||
});
|
||||
it('disables the cohort menu if it is set false', function() {
|
||||
it('disables the group menu if it is set false', function() {
|
||||
DiscussionSpecHelper.makeModerator();
|
||||
this.view.is_commentable_divided = false;
|
||||
return checkVisibility(this.view, true, true, true);
|
||||
});
|
||||
it('enables the cohort menu if it is set true', function() {
|
||||
it('enables the group menu if it is set true', function() {
|
||||
DiscussionSpecHelper.makeModerator();
|
||||
this.view.is_commentable_divided = true;
|
||||
return checkVisibility(this.view, true, false, true);
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
is_cohorted: true,
|
||||
is_discussion_division_enabled: true,
|
||||
allow_anonymous: false,
|
||||
allow_anonymous_to_peers: false
|
||||
},
|
||||
@@ -170,7 +170,7 @@
|
||||
' <li' +
|
||||
' class="forum-nav-browse-menu-item"' +
|
||||
' data-discussion-id="child"' +
|
||||
' data-cohorted="false"' +
|
||||
' data-divided="false"' +
|
||||
' >' +
|
||||
' <a href="#" class="forum-nav-browse-title">Child</a>' +
|
||||
' </li>' +
|
||||
@@ -178,7 +178,7 @@
|
||||
' <li' +
|
||||
' class="forum-nav-browse-menu-item"' +
|
||||
' data-discussion-id="sibling"' +
|
||||
' data-cohorted="false"' +
|
||||
' data-divided="false"' +
|
||||
' >' +
|
||||
' <a href="#" class="forum-nav-browse-title">Sibling</a>' +
|
||||
' </li>' +
|
||||
@@ -187,7 +187,7 @@
|
||||
' <li' +
|
||||
' class="forum-nav-browse-menu-item"' +
|
||||
' data-discussion-id="other"' +
|
||||
' data-cohorted="true"' +
|
||||
' data-divided="true"' +
|
||||
' >' +
|
||||
' <a href="#" class="forum-nav-browse-title">Other Category</a>' +
|
||||
' </li>' +
|
||||
@@ -203,11 +203,11 @@
|
||||
' <option value="flagged">Flagged</option>' +
|
||||
' </select>' +
|
||||
' </label>' +
|
||||
' <% if (isCohorted && isPrivilegedUser) { %>' +
|
||||
' <% if (isDiscussionDivisionEnabled && isPrivilegedUser) { %>' +
|
||||
' <label class="forum-nav-filter-cohort">' +
|
||||
' <span class="sr">Cohort:</span>' +
|
||||
' <span class="sr">Group:</span>' +
|
||||
' <select class="forum-nav-filter-cohort-control">' +
|
||||
' <option value="">in all cohorts</option>' +
|
||||
' <option value="">in all groups</option>' +
|
||||
' <option value="1">Cohort1</option>' +
|
||||
' <option value="2">Cohort2</option>' +
|
||||
' </select>' +
|
||||
|
||||
@@ -1 +1 @@
|
||||
<option class="topic-title" data-discussion-id="<%- id %>" data-cohorted="<%- is_divided %>"><%- text %></option>
|
||||
<option class="topic-title" data-discussion-id="<%- id %>" data-divided="<%- is_divided %>"><%- text %></option>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<% } %>
|
||||
<ul class="post-errors" style="display: none"></ul>
|
||||
<div class="forum-new-post-form-wrapper"></div>
|
||||
<% if (cohort_options) { %>
|
||||
<% if (group_options) { %>
|
||||
<div class="post-field group-selector-wrapper <% if (!is_commentable_divided) { print('disabled'); } %>">
|
||||
<label class="field-label">
|
||||
<span class="field-label-text">
|
||||
@@ -17,12 +17,12 @@
|
||||
<%- gettext("Visible to") %>
|
||||
</span>
|
||||
<div class="field-help" id="field_help_visible_to">
|
||||
<%- gettext("Discussion admins, moderators, and TAs can make their posts visible to all students or specify a single cohort.") %>
|
||||
<%- gettext("Discussion admins, moderators, and TAs can make their posts visible to all students or specify a single group.") %>
|
||||
</div>
|
||||
<div class="field-input">
|
||||
<select aria-describedby="field_help_visible_to" class="post-topic field-input js-group-select" name="group_id" <% if (!is_commentable_divided) { print("disabled"); } %>>
|
||||
<option value=""><%- gettext("All Groups") %></option>
|
||||
<% _.each(cohort_options, function(opt) { %>
|
||||
<% _.each(group_options, function(opt) { %>
|
||||
<option value="<%- opt.value %>" <% if (opt.selected) { print("selected"); } %>><%- opt.text %></option>
|
||||
<% }); %>
|
||||
</select>
|
||||
|
||||
@@ -47,6 +47,21 @@ class InstructorDashboardPage(CoursePage):
|
||||
cohort_management_section.wait_for_page()
|
||||
return cohort_management_section
|
||||
|
||||
def select_discussion_management(self):
|
||||
"""
|
||||
Selects the Discussion tab and returns the DiscussionmanagementSection
|
||||
"""
|
||||
self.q(css='[data-section="discussions_management"').first.click()
|
||||
discussion_management_section = DiscussionManagementSection(self.browser)
|
||||
discussion_management_section.wait_for_page()
|
||||
return discussion_management_section
|
||||
|
||||
def is_discussion_management_visible(self):
|
||||
"""
|
||||
Is the Discussion tab visible
|
||||
"""
|
||||
return self.q(css='[data-section="discussions_management"').visible
|
||||
|
||||
def select_data_download(self):
|
||||
"""
|
||||
Selects the data download tab and returns a DataDownloadPage.
|
||||
@@ -254,10 +269,6 @@ class CohortManagementSection(PageObject):
|
||||
no_content_group_button_css = '.cohort-management-details-association-course input.radio-no'
|
||||
select_content_group_button_css = '.cohort-management-details-association-course input.radio-yes'
|
||||
assignment_type_buttons_css = '.cohort-management-assignment-type-settings input'
|
||||
discussion_form_selectors = {
|
||||
'course-wide': '.cohort-course-wide-discussions-form',
|
||||
'inline': '.cohort-inline-discussions-form'
|
||||
}
|
||||
|
||||
def get_cohort_help_element_and_click_help(self):
|
||||
"""
|
||||
@@ -666,61 +677,36 @@ class CohortManagementSection(PageObject):
|
||||
self.q(css=self._bounded_selector('.cohorts-state')).first.click()
|
||||
self.wait_for_ajax()
|
||||
|
||||
def toggles_showing_of_discussion_topics(self):
|
||||
def cohort_management_controls_visible(self):
|
||||
"""
|
||||
Shows the discussion topics.
|
||||
Return the visibility status of cohort management controls(cohort selector section etc).
|
||||
"""
|
||||
self.q(css=self._bounded_selector(".toggle-cohort-management-discussions")).first.click()
|
||||
self.wait_for_element_visibility("#cohort-discussions-management", "Waiting for discussions to appear")
|
||||
return (self.q(css=self._bounded_selector('.cohort-management-nav')).visible and
|
||||
self.q(css=self._bounded_selector('.wrapper-cohort-supplemental')).visible)
|
||||
|
||||
def discussion_topics_visible(self):
|
||||
"""
|
||||
Returns the visibility status of cohort discussion controls.
|
||||
"""
|
||||
EmptyPromise(
|
||||
lambda: self.q(css=self._bounded_selector('.cohort-discussions-nav')).results != 0,
|
||||
"Waiting for discussion section to show"
|
||||
).fulfill()
|
||||
|
||||
return (self.q(css=self._bounded_selector('.cohort-course-wide-discussions-nav')).visible and
|
||||
self.q(css=self._bounded_selector('.cohort-inline-discussions-nav')).visible)
|
||||
class DiscussionManagementSection(PageObject):
|
||||
|
||||
def select_discussion_topic(self, key):
|
||||
"""
|
||||
Selects discussion topic checkbox by clicking on it.
|
||||
"""
|
||||
self.q(css=self._bounded_selector(".check-discussion-subcategory-%s" % key)).first.click()
|
||||
url = None
|
||||
|
||||
def select_always_inline_discussion(self):
|
||||
"""
|
||||
Selects the always_cohort_inline_discussions radio button.
|
||||
"""
|
||||
self.q(css=self._bounded_selector(".check-all-inline-discussions")).first.click()
|
||||
discussion_form_selectors = {
|
||||
'course-wide': '.cohort-course-wide-discussions-form',
|
||||
'inline': '.cohort-inline-discussions-form',
|
||||
'scheme': '.division-scheme-container',
|
||||
}
|
||||
|
||||
def always_inline_discussion_selected(self):
|
||||
"""
|
||||
Returns true if always_cohort_inline_discussions radio button is selected.
|
||||
"""
|
||||
return len(self.q(css=self._bounded_selector(".check-all-inline-discussions:checked"))) > 0
|
||||
NOT_DIVIDED_SCHEME = "none"
|
||||
COHORT_SCHEME = "cohort"
|
||||
ENROLLMENT_TRACK_SCHEME = "enrollment_track"
|
||||
|
||||
def cohort_some_inline_discussion_selected(self):
|
||||
"""
|
||||
Returns true if some_cohort_inline_discussions radio button is selected.
|
||||
"""
|
||||
return len(self.q(css=self._bounded_selector(".check-cohort-inline-discussions:checked"))) > 0
|
||||
def is_browser_on_page(self):
|
||||
return self.q(css=self.discussion_form_selectors['course-wide']).present
|
||||
|
||||
def select_cohort_some_inline_discussion(self):
|
||||
def _bounded_selector(self, selector):
|
||||
"""
|
||||
Selects the cohort_some_inline_discussions radio button.
|
||||
Return `selector`, but limited to the divided discussion management context.
|
||||
"""
|
||||
self.q(css=self._bounded_selector(".check-cohort-inline-discussions")).first.click()
|
||||
|
||||
def inline_discussion_topics_disabled(self):
|
||||
"""
|
||||
Returns the status of inline discussion topics, enabled or disabled.
|
||||
"""
|
||||
inline_topics = self.q(css=self._bounded_selector('.check-discussion-subcategory-inline'))
|
||||
return all(topic.get_attribute('disabled') == 'true' for topic in inline_topics)
|
||||
return '.discussions-management {}'.format(selector)
|
||||
|
||||
def is_save_button_disabled(self, key):
|
||||
"""
|
||||
@@ -730,18 +716,36 @@ class CohortManagementSection(PageObject):
|
||||
disabled = self.q(css=self._bounded_selector(save_button_css)).attrs('disabled')
|
||||
return disabled[0] == 'true'
|
||||
|
||||
def is_category_selected(self):
|
||||
def discussion_topics_visible(self):
|
||||
"""
|
||||
Returns the status for category checkboxes.
|
||||
Returns the visibility status of divide discussion controls.
|
||||
"""
|
||||
return self.q(css=self._bounded_selector('.check-discussion-category:checked')).is_present()
|
||||
return (self.q(css=self._bounded_selector('.course-wide-discussions-nav')).visible and
|
||||
self.q(css=self._bounded_selector('.inline-discussions-nav')).visible)
|
||||
|
||||
def get_cohorted_topics_count(self, key):
|
||||
def divided_discussion_heading_is_visible(self, key):
|
||||
"""
|
||||
Returns the count for cohorted topics.
|
||||
Returns the text of discussion topic headings if it exists, otherwise return False.
|
||||
"""
|
||||
cohorted_topics = self.q(css=self._bounded_selector('.check-discussion-subcategory-%s:checked' % key))
|
||||
return len(cohorted_topics.results)
|
||||
form_heading_css = '%s %s' % (self.discussion_form_selectors[key], '.subsection-title')
|
||||
discussion_heading = self.q(css=self._bounded_selector(form_heading_css))
|
||||
|
||||
if len(discussion_heading) == 0:
|
||||
return False
|
||||
return discussion_heading.first.text[0]
|
||||
|
||||
def select_always_inline_discussion(self):
|
||||
"""
|
||||
Selects the always_divide_inline_discussions radio button.
|
||||
"""
|
||||
self.q(css=self._bounded_selector(".check-all-inline-discussions")).first.click()
|
||||
|
||||
def inline_discussion_topics_disabled(self):
|
||||
"""
|
||||
Returns the status of inline discussion topics, enabled or disabled.
|
||||
"""
|
||||
inline_topics = self.q(css=self._bounded_selector('.check-discussion-subcategory-inline'))
|
||||
return all(topic.get_attribute('disabled') == 'true' for topic in inline_topics)
|
||||
|
||||
def save_discussion_topics(self, key):
|
||||
"""
|
||||
@@ -750,7 +754,38 @@ class CohortManagementSection(PageObject):
|
||||
save_button_css = '%s %s' % (self.discussion_form_selectors[key], '.action-save')
|
||||
self.q(css=self._bounded_selector(save_button_css)).first.click()
|
||||
|
||||
def get_cohort_discussions_message(self, key, msg_type="confirmation"):
|
||||
def always_inline_discussion_selected(self):
|
||||
"""
|
||||
Returns true if always_divide_inline_discussions radio button is selected.
|
||||
"""
|
||||
return len(self.q(css=self._bounded_selector(".check-all-inline-discussions:checked"))) > 0
|
||||
|
||||
def divide_some_inline_discussion_selected(self):
|
||||
"""
|
||||
Returns true if divide_some_inline_discussions radio button is selected.
|
||||
"""
|
||||
return len(self.q(css=self._bounded_selector(".check-cohort-inline-discussions:checked"))) > 0
|
||||
|
||||
def select_divide_some_inline_discussion(self):
|
||||
"""
|
||||
Selects the divide_some_inline_discussions radio button.
|
||||
"""
|
||||
self.q(css=self._bounded_selector(".check-cohort-inline-discussions")).first.click()
|
||||
|
||||
def get_divided_topics_count(self, key):
|
||||
"""
|
||||
Returns the count for divided topics.
|
||||
"""
|
||||
divided_topics = self.q(css=self._bounded_selector('.check-discussion-subcategory-%s:checked' % key))
|
||||
return len(divided_topics.results)
|
||||
|
||||
def select_discussion_topic(self, key):
|
||||
"""
|
||||
Selects discussion topic checkbox by clicking on it.
|
||||
"""
|
||||
self.q(css=self._bounded_selector(".check-discussion-subcategory-%s" % key)).first.click()
|
||||
|
||||
def get_divide_discussions_message(self, key, msg_type="confirmation"):
|
||||
"""
|
||||
Returns the message related to modifying discussion topics.
|
||||
"""
|
||||
@@ -767,23 +802,30 @@ class CohortManagementSection(PageObject):
|
||||
return ''
|
||||
return message_title.first.text[0]
|
||||
|
||||
def cohort_discussion_heading_is_visible(self, key):
|
||||
def is_category_selected(self):
|
||||
"""
|
||||
Returns the visibility of discussion topic headings.
|
||||
Returns the status for category checkboxes.
|
||||
"""
|
||||
form_heading_css = '%s %s' % (self.discussion_form_selectors[key], '.subsection-title')
|
||||
discussion_heading = self.q(css=self._bounded_selector(form_heading_css))
|
||||
return self.q(css=self._bounded_selector('.check-discussion-category:checked')).is_present()
|
||||
|
||||
if len(discussion_heading) == 0:
|
||||
return False
|
||||
return discussion_heading.first.text[0]
|
||||
def get_selected_scheme(self):
|
||||
"""
|
||||
Returns the ID of the selected discussion division scheme
|
||||
("NOT_DIVIDED_SCHEME", "COHORT_SCHEME", or "ENROLLMENT_TRACK_SCHEME)".
|
||||
"""
|
||||
return self.q(css=self._bounded_selector('.division-scheme:checked')).first.attrs('value')[0]
|
||||
|
||||
def cohort_management_controls_visible(self):
|
||||
def select_division_scheme(self, scheme):
|
||||
"""
|
||||
Return the visibility status of cohort management controls(cohort selector section etc).
|
||||
Selects the radio button associated with the specified division scheme.
|
||||
"""
|
||||
return (self.q(css=self._bounded_selector('.cohort-management-nav')).visible and
|
||||
self.q(css=self._bounded_selector('.wrapper-cohort-supplemental')).visible)
|
||||
self.q(css=self._bounded_selector("input.%s" % scheme)).first.click()
|
||||
|
||||
def division_scheme_visible(self, scheme):
|
||||
"""
|
||||
Returns whether or not the specified scheme is visible as an option.
|
||||
"""
|
||||
return self.q(css=self._bounded_selector("input.%s" % scheme)).visible
|
||||
|
||||
|
||||
class MembershipPageAutoEnrollSection(PageObject):
|
||||
|
||||
@@ -76,17 +76,26 @@ class CohortTestMixin(object):
|
||||
|
||||
def enable_cohorting(self, course_fixture):
|
||||
"""
|
||||
enables cohorting for the current course fixture.
|
||||
Enables cohorting for the specified course fixture.
|
||||
"""
|
||||
url = LMS_BASE_URL + "/courses/" + course_fixture._course_key + '/cohorts/settings' # pylint: disable=protected-access
|
||||
data = json.dumps({'always_cohort_inline_discussions': True})
|
||||
url = LMS_BASE_URL + "/courses/" + course_fixture._course_key + '/cohorts/settings'
|
||||
data = json.dumps({'is_cohorted': True})
|
||||
response = course_fixture.session.patch(url, data=data, headers=course_fixture.headers)
|
||||
self.assertTrue(response.ok, "Failed to enable cohorts")
|
||||
|
||||
def enable_always_divide_inline_discussions(self, course_fixture):
|
||||
"""
|
||||
Enables "always_divide_inline_discussions" (but does not enabling cohorting).
|
||||
"""
|
||||
discussions_url = LMS_BASE_URL + "/courses/" + course_fixture._course_key + '/discussions/settings'
|
||||
discussions_data = json.dumps({'always_divide_inline_discussions': True})
|
||||
course_fixture.session.patch(discussions_url, data=discussions_data, headers=course_fixture.headers)
|
||||
|
||||
def disable_cohorting(self, course_fixture):
|
||||
"""
|
||||
Disables cohorting for the current course fixture.
|
||||
Disables cohorting for the specified course fixture.
|
||||
"""
|
||||
url = LMS_BASE_URL + "/courses/" + course_fixture._course_key + '/cohorts/settings' # pylint: disable=protected-access
|
||||
url = LMS_BASE_URL + "/courses/" + course_fixture._course_key + '/cohorts/settings'
|
||||
data = json.dumps({'is_cohorted': False})
|
||||
response = course_fixture.session.patch(url, data=data, headers=course_fixture.headers)
|
||||
self.assertTrue(response.ok, "Failed to disable cohorts")
|
||||
|
||||
@@ -693,267 +693,6 @@ class CohortConfigurationTest(EventsTestMixin, UniqueCourseTest, CohortTestMixin
|
||||
self.cohort_management_page.a11y_audit.check_for_accessibility_errors()
|
||||
|
||||
|
||||
@attr(shard=6)
|
||||
class CohortDiscussionTopicsTest(UniqueCourseTest, CohortTestMixin):
|
||||
"""
|
||||
Tests for cohorting the inline and course-wide discussion topics.
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up a discussion topics
|
||||
"""
|
||||
super(CohortDiscussionTopicsTest, self).setUp()
|
||||
|
||||
self.discussion_id = "test_discussion_{}".format(uuid.uuid4().hex)
|
||||
self.course_fixture = CourseFixture(**self.course_info).add_children(
|
||||
XBlockFixtureDesc("chapter", "Test Section").add_children(
|
||||
XBlockFixtureDesc("sequential", "Test Subsection").add_children(
|
||||
XBlockFixtureDesc("vertical", "Test Unit").add_children(
|
||||
XBlockFixtureDesc(
|
||||
"discussion",
|
||||
"Test Discussion",
|
||||
metadata={"discussion_id": self.discussion_id}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
).install()
|
||||
|
||||
# create course with single cohort and two content groups (user_partition of type "cohort")
|
||||
self.cohort_name = "OnlyCohort"
|
||||
self.setup_cohort_config(self.course_fixture)
|
||||
self.cohort_id = self.add_manual_cohort(self.course_fixture, self.cohort_name)
|
||||
|
||||
# login as an instructor
|
||||
self.instructor_name = "instructor_user"
|
||||
self.instructor_id = AutoAuthPage(
|
||||
self.browser, username=self.instructor_name, email="instructor_user@example.com",
|
||||
course_id=self.course_id, staff=True
|
||||
).visit().get_user_id()
|
||||
|
||||
# go to the membership page on the instructor dashboard
|
||||
self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
|
||||
self.instructor_dashboard_page.visit()
|
||||
self.cohort_management_page = self.instructor_dashboard_page.select_cohort_management()
|
||||
self.cohort_management_page.wait_for_page()
|
||||
|
||||
self.course_wide_key = 'course-wide'
|
||||
self.inline_key = 'inline'
|
||||
|
||||
def cohort_discussion_topics_are_visible(self):
|
||||
"""
|
||||
Assert that discussion topics are visible with appropriate content.
|
||||
"""
|
||||
self.cohort_management_page.toggles_showing_of_discussion_topics()
|
||||
self.assertTrue(self.cohort_management_page.discussion_topics_visible())
|
||||
|
||||
self.assertEqual(
|
||||
"Course-Wide Discussion Topics",
|
||||
self.cohort_management_page.cohort_discussion_heading_is_visible(self.course_wide_key)
|
||||
)
|
||||
self.assertTrue(self.cohort_management_page.is_save_button_disabled(self.course_wide_key))
|
||||
|
||||
self.assertEqual(
|
||||
"Content-Specific Discussion Topics",
|
||||
self.cohort_management_page.cohort_discussion_heading_is_visible(self.inline_key)
|
||||
)
|
||||
self.assertTrue(self.cohort_management_page.is_save_button_disabled(self.inline_key))
|
||||
|
||||
def save_and_verify_discussion_topics(self, key):
|
||||
"""
|
||||
Saves the discussion topics and the verify the changes.
|
||||
"""
|
||||
# click on the inline save button.
|
||||
self.cohort_management_page.save_discussion_topics(key)
|
||||
|
||||
# verifies that changes saved successfully.
|
||||
confirmation_message = self.cohort_management_page.get_cohort_discussions_message(key=key)
|
||||
self.assertEqual("Your changes have been saved.", confirmation_message)
|
||||
|
||||
# save button disabled again.
|
||||
self.assertTrue(self.cohort_management_page.is_save_button_disabled(key))
|
||||
|
||||
def reload_page(self):
|
||||
"""
|
||||
Refresh the page.
|
||||
"""
|
||||
self.browser.refresh()
|
||||
self.cohort_management_page.wait_for_page()
|
||||
|
||||
self.instructor_dashboard_page.select_cohort_management()
|
||||
self.cohort_management_page.wait_for_page()
|
||||
|
||||
self.cohort_discussion_topics_are_visible()
|
||||
|
||||
def verify_discussion_topics_after_reload(self, key, cohorted_topics):
|
||||
"""
|
||||
Verifies the changed topics.
|
||||
"""
|
||||
self.reload_page()
|
||||
self.assertEqual(self.cohort_management_page.get_cohorted_topics_count(key), cohorted_topics)
|
||||
|
||||
def test_cohort_course_wide_discussion_topic(self):
|
||||
"""
|
||||
Scenario: cohort a course-wide discussion topic.
|
||||
|
||||
Given I have a course with a cohort defined,
|
||||
And a course-wide discussion with disabled Save button.
|
||||
When I click on the course-wide discussion topic
|
||||
Then I see the enabled save button
|
||||
When I click on save button
|
||||
Then I see success message
|
||||
When I reload the page
|
||||
Then I see the discussion topic selected
|
||||
"""
|
||||
self.cohort_discussion_topics_are_visible()
|
||||
|
||||
cohorted_topics_before = self.cohort_management_page.get_cohorted_topics_count(self.course_wide_key)
|
||||
self.cohort_management_page.select_discussion_topic(self.course_wide_key)
|
||||
|
||||
self.assertFalse(self.cohort_management_page.is_save_button_disabled(self.course_wide_key))
|
||||
|
||||
self.save_and_verify_discussion_topics(key=self.course_wide_key)
|
||||
cohorted_topics_after = self.cohort_management_page.get_cohorted_topics_count(self.course_wide_key)
|
||||
|
||||
self.assertNotEqual(cohorted_topics_before, cohorted_topics_after)
|
||||
|
||||
self.verify_discussion_topics_after_reload(self.course_wide_key, cohorted_topics_after)
|
||||
|
||||
def test_always_cohort_inline_topic_enabled(self):
|
||||
"""
|
||||
Scenario: Select the always_cohort_inline_topics radio button
|
||||
|
||||
Given I have a course with a cohort defined,
|
||||
And an inline discussion topic with disabled Save button.
|
||||
When I click on always_cohort_inline_topics
|
||||
Then I see enabled save button
|
||||
And I see disabled inline discussion topics
|
||||
When I save the change
|
||||
And I reload the page
|
||||
Then I see the always_cohort_inline_topics option enabled
|
||||
"""
|
||||
self.cohort_discussion_topics_are_visible()
|
||||
|
||||
# enable always inline discussion topics and save the change
|
||||
self.cohort_management_page.select_always_inline_discussion()
|
||||
self.assertFalse(self.cohort_management_page.is_save_button_disabled(self.inline_key))
|
||||
self.assertTrue(self.cohort_management_page.inline_discussion_topics_disabled())
|
||||
self.cohort_management_page.save_discussion_topics(key=self.inline_key)
|
||||
|
||||
self.reload_page()
|
||||
self.assertTrue(self.cohort_management_page.always_inline_discussion_selected())
|
||||
|
||||
def test_cohort_some_inline_topics_enabled(self):
|
||||
"""
|
||||
Scenario: Select the cohort_some_inline_topics radio button
|
||||
|
||||
Given I have a course with a cohort defined and always_cohort_inline_topics set to True
|
||||
And an inline discussion topic with disabled Save button.
|
||||
When I click on cohort_some_inline_topics
|
||||
Then I see enabled save button
|
||||
And I see enabled inline discussion topics
|
||||
When I save the change
|
||||
And I reload the page
|
||||
Then I see the cohort_some_inline_topics option enabled
|
||||
"""
|
||||
self.cohort_discussion_topics_are_visible()
|
||||
# By default always inline discussion topics is False. Enable it (and reload the page).
|
||||
self.assertFalse(self.cohort_management_page.always_inline_discussion_selected())
|
||||
self.cohort_management_page.select_always_inline_discussion()
|
||||
self.cohort_management_page.save_discussion_topics(key=self.inline_key)
|
||||
self.reload_page()
|
||||
self.assertFalse(self.cohort_management_page.cohort_some_inline_discussion_selected())
|
||||
|
||||
# enable some inline discussion topic radio button.
|
||||
self.cohort_management_page.select_cohort_some_inline_discussion()
|
||||
# I see that save button is enabled
|
||||
self.assertFalse(self.cohort_management_page.is_save_button_disabled(self.inline_key))
|
||||
# I see that inline discussion topics are enabled
|
||||
self.assertFalse(self.cohort_management_page.inline_discussion_topics_disabled())
|
||||
self.cohort_management_page.save_discussion_topics(key=self.inline_key)
|
||||
|
||||
self.reload_page()
|
||||
self.assertTrue(self.cohort_management_page.cohort_some_inline_discussion_selected())
|
||||
|
||||
def test_cohort_inline_discussion_topic(self):
|
||||
"""
|
||||
Scenario: cohort inline discussion topic.
|
||||
|
||||
Given I have a course with a cohort defined,
|
||||
And a inline discussion topic with disabled Save button
|
||||
And When I click on inline discussion topic
|
||||
And I see enabled save button
|
||||
And When i click save button
|
||||
Then I see success message
|
||||
When I reload the page
|
||||
Then I see the discussion topic selected
|
||||
"""
|
||||
self.cohort_discussion_topics_are_visible()
|
||||
|
||||
cohorted_topics_before = self.cohort_management_page.get_cohorted_topics_count(self.inline_key)
|
||||
# check the discussion topic.
|
||||
self.cohort_management_page.select_discussion_topic(self.inline_key)
|
||||
|
||||
# Save button enabled.
|
||||
self.assertFalse(self.cohort_management_page.is_save_button_disabled(self.inline_key))
|
||||
|
||||
# verifies that changes saved successfully.
|
||||
self.save_and_verify_discussion_topics(key=self.inline_key)
|
||||
|
||||
cohorted_topics_after = self.cohort_management_page.get_cohorted_topics_count(self.inline_key)
|
||||
self.assertNotEqual(cohorted_topics_before, cohorted_topics_after)
|
||||
|
||||
self.verify_discussion_topics_after_reload(self.inline_key, cohorted_topics_after)
|
||||
|
||||
def test_verify_that_selecting_the_final_child_selects_category(self):
|
||||
"""
|
||||
Scenario: Category should be selected on selecting final child.
|
||||
|
||||
Given I have a course with a cohort defined,
|
||||
And a inline discussion with disabled Save button.
|
||||
When I click on child topics
|
||||
Then I see enabled saved button
|
||||
Then I see parent category to be checked.
|
||||
"""
|
||||
self.cohort_discussion_topics_are_visible()
|
||||
|
||||
# category should not be selected.
|
||||
self.assertFalse(self.cohort_management_page.is_category_selected())
|
||||
|
||||
# check the discussion topic.
|
||||
self.cohort_management_page.select_discussion_topic(self.inline_key)
|
||||
|
||||
# verify that category is selected.
|
||||
self.assertTrue(self.cohort_management_page.is_category_selected())
|
||||
|
||||
def test_verify_that_deselecting_the_final_child_deselects_category(self):
|
||||
"""
|
||||
Scenario: Category should be deselected on deselecting final child.
|
||||
|
||||
Given I have a course with a cohort defined,
|
||||
And a inline discussion with disabled Save button.
|
||||
When I click on final child topics
|
||||
Then I see enabled saved button
|
||||
Then I see parent category to be deselected.
|
||||
"""
|
||||
self.cohort_discussion_topics_are_visible()
|
||||
|
||||
# category should not be selected.
|
||||
self.assertFalse(self.cohort_management_page.is_category_selected())
|
||||
|
||||
# check the discussion topic.
|
||||
self.cohort_management_page.select_discussion_topic(self.inline_key)
|
||||
|
||||
# verify that category is selected.
|
||||
self.assertTrue(self.cohort_management_page.is_category_selected())
|
||||
|
||||
# un-check the discussion topic.
|
||||
self.cohort_management_page.select_discussion_topic(self.inline_key)
|
||||
|
||||
# category should not be selected.
|
||||
self.assertFalse(self.cohort_management_page.is_category_selected())
|
||||
|
||||
|
||||
@attr(shard=6)
|
||||
class CohortContentGroupAssociationTest(UniqueCourseTest, CohortTestMixin):
|
||||
"""
|
||||
|
||||
@@ -47,6 +47,7 @@ class CohortedDiscussionTestMixin(BaseDiscussionMixin, CohortTestMixin):
|
||||
|
||||
# Enable cohorts and verify that the post shows to cohort only.
|
||||
self.enable_cohorting(self.course_fixture)
|
||||
self.enable_always_divide_inline_discussions(self.course_fixture)
|
||||
self.refresh_thread_page(self.thread_id)
|
||||
self.assertEquals(
|
||||
self.thread_page.get_group_visibility_label(),
|
||||
|
||||
@@ -0,0 +1,495 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
End-to-end tests related to the divided discussion management on the LMS Instructor Dashboard
|
||||
"""
|
||||
|
||||
import uuid
|
||||
|
||||
from nose.plugins.attrib import attr
|
||||
|
||||
from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
|
||||
from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
|
||||
from common.test.acceptance.pages.common.utils import add_enrollment_course_modes
|
||||
from common.test.acceptance.pages.lms.discussion import DiscussionTabSingleThreadPage
|
||||
from common.test.acceptance.pages.lms.instructor_dashboard import InstructorDashboardPage
|
||||
from common.test.acceptance.tests.discussion.helpers import BaseDiscussionMixin, CohortTestMixin
|
||||
from common.test.acceptance.tests.helpers import UniqueCourseTest
|
||||
|
||||
|
||||
class BaseDividedDiscussionTest(UniqueCourseTest, CohortTestMixin):
|
||||
"""
|
||||
Base class for tests related to divided discussions.
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up a discussion topic
|
||||
"""
|
||||
super(BaseDividedDiscussionTest, self).setUp()
|
||||
|
||||
self.discussion_id = "test_discussion_{}".format(uuid.uuid4().hex)
|
||||
self.course_fixture = CourseFixture(**self.course_info).add_children(
|
||||
XBlockFixtureDesc("chapter", "Test Section").add_children(
|
||||
XBlockFixtureDesc("sequential", "Test Subsection").add_children(
|
||||
XBlockFixtureDesc("vertical", "Test Unit").add_children(
|
||||
XBlockFixtureDesc(
|
||||
"discussion",
|
||||
"Test Discussion",
|
||||
metadata={"discussion_id": self.discussion_id}
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
).install()
|
||||
|
||||
# create course with single cohort and two content groups (user_partition of type "cohort")
|
||||
self.cohort_name = "OnlyCohort"
|
||||
self.setup_cohort_config(self.course_fixture)
|
||||
self.cohort_id = self.add_manual_cohort(self.course_fixture, self.cohort_name)
|
||||
|
||||
# login as an instructor
|
||||
self.instructor_name = "instructor_user"
|
||||
self.instructor_id = AutoAuthPage(
|
||||
self.browser, username=self.instructor_name, email="instructor_user@example.com",
|
||||
course_id=self.course_id, staff=True
|
||||
).visit().get_user_id()
|
||||
|
||||
# go to the membership page on the instructor dashboard
|
||||
self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
|
||||
self.instructor_dashboard_page.visit()
|
||||
self.discussion_management_page = self.instructor_dashboard_page.select_discussion_management()
|
||||
self.discussion_management_page.wait_for_page()
|
||||
|
||||
self.course_wide_key = 'course-wide'
|
||||
self.inline_key = 'inline'
|
||||
self.scheme_key = 'scheme'
|
||||
|
||||
def check_discussion_topic_visibility(self, visible=True):
|
||||
"""
|
||||
Assert that discussion topics are visible with appropriate content.
|
||||
"""
|
||||
self.assertEqual(visible, self.discussion_management_page.discussion_topics_visible())
|
||||
|
||||
if visible:
|
||||
self.assertEqual(
|
||||
"Course-Wide Discussion Topics",
|
||||
self.discussion_management_page.divided_discussion_heading_is_visible(self.course_wide_key)
|
||||
)
|
||||
self.assertTrue(self.discussion_management_page.is_save_button_disabled(self.course_wide_key))
|
||||
|
||||
self.assertEqual(
|
||||
"Content-Specific Discussion Topics",
|
||||
self.discussion_management_page.divided_discussion_heading_is_visible(self.inline_key)
|
||||
)
|
||||
self.assertTrue(self.discussion_management_page.is_save_button_disabled(self.inline_key))
|
||||
|
||||
def reload_page(self, topics_visible=True):
|
||||
"""
|
||||
Refresh the page, then verify if the discussion topics are visible on the discussion
|
||||
management instructor dashboard tab.
|
||||
"""
|
||||
self.browser.refresh()
|
||||
self.discussion_management_page.wait_for_page()
|
||||
|
||||
self.instructor_dashboard_page.select_discussion_management()
|
||||
self.discussion_management_page.wait_for_page()
|
||||
|
||||
self.check_discussion_topic_visibility(topics_visible)
|
||||
|
||||
def verify_save_confirmation_message(self, key):
|
||||
"""
|
||||
Verify that the save confirmation message for the specified portion of the page is visible.
|
||||
"""
|
||||
confirmation_message = self.discussion_management_page.get_divide_discussions_message(key=key)
|
||||
self.assertIn("Your changes have been saved.", confirmation_message)
|
||||
|
||||
|
||||
@attr(shard=6)
|
||||
class DividedDiscussionTopicsTest(BaseDividedDiscussionTest):
|
||||
"""
|
||||
Tests for dividing the inline and course-wide discussion topics.
|
||||
"""
|
||||
|
||||
def save_and_verify_discussion_topics(self, key):
|
||||
"""
|
||||
Saves the discussion topics and the verify the changes.
|
||||
"""
|
||||
# click on the inline save button.
|
||||
self.discussion_management_page.save_discussion_topics(key)
|
||||
|
||||
# verifies that changes saved successfully.
|
||||
self.verify_save_confirmation_message(key)
|
||||
|
||||
# save button disabled again.
|
||||
self.assertTrue(self.discussion_management_page.is_save_button_disabled(key))
|
||||
|
||||
def verify_discussion_topics_after_reload(self, key, divided_topics):
|
||||
"""
|
||||
Verifies the changed topics.
|
||||
"""
|
||||
self.reload_page()
|
||||
self.assertEqual(self.discussion_management_page.get_divided_topics_count(key), divided_topics)
|
||||
|
||||
def test_divide_course_wide_discussion_topic(self):
|
||||
"""
|
||||
Scenario: divide a course-wide discussion topic.
|
||||
|
||||
Given I have a course with a divide defined,
|
||||
And a course-wide discussion with disabled Save button.
|
||||
When I click on the course-wide discussion topic
|
||||
Then I see the enabled save button
|
||||
When I click on save button
|
||||
Then I see success message
|
||||
When I reload the page
|
||||
Then I see the discussion topic selected
|
||||
"""
|
||||
self.check_discussion_topic_visibility()
|
||||
|
||||
divided_topics_before = self.discussion_management_page.get_divided_topics_count(self.course_wide_key)
|
||||
self.discussion_management_page.select_discussion_topic(self.course_wide_key)
|
||||
|
||||
self.assertFalse(self.discussion_management_page.is_save_button_disabled(self.course_wide_key))
|
||||
|
||||
self.save_and_verify_discussion_topics(key=self.course_wide_key)
|
||||
divided_topics_after = self.discussion_management_page.get_divided_topics_count(self.course_wide_key)
|
||||
|
||||
self.assertNotEqual(divided_topics_before, divided_topics_after)
|
||||
|
||||
self.verify_discussion_topics_after_reload(self.course_wide_key, divided_topics_after)
|
||||
|
||||
def test_always_divide_inline_topic_enabled(self):
|
||||
"""
|
||||
Scenario: Select the always_divide_inline_topics radio button
|
||||
|
||||
Given I have a course with a cohort defined,
|
||||
And an inline discussion topic with disabled Save button.
|
||||
When I click on always_divide_inline_topics
|
||||
Then I see enabled save button
|
||||
And I see disabled inline discussion topics
|
||||
When I save the change
|
||||
And I reload the page
|
||||
Then I see the always_divide_inline_topics option enabled
|
||||
"""
|
||||
self.check_discussion_topic_visibility()
|
||||
|
||||
# enable always inline discussion topics and save the change
|
||||
self.discussion_management_page.select_always_inline_discussion()
|
||||
self.assertFalse(self.discussion_management_page.is_save_button_disabled(self.inline_key))
|
||||
self.assertTrue(self.discussion_management_page.inline_discussion_topics_disabled())
|
||||
self.discussion_management_page.save_discussion_topics(key=self.inline_key)
|
||||
|
||||
self.reload_page()
|
||||
self.assertTrue(self.discussion_management_page.always_inline_discussion_selected())
|
||||
|
||||
def test_divide_some_inline_topics_enabled(self):
|
||||
"""
|
||||
Scenario: Select the divide_some_inline_topics radio button
|
||||
|
||||
Given I have a course with a divide defined and always_divide_inline_topics set to True
|
||||
And an inline discussion topic with disabled Save button.
|
||||
When I click on divide_some_inline_topics
|
||||
Then I see enabled save button
|
||||
And I see enabled inline discussion topics
|
||||
When I save the change
|
||||
And I reload the page
|
||||
Then I see the divide_some_inline_topics option enabled
|
||||
"""
|
||||
self.check_discussion_topic_visibility()
|
||||
# By default always inline discussion topics is False. Enable it (and reload the page).
|
||||
self.assertFalse(self.discussion_management_page.always_inline_discussion_selected())
|
||||
self.discussion_management_page.select_always_inline_discussion()
|
||||
self.discussion_management_page.save_discussion_topics(key=self.inline_key)
|
||||
self.reload_page()
|
||||
self.assertFalse(self.discussion_management_page.divide_some_inline_discussion_selected())
|
||||
|
||||
# enable some inline discussion topic radio button.
|
||||
self.discussion_management_page.select_divide_some_inline_discussion()
|
||||
# I see that save button is enabled
|
||||
self.assertFalse(self.discussion_management_page.is_save_button_disabled(self.inline_key))
|
||||
# I see that inline discussion topics are enabled
|
||||
self.assertFalse(self.discussion_management_page.inline_discussion_topics_disabled())
|
||||
self.discussion_management_page.save_discussion_topics(key=self.inline_key)
|
||||
|
||||
self.reload_page()
|
||||
self.assertTrue(self.discussion_management_page.divide_some_inline_discussion_selected())
|
||||
|
||||
def test_divide_inline_discussion_topic(self):
|
||||
"""
|
||||
Scenario: divide inline discussion topic.
|
||||
|
||||
Given I have a course with a divide defined,
|
||||
And a inline discussion topic with disabled Save button
|
||||
And When I click on inline discussion topic
|
||||
And I see enabled save button
|
||||
And When i click save button
|
||||
Then I see success message
|
||||
When I reload the page
|
||||
Then I see the discussion topic selected
|
||||
"""
|
||||
self.check_discussion_topic_visibility()
|
||||
|
||||
divided_topics_before = self.discussion_management_page.get_divided_topics_count(self.inline_key)
|
||||
# check the discussion topic.
|
||||
self.discussion_management_page.select_discussion_topic(self.inline_key)
|
||||
|
||||
# Save button enabled.
|
||||
self.assertFalse(self.discussion_management_page.is_save_button_disabled(self.inline_key))
|
||||
|
||||
# verifies that changes saved successfully.
|
||||
self.save_and_verify_discussion_topics(key=self.inline_key)
|
||||
|
||||
divided_topics_after = self.discussion_management_page.get_divided_topics_count(self.inline_key)
|
||||
self.assertNotEqual(divided_topics_before, divided_topics_after)
|
||||
|
||||
self.verify_discussion_topics_after_reload(self.inline_key, divided_topics_after)
|
||||
|
||||
def test_verify_that_selecting_the_final_child_selects_category(self):
|
||||
"""
|
||||
Scenario: Category should be selected on selecting final child.
|
||||
|
||||
Given I have a course with a cohort defined,
|
||||
And a inline discussion with disabled Save button.
|
||||
When I click on child topics
|
||||
Then I see enabled saved button
|
||||
Then I see parent category to be checked.
|
||||
"""
|
||||
self.check_discussion_topic_visibility()
|
||||
|
||||
# category should not be selected.
|
||||
self.assertFalse(self.discussion_management_page.is_category_selected())
|
||||
|
||||
# check the discussion topic.
|
||||
self.discussion_management_page.select_discussion_topic(self.inline_key)
|
||||
|
||||
# verify that category is selected.
|
||||
self.assertTrue(self.discussion_management_page.is_category_selected())
|
||||
|
||||
def test_verify_that_deselecting_the_final_child_deselects_category(self):
|
||||
"""
|
||||
Scenario: Category should be deselected on deselecting final child.
|
||||
|
||||
Given I have a course with a cohort defined,
|
||||
And a inline discussion with disabled Save button.
|
||||
When I click on final child topics
|
||||
Then I see enabled saved button
|
||||
Then I see parent category to be deselected.
|
||||
"""
|
||||
self.check_discussion_topic_visibility()
|
||||
|
||||
# category should not be selected.
|
||||
self.assertFalse(self.discussion_management_page.is_category_selected())
|
||||
|
||||
# check the discussion topic.
|
||||
self.discussion_management_page.select_discussion_topic(self.inline_key)
|
||||
|
||||
# verify that category is selected.
|
||||
self.assertTrue(self.discussion_management_page.is_category_selected())
|
||||
|
||||
# un-check the discussion topic.
|
||||
self.discussion_management_page.select_discussion_topic(self.inline_key)
|
||||
|
||||
# category should not be selected.
|
||||
self.assertFalse(self.discussion_management_page.is_category_selected())
|
||||
|
||||
|
||||
@attr(shard=6)
|
||||
class DivisionSchemeTest(BaseDividedDiscussionTest, BaseDiscussionMixin):
|
||||
"""
|
||||
Tests for changing the division scheme for Discussions.
|
||||
"""
|
||||
|
||||
def add_modes_and_view_discussion_mgmt_page(self, modes):
|
||||
"""
|
||||
Adds enrollment modes to the course, and then goes to the
|
||||
discussion tab on the instructor dashboard.
|
||||
"""
|
||||
add_enrollment_course_modes(self.browser, self.course_id, modes)
|
||||
self.view_discussion_management_page()
|
||||
|
||||
def view_discussion_management_page(self):
|
||||
"""
|
||||
Go to the discussion tab on the instructor dashboard.
|
||||
"""
|
||||
self.instructor_dashboard_page.visit()
|
||||
self.assertTrue(self.instructor_dashboard_page.is_discussion_management_visible())
|
||||
self.instructor_dashboard_page.select_discussion_management()
|
||||
self.discussion_management_page.wait_for_page()
|
||||
|
||||
def setup_thread_page(self, thread_id):
|
||||
"""
|
||||
This is called by BaseDiscussionMixin.setup_thread.
|
||||
"""
|
||||
self.thread_page = DiscussionTabSingleThreadPage(
|
||||
self.browser, self.course_id, self.discussion_id, thread_id
|
||||
)
|
||||
self.thread_page.visit()
|
||||
|
||||
def test_not_divided_hides_discussion_topics(self):
|
||||
"""
|
||||
Tests that discussion topics are hidden iff discussion division is disabled.
|
||||
"""
|
||||
# Initially "Cohort" is the selected scheme.
|
||||
self.assertTrue(
|
||||
self.discussion_management_page.division_scheme_visible(self.discussion_management_page.COHORT_SCHEME)
|
||||
)
|
||||
self.assertEqual(
|
||||
self.discussion_management_page.COHORT_SCHEME,
|
||||
self.discussion_management_page.get_selected_scheme()
|
||||
)
|
||||
self.check_discussion_topic_visibility(visible=True)
|
||||
|
||||
self.discussion_management_page.select_division_scheme(self.discussion_management_page.NOT_DIVIDED_SCHEME)
|
||||
self.verify_save_confirmation_message(self.scheme_key)
|
||||
self.check_discussion_topic_visibility(visible=False)
|
||||
|
||||
# Reload the page and make sure that the change was persisted
|
||||
self.reload_page(topics_visible=False)
|
||||
self.assertTrue(self.discussion_management_page.division_scheme_visible(
|
||||
self.discussion_management_page.COHORT_SCHEME)
|
||||
)
|
||||
self.assertEqual(
|
||||
self.discussion_management_page.NOT_DIVIDED_SCHEME,
|
||||
self.discussion_management_page.get_selected_scheme()
|
||||
)
|
||||
|
||||
# Select "cohort" again and make sure that the discussion topics appear.
|
||||
self.discussion_management_page.select_division_scheme(self.discussion_management_page.COHORT_SCHEME)
|
||||
self.verify_save_confirmation_message(self.scheme_key)
|
||||
self.check_discussion_topic_visibility(visible=True)
|
||||
|
||||
def test_disabling_cohorts(self):
|
||||
"""
|
||||
Test that the discussions management tab hides when there is <= 1 enrollment track, the Cohort division scheme
|
||||
is not selected, and cohorts are disabled.
|
||||
(even without reloading the page).
|
||||
"""
|
||||
self.disable_cohorting(self.course_fixture)
|
||||
self.instructor_dashboard_page.visit()
|
||||
self.assertFalse(self.instructor_dashboard_page.is_discussion_management_visible())
|
||||
|
||||
def test_disabling_cohorts_while_selected(self):
|
||||
"""
|
||||
Test that disabling cohorts does not hide the discussion tab when there is more than one enrollment track.
|
||||
Also that the division scheme for cohorts is visible iff it was selected.
|
||||
(even without reloading the page).
|
||||
"""
|
||||
add_enrollment_course_modes(self.browser, self.course_id, ['audit', 'verified'])
|
||||
|
||||
# Verify that the tab is visible, the cohort scheme is selected by default for divided discussions
|
||||
self.disable_cohorting(self.course_fixture)
|
||||
|
||||
# Go to Discussions tab and ensure that the correct scheme options are visible
|
||||
self.view_discussion_management_page()
|
||||
self.assertTrue(
|
||||
self.discussion_management_page.division_scheme_visible(
|
||||
self.discussion_management_page.COHORT_SCHEME
|
||||
)
|
||||
)
|
||||
|
||||
def test_disabling_cohorts_while_not_selected(self):
|
||||
"""
|
||||
Test that disabling cohorts does not hide the discussion tab when there is more than one enrollment track.
|
||||
Also that the division scheme for cohorts is not visible when cohorts are disabled and another scheme is
|
||||
selected for division.
|
||||
(even without reloading the page).
|
||||
"""
|
||||
add_enrollment_course_modes(self.browser, self.course_id, ['audit', 'verified'])
|
||||
|
||||
# Verify that the tab is visible
|
||||
self.view_discussion_management_page()
|
||||
self.discussion_management_page.select_division_scheme(self.discussion_management_page.ENROLLMENT_TRACK_SCHEME)
|
||||
self.verify_save_confirmation_message(self.scheme_key)
|
||||
self.disable_cohorting(self.course_fixture)
|
||||
|
||||
# Go to Discussions tab and ensure that the correct scheme options are visible
|
||||
self.view_discussion_management_page()
|
||||
self.assertFalse(
|
||||
self.discussion_management_page.division_scheme_visible(
|
||||
self.discussion_management_page.COHORT_SCHEME
|
||||
)
|
||||
)
|
||||
|
||||
def test_single_enrollment_mode(self):
|
||||
"""
|
||||
Test that the enrollment track scheme is not visible if there is a single enrollment mode.
|
||||
"""
|
||||
self.add_modes_and_view_discussion_mgmt_page(['audit'])
|
||||
self.assertFalse(
|
||||
self.discussion_management_page.division_scheme_visible(
|
||||
self.discussion_management_page.ENROLLMENT_TRACK_SCHEME
|
||||
)
|
||||
)
|
||||
|
||||
def test_radio_buttons_with_multiple_enrollment_modes(self):
|
||||
"""
|
||||
Test that the enrollment track scheme is visible if there are multiple enrollment tracks,
|
||||
and that the selection can be persisted.
|
||||
|
||||
Also verifies that the cohort division scheme is not presented if cohorts are disabled and cohorts
|
||||
are not the selected division scheme.
|
||||
"""
|
||||
self.add_modes_and_view_discussion_mgmt_page(['audit', 'verified'])
|
||||
self.assertTrue(
|
||||
self.discussion_management_page.division_scheme_visible(
|
||||
self.discussion_management_page.ENROLLMENT_TRACK_SCHEME
|
||||
)
|
||||
)
|
||||
# And the cohort scheme is initially visible because it is selected (and cohorts are enabled).
|
||||
self.assertTrue(
|
||||
self.discussion_management_page.division_scheme_visible(self.discussion_management_page.COHORT_SCHEME)
|
||||
)
|
||||
|
||||
self.discussion_management_page.select_division_scheme(self.discussion_management_page.ENROLLMENT_TRACK_SCHEME)
|
||||
self.verify_save_confirmation_message(self.scheme_key)
|
||||
self.check_discussion_topic_visibility(visible=True)
|
||||
|
||||
# Also disable cohorts so we can verify that the cohort scheme choice goes away.
|
||||
self.disable_cohorting(self.course_fixture)
|
||||
|
||||
self.reload_page(topics_visible=True)
|
||||
self.assertEqual(
|
||||
self.discussion_management_page.ENROLLMENT_TRACK_SCHEME,
|
||||
self.discussion_management_page.get_selected_scheme()
|
||||
)
|
||||
# Verify that the cohort scheme is no longer visible as cohorts are disabled.
|
||||
self.assertFalse(
|
||||
self.discussion_management_page.division_scheme_visible(self.discussion_management_page.COHORT_SCHEME)
|
||||
)
|
||||
|
||||
def test_enrollment_track_discussion_visibility_label(self):
|
||||
"""
|
||||
If enrollment tracks are the division scheme, verifies that discussion visibility labels
|
||||
correctly render.
|
||||
|
||||
Note that there are similar tests for cohorts in test_cohorts.py.
|
||||
"""
|
||||
def refresh_thread_page():
|
||||
self.browser.refresh()
|
||||
self.thread_page.wait_for_page()
|
||||
|
||||
# Make moderator for viewing all groups in discussions.
|
||||
AutoAuthPage(self.browser, course_id=self.course_id, roles="Moderator", staff=True).visit()
|
||||
|
||||
self.add_modes_and_view_discussion_mgmt_page(['audit', 'verified'])
|
||||
self.discussion_management_page.select_division_scheme(self.discussion_management_page.ENROLLMENT_TRACK_SCHEME)
|
||||
self.verify_save_confirmation_message(self.scheme_key)
|
||||
# Set "always divide" as the thread we will be creating will be an inline thread,
|
||||
# and this way the thread does not need to be explicitly divided.
|
||||
self.enable_always_divide_inline_discussions(self.course_fixture)
|
||||
|
||||
# Create a thread with group_id corresponding to the Audit enrollment mode.
|
||||
# The Audit group ID is 1, and for the comment service group_id we negate it.
|
||||
self.setup_thread(1, group_id=-1)
|
||||
|
||||
refresh_thread_page()
|
||||
self.assertEquals(
|
||||
self.thread_page.get_group_visibility_label(),
|
||||
"This post is visible only to {}.".format("Audit")
|
||||
)
|
||||
|
||||
# Disable dividing discussions and verify that the post now shows as visible to everyone.
|
||||
self.view_discussion_management_page()
|
||||
self.discussion_management_page.select_division_scheme(self.discussion_management_page.NOT_DIVIDED_SCHEME)
|
||||
self.verify_save_confirmation_message(self.scheme_key)
|
||||
|
||||
self.thread_page.visit()
|
||||
self.assertEquals(self.thread_page.get_group_visibility_label(), "This post is visible to everyone.")
|
||||
@@ -7,7 +7,6 @@ import uuid
|
||||
|
||||
from nose.plugins.attrib import attr
|
||||
|
||||
from common.test.acceptance.fixtures import LMS_BASE_URL
|
||||
from common.test.acceptance.fixtures.course import XBlockFixtureDesc
|
||||
from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
|
||||
from common.test.acceptance.pages.common.logout import LogoutPage
|
||||
@@ -17,12 +16,13 @@ from common.test.acceptance.pages.lms.staff_view import StaffCoursewarePage
|
||||
from common.test.acceptance.pages.studio.component_editor import ComponentVisibilityEditorView
|
||||
from common.test.acceptance.pages.studio.overview import CourseOutlinePage as StudioCourseOutlinePage
|
||||
from common.test.acceptance.pages.studio.settings_group_configurations import GroupConfigurationsPage
|
||||
from common.test.acceptance.tests.discussion.helpers import CohortTestMixin
|
||||
from common.test.acceptance.tests.helpers import remove_file
|
||||
from common.test.acceptance.tests.studio.base_studio_test import ContainerBase
|
||||
|
||||
|
||||
@attr(shard=1)
|
||||
class CoursewareSearchCohortTest(ContainerBase):
|
||||
class CoursewareSearchCohortTest(ContainerBase, CohortTestMixin):
|
||||
"""
|
||||
Test courseware search.
|
||||
"""
|
||||
@@ -132,15 +132,6 @@ class CoursewareSearchCohortTest(ContainerBase):
|
||||
)
|
||||
)
|
||||
|
||||
def enable_cohorting(self, course_fixture):
|
||||
"""
|
||||
Enables cohorting for the current course.
|
||||
"""
|
||||
url = LMS_BASE_URL + "/courses/" + course_fixture._course_key + '/cohorts/settings' # pylint: disable=protected-access
|
||||
data = json.dumps({'is_cohorted': True})
|
||||
response = course_fixture.session.patch(url, data=data, headers=course_fixture.headers)
|
||||
self.assertTrue(response.ok, "Failed to enable cohorts")
|
||||
|
||||
def create_content_groups(self):
|
||||
"""
|
||||
Creates two content groups in Studio Group Configurations Settings.
|
||||
|
||||
@@ -2,17 +2,15 @@
|
||||
Test Help links in LMS
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from common.test.acceptance.fixtures import LMS_BASE_URL
|
||||
from common.test.acceptance.fixtures.course import CourseFixture
|
||||
from common.test.acceptance.pages.lms.instructor_dashboard import InstructorDashboardPage
|
||||
from common.test.acceptance.tests.discussion.helpers import CohortTestMixin
|
||||
from common.test.acceptance.tests.helpers import assert_opened_help_link_is_correct, url_for_help
|
||||
from common.test.acceptance.tests.lms.test_lms_instructor_dashboard import BaseInstructorDashboardTest
|
||||
from common.test.acceptance.tests.studio.base_studio_test import ContainerBase
|
||||
|
||||
|
||||
class TestCohortHelp(ContainerBase):
|
||||
class TestCohortHelp(ContainerBase, CohortTestMixin):
|
||||
"""
|
||||
Tests help links in Cohort page
|
||||
"""
|
||||
@@ -74,15 +72,6 @@ class TestCohortHelp(ContainerBase):
|
||||
)
|
||||
self.verify_help_link(href)
|
||||
|
||||
def enable_cohorting(self, course_fixture):
|
||||
"""
|
||||
Enables cohorting for the current course.
|
||||
"""
|
||||
url = LMS_BASE_URL + "/courses/" + course_fixture._course_key + '/cohorts/settings' # pylint: disable=protected-access
|
||||
data = json.dumps({'is_cohorted': True})
|
||||
response = course_fixture.session.patch(url, data=data, headers=course_fixture.headers)
|
||||
self.assertTrue(response.ok, "Failed to enable cohorts")
|
||||
|
||||
|
||||
class InstructorDashboardHelp(BaseInstructorDashboardTest):
|
||||
"""
|
||||
|
||||
@@ -2,12 +2,9 @@
|
||||
End-to-end test for cohorted courseware. This uses both Studio and LMS.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from bok_choy.page_object import XSS_INJECTION
|
||||
from nose.plugins.attrib import attr
|
||||
|
||||
from common.test.acceptance.fixtures import LMS_BASE_URL
|
||||
from common.test.acceptance.fixtures.course import XBlockFixtureDesc
|
||||
from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
|
||||
from common.test.acceptance.pages.common.utils import add_enrollment_course_modes, enroll_user_track
|
||||
@@ -15,6 +12,7 @@ from common.test.acceptance.pages.lms.courseware import CoursewarePage
|
||||
from common.test.acceptance.pages.lms.instructor_dashboard import InstructorDashboardPage
|
||||
from common.test.acceptance.pages.studio.component_editor import ComponentVisibilityEditorView
|
||||
from common.test.acceptance.pages.studio.settings_group_configurations import GroupConfigurationsPage
|
||||
from common.test.acceptance.tests.discussion.helpers import CohortTestMixin
|
||||
from common.test.acceptance.tests.lms.test_lms_user_preview import verify_expected_problem_visibility
|
||||
from studio.base_studio_test import ContainerBase
|
||||
|
||||
@@ -23,7 +21,7 @@ VERIFIED_TRACK = "Verified"
|
||||
|
||||
|
||||
@attr(shard=5)
|
||||
class EndToEndCohortedCoursewareTest(ContainerBase):
|
||||
class EndToEndCohortedCoursewareTest(ContainerBase, CohortTestMixin):
|
||||
"""
|
||||
End-to-end of cohorted courseware.
|
||||
"""
|
||||
@@ -113,15 +111,6 @@ class EndToEndCohortedCoursewareTest(ContainerBase):
|
||||
)
|
||||
)
|
||||
|
||||
def enable_cohorting(self, course_fixture):
|
||||
"""
|
||||
Enables cohorting for the current course.
|
||||
"""
|
||||
url = LMS_BASE_URL + "/courses/" + course_fixture._course_key + '/cohorts/settings' # pylint: disable=protected-access
|
||||
data = json.dumps({'is_cohorted': True})
|
||||
response = course_fixture.session.patch(url, data=data, headers=course_fixture.headers)
|
||||
self.assertTrue(response.ok, "Failed to enable cohorts")
|
||||
|
||||
def create_content_groups(self):
|
||||
"""
|
||||
Creates two content groups in Studio Group Configurations Settings.
|
||||
|
||||
Reference in New Issue
Block a user