def test_entrance_exam_has_unit_button(self):
        """
        Test that entrance exam should be created after checking the 'enable entrance exam' checkbox.
        And user has option to add units only instead of any Subsection.
        """
        self.settings_detail.require_entrance_exam(required=True)
        self.settings_detail.save_changes()

        # getting the course outline page.
        course_outline_page = CourseOutlinePage(
            self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run']
        )
        course_outline_page.visit()
        course_outline_page.wait_for_ajax()

        # button with text 'New Unit' should be present.
        self.assertTrue(element_has_text(
            page=course_outline_page,
            css_selector='.add-item a.button-new',
            text='New Unit'
        ))

        # button with text 'New Subsection' should not be present.
        self.assertFalse(element_has_text(
            page=course_outline_page,
            css_selector='.add-item a.button-new',
            text='New Subsection'
        ))
    def test_enable_entrance_exam_for_course(self):
        """
        Test that entrance exam should be created after checking the 'enable entrance exam' checkbox.
        And also that the entrance exam is destroyed after deselecting the checkbox.
        """
        self.settings_detail.require_entrance_exam(required=True)
        self.settings_detail.save_changes()

        # getting the course outline page.
        course_outline_page = CourseOutlinePage(
            self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run']
        )
        course_outline_page.visit()

        # title with text 'Entrance Exam' should be present on page.
        self.assertTrue(element_has_text(
            page=course_outline_page,
            css_selector='span.section-title',
            text='Entrance Exam'
        ))

        # Delete the currently created entrance exam.
        self.settings_detail.visit()
        self.settings_detail.require_entrance_exam(required=False)
        self.settings_detail.save_changes()

        course_outline_page.visit()
        self.assertFalse(element_has_text(
            page=course_outline_page,
            css_selector='span.section-title',
            text='Entrance Exam'
        ))
 def setUp(self):
     super(DiscussionPreviewTest, self).setUp()
     cop = CourseOutlinePage(
         self.browser, self.course_info["org"], self.course_info["number"], self.course_info["run"]
     )
     cop.visit()
     self.unit = cop.section("Test Section").subsection("Test Subsection").expand_subsection().unit("Test Unit")
     self.unit.go_to()
Пример #4
0
 def setUp(self):
     super(DiscussionPreviewTest, self).setUp()
     cop = CourseOutlinePage(
         self.browser,
         self.course_info['org'],
         self.course_info['number'],
         self.course_info['run']
     )
     cop.visit()
     self.unit = cop.section('Test Section').subsection('Test Subsection').expand_subsection().unit('Test Unit')
     self.unit.go_to()
Пример #5
0
class CourseOutlineHelpTest(StudioCourseTest):
    """
    Tests help links on course outline page.
    """
    def setUp(self):  # pylint: disable=arguments-differ
        super(CourseOutlineHelpTest, self).setUp()
        self.course_outline_page = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )
        self.course_outline_page.visit()

    @skip("This scenario depends upon TNL-5460")
    def test_course_outline_nav_help(self):
        """
        Scenario: Help link in navigation bar is working on Course Outline page
        Given that I am on the Course Outline page
        And I want help about the process
        And I click the 'Help' in the navigation bar
        Then Help link should open.
        And help url should end with 'developing_course/course_outline.html'
        """
        href = 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course' \
               '/en/open-release-ficus.master/developing_course/course_outline.html'

        # Assert that help link is correct.
        assert_nav_help_link(
            test=self,
            page=self.course_outline_page,
            href=href
        )

    def test_course_outline_side_bar_help(self):
        """
        Scenario: Help link in sidebar links is working on Course Outline page
        Given that I am on the Course Outline page.
        And I want help about the process
        And I click the 'Learn more about the course outline' in the sidebar links
        Then Help link should open.
        And help url should end with 'developing_course/course_outline.html'
        """
        href = 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course' \
               '/en/open-release-ficus.master/developing_course/course_outline.html'

        # Assert that help link is correct.
        assert_side_bar_help_link(
            test=self,
            page=self.course_outline_page,
            href=href,
            help_text='Learn more about the course outline',
            index=0
        )
 def record_visit_outline(self):
     """
     Produce a HAR for loading the course outline page.
     """
     course_outline_page = CourseOutlinePage(self.browser, self.course_org, self.course_num, self.course_run)
     har_name = 'OutlinePage_{org}_{course}'.format(
         org=self.course_org,
         course=self.course_num
     )
     self.har_capturer.add_page(self.browser, har_name)
     course_outline_page.visit()
     self.har_capturer.save_har(self.browser, har_name)
Пример #7
0
class CourseOutlineHelpTest(StudioCourseTest):
    """
    Tests help links on course outline page.
    """
    def setUp(self):  # pylint: disable=arguments-differ
        super(CourseOutlineHelpTest, self).setUp()
        self.course_outline_page = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )
        self.course_outline_page.visit()

    @skip("This scenario depends upon TNL-5460")
    def test_course_outline_nav_help(self):
        """
        Scenario: Help link in navigation bar is working on Course Outline page
        Given that I am on the Course Outline page
        And I want help about the process
        And I click the 'Help' in the navigation bar
        Then Help link should open.
        And help url should be correct
        """
        expected_url = _get_expected_documentation_url('/developing_course/course_outline.html')

        # Assert that help link is correct.
        assert_nav_help_link(
            test=self,
            page=self.course_outline_page,
            href=expected_url
        )

    def test_course_outline_side_bar_help(self):
        """
        Scenario: Help link in sidebar links is working on Course Outline page
        Given that I am on the Course Outline page.
        And I want help about the process
        And I click the 'Learn more about the course outline' in the sidebar links
        Then Help link should open.
        And help url should be correct
        """
        expected_url = _get_expected_documentation_url('/developing_course/course_outline.html')

        # Assert that help link is correct.
        assert_side_bar_help_link(
            test=self,
            page=self.course_outline_page,
            href=expected_url,
            help_text='Learn more about the course outline',
            index=0
        )
 def record_visit_unit(self, section_title, subsection_title, unit_title):
     """
     Produce a HAR for loading a unit page.
     """
     course_outline_page = CourseOutlinePage(self.browser, self.course_org, self.course_num, self.course_run).visit()
     course_outline_unit = course_outline_page.section(section_title).subsection(subsection_title).expand_subsection().unit(unit_title)
     har_name = 'UnitPage_{org}_{course}'.format(
         org=self.course_org,
         course=self.course_num
     )
     self.har_capturer.add_page(self.browser, har_name)
     course_outline_unit.go_to()
     self.har_capturer.save_har(self.browser, har_name)
Пример #9
0
class ContainerBase(StudioCourseTest):
    """
    Base class for tests that do operations on the container page.
    """

    def setUp(self, is_staff=False):
        """
        Create a unique identifier for the course used in this test.
        """
        # Ensure that the superclass sets up
        super(ContainerBase, self).setUp(is_staff=is_staff)

        self.outline = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )

    def go_to_nested_container_page(self):
        """
        Go to the nested container page.
        """
        unit = self.go_to_unit_page()
        # The 0th entry is the unit page itself.
        container = unit.xblocks[1].go_to_container()
        return container

    def go_to_unit_page(self, section_name='Test Section', subsection_name='Test Subsection', unit_name='Test Unit'):
        """
        Go to the test unit page.

        If make_draft is true, the unit page will be put into draft mode.
        """
        self.outline.visit()
        subsection = self.outline.section(section_name).subsection(subsection_name)
        return subsection.expand_subsection().unit(unit_name).go_to()

    def do_action_and_verify(self, action, expected_ordering):
        """
        Perform the supplied action and then verify the resulting ordering.
        """
        container = self.go_to_nested_container_page()
        action(container)

        verify_ordering(self, container, expected_ordering)

        # Reload the page to see that the change was persisted.
        container = self.go_to_nested_container_page()
        verify_ordering(self, container, expected_ordering)
Пример #10
0
    def setUp(self):  # pylint: disable=arguments-differ
        super(SignUpAndSignInTest, self).setUp()
        self.sign_up_page = SignupPage(self.browser)
        self.login_page = LoginPage(self.browser)

        self.course_outline_page = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )
        self.course_outline_sign_in_redirect_page = CourseOutlineSignInRedirectPage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )

        self.course_fixture = CourseFixture(
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run'],
            self.course_info['display_name'],
        )
        self.user = None
    def setUp(self):
        """
        Create a unique identifier for the course used in this test.
        """
        # Ensure that the superclass sets up
        super(XBlockAcidBase, self).setUp()

        # Define a unique course identifier
        self.course_info = {
            "org": "test_org",
            "number": "course_" + self.unique_id[:5],
            "run": "test_" + self.unique_id,
            "display_name": "Test Course " + self.unique_id,
        }

        self.outline = CourseOutlinePage(
            self.browser, self.course_info["org"], self.course_info["number"], self.course_info["run"]
        )

        self.course_id = "{org}.{number}.{run}".format(**self.course_info)

        self.setup_fixtures()

        self.auth_page = AutoAuthPage(
            self.browser,
            staff=False,
            username=self.user.get("username"),
            email=self.user.get("email"),
            password=self.user.get("password"),
        )
        self.auth_page.visit()
Пример #12
0
    def setUp(self):
        """
        Create search page and course content to search
        """
        # create test file in which index for this test will live
        with open(self.TEST_INDEX_FILENAME, "w+") as index_file:
            json.dump({}, index_file)
        self.addCleanup(remove_file, self.TEST_INDEX_FILENAME)

        super(CoursewareSearchTest, self).setUp()
        self.courseware_search_page = CoursewareSearchPage(self.browser, self.course_id)

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

        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', 'Section 1').add_children(
                XBlockFixtureDesc('sequential', 'Subsection 1')
            )
        ).add_children(
            XBlockFixtureDesc('chapter', 'Section 2').add_children(
                XBlockFixtureDesc('sequential', 'Subsection 2')
            )
        ).install()
    def setUp(self):
        """
        Create a unique identifier for the course used in this test.
        """
        # Ensure that the superclass sets up
        super(XBlockAcidBase, self).setUp()

        # Define a unique course identifier
        self.course_info = {
            'org': 'test_org',
            'number': 'course_' + self.unique_id[:5],
            'run': 'test_' + self.unique_id,
            'display_name': 'Test Course ' + self.unique_id
        }

        self.outline = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )

        self.course_id = '{org}.{number}.{run}'.format(**self.course_info)

        self.setup_fixtures()

        self.auth_page = AutoAuthPage(
            self.browser,
            staff=False,
            username=self.user.get('username'),
            email=self.user.get('email'),
            password=self.user.get('password')
        )
        self.auth_page.visit()
    def setUp(self):
        """
        Create search page and course content to search
        """
        # create test file in which index for this test will live
        with open(self.TEST_INDEX_FILENAME, "w+") as index_file:
            json.dump({}, index_file)
        self.addCleanup(remove_file, self.TEST_INDEX_FILENAME)

        super(CoursewareSearchTest, self).setUp()
        self.courseware_search_page = CoursewareSearchPage(self.browser, self.course_id)

        self.course_outline = CourseOutlinePage(
            self.browser, self.course_info["org"], self.course_info["number"], self.course_info["run"]
        )

        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", "Section 1").add_children(XBlockFixtureDesc("sequential", "Subsection 1"))
        ).add_children(
            XBlockFixtureDesc("chapter", "Section 2").add_children(XBlockFixtureDesc("sequential", "Subsection 2"))
        ).install()
Пример #15
0
    def setUp(self):
        """
        Initialization of pages and course fixture for tests
        """
        super(CMSVideoBaseTest, self).setUp()

        self.video = VideoComponentPage(self.browser)

        # This will be initialized later
        self.unit_page = None

        self.outline = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )

        self.course_fixture = CourseFixture(
            self.course_info['org'], self.course_info['number'],
            self.course_info['run'], self.course_info['display_name']
        )

        self.assets = []
        self.metadata = None
        self.addCleanup(YouTubeStubConfig.reset)
    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)
Пример #17
0
 def setUp(self):  # pylint: disable=arguments-differ
     super(CourseOutlineHelpTest, self).setUp()
     self.course_outline_page = CourseOutlinePage(
         self.browser,
         self.course_info['org'],
         self.course_info['number'],
         self.course_info['run']
     )
     self.course_outline_page.visit()
    def test_create_course_with_existing_org_via_autocomplete(self):
        """
        Scenario: Ensure that the course creation with an existing org should be successful.
        Given I have filled course creation form with an existing org and all required fields
        And I selected `Course Organization` input via autocomplete
        When I click 'Create' button
        Form validation should pass
        Then I see the course listing page with newly created course
        """

        self.auth_page.visit()
        self.dashboard_page.visit()
        new_org = 'orgX2'
        self.assertFalse(self.dashboard_page.has_course(
            org=new_org, number=self.course_number, run=self.course_run
        ))
        self.assertTrue(self.dashboard_page.new_course_button.present)

        self.dashboard_page.click_new_course_button()
        self.assertTrue(self.dashboard_page.is_new_course_form_visible())
        self.dashboard_page.fill_new_course_form(
            self.course_name, '', self.course_number, self.course_run
        )
        self.dashboard_page.course_org_field.fill('org')
        self.dashboard_page.select_item_in_autocomplete_widget(new_org)
        self.assertTrue(self.dashboard_page.is_new_course_form_valid())
        self.dashboard_page.submit_new_course_form()

        # Successful creation of course takes user to course outline page
        course_outline_page = CourseOutlinePage(
            self.browser,
            new_org,
            self.course_number,
            self.course_run
        )
        course_outline_page.visit()
        course_outline_page.wait_for_page()

        # Go back to dashboard and verify newly created course exists there
        self.dashboard_page.visit()
        self.assertTrue(self.dashboard_page.has_course(
            org=new_org, number=self.course_number, run=self.course_run
        ))
    def setUp(self, is_staff=True):
        """
        Create search page and course content to search
        """
        # create test file in which index for this test will live
        with open(self.TEST_INDEX_FILENAME, "w+") as index_file:
            json.dump({}, index_file)
        self.addCleanup(remove_file, self.TEST_INDEX_FILENAME)

        super(CoursewareSearchCohortTest, self).setUp(is_staff=is_staff)
        self.staff_user = self.user

        self.course_outline = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )

        self.content_group_a = "Content Group A"
        self.content_group_b = "Content Group B"

        # Create a student who will be in "Cohort A"
        self.cohort_a_student_username = "******" + str(uuid.uuid4().hex)[:12]
        self.cohort_a_student_email = self.cohort_a_student_username + "@example.com"
        StudioAutoAuthPage(
            self.browser, username=self.cohort_a_student_username, email=self.cohort_a_student_email, no_login=True
        ).visit()

        # Create a student who will be in "Cohort B"
        self.cohort_b_student_username = "******" + str(uuid.uuid4().hex)[:12]
        self.cohort_b_student_email = self.cohort_b_student_username + "@example.com"
        StudioAutoAuthPage(
            self.browser, username=self.cohort_b_student_username, email=self.cohort_b_student_email, no_login=True
        ).visit()

        # Create a student who will end up in the default cohort group
        self.cohort_default_student_username = "******"
        self.cohort_default_student_email = "*****@*****.**"
        StudioAutoAuthPage(
            self.browser, username=self.cohort_default_student_username,
            email=self.cohort_default_student_email, no_login=True
        ).visit()

        self.courseware_search_page = CoursewareSearchPage(self.browser, self.course_id)

        # Enable Cohorting and assign cohorts and content groups
        self._auto_auth(self.staff_user["username"], self.staff_user["email"], True)
        self.enable_cohorting(self.course_fixture)
        self.create_content_groups()
        self.link_html_to_content_groups_and_publish()
        self.create_cohorts_and_assign_students()

        self._studio_reindex()
