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): + +
+ +
+
+

${_("Course Pacing")}

+ ${_("Set the pacing for this course")} +
+ + + + + +
+ + % 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')