From c23c0b991b854f0c8bc0201ca053ae01839eb365 Mon Sep 17 00:00:00 2001
From: "Albert St. Aubin"
Date: Tue, 2 May 2017 14:01:14 -0400
Subject: [PATCH] EDUCATOR-141: Moved the Discussions Mgt to its own tab and
updated tests
---
.../pages/lms/instructor_dashboard.py | 152 +++---
.../acceptance/tests/discussion/helpers.py | 9 +-
.../discussion/test_cohort_management.py | 261 ---------
.../discussion/test_discussion_management.py | 273 ++++++++++
.../django_comment_client/tests/test_utils.py | 40 +-
.../instructor/views/instructor_dashboard.py | 17 +-
lms/envs/common.py | 1 +
.../models/course_discussions_detail.js} | 4 +-
.../models/course_discussions_settings.js | 14 +
.../views/discussions.js | 49 ++
.../views/discussions_dashboard_factory.js | 33 ++
.../views/divided_discussions.js} | 37 +-
.../views/divided_discussions_course_wide.js} | 28 +-
.../views/divided_discussions_inline.js} | 57 +-
.../groups/models/course_cohort_settings.js | 5 +-
lms/static/js/groups/views/cohorts.js | 26 +-
.../groups/views/cohorts_dashboard_factory.js | 26 +-
.../discussions_management.js | 12 +
.../instructor_dashboard.js | 3 +
.../js/spec/groups/views/cohorts_spec.js | 504 +----------------
.../js/spec/groups/views/discussions_spec.js | 512 ++++++++++++++++++
lms/static/lms/js/build.js | 1 +
lms/static/lms/js/spec/main.js | 1 +
.../sass/course/instructor/_instructor_2.scss | 114 ++--
.../cohort-discussions-subcategory.underscore | 2 +-
.../cohort_management.html | 1 -
.../instructor_dashboard_2/cohorts.underscore | 10 -
.../discussions.underscore | 5 +
.../discussions_management.html | 22 +
...ivided-discussions-course-wide.underscore} | 2 +-
... => divided-discussions-inline.underscore} | 10 +-
.../instructor_dashboard_2.html | 2 +-
lms/urls.py | 15 +-
.../core/djangoapps/course_groups/cohorts.py | 10 +
.../djangoapps/course_groups/tests/helpers.py | 61 ++-
.../course_groups/tests/test_views.py | 260 +++++----
.../core/djangoapps/course_groups/views.py | 124 +++--
37 files changed, 1523 insertions(+), 1180 deletions(-)
create mode 100644 common/test/acceptance/tests/discussion/test_discussion_management.py
rename lms/static/js/{groups/models/cohort_discussions.js => discussions_management/models/course_discussions_detail.js} (68%)
create mode 100644 lms/static/js/discussions_management/models/course_discussions_settings.js
create mode 100644 lms/static/js/discussions_management/views/discussions.js
create mode 100644 lms/static/js/discussions_management/views/discussions_dashboard_factory.js
rename lms/static/js/{groups/views/cohort_discussions.js => discussions_management/views/divided_discussions.js} (74%)
rename lms/static/js/{groups/views/cohort_discussions_course_wide.js => discussions_management/views/divided_discussions_course_wide.js} (77%)
rename lms/static/js/{groups/views/cohort_discussions_inline.js => discussions_management/views/divided_discussions_inline.js} (75%)
create mode 100644 lms/static/js/instructor_dashboard/discussions_management.js
create mode 100644 lms/static/js/spec/groups/views/discussions_spec.js
create mode 100644 lms/templates/instructor/instructor_dashboard_2/discussions.underscore
create mode 100644 lms/templates/instructor/instructor_dashboard_2/discussions_management.html
rename lms/templates/instructor/instructor_dashboard_2/{cohort-discussions-course-wide.underscore => divided-discussions-course-wide.underscore} (94%)
rename lms/templates/instructor/instructor_dashboard_2/{cohort-discussions-inline.underscore => divided-discussions-inline.underscore} (86%)
diff --git a/common/test/acceptance/pages/lms/instructor_dashboard.py b/common/test/acceptance/pages/lms/instructor_dashboard.py
index b6000e0e3f..077c7d02f2 100644
--- a/common/test/acceptance/pages/lms/instructor_dashboard.py
+++ b/common/test/acceptance/pages/lms/instructor_dashboard.py
@@ -47,6 +47,15 @@ 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 select_data_download(self):
"""
Selects the data download tab and returns a DataDownloadPage.
@@ -666,61 +675,31 @@ 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'
+ }
- 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
+ def is_browser_on_page(self):
+ return self.q(css=self.discussion_form_selectors['course-wide']).present
- def cohort_some_inline_discussion_selected(self):
+ def _bounded_selector(self, selector):
"""
- Returns true if some_cohort_inline_discussions radio button is selected.
+ Return `selector`, but limited to the divided discussion management context.
"""
- return len(self.q(css=self._bounded_selector(".check-cohort-inline-discussions:checked"))) > 0
-
- def select_cohort_some_inline_discussion(self):
- """
- Selects the cohort_some_inline_discussions radio button.
- """
- 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 +709,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 +747,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 +795,11 @@ 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))
-
- if len(discussion_heading) == 0:
- return False
- return discussion_heading.first.text[0]
-
- def cohort_management_controls_visible(self):
- """
- Return the visibility status of cohort management controls(cohort selector section etc).
- """
- return (self.q(css=self._bounded_selector('.cohort-management-nav')).visible and
- self.q(css=self._bounded_selector('.wrapper-cohort-supplemental')).visible)
+ return self.q(css=self._bounded_selector('.check-discussion-category:checked')).is_present()
class MembershipPageAutoEnrollSection(PageObject):
diff --git a/common/test/acceptance/tests/discussion/helpers.py b/common/test/acceptance/tests/discussion/helpers.py
index 95c4543c91..3e216dd4a4 100644
--- a/common/test/acceptance/tests/discussion/helpers.py
+++ b/common/test/acceptance/tests/discussion/helpers.py
@@ -76,11 +76,16 @@ class CohortTestMixin(object):
def enable_cohorting(self, course_fixture):
"""
- enables cohorting for the current course fixture.
+ enables cohorts and always_divide_inline_discussions for the current 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})
+ discussions_url = LMS_BASE_URL + "/courses/" + course_fixture._course_key + '/discussions/settings' # pylint: disable=protected-access
+
+ data = json.dumps({'is_cohorted': True})
+ discussions_data = json.dumps({'always_divide_inline_discussions': True})
+
response = course_fixture.session.patch(url, data=data, headers=course_fixture.headers)
+ course_fixture.session.patch(discussions_url, data=discussions_data, headers=course_fixture.headers)
def disable_cohorting(self, course_fixture):
"""
diff --git a/common/test/acceptance/tests/discussion/test_cohort_management.py b/common/test/acceptance/tests/discussion/test_cohort_management.py
index c3546f8a60..815971b732 100644
--- a/common/test/acceptance/tests/discussion/test_cohort_management.py
+++ b/common/test/acceptance/tests/discussion/test_cohort_management.py
@@ -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):
"""
diff --git a/common/test/acceptance/tests/discussion/test_discussion_management.py b/common/test/acceptance/tests/discussion/test_discussion_management.py
new file mode 100644
index 0000000000..f49109bc8f
--- /dev/null
+++ b/common/test/acceptance/tests/discussion/test_discussion_management.py
@@ -0,0 +1,273 @@
+# -*- coding: utf-8 -*-
+"""
+End-to-end tests related to the divided discussion management on the LMS Instructor Dashboard
+"""
+
+from nose.plugins.attrib import attr
+from common.test.acceptance.tests.discussion.helpers import CohortTestMixin
+from common.test.acceptance.tests.helpers import UniqueCourseTest
+from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
+from common.test.acceptance.pages.lms.auto_auth import AutoAuthPage
+from common.test.acceptance.pages.lms.instructor_dashboard import InstructorDashboardPage
+
+import uuid
+
+
+@attr(shard=6)
+class DividedDiscussionTopicsTest(UniqueCourseTest, CohortTestMixin):
+ """
+ Tests for dividing the inline and course-wide discussion topics.
+ """
+ def setUp(self):
+ """
+ Set up a discussion topic
+ """
+ super(DividedDiscussionTopicsTest, 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'
+
+ def divided_discussion_topics_are_visible(self):
+ """
+ Assert that discussion topics are visible with appropriate content.
+ """
+ self.assertTrue(self.discussion_management_page.discussion_topics_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 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.
+ confirmation_message = self.discussion_management_page.get_divide_discussions_message(key=key)
+ self.assertEqual("Your changes have been saved.", confirmation_message)
+
+ # save button disabled again.
+ self.assertTrue(self.discussion_management_page.is_save_button_disabled(key))
+
+ def reload_page(self):
+ """
+ Refresh the page.
+ """
+ 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.divided_discussion_topics_are_visible()
+
+ 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.divided_discussion_topics_are_visible()
+
+ 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.divided_discussion_topics_are_visible()
+
+ # 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.divided_discussion_topics_are_visible()
+ # 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.divided_discussion_topics_are_visible()
+
+ 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.divided_discussion_topics_are_visible()
+
+ # 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.divided_discussion_topics_are_visible()
+
+ # 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())
diff --git a/lms/djangoapps/django_comment_client/tests/test_utils.py b/lms/djangoapps/django_comment_client/tests/test_utils.py
index d1a57384de..26ce771cba 100644
--- a/lms/djangoapps/django_comment_client/tests/test_utils.py
+++ b/lms/djangoapps/django_comment_client/tests/test_utils.py
@@ -26,7 +26,7 @@ from courseware.tests.factories import InstructorFactory
from courseware.tabs import get_course_tab_list
from openedx.core.djangoapps.course_groups import cohorts
from openedx.core.djangoapps.course_groups.cohorts import set_course_cohorted
-from openedx.core.djangoapps.course_groups.tests.helpers import config_course_cohorts, topic_name_to_id, CohortFactory
+from openedx.core.djangoapps.course_groups.tests.helpers import config_course_cohorts, config_course_discussions, topic_name_to_id, CohortFactory
from student.tests.factories import UserFactory, AdminFactory, CourseEnrollmentFactory
from openedx.core.djangoapps.content.course_structures.models import CourseStructure
from openedx.core.djangoapps.util.testing import ContentGroupTestCase
@@ -478,7 +478,7 @@ class CategoryMapTestCase(CategoryMapTestMixin, ModuleStoreTestCase):
}
)
- def test_inline_with_always_cohort_inline_discussion_flag(self):
+ def test_inline_with_always_divide_inline_discussion_flag(self):
self.create_discussion("Chapter", "Discussion")
set_discussion_division_settings(self.course.id, enable_cohorts=True, always_divide_inline_discussions=True)
@@ -502,7 +502,7 @@ class CategoryMapTestCase(CategoryMapTestMixin, ModuleStoreTestCase):
}
)
- def test_inline_without_always_cohort_inline_discussion_flag(self):
+ def test_inline_without_always_divide_inline_discussion_flag(self):
self.create_discussion("Chapter", "Discussion")
set_discussion_division_settings(self.course.id, enable_cohorts=True)
@@ -1301,15 +1301,16 @@ class IsCommentableDividedTestCase(ModuleStoreTestCase):
)
# not cohorted
- config_course_cohorts(course, is_cohorted=False, discussion_topics=["General", "Feedback"])
-
+ config_course_cohorts(course, is_cohorted=False)
+ config_course_discussions(course, discussion_topics=["General", "Feedback"])
self.assertFalse(
utils.is_commentable_divided(course.id, to_id("General")),
"Course isn't cohorted"
)
# cohorted, but top level topics aren't
- config_course_cohorts(course, is_cohorted=True, discussion_topics=["General", "Feedback"])
+ config_course_cohorts(course, is_cohorted=True)
+ config_course_discussions(course, discussion_topics=["General", "Feedback"])
self.assertTrue(cohorts.is_course_cohorted(course.id))
self.assertFalse(
@@ -1320,10 +1321,9 @@ class IsCommentableDividedTestCase(ModuleStoreTestCase):
# cohorted, including "Feedback" top-level topics aren't
config_course_cohorts(
course,
- is_cohorted=True,
- discussion_topics=["General", "Feedback"],
- divided_discussions=["Feedback"]
+ is_cohorted=True
)
+ config_course_discussions(course, discussion_topics=["General", "Feedback"], divided_discussions=["Feedback"])
self.assertTrue(cohorts.is_course_cohorted(course.id))
self.assertFalse(
@@ -1345,42 +1345,52 @@ class IsCommentableDividedTestCase(ModuleStoreTestCase):
config_course_cohorts(
course,
is_cohorted=True,
+ )
+ config_course_discussions(
+ course,
discussion_topics=["General", "Feedback"],
divided_discussions=["Feedback", "random_inline"]
)
+
self.assertFalse(
utils.is_commentable_divided(course.id, to_id("random")),
"By default, Non-top-level discussions are not cohorted in a cohorted courses."
)
- # if always_cohort_inline_discussions is set to False, non-top-level discussion are always
- # non cohorted unless they are explicitly set in cohorted_discussions
+ # if always_divide_inline_discussions is set to False, non-top-level discussion are always
+ # not divided unless they are explicitly set in divided_discussions
config_course_cohorts(
course,
is_cohorted=True,
+ )
+ config_course_discussions(
+ course,
discussion_topics=["General", "Feedback"],
divided_discussions=["Feedback", "random_inline"],
always_divide_inline_discussions=False
)
+
self.assertFalse(
utils.is_commentable_divided(course.id, to_id("random")),
- "Non-top-level discussion is not cohorted if always_cohort_inline_discussions is False."
+ "Non-top-level discussion is not cohorted if always_divide_inline_discussions is False."
)
self.assertTrue(
utils.is_commentable_divided(course.id, to_id("random_inline")),
- "If always_cohort_inline_discussions set to False, Non-top-level discussion is "
+ "If always_divide_inline_discussions set to False, Non-top-level discussion is "
"cohorted if explicitly set in cohorted_discussions."
)
self.assertTrue(
utils.is_commentable_divided(course.id, to_id("Feedback")),
- "If always_cohort_inline_discussions set to False, top-level discussion are not affected."
+ "If always_divide_inline_discussions set to False, top-level discussion are not affected."
)
def test_is_commentable_divided_team(self):
course = modulestore().get_course(self.toy_course_key)
self.assertFalse(cohorts.is_course_cohorted(course.id))
- config_course_cohorts(course, is_cohorted=True, always_divide_inline_discussions=True)
+ config_course_cohorts(course, is_cohorted=True)
+ config_course_discussions(course, always_divide_inline_discussions=True)
+
team = CourseTeamFactory(course_id=course.id)
# Verify that team discussions are not cohorted, but other discussions are
diff --git a/lms/djangoapps/instructor/views/instructor_dashboard.py b/lms/djangoapps/instructor/views/instructor_dashboard.py
index 5aecf20927..3250e335e6 100644
--- a/lms/djangoapps/instructor/views/instructor_dashboard.py
+++ b/lms/djangoapps/instructor/views/instructor_dashboard.py
@@ -125,6 +125,7 @@ def instructor_dashboard_2(request, course_id):
_section_course_info(course, access),
_section_membership(course, access, is_white_label),
_section_cohort_management(course, access),
+ _section_discussions_management(course, access),
_section_student_admin(course, access),
_section_data_download(course, access),
]
@@ -513,7 +514,6 @@ def _section_cohort_management(course, access):
),
'cohorts_url': reverse('cohorts', kwargs={'course_key_string': unicode(course_key)}),
'upload_cohorts_csv_url': reverse('add_users_to_cohorts', kwargs={'course_id': unicode(course_key)}),
- 'discussion_topics_url': reverse('cohort_discussion_topics', kwargs={'course_key_string': unicode(course_key)}),
'verified_track_cohorting_url': reverse(
'verified_track_cohorting', kwargs={'course_key_string': unicode(course_key)}
),
@@ -521,6 +521,21 @@ def _section_cohort_management(course, access):
return section_data
+def _section_discussions_management(course, access):
+ """ Provide data for the corresponding discussion management section """
+ course_key = course.id
+ section_data = {
+ 'section_key': 'discussions_management',
+ 'section_display_name': _('Discussions'),
+ 'discussion_topics_url': reverse('discussion_topics', kwargs={'course_key_string': unicode(course_key)}),
+ 'course_discussion_settings': reverse(
+ 'course_discussions_settings',
+ kwargs={'course_key_string': unicode(course_key)}
+ ),
+ }
+ return section_data
+
+
def _is_small_course(course_key):
""" Compares against MAX_ENROLLMENT_INSTR_BUTTONS to determine if course enrollment is considered small. """
is_small_course = False
diff --git a/lms/envs/common.py b/lms/envs/common.py
index bb4656451e..13b86f4a20 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -1759,6 +1759,7 @@ REQUIRE_JS_PATH_OVERRIDES = {
'js/student_profile/views/learner_profile_factory': 'js/student_profile/views/learner_profile_factory.js',
'js/courseware/courseware_factory': 'js/courseware/courseware_factory.js',
'js/groups/views/cohorts_dashboard_factory': 'js/groups/views/cohorts_dashboard_factory.js',
+ 'js/groups/discussions_management/discussions_dashboard_factory': 'js/discussions_management/views/discussions_dashboard_factory.js',
'draggabilly': 'js/vendor/draggabilly.js',
'hls': 'common/js/vendor/hls.js'
}
diff --git a/lms/static/js/groups/models/cohort_discussions.js b/lms/static/js/discussions_management/models/course_discussions_detail.js
similarity index 68%
rename from lms/static/js/groups/models/cohort_discussions.js
rename to lms/static/js/discussions_management/models/course_discussions_detail.js
index 8a309cb96b..13df59db79 100644
--- a/lms/static/js/groups/models/cohort_discussions.js
+++ b/lms/static/js/discussions_management/models/course_discussions_detail.js
@@ -1,12 +1,12 @@
(function(define) {
'use strict';
define(['backbone'], function(Backbone) {
- var DiscussionTopicsSettingsModel = Backbone.Model.extend({
+ var CourseDiscussionTopicDetailsModel = Backbone.Model.extend({
defaults: {
course_wide_discussions: {},
inline_discussions: {}
}
});
- return DiscussionTopicsSettingsModel;
+ return CourseDiscussionTopicDetailsModel;
});
}).call(this, define || RequireJS.define);
diff --git a/lms/static/js/discussions_management/models/course_discussions_settings.js b/lms/static/js/discussions_management/models/course_discussions_settings.js
new file mode 100644
index 0000000000..6eb50f0d5f
--- /dev/null
+++ b/lms/static/js/discussions_management/models/course_discussions_settings.js
@@ -0,0 +1,14 @@
+(function(define) {
+ 'use strict';
+ define(['backbone'], function(Backbone) {
+ var CourseDiscussionsSettingsModel = Backbone.Model.extend({
+ idAttribute: 'id',
+ defaults: {
+ divided_inline_discussions: [],
+ divided_course_wide_discussions: [],
+ always_divide_inline_discussions: false
+ }
+ });
+ return CourseDiscussionsSettingsModel;
+ });
+}).call(this, define || RequireJS.define);
diff --git a/lms/static/js/discussions_management/views/discussions.js b/lms/static/js/discussions_management/views/discussions.js
new file mode 100644
index 0000000000..a784ee74ac
--- /dev/null
+++ b/lms/static/js/discussions_management/views/discussions.js
@@ -0,0 +1,49 @@
+(function(define) {
+ 'use strict';
+ define(['jquery', 'underscore', 'backbone', 'gettext',
+ 'js/discussions_management/views/divided_discussions_inline',
+ 'js/discussions_management/views/divided_discussions_course_wide',
+ 'edx-ui-toolkit/js/utils/html-utils'
+ ],
+
+ function($, _, Backbone, gettext, InlineDiscussionsView, CourseWideDiscussionsView, HtmlUtils) {
+ var DiscussionsView = Backbone.View.extend({
+
+ initialize: function(options) {
+ this.template = HtmlUtils.template($('#discussions-tpl').text());
+ this.context = options.context;
+ this.discussionSettings = options.discussionSettings;
+ },
+
+ render: function() {
+ HtmlUtils.setHtml(this.$el, this.template({}));
+ this.showDiscussionTopics();
+ return this;
+ },
+
+ getSectionCss: function(section) {
+ return ".instructor-nav .nav-item [data-section='" + section + "']";
+ },
+
+ showDiscussionTopics: function() {
+ var dividedDiscussionsElement = this.$('.discussions-nav');
+ if (!this.CourseWideDiscussionsView) {
+ this.CourseWideDiscussionsView = new CourseWideDiscussionsView({
+ el: dividedDiscussionsElement,
+ model: this.context.courseDiscussionTopicDetailsModel,
+ discussionSettings: this.discussionSettings
+ }).render();
+ }
+
+ if (!this.InlineDiscussionsView) {
+ this.InlineDiscussionsView = new InlineDiscussionsView({
+ el: dividedDiscussionsElement,
+ model: this.context.courseDiscussionTopicDetailsModel,
+ discussionSettings: this.discussionSettings
+ }).render();
+ }
+ }
+ });
+ return DiscussionsView;
+ });
+}).call(this, define || RequireJS.define);
diff --git a/lms/static/js/discussions_management/views/discussions_dashboard_factory.js b/lms/static/js/discussions_management/views/discussions_dashboard_factory.js
new file mode 100644
index 0000000000..c2f0cdf8d5
--- /dev/null
+++ b/lms/static/js/discussions_management/views/discussions_dashboard_factory.js
@@ -0,0 +1,33 @@
+(function(define) {
+ 'use strict';
+ define('js/discussions_management/views/discussions_dashboard_factory',
+ ['jquery', 'js/discussions_management/views/discussions',
+ 'js/discussions_management/models/course_discussions_detail',
+ 'js/discussions_management/models/course_discussions_settings'],
+ function($, DiscussionsView, CourseDiscussionTopicDetailsModel, CourseDiscussionsSettingsModel) {
+ return function() {
+ var courseDiscussionSettings = new CourseDiscussionsSettingsModel(),
+ discussionTopicsSettings = new CourseDiscussionTopicDetailsModel(),
+ $discussionsManagementElement = $('.discussions-management'),
+ discussionsView;
+
+ courseDiscussionSettings.url = $discussionsManagementElement.data('course-discussion-settings-url');
+ discussionTopicsSettings.url = $discussionsManagementElement.data('discussion-topics-url');
+
+ discussionsView = new DiscussionsView({
+ el: $discussionsManagementElement,
+ discussionSettings: courseDiscussionSettings,
+ context: {
+ courseDiscussionTopicDetailsModel: discussionTopicsSettings
+ }
+ });
+
+ courseDiscussionSettings.fetch().done(function() {
+ discussionTopicsSettings.fetch().done(function() {
+ discussionsView.render();
+ });
+ });
+ };
+ });
+}).call(this, define || RequireJS.define);
+
diff --git a/lms/static/js/groups/views/cohort_discussions.js b/lms/static/js/discussions_management/views/divided_discussions.js
similarity index 74%
rename from lms/static/js/groups/views/cohort_discussions.js
rename to lms/static/js/discussions_management/views/divided_discussions.js
index 8b13aa5056..be498d6196 100644
--- a/lms/static/js/groups/views/cohort_discussions.js
+++ b/lms/static/js/discussions_management/views/divided_discussions.js
@@ -1,8 +1,9 @@
(function(define) {
'use strict';
define(['jquery', 'underscore', 'backbone', 'gettext', 'js/models/notification', 'js/views/notification'],
- function($, _, Backbone) {
- var CohortDiscussionConfigurationView = Backbone.View.extend({
+ function($, _, Backbone, gettext) {
+ /* global NotificationModel, NotificationView */
+ var DividedDiscussionConfigurationView = Backbone.View.extend({
/**
* Add/Remove the disabled attribute on given element.
@@ -14,53 +15,53 @@
},
/**
- * Returns the cohorted discussions list.
+ * Returns the divided discussions list.
* @param {string} selector - To select the discussion elements whose ids to return.
- * @returns {Array} - Cohorted discussions.
+ * @returns {Array} - Divided discussions.
*/
- getCohortedDiscussions: function(selector) {
+ getDividedDiscussions: function(selector) {
var self = this,
- cohortedDiscussions = [];
+ dividedDiscussions = [];
_.each(self.$(selector), function(topic) {
- cohortedDiscussions.push($(topic).data('id'));
+ dividedDiscussions.push($(topic).data('id'));
});
- return cohortedDiscussions;
+ return dividedDiscussions;
},
/**
- * Save the cohortSettings' changed attributes to the server via PATCH method.
+ * Save the discussionSettings' changed attributes to the server via PATCH method.
* It shows the error message(s) if any.
* @param {object} $element - Messages would be shown before this element.
* @param {object} fieldData - Data to update on the server.
*/
saveForm: function($element, fieldData) {
var self = this,
- cohortSettingsModel = this.cohortSettings,
+ discussionSettingsModel = this.discussionSettings,
saveOperation = $.Deferred(),
showErrorMessage;
-
- showErrorMessage = function(message, $element) {
+ showErrorMessage = function(message) {
self.showMessage(message, $element, 'error');
};
this.removeNotification();
- cohortSettingsModel.save(
+ discussionSettingsModel.save(
fieldData, {patch: true, wait: true}
).done(function() {
saveOperation.resolve();
}).fail(function(result) {
- var errorMessage = null;
+ var errorMessage = null,
+ jsonResponse;
try {
- var jsonResponse = JSON.parse(result.responseText);
+ jsonResponse = JSON.parse(result.responseText);
errorMessage = jsonResponse.error;
} catch (e) {
// Ignore the exception and show the default error message instead.
}
if (!errorMessage) {
- errorMessage = gettext("We've encountered an error. Refresh your browser and then try again.");
+ errorMessage = gettext("We've encountered an error. Refresh your browser and then try again."); // eslint-disable-line max-len
}
- showErrorMessage(errorMessage, $element);
+ showErrorMessage(errorMessage);
saveOperation.reject();
});
return saveOperation.promise();
@@ -92,6 +93,6 @@
}
});
- return CohortDiscussionConfigurationView;
+ return DividedDiscussionConfigurationView;
});
}).call(this, define || RequireJS.define);
diff --git a/lms/static/js/groups/views/cohort_discussions_course_wide.js b/lms/static/js/discussions_management/views/divided_discussions_course_wide.js
similarity index 77%
rename from lms/static/js/groups/views/cohort_discussions_course_wide.js
rename to lms/static/js/discussions_management/views/divided_discussions_course_wide.js
index 71fdcd750e..774f734077 100644
--- a/lms/static/js/groups/views/cohort_discussions_course_wide.js
+++ b/lms/static/js/discussions_management/views/divided_discussions_course_wide.js
@@ -1,21 +1,21 @@
(function(define) {
'use strict';
- define(['jquery', 'underscore', 'backbone', 'gettext', 'js/groups/views/cohort_discussions',
+ define(['jquery', 'underscore', 'backbone', 'gettext', 'js/discussions_management/views/divided_discussions',
'edx-ui-toolkit/js/utils/html-utils'],
- function($, _, Backbone, gettext, CohortDiscussionConfigurationView, HtmlUtils) {
- var CourseWideDiscussionsView = CohortDiscussionConfigurationView.extend({
+ function($, _, Backbone, gettext, DividedDiscussionConfigurationView, HtmlUtils) {
+ var CourseWideDiscussionsView = DividedDiscussionConfigurationView.extend({
events: {
'change .check-discussion-subcategory-course-wide': 'discussionCategoryStateChanged',
'click .cohort-course-wide-discussions-form .action-save': 'saveCourseWideDiscussionsForm'
},
initialize: function(options) {
- this.template = HtmlUtils.template($('#cohort-discussions-course-wide-tpl').text());
- this.cohortSettings = options.cohortSettings;
+ this.template = HtmlUtils.template($('#divided-discussions-course-wide-tpl').text());
+ this.discussionSettings = options.discussionSettings;
},
render: function() {
- HtmlUtils.setHtml(this.$('.cohort-course-wide-discussions-nav'), this.template({
+ HtmlUtils.setHtml(this.$('.course-wide-discussions-nav'), this.template({
courseWideTopicsHtml: this.getCourseWideDiscussionsHtml(
this.model.get('course_wide_discussions')
)
@@ -56,25 +56,27 @@
},
/**
- * Sends the cohorted_course_wide_discussions to the server and renders the view.
+ * Sends the courseWideDividedDiscussions to the server and renders the view.
*/
saveCourseWideDiscussionsForm: function(event) {
- event.preventDefault();
-
var self = this,
- courseWideCohortedDiscussions = self.getCohortedDiscussions(
+ courseWideDividedDiscussions = self.getDividedDiscussions(
'.check-discussion-subcategory-course-wide:checked'
),
- fieldData = {cohorted_course_wide_discussions: courseWideCohortedDiscussions};
+ fieldData = {divided_course_wide_discussions: courseWideDividedDiscussions};
+
+ event.preventDefault();
self.saveForm(self.$('.course-wide-discussion-topics'), fieldData)
.done(function() {
self.model.fetch()
.done(function() {
self.render();
- self.showMessage(gettext('Your changes have been saved.'), self.$('.course-wide-discussion-topics'));
+ self.showMessage(gettext('Your changes have been saved.'),
+ self.$('.course-wide-discussion-topics')
+ );
}).fail(function() {
- var errorMessage = gettext("We've encountered an error. Refresh your browser and then try again.");
+ var errorMessage = gettext("We've encountered an error. Refresh your browser and then try again."); // eslint-disable-line max-len
self.showMessage(errorMessage, self.$('.course-wide-discussion-topics'), 'error');
});
});
diff --git a/lms/static/js/groups/views/cohort_discussions_inline.js b/lms/static/js/discussions_management/views/divided_discussions_inline.js
similarity index 75%
rename from lms/static/js/groups/views/cohort_discussions_inline.js
rename to lms/static/js/discussions_management/views/divided_discussions_inline.js
index 817b07d58f..a965b81ec9 100644
--- a/lms/static/js/groups/views/cohort_discussions_inline.js
+++ b/lms/static/js/discussions_management/views/divided_discussions_inline.js
@@ -1,9 +1,9 @@
(function(define) {
'use strict';
- define(['jquery', 'underscore', 'backbone', 'gettext', 'js/groups/views/cohort_discussions',
+ define(['jquery', 'underscore', 'backbone', 'gettext', 'js/discussions_management/views/divided_discussions',
'edx-ui-toolkit/js/utils/html-utils', 'js/vendor/jquery.qubit'],
- function($, _, Backbone, gettext, CohortDiscussionConfigurationView, HtmlUtils) {
- var InlineDiscussionsView = CohortDiscussionConfigurationView.extend({
+ function($, _, Backbone, gettext, DividedDiscussionConfigurationView, HtmlUtils) {
+ var InlineDiscussionsView = DividedDiscussionConfigurationView.extend({
events: {
'change .check-discussion-category': 'setSaveButton',
'change .check-discussion-subcategory-inline': 'setSaveButton',
@@ -13,17 +13,19 @@
},
initialize: function(options) {
- this.template = HtmlUtils.template($('#cohort-discussions-inline-tpl').text());
- this.cohortSettings = options.cohortSettings;
+ this.template = HtmlUtils.template($('#divided-discussions-inline-tpl').text());
+ this.discussionSettings = options.discussionSettings;
},
render: function() {
- var alwaysCohortInlineDiscussions = this.cohortSettings.get('always_cohort_inline_discussions'),
- inline_discussions = this.model.get('inline_discussions');
+ var inlineDiscussions = this.model.get('inline_discussions'),
+ alwaysDivideInlineDiscussions = this.discussionSettings.get(
+ 'always_divide_inline_discussions'
+ );
- HtmlUtils.setHtml(this.$('.cohort-inline-discussions-nav'), this.template({
- inlineDiscussionTopicsHtml: this.getInlineDiscussionsHtml(inline_discussions),
- alwaysCohortInlineDiscussions: alwaysCohortInlineDiscussions
+ HtmlUtils.setHtml(this.$('.inline-discussions-nav'), this.template({
+ inlineDiscussionTopicsHtml: this.getInlineDiscussionsHtml(inlineDiscussions),
+ alwaysDivideInlineDiscussions: alwaysDivideInlineDiscussions
}));
// Provides the semantics for a nested list of tri-state checkboxes.
@@ -32,7 +34,7 @@
// based on the checked values of any checkboxes in child elements of the DOM.
this.$('ul.inline-topics').qubit();
- this.setElementsEnabled(alwaysCohortInlineDiscussions, true);
+ this.setElementsEnabled(alwaysDivideInlineDiscussions, true);
},
/**
@@ -99,45 +101,48 @@
*
* Enable/Disable the category and sub-category checkboxes.
* Enable/Disable the save button.
- * @param {bool} enable_checkboxes - The flag to enable/disable the checkboxes.
- * @param {bool} enable_save_button - The flag to enable/disable the save button.
+ * @param {bool} enableCheckboxes - The flag to enable/disable the checkboxes.
+ * @param {bool} enableSaveButton - The flag to enable/disable the save button.
*/
- setElementsEnabled: function(enable_checkboxes, enable_save_button) {
- this.setDisabled(this.$('.check-discussion-category'), enable_checkboxes);
- this.setDisabled(this.$('.check-discussion-subcategory-inline'), enable_checkboxes);
- this.setDisabled(this.$('.cohort-inline-discussions-form .action-save'), enable_save_button);
+ setElementsEnabled: function(enableCheckboxes, enableSaveButton) {
+ this.setDisabled(this.$('.check-discussion-category'), enableCheckboxes);
+ this.setDisabled(this.$('.check-discussion-subcategory-inline'), enableCheckboxes);
+ this.setDisabled(this.$('.cohort-inline-discussions-form .action-save'), enableSaveButton);
},
/**
* Enables the save button for inline discussions.
*/
- setSaveButton: function(event) {
+ setSaveButton: function() {
this.setDisabled(this.$('.cohort-inline-discussions-form .action-save'), false);
},
/**
- * Sends the cohorted_inline_discussions to the server and renders the view.
+ * Sends the dividedInlineDiscussions to the server and renders the view.
*/
saveInlineDiscussionsForm: function(event) {
- event.preventDefault();
-
var self = this,
- cohortedInlineDiscussions = self.getCohortedDiscussions(
+ dividedInlineDiscussions = self.getDividedDiscussions(
'.check-discussion-subcategory-inline:checked'
),
fieldData = {
- cohorted_inline_discussions: cohortedInlineDiscussions,
- always_cohort_inline_discussions: self.$('.check-all-inline-discussions').prop('checked')
+ divided_inline_discussions: dividedInlineDiscussions,
+ always_divide_inline_discussions: self.$(
+ '.check-all-inline-discussions'
+ ).prop('checked')
};
+ event.preventDefault();
+
self.saveForm(self.$('.inline-discussion-topics'), fieldData)
.done(function() {
self.model.fetch()
.done(function() {
self.render();
- self.showMessage(gettext('Your changes have been saved.'), self.$('.inline-discussion-topics'));
+ self.showMessage(gettext('Your changes have been saved.'),
+ self.$('.inline-discussion-topics'));
}).fail(function() {
- var errorMessage = gettext("We've encountered an error. Refresh your browser and then try again.");
+ var errorMessage = gettext("We've encountered an error. Refresh your browser and then try again."); // eslint-disable-line max-len
self.showMessage(errorMessage, self.$('.inline-discussion-topics'), 'error');
});
});
diff --git a/lms/static/js/groups/models/course_cohort_settings.js b/lms/static/js/groups/models/course_cohort_settings.js
index 213a14ec97..1c2a9c9d14 100644
--- a/lms/static/js/groups/models/course_cohort_settings.js
+++ b/lms/static/js/groups/models/course_cohort_settings.js
@@ -4,10 +4,7 @@
var CourseCohortSettingsModel = Backbone.Model.extend({
idAttribute: 'id',
defaults: {
- is_cohorted: false,
- cohorted_inline_discussions: [],
- cohorted_course_wide_discussions: [],
- always_cohort_inline_discussions: false
+ is_cohorted: false
}
});
return CourseCohortSettingsModel;
diff --git a/lms/static/js/groups/views/cohorts.js b/lms/static/js/groups/views/cohorts.js
index 8c9614c680..44d2c0cdda 100644
--- a/lms/static/js/groups/views/cohorts.js
+++ b/lms/static/js/groups/views/cohorts.js
@@ -4,13 +4,11 @@
'js/groups/models/verified_track_settings',
'js/groups/views/cohort_editor', 'js/groups/views/cohort_form',
'js/groups/views/course_cohort_settings_notification',
- 'js/groups/views/cohort_discussions_inline', 'js/groups/views/cohort_discussions_course_wide',
'js/groups/views/verified_track_settings_notification',
'edx-ui-toolkit/js/utils/html-utils',
'js/views/file_uploader', 'js/models/notification', 'js/views/notification', 'string_utils'],
function($, _, Backbone, gettext, CohortModel, VerifiedTrackSettingsModel, CohortEditorView, CohortFormView,
- CourseCohortSettingsNotificationView, InlineDiscussionsView, CourseWideDiscussionsView,
- VerifiedTrackSettingsNotificationView, HtmlUtils) {
+ CourseCohortSettingsNotificationView, VerifiedTrackSettingsNotificationView, HtmlUtils) {
var hiddenClass = 'is-hidden',
disabledClass = 'is-disabled',
enableCohortsSelector = '.cohorts-state';
@@ -25,7 +23,6 @@
'click .cohort-management-add-form .action-cancel': 'cancelAddCohortForm',
'click .link-cross-reference': 'showSection',
'click .toggle-cohort-management-secondary': 'showCsvUpload',
- 'click .toggle-cohort-management-discussions': 'showDiscussionTopics'
},
initialize: function(options) {
@@ -306,27 +303,6 @@
}).render();
}
},
- showDiscussionTopics: function(event) {
- event.preventDefault();
-
- $(event.currentTarget).addClass(hiddenClass);
- var cohortDiscussionsElement = this.$('.cohort-discussions-nav').removeClass(hiddenClass);
-
- if (!this.CourseWideDiscussionsView) {
- this.CourseWideDiscussionsView = new CourseWideDiscussionsView({
- el: cohortDiscussionsElement,
- model: this.context.discussionTopicsSettingsModel,
- cohortSettings: this.cohortSettings
- }).render();
- }
- if (!this.InlineDiscussionsView) {
- this.InlineDiscussionsView = new InlineDiscussionsView({
- el: cohortDiscussionsElement,
- model: this.context.discussionTopicsSettingsModel,
- cohortSettings: this.cohortSettings
- }).render();
- }
- },
getSectionCss: function(section) {
return ".instructor-nav .nav-item [data-section='" + section + "']";
diff --git a/lms/static/js/groups/views/cohorts_dashboard_factory.js b/lms/static/js/groups/views/cohorts_dashboard_factory.js
index e85ade7bdf..02e0a73b07 100644
--- a/lms/static/js/groups/views/cohorts_dashboard_factory.js
+++ b/lms/static/js/groups/views/cohorts_dashboard_factory.js
@@ -1,8 +1,8 @@
(function(define, undefined) {
'use strict';
define(['jquery', 'js/groups/views/cohorts', 'js/groups/collections/cohort', 'js/groups/models/course_cohort_settings',
- 'js/groups/models/cohort_discussions', 'js/groups/models/content_group'],
- function($, CohortsView, CohortCollection, CourseCohortSettingsModel, DiscussionTopicsSettingsModel, ContentGroupModel) {
+ 'js/groups/models/content_group'],
+ function($, CohortsView, CohortCollection, CourseCohortSettingsModel, ContentGroupModel) {
return function(contentGroups, studioGroupConfigurationsUrl) {
var contentGroupModels = $.map(contentGroups, function(group) {
return new ContentGroupModel({
@@ -14,33 +14,27 @@
var cohorts = new CohortCollection(),
courseCohortSettings = new CourseCohortSettingsModel(),
- discussionTopicsSettings = new DiscussionTopicsSettingsModel();
+ $cohortManagementElement = $('.cohort-management');
- var cohortManagementElement = $('.cohort-management');
-
- cohorts.url = cohortManagementElement.data('cohorts_url');
- courseCohortSettings.url = cohortManagementElement.data('course_cohort_settings_url');
- discussionTopicsSettings.url = cohortManagementElement.data('discussion-topics-url');
+ cohorts.url = $cohortManagementElement.data('cohorts_url');
+ courseCohortSettings.url = $cohortManagementElement.data('course_cohort_settings_url');
var cohortsView = new CohortsView({
- el: cohortManagementElement,
+ el: $cohortManagementElement,
model: cohorts,
contentGroups: contentGroupModels,
cohortSettings: courseCohortSettings,
context: {
- discussionTopicsSettingsModel: discussionTopicsSettings,
- uploadCohortsCsvUrl: cohortManagementElement.data('upload_cohorts_csv_url'),
- verifiedTrackCohortingUrl: cohortManagementElement.data('verified_track_cohorting_url'),
+ uploadCohortsCsvUrl: $cohortManagementElement.data('upload_cohorts_csv_url'),
+ verifiedTrackCohortingUrl: $cohortManagementElement.data('verified_track_cohorting_url'),
studioGroupConfigurationsUrl: studioGroupConfigurationsUrl,
- isCcxEnabled: cohortManagementElement.data('is_ccx_enabled')
+ isCcxEnabled: $cohortManagementElement.data('is_ccx_enabled')
}
});
cohorts.fetch().done(function() {
courseCohortSettings.fetch().done(function() {
- discussionTopicsSettings.fetch().done(function() {
- cohortsView.render();
- });
+ cohortsView.render();
});
});
};
diff --git a/lms/static/js/instructor_dashboard/discussions_management.js b/lms/static/js/instructor_dashboard/discussions_management.js
new file mode 100644
index 0000000000..1e883b708b
--- /dev/null
+++ b/lms/static/js/instructor_dashboard/discussions_management.js
@@ -0,0 +1,12 @@
+(function() {
+ 'use strict';
+
+ function DiscussionsManagement($section) {
+ this.$section = $section;
+ this.$section.data('wrapper', this);
+ }
+
+ DiscussionsManagement.prototype.onClickTitle = function() {};
+
+ window.InstructorDashboard.sections.DiscussionsManagement = DiscussionsManagement;
+}).call(this);
diff --git a/lms/static/js/instructor_dashboard/instructor_dashboard.js b/lms/static/js/instructor_dashboard/instructor_dashboard.js
index 272e241855..d5352c7b72 100644
--- a/lms/static/js/instructor_dashboard/instructor_dashboard.js
+++ b/lms/static/js/instructor_dashboard/instructor_dashboard.js
@@ -187,6 +187,9 @@ such that the value can be defined later than this assignment (file load order).
}, {
constructor: window.InstructorDashboard.sections.CohortManagement,
$element: idashContent.find('.' + CSS_IDASH_SECTION + '#cohort_management')
+ }, {
+ constructor: window.InstructorDashboard.sections.DiscussionsManagement,
+ $element: idashContent.find('.' + CSS_IDASH_SECTION + '#discussions_management')
}, {
constructor: window.InstructorDashboard.sections.Certificates,
$element: idashContent.find('.' + CSS_IDASH_SECTION + '#certificates')
diff --git a/lms/static/js/spec/groups/views/cohorts_spec.js b/lms/static/js/spec/groups/views/cohorts_spec.js
index ba29285340..96ae5576de 100644
--- a/lms/static/js/spec/groups/views/cohorts_spec.js
+++ b/lms/static/js/spec/groups/views/cohorts_spec.js
@@ -1,14 +1,11 @@
define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
- 'common/js/spec_helpers/template_helpers',
- 'js/groups/views/cohorts', 'js/groups/collections/cohort', 'js/groups/models/content_group',
- 'js/groups/models/course_cohort_settings', 'js/utils/animation', 'js/vendor/jquery.qubit',
- 'js/groups/views/course_cohort_settings_notification', 'js/groups/models/cohort_discussions',
- 'js/groups/views/cohort_discussions', 'js/groups/views/cohort_discussions_course_wide',
- 'js/groups/views/cohort_discussions_inline'
- ],
+ 'common/js/spec_helpers/template_helpers',
+ 'js/groups/views/cohorts', 'js/groups/collections/cohort', 'js/groups/models/content_group',
+ 'js/groups/models/course_cohort_settings', 'js/utils/animation', 'js/vendor/jquery.qubit',
+ 'js/groups/views/course_cohort_settings_notification'
+],
function(Backbone, $, AjaxHelpers, TemplateHelpers, CohortsView, CohortCollection, ContentGroupModel,
- CourseCohortSettingsModel, AnimationUtil, Qubit, CourseCohortSettingsNotificationView, DiscussionTopicsSettingsModel,
- CohortDiscussionsView, CohortCourseWideDiscussionsView, CohortInlineDiscussionsView) {
+ CourseCohortSettingsModel, AnimationUtil, Qubit, CourseCohortSettingsNotificationView) {
'use strict';
describe('Cohorts View', function() {
@@ -20,16 +17,7 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
getAddModal, selectContentGroup, clearContentGroup,
saveFormAndExpectErrors, createMockCohortSettings, MOCK_COHORTED_USER_PARTITION_ID,
MOCK_UPLOAD_COHORTS_CSV_URL, MOCK_STUDIO_ADVANCED_SETTINGS_URL, MOCK_STUDIO_GROUP_CONFIGURATIONS_URL,
- MOCK_VERIFIED_TRACK_COHORTING_URL, MOCK_MANUAL_ASSIGNMENT, MOCK_RANDOM_ASSIGNMENT,
- createMockCohortDiscussionsJson, createMockCohortDiscussions, showAndAssertDiscussionTopics;
-
- // Selectors
- var discussionsToggle = '.toggle-cohort-management-discussions',
- inlineDiscussionsFormCss = '.cohort-inline-discussions-form',
- courseWideDiscussionsFormCss = '.cohort-course-wide-discussions-form',
- courseWideDiscussionsSaveButtonCss = '.cohort-course-wide-discussions-form .action-save',
- inlineDiscussionsSaveButtonCss = '.cohort-inline-discussions-form .action-save',
- inlineDiscussionsForm, courseWideDiscussionsForm;
+ MOCK_VERIFIED_TRACK_COHORTING_URL, MOCK_MANUAL_ASSIGNMENT, MOCK_RANDOM_ASSIGNMENT;
MOCK_MANUAL_ASSIGNMENT = 'manual';
MOCK_RANDOM_ASSIGNMENT = 'random';
@@ -70,66 +58,16 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
];
};
- createMockCohortSettingsJson = function(isCohorted, cohortedInlineDiscussions, cohortedCourseWideDiscussions, alwaysCohortInlineDiscussions) {
+ createMockCohortSettingsJson = function(isCohorted) {
return {
id: 0,
- is_cohorted: isCohorted || false,
- cohorted_inline_discussions: cohortedInlineDiscussions || [],
- cohorted_course_wide_discussions: cohortedCourseWideDiscussions || [],
- always_cohort_inline_discussions: alwaysCohortInlineDiscussions || false
+ is_cohorted: isCohorted || false
};
};
- createMockCohortSettings = function(isCohorted, cohortedInlineDiscussions, cohortedCourseWideDiscussions, alwaysCohortInlineDiscussions) {
+ createMockCohortSettings = function(isCohorted) {
return new CourseCohortSettingsModel(
- createMockCohortSettingsJson(isCohorted, cohortedInlineDiscussions, cohortedCourseWideDiscussions, alwaysCohortInlineDiscussions)
- );
- };
-
- createMockCohortDiscussionsJson = function(allCohorted) {
- return {
- course_wide_discussions: {
- children: [['Topic_C_1', 'entry'], ['Topic_C_2', 'entry']],
- entries: {
- Topic_C_1: {
- sort_key: null,
- is_divided: true,
- id: 'Topic_C_1'
- },
- Topic_C_2: {
- sort_key: null,
- is_divided: false,
- id: 'Topic_C_2'
- }
- }
- },
- inline_discussions: {
- subcategories: {
- Topic_I_1: {
- subcategories: {},
- children: [['Inline_Discussion_1', 'entry'], ['Inline_Discussion_2', 'entry']],
- entries: {
- Inline_Discussion_1: {
- sort_key: null,
- is_divided: true,
- id: 'Inline_Discussion_1'
- },
- Inline_Discussion_2: {
- sort_key: null,
- is_divided: allCohorted || false,
- id: 'Inline_Discussion_2'
- }
- }
- }
- },
- children: [['Topic_I_1', 'subcategory']]
- }
- };
- };
-
- createMockCohortDiscussions = function(allCohorted) {
- return new DiscussionTopicsSettingsModel(
- createMockCohortDiscussionsJson(allCohorted)
+ createMockCohortSettingsJson(isCohorted)
);
};
@@ -146,7 +84,7 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
};
createCohortsView = function(test, options) {
- var cohortsJson, cohorts, contentGroups, cohortSettings, cohortDiscussions;
+ var cohortsJson, cohorts, contentGroups, cohortSettings;
options = options || {};
cohortsJson = options.cohorts ? {cohorts: options.cohorts} : createMockCohorts();
cohorts = new CohortCollection(cohortsJson, {parse: true});
@@ -155,16 +93,12 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
cohortSettings.url = '/mock_service/cohorts/settings';
cohorts.url = '/mock_service/cohorts';
- cohortDiscussions = options.cohortDiscussions || createMockCohortDiscussions();
- cohortDiscussions.url = '/mock_service/cohorts/discussion/topics';
-
requests = AjaxHelpers.requests(test);
cohortsView = new CohortsView({
model: cohorts,
contentGroups: contentGroups,
cohortSettings: cohortSettings,
context: {
- discussionTopicsSettingsModel: cohortDiscussions,
uploadCohortsCsvUrl: MOCK_UPLOAD_COHORTS_CSV_URL,
studioAdvancedSettingsUrl: MOCK_STUDIO_ADVANCED_SETTINGS_URL,
studioGroupConfigurationsUrl: MOCK_STUDIO_GROUP_CONFIGURATIONS_URL,
@@ -314,40 +248,6 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
verifyDetailedMessage(expectedTitle, 'error', errors);
};
- showAndAssertDiscussionTopics = function(that) {
- createCohortsView(that);
-
- // Should see the control to toggle cohort discussions.
- expect(cohortsView.$(discussionsToggle)).not.toHaveClass('is-hidden');
- // But discussions form should not be visible until toggle is clicked.
- expect(cohortsView.$(inlineDiscussionsFormCss).length).toBe(0);
- expect(cohortsView.$(courseWideDiscussionsFormCss).length).toBe(0);
-
- expect(cohortsView.$(discussionsToggle).text()).
- toContain('Specify whether discussion topics are divided by cohort');
-
- cohortsView.$(discussionsToggle).click();
- // After toggle is clicked, it should be hidden.
- expect(cohortsView.$(discussionsToggle)).toHaveClass('is-hidden');
-
- // Should see the course wide discussions form and its content
- courseWideDiscussionsForm = cohortsView.$(courseWideDiscussionsFormCss);
- expect(courseWideDiscussionsForm.length).toBe(1);
-
- expect(courseWideDiscussionsForm.text()).
- toContain('Course-Wide Discussion Topics');
- expect(courseWideDiscussionsForm.text()).
- toContain('Select the course-wide discussion topics that you want to divide by cohort.');
-
- // Should see the inline discussions form and its content
- inlineDiscussionsForm = cohortsView.$(inlineDiscussionsFormCss);
- expect(inlineDiscussionsForm.length).toBe(1);
- expect(inlineDiscussionsForm.text()).
- toContain('Content-Specific Discussion Topics');
- expect(inlineDiscussionsForm.text()).
- toContain('Specify whether content-specific discussion topics are divided by cohort.');
- };
-
unknownUserMessage = function(name) {
return 'Unknown user: ' + name;
};
@@ -364,10 +264,6 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-group-header');
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/notification');
TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-state');
- TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-discussions-category');
- TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-discussions-subcategory');
- TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-discussions-course-wide');
- TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/cohort-discussions-inline');
TemplateHelpers.installTemplate('templates/file-upload');
});
@@ -381,8 +277,6 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
// If no cohorts have been created, can't upload a CSV file.
expect(cohortsView.$('.wrapper-cohort-supplemental')).toHaveClass('is-hidden');
- // if no cohorts have been created, can't show the link to discussion topics.
- expect(cohortsView.$('.cohort-discussions-nav')).toHaveClass('is-hidden');
});
it('syncs data when membership tab is clicked', function() {
@@ -422,10 +316,6 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
.toBe("Your file 'upload_file.txt' has been uploaded. Allow a few minutes for processing.");
});
- it('can show discussion topics if cohort exists', function() {
- showAndAssertDiscussionTopics(this);
- });
-
describe('Cohort Selector', function() {
it('has no initial selection', function() {
createCohortsView(this);
@@ -1172,375 +1062,5 @@ define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers
});
});
});
-
- describe('Discussion Topics', function() {
- var createCourseWideView, createInlineView,
- inlineView, courseWideView, assertCohortedTopics;
-
- createCourseWideView = function(that) {
- createCohortsView(that);
-
- courseWideView = new CohortCourseWideDiscussionsView({
- el: cohortsView.$('.cohort-discussions-nav').removeClass('is-hidden'),
- model: cohortsView.context.discussionTopicsSettingsModel,
- cohortSettings: cohortsView.cohortSettings
- });
- courseWideView.render();
- };
-
- createInlineView = function(that, discussionTopicsSettingsModel) {
- createCohortsView(that);
-
- inlineView = new CohortInlineDiscussionsView({
- el: cohortsView.$('.cohort-discussions-nav').removeClass('is-hidden'),
- model: discussionTopicsSettingsModel || cohortsView.context.discussionTopicsSettingsModel,
- cohortSettings: cohortsView.cohortSettings
- });
- inlineView.render();
- };
-
- assertCohortedTopics = function(view, type) {
- expect(view.$('.check-discussion-subcategory-' + type).length).toBe(2);
- expect(view.$('.check-discussion-subcategory-' + type + ':checked').length).toBe(1);
- };
-
- it('renders the view properly', function() {
- showAndAssertDiscussionTopics(this);
- });
-
- describe('Course Wide', function() {
- it('shows the "Save" button as disabled initially', function() {
- createCourseWideView(this);
- expect(courseWideView.$(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeTruthy();
- });
-
- it('has one cohorted and one non-cohorted topic', function() {
- createCourseWideView(this);
-
- assertCohortedTopics(courseWideView, 'course-wide');
-
- expect(courseWideView.$('.cohorted-text').length).toBe(2);
- expect(courseWideView.$('.cohorted-text.hidden').length).toBe(1);
- });
-
- it('enables the "Save" button after changing checkbox', function() {
- createCourseWideView(this);
-
- // save button is disabled.
- expect(courseWideView.$(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeTruthy();
-
- $(courseWideView.$('.check-discussion-subcategory-course-wide')[0]).prop('checked', false).change();
-
- // save button is enabled.
- expect(courseWideView.$(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeFalsy();
- });
-
- it('saves the topic successfully', function() {
- createCourseWideView(this);
-
- $(courseWideView.$('.check-discussion-subcategory-course-wide')[1]).prop('checked', 'checked').change();
- expect(courseWideView.$(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeFalsy();
-
- // Save the updated settings
- courseWideView.$('.action-save').click();
-
- // fake requests for cohort settings with PATCH method.
- AjaxHelpers.expectJsonRequest(
- requests, 'PATCH', '/mock_service/cohorts/settings',
- {cohorted_course_wide_discussions: ['Topic_C_1', 'Topic_C_2']}
- );
- AjaxHelpers.respondWithJson(
- requests,
- {cohorted_course_wide_discussions: ['Topic_C_1', 'Topic_C_2']}
- );
-
- // fake request for discussion/topics with GET method.
- AjaxHelpers.expectJsonRequest(
- requests, 'GET', '/mock_service/cohorts/discussion/topics'
- );
- AjaxHelpers.respondWithJson(
- requests,
- createMockCohortDiscussions()
- );
-
- // verify the success message.
- expect(courseWideView.$(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeTruthy();
- verifyMessage('Your changes have been saved.', 'confirmation');
- });
-
- it('shows an appropriate message when subsequent "GET" returns HTTP500', function() {
- createCourseWideView(this);
-
- $(courseWideView.$('.check-discussion-subcategory-course-wide')[1]).prop('checked', 'checked').change();
- expect(courseWideView.$(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeFalsy();
-
- // Save the updated settings
- courseWideView.$('.action-save').click();
-
- // fake requests for cohort settings with PATCH method.
- AjaxHelpers.expectJsonRequest(
- requests, 'PATCH', '/mock_service/cohorts/settings',
- {cohorted_course_wide_discussions: ['Topic_C_1', 'Topic_C_2']}
- );
- AjaxHelpers.respondWithJson(
- requests,
- {cohorted_course_wide_discussions: ['Topic_C_1', 'Topic_C_2']}
- );
-
- // fake request for discussion/topics with GET method.
- AjaxHelpers.expectJsonRequest(
- requests, 'GET', '/mock_service/cohorts/discussion/topics'
- );
- AjaxHelpers.respondWithError(requests, 500);
-
- var expectedTitle = "We've encountered an error. Refresh your browser and then try again.";
- expect(courseWideView.$('.message-title').text().trim()).toBe(expectedTitle);
- });
-
- it('shows an appropriate error message for HTTP500', function() {
- createCourseWideView(this);
-
- $(courseWideView.$('.check-discussion-subcategory-course-wide')[1]).prop('checked', 'checked').change();
- courseWideView.$('.action-save').click();
-
- AjaxHelpers.respondWithError(requests, 500);
- var expectedTitle = "We've encountered an error. Refresh your browser and then try again.";
- expect(courseWideView.$('.message-title').text().trim()).toBe(expectedTitle);
- });
- });
-
- describe('Inline', function() {
- var enableSaveButton, mockGetRequest, verifySuccess, mockPatchRequest;
-
- enableSaveButton = function() {
- // enable the inline discussion topics.
- inlineView.$('.check-cohort-inline-discussions').prop('checked', 'checked').change();
-
- $(inlineView.$('.check-discussion-subcategory-inline')[0]).prop('checked', 'checked').change();
-
- expect(inlineView.$(inlineDiscussionsSaveButtonCss).prop('disabled')).toBeFalsy();
- };
-
- verifySuccess = function() {
- // verify the success message.
- expect(inlineView.$(inlineDiscussionsSaveButtonCss).prop('disabled')).toBeTruthy();
- verifyMessage('Your changes have been saved.', 'confirmation');
- };
-
- mockPatchRequest = function(cohortedInlineDiscussions) {
- AjaxHelpers.expectJsonRequest(
- requests, 'PATCH', '/mock_service/cohorts/settings',
- {
- cohorted_inline_discussions: cohortedInlineDiscussions,
- always_cohort_inline_discussions: false
- }
- );
- AjaxHelpers.respondWithJson(
- requests,
- {
- cohorted_inline_discussions: cohortedInlineDiscussions,
- always_cohort_inline_discussions: false
- }
- );
- };
-
- mockGetRequest = function(allCohorted) {
- // fake request for discussion/topics with GET method.
- AjaxHelpers.expectJsonRequest(
- requests, 'GET', '/mock_service/cohorts/discussion/topics'
- );
- AjaxHelpers.respondWithJson(
- requests,
- createMockCohortDiscussions(allCohorted)
- );
- };
-
- it('shows the "Save" button as disabled initially', function() {
- createInlineView(this);
- expect(inlineView.$(inlineDiscussionsSaveButtonCss).prop('disabled')).toBeTruthy();
- });
-
- it('shows always cohort radio button as selected', function() {
- createInlineView(this);
- inlineView.$('.check-all-inline-discussions').prop('checked', 'checked').change();
-
- // verify always cohort inline discussions is being selected.
- expect(inlineView.$('.check-all-inline-discussions').prop('checked')).toBeTruthy();
-
- // verify that inline topics are disabled
- expect(inlineView.$('.check-discussion-subcategory-inline').prop('disabled')).toBeTruthy();
- expect(inlineView.$('.check-discussion-category').prop('disabled')).toBeTruthy();
-
- // verify that cohort some topics are not being selected.
- expect(inlineView.$('.check-cohort-inline-discussions').prop('checked')).toBeFalsy();
- });
-
- it('shows cohort some topics radio button as selected', function() {
- createInlineView(this);
- inlineView.$('.check-cohort-inline-discussions').prop('checked', 'checked').change();
-
- // verify some cohort inline discussions radio is being selected.
- expect(inlineView.$('.check-cohort-inline-discussions').prop('checked')).toBeTruthy();
-
- // verify always cohort radio is not selected.
- expect(inlineView.$('.check-all-inline-discussions').prop('checked')).toBeFalsy();
-
- // verify that inline topics are enabled
- expect(inlineView.$('.check-discussion-subcategory-inline').prop('disabled')).toBeFalsy();
- expect(inlineView.$('.check-discussion-category').prop('disabled')).toBeFalsy();
- });
-
- it('has cohorted and non-cohorted topics', function() {
- createInlineView(this);
- enableSaveButton();
- assertCohortedTopics(inlineView, 'inline');
- });
-
- it('enables "Save" button after changing from always inline option', function() {
- createInlineView(this);
- enableSaveButton();
- });
-
- it('saves the topic', function() {
- createInlineView(this);
- enableSaveButton();
-
- // Save the updated settings
- inlineView.$('.action-save').click();
-
- mockPatchRequest(['Inline_Discussion_1']);
- mockGetRequest();
-
- verifySuccess();
- });
-
- it('selects the parent category when all children are selected', function() {
- createInlineView(this);
- enableSaveButton();
-
- // parent category should be indeterminate.
- expect(inlineView.$('.check-discussion-category:checked').length).toBe(0);
- expect(inlineView.$('.check-discussion-category:indeterminate').length).toBe(1);
-
- inlineView.$('.check-discussion-subcategory-inline').prop('checked', 'checked').change();
- // parent should be checked as we checked all children
- expect(inlineView.$('.check-discussion-category:checked').length).toBe(1);
- });
-
- it('selects/deselects all children when a parent category is selected/deselected', function() {
- createInlineView(this);
- enableSaveButton();
-
- expect(inlineView.$('.check-discussion-category:checked').length).toBe(0);
-
- inlineView.$('.check-discussion-category').prop('checked', 'checked').change();
-
- expect(inlineView.$('.check-discussion-category:checked').length).toBe(1);
- expect(inlineView.$('.check-discussion-subcategory-inline:checked').length).toBe(2);
-
- // un-check the parent, all children should be unchecd.
- inlineView.$('.check-discussion-category').prop('checked', false).change();
- expect(inlineView.$('.check-discussion-category:checked').length).toBe(0);
- expect(inlineView.$('.check-discussion-subcategory-inline:checked').length).toBe(0);
- });
-
- it('saves correctly when a subset of topics are selected within a category', function() {
- createInlineView(this);
- enableSaveButton();
-
- // parent category should be indeterminate.
- expect(inlineView.$('.check-discussion-category:checked').length).toBe(0);
- expect(inlineView.$('.check-discussion-category:indeterminate').length).toBe(1);
-
- // Save the updated settings
- inlineView.$('.action-save').click();
-
- mockPatchRequest(['Inline_Discussion_1']);
- mockGetRequest();
-
- verifySuccess();
- // parent category should be indeterminate.
- expect(inlineView.$('.check-discussion-category:indeterminate').length).toBe(1);
- });
-
- it('saves correctly when all child topics are selected within a category', function() {
- createInlineView(this);
- enableSaveButton();
-
- // parent category should be indeterminate.
- expect(inlineView.$('.check-discussion-category:checked').length).toBe(0);
- expect(inlineView.$('.check-discussion-category:indeterminate').length).toBe(1);
-
- inlineView.$('.check-discussion-subcategory-inline').prop('checked', 'checked').change();
- // Save the updated settings
- inlineView.$('.action-save').click();
-
- mockPatchRequest(['Inline_Discussion_1', 'Inline_Discussion_2']);
- mockGetRequest(true);
-
- verifySuccess();
- // parent category should be checked.
- expect(inlineView.$('.check-discussion-category:checked').length).toBe(1);
- });
-
- it('shows an appropriate message when no inline topics exist', function() {
- var topicsJson, discussionTopicsSettingsModel;
-
- topicsJson = {
- course_wide_discussions: {
- children: [['Topic_C_1', 'entry']],
- entries: {
- Topic_C_1: {
- sort_key: null,
- is_divided: true,
- id: 'Topic_C_1'
- }
- }
- },
- inline_discussions: {
- subcategories: {},
- children: []
- }
- };
- discussionTopicsSettingsModel = new DiscussionTopicsSettingsModel(topicsJson);
-
- createInlineView(this, discussionTopicsSettingsModel);
-
- var expectedTitle = 'No content-specific discussion topics exist.';
- expect(inlineView.$('.no-topics').text().trim()).toBe(expectedTitle);
- });
-
- it('shows an appropriate message when subsequent "GET" returns HTTP500', function() {
- createInlineView(this);
- enableSaveButton();
-
- // Save the updated settings
- inlineView.$('.action-save').click();
-
- mockPatchRequest(['Inline_Discussion_1']);
-
- // fake request for discussion/topics with GET method.
- AjaxHelpers.expectJsonRequest(
- requests, 'GET', '/mock_service/cohorts/discussion/topics'
- );
- AjaxHelpers.respondWithError(requests, 500);
-
- var expectedTitle = "We've encountered an error. Refresh your browser and then try again.";
- expect(inlineView.$('.message-title').text().trim()).toBe(expectedTitle);
- });
-
- it('shows an appropriate error message for HTTP500', function() {
- createInlineView(this);
- enableSaveButton();
-
- $(inlineView.$('.check-discussion-subcategory-inline')[1]).prop('checked', 'checked').change();
- inlineView.$('.action-save').click();
-
- AjaxHelpers.respondWithError(requests, 500);
- var expectedTitle = "We've encountered an error. Refresh your browser and then try again.";
- expect(inlineView.$('.message-title').text().trim()).toBe(expectedTitle);
- });
- });
- });
});
});
diff --git a/lms/static/js/spec/groups/views/discussions_spec.js b/lms/static/js/spec/groups/views/discussions_spec.js
new file mode 100644
index 0000000000..6a11910755
--- /dev/null
+++ b/lms/static/js/spec/groups/views/discussions_spec.js
@@ -0,0 +1,512 @@
+define(['backbone', 'jquery', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
+ 'common/js/spec_helpers/template_helpers',
+ 'js/discussions_management/views/discussions', 'js/discussions_management/models/course_discussions_detail',
+ 'js/discussions_management/models/course_discussions_settings'
+],
+ function(Backbone, $, AjaxHelpers, TemplateHelpers, DiscussionsView, CourseDiscussionTopicDetailsModel,
+ CourseDiscussionsSettingsModel) {
+ 'use strict';
+
+ describe('Discussions View', function() {
+ var createMockDiscussionsSettingsJson, createDiscussionsView, discussionsView, requests, verifyMessage,
+ createMockDiscussionsSettings, createMockDiscussionsJson, createMockDiscussions,
+ showAndAssertDiscussionTopics;
+
+ // Selectors
+ var inlineDiscussionsFormCss = '.cohort-inline-discussions-form',
+ courseWideDiscussionsFormCss = '.cohort-course-wide-discussions-form',
+ courseWideDiscussionsSaveButtonCss = '.cohort-course-wide-discussions-form .action-save',
+ inlineDiscussionsSaveButtonCss = '.cohort-inline-discussions-form .action-save';
+
+ createMockDiscussionsSettingsJson = function(dividedInlineDiscussions,
+ dividedCourseWideDiscussions,
+ alwaysDivideInlineDiscussions) {
+ return {
+ id: 0,
+ divided_inline_discussions: dividedInlineDiscussions || [],
+ divided_course_wide_discussions: dividedCourseWideDiscussions || [],
+ always_divide_inline_discussions: alwaysDivideInlineDiscussions || false
+ };
+ };
+
+ createMockDiscussionsSettings = function(dividedInlineDiscussions,
+ dividedCourseWideDiscussions,
+ alwaysDivideInlineDiscussions) {
+ return new CourseDiscussionsSettingsModel(
+ createMockDiscussionsSettingsJson(dividedInlineDiscussions,
+ dividedCourseWideDiscussions,
+ alwaysDivideInlineDiscussions)
+ );
+ };
+
+ createMockDiscussionsJson = function(allDivided) {
+ return {
+ course_wide_discussions: {
+ children: [['Topic_C_1', 'entry'], ['Topic_C_2', 'entry']],
+ entries: {
+ Topic_C_1: {
+ sort_key: null,
+ is_divided: true,
+ id: 'Topic_C_1'
+ },
+ Topic_C_2: {
+ sort_key: null,
+ is_divided: false,
+ id: 'Topic_C_2'
+ }
+ }
+ },
+ inline_discussions: {
+ subcategories: {
+ Topic_I_1: {
+ subcategories: {},
+ children: [['Inline_Discussion_1', 'entry'], ['Inline_Discussion_2', 'entry']],
+ entries: {
+ Inline_Discussion_1: {
+ sort_key: null,
+ is_divided: true,
+ id: 'Inline_Discussion_1'
+ },
+ Inline_Discussion_2: {
+ sort_key: null,
+ is_divided: allDivided || false,
+ id: 'Inline_Discussion_2'
+ }
+ }
+ }
+ },
+ children: [['Topic_I_1', 'subcategory']]
+ }
+ };
+ };
+
+ createMockDiscussions = function(allDivided) {
+ return new CourseDiscussionTopicDetailsModel(
+ createMockDiscussionsJson(allDivided)
+ );
+ };
+
+ verifyMessage = function(expectedTitle, expectedMessageType, expectedAction, hasDetails) {
+ expect(discussionsView.$('.message-title').text().trim()).toBe(expectedTitle);
+ expect(discussionsView.$('div.message')).toHaveClass('message-' + expectedMessageType);
+ if (expectedAction) {
+ expect(discussionsView.$('.message-actions .action-primary').text().trim()).toBe(expectedAction);
+ } else {
+ expect(discussionsView.$('.message-actions .action-primary').length).toBe(0);
+ } if (!hasDetails) {
+ expect(discussionsView.$('.summary-items').length).toBe(0);
+ }
+ };
+
+ createDiscussionsView = function(test, options) {
+ var discussionSettings, dividedDiscussions, discussionOptions;
+ discussionOptions = options || {};
+ discussionSettings = discussionOptions.cohortSettings || createMockDiscussionsSettings();
+ discussionSettings.url = '/mock_service/discussions/settings';
+
+ dividedDiscussions = discussionOptions.dividedDiscussions || createMockDiscussions();
+ dividedDiscussions.url = '/mock_service/discussion/topics';
+
+ requests = AjaxHelpers.requests(test);
+ discussionsView = new DiscussionsView({
+ el: $('.discussions-management'),
+ discussionSettings: discussionSettings,
+ context: {
+ courseDiscussionTopicDetailsModel: dividedDiscussions
+ }
+ });
+ discussionsView.render();
+ };
+
+ showAndAssertDiscussionTopics = function() {
+ var $courseWideDiscussionsForm,
+ $inlineDiscussionsForm;
+
+ $courseWideDiscussionsForm = discussionsView.$(courseWideDiscussionsFormCss);
+ $inlineDiscussionsForm = discussionsView.$(inlineDiscussionsFormCss);
+
+ // Discussions form should not be visible.
+ expect($inlineDiscussionsForm.length).toBe(1);
+ expect($courseWideDiscussionsForm.length).toBe(1);
+
+ expect($courseWideDiscussionsForm.text()).
+ toContain('Course-Wide Discussion Topics');
+ expect($courseWideDiscussionsForm.text()).
+ toContain('Select the course-wide discussion topics that you want to divide by cohort.');
+
+ // Should see the inline discussions form and its content
+ expect($inlineDiscussionsForm.length).toBe(1);
+ expect($inlineDiscussionsForm.text()).
+ toContain('Content-Specific Discussion Topics');
+ expect($inlineDiscussionsForm.text()).
+ toContain('Specify whether content-specific discussion topics are divided by cohort.');
+ };
+
+ beforeEach(function() {
+ setFixtures('' +
+ '');
+ TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/discussions');
+ TemplateHelpers.installTemplate(
+ 'templates/instructor/instructor_dashboard_2/divided-discussions-course-wide'
+ );
+ TemplateHelpers.installTemplate(
+ 'templates/instructor/instructor_dashboard_2/divided-discussions-inline'
+ );
+ TemplateHelpers.installTemplate(
+ 'templates/instructor/instructor_dashboard_2/cohort-discussions-category'
+ );
+ TemplateHelpers.installTemplate(
+ 'templates/instructor/instructor_dashboard_2/cohort-discussions-subcategory'
+ );
+ TemplateHelpers.installTemplate('templates/instructor/instructor_dashboard_2/notification');
+ });
+
+ describe('Discussion Topics', function() {
+ var courseWideView, assertDividedTopics;
+
+ assertDividedTopics = function(view, type) {
+ expect($('.check-discussion-subcategory-' + type).length).toBe(2);
+ expect($('.check-discussion-subcategory-' + type + ':checked').length).toBe(1);
+ };
+
+ it('renders the view properly', function() {
+ createDiscussionsView(this);
+ showAndAssertDiscussionTopics(this);
+ });
+
+ describe('Course Wide', function() {
+ it('shows the "Save" button as disabled initially', function() {
+ createDiscussionsView(this);
+ expect($(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeTruthy();
+ });
+
+ it('has one divided and one non-divided topic', function() {
+ createDiscussionsView(this);
+ assertDividedTopics(courseWideView, 'course-wide');
+
+ expect($('.course-wide-discussion-topics .divided-discussion-text').length).toBe(2);
+ expect($('.course-wide-discussion-topics .divided-discussion-text.hidden').length).toBe(1);
+ });
+
+ it('enables the "Save" button after changing checkbox', function() {
+ createDiscussionsView(this);
+ // save button is disabled.
+ expect($(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeTruthy();
+
+ $($('.check-discussion-subcategory-course-wide')[0]).prop('checked', false).change();
+
+ // save button is enabled.
+ expect($(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeFalsy();
+ });
+
+ it('saves the topic successfully', function() {
+ createDiscussionsView(this);
+ $($('.check-discussion-subcategory-course-wide')[1]).prop('checked', 'checked').change();
+ expect($(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeFalsy();
+
+ // Save the updated settings
+ $('#cohort-course-wide-discussions-form .action-save').click();
+
+ // fake requests for discussions settings with PATCH method.
+ AjaxHelpers.expectJsonRequest(
+ requests, 'PATCH', '/mock_service/discussions/settings',
+ {divided_course_wide_discussions: ['Topic_C_1', 'Topic_C_2']}
+ );
+ AjaxHelpers.respondWithJson(
+ requests,
+ {divided_course_wide_discussions: ['Topic_C_1', 'Topic_C_2']}
+ );
+
+ // fake request for discussion/topics with GET method.
+ AjaxHelpers.expectJsonRequest(
+ requests, 'GET', '/mock_service/discussion/topics'
+ );
+ AjaxHelpers.respondWithJson(
+ requests,
+ createMockDiscussions()
+ );
+
+ // verify the success message.
+ expect($(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeTruthy();
+ verifyMessage('Your changes have been saved.', 'confirmation');
+ });
+
+ it('shows an appropriate message when subsequent "GET" returns HTTP500', function() {
+ var expectedTitle;
+ createDiscussionsView(this);
+ $($('.check-discussion-subcategory-course-wide')[1]).prop('checked', 'checked').change();
+ expect($(courseWideDiscussionsSaveButtonCss).prop('disabled')).toBeFalsy();
+
+ // Save the updated settings
+ $('#cohort-course-wide-discussions-form .action-save').click();
+
+ // fake requests for discussion settings with PATCH method.
+ AjaxHelpers.expectJsonRequest(
+ requests, 'PATCH', '/mock_service/discussions/settings',
+ {divided_course_wide_discussions: ['Topic_C_1', 'Topic_C_2']}
+ );
+ AjaxHelpers.respondWithJson(
+ requests,
+ {divided_course_wide_discussions: ['Topic_C_1', 'Topic_C_2']}
+ );
+
+ // fake request for discussion/topics with GET method.
+ AjaxHelpers.expectJsonRequest(
+ requests, 'GET', '/mock_service/discussion/topics'
+ );
+ AjaxHelpers.respondWithError(requests, 500);
+
+ expectedTitle = "We've encountered an error. Refresh your browser and then try again.";
+ expect($('.message-title').text().trim()).toBe(expectedTitle);
+ });
+
+ it('shows an appropriate error message for HTTP500', function() {
+ var expectedTitle;
+ createDiscussionsView(this);
+ $($('.check-discussion-subcategory-course-wide')[1]).prop('checked', 'checked').change();
+ $('.action-save').click();
+
+ AjaxHelpers.respondWithError(requests, 500);
+ expectedTitle = "We've encountered an error. Refresh your browser and then try again.";
+ expect($('.message-title').text().trim()).toBe(expectedTitle);
+ });
+ });
+
+ describe('Inline', function() {
+ var enableSaveButton, mockGetRequest, verifySuccess, mockPatchRequest;
+
+ enableSaveButton = function() {
+ // enable the inline discussion topics.
+ $('.check-cohort-inline-discussions').prop('checked', 'checked').change();
+
+ $($('.check-discussion-subcategory-inline')[0]).prop('checked', 'checked').change();
+
+ expect($(inlineDiscussionsSaveButtonCss).prop('disabled')).toBeFalsy();
+ };
+
+ verifySuccess = function() {
+ // verify the success message.
+ expect($(inlineDiscussionsSaveButtonCss).prop('disabled')).toBeTruthy();
+ verifyMessage('Your changes have been saved.', 'confirmation');
+ };
+
+ mockPatchRequest = function(dividededInlineDiscussions) {
+ AjaxHelpers.expectJsonRequest(
+ requests, 'PATCH', '/mock_service/discussions/settings',
+ {
+ divided_inline_discussions: dividededInlineDiscussions,
+ always_divide_inline_discussions: false
+ }
+ );
+ AjaxHelpers.respondWithJson(
+ requests,
+ {
+ divided_inline_discussions: dividededInlineDiscussions,
+ always_divide_inline_discussions: false
+ }
+ );
+ };
+
+ mockGetRequest = function(alldivided) {
+ // fake request for discussion/topics with GET method.
+ AjaxHelpers.expectJsonRequest(
+ requests, 'GET', '/mock_service/discussion/topics'
+ );
+ AjaxHelpers.respondWithJson(
+ requests,
+ createMockDiscussions(alldivided)
+ );
+ };
+
+ it('shows the "Save" button as disabled initially', function() {
+ createDiscussionsView(this);
+ expect($(inlineDiscussionsSaveButtonCss).prop('disabled')).toBeTruthy();
+ });
+
+ it('shows always divide radio button as selected', function() {
+ createDiscussionsView(this);
+ $('.check-all-inline-discussions').prop('checked', 'checked').change();
+
+ // verify always divide inline discussions is being selected.
+ expect($('.check-all-inline-discussions').prop('checked')).toBeTruthy();
+
+ // verify that inline topics are disabled
+ expect($('.check-discussion-subcategory-inline').prop('disabled')).toBeTruthy();
+ expect($('.check-discussion-category').prop('disabled')).toBeTruthy();
+
+ // verify that divide some topics are not being selected.
+ expect($('.check-cohort-inline-discussions').prop('checked')).toBeFalsy();
+ });
+
+ it('shows divide some topics radio button as selected', function() {
+ createDiscussionsView(this);
+ $('.check-cohort-inline-discussions').prop('checked', 'checked').change();
+
+ // verify some divide inline discussions radio is being selected.
+ expect($('.check-cohort-inline-discussions').prop('checked')).toBeTruthy();
+
+ // verify always divide radio is not selected.
+ expect($('.check-all-inline-discussions').prop('checked')).toBeFalsy();
+
+ // verify that inline topics are enabled
+ expect($('.check-discussion-subcategory-inline').prop('disabled')).toBeFalsy();
+ expect($('.check-discussion-category').prop('disabled')).toBeFalsy();
+ });
+
+ it('has divided and non-divided topics', function() {
+ createDiscussionsView(this);
+ enableSaveButton();
+ assertDividedTopics(this, 'inline');
+ });
+
+ it('enables "Save" button after changing from always inline option', function() {
+ createDiscussionsView(this);
+ enableSaveButton();
+ });
+
+ it('saves the topic', function() {
+ createDiscussionsView(this);
+ enableSaveButton();
+
+ // Save the updated settings
+ $('.action-save').click();
+
+ mockPatchRequest(['Inline_Discussion_1']);
+ mockGetRequest();
+
+ verifySuccess();
+ });
+
+ it('selects the parent category when all children are selected', function() {
+ createDiscussionsView(this);
+ enableSaveButton();
+
+ // parent category should be indeterminate.
+ expect($('.check-discussion-category:checked').length).toBe(0);
+ expect($('.check-discussion-category:indeterminate').length).toBe(1);
+
+ $('.check-discussion-subcategory-inline').prop('checked', 'checked').change();
+ // parent should be checked as we checked all children
+ expect($('.check-discussion-category:checked').length).toBe(1);
+ });
+
+ it('selects/deselects all children when a parent category is selected/deselected', function() {
+ createDiscussionsView(this);
+ enableSaveButton();
+
+ expect($('.check-discussion-category:checked').length).toBe(0);
+
+ $('.check-discussion-category').prop('checked', 'checked').change();
+
+ expect($('.check-discussion-category:checked').length).toBe(1);
+ expect($('.check-discussion-subcategory-inline:checked').length).toBe(2);
+
+ // un-check the parent, all children should be unchecd.
+ $('.check-discussion-category').prop('checked', false).change();
+ expect($('.check-discussion-category:checked').length).toBe(0);
+ expect($('.check-discussion-subcategory-inline:checked').length).toBe(0);
+ });
+
+ it('saves correctly when a subset of topics are selected within a category', function() {
+ createDiscussionsView(this);
+ enableSaveButton();
+
+ // parent category should be indeterminate.
+ expect($('.check-discussion-category:checked').length).toBe(0);
+ expect($('.check-discussion-category:indeterminate').length).toBe(1);
+
+ // Save the updated settings
+ $('#cohort-inline-discussions-form .action-save').click();
+
+ mockPatchRequest(['Inline_Discussion_1']);
+ mockGetRequest();
+
+ verifySuccess();
+ // parent category should be indeterminate.
+ expect($('.check-discussion-category:indeterminate').length).toBe(1);
+ });
+
+ it('saves correctly when all child topics are selected within a category', function() {
+ createDiscussionsView(this);
+ enableSaveButton();
+
+ // parent category should be indeterminate.
+ expect($('.check-discussion-category:checked').length).toBe(0);
+ expect($('.check-discussion-category:indeterminate').length).toBe(1);
+
+ $('.check-discussion-subcategory-inline').prop('checked', 'checked').change();
+ // Save the updated settings
+ $('#cohort-inline-discussions-form .action-save').click();
+
+ mockPatchRequest(['Inline_Discussion_1', 'Inline_Discussion_2']);
+ mockGetRequest(true);
+
+ verifySuccess();
+ // parent category should be checked.
+ expect($('.check-discussion-category:checked').length).toBe(1);
+ });
+
+ it('shows an appropriate message when no inline topics exist', function() {
+ var topicsJson, options, expectedTitle;
+
+ topicsJson = {
+ course_wide_discussions: {
+ children: [['Topic_C_1', 'entry']],
+ entries: {
+ Topic_C_1: {
+ sort_key: null,
+ is_divided: true,
+ id: 'Topic_C_1'
+ }
+ }
+ },
+ inline_discussions: {
+ subcategories: {},
+ children: []
+ }
+ };
+ options = {dividedDiscussions: new CourseDiscussionTopicDetailsModel(topicsJson)};
+ createDiscussionsView(this, options);
+
+ expectedTitle = 'No content-specific discussion topics exist.';
+ expect($('.no-topics').text().trim()).toBe(expectedTitle);
+ });
+
+ it('shows an appropriate message when subsequent "GET" returns HTTP500', function() {
+ var expectedTitle;
+ createDiscussionsView(this);
+ enableSaveButton();
+
+ // Save the updated settings
+ $('#cohort-inline-discussions-form .action-save').click();
+
+ mockPatchRequest(['Inline_Discussion_1']);
+
+ // fake request for discussion/topics with GET method.
+ AjaxHelpers.expectJsonRequest(
+ requests, 'GET', '/mock_service/discussion/topics'
+ );
+ AjaxHelpers.respondWithError(requests, 500);
+
+ expectedTitle = "We've encountered an error. Refresh your browser and then try again.";
+ expect($('.message-title').text().trim()).toBe(expectedTitle);
+ });
+
+ it('shows an appropriate error message for HTTP500', function() {
+ var expectedTitle;
+ createDiscussionsView(this);
+ enableSaveButton();
+
+ $($('.check-discussion-subcategory-inline')[1]).prop('checked', 'checked').change();
+ $('#cohort-inline-discussions-form .action-save').click();
+
+ AjaxHelpers.respondWithError(requests, 500);
+ expectedTitle = "We've encountered an error. Refresh your browser and then try again.";
+ expect($('.message-title').text().trim()).toBe(expectedTitle);
+ });
+ });
+ });
+ });
+ });
diff --git a/lms/static/lms/js/build.js b/lms/static/lms/js/build.js
index a24009f863..999b38585c 100644
--- a/lms/static/lms/js/build.js
+++ b/lms/static/lms/js/build.js
@@ -28,6 +28,7 @@
'js/edxnotes/views/page_factory',
'js/financial-assistance/financial_assistance_form_factory',
'js/groups/views/cohorts_dashboard_factory',
+ 'js/discussions_management/views/discussions_dashboard_factory',
'js/header_factory',
'js/learner_dashboard/program_details_factory',
'js/learner_dashboard/program_list_factory',
diff --git a/lms/static/lms/js/spec/main.js b/lms/static/lms/js/spec/main.js
index 7832e92fce..28e00e7267 100644
--- a/lms/static/lms/js/spec/main.js
+++ b/lms/static/lms/js/spec/main.js
@@ -731,6 +731,7 @@
'js/spec/edxnotes/views/visibility_decorator_spec.js',
'js/spec/financial-assistance/financial_assistance_form_view_spec.js',
'js/spec/groups/views/cohorts_spec.js',
+ 'js/spec/groups/views/discussions_spec.js',
'js/spec/instructor_dashboard/certificates_bulk_exception_spec.js',
'js/spec/instructor_dashboard/certificates_exception_spec.js',
'js/spec/instructor_dashboard/certificates_invalidation_spec.js',
diff --git a/lms/static/sass/course/instructor/_instructor_2.scss b/lms/static/sass/course/instructor/_instructor_2.scss
index 55caeeb5f2..d01e2e7d62 100644
--- a/lms/static/sass/course/instructor/_instructor_2.scss
+++ b/lms/static/sass/course/instructor/_instructor_2.scss
@@ -1181,49 +1181,6 @@
}
}
- // cohort discussions interface.
- .cohort-discussions-nav {
-
- .cohort-course-wide-discussions-form {
-
- .form-actions {
- padding-top: ($baseline/2);
- }
- }
-
- .category-title,
- .topic-name,
- .all-inline-discussions,
- .always_cohort_inline_discussions,
- .cohort_inline_discussions {
- padding-left: ($baseline/2);
- }
-
- .always_cohort_inline_discussions,
- .cohort_inline_discussions {
- padding-top: ($baseline/2);
- }
-
- .category-item,
- .subcategory-item {
- padding-top: ($baseline/2);
- }
-
- .cohorted-text {
- color: $uxpl-blue-base;
- }
-
- .discussions-wrapper {
- @extend %ui-no-list;
- padding: 0 ($baseline/2);
-
- .subcategories {
- padding: 0 ($baseline*1.5);
- }
- }
- }
-
-
.wrapper-tabs { // This applies to the tab-like interface that toggles between the student management and the group settings
@extend %ui-no-list;
@extend %ui-depth1;
@@ -1280,6 +1237,77 @@
}
}
+// view - discussions management
+// --------------------
+.instructor-dashboard-wrapper-2 section.idash-section#discussions_management {
+
+ .form-submit {
+ @include idashbutton($uxpl-blue-base);
+ @include font-size(14);
+ @include line-height(14);
+ margin-right: ($baseline/2);
+ margin-bottom: 0;
+ text-shadow: none;
+ }
+
+ .discussions-management-supplemental {
+ @extend %t-copy-sub1;
+ margin-top: $baseline;
+ padding: ($baseline/2) $baseline;
+ background: $gray-l6;
+ border-radius: ($baseline/10);
+ }
+
+ // cohort discussions interface.
+ .discussions-nav {
+
+ .cohort-course-wide-discussions-form {
+
+ .form-actions {
+ padding-top: ($baseline/2);
+ }
+ }
+
+ .category-title,
+ .topic-name,
+ .all-inline-discussions,
+ .always_divide_inline_discussions,
+ .divide_inline_discussions {
+ padding-left: ($baseline/2);
+ }
+
+ .always_divide_inline_discussions,
+ .divide_inline_discussions {
+ padding-top: ($baseline/2);
+ }
+
+ .category-item,
+ .subcategory-item {
+ padding-top: ($baseline/2);
+ }
+
+ .divided-discussion-text{
+ color: $uxpl-blue-base;
+ }
+
+ .discussions-wrapper {
+ @extend %ui-no-list;
+ padding: 0 ($baseline/2);
+
+ .subcategories {
+ padding: 0 ($baseline*1.5);
+ }
+ }
+ }
+ .wrapper-tabs {
+ @extend %ui-no-list;
+ @extend %ui-depth1;
+ position: relative;
+ top: 1px;
+ padding: 0 $baseline;
+ }
+}
+
// view - student admin
// --------------------
diff --git a/lms/templates/instructor/instructor_dashboard_2/cohort-discussions-subcategory.underscore b/lms/templates/instructor/instructor_dashboard_2/cohort-discussions-subcategory.underscore
index 95ed87faf3..a93ba78877 100644
--- a/lms/templates/instructor/instructor_dashboard_2/cohort-discussions-subcategory.underscore
+++ b/lms/templates/instructor/instructor_dashboard_2/cohort-discussions-subcategory.underscore
@@ -3,7 +3,7 @@
diff --git a/lms/templates/instructor/instructor_dashboard_2/cohort_management.html b/lms/templates/instructor/instructor_dashboard_2/cohort_management.html
index 8e7bad3439..9aa5c6c863 100644
--- a/lms/templates/instructor/instructor_dashboard_2/cohort_management.html
+++ b/lms/templates/instructor/instructor_dashboard_2/cohort_management.html
@@ -13,7 +13,6 @@ from openedx.core.djangoapps.course_groups.partition_scheme import get_cohorted_
data-cohorts_url="${section_data['cohorts_url']}"
data-upload_cohorts_csv_url="${section_data['upload_cohorts_csv_url']}"
data-course_cohort_settings_url="${section_data['course_cohort_settings_url']}"
- data-discussion-topics-url="${section_data['discussion_topics_url']}"
data-verified_track_cohorting_url="${section_data['verified_track_cohorting_url']}"
data-is_ccx_enabled="${'true' if section_data['ccx_is_enabled'] else 'false'}"
>
diff --git a/lms/templates/instructor/instructor_dashboard_2/cohorts.underscore b/lms/templates/instructor/instructor_dashboard_2/cohorts.underscore
index 82eb5ea17b..21e9087fd1 100644
--- a/lms/templates/instructor/instructor_dashboard_2/cohorts.underscore
+++ b/lms/templates/instructor/instructor_dashboard_2/cohorts.underscore
@@ -49,15 +49,5 @@
%>
-
-
-
-
-
-
-
<% } %>
diff --git a/lms/templates/instructor/instructor_dashboard_2/discussions.underscore b/lms/templates/instructor/instructor_dashboard_2/discussions.underscore
new file mode 100644
index 0000000000..444788c395
--- /dev/null
+++ b/lms/templates/instructor/instructor_dashboard_2/discussions.underscore
@@ -0,0 +1,5 @@
+
+
diff --git a/lms/templates/instructor/instructor_dashboard_2/discussions_management.html b/lms/templates/instructor/instructor_dashboard_2/discussions_management.html
new file mode 100644
index 0000000000..326bd8ea8c
--- /dev/null
+++ b/lms/templates/instructor/instructor_dashboard_2/discussions_management.html
@@ -0,0 +1,22 @@
+<%page expression_filter="h" args="section_data"/>
+
+<%namespace name='static' file='../../static_content.html'/>
+<%!
+from django.utils.translation import ugettext as _
+from openedx.core.djangolib.js_utils import js_escaped_string, dump_js_escaped_json
+from courseware.courses import get_studio_url
+from openedx.core.djangoapps.course_groups.partition_scheme import get_cohorted_user_partition
+%>
+
+
+
+
+
+<%block name="js_extra">
+<%static:require_module module_name="js/discussions_management/views/discussions_dashboard_factory" class_name="DiscussionsFactory">
+ DiscussionsFactory();
+%static:require_module>
+%block>
diff --git a/lms/templates/instructor/instructor_dashboard_2/cohort-discussions-course-wide.underscore b/lms/templates/instructor/instructor_dashboard_2/divided-discussions-course-wide.underscore
similarity index 94%
rename from lms/templates/instructor/instructor_dashboard_2/cohort-discussions-course-wide.underscore
rename to lms/templates/instructor/instructor_dashboard_2/divided-discussions-course-wide.underscore
index 6038113dbd..f359bf75b4 100644
--- a/lms/templates/instructor/instructor_dashboard_2/cohort-discussions-course-wide.underscore
+++ b/lms/templates/instructor/instructor_dashboard_2/divided-discussions-course-wide.underscore
@@ -1,6 +1,6 @@
<%- gettext('Specify whether discussion topics are divided by cohort') %>