Пример #20
0
    def setUp(self):  # pylint: disable=arguments-differ
        browser = os.environ.get('SELENIUM_BROWSER', 'firefox')

        # This test will fail if run using phantomjs < 2.0, due to an issue with bind()
        # See https://github.com/ariya/phantomjs/issues/10522 for details.

        # The course_outline uses this function, and as such will not fully load when run
        # under phantomjs 1.9.8. So, to prevent this test from timing out at course_outline.visit(),
        # force the use of firefox vs the standard a11y test usage of phantomjs 1.9.8.

        # TODO: remove this block once https://openedx.atlassian.net/browse/TE-1047 is resolved.
        if browser == 'phantomjs':
            browser = 'firefox'

        with patch.dict(os.environ, {'SELENIUM_BROWSER': browser}):
            super(StudioSubsectionSettingsA11yTest, self).setUp(is_staff=True)

        self.course_outline = CourseOutlinePage(self.browser,
                                                self.course_info['org'],
                                                self.course_info['number'],
                                                self.course_info['run'])
Пример #21
0
    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"}, 'enable_proctored_exams': {"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')
                ),
                XBlockFixtureDesc('sequential', 'Test Subsection 3').add_children(
                    XBlockFixtureDesc('problem', 'Test Problem 3')
                ),

            )
        ).install()
Пример #22
0
    def setUp(self):
        """
        Install library with some content and a course using fixtures
        """
        self._create_search_index()
        super(StudioLibraryContainerTest, self).setUp()
        # Also create a course:
        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()
        self.outline = CourseOutlinePage(self.browser, self.course_info['org'],
                                         self.course_info['number'],
                                         self.course_info['run'])

        self.outline.visit()
        subsection = self.outline.section(SECTION_NAME).subsection(
            SUBSECTION_NAME)
        self.unit_page = subsection.expand_subsection().unit(UNIT_NAME).go_to()
Пример #23
0
    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"}, 'enable_proctored_exams': {"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')
                ),
                XBlockFixtureDesc('sequential', 'Test Subsection 3').add_children(
                    XBlockFixtureDesc('problem', 'Test Problem 3')
                ),

            )
        ).install()
    def test_enable_entrance_exam_for_course(self):
        """
        Test that entrance exam should be created after checking the 'enable entrance exam' checkbox.
        And also that the entrance exam is destroyed after deselecting the checkbox.
        """
        self.settings_detail.require_entrance_exam(required=True)
        self.settings_detail.save_changes()

        # getting the course outline page.
        course_outline_page = CourseOutlinePage(
            self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run']
        )
        course_outline_page.visit()

        # title with text 'Entrance Exam' should be present on page.
        self.assertTrue(element_has_text(
            page=course_outline_page,
            css_selector='span.section-title',
            text='Entrance Exam'
        ))

        # Delete the currently created entrance exam.
        self.settings_detail.visit()
        self.settings_detail.require_entrance_exam(required=False)
        self.settings_detail.save_changes()

        course_outline_page.visit()
        self.assertFalse(element_has_text(
            page=course_outline_page,
            css_selector='span.section-title',
            text='Entrance Exam'
        ))
    def test_entrance_exam_has_unit_button(self):
        """
        Test that entrance exam should be created after checking the 'enable entrance exam' checkbox.
        And user has option to add units only instead of any Subsection.
        """
        self.settings_detail.require_entrance_exam(required=True)
        self.settings_detail.save_changes()

        # getting the course outline page.
        course_outline_page = CourseOutlinePage(self.browser,
                                                self.course_info['org'],
                                                self.course_info['number'],
                                                self.course_info['run'])
        course_outline_page.visit()
        course_outline_page.wait_for_ajax()

        # button with text 'New Unit' should be present.
        self.assertTrue(
            element_has_text(page=course_outline_page,
                             css_selector='.add-item a.button-new',
                             text='New Unit'))

        # button with text 'New Subsection' should not be present.
        self.assertFalse(
            element_has_text(page=course_outline_page,
                             css_selector='.add-item a.button-new',
                             text='New Subsection'))
 def test_no_error_appears_for_long_course_name(self):
     """
     Scenario: Ensure that the course creation with 66 characters long course name is successful.
     Given I have filled course creation form with 66 characters long course name.
     And I have filled remaining form within the allowed characters length.
     When I click 'Create' button
     Form validation should pass
     Then I see the course listing page with newly created course
     """
     course_name = ''.join(
         random.choice(string.ascii_uppercase) for _ in range(66))
     self.auth_page.visit()
     self.dashboard_page.visit()
     self.assertFalse(
         self.dashboard_page.has_course(org=self.course_org,
                                        number=self.course_number,
                                        run=self.course_run))
     self.dashboard_page.click_new_course_button()
     self.assertTrue(self.dashboard_page.is_new_course_form_visible())
     self.dashboard_page.fill_new_course_form(course_name, self.course_org,
                                              self.course_number,
                                              self.course_run)
     self.dashboard_page.submit_new_course_form()
     # Successful creation of course takes user to course outline page
     course_outline_page = CourseOutlinePage(self.browser, self.course_org,
                                             self.course_number,
                                             self.course_run)
     course_outline_page.visit()
     course_outline_page.wait_for_page()
     self.dashboard_page.visit()
     # Assert that course is present on dashboard
     self.assertTrue(
         self.dashboard_page.has_course(org=self.course_org,
                                        number=self.course_number,
                                        run=self.course_run))
    def test_course_rerun(self):
        """
        Scenario: Courses can be rerun
            Given I have a course with a section, subsesction, vertical, and html component with content 'Test Content'
            When I visit the course rerun page
            And I type 'test_rerun' in the course run field
            And I click Create Rerun
            And I visit the course listing page
            And I wait for all courses to finish processing
            And I click on the course with run 'test_rerun'
            Then I see a rerun notification on the course outline page
            And when I click 'Dismiss' on the notification
            Then I do not see a rerun notification
            And when I expand the subsection and click on the unit
            And I click 'View Live Version'
            Then I see one html component with the content 'Test Content'
        """
        course_info = (self.course_info['org'], self.course_info['number'], self.course_info['run'])
        updated_course_info = course_info[0] + "+" + course_info[1] + "+" + course_info[2]

        self.dashboard_page.visit()
        self.dashboard_page.scroll_to_course(course_info[1])
        self.dashboard_page.create_rerun(updated_course_info)

        rerun_page = CourseRerunPage(self.browser, *course_info)
        rerun_page.wait_for_page()
        course_run = 'test_rerun_' + str(random.randrange(1000000, 9999999))
        rerun_page.course_run = course_run
        rerun_page.create_rerun()

        def finished_processing():
            self.dashboard_page.visit()
            return not self.dashboard_page.has_processing_courses

        EmptyPromise(finished_processing, "Rerun finished processing", try_interval=5, timeout=60).fulfill()

        assert course_run in self.dashboard_page.course_runs
        self.dashboard_page.click_course_run(course_run)

        outline_page = CourseOutlinePage(self.browser, *course_info)
        outline_page.wait_for_page()
        self.assertTrue(outline_page.has_rerun_notification)

        outline_page.dismiss_rerun_notification()
        EmptyPromise(lambda: not outline_page.has_rerun_notification, "Rerun notification dismissed").fulfill()

        subsection = outline_page.section(self.SECTION_NAME).subsection(self.SUBSECITON_NAME)
        subsection.expand_subsection()
        unit_page = subsection.unit(self.UNIT_NAME).go_to()

        unit_page.view_published_version()
        courseware = CoursewarePage(self.browser, self.course_id)
        courseware.wait_for_page()
        self.assertEqual(courseware.num_xblock_components, 1)
        self.assertEqual(courseware.xblock_component_html_content(), self.COMPONENT_CONTENT)
    def setUp(self, is_staff=True):
        """
        Create search page and course content to search
        """
        # create test file in which index for this test will live
        with open(self.TEST_INDEX_FILENAME, "w+") as index_file:
            json.dump({}, index_file)
        self.addCleanup(remove_file, self.TEST_INDEX_FILENAME)

        super(SplitTestCoursewareSearchTest, self).setUp(is_staff=is_staff)
        self.staff_user = self.user

        self.courseware_search_page = CoursewareSearchPage(
            self.browser, self.course_id)
        self.course_navigation_page = CourseNavPage(self.browser)
        self.course_outline = CourseOutlinePage(self.browser,
                                                self.course_info['org'],
                                                self.course_info['number'],
                                                self.course_info['run'])

        self._create_group_configuration()
        self._studio_reindex()
    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']
        )
    def setUp(self):
        """
        Initialization of pages and course fixture for tests
        """
        super(CMSVideoBaseTest, self).setUp()

        self.video = VideoComponentPage(self.browser)

        # This will be initialized later
        self.unit_page = None

        self.outline = CourseOutlinePage(self.browser, self.course_info['org'],
                                         self.course_info['number'],
                                         self.course_info['run'])

        self.course_fixture = CourseFixture(self.course_info['org'],
                                            self.course_info['number'],
                                            self.course_info['run'],
                                            self.course_info['display_name'])

        self.assets = []
        self.addCleanup(YouTubeStubConfig.reset)
Пример #31
0
    def setUp(self, is_staff=False):
        """
        Create a unique identifier for the course used in this test.
        """
        # Ensure that the superclass sets up
        super(ContainerBase, self).setUp(is_staff=is_staff)

        self.outline = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )
Пример #32
0
class StudioSubsectionSettingsA11yTest(StudioCourseTest):
    """
    Class to test accessibility on the subsection settings modals.
    """
    def setUp(self):  # pylint: disable=arguments-differ
        browser = os.environ.get('SELENIUM_BROWSER', 'firefox')

        # This test will fail if run using phantomjs < 2.0, due to an issue with bind()
        # See https://github.com/ariya/phantomjs/issues/10522 for details.

        # The course_outline uses this function, and as such will not fully load when run
        # under phantomjs 1.9.8. So, to prevent this test from timing out at course_outline.visit(),
        # force the use of firefox vs the standard a11y test usage of phantomjs 1.9.8.

        # TODO: remove this block once https://openedx.atlassian.net/browse/TE-1047 is resolved.
        if browser == 'phantomjs':
            browser = 'firefox'

        with patch.dict(os.environ, {'SELENIUM_BROWSER': browser}):
            super(StudioSubsectionSettingsA11yTest, self).setUp(is_staff=True)

        self.course_outline = CourseOutlinePage(self.browser,
                                                self.course_info['org'],
                                                self.course_info['number'],
                                                self.course_info['run'])

    def populate_course_fixture(self, course_fixture):
        course_fixture.add_advanced_settings(
            {"enable_proctored_exams": {
                "value": "true"
            }})

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

    def test_special_exams_menu_a11y(self):
        """
        Given that I am a staff member
        And I am editing settings on the special exams menu
        Then that menu is accessible
        """
        self.course_outline.visit()
        self.course_outline.open_subsection_settings_dialog()
        self.course_outline.select_advanced_tab()

        self.course_outline.a11y_audit.config.set_rules({
            "ignore": [
                'section',  # TODO: AC-491
            ],
        })
        # limit the scope of the audit to the special exams tab on the modal dialog
        self.course_outline.a11y_audit.config.set_scope(
            include=['section.edit-settings-timed-examination'])
        self.course_outline.a11y_audit.check_for_accessibility_errors()
Пример #33
0
    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")
Пример #34
0
class StudioSubsectionSettingsA11yTest(StudioCourseTest):
    """
    Class to test accessibility on the subsection settings modals.
    """

    def setUp(self):  # pylint: disable=arguments-differ
        browser = os.environ.get('SELENIUM_BROWSER', 'firefox')

        # This test will fail if run using phantomjs < 2.0, due to an issue with bind()
        # See https://github.com/ariya/phantomjs/issues/10522 for details.

        # The course_outline uses this function, and as such will not fully load when run
        # under phantomjs 1.9.8. So, to prevent this test from timing out at course_outline.visit(),
        # force the use of firefox vs the standard a11y test usage of phantomjs 1.9.8.

        # TODO: remove this block once https://openedx.atlassian.net/browse/TE-1047 is resolved.
        if browser == 'phantomjs':
            browser = 'firefox'

        with patch.dict(os.environ, {'SELENIUM_BROWSER': browser}):
            super(StudioSubsectionSettingsA11yTest, self).setUp(is_staff=True)

        self.course_outline = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )

    def populate_course_fixture(self, course_fixture):
        course_fixture.add_advanced_settings({
            "enable_proctored_exams": {"value": "true"}
        })

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

    def test_special_exams_menu_a11y(self):
        """
        Given that I am a staff member
        And I am editing settings on the special exams menu
        Then that menu is accessible
        """
        self.course_outline.visit()
        self.course_outline.open_subsection_settings_dialog()
        self.course_outline.select_advanced_tab()

        # limit the scope of the audit to the special exams tab on the modal dialog
        self.course_outline.a11y_audit.config.set_scope(
            include=['section.edit-settings-timed-examination']
        )
        self.course_outline.a11y_audit.check_for_accessibility_errors()
    def setUp(self):
        super(GroupConfigurationsTest, self).setUp()
        self.page = GroupConfigurationsPage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )

        self.outline_page = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )
 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()
Пример #37
0
    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.course_outline = CourseOutlinePage(
            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,
            'has_score': False
        }

        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()
Пример #38
0
    def test_course_rerun(self):
        """
        Scenario: Courses can be rerun
            Given I have a course with a section, subsesction, vertical, and html component with content 'Test Content'
            When I visit the course rerun page
            And I type 'test_rerun' in the course run field
            And I click Create Rerun
            And I visit the course listing page
            And I wait for all courses to finish processing
            And I click on the course with run 'test_rerun'
            Then I see a rerun notification on the course outline page
            And when I click 'Dismiss' on the notification
            Then I do not see a rerun notification
            And when I expand the subsection and click on the unit
            And I click 'View Live Version'
            Then I see one html component with the content 'Test Content'
        """
        course_info = (self.course_info['org'], self.course_info['number'], self.course_info['run'])
        updated_course_info = course_info[0] + "+" + course_info[1] + "+" + course_info[2]

        self.dashboard_page.visit()
        self.dashboard_page.scroll_to_course(course_info[1])
        self.dashboard_page.create_rerun(updated_course_info)

        rerun_page = CourseRerunPage(self.browser, *course_info)
        rerun_page.wait_for_page()
        course_run = 'test_rerun_' + str(random.randrange(1000000, 9999999))
        rerun_page.course_run = course_run
        rerun_page.create_rerun()

        def finished_processing():
            self.dashboard_page.visit()
            return not self.dashboard_page.has_processing_courses

        EmptyPromise(finished_processing, "Rerun finished processing", try_interval=5, timeout=60).fulfill()

        assert_in(course_run, self.dashboard_page.course_runs)
        self.dashboard_page.click_course_run(course_run)

        outline_page = CourseOutlinePage(self.browser, *course_info)
        outline_page.wait_for_page()
        self.assertTrue(outline_page.has_rerun_notification)

        outline_page.dismiss_rerun_notification()
        EmptyPromise(lambda: not outline_page.has_rerun_notification, "Rerun notification dismissed").fulfill()

        subsection = outline_page.section(self.SECTION_NAME).subsection(self.SUBSECITON_NAME)
        subsection.expand_subsection()
        unit_page = subsection.unit(self.UNIT_NAME).go_to()

        unit_page.view_published_version()
        courseware = CoursewarePage(self.browser, self.course_id)
        courseware.wait_for_page()
        self.assertEqual(courseware.num_xblock_components, 1)
        self.assertEqual(courseware.xblock_component_html_content(), self.COMPONENT_CONTENT)
Пример #39
0
    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': 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 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)
