class StaffViewTest(UniqueCourseTest):
    """
    Tests that verify the staff view.
    """
    USERNAME = "******"
    EMAIL = "*****@*****.**"

    def setUp(self):
        super(StaffViewTest, self).setUp()

        self.courseware_page = CoursewarePage(self.browser, self.course_id)

        # Install a course with sections/problems, tabs, updates, and handouts
        self.course_fixture = CourseFixture(
            self.course_info['org'], self.course_info['number'],
            self.course_info['run'], self.course_info['display_name']
        )

        self.populate_course_fixture(self.course_fixture)

        self.course_fixture.install()

        # Auto-auth register for the course.
        # Do this as global staff so that you will see the Staff View
        AutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL,
                     course_id=self.course_id, staff=True).visit()

    def _goto_staff_page(self):
        """
        Open staff page with assertion
        """
        self.courseware_page.visit()
        staff_page = StaffCoursewarePage(self.browser, self.course_id)
        self.assertEqual(staff_page.staff_view_mode, 'Staff')
        return staff_page
class StaffViewTest(UniqueCourseTest):
    """
    Tests that verify the staff view.
    """
    USERNAME = "******"
    EMAIL = "*****@*****.**"

    def setUp(self):
        super(StaffViewTest, self).setUp()

        self.courseware_page = CoursewarePage(self.browser, self.course_id)

        # Install a course with sections/problems, tabs, updates, and handouts
        self.course_fixture = CourseFixture(
            self.course_info['org'], self.course_info['number'],
            self.course_info['run'], self.course_info['display_name']
        )

        self.populate_course_fixture(self.course_fixture)  # pylint: disable=no-member

        self.course_fixture.install()

        # Auto-auth register for the course.
        # Do this as global staff so that you will see the Staff View
        AutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL,
                     course_id=self.course_id, staff=True).visit()

    def _goto_staff_page(self):
        """
        Open staff page with assertion
        """
        self.courseware_page.visit()
        staff_page = StaffPage(self.browser, self.course_id)
        self.assertEqual(staff_page.staff_view_mode, 'Staff')
        return staff_page
Пример #3
0
class TooltipTest(UniqueCourseTest):
    """
    Tests that tooltips are displayed
    """
    def setUp(self):
        """
        Initialize pages and install a course fixture.
        """
        super(TooltipTest, self).setUp()

        self.course_home_page = CourseHomePage(self.browser, self.course_id)
        self.tab_nav = TabNavPage(self.browser)

        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('static_tab', 'Test Static Tab'),
            XBlockFixtureDesc('chapter', 'Test Section').add_children(
                XBlockFixtureDesc(
                    'sequential', 'Test Subsection').add_children(
                        XBlockFixtureDesc(
                            'problem',
                            'Test Problem 1',
                            data=load_data_str('multiple_choice.xml')),
                        XBlockFixtureDesc(
                            'problem',
                            'Test Problem 2',
                            data=load_data_str('formula_problem.xml')),
                        XBlockFixtureDesc('html', 'Test HTML'),
                    ))).install()

        self.courseware_page = CoursewarePage(self.browser, self.course_id)
        # Auto-auth register for the course
        AutoAuthPage(self.browser, course_id=self.course_id).visit()

    def test_tooltip(self):
        """
        Verify that tooltips are displayed when you hover over the sequence nav bar.
        """
        self.courseware_page.visit()

        self.courseware_page.verify_tooltips_displayed()
Пример #4
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
        When I visit the LMS Course Home page
        Then I should see a section named 'Section' or 'Entrance Exam'
        When I switch the view mode to student view
        Then I should only see a section named 'Entrance Exam'
        When I visit the courseware page
        Then a message regarding the '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()

        course_home = CourseHomePage(self.browser, self.course_id)
        course_home.visit()
        self.assertEqual(course_home.outline.num_sections, 2)
        course_home.preview.set_staff_view_mode('Learner')
        self.assertEqual(course_home.outline.num_sections, 1)

        courseware = CoursewarePage(self.browser, self.course_id)
        courseware.visit()
        StaffCoursewarePage(self.browser, self.course_id).set_staff_view_mode('Learner')
        self.assertIn(
            "To access course materials, you must score", courseware.entrance_exam_message_selector.text[0]
        )
Пример #5
0
class TooltipTest(UniqueCourseTest):
    """
    Tests that tooltips are displayed
    """

    def setUp(self):
        """
        Initialize pages and install a course fixture.
        """
        super(TooltipTest, self).setUp()

        self.course_home_page = CourseHomePage(self.browser, self.course_id)
        self.tab_nav = TabNavPage(self.browser)

        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('static_tab', 'Test Static Tab'),
            XBlockFixtureDesc('chapter', 'Test Section').add_children(
                XBlockFixtureDesc('sequential', 'Test Subsection').add_children(
                    XBlockFixtureDesc('problem', 'Test Problem 1', data=load_data_str('multiple_choice.xml')),
                    XBlockFixtureDesc('problem', 'Test Problem 2', data=load_data_str('formula_problem.xml')),
                    XBlockFixtureDesc('html', 'Test HTML'),
                )
            )
        ).install()

        self.courseware_page = CoursewarePage(self.browser, self.course_id)
        # Auto-auth register for the course
        AutoAuthPage(self.browser, course_id=self.course_id).visit()

    def test_tooltip(self):
        """
        Verify that tooltips are displayed when you hover over the sequence nav bar.
        """
        self.courseware_page.visit()

        self.courseware_page.verify_tooltips_displayed()
Пример #6
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
        When I visit the LMS Course Home page
        Then I should see a section named 'Section' or 'Entrance Exam'
        When I switch the view mode to student view
        Then I should only see a section named 'Entrance Exam'
        When I visit the courseware page
        Then a message regarding the '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()

        course_home = CourseHomePage(self.browser, self.course_id)
        course_home.visit()
        self.assertEqual(course_home.outline.num_sections, 2)
        course_home.preview.set_staff_view_mode('Learner')
        self.assertEqual(course_home.outline.num_sections, 1)

        courseware = CoursewarePage(self.browser, self.course_id)
        courseware.visit()
        StaffCoursewarePage(self.browser, self.course_id).set_staff_view_mode('Learner')
        self.assertIn(
            "To access course materials, you must score", courseware.entrance_exam_message_selector.text[0]
        )
Пример #7
0
    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'
        ))
Пример #8
0
    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'))
Пример #9
0
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)
Пример #10
0
class DragAndDropXblockWithMixinsTest(UniqueCourseTest):
    """
    Test Suite to verify various behaviors of DragAndDrop Xblock on the LMS.
    """

    def setUp(self):
        super(DragAndDropXblockWithMixinsTest, self).setUp()

        self.username = "******".format(uuid=self.unique_id[0:8])
        self.email = "{username}@example.com".format(username=self.username)
        self.password = "******"
        self.courseware_page = CoursewarePage(self.browser, self.course_id)

        # Install a course with a hierarchy and problems
        self.course_fixture = CourseFixture(
            self.course_info['org'], self.course_info['number'],
            self.course_info['run'], self.course_info['display_name'],
            start_date=datetime.now() + timedelta(days=10)
        )
        self.browser.set_window_size(1024, 1024)

    def setup_sequential(self, metadata):
        """
        Setup a sequential with DnD problem, alongwith the metadata provided.

        This method will allow to customize the sequential, such as changing the
        due date for individual tests.
        """
        problem = self.get_problem()
        sequential = self.get_sequential(metadata=metadata)
        self.course_fixture.add_children(
            XBlockFixtureDesc('chapter', 'Test Section').add_children(
                sequential.add_children(problem)
            )
        ).install()

        # Auto-auth register for the course.
        AutoAuthPage(
            self.browser,
            username=self.username,
            email=self.email,
            password=self.password,
            course_id=self.course_id,
            staff=True
        ).visit()

    def format_date(self, date_value):
        """
        Get the date in isoformat as this is required format to add date data
        in the sequential.
        """
        return date_value.isoformat()

    def get_problem(self):
        """
        Creating a DnD problem with assessment mode
        """
        return XBlockFixtureDesc('drag-and-drop-v2', 'DnD', metadata={'mode': "assessment"})

    def get_sequential(self, metadata=None):
        return XBlockFixtureDesc('sequential', 'Test Subsection', metadata=metadata)

    @ddt.data(
        (datetime.now(), True),
        (datetime.now() - timedelta(days=1), True),
        (datetime.now() + timedelta(days=1), False)
    )
    @ddt.unpack
    def test_submit_button_status_with_due_date(self, due_date, is_button_disabled):
        """
        Scenario: Test that DnD submit button will be enabled if section is not past due.

        Given I have a sequential in instructor-paced course
        And a DnD problem with assessment mode is present in the sequential
        When I visit the problem
        Then the submit button should be present
        And button should be disabled as some item needs to be on a zone
        When I drag an item to a zone
        Then submit button will be enabled if due date has not passed, else disabled
        """
        problem_page = DragAndDropPage(self.browser)
        self.setup_sequential(metadata={'due': self.format_date(due_date)})
        self.courseware_page.visit()
        self.assertTrue(problem_page.is_submit_button_present())
        self.assertTrue(problem_page.is_submit_disabled())
        problem_page.drag_item_to_zone(0, 'middle')
        self.assertEqual(is_button_disabled, problem_page.is_submit_disabled())

    def test_submit_button_when_pacing_change_self_paced(self):
        """
        Scenario: For a self-paced course, the submit button of DnD problems will be
        be enabled, regardless of the subsection due date.

        Given a DnD problem in a subsection with past due date
        And the course is instructor-paced
        Then the submit button will remain disabled after initial drag
        When the pacing is changed to self-paced
        Then the submit button is not disabled anymore
        """
        problem_page = DragAndDropPage(self.browser)
        self.setup_sequential(metadata={'due': self.format_date(datetime.now())})
        self.courseware_page.visit()
        problem_page.drag_item_to_zone(0, 'middle')
        self.assertTrue(problem_page.is_submit_disabled())
        self.course_fixture.add_course_details({'self_paced': True})
        self.course_fixture.configure_course()
        self.courseware_page.visit()
        self.assertFalse(problem_page.is_submit_disabled())
