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):
            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)
Ejemplo n.º 2
0
    def create_cohorts_and_assign_students(self, student_a_username, student_b_username):
        """
        Adds 2 manual cohorts, linked to content groups, to the course.
        Each cohort is assigned one learner.
        """
        instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
        instructor_dashboard_page.visit()
        cohort_management_page = instructor_dashboard_page.select_cohort_management()
        cohort_management_page.is_cohorted = True

        def add_cohort_with_student(cohort_name, content_group, student):
            """ Create cohort and assign learner 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 Alpha", "alpha", student_a_username)
        add_cohort_with_student("Cohort Beta", "beta", student_b_username)
        cohort_management_page.wait_for_ajax()
    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):
            cohort_management_page.add_cohort(cohort_name, content_group=content_group)
            # After adding the cohort, it should automatically be selected
            EmptyPromise(
                lambda: cohort_name == cohort_management_page.get_selected_cohort(), "Waiting for new cohort"
            ).fulfill()
            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)
Ejemplo n.º 4
0
 def setUp(self):
     super(TestLTIConusmer, self).setUp()
     self.courseware_page = CoursewarePage(self.browser, self.course_id)
     self.lti_iframe = LTIContentIframe(self.browser, self.course_id)
     self.tab_nav = TabNavPage(self.browser)
     self.progress_page = ProgressPage(self.browser, self.course_id)
     self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
     self.grade_book_page = GradeBookPage(self.browser)
     # Install a course
     display_name = "Test Course" + self.unique_id
     self.course_fix = CourseFixture(
         self.course_info['org'], self.course_info['number'],
         self.course_info['run'], display_name=display_name
     )
    def upload_file(self, filename, wait_for_upload_button=True):
        """
        Helper method to upload an image file.
        """
        if wait_for_upload_button:
            self.wait_for_element_visibility('.u-field-upload-button', "upload button is visible")
        file_path = InstructorDashboardPage.get_asset_path(filename)

        # make the elements visible.
        self.browser.execute_script('$(".u-field-upload-button").css("opacity",1);')
        self.browser.execute_script('$(".upload-button-input").css("opacity",1);')

        self.wait_for_element_visibility('.upload-button-input', "upload button is visible")
        self.q(css='.upload-button-input').results[0].send_keys(file_path)
        self.wait_for_ajax()
Ejemplo n.º 6
0
    def upload_file(self, filename, wait_for_upload_button=True):
        """
        Helper method to upload an image file.
        """
        if wait_for_upload_button:
            self.wait_for_element_visibility('.u-field-upload-button', "upload button is visible")
        file_path = InstructorDashboardPage.get_asset_path(filename)

        # make the elements visible.
        self.browser.execute_script('$(".u-field-upload-button").css("opacity",1);')
        self.browser.execute_script('$(".upload-button-input").css("opacity",1);')

        self.wait_for_element_visibility('.upload-button-input', "upload button is visible")
        self.q(css='.upload-button-input').results[0].send_keys(file_path)
        self.wait_for_ajax()
Ejemplo n.º 7
0
    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 setUp(self):
        """
        Set up a discussion topic
        """
        super(BaseDividedDiscussionTest, self).setUp()

        self.discussion_id = "test_discussion_{}".format(uuid.uuid4().hex)
        self.course_fixture = CourseFixture(**self.course_info).add_children(
            XBlockFixtureDesc("chapter", "Test Section").add_children(
                XBlockFixtureDesc("sequential", "Test Subsection").add_children(
                    XBlockFixtureDesc("vertical", "Test Unit").add_children(
                        XBlockFixtureDesc(
                            "discussion",
                            "Test Discussion",
                            metadata={"discussion_id": self.discussion_id}
                        )
                    )
                )
            )
        ).install()

        # create course with single cohort and two content groups (user_partition of type "cohort")
        self.cohort_name = "OnlyCohort"
        self.setup_cohort_config(self.course_fixture)
        self.cohort_id = self.add_manual_cohort(self.course_fixture, self.cohort_name)

        # login as an instructor
        self.instructor_name = "instructor_user"
        self.instructor_id = AutoAuthPage(
            self.browser, username=self.instructor_name, email="*****@*****.**",
            course_id=self.course_id, staff=True
        ).visit().get_user_id()

        # go to the membership page on the instructor dashboard
        self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
        self.instructor_dashboard_page.visit()
        self.discussion_management_page = self.instructor_dashboard_page.select_discussion_management()
        self.discussion_management_page.wait_for_page()

        self.course_wide_key = 'course-wide'
        self.inline_key = 'inline'
        self.scheme_key = 'scheme'
Ejemplo n.º 9
0
    def setUp(self):
        """
        Set up a cohorted course
        """
        super(CohortConfigurationTest, self).setUp()

        # create course with cohorts
        self.manual_cohort_name = "ManualCohort1"
        self.auto_cohort_name = "AutoCohort1"
        self.course_fixture = CourseFixture(**self.course_info).install()
        self.setup_cohort_config(self.course_fixture, auto_cohort_groups=[self.auto_cohort_name])
        self.manual_cohort_id = self.add_manual_cohort(self.course_fixture, self.manual_cohort_name)

        # create a non-instructor who will be registered for the course and in the manual cohort.
        self.student_name, self.student_email = self._generate_unique_user_data()
        self.student_id = AutoAuthPage(
            self.browser, username=self.student_name, email=self.student_email,
            course_id=self.course_id, staff=False
        ).visit().get_user_id()
        self.add_user_to_cohort(self.course_fixture, self.student_name, self.manual_cohort_id)

        # create a second student user
        self.other_student_name, self.other_student_email = self._generate_unique_user_data()
        self.other_student_id = AutoAuthPage(
            self.browser, username=self.other_student_name, email=self.other_student_email,
            course_id=self.course_id, staff=False
        ).visit().get_user_id()

        # login as an instructor
        self.instructor_name, self.instructor_email = self._generate_unique_user_data()
        self.instructor_id = AutoAuthPage(
            self.browser, username=self.instructor_name, email=self.instructor_email,
            course_id=self.course_id, staff=True
        ).visit().get_user_id()

        # go to the membership page on the instructor dashboard
        self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
        self.instructor_dashboard_page.visit()
        self.cohort_management_page = self.instructor_dashboard_page.select_cohort_management()
Ejemplo n.º 10
0
    def setUp(self):
        """
        Set up a cohorted course with a user_partition of scheme "cohort".
        """
        super(CohortContentGroupAssociationTest, self).setUp()

        # create course with single cohort and two content groups (user_partition of type "cohort")
        self.cohort_name = "OnlyCohort"
        self.course_fixture = CourseFixture(**self.course_info).install()
        self.setup_cohort_config(self.course_fixture)
        self.cohort_id = self.add_manual_cohort(self.course_fixture, self.cohort_name)

        self.course_fixture._update_xblock(self.course_fixture._course_location, {
            "metadata": {
                u"user_partitions": [
                    create_user_partition_json(
                        0,
                        'Apples, Bananas',
                        'Content Group Partition',
                        [Group("0", 'Apples'), Group("1", 'Bananas')],
                        scheme="cohort"
                    )
                ],
            },
        })

        # login as an instructor
        self.instructor_name = "instructor_user"
        self.instructor_id = AutoAuthPage(
            self.browser, username=self.instructor_name, email="*****@*****.**",
            course_id=self.course_id, staff=True
        ).visit().get_user_id()

        # go to the membership page on the instructor dashboard
        self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
        self.instructor_dashboard_page.visit()
        self.cohort_management_page = self.instructor_dashboard_page.select_cohort_management()
Ejemplo n.º 11
0
class TestCohortHelp(ContainerBase):
    """
    Tests help links in Cohort page
    """
    def setUp(self, is_staff=True):
        super(TestCohortHelp, self).setUp(is_staff=is_staff)
        self.enable_cohorting(self.course_fixture)
        self.instructor_dashboard_page = InstructorDashboardPage(
            self.browser, self.course_id)
        self.instructor_dashboard_page.visit()
        self.cohort_management = self.instructor_dashboard_page.select_cohort_management(
        )

    def verify_help_link(self, href):
        """
        Verifies that help link is correct
        Arguments:
            href (str): Help url
        """
        actual_link = self.cohort_management.get_cohort_help_element_and_click_help(
        )
        self.assertEqual(actual_link.text, "What does this mean?")
        assert_opened_help_link_is_correct(self, href)

    def test_manual_cohort_help(self):
        """
        Scenario: Help in 'What does it mean?' is correct when we create cohort manually.
        Given that I am at 'Cohort' tab of LMS instructor dashboard
        And I check 'Enable Cohorts'
        And I add cohort name it, choose Manual for Cohort Assignment Method and
        No content group for Associated Content Group and save the cohort
        Then you see the UI text "Learners are added to this cohort only when..."
        followed by "What does this mean" link.
        And I click "What does this mean" link then help link should end with
        course_features/cohorts/cohort_config.html#assign-learners-to-cohorts-manually
        """
        self.cohort_management.add_cohort('cohort_name')

        href = url_for_help(
            'course_author',
            '/course_features/cohorts/cohort_config.html#assign-learners-to-cohorts-manually',
        )
        self.verify_help_link(href)

    def test_automatic_cohort_help(self):
        """
        Scenario: Help in 'What does it mean?' is correct when we create cohort automatically.
        Given that I am at 'Cohort' tab of LMS instructor dashboard
        And I check 'Enable Cohorts'
        And I add cohort name it, choose Automatic for Cohort Assignment Method and
        No content group for Associated Content Group and save the cohort
        Then you see the UI text "Learners are added to this cohort automatically"
        followed by "What does this mean" link.
        And I click "What does this mean" link then help link should end with
        course_features/cohorts/cohorts_overview.html#all-automated-assignment
        """

        self.cohort_management.add_cohort('cohort_name',
                                          assignment_type='random')

        href = url_for_help(
            'course_author',
            '/course_features/cohorts/cohorts_overview.html#all-automated-assignment',
        )
        self.verify_help_link(href)

    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")
Ejemplo n.º 12
0
class TestLTIConusmer(UniqueCourseTest):
    """
    Base class for tests of LTI xblock in the LMS.
    """

    USERNAME = "******"
    EMAIL = "*****@*****.**"
    host = os.environ.get('BOK_CHOY_HOSTNAME', '127.0.0.1')

    def setUp(self):
        super(TestLTIConusmer, self).setUp()
        self.courseware_page = CoursewarePage(self.browser, self.course_id)
        self.lti_iframe = LTIContentIframe(self.browser, self.course_id)
        self.tab_nav = TabNavPage(self.browser)
        self.progress_page = ProgressPage(self.browser, self.course_id)
        self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
        self.grade_book_page = GradeBookPage(self.browser)
        # Install a course
        display_name = "Test Course" + self.unique_id
        self.course_fix = CourseFixture(
            self.course_info['org'], self.course_info['number'],
            self.course_info['run'], display_name=display_name
        )

    def test_lti_no_launch_url_is_not_rendered(self):
        """
        Scenario: LTI component in LMS with no launch_url is not rendered
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with no_launch_url fields:
            Then I view the LTI and error is shown
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'launch_url': '',
            'open_in_a_new_page': False
        }
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True, self.course_id)
        self.courseware_page.visit()
        self.assertTrue(self.courseware_page.is_error_message_present())
        self.assertFalse(self.courseware_page.is_iframe_present())
        self.assertFalse(self.courseware_page.is_launch_url_present())

    def test_incorrect_lti_id_is_rendered_incorrectly(self):
        """
        Scenario: LTI component in LMS with incorrect lti_id is rendered incorrectly
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with incorrect_lti_id fields:
            Then I view the LTI but incorrect_signature warning is rendered
        """
        metadata_advance_settings = "test_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id': 'incorrect_lti_id',
            'launch_url': 'http://{}:{}/{}'.format(self.host, '8765', 'correct_lti_endpoint'),
            'open_in_a_new_page': False
        }
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True, self.course_id)
        self.courseware_page.visit()
        self.assertTrue(self.courseware_page.is_iframe_present())
        self.assertFalse(self.courseware_page.is_launch_url_present())
        self.assertFalse(self.courseware_page.is_error_message_present())
        self.courseware_page.go_to_lti_container()
        self.assertEqual("Wrong LTI signature", self.lti_iframe.lti_content)

    def test_incorrect_lti_credentials_is_rendered_incorrectly(self):
        """
        Scenario: LTI component in LMS with icorrect LTI credentials is rendered incorrectly
        Given the course has incorrect LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            I view the LTI but incorrect_signature warning is rendered
        """
        metadata_advance_settings = "test_lti_id:test_client_key:incorrect_lti_secret_key"
        metadata_lti_xblock = {
            'lti_id': 'correct_lti_id',
            'launch_url': 'http://{}:{}/{}'.format(self.host, '8765', 'correct_lti_endpoint'),
            'open_in_a_new_page': False
        }
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True, self.course_id)
        self.courseware_page.visit()
        self.assertTrue(self.courseware_page.is_iframe_present())
        self.assertFalse(self.courseware_page.is_launch_url_present())
        self.assertFalse(self.courseware_page.is_error_message_present())
        self.courseware_page.go_to_lti_container()
        self.assertEqual("Wrong LTI signature", self.lti_iframe.lti_content)

    def test_lti_is_rendered_in_iframe_correctly(self):
        """
        Scenario: LTI component in LMS is correctly rendered in iframe
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            I view the LTI and it is rendered in iframe correctly
        """

        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id': 'correct_lti_id',
            'launch_url': 'http://{}:{}/{}'.format(self.host, '8765', 'correct_lti_endpoint'),
            'open_in_a_new_page': False
        }

        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True, self.course_id)
        self.courseware_page.visit()
        self.assertTrue(self.courseware_page.is_iframe_present())
        self.assertFalse(self.courseware_page.is_launch_url_present())
        self.assertFalse(self.courseware_page.is_error_message_present())
        self.courseware_page.go_to_lti_container()
        self.assertEqual("This is LTI tool. Success.", self.lti_iframe.lti_content)

    def test_lti_graded_component_for_staff(self):
        """
        Scenario: Graded LTI component in LMS is correctly works for staff
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            verify scores on progress and grade book pages.
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id': 'correct_lti_id',
            'launch_url': 'http://{}:{}/{}'.format(self.host, '8765', 'correct_lti_endpoint'),
            'open_in_a_new_page': False,
            'weight': 10,
            'graded': True,
            'has_score': True
        }
        expected_scores = [(5, 10)]
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True, self.course_id)
        self.courseware_page.visit()
        self.courseware_page.go_to_lti_container()
        self.lti_iframe.submit_lti_answer('#submit-button')
        self.assertIn("LTI consumer (edX) responded with XML content", self.lti_iframe.lti_content)
        self.lti_iframe.switch_to_default()
        self.tab_nav.go_to_tab('Progress')
        actual_scores = self.progress_page.scores("Test Chapter", "Test Section")
        self.assertEqual(actual_scores, expected_scores)
        self.assertEqual(['Overall Score', 'Overall Score\n1%'], self.progress_page.graph_overall_score())
        self.tab_nav.go_to_tab('Instructor')
        student_admin_section = self.instructor_dashboard_page.select_student_admin(StudentAdminPage)
        student_admin_section.click_grade_book_link()
        self.assertEqual("50", self.grade_book_page.get_value_in_the_grade_book('Homework 1 - Test Section', 1))
        self.assertEqual("1", self.grade_book_page.get_value_in_the_grade_book('Total', 1))

    def test_lti_switch_role_works_correctly(self):
        """
        Scenario: Graded LTI component in LMS role's masquerading correctly works
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            switch role from instructor to learner and verify that it works correctly
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id': 'correct_lti_id',
            'launch_url': 'http://{}:{}/{}'.format(self.host, '8765', 'correct_lti_endpoint'),
            'open_in_a_new_page': False,
            'has_score': True
        }
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True, self.course_id)
        self.courseware_page.visit()
        self.assertTrue(self.courseware_page.is_iframe_present())
        self.assertFalse(self.courseware_page.is_launch_url_present())
        self.assertFalse(self.courseware_page.is_error_message_present())
        self.courseware_page.go_to_lti_container()
        self.assertEqual("This is LTI tool. Success.", self.lti_iframe.lti_content)
        self.assertEqual("Role: Instructor", self.lti_iframe.get_user_role)
        self.lti_iframe.switch_to_default()
        select_option_by_text(self.courseware_page.get_role_selector, 'Learner')
        self.courseware_page.wait_for_ajax()
        self.assertTrue(self.courseware_page.is_iframe_present())
        self.assertFalse(self.courseware_page.is_launch_url_present())
        self.assertFalse(self.courseware_page.is_error_message_present())
        self.courseware_page.go_to_lti_container()
        self.assertEqual("This is LTI tool. Success.", self.lti_iframe.lti_content)
        self.assertEqual("Role: Student", self.lti_iframe.get_user_role)

    def test_lti_graded_component_for_learner(self):
        """
        Scenario: Graded LTI component in LMS is correctly works for learners
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            verify scores on progress
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id': 'correct_lti_id',
            'launch_url': 'http://{}:{}/{}'.format(self.host, '8765', 'correct_lti_endpoint'),
            'open_in_a_new_page': False,
            'weight': 10,
            'graded': True,
            'has_score': True
        }
        expected_scores = [(5, 10)]
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, False, self.course_id)
        self.courseware_page.visit()
        self.courseware_page.go_to_lti_container()
        self.lti_iframe.submit_lti_answer('#submit-button')
        self.assertIn("LTI consumer (edX) responded with XML content", self.lti_iframe.lti_content)
        self.lti_iframe.switch_to_default()
        self.tab_nav.go_to_tab('Progress')
        actual_scores = self.progress_page.scores("Test Chapter", "Test Section")
        self.assertEqual(actual_scores, expected_scores)
        self.assertEqual(['Overall Score', 'Overall Score\n1%'], self.progress_page.graph_overall_score())

    def test_lti_v2_callback_graded_component(self):
        """
        Scenario: Graded LTI component in LMS is correctly works with LTI2v0 PUT callback
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            verify scores on progress and grade book pages.
            verify feedback in LTI component.
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id': 'correct_lti_id',
            'launch_url': 'http://{}:{}/{}'.format(self.host, '8765', 'correct_lti_endpoint'),
            'open_in_a_new_page': False,
            'weight': 10,
            'graded': True,
            'has_score': True
        }
        expected_scores = [(8, 10)]
        problem_score = '(8.0 / 10.0 points)'
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True, self.course_id)
        self.courseware_page.visit()
        self.courseware_page.go_to_lti_container()
        self.lti_iframe.submit_lti_answer("#submit-lti2-button")
        self.assertIn("LTI consumer (edX) responded with HTTP 200", self.lti_iframe.lti_content)
        self.lti_iframe.switch_to_default()
        self.tab_nav.go_to_tab('Progress')
        actual_scores = self.progress_page.scores("Test Chapter", "Test Section")
        self.assertEqual(actual_scores, expected_scores)
        self.assertEqual(['Overall Score', 'Overall Score\n1%'], self.progress_page.graph_overall_score())
        self.tab_nav.go_to_tab('Instructor')
        student_admin_section = self.instructor_dashboard_page.select_student_admin(StudentAdminPage)
        student_admin_section.click_grade_book_link()
        self.assertEqual("80", self.grade_book_page.get_value_in_the_grade_book('Homework 1 - Test Section', 1))
        self.assertEqual("1", self.grade_book_page.get_value_in_the_grade_book('Total', 1))
        self.tab_nav.go_to_tab('Course')
        self.assertEqual(problem_score, self.courseware_page.get_elem_text('.problem-progress'))
        self.assertEqual("This is awesome.", self.courseware_page.get_elem_text('.problem-feedback'))

    def test_lti_delete_callback_graded_component(self):
        """
        Scenario: Graded LTI component in LMS is correctly works with LTI2v0 PUT delete callback
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            Verify LTI provider deletes my grade on progress and grade book page
            verify LTI provider deletes feedback from LTI Component
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id': 'correct_lti_id',
            'launch_url': 'http://{}:{}/{}'.format(self.host, '8765', 'correct_lti_endpoint'),
            'open_in_a_new_page': False,
            'weight': 10,
            'graded': True,
            'has_score': True
        }
        expected_scores = [(0, 10)]
        problem_score = '(8.0 / 10.0 points)'
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True, self.course_id)
        self.courseware_page.visit()
        self.courseware_page.go_to_lti_container()
        self.lti_iframe.submit_lti_answer("#submit-lti2-button")
        self.assertIn("LTI consumer (edX) responded with HTTP 200", self.lti_iframe.lti_content)
        self.lti_iframe.switch_to_default()
        self.courseware_page.visit()
        self.assertEqual(problem_score, self.courseware_page.get_elem_text('.problem-progress'))
        self.assertEqual("This is awesome.", self.courseware_page.get_elem_text('.problem-feedback'))
        self.courseware_page.go_to_lti_container()
        self.lti_iframe.submit_lti_answer("#submit-lti-delete-button")
        self.courseware_page.visit()
        self.assertEqual("(10.0 points possible)", self.courseware_page.get_elem_text('.problem-progress'))
        self.assertFalse(self.courseware_page.is_lti_component_present('.problem-feedback'))
        self.tab_nav.go_to_tab('Progress')
        actual_scores = self.progress_page.scores("Test Chapter", "Test Section")
        self.assertEqual(actual_scores, expected_scores)
        self.assertEqual(['Overall Score', 'Overall Score\n0%'], self.progress_page.graph_overall_score())
        self.tab_nav.go_to_tab('Instructor')
        student_admin_section = self.instructor_dashboard_page.select_student_admin(StudentAdminPage)
        student_admin_section.click_grade_book_link()
        self.assertEqual("0", self.grade_book_page.get_value_in_the_grade_book('Homework 1 - Test Section', 1))
        self.assertEqual("0", self.grade_book_page.get_value_in_the_grade_book('Total', 1))

    def test_lti_hide_launch_shows_no_button(self):
        """
        Scenario: LTI component that set to hide_launch and open_in_a_new_page shows no button
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            verify LTI component don't show launch button with text "LTI (External resource)"
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id': 'correct_lti_id',
            'launch_url': 'http://{}:{}/{}'.format(self.host, '8765', 'correct_lti_endpoint'),
            'open_in_a_new_page': False,
            'hide_launch': True
        }
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True, self.course_id)
        self.courseware_page.visit()
        self.assertFalse(self.courseware_page.is_lti_component_present('.link_lti_new_window'))
        self.assertEqual("LTI (External resource)", self.courseware_page.get_elem_text('.problem-header'))

    def test_lti_hide_launch_shows_no_iframe(self):
        """
        Scenario: LTI component that set to hide_launch and not open_in_a_new_page shows no iframe
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            verify LTI component don't show LTI iframe with text "LTI (External resource)"
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id': 'correct_lti_id',
            'launch_url': 'http://{}:{}/{}'.format(self.host, '8765', 'correct_lti_endpoint'),
            'open_in_a_new_page': True,
            'hide_launch': True
        }
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True, self.course_id)
        self.courseware_page.visit()
        self.assertFalse(self.courseware_page.is_lti_component_present('.ltiLaunchFrame'))
        self.assertEqual("LTI (External resource)", self.courseware_page.get_elem_text('.problem-header'))

    def test_lti_button_text_correctly_displayed(self):
        """
        Scenario: LTI component button text is correctly displayed
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            verify LTI component button with text "Launch Application"
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id': 'correct_lti_id',
            'launch_url': 'http://{}:{}/{}'.format(self.host, '8765', 'correct_lti_endpoint'),
            'button_text': 'Launch Application'
        }
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True, self.course_id)
        self.courseware_page.visit()
        self.assertEqual("Launch Application", self.courseware_page.get_elem_text('.link_lti_new_window'))

    def test_lti_component_description_correctly_displayed(self):
        """
        Scenario: LTI component description is correctly displayed
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            LTI component description with text "Application description"
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id': 'correct_lti_id',
            'launch_url': 'http://{}:{}/{}'.format(self.host, '8765', 'correct_lti_endpoint'),
            'description': 'Application description'
        }
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True, self.course_id)
        self.courseware_page.visit()
        self.assertEqual("Application description", self.courseware_page.get_elem_text('.lti-description'))

    def set_advance_settings(self, metadata_advance_settings):

        # Set value against advanced modules in advanced settings
        self.course_fix.add_advanced_settings({
            "advanced_modules": {"value": ["lti_consumer"]},
            'lti_passports': {"value": [metadata_advance_settings]}
        })

    def create_lti_xblock(self, metadata_lti_xblock):
        self.course_fix.add_children(
            XBlockFixtureDesc(category='chapter', display_name='Test Chapter').add_children(
                XBlockFixtureDesc(
                    category='sequential', display_name='Test Section', grader_type='Homework', graded=True
                ).add_children(
                    XBlockFixtureDesc(category='lti', display_name='LTI', metadata=metadata_lti_xblock).add_children(
                    )
                )
            )
        ).install()
Ejemplo n.º 13
0
class TestCohortHelp(ContainerBase):
    """
    Tests help links in Cohort page
    """
    def setUp(self, is_staff=True):
        super(TestCohortHelp, self).setUp(is_staff=is_staff)
        self.enable_cohorting(self.course_fixture)
        self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
        self.instructor_dashboard_page.visit()
        self.cohort_management = self.instructor_dashboard_page.select_cohort_management()

    def get_url_with_changed_domain(self, url):
        """
        Replaces .org with .io in the url
        Arguments:
            url (str): The url to perform replace operation on.
        Returns:
        str: The updated url
        """
        return url.replace('.org/', '.io/')

    def verify_help_link(self, href):
        """
        Verifies that help link is correct
        Arguments:
            href (str): Help url
        """
        expected_link = {
            'href': href,
            'text': 'What does this mean?'
        }
        actual_link = self.cohort_management.get_cohort_help_element_and_click_help()

        assert_link(self, expected_link, actual_link)
        assert_opened_help_link_is_correct(self, self.get_url_with_changed_domain(href))

    def test_manual_cohort_help(self):
        """
        Scenario: Help in 'What does it mean?' is correct when we create cohort manually.
        Given that I am at 'Cohort' tab of LMS instructor dashboard
        And I check 'Enable Cohorts'
        And I add cohort name it, choose Manual for Cohort Assignment Method and
        No content group for Associated Content Group and save the cohort
        Then you see the UI text "Learners are added to this cohort only when..."
        followed by "What does this mean" link.
        And I click "What does this mean" link then help link should end with
        course_features/cohorts/cohort_config.html#assign-learners-to-cohorts-manually
        """
        self.cohort_management.add_cohort('cohort_name')

        href = 'http://edx.readthedocs.org/projects/edx-partner-course-staff/en/latest/' \
               'course_features/cohorts/cohort_config.html#assign-learners-to-cohorts-manually'

        self.verify_help_link(href)

    @flaky  # TODO fix this, see TNL-5709
    def test_automatic_cohort_help(self):
        """
        Scenario: Help in 'What does it mean?' is correct when we create cohort automatically.
        Given that I am at 'Cohort' tab of LMS instructor dashboard
        And I check 'Enable Cohorts'
        And I add cohort name it, choose Automatic for Cohort Assignment Method and
        No content group for Associated Content Group and save the cohort
        Then you see the UI text "Learners are added to this cohort automatically"
        followed by "What does this mean" link.
        And I click "What does this mean" link then help link should end with
        course_features/cohorts/cohorts_overview.html#all-automated-assignment
        """

        self.cohort_management.add_cohort('cohort_name', assignment_type='random')

        href = 'http://edx.readthedocs.org/projects/edx-partner-course-staff/en/latest/' \
               'course_features/cohorts/cohorts_overview.html#all-automated-assignment'

        self.verify_help_link(href)

    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")
Ejemplo n.º 14
0
class CohortContentGroupAssociationTest(UniqueCourseTest, CohortTestMixin):
    """
    Tests for linking between content groups and cohort in the instructor dashboard.
    """

    def setUp(self):
        """
        Set up a cohorted course with a user_partition of scheme "cohort".
        """
        super(CohortContentGroupAssociationTest, self).setUp()

        # create course with single cohort and two content groups (user_partition of type "cohort")
        self.cohort_name = "OnlyCohort"
        self.course_fixture = CourseFixture(**self.course_info).install()
        self.setup_cohort_config(self.course_fixture)
        self.cohort_id = self.add_manual_cohort(self.course_fixture, self.cohort_name)

        self.course_fixture._update_xblock(self.course_fixture._course_location, {
            "metadata": {
                u"user_partitions": [
                    create_user_partition_json(
                        0,
                        'Apples, Bananas',
                        'Content Group Partition',
                        [Group("0", 'Apples'), Group("1", 'Bananas')],
                        scheme="cohort"
                    )
                ],
            },
        })

        # login as an instructor
        self.instructor_name = "instructor_user"
        self.instructor_id = AutoAuthPage(
            self.browser, username=self.instructor_name, email="*****@*****.**",
            course_id=self.course_id, staff=True
        ).visit().get_user_id()

        # go to the membership page on the instructor dashboard
        self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
        self.instructor_dashboard_page.visit()
        self.cohort_management_page = self.instructor_dashboard_page.select_cohort_management()

    def test_no_content_group_linked(self):
        """
        Scenario: In a course with content groups, cohorts are initially not linked to a content group

        Given I have a course with a cohort defined and content groups defined
        When I view the cohort in the instructor dashboard and select settings
        Then the cohort is not linked to a content group
        And there is no text stating that content groups are undefined
        And the content groups are listed in the selector
        """
        self.cohort_management_page.select_cohort(self.cohort_name)
        self.assertIsNone(self.cohort_management_page.get_cohort_associated_content_group())
        self.assertIsNone(self.cohort_management_page.get_cohort_related_content_group_message())
        self.assertEquals(["Apples", "Bananas"], self.cohort_management_page.get_all_content_groups())

    def test_link_to_content_group(self):
        """
        Scenario: In a course with content groups, cohorts can be linked to content groups

        Given I have a course with a cohort defined and content groups defined
        When I view the cohort in the instructor dashboard and select settings
        And I link the cohort to one of the content groups and save
        Then there is a notification that my cohort has been saved
        And when I reload the page
        And I view the cohort in the instructor dashboard and select settings
        Then the cohort is still linked to the content group
        """
        self._link_cohort_to_content_group(self.cohort_name, "Bananas")
        self.assertEqual("Bananas", self.cohort_management_page.get_cohort_associated_content_group())

    def test_unlink_from_content_group(self):
        """
        Scenario: In a course with content groups, cohorts can be unlinked from content groups

        Given I have a course with a cohort defined and content groups defined
        When I view the cohort in the instructor dashboard and select settings
        And I link the cohort to one of the content groups and save
        Then there is a notification that my cohort has been saved
        And I reload the page
        And I view the cohort in the instructor dashboard and select settings
        And I unlink the cohort from any content group and save
        Then there is a notification that my cohort has been saved
        And when I reload the page
        And I view the cohort in the instructor dashboard and select settings
        Then the cohort is not linked to any content group
        """
        self._link_cohort_to_content_group(self.cohort_name, "Bananas")
        self.cohort_management_page.set_cohort_associated_content_group(None)
        self._verify_settings_saved_and_reload(self.cohort_name)
        self.assertEqual(None, self.cohort_management_page.get_cohort_associated_content_group())

    def test_create_new_cohort_linked_to_content_group(self):
        """
        Scenario: In a course with content groups, a new cohort can be linked to a content group
            at time of creation.

        Given I have a course with a cohort defined and content groups defined
        When I create a new cohort and link it to a content group
        Then when I select settings I see that the cohort is linked to the content group
        And when I reload the page
        And I view the cohort in the instructor dashboard and select settings
        Then the cohort is still linked to the content group
        """
        new_cohort = "correctly linked cohort"
        self._create_new_cohort_linked_to_content_group(new_cohort, "Apples")
        self.browser.refresh()
        self.cohort_management_page.wait_for_page()
        self.cohort_management_page.select_cohort(new_cohort)
        self.assertEqual("Apples", self.cohort_management_page.get_cohort_associated_content_group())

    def test_missing_content_group(self):
        """
        Scenario: In a course with content groups, if a cohort is associated with a content group that no longer
            exists, a warning message is shown

        Given I have a course with a cohort defined and content groups defined
        When I create a new cohort and link it to a content group
        And I delete that content group from the course
        And I reload the page
        And I view the cohort in the instructor dashboard and select settings
        Then the settings display a message that the content group no longer exists
        And when I select a different content group and save
        Then the error message goes away
        """
        new_cohort = "linked to missing content group"
        self._create_new_cohort_linked_to_content_group(new_cohort, "Apples")
        self.course_fixture._update_xblock(self.course_fixture._course_location, {
            "metadata": {
                u"user_partitions": [
                    create_user_partition_json(
                        0,
                        'Apples, Bananas',
                        'Content Group Partition',
                        [Group("2", 'Pears'), Group("1", 'Bananas')],
                        scheme="cohort"
                    )
                ],
            },
        })
        self.browser.refresh()
        self.cohort_management_page.wait_for_page()
        self.cohort_management_page.select_cohort(new_cohort)
        self.assertEqual("Deleted Content Group", self.cohort_management_page.get_cohort_associated_content_group())
        self.assertEquals(
            ["Bananas", "Pears", "Deleted Content Group"],
            self.cohort_management_page.get_all_content_groups()
        )
        self.assertEqual(
            "Warning:\nThe previously selected content group was deleted. Select another content group.",
            self.cohort_management_page.get_cohort_related_content_group_message()
        )
        self.cohort_management_page.set_cohort_associated_content_group("Pears")
        confirmation_messages = self.cohort_management_page.get_cohort_settings_messages()
        self.assertEqual(["Saved cohort"], confirmation_messages)
        self.assertIsNone(self.cohort_management_page.get_cohort_related_content_group_message())
        self.assertEquals(["Bananas", "Pears"], self.cohort_management_page.get_all_content_groups())

    def _create_new_cohort_linked_to_content_group(self, new_cohort, cohort_group):
        """
        Creates a new cohort linked to a content group.
        """
        self.cohort_management_page.add_cohort(new_cohort, content_group=cohort_group)
        self.assertEqual(cohort_group, self.cohort_management_page.get_cohort_associated_content_group())

    def _link_cohort_to_content_group(self, cohort_name, content_group):
        """
        Links a cohort to a content group. Saves the changes and verifies the cohort updated properly.
        Then refreshes the page and selects the cohort.
        """
        self.cohort_management_page.select_cohort(cohort_name)
        self.cohort_management_page.set_cohort_associated_content_group(content_group)
        self._verify_settings_saved_and_reload(cohort_name)

    def _verify_settings_saved_and_reload(self, cohort_name):
        """
        Verifies the confirmation message indicating that a cohort's settings have been updated.
        Then refreshes the page and selects the cohort.
        """
        confirmation_messages = self.cohort_management_page.get_cohort_settings_messages()
        self.assertEqual(["Saved cohort"], confirmation_messages)
        self.browser.refresh()
        self.cohort_management_page.wait_for_page()
        self.cohort_management_page.select_cohort(cohort_name)
Ejemplo n.º 15
0
class CohortConfigurationTest(EventsTestMixin, UniqueCourseTest, CohortTestMixin):
    """
    Tests for cohort management on the LMS Instructor Dashboard
    """

    def setUp(self):
        """
        Set up a cohorted course
        """
        super(CohortConfigurationTest, self).setUp()

        # create course with cohorts
        self.manual_cohort_name = "ManualCohort1"
        self.auto_cohort_name = "AutoCohort1"
        self.course_fixture = CourseFixture(**self.course_info).install()
        self.setup_cohort_config(self.course_fixture, auto_cohort_groups=[self.auto_cohort_name])
        self.manual_cohort_id = self.add_manual_cohort(self.course_fixture, self.manual_cohort_name)

        # create a non-instructor who will be registered for the course and in the manual cohort.
        self.student_name, self.student_email = self._generate_unique_user_data()
        self.student_id = AutoAuthPage(
            self.browser, username=self.student_name, email=self.student_email,
            course_id=self.course_id, staff=False
        ).visit().get_user_id()
        self.add_user_to_cohort(self.course_fixture, self.student_name, self.manual_cohort_id)

        # create a second student user
        self.other_student_name, self.other_student_email = self._generate_unique_user_data()
        self.other_student_id = AutoAuthPage(
            self.browser, username=self.other_student_name, email=self.other_student_email,
            course_id=self.course_id, staff=False
        ).visit().get_user_id()

        # login as an instructor
        self.instructor_name, self.instructor_email = self._generate_unique_user_data()
        self.instructor_id = AutoAuthPage(
            self.browser, username=self.instructor_name, email=self.instructor_email,
            course_id=self.course_id, staff=True
        ).visit().get_user_id()

        # go to the membership page on the instructor dashboard
        self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
        self.instructor_dashboard_page.visit()
        self.cohort_management_page = self.instructor_dashboard_page.select_cohort_management()

    def verify_cohort_description(self, cohort_name, expected_description):
        """
        Selects the cohort with the given name and verifies the expected description is presented.
        """
        self.cohort_management_page.select_cohort(cohort_name)
        self.assertEquals(self.cohort_management_page.get_selected_cohort(), cohort_name)
        self.assertIn(expected_description, self.cohort_management_page.get_cohort_group_setup())

    def test_cohort_description(self):
        """
        Scenario: the cohort configuration management in the instructor dashboard specifies whether
        students are automatically or manually assigned to specific cohorts.

        Given I have a course with a manual cohort and an automatic cohort defined
        When I view the manual cohort in the instructor dashboard
        There is text specifying that students are only added to the cohort manually
        And when I view the automatic cohort in the instructor dashboard
        There is text specifying that students are automatically added to the cohort
        """
        self.verify_cohort_description(
            self.manual_cohort_name,
            'Learners are added to this cohort only when you provide '
            'their email addresses or usernames on this page',
        )
        self.verify_cohort_description(
            self.auto_cohort_name,
            'Learners are added to this cohort automatically',
        )

    def test_no_content_groups(self):
        """
        Scenario: if the course has no content groups defined (user_partitions of type cohort),
        the settings in the cohort management tab reflect this

        Given I have a course with a cohort defined but no content groups
        When I view the cohort in the instructor dashboard and select settings
        Then the cohort is not linked to a content group
        And there is text stating that no content groups are defined
        And I cannot select the radio button to enable content group association
        And there is a link I can select to open Group settings in Studio
        """
        self.cohort_management_page.select_cohort(self.manual_cohort_name)
        self.assertIsNone(self.cohort_management_page.get_cohort_associated_content_group())
        self.assertEqual(
            "Warning:\nNo content groups exist. Create a content group",
            self.cohort_management_page.get_cohort_related_content_group_message()
        )
        self.assertFalse(self.cohort_management_page.select_content_group_radio_button())
        self.cohort_management_page.select_studio_group_settings()
        group_settings_page = GroupConfigurationsPage(
            self.browser,
            self.course_info['org'],
            self.course_info['number'],
            self.course_info['run']
        )
        group_settings_page.wait_for_page()

    def test_add_students_to_cohort_success(self):
        """
        Scenario: When students are added to a cohort, the appropriate notification is shown.

        Given I have a course with two cohorts
        And there is a user in one cohort
        And there is a user in neither cohort
        When I add the two users to the cohort that initially had no users
        Then there are 2 users in total in the cohort
        And I get a notification that 2 users have been added to the cohort
        And I get a notification that 1 user was moved from the other cohort
        And the user input field is empty
        And appropriate events have been emitted
        """
        start_time = datetime.now(UTC)
        self.cohort_management_page.select_cohort(self.auto_cohort_name)
        self.assertEqual(0, self.cohort_management_page.get_selected_cohort_count())
        self.cohort_management_page.add_students_to_selected_cohort([self.student_name, self.instructor_name])
        # Wait for the number of users in the cohort to change, indicating that the add operation is complete.
        EmptyPromise(
            lambda: 2 == self.cohort_management_page.get_selected_cohort_count(), 'Waiting for added students'
        ).fulfill()
        confirmation_messages = self.cohort_management_page.get_cohort_confirmation_messages()
        self.assertEqual(
            [
                "2 learners have been added to this cohort.",
                "1 learner was moved from " + self.manual_cohort_name
            ],
            confirmation_messages
        )
        self.assertEqual("", self.cohort_management_page.get_cohort_student_input_field_value())
        self.assertEqual(
            self.event_collection.find({
                "name": "edx.cohort.user_added",
                "time": {"$gt": start_time},
                "event.user_id": {"$in": [int(self.instructor_id), int(self.student_id)]},
                "event.cohort_name": self.auto_cohort_name,
            }).count(),
            2
        )
        self.assertEqual(
            self.event_collection.find({
                "name": "edx.cohort.user_removed",
                "time": {"$gt": start_time},
                "event.user_id": int(self.student_id),
                "event.cohort_name": self.manual_cohort_name,
            }).count(),
            1
        )
        self.assertEqual(
            self.event_collection.find({
                "name": "edx.cohort.user_add_requested",
                "time": {"$gt": start_time},
                "event.user_id": int(self.instructor_id),
                "event.cohort_name": self.auto_cohort_name,
                "event.previous_cohort_name": None,
            }).count(),
            1
        )
        self.assertEqual(
            self.event_collection.find({
                "name": "edx.cohort.user_add_requested",
                "time": {"$gt": start_time},
                "event.user_id": int(self.student_id),
                "event.cohort_name": self.auto_cohort_name,
                "event.previous_cohort_name": self.manual_cohort_name,
            }).count(),
            1
        )

    def test_add_students_to_cohort_failure(self):
        """
        Scenario: When errors occur when adding students to a cohort, the appropriate notification is shown.

        Given I have a course with a cohort and a user already in it
        When I add the user already in a cohort to that same cohort
        And I add a non-existing user to that cohort
        Then there is no change in the number of students in the cohort
        And I get a notification that one user was already in the cohort
        And I get a notification that one user is unknown
        And the user input field still contains the incorrect email addresses
        """
        self.cohort_management_page.select_cohort(self.manual_cohort_name)
        self.assertEqual(1, self.cohort_management_page.get_selected_cohort_count())
        self.cohort_management_page.add_students_to_selected_cohort([self.student_name, "unknown_user"])
        # Wait for notification messages to appear, indicating that the add operation is complete.
        EmptyPromise(
            lambda: 2 == len(self.cohort_management_page.get_cohort_confirmation_messages()), 'Waiting for notification'
        ).fulfill()
        self.assertEqual(1, self.cohort_management_page.get_selected_cohort_count())

        self.assertEqual(
            [
                "0 learners have been added to this cohort.",
                "1 learner was already in the cohort"
            ],
            self.cohort_management_page.get_cohort_confirmation_messages()
        )

        self.assertEqual(
            [
                "There was an error when trying to add learners:",
                "Unknown username: unknown_user"
            ],
            self.cohort_management_page.get_cohort_error_messages()
        )
        self.assertEqual(
            self.student_name + ",unknown_user,",
            self.cohort_management_page.get_cohort_student_input_field_value()
        )

    def _verify_cohort_settings(
            self,
            cohort_name,
            assignment_type=None,
            new_cohort_name=None,
            new_assignment_type=None,
            verify_updated=False
    ):

        """
        Create a new cohort and verify the new and existing settings.
        """
        start_time = datetime.now(UTC)
        self.assertNotIn(cohort_name, self.cohort_management_page.get_cohorts())
        self.cohort_management_page.add_cohort(cohort_name, assignment_type=assignment_type)
        self.assertEqual(0, self.cohort_management_page.get_selected_cohort_count())
        # After adding the cohort, it should automatically be selected and its
        # assignment_type should be "manual" as this is the default assignment type
        _assignment_type = assignment_type or 'manual'
        msg = "Waiting for currently selected cohort assignment type"
        EmptyPromise(
            lambda: _assignment_type == self.cohort_management_page.get_cohort_associated_assignment_type(), msg
        ).fulfill()
        # Go back to Manage Students Tab
        self.cohort_management_page.select_manage_settings()
        self.cohort_management_page.add_students_to_selected_cohort([self.instructor_name])
        # Wait for the number of users in the cohort to change, indicating that the add operation is complete.
        EmptyPromise(
            lambda: 1 == self.cohort_management_page.get_selected_cohort_count(), 'Waiting for student to be added'
        ).fulfill()
        self.assertFalse(self.cohort_management_page.is_assignment_settings_disabled)
        self.assertEqual('', self.cohort_management_page.assignment_settings_message)
        self.assertEqual(
            self.event_collection.find({
                "name": "edx.cohort.created",
                "time": {"$gt": start_time},
                "event.cohort_name": cohort_name,
            }).count(),
            1
        )
        self.assertEqual(
            self.event_collection.find({
                "name": "edx.cohort.creation_requested",
                "time": {"$gt": start_time},
                "event.cohort_name": cohort_name,
            }).count(),
            1
        )

        if verify_updated:
            self.cohort_management_page.select_cohort(cohort_name)
            self.cohort_management_page.select_cohort_settings()
            self.cohort_management_page.set_cohort_name(new_cohort_name)
            self.cohort_management_page.set_assignment_type(new_assignment_type)
            self.cohort_management_page.save_cohort_settings()

            # If cohort name is empty, then we should get/see an error message.
            if not new_cohort_name:
                confirmation_messages = self.cohort_management_page.get_cohort_settings_messages(type='error')
                self.assertEqual(
                    ["The cohort cannot be saved", "You must specify a name for the cohort"],
                    confirmation_messages
                )
            else:
                confirmation_messages = self.cohort_management_page.get_cohort_settings_messages()
                self.assertEqual(["Saved cohort"], confirmation_messages)
                self.assertEqual(new_cohort_name, self.cohort_management_page.cohort_name_in_header)
                self.assertIn(new_cohort_name, self.cohort_management_page.get_cohorts())
                self.assertEqual(1, self.cohort_management_page.get_selected_cohort_count())
                self.assertEqual(
                    new_assignment_type,
                    self.cohort_management_page.get_cohort_associated_assignment_type()
                )

    def _create_csv_file(self, filename, csv_text_as_lists):
        """
        Create a csv file with the provided list of lists.

        :param filename: this is the name that will be used for the csv file. Its location will
         be under the test upload data directory
        :param csv_text_as_lists: provide the contents of the csv file int he form of a list of lists
        """
        filename = self.instructor_dashboard_page.get_asset_path(filename)
        with open(filename, 'w+') as csv_file:
            writer = unicodecsv.writer(csv_file)
            for line in csv_text_as_lists:
                writer.writerow(line)
        self.addCleanup(os.remove, filename)

    def _generate_unique_user_data(self):
        """
        Produce unique username and e-mail.
        """
        unique_username = '******' + str(uuid.uuid4().hex)[:12]
        unique_email = unique_username + "@example.com"
        return unique_username, unique_email

    def test_add_new_cohort(self):
        """
        Scenario: A new manual cohort can be created, and a student assigned to it.

        Given I have a course with a user in the course
        When I add a new manual cohort to the course via the LMS instructor dashboard
        Then the new cohort is displayed and has no users in it
        And assignment type of displayed cohort to "manual" because this is the default
        And when I add the user to the new cohort
        Then the cohort has 1 user
        And appropriate events have been emitted
        """
        cohort_name = str(uuid.uuid4().get_hex()[0:20])
        self._verify_cohort_settings(cohort_name=cohort_name, assignment_type=None)

    def test_add_new_cohort_with_manual_assignment_type(self):
        """
        Scenario: A new cohort with manual assignment type can be created, and a student assigned to it.

        Given I have a course with a user in the course
        When I add a new manual cohort with manual assignment type to the course via the LMS instructor dashboard
        Then the new cohort is displayed and has no users in it
        And assignment type of displayed cohort is "manual"
        And when I add the user to the new cohort
        Then the cohort has 1 user
        And appropriate events have been emitted
        """
        cohort_name = str(uuid.uuid4().get_hex()[0:20])
        self._verify_cohort_settings(cohort_name=cohort_name, assignment_type='manual')

    def test_add_new_cohort_with_random_assignment_type(self):
        """
        Scenario: A new cohort with random assignment type can be created, and a student assigned to it.

        Given I have a course with a user in the course
        When I add a new manual cohort with random assignment type to the course via the LMS instructor dashboard
        Then the new cohort is displayed and has no users in it
        And assignment type of displayed cohort is "random"
        And when I add the user to the new cohort
        Then the cohort has 1 user
        And appropriate events have been emitted
        """
        cohort_name = str(uuid.uuid4().get_hex()[0:20])
        self._verify_cohort_settings(cohort_name=cohort_name, assignment_type='random')

    def test_update_existing_cohort_settings(self):
        """
        Scenario: Update existing cohort settings(cohort name, assignment type)

        Given I have a course with a user in the course
        When I add a new cohort with random assignment type to the course via the LMS instructor dashboard
        Then the new cohort is displayed and has no users in it
        And assignment type of displayed cohort is "random"
        And when I add the user to the new cohort
        Then the cohort has 1 user
        And appropriate events have been emitted
        Then I select the cohort (that you just created) from existing cohorts
        Then I change its name and assignment type set to "manual"
        Then I Save the settings
        And cohort with new name is present in cohorts dropdown list
        And cohort assignment type should be "manual"
        """
        cohort_name = str(uuid.uuid4().get_hex()[0:20])
        new_cohort_name = '{old}__NEW'.format(old=cohort_name)
        self._verify_cohort_settings(
            cohort_name=cohort_name,
            assignment_type='random',
            new_cohort_name=new_cohort_name,
            new_assignment_type='manual',
            verify_updated=True
        )

    def test_update_existing_cohort_settings_with_empty_cohort_name(self):
        """
        Scenario: Update existing cohort settings(cohort name, assignment type).

        Given I have a course with a user in the course
        When I add a new cohort with random assignment type to the course via the LMS instructor dashboard
        Then the new cohort is displayed and has no users in it
        And assignment type of displayed cohort is "random"
        And when I add the user to the new cohort
        Then the cohort has 1 user
        And appropriate events have been emitted
        Then I select a cohort from existing cohorts
        Then I set its name as empty string and assignment type set to "manual"
        And I click on Save button
        Then I should see an error message
        """
        cohort_name = str(uuid.uuid4().get_hex()[0:20])
        new_cohort_name = ''
        self._verify_cohort_settings(
            cohort_name=cohort_name,
            assignment_type='random',
            new_cohort_name=new_cohort_name,
            new_assignment_type='manual',
            verify_updated=True
        )

    def test_default_cohort_assignment_settings(self):
        """
        Scenario: Cohort assignment settings are disabled for default cohort.

        Given I have a course with a user in the course
        And I have added a manual cohort
        And I have added a random cohort
        When I select the random cohort
        Then cohort assignment settings are disabled
        """
        self.cohort_management_page.select_cohort("AutoCohort1")
        self.cohort_management_page.select_cohort_settings()

        self.assertTrue(self.cohort_management_page.is_assignment_settings_disabled)

        message = "There must be one cohort to which students can automatically be assigned."
        self.assertEqual(message, self.cohort_management_page.assignment_settings_message)

    def test_cohort_enable_disable(self):
        """
        Scenario: Cohort Enable/Disable checkbox related functionality is working as intended.

        Given I have a cohorted course with a user.
        And I can see the `Enable Cohorts` checkbox is checked.
        And cohort management controls are visible.
        When I uncheck the `Enable Cohorts` checkbox.
        Then cohort management controls are not visible.
        And When I reload the page.
        Then I can see the `Enable Cohorts` checkbox is unchecked.
        And cohort management controls are not visible.
        """
        self.assertTrue(self.cohort_management_page.is_cohorted)
        self.assertTrue(self.cohort_management_page.cohort_management_controls_visible())
        self.cohort_management_page.is_cohorted = False
        self.assertFalse(self.cohort_management_page.cohort_management_controls_visible())
        self.browser.refresh()
        self.cohort_management_page.wait_for_page()
        self.assertFalse(self.cohort_management_page.is_cohorted)
        self.assertFalse(self.cohort_management_page.cohort_management_controls_visible())

    def test_link_to_data_download(self):
        """
        Scenario: a link is present from the cohort configuration in
        the instructor dashboard to the Data Download section.

        Given I have a course with a cohort defined
        When I view the cohort in the LMS instructor dashboard
        There is a link to take me to the Data Download section of the Instructor Dashboard.
        """
        self.cohort_management_page.select_data_download()
        data_download_page = DataDownloadPage(self.browser)
        data_download_page.wait_for_page()

    def test_cohort_by_csv_both_columns(self):
        """
        Scenario: the instructor can upload a file with user and cohort assignments, using both emails and usernames.

        Given I have a course with two cohorts defined
        When I go to the cohort management section of the instructor dashboard
        I can upload a CSV file with assignments of users to cohorts via both usernames and emails
        Then I can download a file with results
        And appropriate events have been emitted
        """
        csv_contents = [
            ['username', 'email', 'ignored_column', 'cohort'],
            [self.instructor_name, '', 'June', 'ManualCohort1'],
            ['', self.student_email, 'Spring', 'AutoCohort1'],
            [self.other_student_name, '', 'Fall', 'ManualCohort1'],
        ]
        filename = "cohort_csv_both_columns_1.csv"
        self._create_csv_file(filename, csv_contents)
        self._verify_csv_upload_acceptable_file(filename)

    def test_cohort_by_csv_only_email(self):
        """
        Scenario: the instructor can upload a file with user and cohort assignments, using only emails.

        Given I have a course with two cohorts defined
        When I go to the cohort management section of the instructor dashboard
        I can upload a CSV file with assignments of users to cohorts via only emails
        Then I can download a file with results
        And appropriate events have been emitted
        """
        csv_contents = [
            ['email', 'cohort'],
            [self.instructor_email, 'ManualCohort1'],
            [self.student_email, 'AutoCohort1'],
            [self.other_student_email, 'ManualCohort1'],
        ]
        filename = "cohort_csv_emails_only.csv"
        self._create_csv_file(filename, csv_contents)
        self._verify_csv_upload_acceptable_file(filename)

    def test_cohort_by_csv_only_username(self):
        """
        Scenario: the instructor can upload a file with user and cohort assignments, using only usernames.

        Given I have a course with two cohorts defined
        When I go to the cohort management section of the instructor dashboard
        I can upload a CSV file with assignments of users to cohorts via only usernames
        Then I can download a file with results
        And appropriate events have been emitted
        """
        csv_contents = [
            ['username', 'cohort'],
            [self.instructor_name, 'ManualCohort1'],
            [self.student_name, 'AutoCohort1'],
            [self.other_student_name, 'ManualCohort1'],
        ]
        filename = "cohort_users_only_username1.csv"
        self._create_csv_file(filename, csv_contents)
        self._verify_csv_upload_acceptable_file(filename)

    # TODO: Change unicode_hello_in_korean = u'ßßßßßß' to u'안녕하세요', after up gradation of Chrome driver. See TNL-3944
    def test_cohort_by_csv_unicode(self):
        """
        Scenario: the instructor can upload a file with user and cohort assignments, using both emails and usernames.

        Given I have a course with two cohorts defined
        And I add another cohort with a unicode name
        When I go to the cohort management section of the instructor dashboard
        I can upload a CSV file with assignments of users to the unicode cohort via both usernames and emails
        Then I can download a file with results

        TODO: refactor events verification to handle this scenario. Events verification assumes movements
        between other cohorts (manual and auto).
        """
        unicode_hello_in_korean = u'ßßßßßß'
        self._verify_cohort_settings(cohort_name=unicode_hello_in_korean, assignment_type=None)
        csv_contents = [
            ['username', 'email', 'cohort'],
            [self.instructor_name, '', unicode_hello_in_korean],
            ['', self.student_email, unicode_hello_in_korean],
            [self.other_student_name, '', unicode_hello_in_korean]
        ]
        filename = "cohort_unicode_name.csv"
        self._create_csv_file(filename, csv_contents)
        self._verify_csv_upload_acceptable_file(filename, skip_events=True)

    def _verify_csv_upload_acceptable_file(self, filename, skip_events=None):
        """
        Helper method to verify cohort assignments after a successful CSV upload.

        When skip_events is specified, no assertions are made on events.
        """
        start_time = datetime.now(UTC)
        self.cohort_management_page.upload_cohort_file(filename)
        self._verify_cohort_by_csv_notification(
            u"Your file '{}' has been uploaded. Allow a few minutes for processing.".format(filename)
        )

        if not skip_events:
            # student_user is moved from manual cohort to auto cohort
            self.assertEqual(
                self.event_collection.find({
                    "name": "edx.cohort.user_added",
                    "time": {"$gt": start_time},
                    "event.user_id": {"$in": [int(self.student_id)]},
                    "event.cohort_name": self.auto_cohort_name,
                }).count(),
                1
            )
            self.assertEqual(
                self.event_collection.find({
                    "name": "edx.cohort.user_removed",
                    "time": {"$gt": start_time},
                    "event.user_id": int(self.student_id),
                    "event.cohort_name": self.manual_cohort_name,
                }).count(),
                1
            )
            # instructor_user (previously unassigned) is added to manual cohort
            self.assertEqual(
                self.event_collection.find({
                    "name": "edx.cohort.user_added",
                    "time": {"$gt": start_time},
                    "event.user_id": {"$in": [int(self.instructor_id)]},
                    "event.cohort_name": self.manual_cohort_name,
                }).count(),
                1
            )
            # other_student_user (previously unassigned) is added to manual cohort
            self.assertEqual(
                self.event_collection.find({
                    "name": "edx.cohort.user_added",
                    "time": {"$gt": start_time},
                    "event.user_id": {"$in": [int(self.other_student_id)]},
                    "event.cohort_name": self.manual_cohort_name,
                }).count(),
                1
            )

        # Verify the results can be downloaded.
        data_download = self.instructor_dashboard_page.select_data_download()
        data_download.wait_for_available_report()
        report = data_download.get_available_reports_for_download()[0]
        base_file_name = "cohort_results_"
        self.assertIn("{}_{}".format(
            '_'.join([self.course_info['org'], self.course_info['number'], self.course_info['run']]), base_file_name
        ), report)
        report_datetime = datetime.strptime(
            report[report.index(base_file_name) + len(base_file_name):-len(".csv")],
            "%Y-%m-%d-%H%M"
        )
        self.assertLessEqual(start_time.replace(second=0, microsecond=0), utc.localize(report_datetime))

    def test_cohort_by_csv_wrong_file_type(self):
        """
        Scenario: if the instructor uploads a non-csv file, an error message is presented.

        Given I have a course with cohorting enabled
        When I go to the cohort management section of the instructor dashboard
        And I upload a file without the CSV extension
        Then I get an error message stating that the file must have a CSV extension
        """
        self.cohort_management_page.upload_cohort_file("image.jpg")
        self._verify_cohort_by_csv_notification("The file must end with the extension '.csv'.")

    def test_cohort_by_csv_missing_cohort(self):
        """
        Scenario: if the instructor uploads a csv file with no cohort column, an error message is presented.

        Given I have a course with cohorting enabled
        When I go to the cohort management section of the instructor dashboard
        And I upload a CSV file that is missing the cohort column
        Then I get an error message stating that the file must have a cohort column
        """
        self.cohort_management_page.upload_cohort_file("cohort_users_missing_cohort_column.csv")
        self._verify_cohort_by_csv_notification("The file must contain a 'cohort' column containing cohort names.")

    def test_cohort_by_csv_missing_user(self):
        """
        Scenario: if the instructor uploads a csv file with no username or email column, an error message is presented.

        Given I have a course with cohorting enabled
        When I go to the cohort management section of the instructor dashboard
        And I upload a CSV file that is missing both the username and email columns
        Then I get an error message stating that the file must have either a username or email column
        """
        self.cohort_management_page.upload_cohort_file("cohort_users_missing_user_columns.csv")
        self._verify_cohort_by_csv_notification(
            "The file must contain a 'username' column, an 'email' column, or both."
        )

    def _verify_cohort_by_csv_notification(self, expected_message):
        """
        Helper method to check the CSV file upload notification message.
        """
        # Wait for notification message to appear, indicating file has been uploaded.
        EmptyPromise(
            lambda: 1 == len(self.cohort_management_page.get_csv_messages()), 'Waiting for notification'
        ).fulfill()
        messages = self.cohort_management_page.get_csv_messages()
        self.assertEquals(expected_message, messages[0])

    @attr('a11y')
    def test_cohorts_management_a11y(self):
        """
        Run accessibility audit for cohort management.
        """
        self.cohort_management_page.a11y_audit.config.set_rules({
            "ignore": [
                'aria-valid-attr',  # TODO: LEARNER-6611 & LEARNER-6865
            ]
        })
        self.cohort_management_page.a11y_audit.check_for_accessibility_errors()
Ejemplo n.º 16
0
class TestCohortHelp(ContainerBase, CohortTestMixin):
    """
    Tests help links in Cohort page
    """
    def setUp(self, is_staff=True):
        super(TestCohortHelp, self).setUp(is_staff=is_staff)
        self.enable_cohorting(self.course_fixture)
        self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
        self.instructor_dashboard_page.visit()
        self.cohort_management = self.instructor_dashboard_page.select_cohort_management()

    def verify_help_link(self, href):
        """
        Verifies that help link is correct
        Arguments:
            href (str): Help url
        """
        help_element = self.cohort_management.get_cohort_help_element()
        self.assertEqual(help_element.text, "What does this mean?")
        click_and_wait_for_window(self, help_element)
        assert_opened_help_link_is_correct(self, href)

    def test_manual_cohort_help(self):
        """
        Scenario: Help in 'What does it mean?' is correct when we create cohort manually.
        Given that I am at 'Cohort' tab of LMS instructor dashboard
        And I check 'Enable Cohorts'
        And I add cohort name it, choose Manual for Cohort Assignment Method and
        No content group for Associated Content Group and save the cohort
        Then you see the UI text "Learners are added to this cohort only when..."
        followed by "What does this mean" link.
        And I click "What does this mean" link then help link should end with
        course_features/cohorts/cohort_config.html#assign-learners-to-cohorts-manually
        """
        self.cohort_management.add_cohort('cohort_name')

        href = url_for_help(
            'course_author',
            '/course_features/cohorts/cohort_config.html#assign-learners-to-cohorts-manually',
        )
        self.verify_help_link(href)

    def test_automatic_cohort_help(self):
        """
        Scenario: Help in 'What does it mean?' is correct when we create cohort automatically.
        Given that I am at 'Cohort' tab of LMS instructor dashboard
        And I check 'Enable Cohorts'
        And I add cohort name it, choose Automatic for Cohort Assignment Method and
        No content group for Associated Content Group and save the cohort
        Then you see the UI text "Learners are added to this cohort automatically"
        followed by "What does this mean" link.
        And I click "What does this mean" link then help link should end with
        course_features/cohorts/cohorts_overview.html#all-automated-assignment
        """

        self.cohort_management.add_cohort('cohort_name', assignment_type='random')

        href = url_for_help(
            'course_author',
            '/course_features/cohorts/cohorts_overview.html#all-automated-assignment',
        )
        self.verify_help_link(href)
Ejemplo n.º 17
0
 def setUp(self, is_staff=True):
     super(TestCohortHelp, self).setUp(is_staff=is_staff)
     self.enable_cohorting(self.course_fixture)
     self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
     self.instructor_dashboard_page.visit()
     self.cohort_management = self.instructor_dashboard_page.select_cohort_management()
class CohortConfigurationTest(EventsTestMixin, UniqueCourseTest,
                              CohortTestMixin):
    """
    Tests for cohort management on the LMS Instructor Dashboard
    """
    def setUp(self):
        """
        Set up a cohorted course
        """
        super(CohortConfigurationTest, self).setUp()

        # create course with cohorts
        self.manual_cohort_name = "ManualCohort1"
        self.auto_cohort_name = "AutoCohort1"
        self.course_fixture = CourseFixture(**self.course_info).install()
        self.setup_cohort_config(self.course_fixture,
                                 auto_cohort_groups=[self.auto_cohort_name])
        self.manual_cohort_id = self.add_manual_cohort(self.course_fixture,
                                                       self.manual_cohort_name)

        # create a non-instructor who will be registered for the course and in the manual cohort.
        self.student_name, self.student_email = self._generate_unique_user_data(
        )
        self.student_id = AutoAuthPage(self.browser,
                                       username=self.student_name,
                                       email=self.student_email,
                                       course_id=self.course_id,
                                       staff=False).visit().get_user_id()
        self.add_user_to_cohort(self.course_fixture, self.student_name,
                                self.manual_cohort_id)

        # create a second student user
        self.other_student_name, self.other_student_email = self._generate_unique_user_data(
        )
        self.other_student_id = AutoAuthPage(
            self.browser,
            username=self.other_student_name,
            email=self.other_student_email,
            course_id=self.course_id,
            staff=False).visit().get_user_id()

        # login as an instructor
        self.instructor_name, self.instructor_email = self._generate_unique_user_data(
        )
        self.instructor_id = AutoAuthPage(self.browser,
                                          username=self.instructor_name,
                                          email=self.instructor_email,
                                          course_id=self.course_id,
                                          staff=True).visit().get_user_id()

        # go to the membership page on the instructor dashboard
        self.instructor_dashboard_page = InstructorDashboardPage(
            self.browser, self.course_id)
        self.instructor_dashboard_page.visit()
        self.cohort_management_page = self.instructor_dashboard_page.select_cohort_management(
        )

    def verify_cohort_description(self, cohort_name, expected_description):
        """
        Selects the cohort with the given name and verifies the expected description is presented.
        """
        self.cohort_management_page.select_cohort(cohort_name)
        self.assertEquals(self.cohort_management_page.get_selected_cohort(),
                          cohort_name)
        self.assertIn(expected_description,
                      self.cohort_management_page.get_cohort_group_setup())

    def test_cohort_description(self):
        """
        Scenario: the cohort configuration management in the instructor dashboard specifies whether
        students are automatically or manually assigned to specific cohorts.

        Given I have a course with a manual cohort and an automatic cohort defined
        When I view the manual cohort in the instructor dashboard
        There is text specifying that students are only added to the cohort manually
        And when I view the automatic cohort in the instructor dashboard
        There is text specifying that students are automatically added to the cohort
        """
        self.verify_cohort_description(
            self.manual_cohort_name,
            'Learners are added to this cohort only when you provide '
            'their email addresses or usernames on this page',
        )
        self.verify_cohort_description(
            self.auto_cohort_name,
            'Learners are added to this cohort automatically',
        )

    def test_no_content_groups(self):
        """
        Scenario: if the course has no content groups defined (user_partitions of type cohort),
        the settings in the cohort management tab reflect this

        Given I have a course with a cohort defined but no content groups
        When I view the cohort in the instructor dashboard and select settings
        Then the cohort is not linked to a content group
        And there is text stating that no content groups are defined
        And I cannot select the radio button to enable content group association
        And there is a link I can select to open Group settings in Studio
        """
        self.cohort_management_page.select_cohort(self.manual_cohort_name)
        self.assertIsNone(
            self.cohort_management_page.get_cohort_associated_content_group())
        self.assertEqual(
            "Warning:\nNo content groups exist. Create a content group",
            self.cohort_management_page.
            get_cohort_related_content_group_message())
        self.assertFalse(
            self.cohort_management_page.select_content_group_radio_button())
        self.cohort_management_page.select_studio_group_settings()
        group_settings_page = GroupConfigurationsPage(
            self.browser, self.course_info['org'], self.course_info['number'],
            self.course_info['run'])
        group_settings_page.wait_for_page()

    def test_add_students_to_cohort_success(self):
        """
        Scenario: When students are added to a cohort, the appropriate notification is shown.

        Given I have a course with two cohorts
        And there is a user in one cohort
        And there is a user in neither cohort
        When I add the two users to the cohort that initially had no users
        Then there are 2 users in total in the cohort
        And I get a notification that 2 users have been added to the cohort
        And I get a notification that 1 user was moved from the other cohort
        And the user input field is empty
        And appropriate events have been emitted
        """
        start_time = datetime.now(UTC)
        self.cohort_management_page.select_cohort(self.auto_cohort_name)
        self.assertEqual(
            0, self.cohort_management_page.get_selected_cohort_count())
        self.cohort_management_page.add_students_to_selected_cohort(
            [self.student_name, self.instructor_name])
        # Wait for the number of users in the cohort to change, indicating that the add operation is complete.
        EmptyPromise(
            lambda: 2 == self.cohort_management_page.get_selected_cohort_count(
            ), 'Waiting for added students').fulfill()
        confirmation_messages = self.cohort_management_page.get_cohort_confirmation_messages(
        )
        self.assertEqual([
            "2 learners have been added to this cohort.",
            "1 learner was moved from " + self.manual_cohort_name
        ], confirmation_messages)
        self.assertEqual(
            "",
            self.cohort_management_page.get_cohort_student_input_field_value())
        self.assertEqual(
            self.event_collection.find({
                "name":
                "edx.cohort.user_added",
                "time": {
                    "$gt": start_time
                },
                "event.user_id": {
                    "$in": [int(self.instructor_id),
                            int(self.student_id)]
                },
                "event.cohort_name":
                self.auto_cohort_name,
            }).count(), 2)
        self.assertEqual(
            self.event_collection.find({
                "name":
                "edx.cohort.user_removed",
                "time": {
                    "$gt": start_time
                },
                "event.user_id":
                int(self.student_id),
                "event.cohort_name":
                self.manual_cohort_name,
            }).count(), 1)
        self.assertEqual(
            self.event_collection.find({
                "name": "edx.cohort.user_add_requested",
                "time": {
                    "$gt": start_time
                },
                "event.user_id": int(self.instructor_id),
                "event.cohort_name": self.auto_cohort_name,
                "event.previous_cohort_name": None,
            }).count(), 1)
        self.assertEqual(
            self.event_collection.find({
                "name":
                "edx.cohort.user_add_requested",
                "time": {
                    "$gt": start_time
                },
                "event.user_id":
                int(self.student_id),
                "event.cohort_name":
                self.auto_cohort_name,
                "event.previous_cohort_name":
                self.manual_cohort_name,
            }).count(), 1)

    def test_add_students_to_cohort_failure(self):
        """
        Scenario: When errors occur when adding students to a cohort, the appropriate notification is shown.

        Given I have a course with a cohort and a user already in it
        When I add the user already in a cohort to that same cohort
        And I add a non-existing user to that cohort
        Then there is no change in the number of students in the cohort
        And I get a notification that one user was already in the cohort
        And I get a notification that one user is unknown
        And the user input field still contains the incorrect email addresses
        """
        self.cohort_management_page.select_cohort(self.manual_cohort_name)
        self.assertEqual(
            1, self.cohort_management_page.get_selected_cohort_count())
        self.cohort_management_page.add_students_to_selected_cohort(
            [self.student_name, "unknown_user"])
        # Wait for notification messages to appear, indicating that the add operation is complete.
        EmptyPromise(
            lambda: 2 == len(self.cohort_management_page.
                             get_cohort_confirmation_messages()),
            'Waiting for notification').fulfill()
        self.assertEqual(
            1, self.cohort_management_page.get_selected_cohort_count())

        self.assertEqual([
            "0 learners have been added to this cohort.",
            "1 learner was already in the cohort"
        ], self.cohort_management_page.get_cohort_confirmation_messages())

        self.assertEqual([
            "There was an error when trying to add learners:",
            "Unknown username: unknown_user"
        ], self.cohort_management_page.get_cohort_error_messages())
        self.assertEqual(
            self.student_name + ",unknown_user,",
            self.cohort_management_page.get_cohort_student_input_field_value())

    def _verify_cohort_settings(self,
                                cohort_name,
                                assignment_type=None,
                                new_cohort_name=None,
                                new_assignment_type=None,
                                verify_updated=False):
        """
        Create a new cohort and verify the new and existing settings.
        """
        start_time = datetime.now(UTC)
        self.assertNotIn(cohort_name,
                         self.cohort_management_page.get_cohorts())
        self.cohort_management_page.add_cohort(cohort_name,
                                               assignment_type=assignment_type)
        self.assertEqual(
            0, self.cohort_management_page.get_selected_cohort_count())
        # After adding the cohort, it should automatically be selected and its
        # assignment_type should be "manual" as this is the default assignment type
        _assignment_type = assignment_type or 'manual'
        msg = "Waiting for currently selected cohort assignment type"
        EmptyPromise(
            lambda: _assignment_type == self.cohort_management_page.
            get_cohort_associated_assignment_type(), msg).fulfill()
        # Go back to Manage Students Tab
        self.cohort_management_page.select_manage_settings()
        self.cohort_management_page.add_students_to_selected_cohort(
            [self.instructor_name])
        # Wait for the number of users in the cohort to change, indicating that the add operation is complete.
        EmptyPromise(
            lambda: 1 == self.cohort_management_page.get_selected_cohort_count(
            ), 'Waiting for student to be added').fulfill()
        self.assertFalse(
            self.cohort_management_page.is_assignment_settings_disabled)
        self.assertEqual(
            '', self.cohort_management_page.assignment_settings_message)
        self.assertEqual(
            self.event_collection.find({
                "name": "edx.cohort.created",
                "time": {
                    "$gt": start_time
                },
                "event.cohort_name": cohort_name,
            }).count(), 1)
        self.assertEqual(
            self.event_collection.find({
                "name": "edx.cohort.creation_requested",
                "time": {
                    "$gt": start_time
                },
                "event.cohort_name": cohort_name,
            }).count(), 1)

        if verify_updated:
            self.cohort_management_page.select_cohort(cohort_name)
            self.cohort_management_page.select_cohort_settings()
            self.cohort_management_page.set_cohort_name(new_cohort_name)
            self.cohort_management_page.set_assignment_type(
                new_assignment_type)
            self.cohort_management_page.save_cohort_settings()

            # If cohort name is empty, then we should get/see an error message.
            if not new_cohort_name:
                confirmation_messages = self.cohort_management_page.get_cohort_settings_messages(
                    type='error')
                self.assertEqual([
                    "The cohort cannot be saved",
                    "You must specify a name for the cohort"
                ], confirmation_messages)
            else:
                confirmation_messages = self.cohort_management_page.get_cohort_settings_messages(
                )
                self.assertEqual(["Saved cohort"], confirmation_messages)
                self.assertEqual(
                    new_cohort_name,
                    self.cohort_management_page.cohort_name_in_header)
                self.assertIn(new_cohort_name,
                              self.cohort_management_page.get_cohorts())
                self.assertEqual(
                    1, self.cohort_management_page.get_selected_cohort_count())
                self.assertEqual(
                    new_assignment_type,
                    self.cohort_management_page.
                    get_cohort_associated_assignment_type())

    def _create_csv_file(self, filename, csv_text_as_lists):
        """
        Create a csv file with the provided list of lists.

        :param filename: this is the name that will be used for the csv file. Its location will
         be under the test upload data directory
        :param csv_text_as_lists: provide the contents of the csv file int he form of a list of lists
        """
        filename = self.instructor_dashboard_page.get_asset_path(filename)
        with open(filename, 'w+') as csv_file:
            writer = unicodecsv.writer(csv_file)
            for line in csv_text_as_lists:
                writer.writerow(line)
        self.addCleanup(os.remove, filename)

    def _generate_unique_user_data(self):
        """
        Produce unique username and e-mail.
        """
        unique_username = '******' + str(uuid.uuid4().hex)[:12]
        unique_email = unique_username + "@example.com"
        return unique_username, unique_email

    def test_add_new_cohort_with_manual_assignment_type(self):
        """
        Scenario: A new cohort with manual assignment type can be created, and a student assigned to it.

        Given I have a course with a user in the course
        When I add a new manual cohort with manual assignment type to the course via the LMS instructor dashboard
        Then the new cohort is displayed and has no users in it
        And assignment type of displayed cohort is "manual"
        And when I add the user to the new cohort
        Then the cohort has 1 user
        And appropriate events have been emitted
        """
        cohort_name = str(uuid.uuid4().hex[0:20])
        self._verify_cohort_settings(cohort_name=cohort_name,
                                     assignment_type='manual')

    def test_add_new_cohort_with_random_assignment_type(self):
        """
        Scenario: A new cohort with random assignment type can be created, and a student assigned to it.

        Given I have a course with a user in the course
        When I add a new manual cohort with random assignment type to the course via the LMS instructor dashboard
        Then the new cohort is displayed and has no users in it
        And assignment type of displayed cohort is "random"
        And when I add the user to the new cohort
        Then the cohort has 1 user
        And appropriate events have been emitted
        """
        cohort_name = str(uuid.uuid4().hex[0:20])
        self._verify_cohort_settings(cohort_name=cohort_name,
                                     assignment_type='random')

    def test_update_existing_cohort_settings(self):
        """
        Scenario: Update existing cohort settings(cohort name, assignment type)

        Given I have a course with a user in the course
        When I add a new cohort with random assignment type to the course via the LMS instructor dashboard
        Then the new cohort is displayed and has no users in it
        And assignment type of displayed cohort is "random"
        And when I add the user to the new cohort
        Then the cohort has 1 user
        And appropriate events have been emitted
        Then I select the cohort (that you just created) from existing cohorts
        Then I change its name and assignment type set to "manual"
        Then I Save the settings
        And cohort with new name is present in cohorts dropdown list
        And cohort assignment type should be "manual"
        """
        cohort_name = str(uuid.uuid4().hex[0:20])
        new_cohort_name = '{old}__NEW'.format(old=cohort_name)
        self._verify_cohort_settings(cohort_name=cohort_name,
                                     assignment_type='random',
                                     new_cohort_name=new_cohort_name,
                                     new_assignment_type='manual',
                                     verify_updated=True)

    def test_update_existing_cohort_settings_with_empty_cohort_name(self):
        """
        Scenario: Update existing cohort settings(cohort name, assignment type).

        Given I have a course with a user in the course
        When I add a new cohort with random assignment type to the course via the LMS instructor dashboard
        Then the new cohort is displayed and has no users in it
        And assignment type of displayed cohort is "random"
        And when I add the user to the new cohort
        Then the cohort has 1 user
        And appropriate events have been emitted
        Then I select a cohort from existing cohorts
        Then I set its name as empty string and assignment type set to "manual"
        And I click on Save button
        Then I should see an error message
        """
        cohort_name = str(uuid.uuid4().hex[0:20])
        new_cohort_name = ''
        self._verify_cohort_settings(cohort_name=cohort_name,
                                     assignment_type='random',
                                     new_cohort_name=new_cohort_name,
                                     new_assignment_type='manual',
                                     verify_updated=True)

    def test_default_cohort_assignment_settings(self):
        """
        Scenario: Cohort assignment settings are disabled for default cohort.

        Given I have a course with a user in the course
        And I have added a manual cohort
        And I have added a random cohort
        When I select the random cohort
        Then cohort assignment settings are disabled
        """
        self.cohort_management_page.select_cohort("AutoCohort1")
        self.cohort_management_page.select_cohort_settings()

        self.assertTrue(
            self.cohort_management_page.is_assignment_settings_disabled)

        message = "There must be one cohort to which students can automatically be assigned."
        self.assertEqual(
            message, self.cohort_management_page.assignment_settings_message)

    def test_cohort_enable_disable(self):
        """
        Scenario: Cohort Enable/Disable checkbox related functionality is working as intended.

        Given I have a cohorted course with a user.
        And I can see the `Enable Cohorts` checkbox is checked.
        And cohort management controls are visible.
        When I uncheck the `Enable Cohorts` checkbox.
        Then cohort management controls are not visible.
        And When I reload the page.
        Then I can see the `Enable Cohorts` checkbox is unchecked.
        And cohort management controls are not visible.
        """
        self.assertTrue(self.cohort_management_page.is_cohorted)
        self.assertTrue(
            self.cohort_management_page.cohort_management_controls_visible())
        self.cohort_management_page.is_cohorted = False
        self.assertFalse(
            self.cohort_management_page.cohort_management_controls_visible())
        self.browser.refresh()
        self.cohort_management_page.wait_for_page()
        self.assertFalse(self.cohort_management_page.is_cohorted)
        self.assertFalse(
            self.cohort_management_page.cohort_management_controls_visible())

    def test_link_to_data_download(self):
        """
        Scenario: a link is present from the cohort configuration in
        the instructor dashboard to the Data Download section.

        Given I have a course with a cohort defined
        When I view the cohort in the LMS instructor dashboard
        There is a link to take me to the Data Download section of the Instructor Dashboard.
        """
        self.cohort_management_page.select_data_download()
        data_download_page = DataDownloadPage(self.browser)
        data_download_page.wait_for_page()

    def test_cohort_by_csv_both_columns(self):
        """
        Scenario: the instructor can upload a file with user and cohort assignments, using both emails and usernames.

        Given I have a course with two cohorts defined
        When I go to the cohort management section of the instructor dashboard
        I can upload a CSV file with assignments of users to cohorts via both usernames and emails
        Then I can download a file with results
        And appropriate events have been emitted
        """
        csv_contents = [
            ['username', 'email', 'ignored_column', 'cohort'],
            [self.instructor_name, '', 'June', 'ManualCohort1'],
            ['', self.student_email, 'Spring', 'AutoCohort1'],
            [self.other_student_name, '', 'Fall', 'ManualCohort1'],
        ]
        filename = "cohort_csv_both_columns_1.csv"
        self._create_csv_file(filename, csv_contents)
        self._verify_csv_upload_acceptable_file(filename)

    def test_cohort_by_csv_only_email(self):
        """
        Scenario: the instructor can upload a file with user and cohort assignments, using only emails.

        Given I have a course with two cohorts defined
        When I go to the cohort management section of the instructor dashboard
        I can upload a CSV file with assignments of users to cohorts via only emails
        Then I can download a file with results
        And appropriate events have been emitted
        """
        csv_contents = [
            ['email', 'cohort'],
            [self.instructor_email, 'ManualCohort1'],
            [self.student_email, 'AutoCohort1'],
            [self.other_student_email, 'ManualCohort1'],
        ]
        filename = "cohort_csv_emails_only.csv"
        self._create_csv_file(filename, csv_contents)
        self._verify_csv_upload_acceptable_file(filename)

    def test_cohort_by_csv_only_username(self):
        """
        Scenario: the instructor can upload a file with user and cohort assignments, using only usernames.

        Given I have a course with two cohorts defined
        When I go to the cohort management section of the instructor dashboard
        I can upload a CSV file with assignments of users to cohorts via only usernames
        Then I can download a file with results
        And appropriate events have been emitted
        """
        csv_contents = [
            ['username', 'cohort'],
            [self.instructor_name, 'ManualCohort1'],
            [self.student_name, 'AutoCohort1'],
            [self.other_student_name, 'ManualCohort1'],
        ]
        filename = "cohort_users_only_username1.csv"
        self._create_csv_file(filename, csv_contents)
        self._verify_csv_upload_acceptable_file(filename)

    # TODO: Change unicode_hello_in_korean = u'ßßßßßß' to u'안녕하세요', after up gradation of Chrome driver. See TNL-3944
    def test_cohort_by_csv_unicode(self):
        """
        Scenario: the instructor can upload a file with user and cohort assignments, using both emails and usernames.

        Given I have a course with two cohorts defined
        And I add another cohort with a unicode name
        When I go to the cohort management section of the instructor dashboard
        I can upload a CSV file with assignments of users to the unicode cohort via both usernames and emails
        Then I can download a file with results

        TODO: refactor events verification to handle this scenario. Events verification assumes movements
        between other cohorts (manual and auto).
        """
        unicode_hello_in_korean = u'ßßßßßß'
        self._verify_cohort_settings(cohort_name=unicode_hello_in_korean,
                                     assignment_type=None)
        csv_contents = [['username', 'email', 'cohort'],
                        [self.instructor_name, '', unicode_hello_in_korean],
                        ['', self.student_email, unicode_hello_in_korean],
                        [self.other_student_name, '', unicode_hello_in_korean]]
        filename = "cohort_unicode_name.csv"
        self._create_csv_file(filename, csv_contents)
        self._verify_csv_upload_acceptable_file(filename, skip_events=True)

    def _verify_csv_upload_acceptable_file(self, filename, skip_events=None):
        """
        Helper method to verify cohort assignments after a successful CSV upload.

        When skip_events is specified, no assertions are made on events.
        """
        start_time = datetime.now(UTC)
        self.cohort_management_page.upload_cohort_file(filename)
        self._verify_cohort_by_csv_notification(
            u"Your file '{}' has been uploaded. Allow a few minutes for processing."
            .format(filename))

        if not skip_events:
            # student_user is moved from manual cohort to auto cohort
            self.assertEqual(
                self.event_collection.find({
                    "name":
                    "edx.cohort.user_added",
                    "time": {
                        "$gt": start_time
                    },
                    "event.user_id": {
                        "$in": [int(self.student_id)]
                    },
                    "event.cohort_name":
                    self.auto_cohort_name,
                }).count(), 1)
            self.assertEqual(
                self.event_collection.find({
                    "name":
                    "edx.cohort.user_removed",
                    "time": {
                        "$gt": start_time
                    },
                    "event.user_id":
                    int(self.student_id),
                    "event.cohort_name":
                    self.manual_cohort_name,
                }).count(), 1)
            # instructor_user (previously unassigned) is added to manual cohort
            self.assertEqual(
                self.event_collection.find({
                    "name":
                    "edx.cohort.user_added",
                    "time": {
                        "$gt": start_time
                    },
                    "event.user_id": {
                        "$in": [int(self.instructor_id)]
                    },
                    "event.cohort_name":
                    self.manual_cohort_name,
                }).count(), 1)
            # other_student_user (previously unassigned) is added to manual cohort
            self.assertEqual(
                self.event_collection.find({
                    "name":
                    "edx.cohort.user_added",
                    "time": {
                        "$gt": start_time
                    },
                    "event.user_id": {
                        "$in": [int(self.other_student_id)]
                    },
                    "event.cohort_name":
                    self.manual_cohort_name,
                }).count(), 1)

        # Verify the results can be downloaded.
        data_download = self.instructor_dashboard_page.select_data_download()
        data_download.wait_for_available_report()
        report = data_download.get_available_reports_for_download()[0]
        base_file_name = "cohort_results_"
        self.assertIn(
            "{}_{}".format(
                '_'.join([
                    self.course_info['org'], self.course_info['number'],
                    self.course_info['run']
                ]), base_file_name), report)
        report_datetime = datetime.strptime(
            report[report.index(base_file_name) +
                   len(base_file_name):-len(".csv")], "%Y-%m-%d-%H%M")
        self.assertLessEqual(start_time.replace(second=0, microsecond=0),
                             utc.localize(report_datetime))

    def test_cohort_by_csv_wrong_file_type(self):
        """
        Scenario: if the instructor uploads a non-csv file, an error message is presented.

        Given I have a course with cohorting enabled
        When I go to the cohort management section of the instructor dashboard
        And I upload a file without the CSV extension
        Then I get an error message stating that the file must have a CSV extension
        """
        self.cohort_management_page.upload_cohort_file("image.jpg")
        self._verify_cohort_by_csv_notification(
            "The file must end with the extension '.csv'.")

    def test_cohort_by_csv_missing_cohort(self):
        """
        Scenario: if the instructor uploads a csv file with no cohort column, an error message is presented.

        Given I have a course with cohorting enabled
        When I go to the cohort management section of the instructor dashboard
        And I upload a CSV file that is missing the cohort column
        Then I get an error message stating that the file must have a cohort column
        """
        self.cohort_management_page.upload_cohort_file(
            "cohort_users_missing_cohort_column.csv")
        self._verify_cohort_by_csv_notification(
            "The file must contain a 'cohort' column containing cohort names.")

    def test_cohort_by_csv_missing_user(self):
        """
        Scenario: if the instructor uploads a csv file with no username or email column, an error message is presented.

        Given I have a course with cohorting enabled
        When I go to the cohort management section of the instructor dashboard
        And I upload a CSV file that is missing both the username and email columns
        Then I get an error message stating that the file must have either a username or email column
        """
        self.cohort_management_page.upload_cohort_file(
            "cohort_users_missing_user_columns.csv")
        self._verify_cohort_by_csv_notification(
            "The file must contain a 'username' column, an 'email' column, or both."
        )

    def _verify_cohort_by_csv_notification(self, expected_message):
        """
        Helper method to check the CSV file upload notification message.
        """
        # Wait for notification message to appear, indicating file has been uploaded.
        EmptyPromise(
            lambda: 1 == len(self.cohort_management_page.get_csv_messages()),
            'Waiting for notification').fulfill()
        messages = self.cohort_management_page.get_csv_messages()
        self.assertEquals(expected_message, messages[0])

    @attr('a11y')
    def test_cohorts_management_a11y(self):
        """
        Run accessibility audit for cohort management.
        """
        self.cohort_management_page.a11y_audit.config.set_rules({
            "ignore": [
                'aria-valid-attr',  # TODO: LEARNER-6611 & LEARNER-6865
                'region',  # TODO: AC-932
            ]
        })
        self.cohort_management_page.a11y_audit.check_for_accessibility_errors()
Ejemplo n.º 19
0
class CohortConfigurationTest(EventsTestMixin, UniqueCourseTest,
                              CohortTestMixin):
    """
    Tests for cohort management on the LMS Instructor Dashboard
    """
    def setUp(self):
        """
        Set up a cohorted course
        """
        super(CohortConfigurationTest, self).setUp()  # lint-amnesty, pylint: disable=super-with-arguments

        # create course with cohorts
        self.manual_cohort_name = "ManualCohort1"
        self.auto_cohort_name = "AutoCohort1"
        self.course_fixture = CourseFixture(**self.course_info).install()
        self.setup_cohort_config(self.course_fixture,
                                 auto_cohort_groups=[self.auto_cohort_name])
        self.manual_cohort_id = self.add_manual_cohort(self.course_fixture,
                                                       self.manual_cohort_name)

        # create a non-instructor who will be registered for the course and in the manual cohort.
        self.student_name, self.student_email = self._generate_unique_user_data(
        )
        self.student_id = AutoAuthPage(self.browser,
                                       username=self.student_name,
                                       email=self.student_email,
                                       course_id=self.course_id,
                                       staff=False).visit().get_user_id()
        self.add_user_to_cohort(self.course_fixture, self.student_name,
                                self.manual_cohort_id)

        # create a second student user
        self.other_student_name, self.other_student_email = self._generate_unique_user_data(
        )
        self.other_student_id = AutoAuthPage(
            self.browser,
            username=self.other_student_name,
            email=self.other_student_email,
            course_id=self.course_id,
            staff=False).visit().get_user_id()

        # login as an instructor
        self.instructor_name, self.instructor_email = self._generate_unique_user_data(
        )
        self.instructor_id = AutoAuthPage(self.browser,
                                          username=self.instructor_name,
                                          email=self.instructor_email,
                                          course_id=self.course_id,
                                          staff=True).visit().get_user_id()

        # go to the membership page on the instructor dashboard
        self.instructor_dashboard_page = InstructorDashboardPage(
            self.browser, self.course_id)
        self.instructor_dashboard_page.visit()
        self.cohort_management_page = self.instructor_dashboard_page.select_cohort_management(
        )

    def _generate_unique_user_data(self):
        """
        Produce unique username and e-mail.
        """
        unique_username = '******' + str(uuid.uuid4().hex)[:12]
        unique_email = unique_username + "@example.com"
        return unique_username, unique_email

    @attr('a11y')
    def test_cohorts_management_a11y(self):
        """
        Run accessibility audit for cohort management.
        """
        self.cohort_management_page.a11y_audit.config.set_rules({
            "ignore": [
                'aria-valid-attr',  # TODO: LEARNER-6611 & LEARNER-6865
                'region',  # TODO: AC-932
            ]
        })
        self.cohort_management_page.a11y_audit.check_for_accessibility_errors()
Ejemplo n.º 20
0
class TestCohortHelp(ContainerBase):
    """
    Tests help links in Cohort page
    """
    def setUp(self, is_staff=True):
        super(TestCohortHelp, self).setUp(is_staff=is_staff)
        self.enable_cohorting(self.course_fixture)
        self.instructor_dashboard_page = InstructorDashboardPage(
            self.browser, self.course_id)
        self.instructor_dashboard_page.visit()
        self.cohort_management = self.instructor_dashboard_page.select_cohort_management(
        )

    def get_url_with_changed_domain(self, url):
        """
        Replaces .org with .io in the url
        Arguments:
            url (str): The url to perform replace operation on.
        Returns:
        str: The updated url
        """
        return url.replace('.org/', '.io/')

    def verify_help_link(self, href):
        """
        Verifies that help link is correct
        Arguments:
            href (str): Help url
        """
        expected_link = {'href': href, 'text': 'What does this mean?'}
        actual_link = self.cohort_management.get_cohort_help_element_and_click_help(
        )

        assert_link(self, expected_link, actual_link)
        assert_opened_help_link_is_correct(
            self, self.get_url_with_changed_domain(href))

    def test_manual_cohort_help(self):
        """
        Scenario: Help in 'What does it mean?' is correct when we create cohort manually.
        Given that I am at 'Cohort' tab of LMS instructor dashboard
        And I check 'Enable Cohorts'
        And I add cohort name it, choose Manual for Cohort Assignment Method and
        No content group for Associated Content Group and save the cohort
        Then you see the UI text "Learners are added to this cohort only when..."
        followed by "What does this mean" link.
        And I click "What does this mean" link then help link should end with
        course_features/cohorts/cohort_config.html#assign-learners-to-cohorts-manually
        """
        self.cohort_management.add_cohort('cohort_name')

        href = 'http://edx.readthedocs.org/projects/edx-partner-course-staff/en/latest/' \
               'course_features/cohorts/cohort_config.html#assign-learners-to-cohorts-manually'

        self.verify_help_link(href)

    def test_automatic_cohort_help(self):
        """
        Scenario: Help in 'What does it mean?' is correct when we create cohort automatically.
        Given that I am at 'Cohort' tab of LMS instructor dashboard
        And I check 'Enable Cohorts'
        And I add cohort name it, choose Automatic for Cohort Assignment Method and
        No content group for Associated Content Group and save the cohort
        Then you see the UI text "Learners are added to this cohort automatically"
        followed by "What does this mean" link.
        And I click "What does this mean" link then help link should end with
        course_features/cohorts/cohorts_overview.html#all-automated-assignment
        """

        self.cohort_management.add_cohort('cohort_name',
                                          assignment_type='random')

        href = 'http://edx.readthedocs.org/projects/edx-partner-course-staff/en/latest/' \
               'course_features/cohorts/cohorts_overview.html#all-automated-assignment'

        self.verify_help_link(href)

    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")
Ejemplo n.º 21
0
class TestCohortHelp(ContainerBase, CohortTestMixin):
    """
    Tests help links in Cohort page
    """
    def setUp(self, is_staff=True):
        super(TestCohortHelp, self).setUp(is_staff=is_staff)
        self.enable_cohorting(self.course_fixture)
        self.instructor_dashboard_page = InstructorDashboardPage(
            self.browser, self.course_id)
        self.instructor_dashboard_page.visit()
        self.cohort_management = self.instructor_dashboard_page.select_cohort_management(
        )

    def verify_help_link(self, href):
        """
        Verifies that help link is correct
        Arguments:
            href (str): Help url
        """
        help_element = self.cohort_management.get_cohort_help_element()
        self.assertEqual(help_element.text, "What does this mean?")
        click_and_wait_for_window(self, help_element)
        assert_opened_help_link_is_correct(self, href)

    def test_manual_cohort_help(self):
        """
        Scenario: Help in 'What does it mean?' is correct when we create cohort manually.
        Given that I am at 'Cohort' tab of LMS instructor dashboard
        And I check 'Enable Cohorts'
        And I add cohort name it, choose Manual for Cohort Assignment Method and
        No content group for Associated Content Group and save the cohort
        Then you see the UI text "Learners are added to this cohort only when..."
        followed by "What does this mean" link.
        And I click "What does this mean" link then help link should end with
        course_features/cohorts/cohort_config.html#assign-learners-to-cohorts-manually
        """
        self.cohort_management.add_cohort('cohort_name')

        href = url_for_help(
            'course_author',
            '/course_features/cohorts/cohort_config.html#assign-learners-to-cohorts-manually',
        )
        self.verify_help_link(href)

    def test_automatic_cohort_help(self):
        """
        Scenario: Help in 'What does it mean?' is correct when we create cohort automatically.
        Given that I am at 'Cohort' tab of LMS instructor dashboard
        And I check 'Enable Cohorts'
        And I add cohort name it, choose Automatic for Cohort Assignment Method and
        No content group for Associated Content Group and save the cohort
        Then you see the UI text "Learners are added to this cohort automatically"
        followed by "What does this mean" link.
        And I click "What does this mean" link then help link should end with
        course_features/cohorts/cohorts_overview.html#all-automated-assignment
        """

        self.cohort_management.add_cohort('cohort_name',
                                          assignment_type='random')

        href = url_for_help(
            'course_author',
            '/course_features/cohorts/cohorts_overview.html#all-automated-assignment',
        )
        self.verify_help_link(href)
class CohortContentGroupAssociationTest(UniqueCourseTest, CohortTestMixin):
    """
    Tests for linking between content groups and cohort in the instructor dashboard.
    """
    def setUp(self):
        """
        Set up a cohorted course with a user_partition of scheme "cohort".
        """
        super(CohortContentGroupAssociationTest, self).setUp()

        # create course with single cohort and two content groups (user_partition of type "cohort")
        self.cohort_name = "OnlyCohort"
        self.course_fixture = CourseFixture(**self.course_info).install()
        self.setup_cohort_config(self.course_fixture)
        self.cohort_id = self.add_manual_cohort(self.course_fixture,
                                                self.cohort_name)

        self.course_fixture._update_xblock(
            self.course_fixture._course_location, {
                "metadata": {
                    u"user_partitions": [
                        create_user_partition_json(
                            0,
                            'Apples, Bananas',
                            'Content Group Partition',
                            [Group("0", 'Apples'),
                             Group("1", 'Bananas')],
                            scheme="cohort")
                    ],
                },
            })

        # login as an instructor
        self.instructor_name = "instructor_user"
        self.instructor_id = AutoAuthPage(self.browser,
                                          username=self.instructor_name,
                                          email="*****@*****.**",
                                          course_id=self.course_id,
                                          staff=True).visit().get_user_id()

        # go to the membership page on the instructor dashboard
        self.instructor_dashboard_page = InstructorDashboardPage(
            self.browser, self.course_id)
        self.instructor_dashboard_page.visit()
        self.cohort_management_page = self.instructor_dashboard_page.select_cohort_management(
        )

    def test_no_content_group_linked(self):
        """
        Scenario: In a course with content groups, cohorts are initially not linked to a content group

        Given I have a course with a cohort defined and content groups defined
        When I view the cohort in the instructor dashboard and select settings
        Then the cohort is not linked to a content group
        And there is no text stating that content groups are undefined
        And the content groups are listed in the selector
        """
        self.cohort_management_page.select_cohort(self.cohort_name)
        self.assertIsNone(
            self.cohort_management_page.get_cohort_associated_content_group())
        self.assertIsNone(self.cohort_management_page.
                          get_cohort_related_content_group_message())
        self.assertEquals(["Apples", "Bananas"],
                          self.cohort_management_page.get_all_content_groups())

    def test_link_to_content_group(self):
        """
        Scenario: In a course with content groups, cohorts can be linked to content groups

        Given I have a course with a cohort defined and content groups defined
        When I view the cohort in the instructor dashboard and select settings
        And I link the cohort to one of the content groups and save
        Then there is a notification that my cohort has been saved
        And when I reload the page
        And I view the cohort in the instructor dashboard and select settings
        Then the cohort is still linked to the content group
        """
        self._link_cohort_to_content_group(self.cohort_name, "Bananas")
        self.assertEqual(
            "Bananas",
            self.cohort_management_page.get_cohort_associated_content_group())

    def test_unlink_from_content_group(self):
        """
        Scenario: In a course with content groups, cohorts can be unlinked from content groups

        Given I have a course with a cohort defined and content groups defined
        When I view the cohort in the instructor dashboard and select settings
        And I link the cohort to one of the content groups and save
        Then there is a notification that my cohort has been saved
        And I reload the page
        And I view the cohort in the instructor dashboard and select settings
        And I unlink the cohort from any content group and save
        Then there is a notification that my cohort has been saved
        And when I reload the page
        And I view the cohort in the instructor dashboard and select settings
        Then the cohort is not linked to any content group
        """
        self._link_cohort_to_content_group(self.cohort_name, "Bananas")
        self.cohort_management_page.set_cohort_associated_content_group(None)
        self._verify_settings_saved_and_reload(self.cohort_name)
        self.assertEqual(
            None,
            self.cohort_management_page.get_cohort_associated_content_group())

    def test_create_new_cohort_linked_to_content_group(self):
        """
        Scenario: In a course with content groups, a new cohort can be linked to a content group
            at time of creation.

        Given I have a course with a cohort defined and content groups defined
        When I create a new cohort and link it to a content group
        Then when I select settings I see that the cohort is linked to the content group
        And when I reload the page
        And I view the cohort in the instructor dashboard and select settings
        Then the cohort is still linked to the content group
        """
        new_cohort = "correctly linked cohort"
        self._create_new_cohort_linked_to_content_group(new_cohort, "Apples")
        self.browser.refresh()
        self.cohort_management_page.wait_for_page()
        self.cohort_management_page.select_cohort(new_cohort)
        self.assertEqual(
            "Apples",
            self.cohort_management_page.get_cohort_associated_content_group())

    def test_missing_content_group(self):
        """
        Scenario: In a course with content groups, if a cohort is associated with a content group that no longer
            exists, a warning message is shown

        Given I have a course with a cohort defined and content groups defined
        When I create a new cohort and link it to a content group
        And I delete that content group from the course
        And I reload the page
        And I view the cohort in the instructor dashboard and select settings
        Then the settings display a message that the content group no longer exists
        And when I select a different content group and save
        Then the error message goes away
        """
        new_cohort = "linked to missing content group"
        self._create_new_cohort_linked_to_content_group(new_cohort, "Apples")
        self.course_fixture._update_xblock(
            self.course_fixture._course_location, {
                "metadata": {
                    u"user_partitions": [
                        create_user_partition_json(
                            0,
                            'Apples, Bananas',
                            'Content Group Partition',
                            [Group("2", 'Pears'),
                             Group("1", 'Bananas')],
                            scheme="cohort")
                    ],
                },
            })
        self.browser.refresh()
        self.cohort_management_page.wait_for_page()
        self.cohort_management_page.select_cohort(new_cohort)
        self.assertEqual(
            "Deleted Content Group",
            self.cohort_management_page.get_cohort_associated_content_group())
        self.assertEquals(["Bananas", "Pears", "Deleted Content Group"],
                          self.cohort_management_page.get_all_content_groups())
        self.assertEqual(
            "Warning:\nThe previously selected content group was deleted. Select another content group.",
            self.cohort_management_page.
            get_cohort_related_content_group_message())
        self.cohort_management_page.set_cohort_associated_content_group(
            "Pears")
        confirmation_messages = self.cohort_management_page.get_cohort_settings_messages(
        )
        self.assertEqual(["Saved cohort"], confirmation_messages)
        self.assertIsNone(self.cohort_management_page.
                          get_cohort_related_content_group_message())
        self.assertEquals(["Bananas", "Pears"],
                          self.cohort_management_page.get_all_content_groups())

    def _create_new_cohort_linked_to_content_group(self, new_cohort,
                                                   cohort_group):
        """
        Creates a new cohort linked to a content group.
        """
        self.cohort_management_page.add_cohort(new_cohort,
                                               content_group=cohort_group)
        self.assertEqual(
            cohort_group,
            self.cohort_management_page.get_cohort_associated_content_group())

    def _link_cohort_to_content_group(self, cohort_name, content_group):
        """
        Links a cohort to a content group. Saves the changes and verifies the cohort updated properly.
        Then refreshes the page and selects the cohort.
        """
        self.cohort_management_page.select_cohort(cohort_name)
        self.cohort_management_page.set_cohort_associated_content_group(
            content_group)
        self._verify_settings_saved_and_reload(cohort_name)

    def _verify_settings_saved_and_reload(self, cohort_name):
        """
        Verifies the confirmation message indicating that a cohort's settings have been updated.
        Then refreshes the page and selects the cohort.
        """
        confirmation_messages = self.cohort_management_page.get_cohort_settings_messages(
        )
        self.assertEqual(["Saved cohort"], confirmation_messages)
        self.browser.refresh()
        self.cohort_management_page.wait_for_page()
        self.cohort_management_page.select_cohort(cohort_name)
Ejemplo n.º 23
0
class TestLTIConusmer(UniqueCourseTest):
    """
    Base class for tests of LTI xblock in the LMS.
    """

    USERNAME = "******"
    EMAIL = "*****@*****.**"
    host = os.environ.get('BOK_CHOY_HOSTNAME', '127.0.0.1')

    def setUp(self):
        super(TestLTIConusmer, self).setUp()
        self.courseware_page = CoursewarePage(self.browser, self.course_id)
        self.lti_iframe = LTIContentIframe(self.browser, self.course_id)
        self.tab_nav = TabNavPage(self.browser)
        self.progress_page = ProgressPage(self.browser, self.course_id)
        self.instructor_dashboard_page = InstructorDashboardPage(
            self.browser, self.course_id)
        self.grade_book_page = GradeBookPage(self.browser)
        # Install a course
        display_name = "Test Course" + self.unique_id
        self.course_fix = CourseFixture(self.course_info['org'],
                                        self.course_info['number'],
                                        self.course_info['run'],
                                        display_name=display_name)

    def test_lti_no_launch_url_is_not_rendered(self):
        """
        Scenario: LTI component in LMS with no launch_url is not rendered
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with no_launch_url fields:
            Then I view the LTI and error is shown
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {'launch_url': '', 'open_in_a_new_page': False}
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True,
                  self.course_id)
        self.courseware_page.visit()
        self.assertTrue(self.courseware_page.is_error_message_present())
        self.assertFalse(self.courseware_page.is_iframe_present())
        self.assertFalse(self.courseware_page.is_launch_url_present())

    def test_incorrect_lti_id_is_rendered_incorrectly(self):
        """
        Scenario: LTI component in LMS with incorrect lti_id is rendered incorrectly
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with incorrect_lti_id fields:
            Then I view the LTI but incorrect_signature warning is rendered
        """
        metadata_advance_settings = "test_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id':
            'incorrect_lti_id',
            'launch_url':
            'http://{}:{}/{}'.format(self.host, '8765',
                                     'correct_lti_endpoint'),
            'open_in_a_new_page':
            False
        }
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True,
                  self.course_id)
        self.courseware_page.visit()
        self.assertTrue(self.courseware_page.is_iframe_present())
        self.assertFalse(self.courseware_page.is_launch_url_present())
        self.assertFalse(self.courseware_page.is_error_message_present())
        self.courseware_page.go_to_lti_container()
        self.assertEqual("Wrong LTI signature", self.lti_iframe.lti_content)

    def test_incorrect_lti_credentials_is_rendered_incorrectly(self):
        """
        Scenario: LTI component in LMS with icorrect LTI credentials is rendered incorrectly
        Given the course has incorrect LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            I view the LTI but incorrect_signature warning is rendered
        """
        metadata_advance_settings = "test_lti_id:test_client_key:incorrect_lti_secret_key"
        metadata_lti_xblock = {
            'lti_id':
            'correct_lti_id',
            'launch_url':
            'http://{}:{}/{}'.format(self.host, '8765',
                                     'correct_lti_endpoint'),
            'open_in_a_new_page':
            False
        }
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True,
                  self.course_id)
        self.courseware_page.visit()
        self.assertTrue(self.courseware_page.is_iframe_present())
        self.assertFalse(self.courseware_page.is_launch_url_present())
        self.assertFalse(self.courseware_page.is_error_message_present())
        self.courseware_page.go_to_lti_container()
        self.assertEqual("Wrong LTI signature", self.lti_iframe.lti_content)

    def test_lti_is_rendered_in_iframe_correctly(self):
        """
        Scenario: LTI component in LMS is correctly rendered in iframe
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            I view the LTI and it is rendered in iframe correctly
        """

        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id':
            'correct_lti_id',
            'launch_url':
            'http://{}:{}/{}'.format(self.host, '8765',
                                     'correct_lti_endpoint'),
            'open_in_a_new_page':
            False
        }

        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True,
                  self.course_id)
        self.courseware_page.visit()
        self.assertTrue(self.courseware_page.is_iframe_present())
        self.assertFalse(self.courseware_page.is_launch_url_present())
        self.assertFalse(self.courseware_page.is_error_message_present())
        self.courseware_page.go_to_lti_container()
        self.assertEqual("This is LTI tool. Success.",
                         self.lti_iframe.lti_content)

    def test_lti_graded_component_for_staff(self):
        """
        Scenario: Graded LTI component in LMS is correctly works for staff
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            verify scores on progress and grade book pages.
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id':
            'correct_lti_id',
            'launch_url':
            'http://{}:{}/{}'.format(self.host, '8765',
                                     'correct_lti_endpoint'),
            'open_in_a_new_page':
            False,
            'weight':
            10,
            'graded':
            True,
            'has_score':
            True
        }
        expected_scores = [(5, 10)]
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True,
                  self.course_id)
        self.courseware_page.visit()
        self.courseware_page.go_to_lti_container()
        self.lti_iframe.submit_lti_answer('#submit-button')
        self.assertIn("LTI consumer (edX) responded with XML content",
                      self.lti_iframe.lti_content)
        self.lti_iframe.switch_to_default()
        self.tab_nav.go_to_tab('Progress')
        actual_scores = self.progress_page.scores("Test Chapter",
                                                  "Test Section")
        self.assertEqual(actual_scores, expected_scores)
        self.assertEqual(['Overall Score', 'Overall Score\n1%'],
                         self.progress_page.graph_overall_score())
        self.tab_nav.go_to_tab('Instructor')
        student_admin_section = self.instructor_dashboard_page.select_student_admin(
            StudentAdminPage)
        student_admin_section.click_grade_book_link()
        self.assertEqual(
            "50",
            self.grade_book_page.get_value_in_the_grade_book(
                'Homework 1 - Test Section', 1))
        self.assertEqual(
            "1", self.grade_book_page.get_value_in_the_grade_book('Total', 1))

    def test_lti_switch_role_works_correctly(self):
        """
        Scenario: Graded LTI component in LMS role's masquerading correctly works
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            switch role from instructor to learner and verify that it works correctly
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id':
            'correct_lti_id',
            'launch_url':
            'http://{}:{}/{}'.format(self.host, '8765',
                                     'correct_lti_endpoint'),
            'open_in_a_new_page':
            False,
            'has_score':
            True
        }
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True,
                  self.course_id)
        self.courseware_page.visit()
        self.assertTrue(self.courseware_page.is_iframe_present())
        self.assertFalse(self.courseware_page.is_launch_url_present())
        self.assertFalse(self.courseware_page.is_error_message_present())
        self.courseware_page.go_to_lti_container()
        self.assertEqual("This is LTI tool. Success.",
                         self.lti_iframe.lti_content)
        self.assertEqual("Role: Instructor", self.lti_iframe.get_user_role)
        self.lti_iframe.switch_to_default()
        select_option_by_text(self.courseware_page.get_role_selector,
                              'Learner')
        self.courseware_page.wait_for_ajax()
        self.assertTrue(self.courseware_page.is_iframe_present())
        self.assertFalse(self.courseware_page.is_launch_url_present())
        self.assertFalse(self.courseware_page.is_error_message_present())
        self.courseware_page.go_to_lti_container()
        self.assertEqual("This is LTI tool. Success.",
                         self.lti_iframe.lti_content)
        self.assertEqual("Role: Student", self.lti_iframe.get_user_role)

    def test_lti_graded_component_for_learner(self):
        """
        Scenario: Graded LTI component in LMS is correctly works for learners
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            verify scores on progress
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id':
            'correct_lti_id',
            'launch_url':
            'http://{}:{}/{}'.format(self.host, '8765',
                                     'correct_lti_endpoint'),
            'open_in_a_new_page':
            False,
            'weight':
            10,
            'graded':
            True,
            'has_score':
            True
        }
        expected_scores = [(5, 10)]
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, False,
                  self.course_id)
        self.courseware_page.visit()
        self.courseware_page.go_to_lti_container()
        self.lti_iframe.submit_lti_answer('#submit-button')
        self.assertIn("LTI consumer (edX) responded with XML content",
                      self.lti_iframe.lti_content)
        self.lti_iframe.switch_to_default()
        self.tab_nav.go_to_tab('Progress')
        actual_scores = self.progress_page.scores("Test Chapter",
                                                  "Test Section")
        self.assertEqual(actual_scores, expected_scores)
        self.assertEqual(['Overall Score', 'Overall Score\n1%'],
                         self.progress_page.graph_overall_score())

    def test_lti_v2_callback_graded_component(self):
        """
        Scenario: Graded LTI component in LMS is correctly works with LTI2v0 PUT callback
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            verify scores on progress and grade book pages.
            verify feedback in LTI component.
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id':
            'correct_lti_id',
            'launch_url':
            'http://{}:{}/{}'.format(self.host, '8765',
                                     'correct_lti_endpoint'),
            'open_in_a_new_page':
            False,
            'weight':
            10,
            'graded':
            True,
            'has_score':
            True
        }
        expected_scores = [(8, 10)]
        problem_score = '(8.0 / 10.0 points)'
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True,
                  self.course_id)
        self.courseware_page.visit()
        self.courseware_page.go_to_lti_container()
        self.lti_iframe.submit_lti_answer("#submit-lti2-button")
        self.assertIn("LTI consumer (edX) responded with HTTP 200",
                      self.lti_iframe.lti_content)
        self.lti_iframe.switch_to_default()
        self.tab_nav.go_to_tab('Progress')
        actual_scores = self.progress_page.scores("Test Chapter",
                                                  "Test Section")
        self.assertEqual(actual_scores, expected_scores)
        self.assertEqual(['Overall Score', 'Overall Score\n1%'],
                         self.progress_page.graph_overall_score())
        self.tab_nav.go_to_tab('Instructor')
        student_admin_section = self.instructor_dashboard_page.select_student_admin(
            StudentAdminPage)
        student_admin_section.click_grade_book_link()
        self.assertEqual(
            "80",
            self.grade_book_page.get_value_in_the_grade_book(
                'Homework 1 - Test Section', 1))
        self.assertEqual(
            "1", self.grade_book_page.get_value_in_the_grade_book('Total', 1))
        self.tab_nav.go_to_tab('Course')
        self.assertEqual(
            problem_score,
            self.courseware_page.get_elem_text('.problem-progress'))
        self.assertEqual(
            "This is awesome.",
            self.courseware_page.get_elem_text('.problem-feedback'))

    def test_lti_delete_callback_graded_component(self):
        """
        Scenario: Graded LTI component in LMS is correctly works with LTI2v0 PUT delete callback
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            Verify LTI provider deletes my grade on progress and grade book page
            verify LTI provider deletes feedback from LTI Component
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id':
            'correct_lti_id',
            'launch_url':
            'http://{}:{}/{}'.format(self.host, '8765',
                                     'correct_lti_endpoint'),
            'open_in_a_new_page':
            False,
            'weight':
            10,
            'graded':
            True,
            'has_score':
            True
        }
        expected_scores = [(0, 10)]
        problem_score = '(8.0 / 10.0 points)'
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True,
                  self.course_id)
        self.courseware_page.visit()
        self.courseware_page.go_to_lti_container()
        self.lti_iframe.submit_lti_answer("#submit-lti2-button")
        self.assertIn("LTI consumer (edX) responded with HTTP 200",
                      self.lti_iframe.lti_content)
        self.lti_iframe.switch_to_default()
        self.courseware_page.visit()
        self.assertEqual(
            problem_score,
            self.courseware_page.get_elem_text('.problem-progress'))
        self.assertEqual(
            "This is awesome.",
            self.courseware_page.get_elem_text('.problem-feedback'))
        self.courseware_page.go_to_lti_container()
        self.lti_iframe.submit_lti_answer("#submit-lti-delete-button")
        self.courseware_page.visit()
        self.assertEqual(
            "(10.0 points possible)",
            self.courseware_page.get_elem_text('.problem-progress'))
        self.assertFalse(
            self.courseware_page.is_lti_component_present('.problem-feedback'))
        self.tab_nav.go_to_tab('Progress')
        actual_scores = self.progress_page.scores("Test Chapter",
                                                  "Test Section")
        self.assertEqual(actual_scores, expected_scores)
        self.assertEqual(['Overall Score', 'Overall Score\n0%'],
                         self.progress_page.graph_overall_score())
        self.tab_nav.go_to_tab('Instructor')
        student_admin_section = self.instructor_dashboard_page.select_student_admin(
            StudentAdminPage)
        student_admin_section.click_grade_book_link()
        self.assertEqual(
            "0",
            self.grade_book_page.get_value_in_the_grade_book(
                'Homework 1 - Test Section', 1))
        self.assertEqual(
            "0", self.grade_book_page.get_value_in_the_grade_book('Total', 1))

    def test_lti_hide_launch_shows_no_button(self):
        """
        Scenario: LTI component that set to hide_launch and open_in_a_new_page shows no button
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            verify LTI component don't show launch button with text "LTI (External resource)"
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id':
            'correct_lti_id',
            'launch_url':
            'http://{}:{}/{}'.format(self.host, '8765',
                                     'correct_lti_endpoint'),
            'open_in_a_new_page':
            False,
            'hide_launch':
            True
        }
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True,
                  self.course_id)
        self.courseware_page.visit()
        self.assertFalse(
            self.courseware_page.is_lti_component_present(
                '.link_lti_new_window'))
        self.assertEqual("LTI (External resource)",
                         self.courseware_page.get_elem_text('.problem-header'))

    def test_lti_hide_launch_shows_no_iframe(self):
        """
        Scenario: LTI component that set to hide_launch and not open_in_a_new_page shows no iframe
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            verify LTI component don't show LTI iframe with text "LTI (External resource)"
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id':
            'correct_lti_id',
            'launch_url':
            'http://{}:{}/{}'.format(self.host, '8765',
                                     'correct_lti_endpoint'),
            'open_in_a_new_page':
            True,
            'hide_launch':
            True
        }
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True,
                  self.course_id)
        self.courseware_page.visit()
        self.assertFalse(
            self.courseware_page.is_lti_component_present('.ltiLaunchFrame'))
        self.assertEqual("LTI (External resource)",
                         self.courseware_page.get_elem_text('.problem-header'))

    def test_lti_button_text_correctly_displayed(self):
        """
        Scenario: LTI component button text is correctly displayed
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            verify LTI component button with text "Launch Application"
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id':
            'correct_lti_id',
            'launch_url':
            'http://{}:{}/{}'.format(self.host, '8765',
                                     'correct_lti_endpoint'),
            'button_text':
            'Launch Application'
        }
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True,
                  self.course_id)
        self.courseware_page.visit()
        self.assertEqual(
            "Launch Application",
            self.courseware_page.get_elem_text('.link_lti_new_window'))

    def test_lti_component_description_correctly_displayed(self):
        """
        Scenario: LTI component description is correctly displayed
        Given the course has correct LTI credentials with registered Instructor
            the course has an LTI component with correct fields:
            LTI component description with text "Application description"
        """
        metadata_advance_settings = "correct_lti_id:test_client_key:test_client_secret"
        metadata_lti_xblock = {
            'lti_id':
            'correct_lti_id',
            'launch_url':
            'http://{}:{}/{}'.format(self.host, '8765',
                                     'correct_lti_endpoint'),
            'description':
            'Application description'
        }
        self.set_advance_settings(metadata_advance_settings)
        self.create_lti_xblock(metadata_lti_xblock)
        auto_auth(self.browser, self.USERNAME, self.EMAIL, True,
                  self.course_id)
        self.courseware_page.visit()
        self.assertEqual(
            "Application description",
            self.courseware_page.get_elem_text('.lti-description'))

    def set_advance_settings(self, metadata_advance_settings):

        # Set value against advanced modules in advanced settings
        self.course_fix.add_advanced_settings({
            "advanced_modules": {
                "value": ["lti_consumer"]
            },
            'lti_passports': {
                "value": [metadata_advance_settings]
            }
        })

    def create_lti_xblock(self, metadata_lti_xblock):
        self.course_fix.add_children(
            XBlockFixtureDesc(
                category='chapter', display_name='Test Chapter').add_children(
                    XBlockFixtureDesc(category='sequential',
                                      display_name='Test Section',
                                      grader_type='Homework',
                                      graded=True).add_children(
                                          XBlockFixtureDesc(
                                              category='lti',
                                              display_name='LTI',
                                              metadata=metadata_lti_xblock).
                                          add_children()))).install()
