course_home.py 7.97 KB
Newer Older
1 2 3 4
"""
LMS Course Home page object
"""

5
from collections import OrderedDict
6 7
from bok_choy.page_object import PageObject

8 9 10 11
from .bookmarks import BookmarksPage
from .course_page import CoursePage
from .courseware import CoursewarePage
from .staff_view import StaffPreviewPage
12 13 14 15 16 17 18 19 20


class CourseHomePage(CoursePage):
    """
    Course home page, including course outline.
    """

    url_path = "course/"

21 22
    HEADER_RESUME_COURSE_SELECTOR = '.page-header .action-resume-course'

23 24 25 26 27 28 29
    def is_browser_on_page(self):
        return self.q(css='.course-outline').present

    def __init__(self, browser, course_id):
        super(CourseHomePage, self).__init__(browser, course_id)
        self.course_id = course_id
        self.outline = CourseOutlinePage(browser, self)
30
        self.preview = StaffPreviewPage(browser, self)
31 32 33
        # TODO: TNL-6546: Remove the following
        self.unified_course_view = False

34 35 36 37 38 39
    def click_bookmarks_button(self):
        """ Click on Bookmarks button """
        self.q(css='.bookmarks-list-button').first.click()
        bookmarks_page = BookmarksPage(self.browser, self.course_id)
        bookmarks_page.visit()

40 41 42 43 44 45 46 47
    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()

48 49 50 51 52 53 54 55

class CourseOutlinePage(PageObject):
    """
    Course outline fragment of page.
    """

    url = None

Robert Raposa committed
56
    SECTION_SELECTOR = '.outline-item.section:nth-of-type({0})'
57
    SECTION_TITLES_SELECTOR = '.section-name h3'
Robert Raposa committed
58
    SUBSECTION_SELECTOR = SECTION_SELECTOR + ' .subsection:nth-of-type({1}) .outline-item'
59
    SUBSECTION_TITLES_SELECTOR = SECTION_SELECTOR + ' .subsection .subsection-title'
60
    OUTLINE_RESUME_COURSE_SELECTOR = '.outline-item .resume-right'
Robert Raposa committed
61

62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
    def __init__(self, browser, parent_page):
        super(CourseOutlinePage, self).__init__(browser)
        self.parent_page = parent_page

    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
85
        outline_dict = OrderedDict()
86 87 88 89 90 91 92

        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:
93
                raise ValueError("Could not find subsections for '{0}'".format(sec_title))
94 95 96 97 98 99
            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

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
    @property
    def num_sections(self):
        """
        Return the number of sections
        """
        return len(self.q(css=self.SECTION_TITLES_SELECTOR))

    @property
    def num_subsections(self, section_title=None):
        """
        Return the number of subsections.

        Arguments:
            section_title: The section for which to return the number of
                subsections. If None, default to the first section.
        """
        if section_title:
            section_index = self._section_title_to_index(section_title)
            if not section_index:
                return
        else:
            section_index = 1

        return len(self.q(css=self.SUBSECTION_TITLES_SELECTOR.format(section_index)))

125 126
    def go_to_section(self, section_title, subsection_title):
        """
127
        Go to the section/subsection in the courseware.
128 129 130 131 132 133
        Every section must have at least one subsection, so specify
        both the section and subsection title.

        Example:
            go_to_section("Week 1", "Lesson 1")
        """
134 135
        section_index = self._section_title_to_index(section_title)
        if section_index is None:
136
            raise ValueError("Could not find section '{0}'".format(section_title))
137 138 139 140

        try:
            subsection_index = self._subsection_titles(section_index + 1).index(subsection_title)
        except ValueError:
141 142 143
            raise ValueError("Could not find subsection '{0}' in section '{1}'".format(
                subsection_title, section_title
            ))
144 145

        # Convert list indices (start at zero) to CSS indices (start at 1)
Robert Raposa committed
146
        subsection_css = self.SUBSECTION_SELECTOR.format(section_index + 1, subsection_index + 1)
147 148 149 150 151 152

        # Click the subsection and ensure that the page finishes reloading
        self.q(css=subsection_css).first.click()

        self._wait_for_course_section(section_title, subsection_title)

153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
    def go_to_section_by_index(self, section_index, subsection_index):
        """
        Go to the section/subsection in the courseware.
        Every section must have at least one subsection, so specify both the
        section and subsection indices.

        Arguments:
            section_index: A 0-based index of the section to navigate to.
            subsection_index: A 0-based index of the subsection to navigate to.

        """
        try:
            section_title = self._section_titles()[section_index]
        except IndexError:
            raise ValueError("Section index '{0}' is out of range.".format(section_index))
        try:
            subsection_title = self._subsection_titles(section_index + 1)[subsection_index]
        except IndexError:
            raise ValueError("Subsection index '{0}' in section index '{1}' is out of range.".format(
                subsection_index, section_index
            ))

        self.go_to_section(section_title, subsection_title)

177 178 179 180 181 182 183
    def _section_title_to_index(self, section_title):
        """
        Get the section title index given the section title.
        """
        try:
            section_index = self._section_titles().index(section_title)
        except ValueError:
184
            raise ValueError("Could not find section '{0}'".format(section_title))
185 186 187

        return section_index

188 189 190 191 192 193 194 195
    def resume_course_from_outline(self):
        """
        Navigate to courseware using Resume Course button in the header.
        """
        self.q(css=self.OUTLINE_RESUME_COURSE_SELECTOR).first.click()
        courseware_page = CoursewarePage(self.browser, self.parent_page.course_id)
        courseware_page.wait_for_page()

196 197 198 199
    def _section_titles(self):
        """
        Return a list of all section titles on the page.
        """
Robert Raposa committed
200
        return self.q(css=self.SECTION_TITLES_SELECTOR).map(lambda el: el.text.strip()).results
201 202 203 204 205 206

    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).
        """
Robert Raposa committed
207 208
        subsection_css = self.SUBSECTION_TITLES_SELECTOR.format(section_index)
        return self.q(css=subsection_css).map(
209 210 211 212 213 214 215
            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.
        """
216 217 218 219 220 221 222
        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()

223
        self.wait_for(
224
            promise_check_func=lambda: courseware_page.nav.is_on_section(section_title, subsection_title),
225 226
            description="Waiting for course page with section '{0}' and subsection '{1}'".format(section_title, subsection_title)
        )