Пример #11
0
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())
Пример #12
0
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()
Пример #13
0
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)
Пример #14
0
class LibraryContentTestBase(UniqueCourseTest):
    """ Base class for library content block tests """
    USERNAME = "******"
    EMAIL = "*****@*****.**"

    STAFF_USERNAME = "******"
    STAFF_EMAIL = "*****@*****.**"

    def populate_library_fixture(self, library_fixture):
        """
        To be overwritten by subclassed tests. Used to install a library to
        run tests on.
        """

    def setUp(self):
        """
        Set up library, course and library content XBlock
        """
        super(LibraryContentTestBase, self).setUp()

        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']
        )

        self.library_fixture = LibraryFixture('test_org', self.unique_id, 'Test Library {}'.format(self.unique_id))
        self.populate_library_fixture(self.library_fixture)

        self.library_fixture.install()
        self.library_info = self.library_fixture.library_info
        self.library_key = self.library_fixture.library_key

        # Install a course with library content xblock
        self.course_fixture = CourseFixture(
            self.course_info['org'], self.course_info['number'],
            self.course_info['run'], self.course_info['display_name']
        )

        library_content_metadata = {
            'source_library_id': unicode(self.library_key),
            'mode': 'random',
            'max_count': 1,
        }

        self.lib_block = XBlockFixtureDesc('library_content', "Library Content", metadata=library_content_metadata)

        self.course_fixture.add_children(
            XBlockFixtureDesc('chapter', SECTION_NAME).add_children(
                XBlockFixtureDesc('sequential', SUBSECTION_NAME).add_children(
                    XBlockFixtureDesc('vertical', UNIT_NAME).add_children(
                        self.lib_block
                    )
                )
            )
        )

        self.course_fixture.install()

    def _change_library_content_settings(self, count=1, capa_type=None):
        """
        Performs library block refresh in Studio, configuring it to show {count} children
        """
        unit_page = self._go_to_unit_page(True)
        library_container_block = StudioLibraryContainerXBlockWrapper.from_xblock_wrapper(unit_page.xblocks[1])
        library_container_block.edit()
        editor = StudioLibraryContentEditor(self.browser, library_container_block.locator)
        editor.count = count
        if capa_type is not None:
            editor.capa_type = capa_type
        editor.save()
        self._go_to_unit_page(change_login=False)
        unit_page.wait_for_page()
        unit_page.publish_action.click()
        unit_page.wait_for_ajax()
        self.assertIn("Published and Live", unit_page.publish_title)

    @property
    def library_xblocks_texts(self):
        """
        Gets texts of all xblocks in library
        """
        return frozenset(child.data for child in self.library_fixture.children)

    def _go_to_unit_page(self, change_login=True):
        """
        Open unit page in Studio
        """
        if change_login:
            LogoutPage(self.browser).visit()
            self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True)
        self.studio_course_outline.visit()

        subsection = self.studio_course_outline.section(SECTION_NAME).subsection(SUBSECTION_NAME)
        return subsection.expand_subsection().unit(UNIT_NAME).go_to()

    def _goto_library_block_page(self, block_id=None):
        """
        Open library page in LMS
        """
        self.courseware_page.visit()
        paragraphs = self.courseware_page.q(css='.course-content p').results
        if not paragraphs:
            course_home_page = CourseHomePage(self.browser, self.course_id)
            course_home_page.visit()
            course_home_page.outline.go_to_section_by_index(0, 0)
        block_id = block_id if block_id is not None else self.lib_block.locator
        #pylint: disable=attribute-defined-outside-init
        self.library_content_page = LibraryContentXBlockWrapper(self.browser, block_id)
        self.library_content_page.wait_for_page()

    def _auto_auth(self, username, email, staff):
        """
        Logout and login with given credentials.
        """
        AutoAuthPage(self.browser, username=username, email=email,
                     course_id=self.course_id, staff=staff).visit()
Пример #15
0
class CoursewareTest(UniqueCourseTest):
    """
    Test courseware.
    """
    USERNAME = "******"
    EMAIL = "*****@*****.**"

    def setUp(self):
        super(CoursewareTest, self).setUp()

        self.courseware_page = CoursewarePage(self.browser, self.course_id)
        self.course_nav = CourseNavPage(self.browser)

        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
        self.course_fix = CourseFixture(self.course_info['org'],
                                        self.course_info['number'],
                                        self.course_info['run'],
                                        self.course_info['display_name'])

        self.course_fix.add_children(
            XBlockFixtureDesc('chapter', 'Test Section 1').add_children(
                XBlockFixtureDesc('sequential',
                                  'Test Subsection 1').add_children(
                                      XBlockFixtureDesc(
                                          'problem', 'Test Problem 1'))),
            XBlockFixtureDesc('chapter', 'Test Section 2').add_children(
                XBlockFixtureDesc('sequential',
                                  'Test Subsection 2').add_children(
                                      XBlockFixtureDesc(
                                          'problem',
                                          'Test Problem 2')))).install()

        # Auto-auth register for the course.
        self._auto_auth(self.USERNAME, self.EMAIL, False)

    def _goto_problem_page(self):
        """
        Open problem page with assertion.
        """
        self.courseware_page.visit()
        self.problem_page = ProblemPage(self.browser)  # pylint: disable=attribute-defined-outside-init
        self.assertEqual(self.problem_page.problem_name, 'Test Problem 1')

    def _create_breadcrumb(self, index):
        """ Create breadcrumb """
        return [
            'Test Section {}'.format(index),
            'Test Subsection {}'.format(index), 'Test Problem {}'.format(index)
        ]

    def _auto_auth(self, username, email, staff):
        """
        Logout and login with given credentials.
        """
        AutoAuthPage(self.browser,
                     username=username,
                     email=email,
                     course_id=self.course_id,
                     staff=staff).visit()

    def test_courseware(self):
        """
        Test courseware if recent visited subsection become unpublished.
        """

        # Visit problem page as a student.
        self._goto_problem_page()

        # Logout and login as a staff user.
        LogoutPage(self.browser).visit()
        self._auto_auth("STAFF_TESTER", "*****@*****.**", True)

        # Visit course outline page in studio.
        self.course_outline.visit()

        # Set release date for subsection in future.
        self.course_outline.change_problem_release_date()

        # Logout and login as a student.
        LogoutPage(self.browser).visit()
        self._auto_auth(self.USERNAME, self.EMAIL, False)

        # Visit courseware as a student.
        self.courseware_page.visit()
        # Problem name should be "Test Problem 2".
        self.assertEqual(self.problem_page.problem_name, 'Test Problem 2')

    def test_course_tree_breadcrumb(self):
        """
        Scenario: Correct course tree breadcrumb is shown.

        Given that I am a registered user
        And I visit my courseware page
        Then I should see correct course tree breadcrumb
        """
        self.courseware_page.visit()

        xblocks = self.course_fix.get_nested_xblocks(category="problem")
        for index in range(1, len(xblocks) + 1):
            self.course_nav.go_to_section('Test Section {}'.format(index),
                                          'Test Subsection {}'.format(index))
            courseware_page_breadcrumb = self.courseware_page.breadcrumb
            expected_breadcrumb = self._create_breadcrumb(index)  # pylint: disable=no-member
            self.assertEqual(courseware_page_breadcrumb, expected_breadcrumb)