class BaseDividedDiscussionTest(UniqueCourseTest, CohortTestMixin):
    """
    Base class for tests related to divided discussions.
    """
    def setUp(self):
        """
        Set up a discussion topic
        """
        super(BaseDividedDiscussionTest, self).setUp()

        self.discussion_id = "test_discussion_{}".format(uuid.uuid4().hex)
        self.course_fixture = CourseFixture(**self.course_info).add_children(
            XBlockFixtureDesc("chapter", "Test Section").add_children(
                XBlockFixtureDesc(
                    "sequential", "Test Subsection").add_children(
                        XBlockFixtureDesc(
                            "vertical", "Test Unit").add_children(
                                XBlockFixtureDesc("discussion",
                                                  "Test Discussion",
                                                  metadata={
                                                      "discussion_id":
                                                      self.discussion_id
                                                  }))))).install()

        # create course with single cohort and two content groups (user_partition of type "cohort")
        self.cohort_name = "OnlyCohort"
        self.setup_cohort_config(self.course_fixture)
        self.cohort_id = self.add_manual_cohort(self.course_fixture,
                                                self.cohort_name)

        # login as an instructor
        self.instructor_name = "instructor_user"
        self.instructor_id = AutoAuthPage(self.browser,
                                          username=self.instructor_name,
                                          email="*****@*****.**",
                                          course_id=self.course_id,
                                          staff=True).visit().get_user_id()

        # go to the membership page on the instructor dashboard
        self.instructor_dashboard_page = InstructorDashboardPage(
            self.browser, self.course_id)
        self.instructor_dashboard_page.visit()
        self.discussion_management_page = self.instructor_dashboard_page.select_discussion_management(
        )
        self.discussion_management_page.wait_for_page()

        self.course_wide_key = 'course-wide'
        self.inline_key = 'inline'
        self.scheme_key = 'scheme'

    def check_discussion_topic_visibility(self, visible=True):
        """
        Assert that discussion topics are visible with appropriate content.
        """
        self.assertEqual(
            visible,
            self.discussion_management_page.discussion_topics_visible())

        if visible:
            self.assertEqual(
                "Course-Wide Discussion Topics",
                self.discussion_management_page.
                divided_discussion_heading_is_visible(self.course_wide_key))
            self.assertTrue(
                self.discussion_management_page.is_save_button_disabled(
                    self.course_wide_key))

            self.assertEqual(
                "Content-Specific Discussion Topics",
                self.discussion_management_page.
                divided_discussion_heading_is_visible(self.inline_key))
            self.assertTrue(
                self.discussion_management_page.is_save_button_disabled(
                    self.inline_key))

    def reload_page(self, topics_visible=True):
        """
        Refresh the page, then verify if the discussion topics are visible on the discussion
        management instructor dashboard tab.
        """
        self.browser.refresh()
        self.discussion_management_page.wait_for_page()

        self.instructor_dashboard_page.select_discussion_management()
        self.discussion_management_page.wait_for_page()

        self.check_discussion_topic_visibility(topics_visible)

    def verify_save_confirmation_message(self, key):
        """
        Verify that the save confirmation message for the specified portion of the page is visible.
        """
        confirmation_message = self.discussion_management_page.get_divide_discussions_message(
            key=key)
        self.assertIn("Your changes have been saved.", confirmation_message)