Пример #41
0
    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.course_outline = CourseOutlinePage(
            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,
            "has_score": False,
        }

        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 test_create_course_with_existing_org(self):
        """
        Scenario: Ensure that the course creation with an existing org should be successful.
        Given I have filled course creation form with an existing org and all required fields
        When I click 'Create' button
        Form validation should pass
        Then I see the course listing page with newly created course
        """

        self.auth_page.visit()
        self.dashboard_page.visit()
        self.assertFalse(
            self.dashboard_page.has_course(org=self.course_org,
                                           number=self.course_number,
                                           run=self.course_run))
        self.assertTrue(self.dashboard_page.new_course_button.present)

        self.dashboard_page.click_new_course_button()
        self.assertTrue(self.dashboard_page.is_new_course_form_visible())
        self.dashboard_page.fill_new_course_form(self.course_name,
                                                 self.course_org,
                                                 self.course_number,
                                                 self.course_run)
        self.assertTrue(self.dashboard_page.is_new_course_form_valid())
        self.dashboard_page.submit_new_course_form()

        # Successful creation of course takes user to course outline page
        course_outline_page = CourseOutlinePage(self.browser, self.course_org,
                                                self.course_number,
                                                self.course_run)
        course_outline_page.visit()
        course_outline_page.wait_for_page()

        # Go back to dashboard and verify newly created course exists there
        self.dashboard_page.visit()
        self.assertTrue(
            self.dashboard_page.has_course(org=self.course_org,
                                           number=self.course_number,
                                           run=self.course_run))
        # Click on the course listing and verify that the Studio course outline page opens.
        self.dashboard_page.click_course_run(self.course_run)
        course_outline_page.wait_for_page()
    def setUp(self):
        """
        Install library with some content and a course using fixtures
        """
        self._create_search_index()
        super(StudioLibraryContainerTest, self).setUp()
        # Also create a course:
        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()
        self.outline = CourseOutlinePage(
            self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run']
        )

        self.outline.visit()
        subsection = self.outline.section(SECTION_NAME).subsection(SUBSECTION_NAME)
        self.unit_page = subsection.expand_subsection().unit(UNIT_NAME).go_to()
    def test_create_course_with_existing_org(self):
        """
        Scenario: Ensure that the course creation with an existing org should be successful.
        Given I have filled course creation form with an existing org and all required fields
        When I click 'Create' button
        Form validation should pass
        Then I see the course listing page with newly created course
        """

        self.auth_page.visit()
        self.dashboard_page.visit()
        self.assertFalse(self.dashboard_page.has_course(
            org=self.course_org, number=self.course_number, run=self.course_run
        ))
        self.assertTrue(self.dashboard_page.new_course_button.present)

        self.dashboard_page.click_new_course_button()
        self.assertTrue(self.dashboard_page.is_new_course_form_visible())
        self.dashboard_page.fill_new_course_form(
            self.course_name, self.course_org, self.course_number, self.course_run
        )
        self.assertTrue(self.dashboard_page.is_new_course_form_valid())
        self.dashboard_page.submit_new_course_form()

        # Successful creation of course takes user to course outline page
        course_outline_page = CourseOutlinePage(
            self.browser,
            self.course_org,
            self.course_number,
            self.course_run
        )
        course_outline_page.visit()
        course_outline_page.wait_for_page()

        # Go back to dashboard and verify newly created course exists there
        self.dashboard_page.visit()
        self.assertTrue(self.dashboard_page.has_course(
            org=self.course_org, number=self.course_number, run=self.course_run
        ))
        # Click on the course listing and verify that the Studio course outline page opens.
        self.dashboard_page.click_course_run(self.course_run)
        course_outline_page.wait_for_page()
    def setUp(self):
        """
        Create search page and course content to search
        """
        # create test file in which index for this test will live
        with open(self.TEST_INDEX_FILENAME, "w+") as index_file:
            json.dump({}, index_file)
        self.addCleanup(remove_file, self.TEST_INDEX_FILENAME)

        super(CoursewareSearchTest, self).setUp()

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

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

        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', 'Section 1').add_children(
                XBlockFixtureDesc('sequential', 'Subsection 1')
            )
        ).add_children(
            XBlockFixtureDesc('chapter', 'Section 2').add_children(
                XBlockFixtureDesc('sequential', 'Subsection 2')
            )
        ).install()
    def test_create_course_with_existing_org_via_autocomplete(self):
        """
        Scenario: Ensure that the course creation with an existing org should be successful.
        Given I have filled course creation form with an existing org and all required fields
        And I selected `Course Organization` input via autocomplete
        When I click 'Create' button
        Form validation should pass
        Then I see the course listing page with newly created course
        """

        self.auth_page.visit()
        self.dashboard_page.visit()
        new_org = 'orgX2'
        self.assertFalse(self.dashboard_page.has_course(
            org=new_org, number=self.course_number, run=self.course_run
        ))
        self.assertTrue(self.dashboard_page.new_course_button.present)

        self.dashboard_page.click_new_course_button()
        self.assertTrue(self.dashboard_page.is_new_course_form_visible())
        self.dashboard_page.fill_new_course_form(
            self.course_name, '', self.course_number, self.course_run
        )
        self.dashboard_page.course_org_field.fill('org')
        self.dashboard_page.select_item_in_autocomplete_widget(new_org)
        self.assertTrue(self.dashboard_page.is_new_course_form_valid())
        self.dashboard_page.submit_new_course_form()

        # Successful creation of course takes user to course outline page
        course_outline_page = CourseOutlinePage(
            self.browser,
            new_org,
            self.course_number,
            self.course_run
        )
        course_outline_page.visit()
        course_outline_page.wait_for_page()

        # Go back to dashboard and verify newly created course exists there
        self.dashboard_page.visit()
        self.assertTrue(self.dashboard_page.has_course(
            org=new_org, number=self.course_number, run=self.course_run
        ))
Пример #47
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())
Пример #48
0
 def setUp(self):  # pylint: disable=arguments-differ
     super(CourseOutlineHelpTest, self).setUp()
     self.course_outline_page = CourseOutlinePage(
         self.browser, self.course_info['org'], self.course_info['number'],
         self.course_info['run'])
     self.course_outline_page.visit()
class ContentGroupConfigurationTest(StudioCourseTest):
    """
    Tests for content groups in the Group Configurations Page.
    There are tests for the experiment groups in test_studio_split_test.
    """
    def setUp(self):
        super(ContentGroupConfigurationTest, self).setUp()
        self.group_configurations_page = GroupConfigurationsPage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )

        self.outline_page = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )

    def populate_course_fixture(self, course_fixture):
        """
        Populates test course with chapter, sequential, and 1 problems.
        The problem is visible only to Group "alpha".
        """
        course_fixture.add_children(
            XBlockFixtureDesc('chapter', 'Test Section').add_children(
                XBlockFixtureDesc('sequential', 'Test Subsection').add_children(
                    XBlockFixtureDesc('vertical', 'Test Unit')
                )
            )
        )

    def create_and_verify_content_group(self, name, existing_groups):
        """
        Creates a new content group and verifies that it was properly created.
        """
        self.assertEqual(existing_groups, len(self.group_configurations_page.content_groups))
        if existing_groups == 0:
            self.group_configurations_page.create_first_content_group()
        else:
            self.group_configurations_page.add_content_group()
        config = self.group_configurations_page.content_groups[existing_groups]
        config.name = name
        # Save the content group
        self.assertEqual(config.get_text('.action-primary'), "Create")
        self.assertFalse(config.delete_button_is_present)
        config.save()
        self.assertIn(name, config.name)
        return config

    def test_no_content_groups_by_default(self):
        """
        Scenario: Ensure that message telling me to create a new content group is
            shown when no content groups exist.
        Given I have a course without content groups
        When I go to the Group Configuration page in Studio
        Then I see "You have not created any content groups yet." message
        """
        self.group_configurations_page.visit()
        self.assertTrue(self.group_configurations_page.no_content_groups_message_is_present)
        self.assertIn(
            "You have not created any content groups yet.",
            self.group_configurations_page.no_content_groups_message_text
        )

    def test_can_create_and_edit_content_groups(self):
        """
        Scenario: Ensure that the content groups can be created and edited correctly.
        Given I have a course without content groups
        When I click button 'Add your first Content Group'
        And I set new the name and click the button 'Create'
        Then I see the new content is added and has correct data
        And I click 'New Content Group' button
        And I set the name and click the button 'Create'
        Then I see the second content group is added and has correct data
        When I edit the second content group
        And I change the name and click the button 'Save'
        Then I see the second content group is saved successfully and has the new name
        """
        self.group_configurations_page.visit()
        self.create_and_verify_content_group("New Content Group", 0)
        second_config = self.create_and_verify_content_group("Second Content Group", 1)

        # Edit the second content group
        second_config.edit()
        second_config.name = "Updated Second Content Group"
        self.assertEqual(second_config.get_text('.action-primary'), "Save")
        second_config.save()

        self.assertIn("Updated Second Content Group", second_config.name)

    def test_cannot_delete_used_content_group(self):
        """
        Scenario: Ensure that the user cannot delete used content group.
        Given I have a course with 1 Content Group
        And I go to the Group Configuration page
        When I try to delete the Content Group with name "New Content Group"
        Then I see the delete button is disabled.
        """
        self.course_fixture._update_xblock(self.course_fixture._course_location, {
            "metadata": {
                u"user_partitions": [
                    create_user_partition_json(
                        0,
                        'Configuration alpha,',
                        'Content Group Partition',
                        [Group("0", 'alpha')],
                        scheme="cohort"
                    )
                ],
            },
        })
        problem_data = dedent("""
            <problem markdown="Simple Problem" max_attempts="" weight="">
              <p>Choose Yes.</p>
              <choiceresponse>
                <checkboxgroup>
                  <choice correct="true">Yes</choice>
                </checkboxgroup>
              </choiceresponse>
            </problem>
        """)
        vertical = self.course_fixture.get_nested_xblocks(category="vertical")[0]
        self.course_fixture.create_xblock(
            vertical.locator,
            XBlockFixtureDesc('problem', "VISIBLE TO ALPHA", data=problem_data, metadata={"group_access": {0: [0]}}),
        )
        self.group_configurations_page.visit()
        config = self.group_configurations_page.content_groups[0]
        self.assertTrue(config.delete_button_is_disabled)

    def test_can_delete_unused_content_group(self):
        """
        Scenario: Ensure that the user can delete unused content group.
        Given I have a course with 1 Content Group
        And I go to the Group Configuration page
        When I delete the Content Group with name "New Content Group"
        Then I see that there is no Content Group
        When I refresh the page
        Then I see that the content group has been deleted
        """
        self.group_configurations_page.visit()
        config = self.create_and_verify_content_group("New Content Group", 0)
        self.assertTrue(config.delete_button_is_present)

        self.assertEqual(len(self.group_configurations_page.content_groups), 1)

        # Delete content group
        config.delete()
        self.assertEqual(len(self.group_configurations_page.content_groups), 0)

        self.group_configurations_page.visit()
        self.assertEqual(len(self.group_configurations_page.content_groups), 0)

    def test_must_supply_name(self):
        """
        Scenario: Ensure that validation of the content group works correctly.
        Given I have a course without content groups
        And I create new content group without specifying a name click the button 'Create'
        Then I see error message "Content Group name is required."
        When I set a name and click the button 'Create'
        Then I see the content group is saved successfully
        """
        self.group_configurations_page.visit()
        self.group_configurations_page.create_first_content_group()
        config = self.group_configurations_page.content_groups[0]
        config.save()
        self.assertEqual(config.mode, 'edit')
        self.assertEqual("Group name is required", config.validation_message)
        config.name = "Content Group Name"
        config.save()
        self.assertIn("Content Group Name", config.name)

    def test_can_cancel_creation_of_content_group(self):
        """
        Scenario: Ensure that creation of a content group can be canceled correctly.
        Given I have a course without content groups
        When I click button 'Add your first Content Group'
        And I set new the name and click the button 'Cancel'
        Then I see that there is no content groups in the course
        """
        self.group_configurations_page.visit()
        self.group_configurations_page.create_first_content_group()
        config = self.group_configurations_page.content_groups[0]
        config.name = "Content Group"
        config.cancel()
        self.assertEqual(0, len(self.group_configurations_page.content_groups))

    def test_content_group_empty_usage(self):
        """
        Scenario: When content group is not used, ensure that the link to outline page works correctly.
        Given I have a course without content group
        And I create new content group
        Then I see a link to the outline page
        When I click on the outline link
        Then I see the outline page
        """
        self.group_configurations_page.visit()
        config = self.create_and_verify_content_group("New Content Group", 0)
        config.toggle()
        config.click_outline_anchor()

        # Waiting for the page load and verify that we've landed on course outline page
        self.outline_page.wait_for_page()