Пример #16
0
class ProctoredExamTest(UniqueCourseTest):
    """
    Test courseware.
    """
    USERNAME = "******"
    EMAIL = "*****@*****.**"

    def setUp(self):
        super(ProctoredExamTest, 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_advanced_settings(
            {"enable_proctored_exams": {
                "value": "true"
            }})

        course_fix.add_children(
            XBlockFixtureDesc('chapter', 'Test Section 1').add_children(
                XBlockFixtureDesc('sequential',
                                  'Test Subsection 1').add_children(
                                      XBlockFixtureDesc(
                                          'problem',
                                          'Test Problem 1')))).install()

        self.track_selection_page = TrackSelectionPage(self.browser,
                                                       self.course_id)
        self.payment_and_verification_flow = PaymentAndVerificationFlow(
            self.browser, self.course_id)
        self.immediate_verification_page = PaymentAndVerificationFlow(
            self.browser, self.course_id, entry_point='verify-now')
        self.upgrade_page = PaymentAndVerificationFlow(self.browser,
                                                       self.course_id,
                                                       entry_point='upgrade')
        self.fake_payment_page = FakePaymentPage(self.browser, self.course_id)
        self.dashboard_page = DashboardPage(self.browser)
        self.problem_page = ProblemPage(self.browser)

        # Add a verified mode to the course
        ModeCreationPage(self.browser,
                         self.course_id,
                         mode_slug=u'verified',
                         mode_display_name=u'Verified Certificate',
                         min_price=10,
                         suggested_prices='10,20').visit()

        # Auto-auth register for the course.
        self._auto_auth(self.USERNAME, self.EMAIL, False)

    def _auto_auth(self, username, email, staff):
        """
        Logout and login with given credentials.
        """
        AutoAuthPage(self.browser,
                     username=username,
                     email=email,
                     course_id=self.course_id,
                     staff=staff).visit()

    def _login_as_a_verified_user(self):
        """
        login as a verififed user
        """

        self._auto_auth(self.USERNAME, self.EMAIL, False)

        # the track selection page cannot be visited. see the other tests to see if any prereq is there.
        # Navigate to the track selection page
        self.track_selection_page.visit()

        # Enter the payment and verification flow by choosing to enroll as verified
        self.track_selection_page.enroll('verified')

        # Proceed to the fake payment page
        self.payment_and_verification_flow.proceed_to_payment()

        # Submit payment
        self.fake_payment_page.submit_payment()

    def test_can_create_proctored_exam_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 Proctored and timed exams
        """
        LogoutPage(self.browser).visit()
        self._auto_auth("STAFF_TESTER", "*****@*****.**", True)
        self.course_outline.visit()

        self.course_outline.open_subsection_settings_dialog()
        self.assertTrue(self.course_outline.proctoring_items_are_displayed())

    def test_proctored_exam_flow(self):
        """
        Given that I am a staff member on the exam settings section
        select advanced settings tab
        When I Make the exam proctored.
        And I login as a verified student.
        And visit the courseware as a verified student.
        Then I can see an option to take the exam as a proctored exam.
        """
        LogoutPage(self.browser).visit()
        self._auto_auth("STAFF_TESTER", "*****@*****.**", True)
        self.course_outline.visit()
        self.course_outline.open_subsection_settings_dialog()

        self.course_outline.select_advanced_tab()
        self.course_outline.make_exam_proctored()

        LogoutPage(self.browser).visit()
        self._login_as_a_verified_user()

        self.courseware_page.visit()
        self.assertTrue(self.courseware_page.can_start_proctored_exam)

    def _setup_and_take_timed_exam(self, hide_after_due=False):
        """
        Helper to perform the common action "set up a timed exam as staff,
        then take it as student"
        """
        LogoutPage(self.browser).visit()
        self._auto_auth("STAFF_TESTER", "*****@*****.**", True)
        self.course_outline.visit()
        self.course_outline.open_subsection_settings_dialog()

        self.course_outline.select_advanced_tab()
        self.course_outline.make_exam_timed(hide_after_due=hide_after_due)

        LogoutPage(self.browser).visit()
        self._login_as_a_verified_user()
        self.courseware_page.visit()

        self.courseware_page.start_timed_exam()
        self.assertTrue(self.courseware_page.is_timer_bar_present)

        self.courseware_page.stop_timed_exam()
        self.assertTrue(self.courseware_page.has_submitted_exam_message())

        LogoutPage(self.browser).visit()

    @ddt.data(True, False)
    def test_timed_exam_flow(self, hide_after_due):
        """
        Given that I am a staff member on the exam settings section
        select advanced settings tab
        When I Make the exam timed.
        And I login as a verified student.
        And visit the courseware as a verified student.
        And I start the timed exam
        Then I am taken to the exam with a timer bar showing
        When I finish the exam
        Then I see the exam submitted dialog in place of the exam
        When I log back into studio as a staff member
        And change the problem's due date to be in the past
        And log back in as the original verified student
        Then I see the exam or message in accordance with the hide_after_due setting
        """
        self._setup_and_take_timed_exam(hide_after_due)

        LogoutPage(self.browser).visit()
        self._auto_auth("STAFF_TESTER", "*****@*****.**", True)
        self.course_outline.visit()
        last_week = (datetime.today() - timedelta(days=7)).strftime("%m/%d/%Y")
        self.course_outline.change_problem_due_date(last_week)

        LogoutPage(self.browser).visit()
        self._auto_auth(self.USERNAME, self.EMAIL, False)
        self.courseware_page.visit()
        self.assertEqual(self.courseware_page.has_submitted_exam_message(),
                         hide_after_due)

    def test_masquerade_visibility_override(self):
        """
        Given that a timed exam problem exists in the course
        And a student has taken that exam
        And that exam is hidden to the student
        And I am a staff user masquerading as the student
        Then I should be able to see the exam content
        """
        self._setup_and_take_timed_exam()

        LogoutPage(self.browser).visit()
        self._auto_auth("STAFF_TESTER", "*****@*****.**", True)
        self.courseware_page.visit()
        staff_page = StaffPage(self.browser, self.course_id)
        self.assertEqual(staff_page.staff_view_mode, 'Staff')

        staff_page.set_staff_view_mode_specific_student(self.USERNAME)
        self.assertFalse(self.courseware_page.has_submitted_exam_message())

    def test_field_visiblity_with_all_exam_types(self):
        """
        Given that I am a staff member
        And I have visited the course outline page in studio.
        And the subsection edit dialog is open
        select advanced settings tab
        For each of None, Timed, Proctored, and Practice exam types
        The time allotted and review rules fields have proper visibility
        None: False, False
        Timed: True, False
        Proctored: True, True
        Practice: True, False
        """
        LogoutPage(self.browser).visit()
        self._auto_auth("STAFF_TESTER", "*****@*****.**", True)
        self.course_outline.visit()

        self.course_outline.open_subsection_settings_dialog()
        self.course_outline.select_advanced_tab()

        self.course_outline.select_none_exam()
        self.assertFalse(self.course_outline.time_allotted_field_visible())
        self.assertFalse(self.course_outline.exam_review_rules_field_visible())

        self.course_outline.select_timed_exam()
        self.assertTrue(self.course_outline.time_allotted_field_visible())
        self.assertFalse(self.course_outline.exam_review_rules_field_visible())

        self.course_outline.select_proctored_exam()
        self.assertTrue(self.course_outline.time_allotted_field_visible())
        self.assertTrue(self.course_outline.exam_review_rules_field_visible())

        self.course_outline.select_practice_exam()
        self.assertTrue(self.course_outline.time_allotted_field_visible())
        self.assertFalse(self.course_outline.exam_review_rules_field_visible())
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 CoursewareTest(UniqueCourseTest):
    """
    Test courseware.
    """
    USERNAME = "******"
    EMAIL = "*****@*****.**"

    def setUp(self):
        super(CoursewareTest, self).setUp()

        self.courseware_page = CoursewarePage(self.browser, self.course_id)
        self.course_nav = CourseNavPage(self.browser)

        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
        self.course_fix = CourseFixture(
            self.course_info['org'], self.course_info['number'],
            self.course_info['run'], self.course_info['display_name']
        )

        self.course_fix.add_children(
            XBlockFixtureDesc('chapter', 'Test Section 1').add_children(
                XBlockFixtureDesc('sequential', 'Test Subsection 1').add_children(
                    XBlockFixtureDesc('problem', 'Test Problem 1')
                )
            ),
            XBlockFixtureDesc('chapter', 'Test Section 2').add_children(
                XBlockFixtureDesc('sequential', 'Test Subsection 2').add_children(
                    XBlockFixtureDesc('problem', 'Test Problem 2')
                )
            )
        ).install()

        # Auto-auth register for the course.
        self._auto_auth(self.USERNAME, self.EMAIL, False)

    def _goto_problem_page(self):
        """
        Open problem page with assertion.
        """
        self.courseware_page.visit()
        self.problem_page = ProblemPage(self.browser)  # pylint: disable=attribute-defined-outside-init
        self.assertEqual(self.problem_page.problem_name, 'Test Problem 1')

    def _create_breadcrumb(self, index):
        """ Create breadcrumb """
        return ['Test Section {}'.format(index), 'Test Subsection {}'.format(index), 'Test Problem {}'.format(index)]

    def _auto_auth(self, username, email, staff):
        """
        Logout and login with given credentials.
        """
        AutoAuthPage(self.browser, username=username, email=email,
                     course_id=self.course_id, staff=staff).visit()

    def test_courseware(self):
        """
        Test courseware if recent visited subsection become unpublished.
        """

        # Visit problem page as a student.
        self._goto_problem_page()

        # Logout and login as a staff user.
        LogoutPage(self.browser).visit()
        self._auto_auth("STAFF_TESTER", "*****@*****.**", True)

        # Visit course outline page in studio.
        self.course_outline.visit()

        # Set release date for subsection in future.
        self.course_outline.change_problem_release_date()

        # Logout and login as a student.
        LogoutPage(self.browser).visit()
        self._auto_auth(self.USERNAME, self.EMAIL, False)

        # Visit courseware as a student.
        self.courseware_page.visit()
        # Problem name should be "Test Problem 2".
        self.assertEqual(self.problem_page.problem_name, 'Test Problem 2')

    def test_course_tree_breadcrumb(self):
        """
        Scenario: Correct course tree breadcrumb is shown.

        Given that I am a registered user
        And I visit my courseware page
        Then I should see correct course tree breadcrumb
        """
        self.courseware_page.visit()

        xblocks = self.course_fix.get_nested_xblocks(category="problem")
        for index in range(1, len(xblocks) + 1):
            self.course_nav.go_to_section('Test Section {}'.format(index), 'Test Subsection {}'.format(index))
            courseware_page_breadcrumb = self.courseware_page.breadcrumb
            expected_breadcrumb = self._create_breadcrumb(index)  # pylint: disable=no-member
            self.assertEqual(courseware_page_breadcrumb, expected_breadcrumb)
class ProctoredExamTest(UniqueCourseTest):
    """
    Test courseware.
    """
    USERNAME = "******"
    EMAIL = "*****@*****.**"

    def setUp(self):
        super(ProctoredExamTest, 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_advanced_settings({
            "enable_proctored_exams": {"value": "true"}
        })

        course_fix.add_children(
            XBlockFixtureDesc('chapter', 'Test Section 1').add_children(
                XBlockFixtureDesc('sequential', 'Test Subsection 1').add_children(
                    XBlockFixtureDesc('problem', 'Test Problem 1')
                )
            )
        ).install()

        self.track_selection_page = TrackSelectionPage(self.browser, self.course_id)
        self.payment_and_verification_flow = PaymentAndVerificationFlow(self.browser, self.course_id)
        self.immediate_verification_page = PaymentAndVerificationFlow(
            self.browser, self.course_id, entry_point='verify-now'
        )
        self.upgrade_page = PaymentAndVerificationFlow(self.browser, self.course_id, entry_point='upgrade')
        self.fake_payment_page = FakePaymentPage(self.browser, self.course_id)
        self.dashboard_page = DashboardPage(self.browser)
        self.problem_page = ProblemPage(self.browser)

        # Add a verified mode to the course
        ModeCreationPage(
            self.browser, self.course_id, mode_slug=u'verified', mode_display_name=u'Verified Certificate',
            min_price=10, suggested_prices='10,20'
        ).visit()

        # Auto-auth register for the course.
        self._auto_auth(self.USERNAME, self.EMAIL, False)

    def _auto_auth(self, username, email, staff):
        """
        Logout and login with given credentials.
        """
        AutoAuthPage(self.browser, username=username, email=email,
                     course_id=self.course_id, staff=staff).visit()

    def _login_as_a_verified_user(self):
        """
        login as a verififed user
        """

        self._auto_auth(self.USERNAME, self.EMAIL, False)

        # the track selection page cannot be visited. see the other tests to see if any prereq is there.
        # Navigate to the track selection page
        self.track_selection_page.visit()

        # Enter the payment and verification flow by choosing to enroll as verified
        self.track_selection_page.enroll('verified')

        # Proceed to the fake payment page
        self.payment_and_verification_flow.proceed_to_payment()

        # Submit payment
        self.fake_payment_page.submit_payment()

    def test_can_create_proctored_exam_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 Proctored and timed exams
        """
        LogoutPage(self.browser).visit()
        self._auto_auth("STAFF_TESTER", "*****@*****.**", True)
        self.course_outline.visit()

        self.course_outline.open_subsection_settings_dialog()
        self.assertTrue(self.course_outline.proctoring_items_are_displayed())

    def test_proctored_exam_flow(self):
        """
        Given that I am a staff member on the exam settings section
        select advanced settings tab
        When I Make the exam proctored.
        And I login as a verified student.
        And visit the courseware as a verified student.
        Then I can see an option to take the exam as a proctored exam.
        """
        LogoutPage(self.browser).visit()
        self._auto_auth("STAFF_TESTER", "*****@*****.**", True)
        self.course_outline.visit()
        self.course_outline.open_subsection_settings_dialog()

        self.course_outline.select_advanced_tab()
        self.course_outline.make_exam_proctored()

        LogoutPage(self.browser).visit()
        self._login_as_a_verified_user()

        self.courseware_page.visit()
        self.assertTrue(self.courseware_page.can_start_proctored_exam)

    def _setup_and_take_timed_exam(self, hide_after_due=False):
        """
        Helper to perform the common action "set up a timed exam as staff,
        then take it as student"
        """
        LogoutPage(self.browser).visit()
        self._auto_auth("STAFF_TESTER", "*****@*****.**", True)
        self.course_outline.visit()
        self.course_outline.open_subsection_settings_dialog()

        self.course_outline.select_advanced_tab()
        self.course_outline.make_exam_timed(hide_after_due=hide_after_due)

        LogoutPage(self.browser).visit()
        self._login_as_a_verified_user()
        self.courseware_page.visit()

        self.courseware_page.start_timed_exam()
        self.assertTrue(self.courseware_page.is_timer_bar_present)

        self.courseware_page.stop_timed_exam()
        self.assertTrue(self.courseware_page.has_submitted_exam_message())

        LogoutPage(self.browser).visit()

    @ddt.data(True, False)
    def test_timed_exam_flow(self, hide_after_due):
        """
        Given that I am a staff member on the exam settings section
        select advanced settings tab
        When I Make the exam timed.
        And I login as a verified student.
        And visit the courseware as a verified student.
        And I start the timed exam
        Then I am taken to the exam with a timer bar showing
        When I finish the exam
        Then I see the exam submitted dialog in place of the exam
        When I log back into studio as a staff member
        And change the problem's due date to be in the past
        And log back in as the original verified student
        Then I see the exam or message in accordance with the hide_after_due setting
        """
        self._setup_and_take_timed_exam(hide_after_due)

        LogoutPage(self.browser).visit()
        self._auto_auth("STAFF_TESTER", "*****@*****.**", True)
        self.course_outline.visit()
        last_week = (datetime.today() - timedelta(days=7)).strftime("%m/%d/%Y")
        self.course_outline.change_problem_due_date(last_week)

        LogoutPage(self.browser).visit()
        self._auto_auth(self.USERNAME, self.EMAIL, False)
        self.courseware_page.visit()
        self.assertEqual(self.courseware_page.has_submitted_exam_message(), hide_after_due)

    def test_masquerade_visibility_override(self):
        """
        Given that a timed exam problem exists in the course
        And a student has taken that exam
        And that exam is hidden to the student
        And I am a staff user masquerading as the student
        Then I should be able to see the exam content
        """
        self._setup_and_take_timed_exam()

        LogoutPage(self.browser).visit()
        self._auto_auth("STAFF_TESTER", "*****@*****.**", True)
        self.courseware_page.visit()
        staff_page = StaffPage(self.browser, self.course_id)
        self.assertEqual(staff_page.staff_view_mode, 'Staff')

        staff_page.set_staff_view_mode_specific_student(self.USERNAME)
        self.assertFalse(self.courseware_page.has_submitted_exam_message())

    def test_field_visiblity_with_all_exam_types(self):
        """
        Given that I am a staff member
        And I have visited the course outline page in studio.
        And the subsection edit dialog is open
        select advanced settings tab
        For each of None, Timed, Proctored, and Practice exam types
        The time allotted and review rules fields have proper visibility
        None: False, False
        Timed: True, False
        Proctored: True, True
        Practice: True, False
        """
        LogoutPage(self.browser).visit()
        self._auto_auth("STAFF_TESTER", "*****@*****.**", True)
        self.course_outline.visit()

        self.course_outline.open_subsection_settings_dialog()
        self.course_outline.select_advanced_tab()

        self.course_outline.select_none_exam()
        self.assertFalse(self.course_outline.time_allotted_field_visible())
        self.assertFalse(self.course_outline.exam_review_rules_field_visible())

        self.course_outline.select_timed_exam()
        self.assertTrue(self.course_outline.time_allotted_field_visible())
        self.assertFalse(self.course_outline.exam_review_rules_field_visible())

        self.course_outline.select_proctored_exam()
        self.assertTrue(self.course_outline.time_allotted_field_visible())
        self.assertTrue(self.course_outline.exam_review_rules_field_visible())

        self.course_outline.select_practice_exam()
        self.assertTrue(self.course_outline.time_allotted_field_visible())
        self.assertFalse(self.course_outline.exam_review_rules_field_visible())
Пример #21
0
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()
Пример #22
0
class BookmarksTestMixin(EventsTestMixin, UniqueCourseTest):
    """
    Mixin with helper methods for testing Bookmarks.
    """
    USERNAME = "******"
    EMAIL = "*****@*****.**"

    def setUp(self):
        super(BookmarksTestMixin, self).setUp()

        self.studio_course_outline_page = StudioCourseOutlinePage(
            self.browser, self.course_info['org'], self.course_info['number'],
            self.course_info['run'])

        self.courseware_page = CoursewarePage(self.browser, self.course_id)
        self.course_home_page = CourseHomePage(self.browser, self.course_id)
        self.bookmarks_page = BookmarksPage(self.browser, self.course_id)

        # 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 setup_test(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.
        AutoAuthPage(self.browser,
                     username=self.USERNAME,
                     email=self.EMAIL,
                     course_id=self.course_id).visit()

        self.courseware_page.visit()

    def create_course_fixture(self, num_chapters):
        """
        Create course fixture

        Arguments:
            num_chapters: number of chapters to create
        """
        self.course_fixture = CourseFixture(  # pylint: disable=attribute-defined-outside-init
            self.course_info['org'], self.course_info['number'],
            self.course_info['run'], self.course_info['display_name'])

        xblocks = []
        for index in range(num_chapters):
            xblocks += [
                XBlockFixtureDesc(
                    'chapter', 'TestSection{}'.format(index)).add_children(
                        XBlockFixtureDesc(
                            'sequential',
                            'TestSubsection{}'.format(index)).add_children(
                                XBlockFixtureDesc(
                                    'vertical',
                                    'TestVertical{}'.format(index))))
            ]
        self.course_fixture.add_children(*xblocks).install()

    def verify_event_data(self, event_type, event_data):
        """
        Verify emitted event data.

        Arguments:
            event_type: expected event type
            event_data: expected event data
        """
        actual_events = self.wait_for_events(
            event_filter={'event_type': event_type}, number_of_matches=1)
        self.assert_events_match(event_data, actual_events)

    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)