class BaseDividedDiscussionTest(UniqueCourseTest, CohortTestMixin):
    """
    Base class for tests related to divided discussions.
    """
    def setUp(self):
        """
        Set up a discussion topic
        """
        super(BaseDividedDiscussionTest, self).setUp()

        self.discussion_id = "test_discussion_{}".format(uuid.uuid4().hex)
        self.course_fixture = CourseFixture(**self.course_info).add_children(
            XBlockFixtureDesc("chapter", "Test Section").add_children(
                XBlockFixtureDesc("sequential", "Test Subsection").add_children(
                    XBlockFixtureDesc("vertical", "Test Unit").add_children(
                        XBlockFixtureDesc(
                            "discussion",
                            "Test Discussion",
                            metadata={"discussion_id": self.discussion_id}
                        )
                    )
                )
            )
        ).install()

        # create course with single cohort and two content groups (user_partition of type "cohort")
        self.cohort_name = "OnlyCohort"
        self.setup_cohort_config(self.course_fixture)
        self.cohort_id = self.add_manual_cohort(self.course_fixture, self.cohort_name)

        # login as an instructor
        self.instructor_name = "instructor_user"
        self.instructor_id = AutoAuthPage(
            self.browser, username=self.instructor_name, email="*****@*****.**",
            course_id=self.course_id, staff=True
        ).visit().get_user_id()

        # go to the membership page on the instructor dashboard
        self.instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
        self.instructor_dashboard_page.visit()
        self.discussion_management_page = self.instructor_dashboard_page.select_discussion_management()
        self.discussion_management_page.wait_for_page()

        self.course_wide_key = 'course-wide'
        self.inline_key = 'inline'
        self.scheme_key = 'scheme'

    def check_discussion_topic_visibility(self, visible=True):
        """
        Assert that discussion topics are visible with appropriate content.
        """
        self.assertEqual(visible, self.discussion_management_page.discussion_topics_visible())

        if visible:
            self.assertEqual(
                "Course-Wide Discussion Topics",
                self.discussion_management_page.divided_discussion_heading_is_visible(self.course_wide_key)
            )
            self.assertTrue(self.discussion_management_page.is_save_button_disabled(self.course_wide_key))

            self.assertEqual(
                "Content-Specific Discussion Topics",
                self.discussion_management_page.divided_discussion_heading_is_visible(self.inline_key)
            )
            self.assertTrue(self.discussion_management_page.is_save_button_disabled(self.inline_key))

    def reload_page(self, topics_visible=True):
        """
        Refresh the page, then verify if the discussion topics are visible on the discussion
        management instructor dashboard tab.
        """
        self.browser.refresh()
        self.discussion_management_page.wait_for_page()

        self.instructor_dashboard_page.select_discussion_management()
        self.discussion_management_page.wait_for_page()

        self.check_discussion_topic_visibility(topics_visible)

    def verify_save_confirmation_message(self, key):
        """
        Verify that the save confirmation message for the specified portion of the page is visible.
        """
        confirmation_message = self.discussion_management_page.get_divide_discussions_message(key=key)
        self.assertIn("Your changes have been saved.", confirmation_message)