class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest,
                                 TestWithSearchIndexMixin):
    """
    Test Library Content block in LMS
    """
    def setUp(self):
        """
        Install library with some content and a course using fixtures
        """
        self._create_search_index()
        super(StudioLibraryContainerTest, self).setUp()
        # Also create a course:
        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()
        self.outline = CourseOutlinePage(self.browser, self.course_info['org'],
                                         self.course_info['number'],
                                         self.course_info['run'])

        self.outline.visit()
        subsection = self.outline.section(SECTION_NAME).subsection(
            SUBSECTION_NAME)
        self.unit_page = subsection.expand_subsection().unit(UNIT_NAME).go_to()

    def tearDown(self):
        """ Tear down method: remove search index backing file """
        self._cleanup_index_file()
        super(StudioLibraryContainerTest, self).tearDown()

    def populate_library_fixture(self, library_fixture):
        """
        Populate the children of the test course fixture.
        """
        library_fixture.add_children(
            XBlockFixtureDesc("html", "Html1"),
            XBlockFixtureDesc("html", "Html2"),
            XBlockFixtureDesc("html", "Html3"),
        )

    def populate_course_fixture(self, course_fixture):
        """ Install a course with sections/problems, tabs, updates, and handouts """
        library_content_metadata = {
            'source_library_id': unicode(self.library_key),
            'mode': 'random',
            'max_count': 1,
            'has_score': False
        }

        course_fixture.add_children(
            XBlockFixtureDesc('chapter', SECTION_NAME).add_children(
                XBlockFixtureDesc('sequential', SUBSECTION_NAME).add_children(
                    XBlockFixtureDesc('vertical', UNIT_NAME).add_children(
                        XBlockFixtureDesc(
                            'library_content',
                            "Library Content",
                            metadata=library_content_metadata)))))

    def _get_library_xblock_wrapper(self, xblock):
        """
        Wraps xblock into :class:`...pages.studio.library.StudioLibraryContainerXBlockWrapper`
        """
        return StudioLibraryContainerXBlockWrapper.from_xblock_wrapper(xblock)

    @ddt.data(
        (1, True),
        (2, False),
        (3, True),
    )
    @ddt.unpack
    def test_can_edit_metadata(self, max_count, scored):
        """
        Scenario: Given I have a library, a course and library content xblock in a course
        When I go to studio unit page for library content block
        And I edit library content metadata and save it
        Then I can ensure that data is persisted
        """
        library_name = self.library_info['display_name']
        library_container = self._get_library_xblock_wrapper(
            self.unit_page.xblocks[1])
        library_container.edit()
        edit_modal = StudioLibraryContentEditor(self.browser,
                                                library_container.locator)
        edit_modal.library_name = library_name
        edit_modal.count = max_count
        edit_modal.scored = scored

        library_container.save_settings()  # saving settings

        # open edit window again to verify changes are persistent
        library_container.edit()
        edit_modal = StudioLibraryContentEditor(self.browser,
                                                library_container.locator)
        self.assertEqual(edit_modal.library_name, library_name)
        self.assertEqual(edit_modal.count, max_count)
        self.assertEqual(edit_modal.scored, scored)

    def test_no_library_shows_library_not_configured(self):
        """
        Scenario: Given I have a library, a course and library content xblock in a course
        When I go to studio unit page for library content block
        And I edit to select "No Library"
        Then I can see that library content block is misconfigured
        """
        expected_text = 'A library has not yet been selected.'
        expected_action = 'Select a Library'
        library_container = self._get_library_xblock_wrapper(
            self.unit_page.xblocks[1])

        # precondition check - the library block should be configured before we remove the library setting
        self.assertFalse(
            library_container.has_validation_not_configured_warning)

        library_container.edit()
        edit_modal = StudioLibraryContentEditor(self.browser,
                                                library_container.locator)
        edit_modal.library_name = "No Library Selected"
        library_container.save_settings()

        self.assertTrue(
            library_container.has_validation_not_configured_warning)
        self.assertIn(expected_text,
                      library_container.validation_not_configured_warning_text)
        self.assertIn(expected_action,
                      library_container.validation_not_configured_warning_text)

    def test_out_of_date_message(self):
        """
        Scenario: Given I have a library, a course and library content xblock in a course
        When I go to studio unit page for library content block
        Then I update the library being used
        Then I refresh the page
        Then I can see that library content block needs to be updated
        When I click on the update link
        Then I can see that the content no longer needs to be updated
        """
        # Formerly flaky: see TE-745
        expected_text = "This component is out of date. The library has new content."
        library_block = self._get_library_xblock_wrapper(
            self.unit_page.xblocks[1])

        self.assertFalse(library_block.has_validation_warning)
        # Removed this assert until a summary message is added back to the author view (SOL-192)
        #self.assertIn("3 matching components", library_block.author_content)

        self.library_fixture.create_xblock(
            self.library_fixture.library_location,
            XBlockFixtureDesc("html", "Html4"))

        self.unit_page.visit()  # Reload the page

        self.assertTrue(library_block.has_validation_warning)
        self.assertIn(expected_text, library_block.validation_warning_text)

        library_block.refresh_children()

        self.unit_page.wait_for_page()  # Wait for the page to reload
        library_block = self._get_library_xblock_wrapper(
            self.unit_page.xblocks[1])

        self.assertFalse(library_block.has_validation_message)
        # Removed this assert until a summary message is added back to the author view (SOL-192)
        #self.assertIn("4 matching components", library_block.author_content)

    def test_no_content_message(self):
        """
        Scenario: Given I have a library, a course and library content xblock in a course
        When I go to studio unit page for library content block
        And I set Problem Type selector so that no libraries have matching content
        Then I can see that "No matching content" warning is shown
        When I set Problem Type selector so that there is matching content
        Then I can see that warning messages are not shown
        """
        # Add a single "Dropdown" type problem to the library (which otherwise has only HTML blocks):
        self.library_fixture.create_xblock(
            self.library_fixture.library_location,
            XBlockFixtureDesc("problem",
                              "Dropdown",
                              data=textwrap.dedent("""
                <problem>
                    <p>Dropdown</p>
                    <optionresponse><optioninput label="Dropdown" options="('1', '2')" correct="'2'"></optioninput></optionresponse>
                </problem>
                """)))

        expected_text = 'There are no matching problem types in the specified libraries. Select another problem type'

        library_container = self._get_library_xblock_wrapper(
            self.unit_page.xblocks[1])

        # precondition check - assert library has children matching filter criteria
        self.assertFalse(library_container.has_validation_error)
        self.assertFalse(library_container.has_validation_warning)

        library_container.edit()
        edit_modal = StudioLibraryContentEditor(self.browser,
                                                library_container.locator)
        self.assertEqual(edit_modal.capa_type,
                         "Any Type")  # precondition check
        edit_modal.capa_type = "Custom Evaluated Script"

        library_container.save_settings()

        self.assertTrue(library_container.has_validation_warning)
        self.assertIn(expected_text, library_container.validation_warning_text)

        library_container.edit()
        edit_modal = StudioLibraryContentEditor(self.browser,
                                                library_container.locator)
        self.assertEqual(edit_modal.capa_type,
                         "Custom Evaluated Script")  # precondition check
        edit_modal.capa_type = "Dropdown"
        library_container.save_settings()

        # Library should contain single Dropdown problem, so now there should be no errors again
        self.assertFalse(library_container.has_validation_error)
        self.assertFalse(library_container.has_validation_warning)

    def test_not_enough_children_blocks(self):
        """
        Scenario: Given I have a library, a course and library content xblock in a course
        When I go to studio unit page for library content block
        And I set Problem Type selector so "Any"
        Then I can see that "No matching content" warning is shown
        """
        expected_tpl = "The specified library is configured to fetch {count} problems, " \
                       "but there are only {actual} matching problems."

        library_container = self._get_library_xblock_wrapper(
            self.unit_page.xblocks[1])

        # precondition check - assert block is configured fine
        self.assertFalse(library_container.has_validation_error)
        self.assertFalse(library_container.has_validation_warning)

        library_container.edit()
        edit_modal = StudioLibraryContentEditor(self.browser,
                                                library_container.locator)
        edit_modal.count = 50
        library_container.save_settings()

        self.assertTrue(library_container.has_validation_warning)
        self.assertIn(
            expected_tpl.format(count=50,
                                actual=len(self.library_fixture.children)),
            library_container.validation_warning_text)

    def test_settings_overrides(self):
        """
        Scenario: Given I have a library, a course and library content xblock in a course
        When I go to studio unit page for library content block
        And when I click the "View" link
        Then I can see a preview of the blocks drawn from the library.

        When I edit one of the blocks to change a setting such as "display_name",
        Then I can see the new setting is overriding the library version.

        When I subsequently click to refresh the content with the latest from the library,
        Then I can see that the overrided version of the setting is preserved.

        When I click to edit the block and reset the setting,
        then I can see that the setting's field defaults back to the library version.
        """
        block_wrapper_unit_page = self._get_library_xblock_wrapper(
            self.unit_page.xblocks[0].children[0])
        container_page = block_wrapper_unit_page.go_to_container()
        library_block = self._get_library_xblock_wrapper(
            container_page.xblocks[0])

        self.assertFalse(library_block.has_validation_message)
        self.assertEqual(len(library_block.children), 3)

        block = library_block.children[0]
        self.assertIn(block.name, ("Html1", "Html2", "Html3"))
        name_default = block.name

        block.edit()
        new_display_name = "A new name for this HTML block"
        block.set_field_val("Display Name", new_display_name)
        block.save_settings()

        self.assertEqual(block.name, new_display_name)

        # Create a new block, causing a new library version:
        self.library_fixture.create_xblock(
            self.library_fixture.library_location,
            XBlockFixtureDesc("html", "Html4"))

        container_page.visit()  # Reload
        self.assertTrue(library_block.has_validation_warning)

        library_block.refresh_children()
        container_page.wait_for_page()  # Wait for the page to reload

        self.assertEqual(len(library_block.children), 4)
        self.assertEqual(block.name, new_display_name)

        # Reset:
        block.edit()
        block.reset_field_val("Display Name")
        block.save_settings()
        self.assertEqual(block.name, name_default)

    def test_cannot_manage(self):
        """
        Scenario: Given I have a library, a course and library content xblock in a course
        When I go to studio unit page for library content block
        And when I click the "View" link
        Then I can see a preview of the blocks drawn from the library.

        And I do not see a duplicate button
        And I do not see a delete button
        """
        block_wrapper_unit_page = self._get_library_xblock_wrapper(
            self.unit_page.xblocks[0].children[0])
        container_page = block_wrapper_unit_page.go_to_container()

        for block in container_page.xblocks:
            self.assertFalse(block.has_duplicate_button)
            self.assertFalse(block.has_delete_button)
            self.assertFalse(block.has_edit_visibility_button)
class XBlockAcidBase(AcceptanceTest):
    """
    Base class for tests that verify that XBlock integration is working correctly
    """
    shard = 21
    __test__ = False

    def setUp(self):
        """
        Create a unique identifier for the course used in this test.
        """
        # Ensure that the superclass sets up
        super(XBlockAcidBase, self).setUp()

        # Define a unique course identifier
        self.course_info = {
            'org': 'test_org',
            'number': 'course_' + self.unique_id[:5],
            'run': 'test_' + self.unique_id,
            'display_name': 'Test Course ' + self.unique_id
        }

        self.outline = CourseOutlinePage(self.browser, self.course_info['org'],
                                         self.course_info['number'],
                                         self.course_info['run'])

        self.course_id = '{org}.{number}.{run}'.format(**self.course_info)

        self.setup_fixtures()

        self.auth_page = AutoAuthPage(self.browser,
                                      staff=False,
                                      username=self.user.get('username'),
                                      email=self.user.get('email'),
                                      password=self.user.get('password'))
        self.auth_page.visit()

    def validate_acid_block_preview(self, acid_block):
        """
        Validate the Acid Block's preview
        """
        self.assertTrue(acid_block.init_fn_passed)
        self.assertTrue(acid_block.resource_url_passed)
        self.assertTrue(acid_block.scope_passed('user_state'))
        self.assertTrue(acid_block.scope_passed('user_state_summary'))
        self.assertTrue(acid_block.scope_passed('preferences'))
        self.assertTrue(acid_block.scope_passed('user_info'))

    def test_acid_block_preview(self):
        """
        Verify that all expected acid block tests pass in studio preview
        """

        self.outline.visit()
        subsection = self.outline.section('Test Section').subsection(
            'Test Subsection')
        unit = subsection.expand_subsection().unit('Test Unit').go_to()

        acid_block = AcidView(self.browser, unit.xblocks[0].preview_selector)
        self.validate_acid_block_preview(acid_block)

    def test_acid_block_editor(self):
        """
        Verify that all expected acid block tests pass in studio editor
        """

        self.outline.visit()
        subsection = self.outline.section('Test Section').subsection(
            'Test Subsection')
        unit = subsection.expand_subsection().unit('Test Unit').go_to()

        acid_block = AcidView(self.browser,
                              unit.xblocks[0].edit().editor_selector)
        self.assertTrue(acid_block.init_fn_passed)
        self.assertTrue(acid_block.resource_url_passed)