Пример #23
0
class AnnotatableProblemTest(UniqueCourseTest):
    """
    Tests for annotation components.
    """
    USERNAME = "******"
    EMAIL = "*****@*****.**"

    DATA_TEMPLATE = dedent("""\
        <annotatable>
            <instructions>Instruction text</instructions>
            <p>{}</p>
        </annotatable>
    """)

    ANNOTATION_TEMPLATE = dedent("""\
        Before {0}.
        <annotation title="region {0}" body="Comment {0}" highlight="yellow" problem="{0}">
            Region Contents {0}
        </annotation>
        After {0}.
    """)

    PROBLEM_TEMPLATE = dedent("""\
    <problem max_attempts="1" weight="">
      <annotationresponse>
        <annotationinput>
          <title>Question {number}</title>
          <text>Region Contents {number}</text>
          <comment>What number is this region?</comment>
          <comment_prompt>Type your response below:</comment_prompt>
          <tag_prompt>What number is this region?</tag_prompt>
          <options>
          {options}
          </options>
        </annotationinput>
      </annotationresponse>
      <solution>
        This problem is checking region {number}
      </solution>
    </problem>
    """)

    OPTION_TEMPLATE = """<option choice="{correctness}">{number}</option>"""

    def setUp(self):
        super(AnnotatableProblemTest, self).setUp()

        self.courseware_page = CoursewarePage(self.browser, self.course_id)

        # Install a course with two annotations and two annotations problems.
        course_fix = CourseFixture(
            self.course_info['org'], self.course_info['number'],
            self.course_info['run'], self.course_info['display_name']
        )

        self.annotation_count = 2
        course_fix.add_children(
            XBlockFixtureDesc('chapter', 'Test Section').add_children(
                XBlockFixtureDesc('sequential', 'Test Subsection').add_children(
                    XBlockFixtureDesc('vertical', 'Test Annotation Vertical').add_children(
                        XBlockFixtureDesc('annotatable', 'Test Annotation Module',
                                          data=self.DATA_TEMPLATE.format("\n".join(
                                              self.ANNOTATION_TEMPLATE.format(i) for i in xrange(self.annotation_count)
                                          ))),
                        XBlockFixtureDesc('problem', 'Test Annotation Problem 0',
                                          data=self.PROBLEM_TEMPLATE.format(number=0, options="\n".join(
                                              self.OPTION_TEMPLATE.format(
                                                  number=k,
                                                  correctness=_correctness(k, 0))
                                              for k in xrange(self.annotation_count)
                                          ))),
                        XBlockFixtureDesc('problem', 'Test Annotation Problem 1',
                                          data=self.PROBLEM_TEMPLATE.format(number=1, options="\n".join(
                                              self.OPTION_TEMPLATE.format(
                                                  number=k,
                                                  correctness=_correctness(k, 1))
                                              for k in xrange(self.annotation_count)
                                          )))
                    )
                )
            )
        ).install()

        # Auto-auth register for the course.
        AutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL,
                     course_id=self.course_id, staff=False).visit()

    def _goto_annotation_component_page(self):
        """
        Open annotation component page with assertion.
        """
        self.courseware_page.visit()
        annotation_component_page = AnnotationComponentPage(self.browser)
        self.assertEqual(
            annotation_component_page.component_name, 'Test Annotation Module'.format()
        )
        return annotation_component_page

    def test_annotation_component(self):
        """
        Test annotation components links to annotation problems.
        """

        annotation_component_page = self._goto_annotation_component_page()
        # This will avoid scrolling related problems on different browsers and instead directly jump on the problem
        disable_animations(annotation_component_page)

        for i in xrange(self.annotation_count):
            annotation_component_page.click_reply_annotation(i)
            self.assertTrue(annotation_component_page.check_scroll_to_problem())

            annotation_component_page.answer_problem()
            self.assertTrue(annotation_component_page.check_feedback())

            annotation_component_page.click_return_to_annotation()
            self.assertTrue(annotation_component_page.check_scroll_to_annotation())
