def test_course_updated_with_entrance_exam(self): """ Given that I visit an empty course before import I should not see a section named 'Section' or 'Entrance Exam' When I visit the import page And I upload a course that has an entrance exam section named 'Entrance Exam' And I visit the course outline page again The section named 'Entrance Exam' should now be available. And when I switch the view mode to student view and Visit CourseWare Then I see one section in the sidebar that is 'Entrance Exam' """ self.landing_page.visit() # Should not exist yet. self.assertRaises(IndexError, self.landing_page.section, "Section") self.assertRaises(IndexError, self.landing_page.section, "Entrance Exam") self.import_page.visit() self.import_page.wait_for_choose_file_click_handler() self.import_page.upload_tarball(self.tarball_name) self.import_page.wait_for_upload() self.landing_page.visit() # There should be two sections. 'Entrance Exam' and 'Section' on the landing page. self.landing_page.section("Entrance Exam") self.landing_page.section("Section") self.landing_page.view_live() courseware = CoursewarePage(self.browser, self.course_id) courseware.wait_for_page() StaffCoursewarePage(self.browser, self.course_id).set_staff_view_mode('Student') self.assertEqual(courseware.num_sections, 1) self.assertIn("To access course materials, you must score", courseware.entrance_exam_message_selector.text[0])
def test_course_updated_with_entrance_exam(self): """ Given that I visit an empty course before import I should not see a section named 'Section' or 'Entrance Exam' When I visit the import page And I upload a course that has an entrance exam section named 'Entrance Exam' And I visit the course outline page again The section named 'Entrance Exam' should now be available. And when I switch the view mode to student view and Visit CourseWare Then I see one section in the sidebar that is 'Entrance Exam' """ self.landing_page.visit() # Should not exist yet. self.assertRaises(IndexError, self.landing_page.section, "Section") self.assertRaises(IndexError, self.landing_page.section, "Entrance Exam") self.import_page.visit() self.import_page.upload_tarball(self.tarball_name) self.import_page.wait_for_upload() self.landing_page.visit() # There should be two sections. 'Entrance Exam' and 'Section' on the landing page. self.landing_page.section("Entrance Exam") self.landing_page.section("Section") self.landing_page.view_live() courseware = CoursewarePage(self.browser, self.course_id) courseware.wait_for_page() StaffPage(self.browser, self.course_id).set_staff_view_mode('Student') self.assertEqual(courseware.num_sections, 1) self.assertIn( "To access course materials, you must score", courseware.entrance_exam_message_selector.text[0] )
def resume_course_from_header(self): """ Navigate to courseware using Resume Course button in the header. """ self.q(css=self.HEADER_RESUME_COURSE_SELECTOR).first.click() courseware_page = CoursewarePage(self.browser, self.course_id) courseware_page.wait_for_page()
def test_hinted_login(self): """ Test the login page when coming from course URL that specified which third party provider to use """ # Create a user account and link it to third party auth with the dummy provider: AutoAuthPage(self.browser, course_id=self.course_id).visit() self._link_dummy_account() try: LogoutPage(self.browser).visit() # When not logged in, try to load a course URL that includes the provider hint ?tpa_hint=... course_page = CoursewarePage(self.browser, self.course_id) self.browser.get(course_page.url + '?tpa_hint=oa2-dummy') # We should now be redirected to the login page self.login_page.wait_for_page() self.assertIn( "Would you like to sign in using your Dummy credentials?", self.login_page.hinted_login_prompt) # Baseline screen-shots are different for chrome and firefox. #self.assertScreenshot('#hinted-login-form', 'hinted-login-{}'.format(self.browser.name), .25) #The line above is commented out temporarily see SOL-1937 self.login_page.click_third_party_dummy_provider() # We should now be redirected to the course page course_page.wait_for_page() finally: self._unlink_dummy_account()
def test_hinted_login(self): """ Test the login page when coming from course URL that specified which third party provider to use """ # Create a user account and link it to third party auth with the dummy provider: AutoAuthPage(self.browser, course_id=self.course_id).visit() self._link_dummy_account() try: LogoutPage(self.browser).visit() # When not logged in, try to load a course URL that includes the provider hint ?tpa_hint=... course_page = CoursewarePage(self.browser, self.course_id) self.browser.get(course_page.url + '?tpa_hint=oa2-dummy') # We should now be redirected to the login page self.login_page.wait_for_page() self.assertIn( "Would you like to sign in using your Dummy credentials?", self.login_page.hinted_login_prompt ) # Baseline screen-shots are different for chrome and firefox. #self.assertScreenshot('#hinted-login-form', 'hinted-login-{}'.format(self.browser.name), .25) #The line above is commented out temporarily see SOL-1937 self.login_page.click_third_party_dummy_provider() # We should now be redirected to the course page course_page.wait_for_page() finally: self._unlink_dummy_account()
def test_course_rerun(self): """ Scenario: Courses can be rerun Given I have a course with a section, subsesction, vertical, and html component with content 'Test Content' When I visit the course rerun page And I type 'test_rerun' in the course run field And I click Create Rerun And I visit the course listing page And I wait for all courses to finish processing And I click on the course with run 'test_rerun' Then I see a rerun notification on the course outline page And when I click 'Dismiss' on the notification Then I do not see a rerun notification And when I expand the subsection and click on the unit And I click 'View Live Version' Then I see one html component with the content 'Test Content' """ course_info = (self.course_info['org'], self.course_info['number'], self.course_info['run']) updated_course_info = course_info[0] + "+" + course_info[1] + "+" + course_info[2] self.dashboard_page.visit() self.dashboard_page.scroll_to_course(course_info[1]) self.dashboard_page.create_rerun(updated_course_info) rerun_page = CourseRerunPage(self.browser, *course_info) rerun_page.wait_for_page() course_run = 'test_rerun_' + str(random.randrange(1000000, 9999999)) rerun_page.course_run = course_run rerun_page.create_rerun() def finished_processing(): self.dashboard_page.visit() return not self.dashboard_page.has_processing_courses EmptyPromise(finished_processing, "Rerun finished processing", try_interval=5, timeout=60).fulfill() assert course_run in self.dashboard_page.course_runs self.dashboard_page.click_course_run(course_run) outline_page = CourseOutlinePage(self.browser, *course_info) outline_page.wait_for_page() self.assertTrue(outline_page.has_rerun_notification) outline_page.dismiss_rerun_notification() EmptyPromise(lambda: not outline_page.has_rerun_notification, "Rerun notification dismissed").fulfill() subsection = outline_page.section(self.SECTION_NAME).subsection(self.SUBSECITON_NAME) subsection.expand_subsection() unit_page = subsection.unit(self.UNIT_NAME).go_to() unit_page.view_published_version() courseware = CoursewarePage(self.browser, self.course_id) courseware.wait_for_page() self.assertEqual(courseware.num_xblock_components, 1) self.assertEqual(courseware.xblock_component_html_content(), self.COMPONENT_CONTENT)
def test_course_rerun(self): """ Scenario: Courses can be rerun Given I have a course with a section, subsesction, vertical, and html component with content 'Test Content' When I visit the course rerun page And I type 'test_rerun' in the course run field And I click Create Rerun And I visit the course listing page And I wait for all courses to finish processing And I click on the course with run 'test_rerun' Then I see a rerun notification on the course outline page And when I click 'Dismiss' on the notification Then I do not see a rerun notification And when I expand the subsection and click on the unit And I click 'View Live Version' Then I see one html component with the content 'Test Content' """ course_info = (self.course_info['org'], self.course_info['number'], self.course_info['run']) updated_course_info = course_info[0] + "+" + course_info[1] + "+" + course_info[2] self.dashboard_page.visit() self.dashboard_page.scroll_to_course(course_info[1]) self.dashboard_page.create_rerun(updated_course_info) rerun_page = CourseRerunPage(self.browser, *course_info) rerun_page.wait_for_page() course_run = 'test_rerun_' + str(random.randrange(1000000, 9999999)) rerun_page.course_run = course_run rerun_page.create_rerun() def finished_processing(): self.dashboard_page.visit() return not self.dashboard_page.has_processing_courses EmptyPromise(finished_processing, "Rerun finished processing", try_interval=5, timeout=60).fulfill() assert_in(course_run, self.dashboard_page.course_runs) self.dashboard_page.click_course_run(course_run) outline_page = CourseOutlinePage(self.browser, *course_info) outline_page.wait_for_page() self.assertTrue(outline_page.has_rerun_notification) outline_page.dismiss_rerun_notification() EmptyPromise(lambda: not outline_page.has_rerun_notification, "Rerun notification dismissed").fulfill() subsection = outline_page.section(self.SECTION_NAME).subsection(self.SUBSECITON_NAME) subsection.expand_subsection() unit_page = subsection.unit(self.UNIT_NAME).go_to() unit_page.view_published_version() courseware = CoursewarePage(self.browser, self.course_id) courseware.wait_for_page() self.assertEqual(courseware.num_xblock_components, 1) self.assertEqual(courseware.xblock_component_html_content(), self.COMPONENT_CONTENT)
def _wait_for_course_section(self, section_title, subsection_title): """ Ensures the user navigates to the course content page with the correct section and subsection. """ courseware_page = CoursewarePage(self.browser, self.parent_page.course_id) courseware_page.wait_for_page() # TODO: TNL-6546: Remove this if/visit_unified_course_view if self.parent_page.unified_course_view: courseware_page.nav.visit_unified_course_view() self.wait_for( promise_check_func=lambda: courseware_page.nav.is_on_section( section_title, subsection_title), description= "Waiting for course page with section '{0}' and subsection '{1}'". format(section_title, subsection_title))
def test_entrance_exam_section_2(self): """ Scenario: Any course that is enabled for an entrance exam, should have entrance exam chapter at course page. Given that I am on the course page When I view the course that has an entrance exam Then there should be an "Entrance Exam" chapter.' """ courseware_page = CoursewarePage(self.browser, self.course_id) entrance_exam_link_selector = '.accordion .course-navigation .chapter .group-heading' # visit course page and make sure there is not entrance exam chapter. courseware_page.visit() courseware_page.wait_for_page() self.assertFalse( element_has_text(page=courseware_page, css_selector=entrance_exam_link_selector, text='Entrance Exam')) # Logout and login as a staff. LogoutPage(self.browser).visit() AutoAuthPage(self.browser, course_id=self.course_id, staff=True).visit() # visit course settings page and set/enabled entrance exam for that course. self.settings_page.visit() self.settings_page.require_entrance_exam() self.settings_page.save_changes() # Logout and login as a student. LogoutPage(self.browser).visit() AutoAuthPage(self.browser, course_id=self.course_id, staff=False).visit() # visit course info page and make sure there is an "Entrance Exam" section. courseware_page.visit() courseware_page.wait_for_page() self.assertTrue( element_has_text(page=courseware_page, css_selector=entrance_exam_link_selector, text='Entrance Exam'))
def test_entrance_exam_section_2(self): """ Scenario: Any course that is enabled for an entrance exam, should have entrance exam chapter at course page. Given that I am on the course page When I view the course that has an entrance exam Then there should be an "Entrance Exam" chapter.' """ courseware_page = CoursewarePage(self.browser, self.course_id) entrance_exam_link_selector = '.accordion .course-navigation .chapter .group-heading' # visit course page and make sure there is not entrance exam chapter. courseware_page.visit() courseware_page.wait_for_page() self.assertFalse(element_has_text( page=courseware_page, css_selector=entrance_exam_link_selector, text='Entrance Exam' )) # Logout and login as a staff. LogoutPage(self.browser).visit() AutoAuthPage(self.browser, course_id=self.course_id, staff=True).visit() # visit course settings page and set/enabled entrance exam for that course. self.settings_page.visit() self.settings_page.require_entrance_exam() self.settings_page.save_changes() # Logout and login as a student. LogoutPage(self.browser).visit() AutoAuthPage(self.browser, course_id=self.course_id, staff=False).visit() # visit course info page and make sure there is an "Entrance Exam" section. courseware_page.visit() courseware_page.wait_for_page() self.assertTrue(element_has_text( page=courseware_page, css_selector=entrance_exam_link_selector, text='Entrance Exam' ))
class CourseOutlinePage(PageObject): """ Course outline fragment of page. """ url = None def __init__(self, browser, parent_page): super(CourseOutlinePage, self).__init__(browser) self.parent_page = parent_page self.courseware_page = CoursewarePage(self.browser, self.parent_page.course_id) def is_browser_on_page(self): return self.parent_page.is_browser_on_page @property def sections(self): """ Return a dictionary representation of sections and subsections. Example: { 'Introduction': ['Course Overview'], 'Week 1': ['Lesson 1', 'Lesson 2', 'Homework'] 'Final Exam': ['Final Exam'] } You can use these titles in `go_to_section` to navigate to the section. """ # Dict to store the result outline_dict = dict() section_titles = self._section_titles() # Get the section titles for each chapter for sec_index, sec_title in enumerate(section_titles): if len(section_titles) < 1: self.warning( "Could not find subsections for '{0}'".format(sec_title)) else: # Add one to convert list index (starts at 0) to CSS index (starts at 1) outline_dict[sec_title] = self._subsection_titles(sec_index + 1) return outline_dict def go_to_section(self, section_title, subsection_title): """ Go to the section in the courseware. Every section must have at least one subsection, so specify both the section and subsection title. Example: go_to_section("Week 1", "Lesson 1") """ # Get the section by index try: section_index = self._section_titles().index(section_title) except ValueError: self.warning("Could not find section '{0}'".format(section_title)) return # Get the subsection by index try: subsection_index = self._subsection_titles( section_index + 1).index(subsection_title) except ValueError: msg = "Could not find subsection '{0}' in section '{1}'".format( subsection_title, section_title) self.warning(msg) return # Convert list indices (start at zero) to CSS indices (start at 1) subsection_css = ( ".outline-item.section:nth-of-type({0}) .subsection:nth-of-type({1}) .outline-item" ).format(section_index + 1, subsection_index + 1) # Click the subsection and ensure that the page finishes reloading self.q(css=subsection_css).first.click() self.courseware_page.wait_for_page() # TODO: TNL-6546: Remove this if/visit_unified_course_view if self.parent_page.unified_course_view: self.courseware_page.nav.visit_unified_course_view() self._wait_for_course_section(section_title, subsection_title) def _section_titles(self): """ Return a list of all section titles on the page. """ section_css = '.section-name span' return self.q(css=section_css).map(lambda el: el.text.strip()).results def _subsection_titles(self, section_index): """ Return a list of all subsection titles on the page for the section at index `section_index` (starts at 1). """ # Retrieve the subsection title for the section # Add one to the list index to get the CSS index, which starts at one subsection_css = ( # TODO: TNL-6387: Will need to switch to this selector for subsections # ".outline-item.section:nth-of-type({0}) .subsection span:nth-of-type(1)" ".outline-item.section:nth-of-type({0}) .subsection a" ).format(section_index) return self.q(css=subsection_css).map( lambda el: el.get_attribute('innerHTML').strip()).results def _wait_for_course_section(self, section_title, subsection_title): """ Ensures the user navigates to the course content page with the correct section and subsection. """ self.wait_for( promise_check_func=lambda: self.courseware_page.nav.is_on_section( section_title, subsection_title), description= "Waiting for course page with section '{0}' and subsection '{1}'". format(section_title, subsection_title))
class GatingTest(UniqueCourseTest): """ Test gating feature in LMS. """ STAFF_USERNAME = "******" STAFF_EMAIL = "*****@*****.**" STUDENT_USERNAME = "******" STUDENT_EMAIL = "*****@*****.**" def setUp(self): super(GatingTest, self).setUp() self.logout_page = LogoutPage(self.browser) self.course_home_page = CourseHomePage(self.browser, self.course_id) self.courseware_page = CoursewarePage(self.browser, self.course_id) self.studio_course_outline = StudioCourseOutlinePage( self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run'] ) xml = dedent(""" <problem> <p>What is height of eiffel tower without the antenna?.</p> <multiplechoiceresponse> <choicegroup label="What is height of eiffel tower without the antenna?" type="MultipleChoice"> <choice correct="false">324 meters<choicehint>Antenna is 24 meters high</choicehint></choice> <choice correct="true">300 meters</choice> <choice correct="false">224 meters</choice> <choice correct="false">400 meters</choice> </choicegroup> </multiplechoiceresponse> </problem> """) self.problem1 = XBlockFixtureDesc('problem', 'HEIGHT OF EIFFEL TOWER', data=xml) # Install a course with sections/problems course_fixture = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'] ) course_fixture.add_advanced_settings({ "enable_subsection_gating": {"value": "true"} }) course_fixture.add_children( XBlockFixtureDesc('chapter', 'Test Section 1').add_children( XBlockFixtureDesc('sequential', 'Test Subsection 1').add_children( self.problem1 ), XBlockFixtureDesc('sequential', 'Test Subsection 2').add_children( XBlockFixtureDesc('problem', 'Test Problem 2') ) ) ).install() def _auto_auth(self, username, email, staff): """ Logout and login with given credentials. """ self.logout_page.visit() AutoAuthPage(self.browser, username=username, email=email, course_id=self.course_id, staff=staff).visit() def _setup_prereq(self): """ Make the first subsection a prerequisite """ # Login as staff self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True) # Make the first subsection a prerequisite self.studio_course_outline.visit() self.studio_course_outline.open_subsection_settings_dialog(0) self.studio_course_outline.select_advanced_tab(desired_item='gated_content') self.studio_course_outline.make_gating_prerequisite() def _setup_gated_subsection(self): """ Gate the second subsection on the first subsection """ # Login as staff self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True) # Gate the second subsection based on the score achieved in the first subsection self.studio_course_outline.visit() self.studio_course_outline.open_subsection_settings_dialog(1) self.studio_course_outline.select_advanced_tab(desired_item='gated_content') self.studio_course_outline.add_prerequisite_to_subsection("80") def _fulfill_prerequisite(self): """ Fulfill the prerequisite needed to see gated content """ problem_page = ProblemPage(self.browser) self.assertEqual(problem_page.wait_for_page().problem_name, 'HEIGHT OF EIFFEL TOWER') problem_page.click_choice('choice_1') problem_page.click_submit() def test_subsection_gating_in_studio(self): """ Given that I am a staff member When I visit the course outline page in studio. And open the subsection edit dialog Then I can view all settings related to Gating And update those settings to gate a subsection """ self._setup_prereq() # Assert settings are displayed correctly for a prerequisite subsection self.studio_course_outline.visit() self.studio_course_outline.open_subsection_settings_dialog(0) self.studio_course_outline.select_advanced_tab(desired_item='gated_content') self.assertTrue(self.studio_course_outline.gating_prerequisite_checkbox_is_visible()) self.assertTrue(self.studio_course_outline.gating_prerequisite_checkbox_is_checked()) self.assertFalse(self.studio_course_outline.gating_prerequisites_dropdown_is_visible()) self.assertFalse(self.studio_course_outline.gating_prerequisite_min_score_is_visible()) self._setup_gated_subsection() # Assert settings are displayed correctly for a gated subsection self.studio_course_outline.visit() self.studio_course_outline.open_subsection_settings_dialog(1) self.studio_course_outline.select_advanced_tab(desired_item='gated_content') self.assertTrue(self.studio_course_outline.gating_prerequisite_checkbox_is_visible()) self.assertTrue(self.studio_course_outline.gating_prerequisites_dropdown_is_visible()) self.assertTrue(self.studio_course_outline.gating_prerequisite_min_score_is_visible()) def test_gated_subsection_in_lms_for_student(self): """ Given that I am a student When I visit the LMS Courseware Then I cannot see a gated subsection When I fulfill the gating Prerequisite Then I can see the gated subsection """ self._setup_prereq() self._setup_gated_subsection() self._auto_auth(self.STUDENT_USERNAME, self.STUDENT_EMAIL, False) self.course_home_page.visit() self.assertEqual(self.course_home_page.outline.num_subsections, 1) # Fulfill prerequisite and verify that gated subsection is shown self.courseware_page.visit() self._fulfill_prerequisite() self.course_home_page.visit() self.assertEqual(self.course_home_page.outline.num_subsections, 2) def test_gated_subsection_in_lms_for_staff(self): """ Given that I am a staff member When I visit the LMS Courseware Then I can see all gated subsections Displayed along with notification banners Then if I masquerade as a student Then I cannot see a gated subsection When I fufill the gating prerequisite Then I can see the gated subsection (without a banner) """ self._setup_prereq() self._setup_gated_subsection() # Fulfill prerequisites for specific student self._auto_auth(self.STUDENT_USERNAME, self.STUDENT_EMAIL, False) self.courseware_page.visit() self._fulfill_prerequisite() self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True) self.course_home_page.visit() self.assertEqual(self.course_home_page.preview.staff_view_mode, 'Staff') self.assertEqual(self.course_home_page.outline.num_subsections, 2) # Click on gated section and check for banner self.course_home_page.outline.go_to_section('Test Section 1', 'Test Subsection 2') self.courseware_page.wait_for_page() self.assertTrue(self.courseware_page.has_banner()) self.course_home_page.visit() self.course_home_page.outline.go_to_section('Test Section 1', 'Test Subsection 1') self.courseware_page.wait_for_page() self.course_home_page.visit() self.course_home_page.preview.set_staff_view_mode('Learner') self.assertEqual(self.course_home_page.outline.num_subsections, 1) self.course_home_page.outline.go_to_section('Test Section 1', 'Test Subsection 1') self.courseware_page.wait_for_page() self.assertFalse(self.courseware_page.has_banner()) self.course_home_page.visit() self.course_home_page.preview.set_staff_view_mode_specific_student(self.STUDENT_USERNAME) self.assertEqual(self.course_home_page.outline.num_subsections, 2) self.course_home_page.outline.go_to_section('Test Section 1', 'Test Subsection 2') self.courseware_page.wait_for_page() self.assertFalse(self.courseware_page.has_banner())
class GatingTest(UniqueCourseTest): """ Test gating feature in LMS. """ STAFF_USERNAME = "******" STAFF_EMAIL = "*****@*****.**" STUDENT_USERNAME = "******" STUDENT_EMAIL = "*****@*****.**" def setUp(self): super(GatingTest, self).setUp() self.logout_page = LogoutPage(self.browser) self.course_home_page = CourseHomePage(self.browser, self.course_id) self.courseware_page = CoursewarePage(self.browser, self.course_id) self.studio_course_outline = StudioCourseOutlinePage( self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run']) xml = dedent(""" <problem> <p>What is height of eiffel tower without the antenna?.</p> <multiplechoiceresponse> <choicegroup label="What is height of eiffel tower without the antenna?" type="MultipleChoice"> <choice correct="false">324 meters<choicehint>Antenna is 24 meters high</choicehint></choice> <choice correct="true">300 meters</choice> <choice correct="false">224 meters</choice> <choice correct="false">400 meters</choice> </choicegroup> </multiplechoiceresponse> </problem> """) self.problem1 = XBlockFixtureDesc('problem', 'HEIGHT OF EIFFEL TOWER', data=xml) # Install a course with sections/problems course_fixture = CourseFixture(self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name']) course_fixture.add_advanced_settings( {"enable_subsection_gating": { "value": "true" }}) course_fixture.add_children( XBlockFixtureDesc('chapter', 'Test Section 1').add_children( XBlockFixtureDesc('sequential', 'Test Subsection 1').add_children( self.problem1), XBlockFixtureDesc('sequential', 'Test Subsection 2').add_children( XBlockFixtureDesc( 'problem', 'Test Problem 2')))).install() def _auto_auth(self, username, email, staff): """ Logout and login with given credentials. """ self.logout_page.visit() AutoAuthPage(self.browser, username=username, email=email, course_id=self.course_id, staff=staff).visit() def _setup_prereq(self): """ Make the first subsection a prerequisite """ # Login as staff self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True) # Make the first subsection a prerequisite self.studio_course_outline.visit() self.studio_course_outline.open_subsection_settings_dialog(0) self.studio_course_outline.select_advanced_tab( desired_item='gated_content') self.studio_course_outline.make_gating_prerequisite() def _setup_gated_subsection(self): """ Gate the second subsection on the first subsection """ # Login as staff self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True) # Gate the second subsection based on the score achieved in the first subsection self.studio_course_outline.visit() self.studio_course_outline.open_subsection_settings_dialog(1) self.studio_course_outline.select_advanced_tab( desired_item='gated_content') self.studio_course_outline.add_prerequisite_to_subsection("80") def _fulfill_prerequisite(self): """ Fulfill the prerequisite needed to see gated content """ problem_page = ProblemPage(self.browser) self.assertEqual(problem_page.wait_for_page().problem_name, 'HEIGHT OF EIFFEL TOWER') problem_page.click_choice('choice_1') problem_page.click_submit() def test_subsection_gating_in_studio(self): """ Given that I am a staff member When I visit the course outline page in studio. And open the subsection edit dialog Then I can view all settings related to Gating And update those settings to gate a subsection """ self._setup_prereq() # Assert settings are displayed correctly for a prerequisite subsection self.studio_course_outline.visit() self.studio_course_outline.open_subsection_settings_dialog(0) self.studio_course_outline.select_advanced_tab( desired_item='gated_content') self.assertTrue(self.studio_course_outline. gating_prerequisite_checkbox_is_visible()) self.assertTrue(self.studio_course_outline. gating_prerequisite_checkbox_is_checked()) self.assertFalse(self.studio_course_outline. gating_prerequisites_dropdown_is_visible()) self.assertFalse(self.studio_course_outline. gating_prerequisite_min_score_is_visible()) self._setup_gated_subsection() # Assert settings are displayed correctly for a gated subsection self.studio_course_outline.visit() self.studio_course_outline.open_subsection_settings_dialog(1) self.studio_course_outline.select_advanced_tab( desired_item='gated_content') self.assertTrue(self.studio_course_outline. gating_prerequisite_checkbox_is_visible()) self.assertTrue(self.studio_course_outline. gating_prerequisites_dropdown_is_visible()) self.assertTrue(self.studio_course_outline. gating_prerequisite_min_score_is_visible()) def test_gated_subsection_in_lms_for_student(self): """ Given that I am a student When I visit the LMS Courseware Then I can see a gated subsection The gated subsection should have a lock icon and be in the format: "<Subsection Title> (Prerequisite Required)" When I fulfill the gating Prerequisite Then I can see the gated subsection Now the gated subsection should have an unlock icon and screen readers should read the section as: "<Subsection Title> Unlocked" """ self._setup_prereq() self._setup_gated_subsection() self._auto_auth(self.STUDENT_USERNAME, self.STUDENT_EMAIL, False) self.course_home_page.visit() self.assertEqual(self.course_home_page.outline.num_subsections, 2) # Fulfill prerequisite and verify that gated subsection is shown self.courseware_page.visit() self._fulfill_prerequisite() self.course_home_page.visit() self.assertEqual(self.course_home_page.outline.num_subsections, 2) def test_gated_subsection_in_lms_for_staff(self): """ Given that I am a staff member When I visit the LMS Courseware Then I can see all gated subsections Displayed along with notification banners Then if I masquerade as a student Then I can see a gated subsection The gated subsection should have a lock icon and be in the format: "<Subsection Title> (Prerequisite Required)" """ self._setup_prereq() self._setup_gated_subsection() self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True) self.course_home_page.visit() self.assertEqual(self.course_home_page.preview.staff_view_mode, 'Staff') self.assertEqual(self.course_home_page.outline.num_subsections, 2) # Click on gated section and check for banner self.course_home_page.outline.go_to_section('Test Section 1', 'Test Subsection 2') self.courseware_page.wait_for_page() self.assertTrue(self.courseware_page.has_banner()) self.course_home_page.visit() self.course_home_page.outline.go_to_section('Test Section 1', 'Test Subsection 1') self.courseware_page.wait_for_page() self.course_home_page.visit() self.course_home_page.preview.set_staff_view_mode('Learner') self.course_home_page.wait_for_page() self.assertEqual(self.course_home_page.outline.num_subsections, 2) self.course_home_page.outline.go_to_section('Test Section 1', 'Test Subsection 1') self.courseware_page.wait_for_page() # banner displayed informing section is a prereq self.assertTrue(self.courseware_page.has_banner())
class VisibleToStaffOnlyTest(UniqueCourseTest): """ Tests that content with visible_to_staff_only set to True cannot be viewed by students. """ def setUp(self): super(VisibleToStaffOnlyTest, self).setUp() 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').add_children( XBlockFixtureDesc('sequential', 'Subsection With Locked Unit'). add_children( XBlockFixtureDesc( 'vertical', 'Locked Unit', metadata={ 'visible_to_staff_only': True }).add_children( XBlockFixtureDesc( 'html', 'Html Child in locked unit', data="<html>Visible only to staff</html>"), ), XBlockFixtureDesc( 'vertical', 'Unlocked Unit').add_children( XBlockFixtureDesc( 'html', 'Html Child in unlocked unit', data="<html>Visible only to all</html>"), )), XBlockFixtureDesc( 'sequential', 'Unlocked Subsection').add_children( XBlockFixtureDesc( 'vertical', 'Test Unit').add_children( XBlockFixtureDesc( 'html', 'Html Child in visible unit', data="<html>Visible to all</html>"), )), XBlockFixtureDesc( 'sequential', 'Locked Subsection', metadata={ 'visible_to_staff_only': True }).add_children( XBlockFixtureDesc( 'vertical', 'Test Unit').add_children( XBlockFixtureDesc( 'html', 'Html Child in locked subsection', data="<html>Visible only to staff</html>")) ))).install() self.course_home_page = CourseHomePage(self.browser, self.course_id) self.courseware_page = CoursewarePage(self.browser, self.course_id) def test_visible_to_staff(self): """ Scenario: All content is visible for a user marked is_staff (different from course staff) Given some of the course content has been marked 'visible_to_staff_only' And I am logged on with an account marked 'is_staff' Then I can see all course content """ AutoAuthPage(self.browser, username="******", email="*****@*****.**", course_id=self.course_id, staff=True).visit() self.course_home_page.visit() self.assertEqual( 3, len(self.course_home_page.outline.sections['Test Section'])) self.course_home_page.outline.go_to_section( "Test Section", "Subsection With Locked Unit") self.courseware_page.wait_for_page() self.assertEqual([u'Locked Unit', u'Unlocked Unit'], self.courseware_page.nav.sequence_items) self.course_home_page.visit() self.course_home_page.outline.go_to_section("Test Section", "Unlocked Subsection") self.courseware_page.wait_for_page() self.assertEqual([u'Test Unit'], self.courseware_page.nav.sequence_items) self.course_home_page.visit() self.course_home_page.outline.go_to_section("Test Section", "Locked Subsection") self.courseware_page.wait_for_page() self.assertEqual([u'Test Unit'], self.courseware_page.nav.sequence_items) def test_visible_to_student(self): """ Scenario: Content marked 'visible_to_staff_only' is not visible for students in the course Given some of the course content has been marked 'visible_to_staff_only' And I am logged on with an authorized student account Then I can only see content without 'visible_to_staff_only' set to True """ AutoAuthPage(self.browser, username="******", email="*****@*****.**", course_id=self.course_id, staff=False).visit() self.course_home_page.visit() self.assertEqual( 2, len(self.course_home_page.outline.sections['Test Section'])) self.course_home_page.outline.go_to_section( "Test Section", "Subsection With Locked Unit") self.courseware_page.wait_for_page() self.assertEqual([u'Unlocked Unit'], self.courseware_page.nav.sequence_items) self.course_home_page.visit() self.course_home_page.outline.go_to_section("Test Section", "Unlocked Subsection") self.courseware_page.wait_for_page() self.assertEqual([u'Test Unit'], self.courseware_page.nav.sequence_items)
class BookmarksTest(BookmarksTestMixin): """ Tests to verify bookmarks functionality. """ def setUp(self): """ Initialize test setup. """ super(BookmarksTest, self).setUp() self.course_outline_page = CourseOutlinePage( self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run']) self.courseware_page = CoursewarePage(self.browser, self.course_id) self.bookmarks_page = BookmarksPage(self.browser, self.course_id) self.course_nav = CourseNavPage(self.browser) # Get session to be used for bookmarking units self.session = requests.Session() params = { 'username': self.USERNAME, 'email': self.EMAIL, 'course_id': self.course_id } response = self.session.get(BASE_URL + "/auto_auth", params=params) self.assertTrue(response.ok, "Failed to get session") def _test_setup(self, num_chapters=2): """ Setup test settings. Arguments: num_chapters: number of chapters to create in course """ self.create_course_fixture(num_chapters) # Auto-auth register for the course. LmsAutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id).visit() self.courseware_page.visit() def _bookmark_unit(self, location): """ Bookmark a unit Arguments: location (str): unit location """ _headers = { 'Content-type': 'application/json', 'X-CSRFToken': self.session.cookies['csrftoken'], } params = {'course_id': self.course_id} data = json.dumps({'usage_id': location}) response = self.session.post(BASE_URL + '/api/bookmarks/v1/bookmarks/', data=data, params=params, headers=_headers) self.assertTrue(response.ok, "Failed to bookmark unit") def _bookmark_units(self, num_units): """ Bookmark first `num_units` units Arguments: num_units(int): Number of units to bookmarks """ xblocks = self.course_fixture.get_nested_xblocks(category="vertical") for index in range(num_units): self._bookmark_unit(xblocks[index].locator) def _breadcrumb(self, num_units, modified_name=None): """ Creates breadcrumbs for the first `num_units` Arguments: num_units(int): Number of units for which we want to create breadcrumbs Returns: list of breadcrumbs """ breadcrumbs = [] for index in range(num_units): breadcrumbs.append([ 'TestSection{}'.format(index), 'TestSubsection{}'.format(index), modified_name if modified_name else 'TestVertical{}'.format(index) ]) return breadcrumbs def _delete_section(self, index): """ Delete a section at index `index` """ # Logout and login as staff LogoutPage(self.browser).visit() StudioAutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id, staff=True).visit() # Visit course outline page in studio. self.course_outline_page.visit() self.course_outline_page.wait_for_page() self.course_outline_page.section_at(index).delete() # Logout and login as a student. LogoutPage(self.browser).visit() LmsAutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id).visit() # Visit courseware as a student. self.courseware_page.visit() self.courseware_page.wait_for_page() def _toggle_bookmark_and_verify(self, bookmark_icon_state, bookmark_button_state, bookmarked_count): """ Bookmark/Un-Bookmark a unit and then verify """ self.assertTrue(self.courseware_page.bookmark_button_visible) self.courseware_page.click_bookmark_unit_button() self.assertEqual(self.courseware_page.bookmark_icon_visible, bookmark_icon_state) self.assertEqual(self.courseware_page.bookmark_button_state, bookmark_button_state) self.bookmarks_page.click_bookmarks_button() self.assertEqual(self.bookmarks_page.count(), bookmarked_count) def _verify_pagination_info(self, bookmark_count_on_current_page, header_text, previous_button_enabled, next_button_enabled, current_page_number, total_pages): """ Verify pagination info """ self.assertEqual(self.bookmarks_page.count(), bookmark_count_on_current_page) self.assertEqual(self.bookmarks_page.get_pagination_header_text(), header_text) self.assertEqual(self.bookmarks_page.is_previous_page_button_enabled(), previous_button_enabled) self.assertEqual(self.bookmarks_page.is_next_page_button_enabled(), next_button_enabled) self.assertEqual(self.bookmarks_page.get_current_page_number(), current_page_number) self.assertEqual(self.bookmarks_page.get_total_pages, total_pages) def _navigate_to_bookmarks_list(self): """ Navigates and verifies the bookmarks list page. """ self.bookmarks_page.click_bookmarks_button() self.assertTrue(self.bookmarks_page.results_present()) self.assertEqual(self.bookmarks_page.results_header_text(), 'My Bookmarks') def _verify_breadcrumbs(self, num_units, modified_name=None): """ Verifies the breadcrumb trail. """ bookmarked_breadcrumbs = self.bookmarks_page.breadcrumbs() # Verify bookmarked breadcrumbs. breadcrumbs = self._breadcrumb(num_units=num_units, modified_name=modified_name) breadcrumbs.reverse() self.assertEqual(bookmarked_breadcrumbs, breadcrumbs) def update_and_publish_block_display_name(self, modified_name): """ Update and publish the block/unit display name. """ self.course_outline_page.visit() self.course_outline_page.wait_for_page() self.course_outline_page.expand_all_subsections() section = self.course_outline_page.section_at(0) container_page = section.subsection_at(0).unit_at(0).go_to() self.course_fixture._update_xblock( container_page.locator, { # pylint: disable=protected-access "metadata": { "display_name": modified_name } }) container_page.visit() container_page.wait_for_page() self.assertEqual(container_page.name, modified_name) container_page.publish_action.click() def test_bookmark_button(self): """ Scenario: Bookmark unit button toggles correctly Given that I am a registered user And I visit my courseware page For first 2 units I visit the unit And I can see the Bookmark button When I click on Bookmark button Then unit should be bookmarked Then I click again on the bookmark button And I should see a unit un-bookmarked """ self._test_setup() for index in range(2): self.course_nav.go_to_section('TestSection{}'.format(index), 'TestSubsection{}'.format(index)) self._toggle_bookmark_and_verify(True, 'bookmarked', 1) self.bookmarks_page.click_bookmarks_button(False) self._toggle_bookmark_and_verify(False, '', 0) def test_empty_bookmarks_list(self): """ Scenario: An empty bookmarks list is shown if there are no bookmarked units. Given that I am a registered user And I visit my courseware page And I can see the Bookmarks button When I click on Bookmarks button Then I should see an empty bookmarks list And empty bookmarks list content is correct """ self._test_setup() self.assertTrue(self.bookmarks_page.bookmarks_button_visible()) self.bookmarks_page.click_bookmarks_button() self.assertEqual(self.bookmarks_page.results_header_text(), 'My Bookmarks') self.assertEqual(self.bookmarks_page.empty_header_text(), 'You have not bookmarked any courseware pages yet.') empty_list_text = ( "Use bookmarks to help you easily return to courseware pages. To bookmark a page, " "select Bookmark in the upper right corner of that page. To see a list of all your " "bookmarks, select Bookmarks in the upper left corner of any courseware page." ) self.assertEqual(self.bookmarks_page.empty_list_text(), empty_list_text) def test_bookmarks_list(self): """ Scenario: A bookmarks list is shown if there are bookmarked units. Given that I am a registered user And I visit my courseware page And I have bookmarked 2 units When I click on Bookmarks button Then I should see a bookmarked list with 2 bookmark links And breadcrumb trail is correct for a bookmark When I click on bookmarked link Then I can navigate to correct bookmarked unit """ self._test_setup() self._bookmark_units(2) self._navigate_to_bookmarks_list() self._verify_breadcrumbs(num_units=2) self._verify_pagination_info(bookmark_count_on_current_page=2, header_text='Showing 1-2 out of 2 total', previous_button_enabled=False, next_button_enabled=False, current_page_number=1, total_pages=1) # get usage ids for units xblocks = self.course_fixture.get_nested_xblocks(category="vertical") xblock_usage_ids = [xblock.locator for xblock in xblocks] # Verify link navigation for index in range(2): self.bookmarks_page.click_bookmarked_block(index) self.courseware_page.wait_for_page() self.assertIn(self.courseware_page.active_usage_id(), xblock_usage_ids) self.courseware_page.visit().wait_for_page() self.bookmarks_page.click_bookmarks_button() def test_bookmark_shows_updated_breadcrumb_after_publish(self): """ Scenario: A bookmark breadcrumb trail is updated after publishing the changed display name. Given that I am a registered user And I visit my courseware page And I can see bookmarked unit Then I visit unit page in studio Then I change unit display_name And I publish the changes Then I visit my courseware page And I visit bookmarks list page When I see the bookmark Then I can see the breadcrumb trail with updated display_name. """ self._test_setup(num_chapters=1) self._bookmark_units(num_units=1) self._navigate_to_bookmarks_list() self._verify_breadcrumbs(num_units=1) LogoutPage(self.browser).visit() LmsAutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id, staff=True).visit() modified_name = "Updated name" self.update_and_publish_block_display_name(modified_name) LogoutPage(self.browser).visit() LmsAutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id).visit() self.courseware_page.visit() self._navigate_to_bookmarks_list() self._verify_breadcrumbs(num_units=1, modified_name=modified_name) def test_unreachable_bookmark(self): """ Scenario: We should get a HTTP 404 for an unreachable bookmark. Given that I am a registered user And I visit my courseware page And I have bookmarked 2 units Then I delete a bookmarked unit Then I click on Bookmarks button And I should see a bookmarked list When I click on deleted bookmark Then I should navigated to 404 page """ self._test_setup(num_chapters=1) self._bookmark_units(1) self._delete_section(0) self._navigate_to_bookmarks_list() self._verify_pagination_info(bookmark_count_on_current_page=1, header_text='Showing 1 out of 1 total', previous_button_enabled=False, next_button_enabled=False, current_page_number=1, total_pages=1) self.bookmarks_page.click_bookmarked_block(0) self.assertTrue(is_404_page(self.browser)) def test_page_size_limit(self): """ Scenario: We can't get bookmarks more than default page size. Given that I am a registered user And I visit my courseware page And I have bookmarked all the 11 units available Then I click on Bookmarks button And I should see a bookmarked list And bookmark list contains 10 bookmarked items """ self._test_setup(11) self._bookmark_units(11) self._navigate_to_bookmarks_list() self._verify_pagination_info( bookmark_count_on_current_page=10, header_text='Showing 1-10 out of 11 total', previous_button_enabled=False, next_button_enabled=True, current_page_number=1, total_pages=2) def test_pagination_with_single_page(self): """ Scenario: Bookmarks list pagination is working as expected for single page Given that I am a registered user And I visit my courseware page And I have bookmarked all the 2 units available Then I click on Bookmarks button And I should see a bookmarked list with 2 bookmarked items And I should see paging header and footer with correct data And previous and next buttons are disabled """ self._test_setup(num_chapters=2) self._bookmark_units(num_units=2) self.bookmarks_page.click_bookmarks_button() self.assertTrue(self.bookmarks_page.results_present()) self._verify_pagination_info(bookmark_count_on_current_page=2, header_text='Showing 1-2 out of 2 total', previous_button_enabled=False, next_button_enabled=False, current_page_number=1, total_pages=1) def test_next_page_button(self): """ Scenario: Next button is working as expected for bookmarks list pagination Given that I am a registered user And I visit my courseware page And I have bookmarked all the 12 units available Then I click on Bookmarks button And I should see a bookmarked list of 10 items And I should see paging header and footer with correct info Then I click on next page button in footer And I should be navigated to second page And I should see a bookmarked list with 2 items And I should see paging header and footer with correct info """ self._test_setup(num_chapters=12) self._bookmark_units(num_units=12) self.bookmarks_page.click_bookmarks_button() self.assertTrue(self.bookmarks_page.results_present()) self._verify_pagination_info( bookmark_count_on_current_page=10, header_text='Showing 1-10 out of 12 total', previous_button_enabled=False, next_button_enabled=True, current_page_number=1, total_pages=2) self.bookmarks_page.press_next_page_button() self._verify_pagination_info( bookmark_count_on_current_page=2, header_text='Showing 11-12 out of 12 total', previous_button_enabled=True, next_button_enabled=False, current_page_number=2, total_pages=2) def test_previous_page_button(self): """ Scenario: Previous button is working as expected for bookmarks list pagination Given that I am a registered user And I visit my courseware page And I have bookmarked all the 12 units available And I click on Bookmarks button Then I click on next page button in footer And I should be navigated to second page And I should see a bookmarked list with 2 items And I should see paging header and footer with correct info Then I click on previous page button And I should be navigated to first page And I should see paging header and footer with correct info """ self._test_setup(num_chapters=12) self._bookmark_units(num_units=12) self.bookmarks_page.click_bookmarks_button() self.assertTrue(self.bookmarks_page.results_present()) self.bookmarks_page.press_next_page_button() self._verify_pagination_info( bookmark_count_on_current_page=2, header_text='Showing 11-12 out of 12 total', previous_button_enabled=True, next_button_enabled=False, current_page_number=2, total_pages=2) self.bookmarks_page.press_previous_page_button() self._verify_pagination_info( bookmark_count_on_current_page=10, header_text='Showing 1-10 out of 12 total', previous_button_enabled=False, next_button_enabled=True, current_page_number=1, total_pages=2) def test_pagination_with_valid_page_number(self): """ Scenario: Bookmarks list pagination works as expected for valid page number Given that I am a registered user And I visit my courseware page And I have bookmarked all the 12 units available Then I click on Bookmarks button And I should see a bookmarked list And I should see total page value is 2 Then I enter 2 in the page number input And I should be navigated to page 2 """ self._test_setup(num_chapters=11) self._bookmark_units(num_units=11) self.bookmarks_page.click_bookmarks_button() self.assertTrue(self.bookmarks_page.results_present()) self.assertEqual(self.bookmarks_page.get_total_pages, 2) self.bookmarks_page.go_to_page(2) self._verify_pagination_info( bookmark_count_on_current_page=1, header_text='Showing 11-11 out of 11 total', previous_button_enabled=True, next_button_enabled=False, current_page_number=2, total_pages=2) def test_pagination_with_invalid_page_number(self): """ Scenario: Bookmarks list pagination works as expected for invalid page number Given that I am a registered user And I visit my courseware page And I have bookmarked all the 11 units available Then I click on Bookmarks button And I should see a bookmarked list And I should see total page value is 2 Then I enter 3 in the page number input And I should stay at page 1 """ self._test_setup(num_chapters=11) self._bookmark_units(num_units=11) self.bookmarks_page.click_bookmarks_button() self.assertTrue(self.bookmarks_page.results_present()) self.assertEqual(self.bookmarks_page.get_total_pages, 2) self.bookmarks_page.go_to_page(3) self._verify_pagination_info( bookmark_count_on_current_page=10, header_text='Showing 1-10 out of 11 total', previous_button_enabled=False, next_button_enabled=True, current_page_number=1, total_pages=2) def test_bookmarked_unit_accessed_event(self): """ Scenario: Bookmark events are emitted with correct data when we access/visit a bookmarked unit. Given that I am a registered user And I visit my courseware page And I have bookmarked a unit When I click on bookmarked unit Then `edx.course.bookmark.accessed` event is emitted """ self._test_setup(num_chapters=1) self.reset_event_tracking() # create expected event data xblocks = self.course_fixture.get_nested_xblocks(category="vertical") event_data = [{ 'event': { 'bookmark_id': '{},{}'.format(self.USERNAME, xblocks[0].locator), 'component_type': xblocks[0].category, 'component_usage_id': xblocks[0].locator, } }] self._bookmark_units(num_units=1) self.bookmarks_page.click_bookmarks_button() self._verify_pagination_info(bookmark_count_on_current_page=1, header_text='Showing 1 out of 1 total', previous_button_enabled=False, next_button_enabled=False, current_page_number=1, total_pages=1) self.bookmarks_page.click_bookmarked_block(0) self.verify_event_data('edx.bookmark.accessed', event_data)
class VisibleToStaffOnlyTest(UniqueCourseTest): """ Tests that content with visible_to_staff_only set to True cannot be viewed by students. """ def setUp(self): super(VisibleToStaffOnlyTest, self).setUp() 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').add_children( XBlockFixtureDesc('sequential', 'Subsection With Locked Unit').add_children( XBlockFixtureDesc('vertical', 'Locked Unit', metadata={'visible_to_staff_only': True}).add_children( XBlockFixtureDesc('html', 'Html Child in locked unit', data="<html>Visible only to staff</html>"), ), XBlockFixtureDesc('vertical', 'Unlocked Unit').add_children( XBlockFixtureDesc('html', 'Html Child in unlocked unit', data="<html>Visible only to all</html>"), ) ), XBlockFixtureDesc('sequential', 'Unlocked Subsection').add_children( XBlockFixtureDesc('vertical', 'Test Unit').add_children( XBlockFixtureDesc('html', 'Html Child in visible unit', data="<html>Visible to all</html>"), ) ), XBlockFixtureDesc('sequential', 'Locked Subsection', metadata={'visible_to_staff_only': True}).add_children( XBlockFixtureDesc('vertical', 'Test Unit').add_children( XBlockFixtureDesc( 'html', 'Html Child in locked subsection', data="<html>Visible only to staff</html>" ) ) ) ) ).install() self.course_home_page = CourseHomePage(self.browser, self.course_id) self.courseware_page = CoursewarePage(self.browser, self.course_id) def test_visible_to_student(self): """ Scenario: Content marked 'visible_to_staff_only' is not visible for students in the course Given some of the course content has been marked 'visible_to_staff_only' And I am logged on with an authorized student account Then I can only see content without 'visible_to_staff_only' set to True """ AutoAuthPage(self.browser, username="******", email="*****@*****.**", course_id=self.course_id, staff=False).visit() self.course_home_page.visit() self.assertEqual(2, len(self.course_home_page.outline.sections['Test Section'])) self.course_home_page.outline.go_to_section("Test Section", "Subsection With Locked Unit") self.courseware_page.wait_for_page() self.assertEqual([u'Unlocked Unit'], self.courseware_page.nav.sequence_items) self.course_home_page.visit() self.course_home_page.outline.go_to_section("Test Section", "Unlocked Subsection") self.courseware_page.wait_for_page() self.assertEqual([u'Test Unit'], self.courseware_page.nav.sequence_items)
class BookmarksTest(BookmarksTestMixin): """ Tests to verify bookmarks functionality. """ def setUp(self): """ Initialize test setup. """ super(BookmarksTest, self).setUp() self.course_outline_page = CourseOutlinePage( self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run'] ) self.courseware_page = CoursewarePage(self.browser, self.course_id) self.bookmarks_page = BookmarksPage(self.browser, self.course_id) self.course_nav = CourseNavPage(self.browser) # Get session to be used for bookmarking units self.session = requests.Session() params = {'username': self.USERNAME, 'email': self.EMAIL, 'course_id': self.course_id} response = self.session.get(BASE_URL + "/auto_auth", params=params) self.assertTrue(response.ok, "Failed to get session") def _test_setup(self, num_chapters=2): """ Setup test settings. Arguments: num_chapters: number of chapters to create in course """ self.create_course_fixture(num_chapters) # Auto-auth register for the course. LmsAutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id).visit() self.courseware_page.visit() def _bookmark_unit(self, location): """ Bookmark a unit Arguments: location (str): unit location """ _headers = { 'Content-type': 'application/json', 'X-CSRFToken': self.session.cookies['csrftoken'], } params = {'course_id': self.course_id} data = json.dumps({'usage_id': location}) response = self.session.post( BASE_URL + '/api/bookmarks/v1/bookmarks/', data=data, params=params, headers=_headers ) self.assertTrue(response.ok, "Failed to bookmark unit") def _bookmark_units(self, num_units): """ Bookmark first `num_units` units Arguments: num_units(int): Number of units to bookmarks """ xblocks = self.course_fixture.get_nested_xblocks(category="vertical") for index in range(num_units): self._bookmark_unit(xblocks[index].locator) def _breadcrumb(self, num_units, modified_name=None): """ Creates breadcrumbs for the first `num_units` Arguments: num_units(int): Number of units for which we want to create breadcrumbs Returns: list of breadcrumbs """ breadcrumbs = [] for index in range(num_units): breadcrumbs.append( [ 'TestSection{}'.format(index), 'TestSubsection{}'.format(index), modified_name if modified_name else 'TestVertical{}'.format(index) ] ) return breadcrumbs def _delete_section(self, index): """ Delete a section at index `index` """ # Logout and login as staff LogoutPage(self.browser).visit() StudioAutoAuthPage( self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id, staff=True ).visit() # Visit course outline page in studio. self.course_outline_page.visit() self.course_outline_page.wait_for_page() self.course_outline_page.section_at(index).delete() # Logout and login as a student. LogoutPage(self.browser).visit() LmsAutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id).visit() # Visit courseware as a student. self.courseware_page.visit() self.courseware_page.wait_for_page() def _toggle_bookmark_and_verify(self, bookmark_icon_state, bookmark_button_state, bookmarked_count): """ Bookmark/Un-Bookmark a unit and then verify """ self.assertTrue(self.courseware_page.bookmark_button_visible) self.courseware_page.click_bookmark_unit_button() self.assertEqual(self.courseware_page.bookmark_icon_visible, bookmark_icon_state) self.assertEqual(self.courseware_page.bookmark_button_state, bookmark_button_state) self.bookmarks_page.click_bookmarks_button() self.assertEqual(self.bookmarks_page.count(), bookmarked_count) def _verify_pagination_info( self, bookmark_count_on_current_page, header_text, previous_button_enabled, next_button_enabled, current_page_number, total_pages ): """ Verify pagination info """ self.assertEqual(self.bookmarks_page.count(), bookmark_count_on_current_page) self.assertEqual(self.bookmarks_page.get_pagination_header_text(), header_text) self.assertEqual(self.bookmarks_page.is_previous_page_button_enabled(), previous_button_enabled) self.assertEqual(self.bookmarks_page.is_next_page_button_enabled(), next_button_enabled) self.assertEqual(self.bookmarks_page.get_current_page_number(), current_page_number) self.assertEqual(self.bookmarks_page.get_total_pages, total_pages) def _navigate_to_bookmarks_list(self): """ Navigates and verifies the bookmarks list page. """ self.bookmarks_page.click_bookmarks_button() self.assertTrue(self.bookmarks_page.results_present()) self.assertEqual(self.bookmarks_page.results_header_text(), 'My Bookmarks') def _verify_breadcrumbs(self, num_units, modified_name=None): """ Verifies the breadcrumb trail. """ bookmarked_breadcrumbs = self.bookmarks_page.breadcrumbs() # Verify bookmarked breadcrumbs. breadcrumbs = self._breadcrumb(num_units=num_units, modified_name=modified_name) breadcrumbs.reverse() self.assertEqual(bookmarked_breadcrumbs, breadcrumbs) def update_and_publish_block_display_name(self, modified_name): """ Update and publish the block/unit display name. """ self.course_outline_page.visit() self.course_outline_page.wait_for_page() self.course_outline_page.expand_all_subsections() section = self.course_outline_page.section_at(0) container_page = section.subsection_at(0).unit_at(0).go_to() self.course_fixture._update_xblock(container_page.locator, { # pylint: disable=protected-access "metadata": { "display_name": modified_name } }) container_page.visit() container_page.wait_for_page() self.assertEqual(container_page.name, modified_name) container_page.publish_action.click() def test_bookmark_button(self): """ Scenario: Bookmark unit button toggles correctly Given that I am a registered user And I visit my courseware page For first 2 units I visit the unit And I can see the Bookmark button When I click on Bookmark button Then unit should be bookmarked Then I click again on the bookmark button And I should see a unit un-bookmarked """ self._test_setup() for index in range(2): self.course_nav.go_to_section('TestSection{}'.format(index), 'TestSubsection{}'.format(index)) self._toggle_bookmark_and_verify(True, 'bookmarked', 1) self.bookmarks_page.click_bookmarks_button(False) self._toggle_bookmark_and_verify(False, '', 0) def test_empty_bookmarks_list(self): """ Scenario: An empty bookmarks list is shown if there are no bookmarked units. Given that I am a registered user And I visit my courseware page And I can see the Bookmarks button When I click on Bookmarks button Then I should see an empty bookmarks list And empty bookmarks list content is correct """ self._test_setup() self.assertTrue(self.bookmarks_page.bookmarks_button_visible()) self.bookmarks_page.click_bookmarks_button() self.assertEqual(self.bookmarks_page.results_header_text(), 'My Bookmarks') self.assertEqual(self.bookmarks_page.empty_header_text(), 'You have not bookmarked any courseware pages yet.') empty_list_text = ("Use bookmarks to help you easily return to courseware pages. To bookmark a page, " "select Bookmark in the upper right corner of that page. To see a list of all your " "bookmarks, select Bookmarks in the upper left corner of any courseware page.") self.assertEqual(self.bookmarks_page.empty_list_text(), empty_list_text) def test_bookmarks_list(self): """ Scenario: A bookmarks list is shown if there are bookmarked units. Given that I am a registered user And I visit my courseware page And I have bookmarked 2 units When I click on Bookmarks button Then I should see a bookmarked list with 2 bookmark links And breadcrumb trail is correct for a bookmark When I click on bookmarked link Then I can navigate to correct bookmarked unit """ self._test_setup() self._bookmark_units(2) self._navigate_to_bookmarks_list() self._verify_breadcrumbs(num_units=2) self._verify_pagination_info( bookmark_count_on_current_page=2, header_text='Showing 1-2 out of 2 total', previous_button_enabled=False, next_button_enabled=False, current_page_number=1, total_pages=1 ) # get usage ids for units xblocks = self.course_fixture.get_nested_xblocks(category="vertical") xblock_usage_ids = [xblock.locator for xblock in xblocks] # Verify link navigation for index in range(2): self.bookmarks_page.click_bookmarked_block(index) self.courseware_page.wait_for_page() self.assertIn(self.courseware_page.active_usage_id(), xblock_usage_ids) self.courseware_page.visit().wait_for_page() self.bookmarks_page.click_bookmarks_button() def test_bookmark_shows_updated_breadcrumb_after_publish(self): """ Scenario: A bookmark breadcrumb trail is updated after publishing the changed display name. Given that I am a registered user And I visit my courseware page And I can see bookmarked unit Then I visit unit page in studio Then I change unit display_name And I publish the changes Then I visit my courseware page And I visit bookmarks list page When I see the bookmark Then I can see the breadcrumb trail with updated display_name. """ self._test_setup(num_chapters=1) self._bookmark_units(num_units=1) self._navigate_to_bookmarks_list() self._verify_breadcrumbs(num_units=1) LogoutPage(self.browser).visit() LmsAutoAuthPage( self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id, staff=True ).visit() modified_name = "Updated name" self.update_and_publish_block_display_name(modified_name) LogoutPage(self.browser).visit() LmsAutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id).visit() self.courseware_page.visit() self._navigate_to_bookmarks_list() self._verify_breadcrumbs(num_units=1, modified_name=modified_name) def test_unreachable_bookmark(self): """ Scenario: We should get a HTTP 404 for an unreachable bookmark. Given that I am a registered user And I visit my courseware page And I have bookmarked 2 units Then I delete a bookmarked unit Then I click on Bookmarks button And I should see a bookmarked list When I click on deleted bookmark Then I should navigated to 404 page """ self._test_setup(num_chapters=1) self._bookmark_units(1) self._delete_section(0) self._navigate_to_bookmarks_list() self._verify_pagination_info( bookmark_count_on_current_page=1, header_text='Showing 1 out of 1 total', previous_button_enabled=False, next_button_enabled=False, current_page_number=1, total_pages=1 ) self.bookmarks_page.click_bookmarked_block(0) self.assertTrue(is_404_page(self.browser)) def test_page_size_limit(self): """ Scenario: We can't get bookmarks more than default page size. Given that I am a registered user And I visit my courseware page And I have bookmarked all the 11 units available Then I click on Bookmarks button And I should see a bookmarked list And bookmark list contains 10 bookmarked items """ self._test_setup(11) self._bookmark_units(11) self._navigate_to_bookmarks_list() self._verify_pagination_info( bookmark_count_on_current_page=10, header_text='Showing 1-10 out of 11 total', previous_button_enabled=False, next_button_enabled=True, current_page_number=1, total_pages=2 ) def test_pagination_with_single_page(self): """ Scenario: Bookmarks list pagination is working as expected for single page Given that I am a registered user And I visit my courseware page And I have bookmarked all the 2 units available Then I click on Bookmarks button And I should see a bookmarked list with 2 bookmarked items And I should see paging header and footer with correct data And previous and next buttons are disabled """ self._test_setup(num_chapters=2) self._bookmark_units(num_units=2) self.bookmarks_page.click_bookmarks_button() self.assertTrue(self.bookmarks_page.results_present()) self._verify_pagination_info( bookmark_count_on_current_page=2, header_text='Showing 1-2 out of 2 total', previous_button_enabled=False, next_button_enabled=False, current_page_number=1, total_pages=1 ) def test_next_page_button(self): """ Scenario: Next button is working as expected for bookmarks list pagination Given that I am a registered user And I visit my courseware page And I have bookmarked all the 12 units available Then I click on Bookmarks button And I should see a bookmarked list of 10 items And I should see paging header and footer with correct info Then I click on next page button in footer And I should be navigated to second page And I should see a bookmarked list with 2 items And I should see paging header and footer with correct info """ self._test_setup(num_chapters=12) self._bookmark_units(num_units=12) self.bookmarks_page.click_bookmarks_button() self.assertTrue(self.bookmarks_page.results_present()) self._verify_pagination_info( bookmark_count_on_current_page=10, header_text='Showing 1-10 out of 12 total', previous_button_enabled=False, next_button_enabled=True, current_page_number=1, total_pages=2 ) self.bookmarks_page.press_next_page_button() self._verify_pagination_info( bookmark_count_on_current_page=2, header_text='Showing 11-12 out of 12 total', previous_button_enabled=True, next_button_enabled=False, current_page_number=2, total_pages=2 ) def test_previous_page_button(self): """ Scenario: Previous button is working as expected for bookmarks list pagination Given that I am a registered user And I visit my courseware page And I have bookmarked all the 12 units available And I click on Bookmarks button Then I click on next page button in footer And I should be navigated to second page And I should see a bookmarked list with 2 items And I should see paging header and footer with correct info Then I click on previous page button And I should be navigated to first page And I should see paging header and footer with correct info """ self._test_setup(num_chapters=12) self._bookmark_units(num_units=12) self.bookmarks_page.click_bookmarks_button() self.assertTrue(self.bookmarks_page.results_present()) self.bookmarks_page.press_next_page_button() self._verify_pagination_info( bookmark_count_on_current_page=2, header_text='Showing 11-12 out of 12 total', previous_button_enabled=True, next_button_enabled=False, current_page_number=2, total_pages=2 ) self.bookmarks_page.press_previous_page_button() self._verify_pagination_info( bookmark_count_on_current_page=10, header_text='Showing 1-10 out of 12 total', previous_button_enabled=False, next_button_enabled=True, current_page_number=1, total_pages=2 ) def test_pagination_with_valid_page_number(self): """ Scenario: Bookmarks list pagination works as expected for valid page number Given that I am a registered user And I visit my courseware page And I have bookmarked all the 12 units available Then I click on Bookmarks button And I should see a bookmarked list And I should see total page value is 2 Then I enter 2 in the page number input And I should be navigated to page 2 """ self._test_setup(num_chapters=11) self._bookmark_units(num_units=11) self.bookmarks_page.click_bookmarks_button() self.assertTrue(self.bookmarks_page.results_present()) self.assertEqual(self.bookmarks_page.get_total_pages, 2) self.bookmarks_page.go_to_page(2) self._verify_pagination_info( bookmark_count_on_current_page=1, header_text='Showing 11-11 out of 11 total', previous_button_enabled=True, next_button_enabled=False, current_page_number=2, total_pages=2 ) def test_pagination_with_invalid_page_number(self): """ Scenario: Bookmarks list pagination works as expected for invalid page number Given that I am a registered user And I visit my courseware page And I have bookmarked all the 11 units available Then I click on Bookmarks button And I should see a bookmarked list And I should see total page value is 2 Then I enter 3 in the page number input And I should stay at page 1 """ self._test_setup(num_chapters=11) self._bookmark_units(num_units=11) self.bookmarks_page.click_bookmarks_button() self.assertTrue(self.bookmarks_page.results_present()) self.assertEqual(self.bookmarks_page.get_total_pages, 2) self.bookmarks_page.go_to_page(3) self._verify_pagination_info( bookmark_count_on_current_page=10, header_text='Showing 1-10 out of 11 total', previous_button_enabled=False, next_button_enabled=True, current_page_number=1, total_pages=2 ) def test_bookmarked_unit_accessed_event(self): """ Scenario: Bookmark events are emitted with correct data when we access/visit a bookmarked unit. Given that I am a registered user And I visit my courseware page And I have bookmarked a unit When I click on bookmarked unit Then `edx.course.bookmark.accessed` event is emitted """ self._test_setup(num_chapters=1) self.reset_event_tracking() # create expected event data xblocks = self.course_fixture.get_nested_xblocks(category="vertical") event_data = [ { 'event': { 'bookmark_id': '{},{}'.format(self.USERNAME, xblocks[0].locator), 'component_type': xblocks[0].category, 'component_usage_id': xblocks[0].locator, } } ] self._bookmark_units(num_units=1) self.bookmarks_page.click_bookmarks_button() self._verify_pagination_info( bookmark_count_on_current_page=1, header_text='Showing 1 out of 1 total', previous_button_enabled=False, next_button_enabled=False, current_page_number=1, total_pages=1 ) self.bookmarks_page.click_bookmarked_block(0) self.verify_event_data('edx.bookmark.accessed', event_data)