diff --git a/common/test/acceptance/pages/lms/instructor_dashboard.py b/common/test/acceptance/pages/lms/instructor_dashboard.py new file mode 100644 index 0000000000..1dccf3829a --- /dev/null +++ b/common/test/acceptance/pages/lms/instructor_dashboard.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +""" +Instructor (2) dashboard page. +""" + +from bok_choy.page_object import PageObject +from .course_page import CoursePage + + +class InstructorDashboardPage(CoursePage): + """ + Instructor dashboard, where course staff can manage a course. + """ + url_path = "instructor" + + def is_browser_on_page(self): + return self.q(css='div.instructor-dashboard-wrapper-2').present + + def select_membership(self): + """ + Selects the membership tab and returns the MembershipSection + """ + self.q(css='a[data-section=membership]').first.click() + membership_section = MembershipPage(self.browser) + membership_section.wait_for_page() + return membership_section + + +class MembershipPage(PageObject): + """ + Membership section of the Instructor dashboard. + """ + url = None + + def is_browser_on_page(self): + return self.q(css='a[data-section=membership].active-section').present + + def _get_cohort_options(self): + """ + Returns the available options in the cohort dropdown, including the initial "Select a cohort". + """ + return self.q(css=".cohort-management #cohort-select option") + + def _name_without_count(self, name_with_count): + """ + Returns the name of the cohort with the count information excluded. + """ + return name_with_count.split(' (')[0] + + def get_cohorts(self): + """ + Returns, as a list, the names of the available cohorts in the drop-down, filtering out "Select a cohort". + """ + return [ + self._name_without_count(opt.text) + for opt in self._get_cohort_options().filter(lambda el: el.get_attribute('value') != "") + ] + + def get_selected_cohort(self): + """ + Returns the name of the selected cohort. + """ + return self._name_without_count( + self._get_cohort_options().filter(lambda el: el.is_selected()).first.text[0] + ) + + def select_cohort(self, cohort_name): + """ + Selects the given cohort in the drop-down. + """ + self.q(css=".cohort-management #cohort-select option").filter( + lambda el: self._name_without_count(el.text) == cohort_name + ).first.click() + + def get_cohort_group_setup(self): + """ + Returns the description of the current cohort + """ + return self.q(css='.cohort-management-group-setup .setup-value').first.text[0] + + def select_edit_settings(self): + self.q(css=".action-edit").first.click() diff --git a/common/test/acceptance/tests/discussion/test_cohorts.py b/common/test/acceptance/tests/discussion/test_cohorts.py index 047978342a..e5cfaaaf81 100644 --- a/common/test/acceptance/tests/discussion/test_cohorts.py +++ b/common/test/acceptance/tests/discussion/test_cohorts.py @@ -3,11 +3,11 @@ Tests related to the cohorting feature. """ from uuid import uuid4 -from helpers import BaseDiscussionMixin -from ...pages.lms.auto_auth import AutoAuthPage +from .helpers import BaseDiscussionMixin +from ..lms.helpers import CohortTestMixin from ..helpers import UniqueCourseTest +from ...pages.lms.auto_auth import AutoAuthPage from ...fixtures.course import (CourseFixture, XBlockFixtureDesc) -from ...fixtures import LMS_BASE_URL from ...pages.lms.discussion import (DiscussionTabSingleThreadPage, InlineDiscussionThreadPage, InlineDiscussionPage) from ...pages.lms.courseware import CoursewarePage @@ -17,7 +17,7 @@ from nose.plugins.attrib import attr class NonCohortedDiscussionTestMixin(BaseDiscussionMixin): """ - Mixin for tests of non-cohorted courses. + Mixin for tests of discussion in non-cohorted courses. """ def setup_cohorts(self): """ @@ -30,36 +30,17 @@ class NonCohortedDiscussionTestMixin(BaseDiscussionMixin): self.assertEquals(self.thread_page.get_group_visibility_label(), "This post is visible to everyone.") -class CohortedDiscussionTestMixin(BaseDiscussionMixin): +class CohortedDiscussionTestMixin(BaseDiscussionMixin, CohortTestMixin): """ - Mixin for tests of cohorted courses. + Mixin for tests of discussion in cohorted courses. """ - def add_cohort(self, name): - """ - Adds a cohort group by name, returning the ID for the group. - """ - url = LMS_BASE_URL + "/courses/" + self.course_fixture._course_key + '/cohorts/add' - data = {"name": name} - response = self.course_fixture.session.post(url, data=data, headers=self.course_fixture.headers) - self.assertTrue(response.ok, "Failed to create cohort") - return response.json()['cohort']['id'] - def setup_cohorts(self): """ Sets up the course to use cohorting with a single defined cohort group. """ - self.course_fixture._update_xblock(self.course_fixture._course_location, { - "metadata": { - u"cohort_config": { - "auto_cohort_groups": [], - "auto_cohort": False, - "cohorted_discussions": [], - "cohorted": True - }, - }, - }) + self.setup_cohort_config(self.course_fixture) self.cohort_1_name = "Cohort Group 1" - self.cohort_1_id = self.add_cohort(self.cohort_1_name) + self.cohort_1_id = self.add_manual_cohort(self.course_fixture, self.cohort_1_name) def test_cohort_visibility_label(self): # Must be moderator to view content in a cohort other than your own diff --git a/common/test/acceptance/tests/discussion/test_discussion.py b/common/test/acceptance/tests/discussion/test_discussion.py index 70b2291ddb..c4dae8872a 100644 --- a/common/test/acceptance/tests/discussion/test_discussion.py +++ b/common/test/acceptance/tests/discussion/test_discussion.py @@ -29,7 +29,7 @@ from ...fixtures.discussion import ( SearchResult, ) -from helpers import BaseDiscussionMixin +from .helpers import BaseDiscussionMixin class DiscussionResponsePaginationTestMixin(BaseDiscussionMixin): diff --git a/common/test/acceptance/tests/lms/__init__.py b/common/test/acceptance/tests/lms/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/test/acceptance/tests/lms/helpers.py b/common/test/acceptance/tests/lms/helpers.py new file mode 100644 index 0000000000..12533e98cf --- /dev/null +++ b/common/test/acceptance/tests/lms/helpers.py @@ -0,0 +1,35 @@ +""" +Helper functions and classes for LMS tests. +""" + +from ...fixtures import LMS_BASE_URL + + +class CohortTestMixin(object): + """ + Mixin for tests of cohorted courses + """ + def setup_cohort_config(self, course_fixture, auto_cohort_groups=None): + """ + Sets up the course to use cohorting with the given list of auto_cohort_groups. + If auto_cohort_groups is None, no auto cohort groups are set. + """ + course_fixture._update_xblock(course_fixture._course_location, { + "metadata": { + u"cohort_config": { + "auto_cohort_groups": auto_cohort_groups or [], + "cohorted_discussions": [], + "cohorted": True + }, + }, + }) + + def add_manual_cohort(self, course_fixture, name): + """ + Adds a cohort group by name, returning the ID for the group. + """ + url = LMS_BASE_URL + "/courses/" + course_fixture._course_key + '/cohorts/add' + data = {"name": name} + response = course_fixture.session.post(url, data=data, headers=course_fixture.headers) + self.assertTrue(response.ok, "Failed to create cohort") + return response.json()['cohort']['id'] diff --git a/common/test/acceptance/tests/test_lms.py b/common/test/acceptance/tests/lms/test_lms.py similarity index 80% rename from common/test/acceptance/tests/test_lms.py rename to common/test/acceptance/tests/lms/test_lms.py index f98788d584..54f9aae3b5 100644 --- a/common/test/acceptance/tests/test_lms.py +++ b/common/test/acceptance/tests/lms/test_lms.py @@ -1,26 +1,25 @@ # -*- coding: utf-8 -*- """ -E2E tests for the LMS. +End-to-end tests for the LMS. """ from textwrap import dedent from unittest import skip from bok_choy.web_app_test import WebAppTest -from .helpers import UniqueCourseTest, load_data_str -from ..pages.lms.auto_auth import AutoAuthPage -from ..pages.lms.find_courses import FindCoursesPage -from ..pages.lms.course_about import CourseAboutPage -from ..pages.lms.course_info import CourseInfoPage -from ..pages.lms.tab_nav import TabNavPage -from ..pages.lms.course_nav import CourseNavPage -from ..pages.lms.progress import ProgressPage -from ..pages.lms.dashboard import DashboardPage -from ..pages.lms.problem import ProblemPage -from ..pages.lms.video.video import VideoPage -from ..pages.xblock.acid import AcidView -from ..pages.lms.courseware import CoursewarePage -from ..fixtures.course import CourseFixture, XBlockFixtureDesc, CourseUpdateDesc +from ..helpers import UniqueCourseTest, load_data_str +from ...pages.lms.auto_auth import AutoAuthPage +from ...pages.lms.find_courses import FindCoursesPage +from ...pages.lms.course_about import CourseAboutPage +from ...pages.lms.course_info import CourseInfoPage +from ...pages.lms.tab_nav import TabNavPage +from ...pages.lms.course_nav import CourseNavPage +from ...pages.lms.progress import ProgressPage +from ...pages.lms.dashboard import DashboardPage +from ...pages.lms.problem import ProblemPage +from ...pages.lms.video.video import VideoPage +from ...pages.lms.courseware import CoursewarePage +from ...fixtures.course import CourseFixture, XBlockFixtureDesc, CourseUpdateDesc class RegistrationTest(UniqueCourseTest): @@ -267,7 +266,7 @@ class VideoTest(UniqueCourseTest): XBlockFixtureDesc('sequential', 'Test Subsection').add_children( XBlockFixtureDesc('vertical', 'Test Unit').add_children( XBlockFixtureDesc('video', 'Video') - )))).install() + )))).install() # Auto-auth register for the course AutoAuthPage(self.browser, course_id=self.course_id).visit() @@ -311,115 +310,6 @@ class VideoTest(UniqueCourseTest): self.assertGreaterEqual(self.video.duration, self.video.elapsed_time) -class XBlockAcidBase(UniqueCourseTest): - """ - Base class for tests that verify that XBlock integration is working correctly - """ - __test__ = False - - def setUp(self): - """ - Create a unique identifier for the course used in this test. - """ - # Ensure that the superclass sets up - super(XBlockAcidBase, self).setUp() - - self.setup_fixtures() - - AutoAuthPage(self.browser, course_id=self.course_id).visit() - - self.course_info_page = CourseInfoPage(self.browser, self.course_id) - self.tab_nav = TabNavPage(self.browser) - - def validate_acid_block_view(self, acid_block): - """ - Verify that the LMS view for the Acid Block is correct - """ - self.assertTrue(acid_block.init_fn_passed) - self.assertTrue(acid_block.resource_url_passed) - self.assertTrue(acid_block.scope_passed('user_state')) - self.assertTrue(acid_block.scope_passed('user_state_summary')) - self.assertTrue(acid_block.scope_passed('preferences')) - self.assertTrue(acid_block.scope_passed('user_info')) - - def test_acid_block(self): - """ - Verify that all expected acid block tests pass in the lms. - """ - - self.course_info_page.visit() - self.tab_nav.go_to_tab('Courseware') - - acid_block = AcidView(self.browser, '.xblock-student_view[data-block-type=acid]') - self.validate_acid_block_view(acid_block) - - -class XBlockAcidNoChildTest(XBlockAcidBase): - """ - Tests of an AcidBlock with no children - """ - __test__ = True - - def setup_fixtures(self): - course_fix = CourseFixture( - self.course_info['org'], - self.course_info['number'], - self.course_info['run'], - self.course_info['display_name'] - ) - - course_fix.add_children( - XBlockFixtureDesc('chapter', 'Test Section').add_children( - XBlockFixtureDesc('sequential', 'Test Subsection').add_children( - XBlockFixtureDesc('vertical', 'Test Unit').add_children( - XBlockFixtureDesc('acid', 'Acid Block') - ) - ) - ) - ).install() - - @skip('Flakey test, TE-401') - def test_acid_block(self): - super(XBlockAcidNoChildTest, self).test_acid_block() - - -class XBlockAcidChildTest(XBlockAcidBase): - """ - Tests of an AcidBlock with children - """ - __test__ = True - - def setup_fixtures(self): - course_fix = CourseFixture( - self.course_info['org'], - self.course_info['number'], - self.course_info['run'], - self.course_info['display_name'] - ) - - course_fix.add_children( - XBlockFixtureDesc('chapter', 'Test Section').add_children( - XBlockFixtureDesc('sequential', 'Test Subsection').add_children( - XBlockFixtureDesc('vertical', 'Test Unit').add_children( - XBlockFixtureDesc('acid_parent', 'Acid Parent Block').add_children( - XBlockFixtureDesc('acid', 'First Acid Child', metadata={'name': 'first'}), - XBlockFixtureDesc('acid', 'Second Acid Child', metadata={'name': 'second'}), - XBlockFixtureDesc('html', 'Html Child', data="Contents"), - ) - ) - ) - ) - ).install() - - def validate_acid_block_view(self, acid_block): - super(XBlockAcidChildTest, self).validate_acid_block_view() - self.assertTrue(acid_block.child_tests_passed) - - @skip('This will fail until we fix support of children in pure XBlocks') - def test_acid_block(self): - super(XBlockAcidChildTest, self).test_acid_block() - - class VisibleToStaffOnlyTest(UniqueCourseTest): """ Tests that content with visible_to_staff_only set to True cannot be viewed by students. @@ -574,7 +464,8 @@ class ProblemExecutionTest(UniqueCourseTest): course_fix.add_children( XBlockFixtureDesc('chapter', 'Test Section').add_children( XBlockFixtureDesc('sequential', 'Test Subsection').add_children( - XBlockFixtureDesc('problem', 'Python Problem', data=dedent("""\ + XBlockFixtureDesc('problem', 'Python Problem', data=dedent( + """\