Пример #24
0
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()
Пример #25
0
class ConditionalTest(UniqueCourseTest):
    """
    Test the conditional module in the lms.
    """
    shard = 23

    def setUp(self):
        super(ConditionalTest, self).setUp()

        self.courseware_page = CoursewarePage(self.browser, self.course_id)
        AutoAuthPage(
            self.browser,
            course_id=self.course_id,
            staff=False
        ).visit()

    def install_course_fixture(self, block_type='problem'):
        """
        Install a course fixture
        """
        course_fixture = CourseFixture(
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run'],
            self.course_info['display_name'],
        )
        vertical = XBlockFixtureDesc('vertical', 'Test Unit')
        # populate the course fixture with the right conditional modules
        course_fixture.add_children(
            XBlockFixtureDesc('chapter', 'Test Section').add_children(
                XBlockFixtureDesc('sequential', 'Test Subsection').add_children(
                    vertical
                )
            )
        )
        course_fixture.install()

        # Construct conditional block
        source_block = None
        conditional_attr = None
        conditional_value = None
        if block_type == 'problem':
            problem_factory = StringResponseXMLFactory()
            problem_xml = problem_factory.build_xml(
                question_text='The answer is "correct string"',
                case_sensitive=False,
                answer='correct string',
            ),
            problem = XBlockFixtureDesc('problem', 'Test Problem', data=problem_xml[0])
            source_block = problem
            conditional_attr = 'attempted'
            conditional_value = 'True'
        elif block_type == 'poll':
            poll = XBlockFixtureDesc(
                'poll_question',
                'Conditional Poll',
                question='Is this a good poll?',
                answers=[
                    {'id': 'yes', 'text': POLL_ANSWER},
                    {'id': 'no', 'text': 'Of course not!'}
                ],
            )
            conditional_attr = 'poll_answer'
            conditional_value = 'yes'
            source_block = poll
        else:
            raise NotImplementedError()

        course_fixture.create_xblock(vertical.locator, source_block)
        # create conditional
        conditional = XBlockFixtureDesc(
            'conditional',
            'Test Conditional',
            sources_list=[source_block.locator],
            conditional_attr=conditional_attr,
            conditional_value=conditional_value
        )
        result_block = XBlockFixtureDesc(
            'html', 'Conditional Contents',
            data='<html><div class="hidden-contents">Hidden Contents</p></html>'
        )
        course_fixture.create_xblock(vertical.locator, conditional)
        course_fixture.create_xblock(conditional.locator, result_block)

    def test_conditional_hides_content(self):
        self.install_course_fixture()
        self.courseware_page.visit()
        conditional_page = ConditionalPage(self.browser)
        self.assertFalse(conditional_page.is_content_visible())

    def test_conditional_displays_content(self):
        self.install_course_fixture()
        self.courseware_page.visit()
        # Answer the problem
        problem_page = ProblemPage(self.browser)
        problem_page.fill_answer('correct string')
        problem_page.click_submit()
        # The conditional does not update on its own, so we need to reload the page.
        self.courseware_page.visit()
        # Verify that we can see the content.
        conditional_page = ConditionalPage(self.browser)
        self.assertTrue(conditional_page.is_content_visible())

    def test_conditional_handles_polls(self):
        self.install_course_fixture(block_type='poll')
        self.courseware_page.visit()
        # Fill in the conditional page poll
        conditional_page = ConditionalPage(self.browser)
        conditional_page.fill_in_poll()
        # The conditional does not update on its own, so we need to reload the page.
        self.courseware_page.visit()
        self.assertTrue(conditional_page.is_content_visible())
class ContentLicenseTest(StudioCourseTest):
    """
    Tests for course-level licensing (that is, setting the license,
    for an entire course's content, to All Rights Reserved or Creative Commons)
    """
    def setUp(self):  # pylint: disable=arguments-differ
        super(ContentLicenseTest, self).setUp()
        self.outline_page = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )
        self.settings_page = SettingsPage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )
        self.lms_courseware = CoursewarePage(
            self.browser,
            self.course_id,
        )
        self.settings_page.visit()

    def test_empty_license(self):
        """
        When I visit the Studio settings page,
        I see that the course license is "All Rights Reserved" by default.
        Then I visit the LMS courseware page,
        and I see that the default course license is displayed.
        """
        self.assertEqual(self.settings_page.course_license, "All Rights Reserved")
        self.lms_courseware.visit()
        self.assertEqual(self.lms_courseware.course_license, "© All Rights Reserved")

    def test_arr_license(self):
        """
        When I visit the Studio settings page,
        and I set the course license to "All Rights Reserved",
        and I refresh the page,
        I see that the course license is "All Rights Reserved".
        Then I visit the LMS courseware page,
        and I see that the course license is "All Rights Reserved".
        """
        self.settings_page.course_license = "All Rights Reserved"
        self.settings_page.save_changes()
        self.settings_page.refresh_and_wait_for_load()
        self.assertEqual(self.settings_page.course_license, "All Rights Reserved")

        self.lms_courseware.visit()
        self.assertEqual(self.lms_courseware.course_license, "© All Rights Reserved")

    def test_cc_license(self):
        """
        When I visit the Studio settings page,
        and I set the course license to "Creative Commons",
        and I refresh the page,
        I see that the course license is "Creative Commons".
        Then I visit the LMS courseware page,
        and I see that the course license is "Some Rights Reserved".
        """
        self.settings_page.course_license = "Creative Commons"
        self.settings_page.save_changes()
        self.settings_page.refresh_and_wait_for_load()
        self.assertEqual(self.settings_page.course_license, "Creative Commons")

        self.lms_courseware.visit()
        # The course_license text will include a bunch of screen reader text to explain
        # the selected options
        self.assertIn("Some Rights Reserved", self.lms_courseware.course_license)
