diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py
index e26395fd64..2e0e516632 100644
--- a/cms/djangoapps/contentstore/tests/test_course_settings.py
+++ b/cms/djangoapps/contentstore/tests/test_course_settings.py
@@ -135,6 +135,11 @@ class CourseDetailsTestCase(CourseTestCase):
jsondetails.self_paced
)
+ @override_settings(FEATURES=dict(settings.FEATURES, ENABLE_SELF_PACED_COURSES=False))
+ def test_enable_self_paced(self):
+ details = CourseDetails.fetch(self.course.id)
+ self.assertNotIn('self_paced', details.__dict__)
+
@override_settings(MKTG_URLS={'ROOT': 'dummy-root'})
def test_marketing_site_fetch(self):
settings_details_url = get_url(self.course.id)
diff --git a/cms/static/js/views/settings/main.js b/cms/static/js/views/settings/main.js
index 9e2486491c..1188c2b6eb 100644
--- a/cms/static/js/views/settings/main.js
+++ b/cms/static/js/views/settings/main.js
@@ -99,6 +99,13 @@ var DetailsView = ValidatingView.extend({
}
this.$('#' + this.fieldToSelectorMap['entrance_exam_minimum_score_pct']).val(this.model.get('entrance_exam_minimum_score_pct'));
+ if (this.model.get('self_paced')) {
+ this.$('#course-pace-self-paced').attr('checked', true);
+ }
+ else {
+ this.$('#course-pace-instructor-led').attr('checked', true);
+ }
+
this.licenseView.render()
return this;
@@ -236,6 +243,11 @@ var DetailsView = ValidatingView.extend({
}
}, this), 1000);
break;
+ case 'course-pace-self-paced':
+ // Fallthrough to handle both radio buttons
+ case 'course-pace-instructor-led':
+ this.model.set('self_paced', JSON.parse(event.currentTarget.value));
+ break;
default: // Everything else is handled by datepickers and CodeMirror.
break;
}
diff --git a/cms/templates/settings.html b/cms/templates/settings.html
index c54d944228..570c4371a5 100644
--- a/cms/templates/settings.html
+++ b/cms/templates/settings.html
@@ -412,6 +412,24 @@ CMS.URL.UPLOAD_ASSET = '${upload_asset_url}';
% endif
+ % if settings.FEATURES.get("ENABLE_SELF_PACED_COURSES", False):
+
+
+
+
+
+ % endif
+
% if settings.FEATURES.get("LICENSING", False):
diff --git a/common/test/acceptance/pages/studio/settings.py b/common/test/acceptance/pages/studio/settings.py
index 9e0c3d90d7..f7fd434d97 100644
--- a/common/test/acceptance/pages/studio/settings.py
+++ b/common/test/acceptance/pages/studio/settings.py
@@ -131,6 +131,27 @@ class SettingsPage(CoursePage):
raise Exception("Invalid license name: {name}".format(name=license_name))
button.click()
+ pacing_css = 'section.pacing input[type=radio]:checked'
+
+ @property
+ def course_pacing(self):
+ """
+ Returns the label text corresponding to the checked pacing radio button.
+ """
+ self.wait_for_element_presence(self.pacing_css, 'course pacing controls present and rendered')
+ checked = self.q(css=self.pacing_css).results[0]
+ checked_id = checked.get_attribute('id')
+ return self.q(css='label[for={checked_id}]'.format(checked_id=checked_id)).results[0].text
+
+ @course_pacing.setter
+ def course_pacing(self, pacing):
+ """
+ Sets the course to either self-paced or instructor-led by checking
+ the appropriate radio button.
+ """
+ self.wait_for_element_presence(self.pacing_css, 'course pacing controls present')
+ self.q(xpath="//label[contains(text(), '{pacing}')]".format(pacing=pacing)).click()
+
################
# Waits
################
diff --git a/common/test/acceptance/tests/studio/test_studio_settings_details.py b/common/test/acceptance/tests/studio/test_studio_settings_details.py
index 0e68e69708..6cff8d6225 100644
--- a/common/test/acceptance/tests/studio/test_studio_settings_details.py
+++ b/common/test/acceptance/tests/studio/test_studio_settings_details.py
@@ -16,12 +16,11 @@ from ..helpers import (
)
-class SettingsMilestonesTest(StudioCourseTest):
- """
- Tests for milestones feature in Studio's settings tab
- """
+class StudioSettingsDetailsTest(StudioCourseTest):
+ """Base class for settings and details page tests."""
+
def setUp(self, is_staff=True):
- super(SettingsMilestonesTest, self).setUp(is_staff=is_staff)
+ super(StudioSettingsDetailsTest, self).setUp(is_staff=is_staff)
self.settings_detail = SettingsPage(
self.browser,
self.course_info['org'],
@@ -33,6 +32,11 @@ class SettingsMilestonesTest(StudioCourseTest):
self.settings_detail.visit()
self.assertTrue(self.settings_detail.is_browser_on_page())
+
+class SettingsMilestonesTest(StudioSettingsDetailsTest):
+ """
+ Tests for milestones feature in Studio's settings tab
+ """
def test_page_has_prerequisite_field(self):
"""
Test to make sure page has pre-requisite course field if milestones app is enabled.
@@ -193,3 +197,32 @@ class SettingsMilestonesTest(StudioCourseTest):
css_selector='.add-item a.button-new',
text='New Subsection'
))
+
+
+class CoursePacingTest(StudioSettingsDetailsTest):
+ """Tests for setting a course to self-paced."""
+
+ def test_default_instructor_led(self):
+ """
+ Test that the 'instructor led' button is checked by default.
+ """
+ self.assertEqual(self.settings_detail.course_pacing, 'Instructor Led')
+
+ def test_self_paced(self):
+ """
+ Test that the 'self-paced' button is checked for a self-paced
+ course.
+ """
+ self.course_fixture.add_course_details({'self_paced': True})
+ self.course_fixture.configure_course()
+ self.settings_detail.refresh_page()
+ self.assertEqual(self.settings_detail.course_pacing, 'Self-Paced')
+
+ def test_set_self_paced(self):
+ """
+ Test that the self-paced option is persisted correctly.
+ """
+ self.settings_detail.course_pacing = 'Self-Paced'
+ self.settings_detail.save_changes()
+ self.settings_detail.refresh_page()
+ self.assertEqual(self.settings_detail.course_pacing, 'Self-Paced')