class VideoBaseTest(UniqueCourseTest): """ Base class for tests of the Video Player Sets up the course and provides helper functions for the Video tests. """ def setUp(self): """ Initialization of pages and course fixture for video tests """ super(VideoBaseTest, self).setUp() self.longMessage = True self.video = VideoPage(self.browser) self.tab_nav = TabNavPage(self.browser) self.courseware_page = CoursewarePage(self.browser, self.course_id) self.course_info_page = CourseInfoPage(self.browser, self.course_id) self.auth_page = AutoAuthPage(self.browser, course_id=self.course_id) self.course_fixture = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'] ) self.metadata = None self.assets = [] self.contents_of_verticals = None self.youtube_configuration = {} self.user_info = {} # reset youtube stub server self.addCleanup(YouTubeStubConfig.reset) def navigate_to_video(self): """ Prepare the course and get to the video and render it """ self._install_course_fixture() self._navigate_to_courseware_video_and_render() def navigate_to_video_no_render(self): """ Prepare the course and get to the video unit however do not wait for it to render, because the has been an error. """ self._install_course_fixture() self._navigate_to_courseware_video_no_render() def _install_course_fixture(self): """ Install the course fixture that has been defined """ if self.assets: self.course_fixture.add_asset(self.assets) chapter_sequential = XBlockFixtureDesc('sequential', 'Test Section') chapter_sequential.add_children(*self._add_course_verticals()) chapter = XBlockFixtureDesc('chapter', 'Test Chapter').add_children(chapter_sequential) self.course_fixture.add_children(chapter) self.course_fixture.install() if len(self.youtube_configuration) > 0: YouTubeStubConfig.configure(self.youtube_configuration) def _add_course_verticals(self): """ Create XBlockFixtureDesc verticals :return: a list of XBlockFixtureDesc """ xblock_verticals = [] _contents_of_verticals = self.contents_of_verticals # Video tests require at least one vertical with a single video. if not _contents_of_verticals: _contents_of_verticals = [[{'display_name': 'Video', 'metadata': self.metadata}]] for vertical_index, vertical in enumerate(_contents_of_verticals): xblock_verticals.append(self._create_single_vertical(vertical, vertical_index)) return xblock_verticals def _create_single_vertical(self, vertical_contents, vertical_index): """ Create a single course vertical of type XBlockFixtureDesc with category `vertical`. A single course vertical can contain single or multiple video modules. :param vertical_contents: a list of items for the vertical to contain :param vertical_index: index for the vertical display name :return: XBlockFixtureDesc """ xblock_course_vertical = XBlockFixtureDesc('vertical', u'Test Vertical-{0}'.format(vertical_index)) for video in vertical_contents: xblock_course_vertical.add_children( XBlockFixtureDesc('video', video['display_name'], metadata=video.get('metadata'))) return xblock_course_vertical def _navigate_to_courseware_video(self): """ Register for the course and navigate to the video unit """ self.auth_page.visit() self.user_info = self.auth_page.user_info self.courseware_page.visit() def _navigate_to_courseware_video_and_render(self): """ Wait for the video player to render """ self._navigate_to_courseware_video() self.video.wait_for_video_player_render() def _navigate_to_courseware_video_no_render(self): """ Wait for the video Xmodule but not for rendering """ self._navigate_to_courseware_video() self.video.wait_for_video_class() def metadata_for_mode(self, player_mode, additional_data=None): """ Create a dictionary for video player configuration according to `player_mode` :param player_mode (str): Video player mode :param additional_data (dict): Optional additional metadata. :return: dict """ metadata = {} youtube_ids = { 'youtube_id_1_0': '', 'youtube_id_0_75': '', 'youtube_id_1_25': '', 'youtube_id_1_5': '', } if player_mode == 'html5': metadata.update(youtube_ids) metadata.update({ 'html5_sources': HTML5_SOURCES }) if player_mode == 'youtube_html5': metadata.update({ 'html5_sources': HTML5_SOURCES, }) if player_mode == 'youtube_html5_unsupported_video': metadata.update({ 'html5_sources': HTML5_SOURCES_INCORRECT }) if player_mode == 'html5_unsupported_video': metadata.update(youtube_ids) metadata.update({ 'html5_sources': HTML5_SOURCES_INCORRECT }) if player_mode == 'hls': metadata.update(youtube_ids) metadata.update({ 'html5_sources': HLS_SOURCES, }) if player_mode == 'html5_and_hls': metadata.update(youtube_ids) metadata.update({ 'html5_sources': HTML5_SOURCES + HLS_SOURCES, }) if additional_data: metadata.update(additional_data) return metadata def go_to_sequential_position(self, position): """ Navigate to sequential specified by `video_display_name` """ self.courseware_page.go_to_sequential_position(position) self.video.wait_for_video_player_render()
class VideoBaseTest(UniqueCourseTest): """ Base class for tests of the Video Player Sets up the course and provides helper functions for the Video tests. """ def setUp(self): """ Initialization of pages and course fixture for video tests """ super(VideoBaseTest, self).setUp() self.longMessage = True self.video = VideoPage(self.browser) self.tab_nav = TabNavPage(self.browser) self.courseware_page = CoursewarePage(self.browser, self.course_id) self.course_info_page = CourseInfoPage(self.browser, self.course_id) self.auth_page = AutoAuthPage(self.browser, course_id=self.course_id) self.course_fixture = CourseFixture(self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name']) self.metadata = None self.assets = [] self.contents_of_verticals = None self.youtube_configuration = {} self.user_info = {} # reset youtube stub server self.addCleanup(YouTubeStubConfig.reset) def navigate_to_video(self): """ Prepare the course and get to the video and render it """ self._install_course_fixture() self._navigate_to_courseware_video_and_render() def navigate_to_video_no_render(self): """ Prepare the course and get to the video unit however do not wait for it to render, because the has been an error. """ self._install_course_fixture() self._navigate_to_courseware_video_no_render() def _install_course_fixture(self): """ Install the course fixture that has been defined """ if self.assets: self.course_fixture.add_asset(self.assets) chapter_sequential = XBlockFixtureDesc('sequential', 'Test Section') chapter_sequential.add_children(*self._add_course_verticals()) chapter = XBlockFixtureDesc( 'chapter', 'Test Chapter').add_children(chapter_sequential) self.course_fixture.add_children(chapter) self.course_fixture.install() if len(self.youtube_configuration) > 0: YouTubeStubConfig.configure(self.youtube_configuration) def _add_course_verticals(self): """ Create XBlockFixtureDesc verticals :return: a list of XBlockFixtureDesc """ xblock_verticals = [] _contents_of_verticals = self.contents_of_verticals # Video tests require at least one vertical with a single video. if not _contents_of_verticals: _contents_of_verticals = [[{ 'display_name': 'Video', 'metadata': self.metadata }]] for vertical_index, vertical in enumerate(_contents_of_verticals): xblock_verticals.append( self._create_single_vertical(vertical, vertical_index)) return xblock_verticals def _create_single_vertical(self, vertical_contents, vertical_index): """ Create a single course vertical of type XBlockFixtureDesc with category `vertical`. A single course vertical can contain single or multiple video modules. :param vertical_contents: a list of items for the vertical to contain :param vertical_index: index for the vertical display name :return: XBlockFixtureDesc """ xblock_course_vertical = XBlockFixtureDesc( 'vertical', u'Test Vertical-{0}'.format(vertical_index)) for video in vertical_contents: xblock_course_vertical.add_children( XBlockFixtureDesc('video', video['display_name'], metadata=video.get('metadata'))) return xblock_course_vertical def _navigate_to_courseware_video(self): """ Register for the course and navigate to the video unit """ self.auth_page.visit() self.user_info = self.auth_page.user_info self.courseware_page.visit() def _navigate_to_courseware_video_and_render(self): """ Wait for the video player to render """ self._navigate_to_courseware_video() self.video.wait_for_video_player_render() def _navigate_to_courseware_video_no_render(self): """ Wait for the video Xmodule but not for rendering """ self._navigate_to_courseware_video() self.video.wait_for_video_class() def metadata_for_mode(self, player_mode, additional_data=None): """ Create a dictionary for video player configuration according to `player_mode` :param player_mode (str): Video player mode :param additional_data (dict): Optional additional metadata. :return: dict """ metadata = {} youtube_ids = { 'youtube_id_1_0': '', 'youtube_id_0_75': '', 'youtube_id_1_25': '', 'youtube_id_1_5': '', } if player_mode == 'html5': metadata.update(youtube_ids) metadata.update({'html5_sources': HTML5_SOURCES}) if player_mode == 'youtube_html5': metadata.update({ 'html5_sources': HTML5_SOURCES, }) if player_mode == 'youtube_html5_unsupported_video': metadata.update({'html5_sources': HTML5_SOURCES_INCORRECT}) if player_mode == 'html5_unsupported_video': metadata.update(youtube_ids) metadata.update({'html5_sources': HTML5_SOURCES_INCORRECT}) if player_mode == 'hls': metadata.update(youtube_ids) metadata.update({ 'html5_sources': HLS_SOURCES, }) if player_mode == 'html5_and_hls': metadata.update(youtube_ids) metadata.update({ 'html5_sources': HTML5_SOURCES + HLS_SOURCES, }) if additional_data: metadata.update(additional_data) return metadata def go_to_sequential_position(self, position): """ Navigate to sequential specified by `video_display_name` """ self.courseware_page.go_to_sequential_position(position) self.video.wait_for_video_player_render()
class ProblemStateOnNavigationTest(UniqueCourseTest): """ Test courseware with problems in multiple verticals """ USERNAME = "******" EMAIL = "*****@*****.**" problem1_name = 'MULTIPLE CHOICE TEST PROBLEM 1' problem2_name = 'MULTIPLE CHOICE TEST PROBLEM 2' def setUp(self): super(ProblemStateOnNavigationTest, self).setUp() self.courseware_page = CoursewarePage(self.browser, self.course_id) # Install a course with section, tabs and multiple choice problems. 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,1').add_children( self.create_multiple_choice_problem( self.problem1_name), self.create_multiple_choice_problem( self.problem2_name), ), ), ).install() # Auto-auth register for the course. AutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id, staff=False).visit() self.courseware_page.visit() self.problem_page = ProblemPage(self.browser) def create_multiple_choice_problem(self, problem_name): """ Return the Multiple Choice Problem Descriptor, given the name of the problem. """ factory = MultipleChoiceResponseXMLFactory() xml_data = factory.build_xml( question_text='The correct answer is Choice 2', choices=[False, False, True, False], choice_names=['choice_0', 'choice_1', 'choice_2', 'choice_3']) return XBlockFixtureDesc('problem', problem_name, data=xml_data, metadata={'rerandomize': 'always'}) def go_to_tab_and_assert_problem(self, position, problem_name): """ Go to sequential tab and assert that we are on problem whose name is given as a parameter. Args: position: Position of the sequential tab problem_name: Name of the problem """ self.courseware_page.go_to_sequential_position(position) self.problem_page.wait_for_element_presence( self.problem_page.CSS_PROBLEM_HEADER, 'wait for problem header') self.assertEqual(self.problem_page.problem_name, problem_name) def test_perform_problem_check_and_navigate(self): """ Scenario: I go to sequential position 1 Facing problem1, I select 'choice_1' Then I click check button Then I go to sequential position 2 Then I came back to sequential position 1 again Facing problem1, I observe the problem1 content is not outdated before and after sequence navigation """ # Go to sequential position 1 and assert that we are on problem 1. self.go_to_tab_and_assert_problem(1, self.problem1_name) # Update problem 1's content state by clicking check button. self.problem_page.click_choice('choice_choice_1') self.problem_page.click_check() self.problem_page.wait_for_expected_status( 'label.choicegroup_incorrect', 'incorrect') # Save problem 1's content state as we're about to switch units in the sequence. problem1_content_before_switch = self.problem_page.problem_content # Go to sequential position 2 and assert that we are on problem 2. self.go_to_tab_and_assert_problem(2, self.problem2_name) # Come back to our original unit in the sequence and assert that the content hasn't changed. self.go_to_tab_and_assert_problem(1, self.problem1_name) problem1_content_after_coming_back = self.problem_page.problem_content self.assertEqual(problem1_content_before_switch, problem1_content_after_coming_back) def test_perform_problem_save_and_navigate(self): """ Scenario: I go to sequential position 1 Facing problem1, I select 'choice_1' Then I click save button Then I go to sequential position 2 Then I came back to sequential position 1 again Facing problem1, I observe the problem1 content is not outdated before and after sequence navigation """ # Go to sequential position 1 and assert that we are on problem 1. self.go_to_tab_and_assert_problem(1, self.problem1_name) # Update problem 1's content state by clicking save button. self.problem_page.click_choice('choice_choice_1') self.problem_page.click_save() self.problem_page.wait_for_expected_status('div.capa_alert', 'saved') # Save problem 1's content state as we're about to switch units in the sequence. problem1_content_before_switch = self.problem_page.problem_content # Go to sequential position 2 and assert that we are on problem 2. self.go_to_tab_and_assert_problem(2, self.problem2_name) # Come back to our original unit in the sequence and assert that the content hasn't changed. self.go_to_tab_and_assert_problem(1, self.problem1_name) problem1_content_after_coming_back = self.problem_page.problem_content self.assertIn(problem1_content_after_coming_back, problem1_content_before_switch) def test_perform_problem_reset_and_navigate(self): """ Scenario: I go to sequential position 1 Facing problem1, I select 'choice_1' Then perform the action – check and reset Then I go to sequential position 2 Then I came back to sequential position 1 again Facing problem1, I observe the problem1 content is not outdated before and after sequence navigation """ # Go to sequential position 1 and assert that we are on problem 1. self.go_to_tab_and_assert_problem(1, self.problem1_name) # Update problem 1's content state – by performing reset operation. self.problem_page.click_choice('choice_choice_1') self.problem_page.click_check() self.problem_page.wait_for_expected_status( 'label.choicegroup_incorrect', 'incorrect') self.problem_page.click_reset() self.problem_page.wait_for_expected_status('span.unanswered', 'unanswered') # Save problem 1's content state as we're about to switch units in the sequence. problem1_content_before_switch = self.problem_page.problem_content # Go to sequential position 2 and assert that we are on problem 2. self.go_to_tab_and_assert_problem(2, self.problem2_name) # Come back to our original unit in the sequence and assert that the content hasn't changed. self.go_to_tab_and_assert_problem(1, self.problem1_name) problem1_content_after_coming_back = self.problem_page.problem_content self.assertEqual(problem1_content_before_switch, problem1_content_after_coming_back)
class CoursewareMultipleVerticalsTest(UniqueCourseTest, EventsTestMixin): """ Test courseware with multiple verticals """ USERNAME = "******" EMAIL = "*****@*****.**" def setUp(self): super(CoursewareMultipleVerticalsTest, 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,1').add_children( XBlockFixtureDesc( 'problem', 'Test Problem 1', data='<problem>problem 1 dummy body</problem>'), XBlockFixtureDesc( 'html', 'html 1', data="<html>html 1 dummy body</html>"), XBlockFixtureDesc( 'problem', 'Test Problem 2', data="<problem>problem 2 dummy body</problem>"), XBlockFixtureDesc( 'html', 'html 2', data="<html>html 2 dummy body</html>"), ), XBlockFixtureDesc( 'sequential', 'Test Subsection 1,2').add_children( XBlockFixtureDesc( 'problem', 'Test Problem 3', data='<problem>problem 3 dummy body</problem>'), ), XBlockFixtureDesc( 'sequential', 'Test HIDDEN Subsection', metadata={ 'visible_to_staff_only': True }).add_children( XBlockFixtureDesc( 'problem', 'Test HIDDEN Problem', data='<problem>hidden problem</problem>'), ), ), XBlockFixtureDesc('chapter', 'Test Section 2').add_children( XBlockFixtureDesc( 'sequential', 'Test Subsection 2,1').add_children( XBlockFixtureDesc( 'problem', 'Test Problem 4', data='<problem>problem 4 dummy body</problem>'), ), ), XBlockFixtureDesc( 'chapter', 'Test HIDDEN Section', metadata={ 'visible_to_staff_only': True }).add_children( XBlockFixtureDesc('sequential', 'Test HIDDEN Subsection'), ), ).install() # Auto-auth register for the course. AutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id, staff=False).visit() self.courseware_page.visit() self.course_nav = CourseNavPage(self.browser) def test_navigation_buttons(self): # start in first section self.assert_navigation_state('Test Section 1', 'Test Subsection 1,1', 0, next_enabled=True, prev_enabled=False) # next takes us to next tab in sequential self.courseware_page.click_next_button_on_top() self.assert_navigation_state('Test Section 1', 'Test Subsection 1,1', 1, next_enabled=True, prev_enabled=True) # go to last sequential position self.courseware_page.go_to_sequential_position(4) self.assert_navigation_state('Test Section 1', 'Test Subsection 1,1', 3, next_enabled=True, prev_enabled=True) # next takes us to next sequential self.courseware_page.click_next_button_on_bottom() self.assert_navigation_state('Test Section 1', 'Test Subsection 1,2', 0, next_enabled=True, prev_enabled=True) # next takes us to next chapter self.courseware_page.click_next_button_on_top() self.assert_navigation_state('Test Section 2', 'Test Subsection 2,1', 0, next_enabled=False, prev_enabled=True) # previous takes us to previous chapter self.courseware_page.click_previous_button_on_top() self.assert_navigation_state('Test Section 1', 'Test Subsection 1,2', 0, next_enabled=True, prev_enabled=True) # previous takes us to last tab in previous sequential self.courseware_page.click_previous_button_on_bottom() self.assert_navigation_state('Test Section 1', 'Test Subsection 1,1', 3, next_enabled=True, prev_enabled=True) # previous takes us to previous tab in sequential self.courseware_page.click_previous_button_on_bottom() self.assert_navigation_state('Test Section 1', 'Test Subsection 1,1', 2, next_enabled=True, prev_enabled=True) # test UI events emitted by navigation filter_sequence_ui_event = lambda event: event.get( 'name', '').startswith('edx.ui.lms.sequence.') sequence_ui_events = self.wait_for_events( event_filter=filter_sequence_ui_event, timeout=2) legacy_events = [ ev for ev in sequence_ui_events if ev['event_type'] in {'seq_next', 'seq_prev', 'seq_goto'} ] nonlegacy_events = [ ev for ev in sequence_ui_events if ev not in legacy_events ] self.assertTrue( all('old' in json.loads(ev['event']) for ev in legacy_events)) self.assertTrue( all('new' in json.loads(ev['event']) for ev in legacy_events)) self.assertFalse( any('old' in json.loads(ev['event']) for ev in nonlegacy_events)) self.assertFalse( any('new' in json.loads(ev['event']) for ev in nonlegacy_events)) self.assert_events_match([ { 'event_type': 'seq_next', 'event': { 'old': 1, 'new': 2, 'current_tab': 1, 'tab_count': 4, 'widget_placement': 'top', } }, { 'event_type': 'seq_goto', 'event': { 'old': 2, 'new': 4, 'current_tab': 2, 'target_tab': 4, 'tab_count': 4, 'widget_placement': 'top', } }, { 'event_type': 'edx.ui.lms.sequence.next_selected', 'event': { 'current_tab': 4, 'tab_count': 4, 'widget_placement': 'bottom', } }, { 'event_type': 'edx.ui.lms.sequence.next_selected', 'event': { 'current_tab': 1, 'tab_count': 1, 'widget_placement': 'top', } }, { 'event_type': 'edx.ui.lms.sequence.previous_selected', 'event': { 'current_tab': 1, 'tab_count': 1, 'widget_placement': 'top', } }, { 'event_type': 'edx.ui.lms.sequence.previous_selected', 'event': { 'current_tab': 1, 'tab_count': 1, 'widget_placement': 'bottom', } }, { 'event_type': 'seq_prev', 'event': { 'old': 4, 'new': 3, 'current_tab': 4, 'tab_count': 4, 'widget_placement': 'bottom', } }, ], sequence_ui_events) def test_outline_selected_events(self): self.course_nav.go_to_section('Test Section 1', 'Test Subsection 1,2') self.course_nav.go_to_section('Test Section 2', 'Test Subsection 2,1') # test UI events emitted by navigating via the course outline filter_selected_events = lambda event: event.get( 'name', '') == 'edx.ui.lms.outline.selected' selected_events = self.wait_for_events( event_filter=filter_selected_events, timeout=2) # note: target_url is tested in unit tests, as the url changes here with every test (it includes GUIDs). self.assert_events_match([ { 'event_type': 'edx.ui.lms.outline.selected', 'name': 'edx.ui.lms.outline.selected', 'event': { 'target_name': 'Test Subsection 1,2 ', 'widget_placement': 'accordion', } }, { 'event_type': 'edx.ui.lms.outline.selected', 'name': 'edx.ui.lms.outline.selected', 'event': { 'target_name': 'Test Subsection 2,1 ', 'widget_placement': 'accordion', } }, ], selected_events) def test_link_clicked_events(self): """ Given that I am a user in the courseware When I navigate via the left-hand nav Then a link clicked event is logged """ self.course_nav.go_to_section('Test Section 1', 'Test Subsection 1,2') self.course_nav.go_to_section('Test Section 2', 'Test Subsection 2,1') filter_link_clicked = lambda event: event.get( 'name', '') == 'edx.ui.lms.link_clicked' link_clicked_events = self.wait_for_events( event_filter=filter_link_clicked, timeout=2) self.assertEqual(len(link_clicked_events), 2) def assert_navigation_state(self, section_title, subsection_title, subsection_position, next_enabled, prev_enabled): """ Verifies that the navigation state is as expected. """ self.assertTrue( self.course_nav.is_on_section(section_title, subsection_title)) self.assertEquals(self.courseware_page.sequential_position, subsection_position) self.assertEquals(self.courseware_page.is_next_button_enabled, next_enabled) self.assertEquals(self.courseware_page.is_previous_button_enabled, prev_enabled) def test_tab_position(self): # test that using the position in the url direct to correct tab in courseware self.course_nav.go_to_section('Test Section 1', 'Test Subsection 1,1') subsection_url = self.course_nav.active_subsection_url url_part_list = subsection_url.split('/') self.assertEqual(len(url_part_list), 9) course_id = url_part_list[4] chapter_id = url_part_list[-3] subsection_id = url_part_list[-2] problem1_page = CoursewareSequentialTabPage(self.browser, course_id=course_id, chapter=chapter_id, subsection=subsection_id, position=1).visit() self.assertIn('problem 1 dummy body', problem1_page.get_selected_tab_content()) html1_page = CoursewareSequentialTabPage(self.browser, course_id=course_id, chapter=chapter_id, subsection=subsection_id, position=2).visit() self.assertIn('html 1 dummy body', html1_page.get_selected_tab_content()) problem2_page = CoursewareSequentialTabPage(self.browser, course_id=course_id, chapter=chapter_id, subsection=subsection_id, position=3).visit() self.assertIn('problem 2 dummy body', problem2_page.get_selected_tab_content()) html2_page = CoursewareSequentialTabPage(self.browser, course_id=course_id, chapter=chapter_id, subsection=subsection_id, position=4).visit() self.assertIn('html 2 dummy body', html2_page.get_selected_tab_content()) @attr('a11y') def test_courseware_a11y(self): """ Run accessibility audit for the problem type. """ self.course_nav.go_to_section('Test Section 1', 'Test Subsection 1,1') # Set the scope to the sequence navigation self.courseware_page.a11y_audit.config.set_scope( include=['div.sequence-nav']) self.courseware_page.a11y_audit.check_for_accessibility_errors()
class ProblemStateOnNavigationTest(UniqueCourseTest): """ Test courseware with problems in multiple verticals """ USERNAME = "******" EMAIL = "*****@*****.**" problem1_name = 'MULTIPLE CHOICE TEST PROBLEM 1' problem2_name = 'MULTIPLE CHOICE TEST PROBLEM 2' def setUp(self): super(ProblemStateOnNavigationTest, self).setUp() self.courseware_page = CoursewarePage(self.browser, self.course_id) # Install a course with section, tabs and multiple choice problems. 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,1').add_children( self.create_multiple_choice_problem(self.problem1_name), self.create_multiple_choice_problem(self.problem2_name), ), ), ).install() # Auto-auth register for the course. AutoAuthPage( self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id, staff=False ).visit() self.courseware_page.visit() self.problem_page = ProblemPage(self.browser) def create_multiple_choice_problem(self, problem_name): """ Return the Multiple Choice Problem Descriptor, given the name of the problem. """ factory = MultipleChoiceResponseXMLFactory() xml_data = factory.build_xml( question_text='The correct answer is Choice 2', choices=[False, False, True, False], choice_names=['choice_0', 'choice_1', 'choice_2', 'choice_3'] ) return XBlockFixtureDesc( 'problem', problem_name, data=xml_data, metadata={'rerandomize': 'always'} ) def go_to_tab_and_assert_problem(self, position, problem_name): """ Go to sequential tab and assert that we are on problem whose name is given as a parameter. Args: position: Position of the sequential tab problem_name: Name of the problem """ self.courseware_page.go_to_sequential_position(position) self.problem_page.wait_for_element_presence( self.problem_page.CSS_PROBLEM_HEADER, 'wait for problem header' ) self.assertEqual(self.problem_page.problem_name, problem_name) def test_perform_problem_check_and_navigate(self): """ Scenario: I go to sequential position 1 Facing problem1, I select 'choice_1' Then I click check button Then I go to sequential position 2 Then I came back to sequential position 1 again Facing problem1, I observe the problem1 content is not outdated before and after sequence navigation """ # Go to sequential position 1 and assert that we are on problem 1. self.go_to_tab_and_assert_problem(1, self.problem1_name) # Update problem 1's content state by clicking check button. self.problem_page.click_choice('choice_choice_1') self.problem_page.click_check() self.problem_page.wait_for_expected_status('label.choicegroup_incorrect', 'incorrect') # Save problem 1's content state as we're about to switch units in the sequence. problem1_content_before_switch = self.problem_page.problem_content # Go to sequential position 2 and assert that we are on problem 2. self.go_to_tab_and_assert_problem(2, self.problem2_name) # Come back to our original unit in the sequence and assert that the content hasn't changed. self.go_to_tab_and_assert_problem(1, self.problem1_name) problem1_content_after_coming_back = self.problem_page.problem_content self.assertEqual(problem1_content_before_switch, problem1_content_after_coming_back) def test_perform_problem_save_and_navigate(self): """ Scenario: I go to sequential position 1 Facing problem1, I select 'choice_1' Then I click save button Then I go to sequential position 2 Then I came back to sequential position 1 again Facing problem1, I observe the problem1 content is not outdated before and after sequence navigation """ # Go to sequential position 1 and assert that we are on problem 1. self.go_to_tab_and_assert_problem(1, self.problem1_name) # Update problem 1's content state by clicking save button. self.problem_page.click_choice('choice_choice_1') self.problem_page.click_save() self.problem_page.wait_for_expected_status('div.capa_alert', 'saved') # Save problem 1's content state as we're about to switch units in the sequence. problem1_content_before_switch = self.problem_page.problem_content # Go to sequential position 2 and assert that we are on problem 2. self.go_to_tab_and_assert_problem(2, self.problem2_name) # Come back to our original unit in the sequence and assert that the content hasn't changed. self.go_to_tab_and_assert_problem(1, self.problem1_name) problem1_content_after_coming_back = self.problem_page.problem_content self.assertIn(problem1_content_after_coming_back, problem1_content_before_switch) def test_perform_problem_reset_and_navigate(self): """ Scenario: I go to sequential position 1 Facing problem1, I select 'choice_1' Then perform the action – check and reset Then I go to sequential position 2 Then I came back to sequential position 1 again Facing problem1, I observe the problem1 content is not outdated before and after sequence navigation """ # Go to sequential position 1 and assert that we are on problem 1. self.go_to_tab_and_assert_problem(1, self.problem1_name) # Update problem 1's content state – by performing reset operation. self.problem_page.click_choice('choice_choice_1') self.problem_page.click_check() self.problem_page.wait_for_expected_status('label.choicegroup_incorrect', 'incorrect') self.problem_page.click_reset() self.problem_page.wait_for_expected_status('span.unanswered', 'unanswered') # Save problem 1's content state as we're about to switch units in the sequence. problem1_content_before_switch = self.problem_page.problem_content # Go to sequential position 2 and assert that we are on problem 2. self.go_to_tab_and_assert_problem(2, self.problem2_name) # Come back to our original unit in the sequence and assert that the content hasn't changed. self.go_to_tab_and_assert_problem(1, self.problem1_name) problem1_content_after_coming_back = self.problem_page.problem_content self.assertEqual(problem1_content_before_switch, problem1_content_after_coming_back)
class CoursewareMultipleVerticalsTest(UniqueCourseTest, EventsTestMixin): """ Test courseware with multiple verticals """ USERNAME = "******" EMAIL = "*****@*****.**" def setUp(self): super(CoursewareMultipleVerticalsTest, 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,1').add_children( XBlockFixtureDesc('problem', 'Test Problem 1', data='<problem>problem 1 dummy body</problem>'), XBlockFixtureDesc('html', 'html 1', data="<html>html 1 dummy body</html>"), XBlockFixtureDesc('problem', 'Test Problem 2', data="<problem>problem 2 dummy body</problem>"), XBlockFixtureDesc('html', 'html 2', data="<html>html 2 dummy body</html>"), ), XBlockFixtureDesc('sequential', 'Test Subsection 1,2').add_children( XBlockFixtureDesc('problem', 'Test Problem 3', data='<problem>problem 3 dummy body</problem>'), ), XBlockFixtureDesc( 'sequential', 'Test HIDDEN Subsection', metadata={'visible_to_staff_only': True} ).add_children( XBlockFixtureDesc('problem', 'Test HIDDEN Problem', data='<problem>hidden problem</problem>'), ), ), XBlockFixtureDesc('chapter', 'Test Section 2').add_children( XBlockFixtureDesc('sequential', 'Test Subsection 2,1').add_children( XBlockFixtureDesc('problem', 'Test Problem 4', data='<problem>problem 4 dummy body</problem>'), ), ), XBlockFixtureDesc('chapter', 'Test HIDDEN Section', metadata={'visible_to_staff_only': True}).add_children( XBlockFixtureDesc('sequential', 'Test HIDDEN Subsection'), ), ).install() # Auto-auth register for the course. AutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id, staff=False).visit() self.courseware_page.visit() self.course_nav = CourseNavPage(self.browser) def test_navigation_buttons(self): # start in first section self.assert_navigation_state('Test Section 1', 'Test Subsection 1,1', 0, next_enabled=True, prev_enabled=False) # next takes us to next tab in sequential self.courseware_page.click_next_button_on_top() self.assert_navigation_state('Test Section 1', 'Test Subsection 1,1', 1, next_enabled=True, prev_enabled=True) # go to last sequential position self.courseware_page.go_to_sequential_position(4) self.assert_navigation_state('Test Section 1', 'Test Subsection 1,1', 3, next_enabled=True, prev_enabled=True) # next takes us to next sequential self.courseware_page.click_next_button_on_bottom() self.assert_navigation_state('Test Section 1', 'Test Subsection 1,2', 0, next_enabled=True, prev_enabled=True) # next takes us to next chapter self.courseware_page.click_next_button_on_top() self.assert_navigation_state('Test Section 2', 'Test Subsection 2,1', 0, next_enabled=False, prev_enabled=True) # previous takes us to previous chapter self.courseware_page.click_previous_button_on_top() self.assert_navigation_state('Test Section 1', 'Test Subsection 1,2', 0, next_enabled=True, prev_enabled=True) # previous takes us to last tab in previous sequential self.courseware_page.click_previous_button_on_bottom() self.assert_navigation_state('Test Section 1', 'Test Subsection 1,1', 3, next_enabled=True, prev_enabled=True) # previous takes us to previous tab in sequential self.courseware_page.click_previous_button_on_bottom() self.assert_navigation_state('Test Section 1', 'Test Subsection 1,1', 2, next_enabled=True, prev_enabled=True) # test UI events emitted by navigation filter_sequence_ui_event = lambda event: event.get('name', '').startswith('edx.ui.lms.sequence.') sequence_ui_events = self.wait_for_events(event_filter=filter_sequence_ui_event, timeout=2) legacy_events = [ev for ev in sequence_ui_events if ev['event_type'] in {'seq_next', 'seq_prev', 'seq_goto'}] nonlegacy_events = [ev for ev in sequence_ui_events if ev not in legacy_events] self.assertTrue(all('old' in json.loads(ev['event']) for ev in legacy_events)) self.assertTrue(all('new' in json.loads(ev['event']) for ev in legacy_events)) self.assertFalse(any('old' in json.loads(ev['event']) for ev in nonlegacy_events)) self.assertFalse(any('new' in json.loads(ev['event']) for ev in nonlegacy_events)) self.assert_events_match( [ { 'event_type': 'seq_next', 'event': { 'old': 1, 'new': 2, 'current_tab': 1, 'tab_count': 4, 'widget_placement': 'top', } }, { 'event_type': 'seq_goto', 'event': { 'old': 2, 'new': 4, 'current_tab': 2, 'target_tab': 4, 'tab_count': 4, 'widget_placement': 'top', } }, { 'event_type': 'edx.ui.lms.sequence.next_selected', 'event': { 'current_tab': 4, 'tab_count': 4, 'widget_placement': 'bottom', } }, { 'event_type': 'edx.ui.lms.sequence.next_selected', 'event': { 'current_tab': 1, 'tab_count': 1, 'widget_placement': 'top', } }, { 'event_type': 'edx.ui.lms.sequence.previous_selected', 'event': { 'current_tab': 1, 'tab_count': 1, 'widget_placement': 'top', } }, { 'event_type': 'edx.ui.lms.sequence.previous_selected', 'event': { 'current_tab': 1, 'tab_count': 1, 'widget_placement': 'bottom', } }, { 'event_type': 'seq_prev', 'event': { 'old': 4, 'new': 3, 'current_tab': 4, 'tab_count': 4, 'widget_placement': 'bottom', } }, ], sequence_ui_events ) def test_outline_selected_events(self): self.course_nav.go_to_section('Test Section 1', 'Test Subsection 1,2') self.course_nav.go_to_section('Test Section 2', 'Test Subsection 2,1') # test UI events emitted by navigating via the course outline filter_selected_events = lambda event: event.get('name', '') == 'edx.ui.lms.outline.selected' selected_events = self.wait_for_events(event_filter=filter_selected_events, timeout=2) # note: target_url is tested in unit tests, as the url changes here with every test (it includes GUIDs). self.assert_events_match( [ { 'event_type': 'edx.ui.lms.outline.selected', 'name': 'edx.ui.lms.outline.selected', 'event': { 'target_name': 'Test Subsection 1,2 ', 'widget_placement': 'accordion', } }, { 'event_type': 'edx.ui.lms.outline.selected', 'name': 'edx.ui.lms.outline.selected', 'event': { 'target_name': 'Test Subsection 2,1 ', 'widget_placement': 'accordion', } }, ], selected_events ) def test_link_clicked_events(self): """ Given that I am a user in the courseware When I navigate via the left-hand nav Then a link clicked event is logged """ self.course_nav.go_to_section('Test Section 1', 'Test Subsection 1,2') self.course_nav.go_to_section('Test Section 2', 'Test Subsection 2,1') filter_link_clicked = lambda event: event.get('name', '') == 'edx.ui.lms.link_clicked' link_clicked_events = self.wait_for_events(event_filter=filter_link_clicked, timeout=2) self.assertEqual(len(link_clicked_events), 2) def assert_navigation_state( self, section_title, subsection_title, subsection_position, next_enabled, prev_enabled ): """ Verifies that the navigation state is as expected. """ self.assertTrue(self.course_nav.is_on_section(section_title, subsection_title)) self.assertEquals(self.courseware_page.sequential_position, subsection_position) self.assertEquals(self.courseware_page.is_next_button_enabled, next_enabled) self.assertEquals(self.courseware_page.is_previous_button_enabled, prev_enabled) def test_tab_position(self): # test that using the position in the url direct to correct tab in courseware self.course_nav.go_to_section('Test Section 1', 'Test Subsection 1,1') subsection_url = self.course_nav.active_subsection_url url_part_list = subsection_url.split('/') self.assertEqual(len(url_part_list), 9) course_id = url_part_list[4] chapter_id = url_part_list[-3] subsection_id = url_part_list[-2] problem1_page = CoursewareSequentialTabPage( self.browser, course_id=course_id, chapter=chapter_id, subsection=subsection_id, position=1 ).visit() self.assertIn('problem 1 dummy body', problem1_page.get_selected_tab_content()) html1_page = CoursewareSequentialTabPage( self.browser, course_id=course_id, chapter=chapter_id, subsection=subsection_id, position=2 ).visit() self.assertIn('html 1 dummy body', html1_page.get_selected_tab_content()) problem2_page = CoursewareSequentialTabPage( self.browser, course_id=course_id, chapter=chapter_id, subsection=subsection_id, position=3 ).visit() self.assertIn('problem 2 dummy body', problem2_page.get_selected_tab_content()) html2_page = CoursewareSequentialTabPage( self.browser, course_id=course_id, chapter=chapter_id, subsection=subsection_id, position=4 ).visit() self.assertIn('html 2 dummy body', html2_page.get_selected_tab_content()) @attr('a11y') def test_courseware_a11y(self): """ Run accessibility audit for the problem type. """ self.course_nav.go_to_section('Test Section 1', 'Test Subsection 1,1') # Set the scope to the sequence navigation self.courseware_page.a11y_audit.config.set_scope( include=['div.sequence-nav']) self.courseware_page.a11y_audit.check_for_accessibility_errors()