diff --git a/common/test/acceptance/pages/common/__init__.py b/common/test/acceptance/pages/common/__init__.py new file mode 100644 index 0000000000..488c0dbe63 --- /dev/null +++ b/common/test/acceptance/pages/common/__init__.py @@ -0,0 +1,7 @@ +import os + +# Get the URL of the instance under test +BASE_URL = os.environ.get('test_url', 'http://localhost:8003') + +# The URL used for user auth in testing +AUTH_BASE_URL = os.environ.get('test_url', 'http://localhost:8031') diff --git a/common/test/acceptance/pages/common/logout.py b/common/test/acceptance/pages/common/logout.py new file mode 100644 index 0000000000..9a8fe54a25 --- /dev/null +++ b/common/test/acceptance/pages/common/logout.py @@ -0,0 +1,16 @@ +""" +Logout Page. +""" +from bok_choy.page_object import PageObject +from . import BASE_URL + + +class LogoutPage(PageObject): + """ + Logout page to logout current logged in user. + """ + + url = BASE_URL + "/logout" + + def is_browser_on_page(self): + return self.q(css='.cta-login').present diff --git a/common/test/acceptance/pages/lms/problem.py b/common/test/acceptance/pages/lms/problem.py new file mode 100644 index 0000000000..7e39b3d696 --- /dev/null +++ b/common/test/acceptance/pages/lms/problem.py @@ -0,0 +1,22 @@ +""" +Problem Page. +""" +from bok_choy.page_object import PageObject + + +class ProblemPage(PageObject): + """ + View of problem page. + """ + + url = None + + def is_browser_on_page(self): + return self.q(css='.xblock-student_view').present + + @property + def problem_name(self): + """ + Return the current problem name. + """ + return self.q(css='.problem-header').text[0] diff --git a/common/test/acceptance/tests/test_courseware.py b/common/test/acceptance/tests/test_courseware.py new file mode 100644 index 0000000000..2d951ca287 --- /dev/null +++ b/common/test/acceptance/tests/test_courseware.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +""" +E2E tests for the LMS. +""" +import time + +from .helpers import UniqueCourseTest +from ..pages.studio.auto_auth import AutoAuthPage +from ..pages.studio.overview import CourseOutlinePage +from ..pages.lms.courseware import CoursewarePage +from ..pages.lms.problem import ProblemPage +from ..pages.common.logout import LogoutPage +from ..fixtures.course import CourseFixture, XBlockFixtureDesc + + +class CoursewareTest(UniqueCourseTest): + """ + Test courseware. + """ + USERNAME = "STUDENT_TESTER" + EMAIL = "student101@example.com" + + def setUp(self): + super(CoursewareTest, self).setUp() + + self.courseware_page = CoursewarePage(self.browser, self.course_id) + + self.course_outline = CourseOutlinePage( + self.browser, + self.course_info['org'], + self.course_info['number'], + self.course_info['run'] + ) + + # Install a course with sections/problems, tabs, updates, and handouts + course_fix = CourseFixture( + self.course_info['org'], self.course_info['number'], + self.course_info['run'], self.course_info['display_name'] + ) + + course_fix.add_children( + XBlockFixtureDesc('chapter', 'Test Section 1').add_children( + XBlockFixtureDesc('sequential', 'Test Subsection 1').add_children( + XBlockFixtureDesc('problem', 'Test Problem 1') + ) + ), + XBlockFixtureDesc('chapter', 'Test Section 2').add_children( + XBlockFixtureDesc('sequential', 'Test Subsection 2').add_children( + XBlockFixtureDesc('problem', 'Test Problem 2') + ) + ) + ).install() + + # Auto-auth register for the course. + self._auto_auth(self.USERNAME, self.EMAIL, False) + + def _goto_problem_page(self): + """ + Open problem page with assertion. + """ + self.courseware_page.visit() + self.problem_page = ProblemPage(self.browser) + self.assertEqual(self.problem_page.problem_name, 'TEST PROBLEM 1') + + def _change_problem_release_date_in_studio(self): + """ + + """ + self.course_outline.q(css=".subsection-header-actions .configure-button").first.click() + self.course_outline.q(css="#start_date").fill("01/01/2015") + self.course_outline.q(css=".action-save").first.click() + + def _auto_auth(self, username, email, staff): + """ + Logout and login with given credentials. + """ + AutoAuthPage(self.browser, username=username, email=email, + course_id=self.course_id, staff=staff).visit() + + + def test_courseware(self): + """ + Test courseware if recent visited subsection become unpublished. + """ + + # Visit problem page as a student. + self._goto_problem_page() + + # Logout and login as a staff user. + LogoutPage(self.browser).visit() + self._auto_auth("STAFF_TESTER", "staff101@example.com", True) + + # Visit course outline page in studio. + self.course_outline.visit() + + # Set release date for subsection in future. + self._change_problem_release_date_in_studio() + + # Wait for 2 seconds to save new date. + time.sleep(2) + + # Logout and login as a student. + LogoutPage(self.browser).visit() + self._auto_auth(self.USERNAME, self.EMAIL, False) + + # Visit courseware as a student. + self.courseware_page.visit() + + # Problem name should be "TEST PROBLEM 2". + self.assertEqual(self.problem_page.problem_name, 'TEST PROBLEM 2') diff --git a/common/test/acceptance/tests/test_lms.py b/common/test/acceptance/tests/test_lms.py index 55f68a5cf5..97ab8eb54f 100644 --- a/common/test/acceptance/tests/test_lms.py +++ b/common/test/acceptance/tests/test_lms.py @@ -45,10 +45,6 @@ class RegistrationTest(UniqueCourseTest): # Visit the main page with the list of courses self.find_courses_page.visit() - # Expect that the fixture course exists - course_ids = self.find_courses_page.course_id_list - self.assertIn(self.course_id, course_ids) - # Go to the course about page and click the register button self.course_about_page.visit() register_page = self.course_about_page.register() diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py index 1117e92f0f..5df386c957 100644 --- a/lms/djangoapps/courseware/views.py +++ b/lms/djangoapps/courseware/views.py @@ -159,7 +159,7 @@ def get_current_child(xmodule, min_depth=None): default_child = child_modules[0] else: content_children = [child for child in child_modules if - child.has_children_at_depth(min_depth - 1)] + child.has_children_at_depth(min_depth - 1) and child.get_display_items()] default_child = content_children[0] if content_children else None return default_child @@ -432,8 +432,12 @@ def index(request, course_id, chapter=None, section=None, studio_url = get_studio_url(course, 'course') prev_section = get_current_child(chapter_module) if prev_section is None: - # Something went wrong -- perhaps this chapter has no sections visible to the user - raise Http404 + # Something went wrong -- perhaps this chapter has no sections visible to the user. + # Clearing out the last-visited state and showing "first-time" view by redirecting + # to courseware. + course_module.position = None + course_module.save() + return redirect(reverse('courseware', args=[course.id.to_deprecated_string()])) prev_section_url = reverse('courseware_section', kwargs={ 'course_id': course_key.to_deprecated_string(), 'chapter': chapter_descriptor.url_name,