Пример #27
0
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)
Пример #28
0
class BookmarksTestMixin(EventsTestMixin, UniqueCourseTest):
    """
    Mixin with helper methods for testing Bookmarks.
    """
    USERNAME = "******"
    EMAIL = "*****@*****.**"

    def setUp(self):
        super(BookmarksTestMixin, self).setUp()

        self.studio_course_outline_page = StudioCourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )

        self.courseware_page = CoursewarePage(self.browser, self.course_id)
        self.course_home_page = CourseHomePage(self.browser, self.course_id)
        self.bookmarks_page = BookmarksPage(self.browser, self.course_id)

        # 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 setup_test(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.
        AutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id).visit()

        self.courseware_page.visit()

    def create_course_fixture(self, num_chapters):
        """
        Create course fixture

        Arguments:
            num_chapters: number of chapters to create
        """
        self.course_fixture = CourseFixture(  # pylint: disable=attribute-defined-outside-init
            self.course_info['org'], self.course_info['number'],
            self.course_info['run'], self.course_info['display_name']
        )

        xblocks = []
        for index in range(num_chapters):
            xblocks += [
                XBlockFixtureDesc('chapter', 'TestSection{}'.format(index)).add_children(
                    XBlockFixtureDesc('sequential', 'TestSubsection{}'.format(index)).add_children(
                        XBlockFixtureDesc('vertical', 'TestVertical{}'.format(index))
                    )
                )
            ]
        self.course_fixture.add_children(*xblocks).install()

    def verify_event_data(self, event_type, event_data):
        """
        Verify emitted event data.

        Arguments:
            event_type: expected event type
            event_data: expected event data
        """
        actual_events = self.wait_for_events(event_filter={'event_type': event_type}, number_of_matches=1)
        self.assert_events_match(event_data, actual_events)

    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)
Пример #29
0
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 VideoLicenseTest(StudioCourseTest):
    """
    Tests for video module-level licensing (that is, setting the license,
    for a specific video module, to All Rights Reserved or Creative Commons)
    """
    def setUp(self):  # pylint: disable=arguments-differ
        super(VideoLicenseTest, self).setUp()

        self.lms_courseware = CoursewarePage(
            self.browser,
            self.course_id,
        )
        self.studio_course_outline = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )

    # used by StudioCourseTest.setUp()
    def populate_course_fixture(self, course_fixture):
        """
        Create a course with a single chapter.
        That chapter has a single section.
        That section has a single vertical.
        That vertical has a single video element.
        """
        video_block = XBlockFixtureDesc('video', "Test Video")
        vertical = XBlockFixtureDesc('vertical', "Test Vertical")
        vertical.add_children(video_block)
        sequential = XBlockFixtureDesc('sequential', "Test Section")
        sequential.add_children(vertical)
        chapter = XBlockFixtureDesc('chapter', "Test Chapter")
        chapter.add_children(sequential)
        self.course_fixture.add_children(chapter)

    def test_empty_license(self):
        """
        When I visit the LMS courseware,
        I can see that the video is present
        but it has no license displayed by default.
        """
        self.lms_courseware.visit()
        video = self.lms_courseware.q(css=".vert .xblock .video")
        self.assertTrue(video.is_present())
        video_license = self.lms_courseware.q(css=".vert .xblock.xmodule_VideoModule .xblock-license")
        self.assertFalse(video_license.is_present())

    def test_arr_license(self):
        """
        When I edit a video element in Studio,
        I can set an "All Rights Reserved" license on that video element.
        When I visit the LMS courseware,
        I can see that the video is present
        and that it has "All Rights Reserved" displayed for the license.
        """
        self.studio_course_outline.visit()
        subsection = self.studio_course_outline.section_at(0).subsection_at(0)
        subsection.expand_subsection()
        unit = subsection.unit_at(0)
        container_page = unit.go_to()
        container_page.edit()
        video = [xb for xb in container_page.xblocks if xb.name == "Test Video"][0]
        video.open_advanced_tab()
        video.set_license('all-rights-reserved')
        video.save_settings()
        container_page.publish_action.click()

        self.lms_courseware.visit()
        video = self.lms_courseware.q(css=".vert .xblock .video")
        self.assertTrue(video.is_present())
        video_license_css = ".vert .xblock.xmodule_VideoModule .xblock-license"
        self.lms_courseware.wait_for_element_presence(
            video_license_css, "Video module license block is present"
        )
        video_license = self.lms_courseware.q(css=video_license_css)
        self.assertEqual(video_license.text[0], "© All Rights Reserved")

    def test_cc_license(self):
        """
        When I edit a video element in Studio,
        I can set a "Creative Commons" license on that video element.
        When I visit the LMS courseware,
        I can see that the video is present
        and that it has "Some Rights Reserved" displayed for the license.
        """
        self.studio_course_outline.visit()
        subsection = self.studio_course_outline.section_at(0).subsection_at(0)
        subsection.expand_subsection()
        unit = subsection.unit_at(0)
        container_page = unit.go_to()
        container_page.edit()
        video = [xb for xb in container_page.xblocks if xb.name == "Test Video"][0]
        video.open_advanced_tab()
        video.set_license('creative-commons')
        video.save_settings()
        container_page.publish_action.click()

        self.lms_courseware.visit()
        video = self.lms_courseware.q(css=".vert .xblock .video")
        self.assertTrue(video.is_present())
        video_license_css = ".vert .xblock.xmodule_VideoModule .xblock-license"
        self.lms_courseware.wait_for_element_presence(
            video_license_css, "Video module license block is present"
        )
        video_license = self.lms_courseware.q(css=video_license_css)
        self.assertIn("Some Rights Reserved", video_license.text[0])
class CrowdsourcehinterProblemTest(UniqueCourseTest):
    """
    Test scenario for the hinter.
    """
    USERNAME = "******"
    EMAIL = "*****@*****.**"

    def setUp(self):
        super(CrowdsourcehinterProblemTest, self).setUp()

        self.courseware_page = CoursewarePage(self.browser, self.course_id)

        # 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']
        )
        problem_data = dedent('''
            <problem>
                <p>A text input problem accepts a line of text from the student, and evaluates the input for correctness based on an expected answer.</p>
                <p>The answer is correct if it matches every character of the expected answer. This can be a problem with international spelling, dates, or anything where the format of the answer is not clear.</p>
                <p>Which US state has Lansing as its capital?</p>
                <stringresponse answer="Michigan" type="ci" >
                      <textline label="Which US state has Lansing as its capital?" size="20"/>
                </stringresponse>
                <solution>
                <div class="detailed-solution">
                <p>Explanation</p>
                <p>Lansing is the capital of Michigan, although it is not Michigan's largest city, or even the seat of the county in which it resides.</p>
                </div>
                </solution>
            </problem>
        ''')

        children = XBlockFixtureDesc('chapter', 'Test Section').add_children(
            XBlockFixtureDesc('sequential', 'Test Subsection').add_children(
                XBlockFixtureDesc('vertical', 'Test Unit').add_children(
                    XBlockFixtureDesc('problem', 'text input problem', data=problem_data),
                    XBlockFixtureDesc('crowdsourcehinter', 'test crowdsourcehinter')
                )
            )
        )

        course_fix.add_children(children).install()

        # Auto-auth register for the course.
        AutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL,
                     course_id=self.course_id, staff=False).visit()

    def _goto_csh_problem_page(self):
        """
        Visit the page courseware page containing the hinter
        """
        self.courseware_page.visit()
        csh_problem_page = CrowdsourcehinterProblemPage(self.browser)
        self.assertGreater(len(self.browser.find_elements_by_class_name('crowdsourcehinter_block')), 0)
        return csh_problem_page

    def test_student_hint_workflow(self):
        """
        Test the basic workflow of a student recieving hints. The student should submit an incorrect answer and
        receive a hint (in this case no hint since none are set), be able to rate that hint, see a different UX
        after submitting a correct answer, and be capable of contributing a new hint to the system.
        """
        csh_problem_page = self._goto_csh_problem_page()

        csh_problem_page.submit_text_answer("michigann")
        csh_problem_page.wait_for_ajax()
        self.assertEqual(csh_problem_page.get_hint_text()[0], u"Hint: Sorry, there are no hints for this answer.")

        self.assertGreater(len(self.browser.find_elements_by_class_name('csh_rate_hint')), 0)
        csh_problem_page.rate_hint()
        csh_problem_page.wait_for_ajax()

        csh_problem_page.submit_text_answer("michigan")
        csh_problem_page.wait_for_ajax()
        self.assertGreater(len(self.browser.find_elements_by_id('show_hint_rating_ux')), 0)

        csh_problem_page.submit_new_hint("new hint text")
Пример #32
0
class ContentLicenseTest(StudioCourseTest):
    """
    Tests for course-level licensing (that is, setting the license,
    for an entire course's content, to All Rights Reserved or Creative Commons)
    """
    def setUp(self):  # pylint: disable=arguments-differ
        super(ContentLicenseTest, self).setUp()
        self.outline_page = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )
        self.settings_page = SettingsPage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )
        self.lms_courseware = CoursewarePage(
            self.browser,
            self.course_id,
        )
        self.settings_page.visit()

    def test_empty_license(self):
        """
        When I visit the Studio settings page,
        I see that the course license is "All Rights Reserved" by default.
        Then I visit the LMS courseware page,
        and I see that the default course license is displayed.
        """
        self.assertEqual(self.settings_page.course_license, "All Rights Reserved")
        self.lms_courseware.visit()
        self.assertEqual(self.lms_courseware.course_license, "© All Rights Reserved")

    def test_arr_license(self):
        """
        When I visit the Studio settings page,
        and I set the course license to "All Rights Reserved",
        and I refresh the page,
        I see that the course license is "All Rights Reserved".
        Then I visit the LMS courseware page,
        and I see that the course license is "All Rights Reserved".
        """
        self.settings_page.course_license = "All Rights Reserved"
        self.settings_page.save_changes()
        self.settings_page.refresh_and_wait_for_load()
        self.assertEqual(self.settings_page.course_license, "All Rights Reserved")

        self.lms_courseware.visit()
        self.assertEqual(self.lms_courseware.course_license, "© All Rights Reserved")

    def test_cc_license(self):
        """
        When I visit the Studio settings page,
        and I set the course license to "Creative Commons",
        and I refresh the page,
        I see that the course license is "Creative Commons".
        Then I visit the LMS courseware page,
        and I see that the course license is "Some Rights Reserved".
        """
        self.settings_page.course_license = "Creative Commons"
        self.settings_page.save_changes()
        self.settings_page.refresh_and_wait_for_load()
        self.assertEqual(self.settings_page.course_license, "Creative Commons")

        self.lms_courseware.visit()
        # The course_license text will include a bunch of screen reader text to explain
        # the selected options
        self.assertIn("Some Rights Reserved", self.lms_courseware.course_license)
