Activate Next and Previous Buttons across sections
MA-2152 MA-2153
This commit is contained in:
@@ -3,7 +3,7 @@ Course navigation page object
|
||||
"""
|
||||
|
||||
import re
|
||||
from bok_choy.page_object import PageObject
|
||||
from bok_choy.page_object import PageObject, unguarded
|
||||
from bok_choy.promise import EmptyPromise
|
||||
|
||||
|
||||
@@ -165,10 +165,11 @@ class CourseNavPage(PageObject):
|
||||
"""
|
||||
desc = "currently at section '{0}' and subsection '{1}'".format(section_title, subsection_title)
|
||||
return EmptyPromise(
|
||||
lambda: self._is_on_section(section_title, subsection_title), desc
|
||||
lambda: self.is_on_section(section_title, subsection_title), desc
|
||||
)
|
||||
|
||||
def _is_on_section(self, section_title, subsection_title):
|
||||
@unguarded
|
||||
def is_on_section(self, section_title, subsection_title):
|
||||
"""
|
||||
Return a boolean indicating whether the user is on the section and subsection
|
||||
with the specified titles.
|
||||
@@ -203,13 +204,9 @@ class CourseNavPage(PageObject):
|
||||
"""
|
||||
return self.REMOVE_SPAN_TAG_RE.search(element.get_attribute('innerHTML')).groups()[0].strip()
|
||||
|
||||
def go_to_sequential_position(self, sequential_position):
|
||||
@property
|
||||
def active_subsection_url(self):
|
||||
"""
|
||||
Within a section/subsection navigate to the sequential position specified by `sequential_position`.
|
||||
|
||||
Arguments:
|
||||
sequential_position (int): position in sequential bar
|
||||
|
||||
return the url of the active subsection in the left nav
|
||||
"""
|
||||
sequential_position_css = '#tab_{0}'.format(sequential_position - 1)
|
||||
self.q(css=sequential_position_css).first.click()
|
||||
return self.q(css='.chapter-content-container .menu-item.active a').attrs('href')[0]
|
||||
|
||||
@@ -20,6 +20,13 @@ class CoursewarePage(CoursePage):
|
||||
def is_browser_on_page(self):
|
||||
return self.q(css='body.courseware').present
|
||||
|
||||
@property
|
||||
def chapter_count_in_navigation(self):
|
||||
"""
|
||||
Returns count of chapters available on LHS navigation.
|
||||
"""
|
||||
return len(self.q(css='nav.course-navigation a.chapter'))
|
||||
|
||||
@property
|
||||
def num_sections(self):
|
||||
"""
|
||||
@@ -101,11 +108,66 @@ class CoursewarePage(CoursePage):
|
||||
return element.text[0]
|
||||
return None
|
||||
|
||||
def get_active_subsection_url(self):
|
||||
def go_to_sequential_position(self, sequential_position):
|
||||
"""
|
||||
return the url of the active subsection in the left nav
|
||||
Within a section/subsection navigate to the sequential position specified by `sequential_position`.
|
||||
|
||||
Arguments:
|
||||
sequential_position (int): position in sequential bar
|
||||
"""
|
||||
return self.q(css='.chapter-content-container .menu-item.active a').attrs('href')[0]
|
||||
sequential_position_css = '#sequence-list #tab_{0}'.format(sequential_position - 1)
|
||||
self.q(css=sequential_position_css).first.click()
|
||||
|
||||
@property
|
||||
def sequential_position(self):
|
||||
"""
|
||||
Returns the position of the active tab in the sequence.
|
||||
"""
|
||||
tab_id = self._active_sequence_tab.attrs('id')[0]
|
||||
return int(tab_id.split('_')[1])
|
||||
|
||||
@property
|
||||
def _active_sequence_tab(self): # pylint: disable=missing-docstring
|
||||
return self.q(css='#sequence-list .nav-item.active')
|
||||
|
||||
@property
|
||||
def is_next_button_enabled(self): # pylint: disable=missing-docstring
|
||||
return not self.q(css='.sequence-nav > .sequence-nav-button.button-next.disabled').is_present()
|
||||
|
||||
@property
|
||||
def is_previous_button_enabled(self): # pylint: disable=missing-docstring
|
||||
return not self.q(css='.sequence-nav > .sequence-nav-button.button-previous.disabled').is_present()
|
||||
|
||||
def click_next_button_on_top(self): # pylint: disable=missing-docstring
|
||||
self._click_navigation_button('sequence-nav', 'button-next')
|
||||
|
||||
def click_next_button_on_bottom(self): # pylint: disable=missing-docstring
|
||||
self._click_navigation_button('sequence-bottom', 'button-next')
|
||||
|
||||
def click_previous_button_on_top(self): # pylint: disable=missing-docstring
|
||||
self._click_navigation_button('sequence-nav', 'button-previous')
|
||||
|
||||
def click_previous_button_on_bottom(self): # pylint: disable=missing-docstring
|
||||
self._click_navigation_button('sequence-bottom', 'button-previous')
|
||||
|
||||
def _click_navigation_button(self, top_or_bottom_class, next_or_previous_class):
|
||||
"""
|
||||
Clicks the navigation button, given the respective CSS classes.
|
||||
"""
|
||||
previous_tab_id = self._active_sequence_tab.attrs('data-id')[0]
|
||||
|
||||
def is_at_new_tab_id():
|
||||
"""
|
||||
Returns whether the active tab has changed. It is defensive
|
||||
against the case where the page is still being loaded.
|
||||
"""
|
||||
active_tab = self._active_sequence_tab
|
||||
return active_tab and previous_tab_id != active_tab.attrs('data-id')[0]
|
||||
|
||||
self.q(
|
||||
css='.{} > .sequence-nav-button.{}'.format(top_or_bottom_class, next_or_previous_class)
|
||||
).first.click()
|
||||
EmptyPromise(is_at_new_tab_id, "Button navigation fulfilled").fulfill()
|
||||
|
||||
@property
|
||||
def can_start_proctored_exam(self):
|
||||
@@ -160,13 +222,6 @@ class CoursewarePage(CoursePage):
|
||||
return self.entrance_exam_message_selector.is_present() \
|
||||
and "You have passed the entrance exam" in self.entrance_exam_message_selector.text[0]
|
||||
|
||||
@property
|
||||
def chapter_count_in_navigation(self):
|
||||
"""
|
||||
Returns count of chapters available on LHS navigation.
|
||||
"""
|
||||
return len(self.q(css='nav.course-navigation a.chapter'))
|
||||
|
||||
@property
|
||||
def is_timer_bar_present(self):
|
||||
"""
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"""
|
||||
End-to-end tests for the LMS.
|
||||
"""
|
||||
import time
|
||||
from nose.plugins.attrib import attr
|
||||
from unittest import skip
|
||||
|
||||
from ..helpers import UniqueCourseTest
|
||||
from ...pages.studio.auto_auth import AutoAuthPage
|
||||
@@ -420,13 +421,20 @@ class CoursewareMultipleVerticalsTest(UniqueCourseTest):
|
||||
|
||||
course_fix.add_children(
|
||||
XBlockFixtureDesc('chapter', 'Test Section 1').add_children(
|
||||
XBlockFixtureDesc('sequential', 'Test Subsection 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 2'),
|
||||
XBlockFixtureDesc('sequential', 'Test Subsection 1,2').add_children(
|
||||
XBlockFixtureDesc('problem', 'Test Problem 3', data='<problem>problem 3 dummy body</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>'),
|
||||
),
|
||||
),
|
||||
).install()
|
||||
|
||||
@@ -436,10 +444,53 @@ class CoursewareMultipleVerticalsTest(UniqueCourseTest):
|
||||
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)
|
||||
|
||||
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')
|
||||
subsection_url = self.courseware_page.get_active_subsection_url()
|
||||
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)
|
||||
|
||||
@@ -481,3 +532,15 @@ class CoursewareMultipleVerticalsTest(UniqueCourseTest):
|
||||
position=4
|
||||
).visit()
|
||||
self.assertIn('html 2 dummy body', html2_page.get_selected_tab_content())
|
||||
|
||||
@skip('Fix a11y test errors.')
|
||||
@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()
|
||||
|
||||
@@ -194,14 +194,14 @@ class EdxNotesDefaultInteractionsTest(EdxNotesTestMixin):
|
||||
self.create_notes(components)
|
||||
self.assert_text_in_notes(self.note_unit_page.notes)
|
||||
|
||||
self.course_nav.go_to_sequential_position(2)
|
||||
self.courseware_page.go_to_sequential_position(2)
|
||||
components = self.note_unit_page.components
|
||||
self.create_notes(components)
|
||||
|
||||
components = self.note_unit_page.refresh()
|
||||
self.assert_text_in_notes(self.note_unit_page.notes)
|
||||
|
||||
self.course_nav.go_to_sequential_position(1)
|
||||
self.courseware_page.go_to_sequential_position(1)
|
||||
components = self.note_unit_page.components
|
||||
self.assert_text_in_notes(self.note_unit_page.notes)
|
||||
|
||||
@@ -227,7 +227,7 @@ class EdxNotesDefaultInteractionsTest(EdxNotesTestMixin):
|
||||
self.edit_notes(components)
|
||||
self.assert_text_in_notes(self.note_unit_page.notes)
|
||||
|
||||
self.course_nav.go_to_sequential_position(2)
|
||||
self.courseware_page.go_to_sequential_position(2)
|
||||
components = self.note_unit_page.components
|
||||
self.edit_notes(components)
|
||||
self.assert_text_in_notes(self.note_unit_page.notes)
|
||||
@@ -235,7 +235,7 @@ class EdxNotesDefaultInteractionsTest(EdxNotesTestMixin):
|
||||
components = self.note_unit_page.refresh()
|
||||
self.assert_text_in_notes(self.note_unit_page.notes)
|
||||
|
||||
self.course_nav.go_to_sequential_position(1)
|
||||
self.courseware_page.go_to_sequential_position(1)
|
||||
components = self.note_unit_page.components
|
||||
self.assert_text_in_notes(self.note_unit_page.notes)
|
||||
|
||||
@@ -261,7 +261,7 @@ class EdxNotesDefaultInteractionsTest(EdxNotesTestMixin):
|
||||
self.remove_notes(components)
|
||||
self.assert_notes_are_removed(components)
|
||||
|
||||
self.course_nav.go_to_sequential_position(2)
|
||||
self.courseware_page.go_to_sequential_position(2)
|
||||
components = self.note_unit_page.components
|
||||
self.remove_notes(components)
|
||||
self.assert_notes_are_removed(components)
|
||||
@@ -269,7 +269,7 @@ class EdxNotesDefaultInteractionsTest(EdxNotesTestMixin):
|
||||
components = self.note_unit_page.refresh()
|
||||
self.assert_notes_are_removed(components)
|
||||
|
||||
self.course_nav.go_to_sequential_position(1)
|
||||
self.courseware_page.go_to_sequential_position(1)
|
||||
components = self.note_unit_page.components
|
||||
self.assert_notes_are_removed(components)
|
||||
|
||||
@@ -1106,10 +1106,10 @@ class EdxNotesPageTest(EventsTestMixin, EdxNotesTestMixin):
|
||||
self.assertTrue(note.is_visible)
|
||||
note = self.note_unit_page.notes[1]
|
||||
self.assertFalse(note.is_visible)
|
||||
self.course_nav.go_to_sequential_position(2)
|
||||
self.courseware_page.go_to_sequential_position(2)
|
||||
note = self.note_unit_page.notes[0]
|
||||
self.assertFalse(note.is_visible)
|
||||
self.course_nav.go_to_sequential_position(1)
|
||||
self.courseware_page.go_to_sequential_position(1)
|
||||
note = self.note_unit_page.notes[0]
|
||||
self.assertFalse(note.is_visible)
|
||||
|
||||
@@ -1494,7 +1494,7 @@ class EdxNotesToggleNotesTest(EdxNotesTestMixin):
|
||||
# Disable all notes
|
||||
self.note_unit_page.toggle_visibility()
|
||||
self.assertEqual(len(self.note_unit_page.notes), 0)
|
||||
self.course_nav.go_to_sequential_position(2)
|
||||
self.courseware_page.go_to_sequential_position(2)
|
||||
self.assertEqual(len(self.note_unit_page.notes), 0)
|
||||
self.course_nav.go_to_section(u"Test Section 1", u"Test Subsection 2")
|
||||
self.assertEqual(len(self.note_unit_page.notes), 0)
|
||||
@@ -1520,7 +1520,7 @@ class EdxNotesToggleNotesTest(EdxNotesTestMixin):
|
||||
# the page.
|
||||
self.note_unit_page.toggle_visibility()
|
||||
self.assertGreater(len(self.note_unit_page.notes), 0)
|
||||
self.course_nav.go_to_sequential_position(2)
|
||||
self.courseware_page.go_to_sequential_position(2)
|
||||
self.assertGreater(len(self.note_unit_page.notes), 0)
|
||||
self.course_nav.go_to_section(u"Test Section 1", u"Test Subsection 2")
|
||||
self.assertGreater(len(self.note_unit_page.notes), 0)
|
||||
|
||||
@@ -1546,7 +1546,7 @@ class PublishSectionTest(CourseOutlineTest):
|
||||
self.assertTrue(section.publish_action)
|
||||
self.courseware.visit()
|
||||
self.assertEqual(1, self.courseware.num_xblock_components)
|
||||
self.course_nav.go_to_sequential_position(2)
|
||||
self.courseware.go_to_sequential_position(2)
|
||||
self.assertEqual(1, self.courseware.num_xblock_components)
|
||||
|
||||
def test_section_publishing(self):
|
||||
@@ -1571,7 +1571,7 @@ class PublishSectionTest(CourseOutlineTest):
|
||||
self.assertFalse(unit.publish_action)
|
||||
self.courseware.visit()
|
||||
self.assertEqual(1, self.courseware.num_xblock_components)
|
||||
self.course_nav.go_to_sequential_position(2)
|
||||
self.courseware.go_to_sequential_position(2)
|
||||
self.assertEqual(1, self.courseware.num_xblock_components)
|
||||
self.course_nav.go_to_section(SECTION_NAME, 'Test Subsection 2')
|
||||
self.assertEqual(1, self.courseware.num_xblock_components)
|
||||
|
||||
@@ -11,6 +11,7 @@ from unittest import skipIf, skip
|
||||
from ..helpers import UniqueCourseTest, is_youtube_available, YouTubeStubConfig
|
||||
from ...pages.lms.video.video import VideoPage
|
||||
from ...pages.lms.tab_nav import TabNavPage
|
||||
from ...pages.lms.courseware import CoursewarePage
|
||||
from ...pages.lms.course_nav import CourseNavPage
|
||||
from ...pages.lms.auto_auth import AutoAuthPage
|
||||
from ...pages.lms.course_info import CourseInfoPage
|
||||
@@ -49,6 +50,7 @@ class VideoBaseTest(UniqueCourseTest):
|
||||
self.video = VideoPage(self.browser)
|
||||
self.tab_nav = TabNavPage(self.browser)
|
||||
self.course_nav = CourseNavPage(self.browser)
|
||||
self.courseware = 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)
|
||||
|
||||
@@ -190,7 +192,7 @@ class VideoBaseTest(UniqueCourseTest):
|
||||
"""
|
||||
Navigate to sequential specified by `video_display_name`
|
||||
"""
|
||||
self.course_nav.go_to_sequential_position(position)
|
||||
self.courseware.go_to_sequential_position(position)
|
||||
self.video.wait_for_video_player_render()
|
||||
|
||||
|
||||
@@ -916,13 +918,13 @@ class YouTubeVideoTest(VideoBaseTest):
|
||||
execute_video_steps(['A'])
|
||||
|
||||
# go to second sequential position
|
||||
self.course_nav.go_to_sequential_position(2)
|
||||
self.courseware.go_to_sequential_position(2)
|
||||
|
||||
execute_video_steps(tab2_video_names)
|
||||
|
||||
# go back to first sequential position
|
||||
# we are again playing tab 1 videos to ensure that switching didn't broke some video functionality.
|
||||
self.course_nav.go_to_sequential_position(1)
|
||||
self.courseware.go_to_sequential_position(1)
|
||||
execute_video_steps(tab1_video_names)
|
||||
|
||||
self.video.browser.refresh()
|
||||
|
||||
Reference in New Issue
Block a user