Пример #52
0
class GroupConfigurationsTest(ContainerBase, SplitTestMixin):
    """
    Tests that Group Configurations page works correctly with previously
    added configurations in Studio
    """
    __test__ = True
    shard = 15

    def setUp(self):
        super(GroupConfigurationsTest, self).setUp()
        self.page = GroupConfigurationsPage(self.browser,
                                            self.course_info['org'],
                                            self.course_info['number'],
                                            self.course_info['run'])

        self.outline_page = CourseOutlinePage(self.browser,
                                              self.course_info['org'],
                                              self.course_info['number'],
                                              self.course_info['run'])

    def _assert_fields(self,
                       config,
                       cid=None,
                       name='',
                       description='',
                       groups=None):
        self.assertEqual(config.mode, 'details')

        if name:
            self.assertIn(name, config.name)

        if cid:
            self.assertEqual(cid, config.id)
        else:
            # To make sure that id is present on the page and it is not an empty.
            # We do not check the value of the id, because it's generated randomly and we cannot
            # predict this value
            self.assertTrue(config.id)

        # Expand the configuration
        config.toggle()

        if description:
            self.assertIn(description, config.description)

        if groups:
            allocation = int(math.floor(100 / len(groups)))
            self.assertEqual(groups, [group.name for group in config.groups])
            for group in config.groups:
                self.assertEqual(str(allocation) + "%", group.allocation)

        # Collapse the configuration
        config.toggle()

    def _add_split_test_to_vertical(self,
                                    number,
                                    group_configuration_metadata=None):
        """
        Add split test to vertical #`number`.

        If `group_configuration_metadata` is not None, use it to assign group configuration to split test.
        """
        vertical = self.course_fixture.get_nested_xblocks(
            category="vertical")[number]
        if group_configuration_metadata:
            split_test = XBlockFixtureDesc(
                'split_test',
                'Test Content Experiment',
                metadata=group_configuration_metadata)
        else:
            split_test = XBlockFixtureDesc('split_test',
                                           'Test Content Experiment')
        self.course_fixture.create_xblock(vertical.locator, split_test)
        return split_test

    def populate_course_fixture(self, course_fixture):
        course_fixture.add_advanced_settings({
            u"advanced_modules": {
                "value": ["split_test"]
            },
        })
        course_fixture.add_children(
            XBlockFixtureDesc('chapter', 'Test Section').add_children(
                XBlockFixtureDesc('sequential',
                                  'Test Subsection').add_children(
                                      XBlockFixtureDesc(
                                          'vertical', 'Test Unit'))))

    def create_group_configuration_experiment(self, groups,
                                              associate_experiment):
        """
        Creates a Group Configuration containing a list of groups.
        Optionally creates a Content Experiment and associates it with previous Group Configuration.

        Returns group configuration or (group configuration, experiment xblock)
        """
        # Create a new group configurations
        self.course_fixture._update_xblock(
            self.course_fixture._course_location, {
                "metadata": {
                    u"user_partitions": [
                        create_user_partition_json(0, "Name", "Description.",
                                                   groups),
                    ],
                },
            })

        if associate_experiment:
            # Assign newly created group configuration to experiment
            vertical = self.course_fixture.get_nested_xblocks(
                category="vertical")[0]
            split_test = XBlockFixtureDesc('split_test',
                                           'Test Content Experiment',
                                           metadata={'user_partition_id': 0})
            self.course_fixture.create_xblock(vertical.locator, split_test)

        # Go to the Group Configuration Page
        self.page.visit()
        config = self.page.experiment_group_configurations[0]

        if associate_experiment:
            return config, split_test
        return config

    def publish_unit_in_lms_and_view(self, courseware_page, publish=True):
        """
        Given course outline page, publish first unit and view it in LMS when publish is false, it will only view
        """
        self.outline_page.visit()
        self.outline_page.expand_all_subsections()
        section = self.outline_page.section_at(0)
        unit = section.subsection_at(0).unit_at(0).go_to()

        # I publish and view in LMS and it is rendered correctly
        if publish:
            unit.publish()
        unit.view_published_version()
        self.assertEqual(len(self.browser.window_handles), 2)
        courseware_page.wait_for_page()

    def get_select_options(self, page, selector):
        """
        Get list of options of dropdown that is specified by selector on a given page.
        """
        select_element = page.q(css=selector)
        self.assertTrue(select_element.is_present())
        return [option.text for option in Select(select_element[0]).options]

    def test_no_group_configurations_added(self):
        """
        Scenario: Ensure that message telling me to create a new group configuration is
        shown when group configurations were not added.
        Given I have a course without group configurations
        When I go to the Group Configuration page in Studio
        Then I see "You have not created any group configurations yet." message
        """
        self.page.visit()
        self.assertTrue(self.page.experiment_group_sections_present)
        self.assertTrue(self.page.no_experiment_groups_message_is_present)
        self.assertIn("You have not created any group configurations yet.",
                      self.page.no_experiment_groups_message_text)

    def test_group_configurations_have_correct_data(self):
        """
        Scenario: Ensure that the group configuration is rendered correctly in expanded/collapsed mode.
        Given I have a course with 2 group configurations
        And I go to the Group Configuration page in Studio
        And I work with the first group configuration
        And I see `name`, `id` are visible and have correct values
        When I expand the first group configuration
        Then I see `description` and `groups` appear and also have correct values
        And I do the same checks for the second group configuration
        """
        self.course_fixture._update_xblock(
            self.course_fixture._course_location, {
                "metadata": {
                    u"user_partitions": [
                        create_user_partition_json(
                            0, 'Name of the Group Configuration',
                            'Description of the group configuration.',
                            [Group("0", 'Group 0'),
                             Group("1", 'Group 1')]),
                        create_user_partition_json(
                            1, 'Name of second Group Configuration',
                            'Second group configuration.', [
                                Group("0", 'Alpha'),
                                Group("1", 'Beta'),
                                Group("2", 'Gamma')
                            ]),
                    ],
                },
            })

        self.page.visit()
        config = self.page.experiment_group_configurations[0]
        # no groups when the the configuration is collapsed
        self.assertEqual(len(config.groups), 0)
        self._assert_fields(
            config,
            cid="0",
            name="Name of the Group Configuration",
            description="Description of the group configuration.",
            groups=["Group 0", "Group 1"])

        config = self.page.experiment_group_configurations[1]

        self._assert_fields(config,
                            name="Name of second Group Configuration",
                            description="Second group configuration.",
                            groups=["Alpha", "Beta", "Gamma"])

    def test_can_create_and_edit_group_configuration(self):
        """
        Scenario: Ensure that the group configuration can be created and edited correctly.
        Given I have a course without group configurations
        When I click button 'Create new Group Configuration'
        And I set new name and description, change name for the 2nd default group, add one new group
        And I click button 'Create'
        Then I see the new group configuration is added and has correct data
        When I edit the group group_configuration
        And I change the name and description, add new group, remove old one and change name for the Group A
        And I click button 'Save'
        Then I see the group configuration is saved successfully and has the new data
        """
        self.page.visit()
        self.assertEqual(len(self.page.experiment_group_configurations), 0)
        # Create new group configuration
        self.page.create_experiment_group_configuration()
        config = self.page.experiment_group_configurations[0]
        config.name = "New Group Configuration Name"
        config.description = "New Description of the group configuration."
        config.groups[1].name = "New Group Name"
        # Add new group
        config.add_group()  # Group C

        # Save the configuration
        self.assertEqual(config.get_text('.action-primary'), "Create")
        self.assertFalse(config.delete_button_is_present)
        config.save()

        self._assert_fields(
            config,
            name="New Group Configuration Name",
            description="New Description of the group configuration.",
            groups=["Group A", "New Group Name", "Group C"])

        # Edit the group configuration
        config.edit()
        # Update fields
        self.assertTrue(config.id)
        config.name = "Second Group Configuration Name"
        config.description = "Second Description of the group configuration."
        self.assertEqual(config.get_text('.action-primary'), "Save")
        # Add new group
        config.add_group()  # Group D
        # Remove group with name "New Group Name"
        config.groups[1].remove()
        # Rename Group A
        config.groups[0].name = "First Group"
        # Save the configuration
        config.save()

        self._assert_fields(
            config,
            name="Second Group Configuration Name",
            description="Second Description of the group configuration.",
            groups=["First Group", "Group C", "Group D"])

    def test_focus_management_in_experiment_group_inputs(self):
        """
        Scenario: Ensure that selecting the focus inputs in the groups list
        sets the .is-focused class on the fieldset
        Given I have a course with experiment group configurations
        When I click the name of the first group
        Then the fieldset wrapping the group names whould get class .is-focused
        When I click away from the first group
        Then the fieldset should not have class .is-focused anymore
        """
        self.page.visit()
        self.page.create_experiment_group_configuration()
        config = self.page.experiment_group_configurations[0]
        group_a = config.groups[0]

        # Assert the fieldset doesn't have .is-focused class
        self.assertFalse(
            self.page.q(css="fieldset.groups-fields.is-focused").visible)

        # Click on the Group A input field
        self.page.q(css=group_a.prefix).click()

        # Assert the fieldset has .is-focused class applied
        self.assertTrue(
            self.page.q(css="fieldset.groups-fields.is-focused").visible)

        # Click away
        self.page.q(css=".page-header").click()

        # Assert the fieldset doesn't have .is-focused class
        self.assertFalse(
            self.page.q(css="fieldset.groups-fields.is-focused").visible)

    def test_use_group_configuration(self):
        """
        Scenario: Ensure that the group configuration can be used by split_module correctly
        Given I have a course without group configurations
        When I create new group configuration
        And I set new name and add a new group, save the group configuration
        And I go to the unit page in Studio
        And I add new advanced module "Content Experiment"
        When I assign created group configuration to the module
        Then I see the module has correct groups
        """
        self.page.visit()
        # Create new group configuration
        self.page.create_experiment_group_configuration()
        config = self.page.experiment_group_configurations[0]
        config.name = "New Group Configuration Name"
        # Add new group
        config.add_group()
        config.groups[2].name = "New group"
        # Save the configuration
        config.save()

        split_test = self._add_split_test_to_vertical(number=0)

        container = ContainerPage(self.browser, split_test.locator)
        container.visit()
        container.edit()
        component_editor = XBlockEditorView(self.browser, container.locator)
        component_editor.set_select_value_and_save(
            'Group Configuration', 'New Group Configuration Name')
        self.verify_groups(container, ['Group A', 'Group B', 'New group'], [])

    def test_container_page_active_verticals_names_are_synced(self):
        """
        Scenario: Ensure that the Content Experiment display synced vertical names and correct groups.
        Given I have a course with group configuration
        And I go to the Group Configuration page in Studio
        And I edit the name of the group configuration, add new group and remove old one
        And I change the name for the group "New group" to "Second Group"
        And I go to the Container page in Studio
        And I edit the Content Experiment
        Then I see the group configuration name is changed in `Group Configuration` dropdown
        And the group configuration name is changed on container page
        And I see the module has 2 active groups and one inactive
        And I see "Add missing groups" link exists
        When I click on "Add missing groups" link
        The I see the module has 3 active groups and one inactive
        """
        self.course_fixture._update_xblock(
            self.course_fixture._course_location, {
                "metadata": {
                    u"user_partitions": [
                        create_user_partition_json(
                            0, 'Name of the Group Configuration',
                            'Description of the group configuration.', [
                                Group("0", 'Group A'),
                                Group("1", 'Group B'),
                                Group("2", 'Group C')
                            ]),
                    ],
                },
            })

        # Add split test to vertical and assign newly created group configuration to it
        split_test = self._add_split_test_to_vertical(
            number=0, group_configuration_metadata={'user_partition_id': 0})

        self.page.visit()
        config = self.page.experiment_group_configurations[0]
        config.edit()
        config.name = "Second Group Configuration Name"
        # `Group C` -> `Second Group`
        config.groups[2].name = "Second Group"
        # Add new group
        config.add_group()  # Group D
        # Remove Group A
        config.groups[0].remove()
        # Save the configuration
        config.save()

        container = ContainerPage(self.browser, split_test.locator)
        container.visit()
        container.edit()
        component_editor = XBlockEditorView(self.browser, container.locator)
        self.assertEqual(
            "Second Group Configuration Name",
            component_editor.get_selected_option_text('Group Configuration'))
        component_editor.cancel()
        self.assertIn("Second Group Configuration Name",
                      container.get_xblock_information_message())
        self.verify_groups(container, ['Group B', 'Second Group'],
                           ['Group ID 0'],
                           verify_missing_groups_not_present=False)
        # Click the add button and verify that the groups were added on the page
        container.add_missing_groups()
        self.verify_groups(container, ['Group B', 'Second Group', 'Group D'],
                           ['Group ID 0'])

    def test_can_cancel_creation_of_group_configuration(self):
        """
        Scenario: Ensure that creation of the group configuration can be canceled correctly.
        Given I have a course without group configurations
        When I click button 'Create new Group Configuration'
        And I set new name and description, add 1 additional group
        And I click button 'Cancel'
        Then I see that there is no new group configurations in the course
        """
        self.page.visit()

        self.assertEqual(len(self.page.experiment_group_configurations), 0)
        # Create new group configuration
        self.page.create_experiment_group_configuration()

        config = self.page.experiment_group_configurations[0]
        config.name = "Name of the Group Configuration"
        config.description = "Description of the group configuration."
        # Add new group
        config.add_group()  # Group C
        # Cancel the configuration
        config.cancel()

        self.assertEqual(len(self.page.experiment_group_configurations), 0)

    def test_can_cancel_editing_of_group_configuration(self):
        """
        Scenario: Ensure that editing of the group configuration can be canceled correctly.
        Given I have a course with group configuration
        When I go to the edit mode of the group configuration
        And I set new name and description, add 2 additional groups
        And I click button 'Cancel'
        Then I see that new changes were discarded
        """
        self.course_fixture._update_xblock(
            self.course_fixture._course_location, {
                "metadata": {
                    u"user_partitions": [
                        create_user_partition_json(
                            0, 'Name of the Group Configuration',
                            'Description of the group configuration.',
                            [Group("0", 'Group 0'),
                             Group("1", 'Group 1')]),
                        create_user_partition_json(
                            1, 'Name of second Group Configuration',
                            'Second group configuration.', [
                                Group("0", 'Alpha'),
                                Group("1", 'Beta'),
                                Group("2", 'Gamma')
                            ]),
                    ],
                },
            })
        self.page.visit()
        config = self.page.experiment_group_configurations[0]
        config.name = "New Group Configuration Name"
        config.description = "New Description of the group configuration."
        # Add 2 new groups
        config.add_group()  # Group C
        config.add_group()  # Group D
        # Cancel the configuration
        config.cancel()

        self._assert_fields(
            config,
            name="Name of the Group Configuration",
            description="Description of the group configuration.",
            groups=["Group 0", "Group 1"])

    def test_group_configuration_validation(self):
        """
        Scenario: Ensure that validation of the group configuration works correctly.
        Given I have a course without group configurations
        And I create new group configuration with 2 default groups
        When I set only description and try to save
        Then I see error message "Group Configuration name is required."
        When I set a name
        And I delete the name of one of the groups and try to save
        Then I see error message "All groups must have a name"
        When I delete all the groups and try to save
        Then I see error message "There must be at least one group."
        When I add a group and try to save
        Then I see the group configuration is saved successfully
        """
        def try_to_save_and_verify_error_message(message):
            # Try to save
            config.save()
            # Verify that configuration is still in editing mode
            self.assertEqual(config.mode, 'edit')
            # Verify error message
            self.assertEqual(message, config.validation_message)

        self.page.visit()
        # Create new group configuration
        self.page.create_experiment_group_configuration()
        # Leave empty required field
        config = self.page.experiment_group_configurations[0]
        config.description = "Description of the group configuration."

        try_to_save_and_verify_error_message(
            "Group Configuration name is required.")

        # Set required field
        config.name = "Name of the Group Configuration"
        config.groups[1].name = ''
        try_to_save_and_verify_error_message("All groups must have a name.")
        config.groups[0].remove()
        config.groups[0].remove()
        try_to_save_and_verify_error_message(
            "There must be at least one group.")
        config.add_group()

        # Save the configuration
        config.save()

        self._assert_fields(
            config,
            name="Name of the Group Configuration",
            description="Description of the group configuration.",
            groups=["Group A"])

    def test_group_configuration_empty_usage(self):
        """
        Scenario: When group configuration is not used, ensure that the link to outline page works correctly.
        Given I have a course without group configurations
        And I create new group configuration with 2 default groups
        Then I see a link to the outline page
        When I click on the outline link
        Then I see the outline page
        """
        # Create a new group configurations
        self.course_fixture._update_xblock(
            self.course_fixture._course_location, {
                "metadata": {
                    u"user_partitions": [
                        create_user_partition_json(
                            0, "Name", "Description.",
                            [Group("0", "Group A"),
                             Group("1", "Group B")]),
                    ],
                },
            })

        # Go to the Group Configuration Page and click on outline anchor
        self.page.visit()
        config = self.page.experiment_group_configurations[0]
        config.toggle()
        config.click_outline_anchor()

        # Waiting for the page load and verify that we've landed on course outline page
        self.outline_page.wait_for_page()

    def test_group_configuration_non_empty_usage(self):
        """
        Scenario: When group configuration is used, ensure that the links to units using a group configuration work correctly.
        Given I have a course without group configurations
        And I create new group configuration with 2 default groups
        And I create a unit and assign the newly created group configuration
        And open the Group Configuration page
        Then I see a link to the newly created unit
        When I click on the unit link
        Then I see correct unit page
        """
        # Create a new group configurations
        self.course_fixture._update_xblock(
            self.course_fixture._course_location, {
                "metadata": {
                    u"user_partitions": [
                        create_user_partition_json(
                            0, "Name", "Description.",
                            [Group("0", "Group A"),
                             Group("1", "Group B")]),
                    ],
                },
            })

        # Assign newly created group configuration to unit
        vertical = self.course_fixture.get_nested_xblocks(
            category="vertical")[0]
        self.course_fixture.create_xblock(
            vertical.locator,
            XBlockFixtureDesc('split_test',
                              'Test Content Experiment',
                              metadata={'user_partition_id': 0}))
        unit = CourseOutlineUnit(self.browser, vertical.locator)

        # Go to the Group Configuration Page and click unit anchor
        self.page.visit()
        config = self.page.experiment_group_configurations[0]
        config.toggle()
        usage = config.usages[0]
        config.click_unit_anchor()

        unit = ContainerPage(self.browser, vertical.locator)
        # Waiting for the page load and verify that we've landed on the unit page
        unit.wait_for_page()

        self.assertIn(unit.name, usage)

    def test_can_delete_unused_group_configuration(self):
        """
        Scenario: Ensure that the user can delete unused group configuration.
        Given I have a course with 2 group configurations
        And I go to the Group Configuration page
        When I delete the Group Configuration with name "Configuration 1"
        Then I see that there is one Group Configuration
        When I edit the Group Configuration with name "Configuration 2"
        And I delete the Group Configuration with name "Configuration 2"
        Then I see that the are no Group Configurations
        """
        self.course_fixture._update_xblock(
            self.course_fixture._course_location, {
                "metadata": {
                    u"user_partitions": [
                        create_user_partition_json(
                            0, 'Configuration 1',
                            'Description of the group configuration.',
                            [Group("0", 'Group 0'),
                             Group("1", 'Group 1')]),
                        create_user_partition_json(
                            1, 'Configuration 2',
                            'Second group configuration.', [
                                Group("0", 'Alpha'),
                                Group("1", 'Beta'),
                                Group("2", 'Gamma')
                            ])
                    ],
                },
            })
        self.page.visit()

        self.assertEqual(len(self.page.experiment_group_configurations), 2)
        config = self.page.experiment_group_configurations[1]
        # Delete first group configuration via detail view
        config.delete()
        self.assertEqual(len(self.page.experiment_group_configurations), 1)

        config = self.page.experiment_group_configurations[0]
        config.edit()
        self.assertFalse(config.delete_button_is_disabled)
        # Delete first group configuration via edit view
        config.delete()
        self.assertEqual(len(self.page.experiment_group_configurations), 0)

    def test_cannot_delete_used_group_configuration(self):
        """
        Scenario: Ensure that the user cannot delete unused group configuration.
        Given I have a course with group configuration that is used in the Content Experiment
        When I go to the Group Configuration page
        Then I do not see delete button and I see a note about that
        When I edit the Group Configuration
        Then I do not see delete button and I see the note about that
        """
        # Create a new group configurations
        self.course_fixture._update_xblock(
            self.course_fixture._course_location, {
                "metadata": {
                    u"user_partitions": [
                        create_user_partition_json(
                            0, "Name", "Description.",
                            [Group("0", "Group A"),
                             Group("1", "Group B")])
                    ],
                },
            })
        vertical = self.course_fixture.get_nested_xblocks(
            category="vertical")[0]
        self.course_fixture.create_xblock(
            vertical.locator,
            XBlockFixtureDesc('split_test',
                              'Test Content Experiment',
                              metadata={'user_partition_id': 0}))
        # Go to the Group Configuration Page and click unit anchor
        self.page.visit()

        config = self.page.experiment_group_configurations[0]
        self.assertTrue(config.delete_button_is_disabled)
        self.assertIn('Cannot delete when in use by an experiment',
                      config.delete_note)

        config.edit()
        self.assertTrue(config.delete_button_is_disabled)
        self.assertIn('Cannot delete when in use by an experiment',
                      config.delete_note)

    def test_easy_access_from_experiment(self):
        """
        Scenario: When a Content Experiment uses a Group Configuration,
        ensure that the link to that Group Configuration works correctly.

        Given I have a course with two Group Configurations
        And Content Experiment is assigned to one Group Configuration
        Then I see a link to Group Configuration
        When I click on the Group Configuration link
        Then I see the Group Configurations page
        And I see that appropriate Group Configuration is expanded.
        """
        # Create a new group configurations
        self.course_fixture._update_xblock(
            self.course_fixture._course_location, {
                "metadata": {
                    u"user_partitions": [
                        create_user_partition_json(
                            0, "Name", "Description.",
                            [Group("0", "Group A"),
                             Group("1", "Group B")]),
                        create_user_partition_json(
                            1, 'Name of second Group Configuration',
                            'Second group configuration.', [
                                Group("0", 'Alpha'),
                                Group("1", 'Beta'),
                                Group("2", 'Gamma')
                            ]),
                    ],
                },
            })

        # Assign newly created group configuration to unit
        vertical = self.course_fixture.get_nested_xblocks(
            category="vertical")[0]
        self.course_fixture.create_xblock(
            vertical.locator,
            XBlockFixtureDesc('split_test',
                              'Test Content Experiment',
                              metadata={'user_partition_id': 1}))

        unit = ContainerPage(self.browser, vertical.locator)
        unit.visit()
        experiment = unit.xblocks[0]

        group_configuration_link_name = experiment.group_configuration_link_name

        experiment.go_to_group_configuration_page()
        self.page.wait_for_page()

        # Appropriate Group Configuration is expanded.
        self.assertFalse(
            self.page.experiment_group_configurations[0].is_expanded)
        self.assertTrue(
            self.page.experiment_group_configurations[1].is_expanded)

        self.assertEqual(group_configuration_link_name,
                         self.page.experiment_group_configurations[1].name)

    def test_details_error_validation_message(self):
        """
        Scenario: When a Content Experiment uses a Group Configuration, ensure
        that an error validation message appears if necessary.

        Given I have a course with a Group Configuration containing two Groups
        And a Content Experiment is assigned to that Group Configuration
        When I go to the Group Configuration Page
        Then I do not see a error icon and message in the Group Configuration details view.
        When I add a Group
        Then I see an error icon and message in the Group Configuration details view
        """

        # Create group configuration and associated experiment
        config, _ = self.create_group_configuration_experiment(
            [Group("0", "Group A"),
             Group("1", "Group B")], True)

        # Display details view
        config.toggle()
        # Check that error icon and message are not present
        self.assertFalse(config.details_error_icon_is_present)
        self.assertFalse(config.details_message_is_present)

        # Add a group
        config.toggle()
        config.edit()
        config.add_group()
        config.save()

        # Display details view
        config.toggle()
        # Check that error icon and message are present
        self.assertTrue(config.details_error_icon_is_present)
        self.assertTrue(config.details_message_is_present)
        self.assertIn(
            "This content experiment has issues that affect content visibility.",
            config.details_message_text)

    def test_details_warning_validation_message(self):
        """
        Scenario: When a Content Experiment uses a Group Configuration, ensure
        that a warning validation message appears if necessary.

        Given I have a course with a Group Configuration containing three Groups
        And a Content Experiment is assigned to that Group Configuration
        When I go to the Group Configuration Page
        Then I do not see a warning icon and message in the Group Configuration details view.
        When I remove a Group
        Then I see a warning icon and message in the Group Configuration details view
        """

        # Create group configuration and associated experiment
        config, _ = self.create_group_configuration_experiment([
            Group("0", "Group A"),
            Group("1", "Group B"),
            Group("2", "Group C")
        ], True)

        # Display details view
        config.toggle()
        # Check that warning icon and message are not present
        self.assertFalse(config.details_warning_icon_is_present)
        self.assertFalse(config.details_message_is_present)

        # Remove a group
        config.toggle()
        config.edit()
        config.groups[2].remove()
        config.save()

        # Display details view
        config.toggle()
        # Check that warning icon and message are present
        self.assertTrue(config.details_warning_icon_is_present)
        self.assertTrue(config.details_message_is_present)
        self.assertIn(
            "This content experiment has issues that affect content visibility.",
            config.details_message_text)

    def test_edit_warning_message_empty_usage(self):
        """
        Scenario: When a Group Configuration is not used, ensure that there are no warning icon and message.

        Given I have a course with a Group Configuration containing two Groups
        When I edit the Group Configuration
        Then I do not see a warning icon and message
        """

        # Create a group configuration with no associated experiment and display edit view
        config = self.create_group_configuration_experiment(
            [Group("0", "Group A"),
             Group("1", "Group B")], False)
        config.edit()
        # Check that warning icon and message are not present
        self.assertFalse(config.edit_warning_icon_is_present)
        self.assertFalse(config.edit_warning_message_is_present)

    def test_edit_warning_message_non_empty_usage(self):
        """
        Scenario: When a Group Configuration is used, ensure that there are a warning icon and message.

        Given I have a course with a Group Configuration containing two Groups
        When I edit the Group Configuration
        Then I see a warning icon and message
        """

        # Create a group configuration with an associated experiment and display edit view
        config, _ = self.create_group_configuration_experiment(
            [Group("0", "Group A"),
             Group("1", "Group B")], True)
        config.edit()
        # Check that warning icon and message are present
        self.assertTrue(config.edit_warning_icon_is_present)
        self.assertTrue(config.edit_warning_message_is_present)
        self.assertIn(
            "This configuration is currently used in content experiments. If you make changes to the groups, you may need to edit those experiments.",
            config.edit_warning_message_text)

    def publish_unit_and_verify_groups_in_lms(self,
                                              courseware_page,
                                              group_names,
                                              publish=True):
        """
        Publish first unit in LMS and verify that Courseware page has given Groups
        """
        self.publish_unit_in_lms_and_view(courseware_page, publish)
        self.assertEqual(u'split_test',
                         courseware_page.xblock_component_type())
        self.assertTrue(
            courseware_page.q(css=".split-test-select").is_present())
        rendered_group_names = self.get_select_options(
            page=courseware_page, selector=".split-test-select")
        self.assertListEqual(group_names, rendered_group_names)

    def test_split_test_LMS_staff_view(self):
        """
        Scenario: Ensure that split test is correctly rendered in LMS staff mode as it is
                  and after inactive group removal.

        Given I have a course with group configurations and split test that assigned to first group configuration
        Then I publish split test and view it in LMS in staff view
        And it is rendered correctly
        Then I go to group configuration and delete group
        Then I publish split test and view it in LMS in staff view
        And it is rendered correctly
        Then I go to split test and delete inactive vertical
        Then I publish unit and view unit in LMS in staff view
        And it is rendered correctly
        """

        config, split_test = self.create_group_configuration_experiment([
            Group("0", "Group A"),
            Group("1", "Group B"),
            Group("2", "Group C")
        ], True)
        container = ContainerPage(self.browser, split_test.locator)

        # render in LMS correctly
        courseware_page = CoursewarePage(self.browser, self.course_id)
        self.publish_unit_and_verify_groups_in_lms(
            courseware_page, [u'Group A', u'Group B', u'Group C'])

        # I go to group configuration and delete group
        self.page.visit()
        self.page.q(css='.group-toggle').first.click()
        config.edit()
        config.groups[2].remove()
        config.save()
        self.page.q(css='.group-toggle').first.click()
        self._assert_fields(config,
                            name="Name",
                            description="Description",
                            groups=["Group A", "Group B"])
        self.browser.close()
        self.browser.switch_to_window(self.browser.window_handles[0])

        # render in LMS to see how inactive vertical is rendered
        self.publish_unit_and_verify_groups_in_lms(
            courseware_page,
            [u'Group A', u'Group B', u'Group ID 2 (inactive)'],
            publish=False)

        self.browser.close()
        self.browser.switch_to_window(self.browser.window_handles[0])

        # I go to split test and delete inactive vertical
        container.visit()
        container.delete(0)

        # render in LMS again
        self.publish_unit_and_verify_groups_in_lms(courseware_page,
                                                   [u'Group A', u'Group B'])