Пример #33
0
class ConditionalTest(UniqueCourseTest):
    """
    Test the conditional module in the lms.
    """
    shard = 23

    def setUp(self):
        super(ConditionalTest, self).setUp()

        self.courseware_page = CoursewarePage(self.browser, self.course_id)
        AutoAuthPage(self.browser, course_id=self.course_id,
                     staff=False).visit()

    def install_course_fixture(self, block_type='problem'):
        """
        Install a course fixture
        """
        course_fixture = CourseFixture(
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run'],
            self.course_info['display_name'],
        )
        vertical = XBlockFixtureDesc('vertical', 'Test Unit')
        # populate the course fixture with the right conditional modules
        course_fixture.add_children(
            XBlockFixtureDesc('chapter', 'Test Section').add_children(
                XBlockFixtureDesc('sequential',
                                  'Test Subsection').add_children(vertical)))
        course_fixture.install()

        # Construct conditional block
        source_block = None
        conditional_attr = None
        conditional_value = None
        if block_type == 'problem':
            problem_factory = StringResponseXMLFactory()
            problem_xml = problem_factory.build_xml(
                question_text='The answer is "correct string"',
                case_sensitive=False,
                answer='correct string',
            ),
            problem = XBlockFixtureDesc('problem',
                                        'Test Problem',
                                        data=problem_xml[0])
            source_block = problem
            conditional_attr = 'attempted'
            conditional_value = 'True'
        elif block_type == 'poll':
            poll = XBlockFixtureDesc(
                'poll_question',
                'Conditional Poll',
                question='Is this a good poll?',
                answers=[{
                    'id': 'yes',
                    'text': POLL_ANSWER
                }, {
                    'id': 'no',
                    'text': 'Of course not!'
                }],
            )
            conditional_attr = 'poll_answer'
            conditional_value = 'yes'
            source_block = poll
        else:
            raise NotImplementedError()

        course_fixture.create_xblock(vertical.locator, source_block)
        # create conditional
        conditional = XBlockFixtureDesc('conditional',
                                        'Test Conditional',
                                        sources_list=[source_block.locator],
                                        conditional_attr=conditional_attr,
                                        conditional_value=conditional_value)
        result_block = XBlockFixtureDesc(
            'html',
            'Conditional Contents',
            data='<html><div class="hidden-contents">Hidden Contents</p></html>'
        )
        course_fixture.create_xblock(vertical.locator, conditional)
        course_fixture.create_xblock(conditional.locator, result_block)

    def test_conditional_hides_content(self):
        self.install_course_fixture()
        self.courseware_page.visit()
        conditional_page = ConditionalPage(self.browser)
        self.assertFalse(conditional_page.is_content_visible())

    def test_conditional_displays_content(self):
        self.install_course_fixture()
        self.courseware_page.visit()
        # Answer the problem
        problem_page = ProblemPage(self.browser)
        problem_page.fill_answer('correct string')
        problem_page.click_submit()
        # The conditional does not update on its own, so we need to reload the page.
        self.courseware_page.visit()
        # Verify that we can see the content.
        conditional_page = ConditionalPage(self.browser)
        self.assertTrue(conditional_page.is_content_visible())

    def test_conditional_handles_polls(self):
        self.install_course_fixture(block_type='poll')
        self.courseware_page.visit()
        # Fill in the conditional page poll
        conditional_page = ConditionalPage(self.browser)
        conditional_page.fill_in_poll()
        # The conditional does not update on its own, so we need to reload the page.
        self.courseware_page.visit()
        self.assertTrue(conditional_page.is_content_visible())
Пример #34
0
class AnnotatableProblemTest(UniqueCourseTest):
    """
    Tests for annotation components.
    """
    USERNAME = "******"
    EMAIL = "*****@*****.**"

    DATA_TEMPLATE = dedent(u"""\
        <annotatable>
            <instructions>Instruction text</instructions>
            <p>{}</p>
        </annotatable>
    """)

    ANNOTATION_TEMPLATE = dedent(u"""\
        Before {0}.
        <annotation title="region {0}" body="Comment {0}" highlight="yellow" problem="{0}">
            Region Contents {0}
        </annotation>
        After {0}.
    """)

    PROBLEM_TEMPLATE = dedent(u"""\
    <problem max_attempts="1" weight="">
      <annotationresponse>
        <annotationinput>
          <title>Question {number}</title>
          <text>Region Contents {number}</text>
          <comment>What number is this region?</comment>
          <comment_prompt>Type your response below:</comment_prompt>
          <tag_prompt>What number is this region?</tag_prompt>
          <options>
          {options}
          </options>
        </annotationinput>
      </annotationresponse>
      <solution>
        This problem is checking region {number}
      </solution>
    </problem>
    """)

    OPTION_TEMPLATE = u"""<option choice="{correctness}">{number}</option>"""

    def setUp(self):
        super(AnnotatableProblemTest, self).setUp()

        self.courseware_page = CoursewarePage(self.browser, self.course_id)

        # Install a course with two annotations and two annotations problems.
        course_fix = CourseFixture(self.course_info['org'],
                                   self.course_info['number'],
                                   self.course_info['run'],
                                   self.course_info['display_name'])

        self.annotation_count = 2
        course_fix.add_children(
            XBlockFixtureDesc('chapter', 'Test Section').add_children(
                XBlockFixtureDesc('sequential', 'Test Subsection').
                add_children(
                    XBlockFixtureDesc(
                        'vertical', 'Test Annotation Vertical').add_children(
                            XBlockFixtureDesc(
                                'annotatable',
                                'Test Annotation Module',
                                data=self.DATA_TEMPLATE.format("\n".join(
                                    self.ANNOTATION_TEMPLATE.format(i)
                                    for i in range(self.annotation_count)))),
                            XBlockFixtureDesc(
                                'problem',
                                'Test Annotation Problem 0',
                                data=self.PROBLEM_TEMPLATE.format(
                                    number=0,
                                    options="\n".join(
                                        self.OPTION_TEMPLATE.format(
                                            number=k,
                                            correctness=_correctness(k, 0)) for
                                        k in range(self.annotation_count)))),
                            XBlockFixtureDesc(
                                'problem',
                                'Test Annotation Problem 1',
                                data=self.PROBLEM_TEMPLATE.format(
                                    number=1,
                                    options="\n".join(
                                        self.OPTION_TEMPLATE.format(
                                            number=k,
                                            correctness=_correctness(k, 1))
                                        for k in range(self.annotation_count)
                                    ))))))).install()

        # Auto-auth register for the course.
        AutoAuthPage(self.browser,
                     username=self.USERNAME,
                     email=self.EMAIL,
                     course_id=self.course_id,
                     staff=False).visit()

    def _goto_annotation_component_page(self):
        """
        Open annotation component page with assertion.
        """
        self.courseware_page.visit()
        annotation_component_page = AnnotationComponentPage(self.browser)
        self.assertEqual(annotation_component_page.component_name,
                         'Test Annotation Module'.format())
        return annotation_component_page

    def test_annotation_component(self):
        """
        Test annotation components links to annotation problems.
        """

        annotation_component_page = self._goto_annotation_component_page()
        # This will avoid scrolling related problems on different browsers and instead directly jump on the problem
        disable_animations(annotation_component_page)

        for i in range(self.annotation_count):
            annotation_component_page.click_reply_annotation(i)
            self.assertTrue(
                annotation_component_page.check_scroll_to_problem())

            annotation_component_page.answer_problem()
            self.assertTrue(annotation_component_page.check_feedback())

            annotation_component_page.click_return_to_annotation()
            self.assertTrue(
                annotation_component_page.check_scroll_to_annotation())
