diff --git a/common/test/acceptance/pages/lms/course_home.py b/common/test/acceptance/pages/lms/course_home.py index bdf62c2debbaf553455bf76edb2ffb58c97e622f..40b83da0fcb3d30a0717a437daf6b6cc0bf6abd0 100644 --- a/common/test/acceptance/pages/lms/course_home.py +++ b/common/test/acceptance/pages/lms/course_home.py @@ -303,6 +303,13 @@ class CourseOutlinePage(PageObject): self._expand_all_outline_folds() return self.q(css=self._subsection_selector).results + def get_subsection_due_date(self, index=0): + """ + Get the due date for the given index sub-section on the LMS outline. + """ + results = self.q(css='div.details > span.subtitle > span.subtitle-name').results + return results[index].text if results else None + def _expand_all_outline_folds(self): ''' Expands all parts of the collapsible outline. diff --git a/common/test/acceptance/tests/lms/test_lms_course_home.py b/common/test/acceptance/tests/lms/test_lms_course_home.py index 715bd7c9164ea4e64dd2b10b9499af34805de79e..ef315714dc02f37841477cc52bc95632f0cb7ba5 100644 --- a/common/test/acceptance/tests/lms/test_lms_course_home.py +++ b/common/test/acceptance/tests/lms/test_lms_course_home.py @@ -4,10 +4,13 @@ End-to-end tests for the LMS that utilize the course home page and course outlin """ from __future__ import absolute_import + +from datetime import datetime, timedelta + import six + from common.test.acceptance.pages.lms.create_mode import ModeCreationPage from openedx.core.lib.tests import attr - from ...fixtures.course import CourseFixture, XBlockFixtureDesc from ...pages.lms.bookmarks import BookmarksPage from ...pages.lms.course_home import CourseHomePage @@ -164,3 +167,73 @@ class CourseHomeA11yTest(CourseHomeBaseTest): ] }) course_search_results_page.a11y_audit.check_for_accessibility_errors() + + +class CourseOutlineTest(UniqueCourseTest): + """ + Test Suite to verify the course outline page on the LMS. + """ + USERNAME = "STUDENT_TESTER" + EMAIL = "student101@example.com" + + def setUp(self): + """ + Initialize pages and install a course fixture. + """ + super(CourseOutlineTest, self).setUp() + + self.course_home_page = CourseHomePage(self.browser, self.course_id) + # Install a course with sections and problems + self.course_fix = CourseFixture( + self.course_info['org'], + self.course_info['number'], + self.course_info['run'], + self.course_info['display_name'], + start_date=datetime.now() + timedelta(days=-10), + end_date=datetime.now() + timedelta(days=10) + ) + + self.course_fix.add_children( + XBlockFixtureDesc('chapter', 'Test Section').add_children( + XBlockFixtureDesc('sequential', 'Test Subsection', metadata={ + 'due': (datetime.now() + timedelta(days=-20)).isoformat(), + 'format': 'Homework' + }).add_children( + XBlockFixtureDesc('problem', 'Test Problem', data=load_data_str('multiple_choice.xml')), + ) + ), + ).install() + # Auto-auth register for the course. + auto_auth(self.browser, self.USERNAME, self.EMAIL, False, self.course_id) + + def change_course_pacing_to_self_paced(self): + """ + Change the course pacing from Instructor Paced to Self-paced course + for a live course. + """ + self.course_fix.add_course_details({'start_date': (datetime.now() + timedelta(days=5))}) + self.course_fix.configure_course() + self.course_fix.add_course_details({'self_paced': True}) + self.course_fix.configure_course() + self.course_fix.add_course_details({'start_date': (datetime.now() + timedelta(days=-10))}) + self.course_fix.configure_course() + + def test_outline_when_pacing_changed_to_self_paced(self): + """ + Scenario: Ensure that due dates are not displayed on the course outline page + when switched to self-paced mode from instructor-paced. + + Given an instructor paced course with a due graded content + Visit the course outline page + Verify the due date visibility + Change the course pacing to self-paced + Visit the course outline page again + Verify that due date is not visible + """ + self.course_home_page.visit() + due_date = self.course_home_page.outline.get_subsection_due_date() + self.assertIn(str(datetime.now().year), due_date) + self.change_course_pacing_to_self_paced() + self.course_home_page.visit() + due_date = self.course_home_page.outline.get_subsection_due_date() + self.assertNotIn(str(datetime.now().year), due_date) diff --git a/lms/templates/courseware/progress.html b/lms/templates/courseware/progress.html index e3754d576b741135241312a9c2ea7a333f067f7a..7ece338bd11363545a873b1b0af1f26f9da6f5da 100644 --- a/lms/templates/courseware/progress.html +++ b/lms/templates/courseware/progress.html @@ -191,7 +191,7 @@ username = get_enterprise_learner_generic_name(request) or student.username %if section.format is not None: ${section.format} %endif - %if section.due is not None: + %if section.due is not None and pacing_type != 'self_paced': <em class="localized-datetime" data-datetime="${section.due}" data-string="${_('due {date}')}" data-timezone="${user_timezone}" data-language="${user_language}"></em> %endif </p> diff --git a/openedx/features/course_experience/templates/course_experience/course-outline-fragment.html b/openedx/features/course_experience/templates/course_experience/course-outline-fragment.html index 7c830ae39720d8d986d714c84b0dafcdf04331e9..ea394d7ec488ea383b8bb36f77c37ac2efaed4b4 100644 --- a/openedx/features/course_experience/templates/course_experience/course-outline-fragment.html +++ b/openedx/features/course_experience/templates/course_experience/course-outline-fragment.html @@ -14,6 +14,7 @@ from openedx.core.djangolib.markup import HTML, Text <% course_sections = blocks.get('children') +self_paced = context.get('self_paced', False) %> <main role="main" class="course-outline" id="main" tabindex="-1"> % if course_sections is not None: @@ -91,14 +92,16 @@ course_sections = blocks.get('children') ## ## Exam subsections expose exam status message field as well as a status icon <% - if subsection.get('due') is None: + if subsection.get('due') is None or self_paced: # examples: Homework, Lab, etc. data_string = subsection.get('format') + data_datetime = "" else: if 'special_exam_info' in subsection: data_string = _('due {date}') else: data_string = _("{subsection_format} due {{date}}").format(subsection_format=subsection.get('format')) + data_datetime = subsection.get('due') %> % if subsection.get('format') or 'special_exam_info' in subsection: <span class="subtitle"> @@ -117,7 +120,7 @@ course_sections = blocks.get('children') % if not subsection['special_exam_info'].get('in_completed_state', False): <span class="localized-datetime subtitle-name" - data-datetime="${subsection.get('due')}" + data-datetime="${data_datetime}" data-string="${data_string}" data-timezone="${user_timezone}" data-language="${user_language}" @@ -128,7 +131,7 @@ course_sections = blocks.get('children') ## this is the standard case in edx-platform <span class="localized-datetime subtitle-name" - data-datetime="${subsection.get('due')}" + data-datetime="${data_datetime}" data-string="${data_string}" data-timezone="${user_timezone}" data-language="${user_language}" diff --git a/openedx/features/course_experience/views/course_outline.py b/openedx/features/course_experience/views/course_outline.py index f97b01972e21a6f26a69d146fa4729dc6ca6897b..66bf66bb3cfda28c5aaef5c10b7b22ee49d6a91a 100644 --- a/openedx/features/course_experience/views/course_outline.py +++ b/openedx/features/course_experience/views/course_outline.py @@ -67,6 +67,10 @@ class CourseOutlineFragmentView(EdxFragmentView): context['gated_content'] = gated_content context['xblock_display_names'] = xblock_display_names + page_context = kwargs.get('page_context', None) + if page_context: + context['self_paced'] = page_context.get('pacing_type', 'instructor_paced') == 'self_paced' + html = render_to_string('course_experience/course-outline-fragment.html', context) return Fragment(html)