Пример #53
0
class SignUpAndSignInTest(UniqueCourseTest):
    """
    Test studio sign-up and sign-in
    """
    shard = 21

    def setUp(self):
        super(SignUpAndSignInTest, self).setUp()
        self.sign_up_page = SignupPage(self.browser)
        self.login_page = LoginPage(self.browser)

        self.course_outline_page = CourseOutlinePage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )
        self.course_outline_sign_in_redirect_page = CourseOutlineSignInRedirectPage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )

        self.course_fixture = CourseFixture(
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run'],
            self.course_info['display_name'],
        )
        self.user = None

    def install_course_fixture(self):
        """
        Install a course fixture
        """
        self.course_fixture.install()
        self.user = self.course_fixture.user

    def test_sign_up_from_home(self):
        """
        Scenario: Sign up from the homepage
        Given I visit the Studio homepage
        When I click the link with the text "Sign Up"
        And I fill in the registration form
        And I press the Create My Account button on the registration form
        Then I should see an email verification prompt
        """
        index_page = IndexPage(self.browser)
        index_page.visit()
        index_page.click_sign_up()

        # Register the user.
        unique_number = uuid.uuid4().hex[:4]
        self.sign_up_page.sign_up_user(
            '{}[email protected]'.format(unique_number),
            '{}-name'.format(unique_number),
            '{}-username'.format(unique_number),
            '{}-password'.format(unique_number),
        )
        home = HomePage(self.browser)
        home.wait_for_page()

    def test_sign_up_with_bad_password(self):
        """
        Scenario: Sign up from the homepage
        Given I visit the Studio homepage
        When I click the link with the text "Sign Up"
        And I fill in the registration form
        When I enter an insufficient password and focus out
        I should see an error message
        """
        index_page = IndexPage(self.browser)
        index_page.visit()
        index_page.click_sign_up()

        password_input = self.sign_up_page.input_password('a')  # Arbitrary short password that will fail
        password_input.send_keys(Keys.TAB)  # Focus out of the element
        index_page.wait_for_element_visibility('#register-password-validation-error', 'Password Error Message')
        self.assertIsNotNone(index_page.q(css='#register-password-validation-error-msg'))  # Error message should exist

    def test_login_with_valid_redirect(self):
        """
        Scenario: Login with a valid redirect
        Given I have opened a new course in Studio
        And I am not logged in
        And I visit the url "/course/slashes:MITx+999+Robot_Super_Course"
        And I should see that the path is "/signin?next=/course/slashes%3AMITx%2B999%2BRobot_Super_Course"
        When I fill in and submit the signin form
        Then I should see that the path is "/course/slashes:MITx+999+Robot_Super_Course"
        """
        self.install_course_fixture()
        # Get the url, browser should land here after sign in.
        course_url = self.course_outline_sign_in_redirect_page.url
        self.course_outline_sign_in_redirect_page.visit()
        # Login
        self.course_outline_sign_in_redirect_page.login(self.user['email'], self.user['password'])
        self.course_outline_page.wait_for_page()
        # Verify that correct course is displayed after sign in.
        self.assertEqual(self.browser.current_url, course_url)

    def test_login_with_invalid_redirect(self):
        """
        Scenario: Login with an invalid redirect
        Given I have opened a new course in Studio
        And I am not logged in
        And I visit the url "/signin?next=http://www.google.com/"
        When I fill in and submit the signin form
        Then I should see that the path is "/home/"
        """
        self.install_course_fixture()
        # Visit course
        self.course_outline_sign_in_redirect_page.visit()
        # Change redirect url
        self.browser.get(self.browser.current_url.split('=')[0] + '=http://www.google.com')
        # Login
        self.course_outline_sign_in_redirect_page.login(self.user['email'], self.user['password'])
        # Verify that we land in LMS instead of the invalid redirect url
        self.assertEqual(self.browser.current_url, LMS_URL + "/dashboard")

    def test_login_with_mistyped_credentials(self):
        """
        Given I have opened a new course in Studio
        And I am not logged in
        And I visit the Studio homepage
        When I click the link with the text "Sign In"
        Then I should see that the path is "/signin"
        And I should not see a login error message
        And I fill in and submit the signin form incorrectly
        Then I should see a login error message
        And I edit the password field
        Then I should not see a login error message
        And I submit the signin form
        And I wait for "2" seconds
        Then I should see that the path is "/course/slashes:MITx+999+Robot_Super_Course"
        """
        self.install_course_fixture()
        self.course_outline_sign_in_redirect_page.visit()
        # Verify login_error is not present
        self.course_outline_sign_in_redirect_page.wait_for_element_absence(
            '#login_error',
            'Login error not be present'
        )
        # Login with wrong credentials
        self.course_outline_sign_in_redirect_page.login(
            self.user['email'],
            'wrong_password',
            expect_success=False
        )
        # Verify that login error is shown
        self.course_outline_sign_in_redirect_page.wait_for_element_visibility(
            ".js-form-errors.status.submission-error",
            'Login error is visible'
        )
        # Login with correct credentials
        self.course_outline_sign_in_redirect_page.login(self.user['email'], self.user['password'])
        self.course_outline_page.wait_for_page()
        # Verify that correct course is displayed after sign in.
        self.assertEqual(self.browser.current_url, self.course_outline_page.url)
class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest, TestWithSearchIndexMixin):
    """
    Test Library Content block in LMS
    """
    shard = 17

    def setUp(self):
        """
        Install library with some content and a course using fixtures
        """
        self._create_search_index()
        super(StudioLibraryContainerTest, self).setUp()
        # Also create a course:
        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()
        self.outline = CourseOutlinePage(
            self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run']
        )

        self.outline.visit()
        subsection = self.outline.section(SECTION_NAME).subsection(SUBSECTION_NAME)
        self.unit_page = subsection.expand_subsection().unit(UNIT_NAME).go_to()

    def tearDown(self):
        """ Tear down method: remove search index backing file """
        self._cleanup_index_file()
        super(StudioLibraryContainerTest, self).tearDown()

    def populate_library_fixture(self, library_fixture):
        """
        Populate the children of the test course fixture.
        """
        library_fixture.add_children(
            XBlockFixtureDesc("html", "Html1"),
            XBlockFixtureDesc("html", "Html2"),
            XBlockFixtureDesc("html", "Html3"),
        )

    def populate_course_fixture(self, course_fixture):
        """ Install a course with sections/problems, tabs, updates, and handouts """
        library_content_metadata = {
            'source_library_id': six.text_type(self.library_key),
            'mode': 'random',
            'max_count': 1,
        }

        course_fixture.add_children(
            XBlockFixtureDesc('chapter', SECTION_NAME).add_children(
                XBlockFixtureDesc('sequential', SUBSECTION_NAME).add_children(
                    XBlockFixtureDesc('vertical', UNIT_NAME).add_children(
                        XBlockFixtureDesc('library_content', "Library Content", metadata=library_content_metadata)
                    )
                )
            )
        )

    def _get_library_xblock_wrapper(self, xblock):
        """
        Wraps xblock into :class:`...pages.studio.library.StudioLibraryContainerXBlockWrapper`
        """
        return StudioLibraryContainerXBlockWrapper.from_xblock_wrapper(xblock)

    @ddt.data(1, 2, 3)
    def test_can_edit_metadata(self, max_count):
        """
        Scenario: Given I have a library, a course and library content xblock in a course
        When I go to studio unit page for library content block
        And I edit library content metadata and save it
        Then I can ensure that data is persisted
        """
        library_name = self.library_info['display_name']
        library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[1])
        library_container.edit()
        edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator)
        edit_modal.library_name = library_name
        edit_modal.count = max_count

        library_container.save_settings()  # saving settings

        # open edit window again to verify changes are persistent
        library_container.edit()
        edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator)
        self.assertEqual(edit_modal.library_name, library_name)
        self.assertEqual(edit_modal.count, max_count)

    def test_no_content_message(self):
        """
        Scenario: Given I have a library, a course and library content xblock in a course
        When I go to studio unit page for library content block
        And I set Problem Type selector so that no libraries have matching content
        Then I can see that "No matching content" warning is shown
        When I set Problem Type selector so that there is matching content
        Then I can see that warning messages are not shown
        """
        # Add a single "Dropdown" type problem to the library (which otherwise has only HTML blocks):
        self.library_fixture.create_xblock(self.library_fixture.library_location, XBlockFixtureDesc(
            "problem", "Dropdown",
            data=textwrap.dedent("""
                <problem>
                    <p>Dropdown</p>
                    <optionresponse><optioninput label="Dropdown" options="('1', '2')" correct="'2'"></optioninput></optionresponse>
                </problem>
                """)
        ))

        expected_text = 'There are no matching problem types in the specified libraries. Select another problem type'

        library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[1])

        # precondition check - assert library has children matching filter criteria
        self.assertFalse(library_container.has_validation_error)
        self.assertFalse(library_container.has_validation_warning)

        library_container.edit()
        edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator)
        self.assertEqual(edit_modal.capa_type, "Any Type")  # precondition check
        edit_modal.capa_type = "Custom Evaluated Script"

        library_container.save_settings()

        self.assertTrue(library_container.has_validation_warning)
        self.assertIn(expected_text, library_container.validation_warning_text)

        library_container.edit()
        edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator)
        self.assertEqual(edit_modal.capa_type, "Custom Evaluated Script")  # precondition check
        edit_modal.capa_type = "Dropdown"
        library_container.save_settings()

        # Library should contain single Dropdown problem, so now there should be no errors again
        self.assertFalse(library_container.has_validation_error)
        self.assertFalse(library_container.has_validation_warning)

    def test_cannot_manage(self):
        """
        Scenario: Given I have a library, a course and library content xblock in a course
        When I go to studio unit page for library content block
        And when I click the "View" link
        Then I can see a preview of the blocks drawn from the library.

        And I do not see a duplicate button
        And I do not see a delete button
        """
        block_wrapper_unit_page = self._get_library_xblock_wrapper(self.unit_page.xblocks[0].children[0])
        container_page = block_wrapper_unit_page.go_to_container()

        for block in container_page.xblocks:
            self.assertFalse(block.has_duplicate_button)
            self.assertFalse(block.has_delete_button)
            self.assertFalse(block.has_edit_visibility_button)