Пример #35
0
class CrowdsourcehinterProblemTest(UniqueCourseTest):
    """
    Test scenario for the hinter.
    """
    shard = 21
    USERNAME = "******"
    EMAIL = "*****@*****.**"

    def setUp(self):
        super(CrowdsourcehinterProblemTest, self).setUp()

        self.courseware_page = CoursewarePage(self.browser, self.course_id)

        # 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'])
        problem_data = dedent('''
            <problem>
                <p>A text input problem accepts a line of text from the student, and evaluates the input for correctness based on an expected answer.</p>
                <p>The answer is correct if it matches every character of the expected answer. This can be a problem with international spelling, dates, or anything where the format of the answer is not clear.</p>
                <p>Which US state has Lansing as its capital?</p>
                <stringresponse answer="Michigan" type="ci" >
                      <textline label="Which US state has Lansing as its capital?" size="20"/>
                </stringresponse>
                <solution>
                <div class="detailed-solution">
                <p>Explanation</p>
                <p>Lansing is the capital of Michigan, although it is not Michigan's largest city, or even the seat of the county in which it resides.</p>
                </div>
                </solution>
            </problem>
        ''')

        children = XBlockFixtureDesc('chapter', 'Test Section').add_children(
            XBlockFixtureDesc('sequential', 'Test Subsection').add_children(
                XBlockFixtureDesc('vertical', 'Test Unit').add_children(
                    XBlockFixtureDesc('problem',
                                      'text input problem',
                                      data=problem_data),
                    XBlockFixtureDesc('crowdsourcehinter',
                                      'test crowdsourcehinter'))))

        course_fix.add_children(children).install()

        # Auto-auth register for the course.
        AutoAuthPage(self.browser,
                     username=self.USERNAME,
                     email=self.EMAIL,
                     course_id=self.course_id,
                     staff=False).visit()

    def _goto_csh_problem_page(self):
        """
        Visit the page courseware page containing the hinter
        """
        self.courseware_page.visit()
        csh_problem_page = CrowdsourcehinterProblemPage(self.browser)
        self.assertGreater(
            len(
                self.browser.find_elements_by_class_name(
                    'crowdsourcehinter_block')), 0)
        return csh_problem_page

    def test_student_hint_workflow(self):
        """
        Test the basic workflow of a student recieving hints. The student should submit an incorrect answer and
        receive a hint (in this case no hint since none are set), be able to rate that hint, see a different UX
        after submitting a correct answer, and be capable of contributing a new hint to the system.
        """
        csh_problem_page = self._goto_csh_problem_page()

        csh_problem_page.submit_text_answer("michigann")
        csh_problem_page.wait_for_ajax()
        self.assertEqual(csh_problem_page.get_hint_text()[0],
                         u"Hint: Sorry, there are no hints for this answer.")

        self.assertGreater(
            len(self.browser.find_elements_by_class_name('csh_rate_hint')), 0)
        csh_problem_page.rate_hint()
        csh_problem_page.wait_for_ajax()

        csh_problem_page.submit_text_answer("michigan")
        csh_problem_page.wait_for_ajax()
        self.assertGreater(
            len(self.browser.find_elements_by_id('show_hint_rating_ux')), 0)

        csh_problem_page.submit_new_hint("new hint text")
Пример #36
0
class LibraryContentTestBase(UniqueCourseTest):
    """ Base class for library content block tests """
    USERNAME = "******"
    EMAIL = "*****@*****.**"

    STAFF_USERNAME = "******"
    STAFF_EMAIL = "*****@*****.**"
    shard = 10

    def populate_library_fixture(self, library_fixture):
        """
        To be overwritten by subclassed tests. Used to install a library to
        run tests on.
        """

    def setUp(self):
        """
        Set up library, course and library content XBlock
        """
        super(LibraryContentTestBase, self).setUp()

        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'])

        self.library_fixture = LibraryFixture(
            'test_org', self.unique_id,
            u'Test Library {}'.format(self.unique_id))
        self.populate_library_fixture(self.library_fixture)

        self.library_fixture.install()
        self.library_info = self.library_fixture.library_info
        self.library_key = self.library_fixture.library_key

        # Install a course with library content xblock
        self.course_fixture = CourseFixture(self.course_info['org'],
                                            self.course_info['number'],
                                            self.course_info['run'],
                                            self.course_info['display_name'])

        library_content_metadata = {
            'source_library_id': six.text_type(self.library_key),
            'mode': 'random',
            'max_count': 1,
        }

        self.lib_block = XBlockFixtureDesc('library_content',
                                           "Library Content",
                                           metadata=library_content_metadata)

        self.course_fixture.add_children(
            XBlockFixtureDesc('chapter', SECTION_NAME).add_children(
                XBlockFixtureDesc('sequential', SUBSECTION_NAME).add_children(
                    XBlockFixtureDesc('vertical', UNIT_NAME).add_children(
                        self.lib_block))))

        self.course_fixture.install()

    def _change_library_content_settings(self, count=1, capa_type=None):
        """
        Performs library block refresh in Studio, configuring it to show {count} children
        """
        unit_page = self._go_to_unit_page(True)
        library_container_block = StudioLibraryContainerXBlockWrapper.from_xblock_wrapper(
            unit_page.xblocks[1])
        library_container_block.edit()
        editor = StudioLibraryContentEditor(self.browser,
                                            library_container_block.locator)
        editor.count = count
        if capa_type is not None:
            editor.capa_type = capa_type
        editor.save()
        self._go_to_unit_page(change_login=False)
        unit_page.wait_for_page()
        unit_page.publish()
        self.assertIn("Published and Live", unit_page.publish_title)

    @property
    def library_xblocks_texts(self):
        """
        Gets texts of all xblocks in library
        """
        return frozenset(child.data for child in self.library_fixture.children)

    def _go_to_unit_page(self, change_login=True):
        """
        Open unit page in Studio
        """
        if change_login:
            LogoutPage(self.browser).visit()
            self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True)
        self.studio_course_outline.visit()

        subsection = self.studio_course_outline.section(
            SECTION_NAME).subsection(SUBSECTION_NAME)
        return subsection.expand_subsection().unit(UNIT_NAME).go_to()

    def _goto_library_block_page(self, block_id=None):
        """
        Open library page in LMS
        """
        self.courseware_page.visit()
        paragraphs = self.courseware_page.q(css='.course-content p').results
        if not paragraphs:
            course_home_page = CourseHomePage(self.browser, self.course_id)
            course_home_page.visit()
            course_home_page.outline.go_to_section_by_index(0, 0)
        block_id = block_id if block_id is not None else self.lib_block.locator
        #pylint: disable=attribute-defined-outside-init
        self.library_content_page = LibraryContentXBlockWrapper(
            self.browser, block_id)
        self.library_content_page.wait_for_page()

    def _auto_auth(self, username, email, staff):
        """
        Logout and login with given credentials.
        """
        AutoAuthPage(self.browser,
                     username=username,
                     email=email,
                     course_id=self.course_id,
                     staff=staff).visit()
class VideoLicenseTest(StudioCourseTest):
    """
    Tests for video module-level licensing (that is, setting the license,
    for a specific video module, to All Rights Reserved or Creative Commons)
    """
    def setUp(self):  # pylint: disable=arguments-differ
        super(VideoLicenseTest, self).setUp()

        self.lms_courseware = CoursewarePage(
            self.browser,
            self.course_id,
        )
        self.studio_course_outline = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )

    # used by StudioCourseTest.setUp()
    def populate_course_fixture(self, course_fixture):
        """
        Create a course with a single chapter.
        That chapter has a single section.
        That section has a single vertical.
        That vertical has a single video element.
        """
        video_block = XBlockFixtureDesc('video', "Test Video")
        vertical = XBlockFixtureDesc('vertical', "Test Vertical")
        vertical.add_children(video_block)
        sequential = XBlockFixtureDesc('sequential', "Test Section")
        sequential.add_children(vertical)
        chapter = XBlockFixtureDesc('chapter', "Test Chapter")
        chapter.add_children(sequential)
        self.course_fixture.add_children(chapter)

    def test_empty_license(self):
        """
        When I visit the LMS courseware,
        I can see that the video is present
        but it has no license displayed by default.
        """
        self.lms_courseware.visit()
        video = self.lms_courseware.q(css=".vert .xblock .video")
        self.assertTrue(video.is_present())
        video_license = self.lms_courseware.q(css=".vert .xblock.xmodule_VideoModule .xblock-license")
        self.assertFalse(video_license.is_present())

    def test_arr_license(self):
        """
        When I edit a video element in Studio,
        I can set an "All Rights Reserved" license on that video element.
        When I visit the LMS courseware,
        I can see that the video is present
        and that it has "All Rights Reserved" displayed for the license.
        """
        self.studio_course_outline.visit()
        subsection = self.studio_course_outline.section_at(0).subsection_at(0)
        subsection.expand_subsection()
        unit = subsection.unit_at(0)
        container_page = unit.go_to()
        container_page.edit()
        video = [xb for xb in container_page.xblocks if xb.name == "Test Video"][0]
        video.open_advanced_tab()
        video.set_license('all-rights-reserved')
        video.save_settings()
        container_page.publish_action.click()

        self.lms_courseware.visit()
        video = self.lms_courseware.q(css=".vert .xblock .video")
        self.assertTrue(video.is_present())
        video_license_css = ".vert .xblock.xmodule_VideoModule .xblock-license"
        self.lms_courseware.wait_for_element_presence(
            video_license_css, "Video module license block is present"
        )
        video_license = self.lms_courseware.q(css=video_license_css)
        self.assertEqual(video_license.text[0], "© All Rights Reserved")

    def test_cc_license(self):
        """
        When I edit a video element in Studio,
        I can set a "Creative Commons" license on that video element.
        When I visit the LMS courseware,
        I can see that the video is present
        and that it has "Some Rights Reserved" displayed for the license.
        """
        self.studio_course_outline.visit()
        subsection = self.studio_course_outline.section_at(0).subsection_at(0)
        subsection.expand_subsection()
        unit = subsection.unit_at(0)
        container_page = unit.go_to()
        container_page.edit()
        video = [xb for xb in container_page.xblocks if xb.name == "Test Video"][0]
        video.open_advanced_tab()
        video.set_license('creative-commons')
        video.save_settings()
        container_page.publish_action.click()

        self.lms_courseware.visit()
        video = self.lms_courseware.q(css=".vert .xblock .video")
        self.assertTrue(video.is_present())
        video_license_css = ".vert .xblock.xmodule_VideoModule .xblock-license"
        self.lms_courseware.wait_for_element_presence(
            video_license_css, "Video module license block is present"
        )
        video_license = self.lms_courseware.q(css=video_license_css)
        self.assertIn("Some Rights Reserved", video_license.text[0])