Пример #55
0
class CoursewareSearchTest(UniqueCourseTest):
    """
    Test courseware search.
    """
    USERNAME = '******'
    EMAIL = '*****@*****.**'

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

    HTML_CONTENT = """
            Someday I'll wish upon a star
            And wake up where the clouds are far
            Behind me.
            Where troubles melt like lemon drops
            Away above the chimney tops
            That's where you'll find me.
        """
    SEARCH_STRING = "chimney"
    EDITED_CHAPTER_NAME = "Section 2 - edited"
    EDITED_SEARCH_STRING = "edited"

    TEST_INDEX_FILENAME = "test_root/index_file.dat"
    shard = 5

    def setUp(self):
        """
        Create search page and course content to search
        """
        # create test file in which index for this test will live
        with open(self.TEST_INDEX_FILENAME, "w+") as index_file:
            json.dump({}, index_file)
        self.addCleanup(remove_file, self.TEST_INDEX_FILENAME)

        super(CoursewareSearchTest, self).setUp()

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

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

        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', 'Section 1').add_children(
                XBlockFixtureDesc('sequential', 'Subsection 1'))).add_children(
                    XBlockFixtureDesc('chapter', 'Section 2').add_children(
                        XBlockFixtureDesc('sequential',
                                          'Subsection 2'))).install()

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

    def _studio_publish_content(self, section_index):
        """
        Publish content on studio course page under specified section
        """
        self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True)
        self.studio_course_outline.visit()
        subsection = self.studio_course_outline.section_at(
            section_index).subsection_at(0)
        subsection.expand_subsection()
        unit = subsection.unit_at(0)
        unit.publish()

    def _studio_edit_chapter_name(self, section_index):
        """
        Edit chapter name on studio course page under specified section
        """
        self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True)
        self.studio_course_outline.visit()
        section = self.studio_course_outline.section_at(section_index)
        section.change_name(self.EDITED_CHAPTER_NAME)

    def _studio_add_content(self, section_index):
        """
        Add content on studio course page under specified section
        """

        self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True)
        # create a unit in course outline
        self.studio_course_outline.visit()
        subsection = self.studio_course_outline.section_at(
            section_index).subsection_at(0)
        subsection.expand_subsection()
        subsection.add_unit()

        # got to unit and create an HTML component and save (not publish)
        unit_page = ContainerPage(self.browser, None)
        unit_page.wait_for_page()
        add_html_component(unit_page, 0)
        unit_page.wait_for_element_presence('.edit-button',
                                            'Edit button is visible')
        click_css(unit_page, '.edit-button', 0, require_notification=False)
        unit_page.wait_for_element_visibility('.modal-editor',
                                              'Modal editor is visible')
        type_in_codemirror(unit_page, 0, self.HTML_CONTENT)
        click_css(unit_page, '.action-save', 0)

    def _studio_reindex(self):
        """
        Reindex course content on studio course page
        """

        self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True)
        self.studio_course_outline.visit()
        self.studio_course_outline.start_reindex()
        self.studio_course_outline.wait_for_ajax()

    def _search_for_content(self, search_term):
        """
        Login and search for specific content

        Arguments:
            search_term - term to be searched for

        Returns:
            (bool) True if search term is found in resulting content; False if not found
        """
        self._auto_auth(self.USERNAME, self.EMAIL, False)
        self.course_home_page.visit()
        course_search_results_page = self.course_home_page.search_for_term(
            search_term)
        if len(course_search_results_page.search_results.html) > 0:
            search_string = course_search_results_page.search_results.html[0]
        else:
            search_string = ""
        return search_term in search_string

    # TODO: TNL-6546: Remove usages of sidebar search
    def _search_for_content_in_sidebar(self,
                                       search_term,
                                       perform_auto_auth=True):
        """
        Login and search for specific content in the legacy sidebar search
        Arguments:
            search_term - term to be searched for
            perform_auto_auth - if False, skip auto_auth call.
        Returns:
            (bool) True if search term is found in resulting content; False if not found
        """
        if perform_auto_auth:
            self._auto_auth(self.USERNAME, self.EMAIL, False)
        self.courseware_search_page = CoursewareSearchPage(
            self.browser, self.course_id)
        self.courseware_search_page.visit()
        self.courseware_search_page.search_for_term(search_term)
        return search_term in self.courseware_search_page.search_results.html[
            0]

    def test_search(self):
        """
        Make sure that you can search for something.
        """

        # Create content in studio without publishing.
        self._studio_add_content(0)

        # Do a search, there should be no results shown.
        self.assertFalse(self._search_for_content(self.SEARCH_STRING))

        # Do a search in the legacy sidebar, there should be no results shown.
        self.assertFalse(
            self._search_for_content_in_sidebar(self.SEARCH_STRING, False))

        # Publish in studio to trigger indexing.
        self._studio_publish_content(0)

        # Do the search again, this time we expect results.
        self.assertTrue(self._search_for_content(self.SEARCH_STRING))

        # Do the search again in the legacy sidebar, this time we expect results.
        self.assertTrue(
            self._search_for_content_in_sidebar(self.SEARCH_STRING, False))
Пример #56
0
class CoursewareSearchCohortTest(ContainerBase, CohortTestMixin):
    """
    Test courseware search.
    """
    shard = 1
    TEST_INDEX_FILENAME = "test_root/index_file.dat"

    def setUp(self, is_staff=True):
        """
        Create search page and course content to search
        """
        # create test file in which index for this test will live
        with open(self.TEST_INDEX_FILENAME, "w+") as index_file:
            json.dump({}, index_file)
        self.addCleanup(remove_file, self.TEST_INDEX_FILENAME)

        super(CoursewareSearchCohortTest, self).setUp(is_staff=is_staff)
        self.staff_user = self.user

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

        self.content_group_a = "Content Group A"
        self.content_group_b = "Content Group B"

        # Create a student who will be in "Cohort A"
        self.cohort_a_student_username = "******" + str(
            uuid.uuid4().hex)[:12]
        self.cohort_a_student_email = self.cohort_a_student_username + "@example.com"
        AutoAuthPage(self.browser,
                     username=self.cohort_a_student_username,
                     email=self.cohort_a_student_email,
                     no_login=True).visit()

        # Create a student who will be in "Cohort B"
        self.cohort_b_student_username = "******" + str(
            uuid.uuid4().hex)[:12]
        self.cohort_b_student_email = self.cohort_b_student_username + "@example.com"
        AutoAuthPage(self.browser,
                     username=self.cohort_b_student_username,
                     email=self.cohort_b_student_email,
                     no_login=True).visit()

        # Create a student who will end up in the default cohort group
        self.cohort_default_student_username = "******"
        self.cohort_default_student_email = "*****@*****.**"
        AutoAuthPage(self.browser,
                     username=self.cohort_default_student_username,
                     email=self.cohort_default_student_email,
                     no_login=True).visit()

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

        # Enable Cohorting and assign cohorts and content groups
        self._auto_auth(self.staff_user["username"], self.staff_user["email"],
                        True)
        self.enable_cohorting(self.course_fixture)
        self.create_content_groups()
        self.link_html_to_content_groups_and_publish()
        self.create_cohorts_and_assign_students()

        self._studio_reindex()

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

    def _studio_reindex(self):
        """
        Reindex course content on studio course page
        """
        self._auto_auth(self.staff_user["username"], self.staff_user["email"],
                        True)
        self.studio_course_outline.visit()
        self.studio_course_outline.start_reindex()
        self.studio_course_outline.wait_for_ajax()

    def _goto_staff_page(self):
        """
        Open staff page with assertion
        """
        self.course_home_page.visit()
        self.course_home_page.resume_course_from_header()
        staff_page = StaffCoursewarePage(self.browser, self.course_id)
        self.assertEqual(staff_page.staff_view_mode, 'Staff')
        return staff_page

    def _search_for_term(self, term):
        """
        Search for term in course and return results.
        """
        self.course_home_page.visit()
        course_search_results_page = self.course_home_page.search_for_term(
            term)
        results = course_search_results_page.search_results.html
        return results[0] if len(results) > 0 else []

    def populate_course_fixture(self, course_fixture):
        """
        Populate the children of the test course fixture.
        """
        self.group_a_html = 'GROUPACONTENT'
        self.group_b_html = 'GROUPBCONTENT'
        self.group_a_and_b_html = 'GROUPAANDBCONTENT'
        self.visible_to_all_html = 'VISIBLETOALLCONTENT'

        course_fixture.add_children(
            XBlockFixtureDesc('chapter', 'Test Section').add_children(
                XBlockFixtureDesc('sequential', 'Test Subsection').
                add_children(
                    XBlockFixtureDesc('vertical', 'Test Unit').add_children(
                        XBlockFixtureDesc('html',
                                          self.group_a_html,
                                          data='<html>GROUPACONTENT</html>'),
                        XBlockFixtureDesc('html',
                                          self.group_b_html,
                                          data='<html>GROUPBCONTENT</html>'),
                        XBlockFixtureDesc(
                            'html',
                            self.group_a_and_b_html,
                            data='<html>GROUPAANDBCONTENT</html>'),
                        XBlockFixtureDesc(
                            'html',
                            self.visible_to_all_html,
                            data='<html>VISIBLETOALLCONTENT</html>')))))

    def create_content_groups(self):
        """
        Creates two content groups in Studio Group Configurations Settings.
        """
        group_configurations_page = GroupConfigurationsPage(
            self.browser, self.course_info['org'], self.course_info['number'],
            self.course_info['run'])
        group_configurations_page.visit()

        group_configurations_page.create_first_content_group()
        config = group_configurations_page.content_groups[0]
        config.name = self.content_group_a
        config.save()

        group_configurations_page.add_content_group()
        config = group_configurations_page.content_groups[1]
        config.name = self.content_group_b
        config.save()

    def link_html_to_content_groups_and_publish(self):
        """
        Updates 3 of the 4 existing html to limit their visibility by content group.
        Publishes the modified units.
        """
        container_page = self.go_to_unit_page()

        def set_visibility(html_block_index, groups):
            """
            Set visibility on html blocks to specified groups.
            """
            html_block = container_page.xblocks[html_block_index]
            html_block.edit_visibility()
            visibility_dialog = XBlockVisibilityEditorView(
                self.browser, html_block.locator)
            visibility_dialog.select_groups_in_partition_scheme(
                visibility_dialog.CONTENT_GROUP_PARTITION, groups)

        set_visibility(1, [self.content_group_a])
        set_visibility(2, [self.content_group_b])
        set_visibility(3, [self.content_group_a, self.content_group_b])

        container_page.publish()

    def create_cohorts_and_assign_students(self):
        """
        Adds 2 manual cohorts, linked to content groups, to the course.
        Each cohort is assigned one student.
        """
        instructor_dashboard_page = InstructorDashboardPage(
            self.browser, self.course_id)
        instructor_dashboard_page.visit()
        cohort_management_page = instructor_dashboard_page.select_cohort_management(
        )

        def add_cohort_with_student(cohort_name, content_group, student):
            """
            Create cohort and assign student to it.
            """
            cohort_management_page.add_cohort(cohort_name,
                                              content_group=content_group)
            cohort_management_page.add_students_to_selected_cohort([student])

        add_cohort_with_student("Cohort A", self.content_group_a,
                                self.cohort_a_student_username)
        add_cohort_with_student("Cohort B", self.content_group_b,
                                self.cohort_b_student_username)
        cohort_management_page.wait_for_ajax()

    def test_cohorted_search_user_a_a_content(self):
        """
        Test user can search content restricted to his cohort.
        """
        self._auto_auth(self.cohort_a_student_username,
                        self.cohort_a_student_email, False)
        search_results = self._search_for_term(self.group_a_html)
        assert self.group_a_html in search_results

    def test_cohorted_search_user_b_a_content(self):
        """
        Test user can not search content restricted to his cohort.
        """
        self._auto_auth(self.cohort_b_student_username,
                        self.cohort_b_student_email, False)
        search_results = self._search_for_term(self.group_a_html)
        assert self.group_a_html not in search_results

    def test_cohorted_search_user_staff_all_content(self):
        """
        Test staff user can search all public content if cohorts used on course.
        """
        self._auto_auth(self.staff_user["username"], self.staff_user["email"],
                        False)
        self._goto_staff_page().set_staff_view_mode('Staff')
        search_results = self._search_for_term(self.visible_to_all_html)
        assert self.visible_to_all_html in search_results
        search_results = self._search_for_term(self.group_a_and_b_html)
        assert self.group_a_and_b_html in search_results
        search_results = self._search_for_term(self.group_a_html)
        assert self.group_a_html in search_results
        search_results = self._search_for_term(self.group_b_html)
        assert self.group_b_html in search_results

    def test_cohorted_search_user_staff_masquerade_student_content(self):
        """
        Test staff user can search just student public content if selected from preview menu.

        NOTE: Although it would be wise to combine these masquerading tests into
        a single test due to expensive setup, doing so revealed a very low
        priority bug where searching seems to stick/cache the access of the
        first user who searches for future searches.

        """
        self._auto_auth(self.staff_user["username"], self.staff_user["email"],
                        False)
        self._goto_staff_page().set_staff_view_mode('Learner')
        search_results = self._search_for_term(self.visible_to_all_html)
        assert self.visible_to_all_html in search_results
        search_results = self._search_for_term(self.group_a_and_b_html)
        assert self.group_a_and_b_html not in search_results
        search_results = self._search_for_term(self.group_a_html)
        assert self.group_a_html not in search_results
        search_results = self._search_for_term(self.group_b_html)
        assert self.group_b_html not in search_results

    def test_cohorted_search_user_staff_masquerade_cohort_content(self):
        """
        Test staff user can search cohort and public content if selected from preview menu.
        """
        self._auto_auth(self.staff_user["username"], self.staff_user["email"],
                        False)
        self._goto_staff_page().set_staff_view_mode('Learner in ' +
                                                    self.content_group_a)
        search_results = self._search_for_term(self.visible_to_all_html)
        assert self.visible_to_all_html in search_results
        search_results = self._search_for_term(self.group_a_and_b_html)
        assert self.group_a_and_b_html in search_results
        search_results = self._search_for_term(self.group_a_html)
        assert self.group_a_html in search_results
        search_results = self._search_for_term(self.group_b_html)
        assert self.group_b_html not in search_results
Пример #57
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])
class CoursewareSearchCohortTest(ContainerBase):
    """
    Test courseware search.
    """

    TEST_INDEX_FILENAME = "test_root/index_file.dat"

    def setUp(self, is_staff=True):
        """
        Create search page and course content to search
        """
        # create test file in which index for this test will live
        with open(self.TEST_INDEX_FILENAME, "w+") as index_file:
            json.dump({}, index_file)
        self.addCleanup(remove_file, self.TEST_INDEX_FILENAME)

        super(CoursewareSearchCohortTest, self).setUp(is_staff=is_staff)
        self.staff_user = self.user

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

        self.content_group_a = "Content Group A"
        self.content_group_b = "Content Group B"

        # Create a student who will be in "Cohort A"
        self.cohort_a_student_username = "******" + str(
            uuid.uuid4().hex)[:12]
        self.cohort_a_student_email = self.cohort_a_student_username + "@example.com"
        StudioAutoAuthPage(self.browser,
                           username=self.cohort_a_student_username,
                           email=self.cohort_a_student_email,
                           no_login=True).visit()

        # Create a student who will be in "Cohort B"
        self.cohort_b_student_username = "******" + str(
            uuid.uuid4().hex)[:12]
        self.cohort_b_student_email = self.cohort_b_student_username + "@example.com"
        StudioAutoAuthPage(self.browser,
                           username=self.cohort_b_student_username,
                           email=self.cohort_b_student_email,
                           no_login=True).visit()

        # Create a student who will end up in the default cohort group
        self.cohort_default_student_username = "******"
        self.cohort_default_student_email = "*****@*****.**"
        StudioAutoAuthPage(self.browser,
                           username=self.cohort_default_student_username,
                           email=self.cohort_default_student_email,
                           no_login=True).visit()

        self.courseware_search_page = CoursewareSearchPage(
            self.browser, self.course_id)

        # Enable Cohorting and assign cohorts and content groups
        self._auto_auth(self.staff_user["username"], self.staff_user["email"],
                        True)
        self.enable_cohorting(self.course_fixture)
        self.create_content_groups()
        self.link_html_to_content_groups_and_publish()
        self.create_cohorts_and_assign_students()

        self._studio_reindex()

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

    def _studio_reindex(self):
        """
        Reindex course content on studio course page
        """
        self._auto_auth(self.staff_user["username"], self.staff_user["email"],
                        True)
        self.studio_course_outline.visit()
        self.studio_course_outline.start_reindex()
        self.studio_course_outline.wait_for_ajax()

    def _goto_staff_page(self):
        """
        Open staff page with assertion
        """
        self.courseware_search_page.visit()
        staff_page = StaffPage(self.browser, self.course_id)
        self.assertEqual(staff_page.staff_view_mode, 'Staff')
        return staff_page

    def populate_course_fixture(self, course_fixture):
        """
        Populate the children of the test course fixture.
        """
        self.group_a_html = 'GROUPACONTENT'
        self.group_b_html = 'GROUPBCONTENT'
        self.group_a_and_b_html = 'GROUPAANDBCONTENT'
        self.visible_to_all_html = 'VISIBLETOALLCONTENT'

        course_fixture.add_children(
            XBlockFixtureDesc('chapter', 'Test Section').add_children(
                XBlockFixtureDesc('sequential', 'Test Subsection').
                add_children(
                    XBlockFixtureDesc('vertical', 'Test Unit').add_children(
                        XBlockFixtureDesc('html',
                                          self.group_a_html,
                                          data='<html>GROUPACONTENT</html>'),
                        XBlockFixtureDesc('html',
                                          self.group_b_html,
                                          data='<html>GROUPBCONTENT</html>'),
                        XBlockFixtureDesc(
                            'html',
                            self.group_a_and_b_html,
                            data='<html>GROUPAANDBCONTENT</html>'),
                        XBlockFixtureDesc(
                            'html',
                            self.visible_to_all_html,
                            data='<html>VISIBLETOALLCONTENT</html>')))))

    def enable_cohorting(self, course_fixture):
        """
        Enables cohorting for the current course.
        """
        url = LMS_BASE_URL + "/courses/" + course_fixture._course_key + '/cohorts/settings'  # pylint: disable=protected-access
        data = json.dumps({'is_cohorted': True})
        response = course_fixture.session.patch(url,
                                                data=data,
                                                headers=course_fixture.headers)
        self.assertTrue(response.ok, "Failed to enable cohorts")

    def create_content_groups(self):
        """
        Creates two content groups in Studio Group Configurations Settings.
        """
        group_configurations_page = GroupConfigurationsPage(
            self.browser, self.course_info['org'], self.course_info['number'],
            self.course_info['run'])
        group_configurations_page.visit()

        group_configurations_page.create_first_content_group()
        config = group_configurations_page.content_groups[0]
        config.name = self.content_group_a
        config.save()

        group_configurations_page.add_content_group()
        config = group_configurations_page.content_groups[1]
        config.name = self.content_group_b
        config.save()

    def link_html_to_content_groups_and_publish(self):
        """
        Updates 3 of the 4 existing html to limit their visibility by content group.
        Publishes the modified units.
        """
        container_page = self.go_to_unit_page()

        def set_visibility(html_block_index,
                           content_group,
                           second_content_group=None):
            """
            Set visibility on html blocks to specified groups.
            """
            html_block = container_page.xblocks[html_block_index]
            html_block.edit_visibility()
            if second_content_group:
                ComponentVisibilityEditorView(
                    self.browser,
                    html_block.locator).select_option(second_content_group,
                                                      save=False)
            ComponentVisibilityEditorView(
                self.browser, html_block.locator).select_option(content_group)

        set_visibility(1, self.content_group_a)
        set_visibility(2, self.content_group_b)
        set_visibility(3, self.content_group_a, self.content_group_b)
        set_visibility(4,
                       'All Students and Staff')  # Does not work without this

        container_page.publish_action.click()

    def create_cohorts_and_assign_students(self):
        """
        Adds 2 manual cohorts, linked to content groups, to the course.
        Each cohort is assigned one student.
        """
        instructor_dashboard_page = InstructorDashboardPage(
            self.browser, self.course_id)
        instructor_dashboard_page.visit()
        cohort_management_page = instructor_dashboard_page.select_cohort_management(
        )

        def add_cohort_with_student(cohort_name, content_group, student):
            """
            Create cohort and assign student to it.
            """
            cohort_management_page.add_cohort(cohort_name,
                                              content_group=content_group)
            cohort_management_page.add_students_to_selected_cohort([student])

        add_cohort_with_student("Cohort A", self.content_group_a,
                                self.cohort_a_student_username)
        add_cohort_with_student("Cohort B", self.content_group_b,
                                self.cohort_b_student_username)
        cohort_management_page.wait_for_ajax()

    def test_page_existence(self):
        """
        Make sure that the page is accessible.
        """
        self._auto_auth(self.cohort_default_student_username,
                        self.cohort_default_student_email, False)
        self.courseware_search_page.visit()

    def test_cohorted_search_user_a_a_content(self):
        """
        Test user can search content restricted to his cohort.
        """
        self._auto_auth(self.cohort_a_student_username,
                        self.cohort_a_student_email, False)
        self.courseware_search_page.visit()
        self.courseware_search_page.search_for_term(self.group_a_html)
        assert self.group_a_html in self.courseware_search_page.search_results.html[
            0]

    def test_cohorted_search_user_b_a_content(self):
        """
        Test user can not search content restricted to his cohort.
        """
        self._auto_auth(self.cohort_b_student_username,
                        self.cohort_b_student_email, False)
        self.courseware_search_page.visit()
        self.courseware_search_page.search_for_term(self.group_a_html)
        assert self.group_a_html not in self.courseware_search_page.search_results.html[
            0]

    def test_cohorted_search_user_default_ab_content(self):
        """
        Test user not enrolled in any cohorts can't see any of restricted content.
        """
        self._auto_auth(self.cohort_default_student_username,
                        self.cohort_default_student_email, False)
        self.courseware_search_page.visit()
        self.courseware_search_page.search_for_term(self.group_a_and_b_html)
        assert self.group_a_and_b_html not in self.courseware_search_page.search_results.html[
            0]

    def test_cohorted_search_user_default_all_content(self):
        """
        Test user can search public content if cohorts used on course.
        """
        self._auto_auth(self.cohort_default_student_username,
                        self.cohort_default_student_email, False)
        self.courseware_search_page.visit()
        self.courseware_search_page.search_for_term(self.visible_to_all_html)
        assert self.visible_to_all_html in self.courseware_search_page.search_results.html[
            0]

    def test_cohorted_search_user_staff_all_content(self):
        """
        Test staff user can search all public content if cohorts used on course.
        """
        self._auto_auth(self.staff_user["username"], self.staff_user["email"],
                        False)
        self._goto_staff_page().set_staff_view_mode('Staff')
        self.courseware_search_page.search_for_term(self.visible_to_all_html)
        assert self.visible_to_all_html in self.courseware_search_page.search_results.html[
            0]
        self.courseware_search_page.clear_search()
        self.courseware_search_page.search_for_term(self.group_a_and_b_html)
        assert self.group_a_and_b_html in self.courseware_search_page.search_results.html[
            0]
        self.courseware_search_page.clear_search()
        self.courseware_search_page.search_for_term(self.group_a_html)
        assert self.group_a_html in self.courseware_search_page.search_results.html[
            0]
        self.courseware_search_page.clear_search()
        self.courseware_search_page.search_for_term(self.group_b_html)
        assert self.group_b_html in self.courseware_search_page.search_results.html[
            0]

    def test_cohorted_search_user_staff_masquerade_student_content(self):
        """
        Test staff user can search just student public content if selected from preview menu.
        """
        self._auto_auth(self.staff_user["username"], self.staff_user["email"],
                        False)
        self._goto_staff_page().set_staff_view_mode('Student')
        self.courseware_search_page.search_for_term(self.visible_to_all_html)
        assert self.visible_to_all_html in self.courseware_search_page.search_results.html[
            0]
        self.courseware_search_page.clear_search()
        self.courseware_search_page.search_for_term(self.group_a_and_b_html)
        assert self.group_a_and_b_html not in self.courseware_search_page.search_results.html[
            0]
        self.courseware_search_page.clear_search()
        self.courseware_search_page.search_for_term(self.group_a_html)
        assert self.group_a_html not in self.courseware_search_page.search_results.html[
            0]
        self.courseware_search_page.clear_search()
        self.courseware_search_page.search_for_term(self.group_b_html)
        assert self.group_b_html not in self.courseware_search_page.search_results.html[
            0]

    def test_cohorted_search_user_staff_masquerade_cohort_content(self):
        """
        Test staff user can search cohort and public content if selected from preview menu.
        """
        self._auto_auth(self.staff_user["username"], self.staff_user["email"],
                        False)
        self._goto_staff_page().set_staff_view_mode('Student in ' +
                                                    self.content_group_a)
        self.courseware_search_page.search_for_term(self.visible_to_all_html)
        assert self.visible_to_all_html in self.courseware_search_page.search_results.html[
            0]
        self.courseware_search_page.clear_search()
        self.courseware_search_page.search_for_term(self.group_a_and_b_html)
        assert self.group_a_and_b_html in self.courseware_search_page.search_results.html[
            0]
        self.courseware_search_page.clear_search()
        self.courseware_search_page.search_for_term(self.group_a_html)
        assert self.group_a_html in self.courseware_search_page.search_results.html[
            0]
        self.courseware_search_page.clear_search()
        self.courseware_search_page.search_for_term(self.group_b_html)
        assert self.group_b_html not in self.courseware_search_page.search_results.html[
            0]
class SplitTestCoursewareSearchTest(ContainerBase):
    """
    Test courseware search on Split Test Module.
    """
    USERNAME = '******'
    EMAIL = '*****@*****.**'

    TEST_INDEX_FILENAME = "test_root/index_file.dat"

    def setUp(self, is_staff=True):
        """
        Create search page and course content to search
        """
        # create test file in which index for this test will live
        with open(self.TEST_INDEX_FILENAME, "w+") as index_file:
            json.dump({}, index_file)
        self.addCleanup(remove_file, self.TEST_INDEX_FILENAME)

        super(SplitTestCoursewareSearchTest, self).setUp(is_staff=is_staff)
        self.staff_user = self.user

        self.courseware_search_page = CoursewareSearchPage(
            self.browser, self.course_id)
        self.course_navigation_page = CourseNavPage(self.browser)
        self.course_outline = CourseOutlinePage(self.browser,
                                                self.course_info['org'],
                                                self.course_info['number'],
                                                self.course_info['run'])

        self._create_group_configuration()
        self._studio_reindex()

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

    def _studio_reindex(self):
        """
        Reindex course content on studio course page
        """
        self._auto_auth(self.staff_user["username"], self.staff_user["email"],
                        True)
        self.course_outline.visit()
        self.course_outline.start_reindex()
        self.course_outline.wait_for_ajax()

    def _create_group_configuration(self):
        """
        Create a group configuration for course
        """
        # pylint: disable=protected-access
        self.course_fixture._update_xblock(
            self.course_fixture._course_location, {
                "metadata": {
                    u"user_partitions": [
                        create_user_partition_json(
                            0, "Configuration A/B", "Content Group Partition.",
                            [Group("0", "Group A"),
                             Group("1", "Group B")])
                    ]
                }
            })

    def populate_course_fixture(self, course_fixture):
        """
        Populate the children of the test course fixture.
        """
        course_fixture.add_advanced_settings({
            u"advanced_modules": {
                "value": ["split_test"]
            },
        })

        course_fixture.add_children(
            XBlockFixtureDesc('chapter', 'Test Section').add_children(
                XBlockFixtureDesc(
                    'sequential', 'Test Subsection').add_children(
                        XBlockFixtureDesc(
                            'vertical', 'Test Unit').add_children(
                                XBlockFixtureDesc(
                                    'html',
                                    'VISIBLE TO A',
                                    data='<html>VISIBLE TO A</html>',
                                    metadata={"group_access": {
                                        0: [0]
                                    }}),
                                XBlockFixtureDesc(
                                    'html',
                                    'VISIBLE TO B',
                                    data='<html>VISIBLE TO B</html>',
                                    metadata={"group_access": {
                                        0: [1]
                                    }})))))

    def test_page_existence(self):
        """
        Make sure that the page is accessible.
        """
        self._auto_auth(self.USERNAME, self.EMAIL, False)
        self.courseware_search_page.visit()

    def test_search_for_experiment_content_user_assigned_to_one_group(self):
        """
        Test user can search for experiment content restricted to his group
        when assigned to just one experiment group
        """
        self._auto_auth(self.USERNAME, self.EMAIL, False)
        self.courseware_search_page.visit()
        self.courseware_search_page.search_for_term("VISIBLE TO")
        assert "1 result" in self.courseware_search_page.search_results.html[0]