class StaffViewTest(UniqueCourseTest): """ Tests that verify the staff view. """ USERNAME = "******" EMAIL = "*****@*****.**" def setUp(self): super(StaffViewTest, self).setUp() self.courseware_page = CoursewarePage(self.browser, self.course_id) # Install a course with sections/problems, tabs, updates, and handouts self.course_fixture = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'] ) self.populate_course_fixture(self.course_fixture) self.course_fixture.install() # Auto-auth register for the course. # Do this as global staff so that you will see the Staff View AutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id, staff=True).visit() def _goto_staff_page(self): """ Open staff page with assertion """ self.courseware_page.visit() staff_page = StaffCoursewarePage(self.browser, self.course_id) self.assertEqual(staff_page.staff_view_mode, 'Staff') return staff_page
def setUp(self): """Create a course that is closed for enrollment, and sign in as a user.""" super(EnrollmentClosedRedirectTest, self).setUp() course = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'] ) now = datetime.now(pytz.UTC) course.add_course_details({ 'enrollment_start': (now - timedelta(days=30)).isoformat(), 'enrollment_end': (now - timedelta(days=1)).isoformat() }) course.install() # Add an honor mode to the course ModeCreationPage(self.browser, self.course_id).visit() # Add a verified mode to the course ModeCreationPage( self.browser, self.course_id, mode_slug=u'verified', mode_display_name=u'Verified Certificate', min_price=10, suggested_prices='10,20' ).visit()
class StaffViewTest(UniqueCourseTest): """ Tests that verify the staff view. """ USERNAME = "******" EMAIL = "*****@*****.**" def setUp(self): super(StaffViewTest, self).setUp() self.courseware_page = CoursewarePage(self.browser, self.course_id) # Install a course with sections/problems, tabs, updates, and handouts self.course_fixture = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'] ) self.populate_course_fixture(self.course_fixture) # pylint: disable=no-member self.course_fixture.install() # Auto-auth register for the course. # Do this as global staff so that you will see the Staff View AutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id, staff=True).visit() def _goto_staff_page(self): """ Open staff page with assertion """ self.courseware_page.visit() staff_page = StaffPage(self.browser, self.course_id) self.assertEqual(staff_page.staff_view_mode, 'Staff') return staff_page
def setUp(self): """ Initialize pages and install a course fixture. """ super(PDFTextBooksTabTest, self).setUp() self.course_home_page = CourseHomePage(self.browser, self.course_id) self.tab_nav = TabNavPage(self.browser) # Install a course with TextBooks course_fix = CourseFixture(self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name']) # Add PDF textbooks to course fixture. for i in range(1, 3): course_fix.add_textbook( "PDF Book {}".format(i), [{ "title": "Chapter Of Book {}".format(i), "url": "" }]) course_fix.install() # Auto-auth register for the course AutoAuthPage(self.browser, course_id=self.course_id).visit()
class BaseDiscussionTestCase(UniqueCourseTest, ForumsConfigMixin): """Base test case class for all discussions-related tests.""" def setUp(self): super(BaseDiscussionTestCase, self).setUp() self.discussion_id = "test_discussion_{}".format(uuid4().hex) self.course_fixture = CourseFixture(**self.course_info) self.course_fixture.add_advanced_settings({ 'discussion_topics': { 'value': { 'Test Discussion Topic': { 'id': self.discussion_id } } } }) self.course_fixture.install() self.enable_forums() def create_single_thread_page(self, thread_id): """ Sets up a `DiscussionTabSingleThreadPage` for a given `thread_id`. """ return DiscussionTabSingleThreadPage(self.browser, self.course_id, self.discussion_id, thread_id)
class BaseDiscussionTestCase(UniqueCourseTest, ForumsConfigMixin): """Base test case class for all discussions-related tests.""" def setUp(self): super(BaseDiscussionTestCase, self).setUp() self.discussion_id = "test_discussion_{}".format(uuid4().hex) self.course_fixture = CourseFixture(**self.course_info) self.course_fixture.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} ) ) ) ) ) self.course_fixture.add_advanced_settings( {'discussion_topics': {'value': {'General': {'id': 'course'}}}} ) self.course_fixture.install() self.enable_forums() def create_single_thread_page(self, thread_id): """ Sets up a `DiscussionTabSingleThreadPage` for a given `thread_id`. """ return DiscussionTabSingleThreadPage(self.browser, self.course_id, self.discussion_id, thread_id)
class CreateCCXCoachTest(EventsTestMixin, UniqueCourseTest): """ Test ccx end to end process. """ USERNAME = "******" EMAIL = "*****@*****.**" shard = 7 def setUp(self): super(CreateCCXCoachTest, self).setUp() self.course_info.update({"settings": {"enable_ccx": "true"}}) self.course_fixture = CourseFixture(**self.course_info) self.course_fixture.add_advanced_settings( {"enable_ccx": { "value": "true" }}) self.course_fixture.install() self.auto_auth(self.USERNAME, self.EMAIL) self.coach_dashboard_page = self.visit_coach_dashboard() def auto_auth(self, username, email): """ Logout and login with given credentials. """ AutoAuthPage(self.browser, username=username, email=email, course_id=self.course_id, staff=True).visit() def visit_coach_dashboard(self): """ Visits the instructor dashboard. """ coach_dashboard_page = CoachDashboardPage(self.browser, self.course_id) coach_dashboard_page.visit() return coach_dashboard_page def test_create_ccx(self): """ Assert that ccx created. """ ccx_name = "Test ccx" self.coach_dashboard_page.fill_ccx_name_text_box(ccx_name) self.coach_dashboard_page.wait_for_page() # Assert that new ccx is created and we are on ccx dashboard/enrollment tab. self.assertTrue( self.coach_dashboard_page.is_browser_on_enrollment_page()) # Assert that the user cannot click in the "View Unit in Studio" button, # which means the user cannot edit the ccx course in studio self.assertFalse( self.coach_dashboard_page.is_button_view_unit_in_studio_visible())
class CreateCCXCoachTest(EventsTestMixin, UniqueCourseTest): """ Test ccx end to end process. """ USERNAME = "******" EMAIL = "*****@*****.**" shard = 7 def setUp(self): super(CreateCCXCoachTest, self).setUp() self.course_info.update({"settings": {"enable_ccx": "true"}}) self.course_fixture = CourseFixture(**self.course_info) self.course_fixture.add_advanced_settings({ "enable_ccx": {"value": "true"} }) self.course_fixture.install() self.auto_auth(self.USERNAME, self.EMAIL) self.coach_dashboard_page = self.visit_coach_dashboard() def auto_auth(self, username, email): """ Logout and login with given credentials. """ AutoAuthPage(self.browser, username=username, email=email, course_id=self.course_id, staff=True).visit() def visit_coach_dashboard(self): """ Visits the instructor dashboard. """ coach_dashboard_page = CoachDashboardPage(self.browser, self.course_id) coach_dashboard_page.visit() return coach_dashboard_page def test_create_ccx(self): """ Assert that ccx created. """ ccx_name = "Test ccx" self.coach_dashboard_page.fill_ccx_name_text_box(ccx_name) self.coach_dashboard_page.wait_for_page() # Assert that new ccx is created and we are on ccx dashboard/enrollment tab. self.assertTrue(self.coach_dashboard_page.is_browser_on_enrollment_page()) # Assert that the user cannot click in the "View Unit in Studio" button, # which means the user cannot edit the ccx course in studio self.assertFalse(self.coach_dashboard_page.is_button_view_unit_in_studio_visible())
class BaseDiscussionTestCase(UniqueCourseTest): def setUp(self): super(BaseDiscussionTestCase, self).setUp() self.discussion_id = "test_discussion_{}".format(uuid4().hex) self.course_fixture = CourseFixture(**self.course_info) self.course_fixture.add_advanced_settings( {'discussion_topics': {'value': {'Test Discussion Topic': {'id': self.discussion_id}}}} ) self.course_fixture.install() def create_single_thread_page(self, thread_id): """ Sets up a `DiscussionTabSingleThreadPage` for a given `thread_id`. """ return DiscussionTabSingleThreadPage(self.browser, self.course_id, self.discussion_id, thread_id)
class BaseLmsDashboardTest(UniqueCourseTest): """ Base test suite for the LMS Student Dashboard """ def setUp(self): """ Initializes the components (page objects, courses, users) for this test suite """ # Some parameters are provided by the parent setUp() routine, such as the following: # self.course_id, self.course_info, self.unique_id super(BaseLmsDashboardTest, self).setUp() # Load page objects for use by the tests self.dashboard_page = DashboardPage(self.browser) # Configure some aspects of the test course and install the settings into the course self.course_fixture = CourseFixture( self.course_info["org"], self.course_info["number"], self.course_info["run"], self.course_info["display_name"], ) self.course_fixture.add_advanced_settings({ u"social_sharing_url": {u"value": "http://custom/course/url"} }) self.course_fixture.install() self.username = "******".format(uuid=self.unique_id[0:6]) self.email = "{user}@example.com".format(user=self.username) # Create the test user, register them for the course, and authenticate AutoAuthPage( self.browser, username=self.username, email=self.email, course_id=self.course_id ).visit() # Navigate the authenticated, enrolled user to the dashboard page and get testing! self.dashboard_page.visit()
def setUp(self): """ Initialize pages and install a course fixture. """ super(PDFTextBooksTabTest, self).setUp() self.course_home_page = CourseHomePage(self.browser, self.course_id) self.tab_nav = TabNavPage(self.browser) # Install a course with TextBooks course_fix = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'] ) # Add PDF textbooks to course fixture. for i in range(1, 3): course_fix.add_textbook("PDF Book {}".format(i), [{"title": "Chapter Of Book {}".format(i), "url": ""}]) course_fix.install() # Auto-auth register for the course AutoAuthPage(self.browser, course_id=self.course_id).visit()
class CertificateProgressPageTest(UniqueCourseTest): """ Tests for verifying Certificate info on Progress tab of course page. """ def setUp(self): super(CertificateProgressPageTest, self).setUp() # set same course number as we have in fixture json self.course_info['number'] = "3355358979513794782079645765720179311111" test_certificate_config = { 'id': 1, 'name': 'Certificate name', 'description': 'Certificate description', 'course_title': 'Course title override', 'signatories': [], 'version': 1, 'is_active': True } course_settings = {'certificates': test_certificate_config} self.course_fixture = CourseFixture(self.course_info["org"], self.course_info["number"], self.course_info["run"], self.course_info["display_name"], settings=course_settings) self.course_fixture.add_advanced_settings({ "cert_html_view_enabled": { "value": "true" }, "certificates_show_before_end": { "value": "true" } }) self.course_fixture.add_update( CourseUpdateDesc(date='January 29, 2014', content='Test course update1')) self.course_fixture.add_children( XBlockFixtureDesc('static_tab', 'Test Static Tab'), XBlockFixtureDesc('chapter', 'Test Section').add_children( XBlockFixtureDesc( 'sequential', 'Test Subsection', grader_type='Final Exam').add_children( XBlockFixtureDesc( 'problem', 'Test Problem 1', data=load_data_str('multiple_choice.xml')), XBlockFixtureDesc('html', 'Test HTML'), )), XBlockFixtureDesc('chapter', 'Test Section 2').add_children( XBlockFixtureDesc( 'sequential', 'Test Subsection 2', grader_type='Midterm Exam').add_children( XBlockFixtureDesc( 'problem', 'Test Problem 2', data=load_data_str('formula_problem.xml')), ))) self.course_fixture.install() self.user_id = "99" # we have created a user with this id in fixture self.cert_fixture = CertificateConfigFixture(self.course_id, test_certificate_config) self.progress_page = ProgressPage(self.browser, self.course_id) self.courseware_page = CoursewarePage(self.browser, self.course_id) self.course_home_page = CourseHomePage(self.browser, self.course_id) self.tab_nav = TabNavPage(self.browser) def log_in_as_unique_user(self): """ Log in as a valid lms user. """ AutoAuthPage(self.browser, username="******", email="*****@*****.**", password="******", course_id=self.course_id).visit() def test_progress_page_has_view_certificate_button(self): """ Scenario: View Certificate option should be present on Course Progress menu if the user is awarded a certificate. And there should be no padding around the box containing certificate info. (See SOL-1196 for details on this) As a Student Given there is a course with certificate configuration And I have passed the course and certificate is generated When I go on the Progress tab for the course Then I should see a 'View Certificate' button And their should be no padding around Certificate info box. """ self.cert_fixture.install() self.log_in_as_unique_user() self.complete_course_problems() self.course_home_page.visit() self.tab_nav.go_to_tab('Progress') self.assertTrue( self.progress_page.q(css='.auto-cert-message').first.visible) actual_padding = get_element_padding(self.progress_page, '.wrapper-msg.wrapper-auto-cert') actual_padding = [ int(padding) for padding in actual_padding.itervalues() ] expected_padding = [0, 0, 0, 0] # Verify that their is no padding around the box containing certificate info. self.assertEqual(actual_padding, expected_padding) def complete_course_problems(self): """ Complete Course Problems. Problems were added in the setUp """ self.course_home_page.visit() # Navigate to Test Subsection in Test Section Section self.course_home_page.outline.go_to_section('Test Section', 'Test Subsection') # Navigate to Test Problem 1 self.courseware_page.nav.go_to_vertical('Test Problem 1') # Select correct value for from select menu self.courseware_page.q( css='select option[value="{}"]'.format('blue')).first.click() # Select correct radio button for the answer self.courseware_page.q( css='fieldset div.field:nth-child(4) input').nth(0).click() # Select correct radio buttons for the answer self.courseware_page.q( css='fieldset div.field:nth-child(2) input').nth(1).click() self.courseware_page.q( css='fieldset div.field:nth-child(4) input').nth(1).click() # Submit the answer self.courseware_page.q(css='button.submit').click() self.courseware_page.wait_for_ajax() # Navigate to the 'Test Subsection 2' of 'Test Section 2' self.course_home_page.visit() self.course_home_page.outline.go_to_section('Test Section 2', 'Test Subsection 2') # Navigate to Test Problem 2 self.courseware_page.nav.go_to_vertical('Test Problem 2') # Fill in the answer of the problem self.courseware_page.q( css='input[id^=input_][id$=_2_1]').fill('A*x^2 + sqrt(y)') # Submit the answer self.courseware_page.q(css='button.submit').click() self.courseware_page.wait_for_ajax()
class LibraryContentTestBase(UniqueCourseTest): """ Base class for library content block tests """ USERNAME = "******" EMAIL = "*****@*****.**" STAFF_USERNAME = "******" STAFF_EMAIL = "*****@*****.**" def populate_library_fixture(self, library_fixture): """ To be overwritten by subclassed tests. Used to install a library to run tests on. """ def setUp(self): """ Set up library, course and library content XBlock """ super(LibraryContentTestBase, self).setUp() self.courseware_page = CoursewarePage(self.browser, self.course_id) self.studio_course_outline = StudioCourseOutlinePage( self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run'] ) self.library_fixture = LibraryFixture('test_org', self.unique_id, 'Test Library {}'.format(self.unique_id)) self.populate_library_fixture(self.library_fixture) self.library_fixture.install() self.library_info = self.library_fixture.library_info self.library_key = self.library_fixture.library_key # Install a course with library content xblock self.course_fixture = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'] ) library_content_metadata = { 'source_library_id': unicode(self.library_key), 'mode': 'random', 'max_count': 1, } self.lib_block = XBlockFixtureDesc('library_content', "Library Content", metadata=library_content_metadata) self.course_fixture.add_children( XBlockFixtureDesc('chapter', SECTION_NAME).add_children( XBlockFixtureDesc('sequential', SUBSECTION_NAME).add_children( XBlockFixtureDesc('vertical', UNIT_NAME).add_children( self.lib_block ) ) ) ) self.course_fixture.install() def _change_library_content_settings(self, count=1, capa_type=None): """ Performs library block refresh in Studio, configuring it to show {count} children """ unit_page = self._go_to_unit_page(True) library_container_block = StudioLibraryContainerXBlockWrapper.from_xblock_wrapper(unit_page.xblocks[1]) library_container_block.edit() editor = StudioLibraryContentEditor(self.browser, library_container_block.locator) editor.count = count if capa_type is not None: editor.capa_type = capa_type editor.save() self._go_to_unit_page(change_login=False) unit_page.wait_for_page() unit_page.publish_action.click() unit_page.wait_for_ajax() self.assertIn("Published and Live", unit_page.publish_title) @property def library_xblocks_texts(self): """ Gets texts of all xblocks in library """ return frozenset(child.data for child in self.library_fixture.children) def _go_to_unit_page(self, change_login=True): """ Open unit page in Studio """ if change_login: LogoutPage(self.browser).visit() self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True) self.studio_course_outline.visit() subsection = self.studio_course_outline.section(SECTION_NAME).subsection(SUBSECTION_NAME) return subsection.expand_subsection().unit(UNIT_NAME).go_to() def _goto_library_block_page(self, block_id=None): """ Open library page in LMS """ self.courseware_page.visit() paragraphs = self.courseware_page.q(css='.course-content p').results if not paragraphs: course_home_page = CourseHomePage(self.browser, self.course_id) course_home_page.visit() course_home_page.outline.go_to_section_by_index(0, 0) block_id = block_id if block_id is not None else self.lib_block.locator #pylint: disable=attribute-defined-outside-init self.library_content_page = LibraryContentXBlockWrapper(self.browser, block_id) self.library_content_page.wait_for_page() def _auto_auth(self, username, email, staff): """ Logout and login with given credentials. """ AutoAuthPage(self.browser, username=username, email=email, course_id=self.course_id, staff=staff).visit()
class CertificateProgressPageTest(UniqueCourseTest): """ Tests for verifying Certificate info on Progress tab of course page. """ def setUp(self): super(CertificateProgressPageTest, self).setUp() # set same course number as we have in fixture json self.course_info["number"] = "3355358979513794782079645765720179311111" test_certificate_config = { "id": 1, "name": "Certificate name", "description": "Certificate description", "course_title": "Course title override", "signatories": [], "version": 1, "is_active": True, } course_settings = {"certificates": test_certificate_config} self.course_fixture = CourseFixture( self.course_info["org"], self.course_info["number"], self.course_info["run"], self.course_info["display_name"], settings=course_settings, ) self.course_fixture.add_advanced_settings({"cert_html_view_enabled": {"value": "true"}}) self.course_fixture.add_update(CourseUpdateDesc(date="January 29, 2014", content="Test course update1")) self.course_fixture.add_children( XBlockFixtureDesc("static_tab", "Test Static Tab"), XBlockFixtureDesc("chapter", "Test Section").add_children( XBlockFixtureDesc("sequential", "Test Subsection", grader_type="Final Exam").add_children( XBlockFixtureDesc("problem", "Test Problem 1", data=load_data_str("multiple_choice.xml")), XBlockFixtureDesc("html", "Test HTML"), ) ), XBlockFixtureDesc("chapter", "Test Section 2").add_children( XBlockFixtureDesc("sequential", "Test Subsection 2", grader_type="Midterm Exam").add_children( XBlockFixtureDesc("problem", "Test Problem 2", data=load_data_str("formula_problem.xml")) ) ), ) self.course_fixture.install() self.user_id = "99" # we have created a user with this id in fixture self.cert_fixture = CertificateConfigFixture(self.course_id, test_certificate_config) self.course_info_page = CourseInfoPage(self.browser, self.course_id) self.progress_page = ProgressPage(self.browser, self.course_id) self.course_nav = CourseNavPage(self.browser) self.tab_nav = TabNavPage(self.browser) def log_in_as_unique_user(self): """ Log in as a valid lms user. """ AutoAuthPage( self.browser, username="******", email="*****@*****.**", password="******", course_id=self.course_id, ).visit() def test_progress_page_has_view_certificate_button(self): """ Scenario: View Certificate option should be present on Course Progress menu if the user is awarded a certificate. And their should be no padding around the box containing certificate info. (See SOL-1196 for details on this) As a Student Given there is a course with certificate configuration And I have passed the course and certificate is generated When I go on the Progress tab for the course Then I should see a 'View Certificate' button And their should be no padding around Certificate info box. """ self.cert_fixture.install() self.log_in_as_unique_user() self.complete_course_problems() self.course_info_page.visit() self.tab_nav.go_to_tab("Progress") self.assertTrue(self.progress_page.q(css=".auto-cert-message").first.visible) actual_padding = get_element_padding(self.progress_page, ".wrapper-msg.wrapper-auto-cert") actual_padding = [int(padding) for padding in actual_padding.itervalues()] expected_padding = [0, 0, 0, 0] # Verify that their is no padding around the box containing certificate info. self.assertEqual(actual_padding, expected_padding) def complete_course_problems(self): """ Complete Course Problems. Problems were added in the setUp """ self.course_info_page.visit() self.tab_nav.go_to_tab("Course") # Navigate to Test Subsection in Test Section Section self.course_nav.go_to_section("Test Section", "Test Subsection") # Navigate to Test Problem 1 self.course_nav.go_to_vertical("Test Problem 1") # Select correct value for from select menu self.course_nav.q(css='select option[value="{}"]'.format("blue")).first.click() # Select correct radio button for the answer self.course_nav.q(css="fieldset div.field:nth-child(3) input").nth(0).click() # Select correct radio buttons for the answer self.course_nav.q(css="fieldset div.field:nth-child(1) input").nth(1).click() self.course_nav.q(css="fieldset div.field:nth-child(3) input").nth(1).click() # Submit the answer self.course_nav.q(css="button.check.Check").click() self.course_nav.wait_for_ajax() # Navigate to the 'Test Subsection 2' of 'Test Section 2' self.course_nav.go_to_section("Test Section 2", "Test Subsection 2") # Navigate to Test Problem 2 self.course_nav.go_to_vertical("Test Problem 2") # Fill in the answer of the problem self.course_nav.q(css="input[id^=input_][id$=_2_1]").fill("A*x^2 + sqrt(y)") # Submit the answer self.course_nav.q(css="button.check.Check").click() self.course_nav.wait_for_ajax()
class SignUpAndSignInTest(UniqueCourseTest): """ Test studio sign-up and sign-in """ shard = 21 def setUp(self): super(SignUpAndSignInTest, self).setUp() self.sign_up_page = SignupPage(self.browser) self.login_page = LoginPage(self.browser) self.course_outline_page = CourseOutlinePage( self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run']) self.course_outline_sign_in_redirect_page = CourseOutlineSignInRedirectPage( self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run']) self.course_fixture = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'], ) self.user = None def install_course_fixture(self): """ Install a course fixture """ self.course_fixture.install() self.user = self.course_fixture.user def test_sign_up_from_home(self): """ Scenario: Sign up from the homepage Given I visit the Studio homepage When I click the link with the text "Sign Up" And I fill in the registration form And I press the Create My Account button on the registration form Then I should see an email verification prompt """ index_page = IndexPage(self.browser) index_page.visit() index_page.click_sign_up() # Register the user. unique_number = uuid.uuid4().hex[:4] self.sign_up_page.sign_up_user( '{}[email protected]'.format(unique_number), '{}-name'.format(unique_number), '{}-username'.format(unique_number), '{}-password'.format(unique_number), ) home = HomePage(self.browser) home.wait_for_page() def test_sign_up_with_bad_password(self): """ Scenario: Sign up from the homepage Given I visit the Studio homepage When I click the link with the text "Sign Up" And I fill in the registration form When I enter an insufficient password and focus out I should see an error message """ index_page = IndexPage(self.browser) index_page.visit() index_page.click_sign_up() password_input = self.sign_up_page.input_password( 'a') # Arbitrary short password that will fail password_input.send_keys(Keys.TAB) # Focus out of the element index_page.wait_for_element_visibility( '#register-password-validation-error', 'Password Error Message') self.assertIsNotNone( index_page.q(css='#register-password-validation-error-msg') ) # Error message should exist def test_login_with_valid_redirect(self): """ Scenario: Login with a valid redirect Given I have opened a new course in Studio And I am not logged in And I visit the url "/course/slashes:MITx+999+Robot_Super_Course" And I should see the path is "/signin_redirect_to_lms?next=/course/slashes%3AMITx%2B999%2BRobot_Super_Course" When I fill in and submit the LMS login form Then I should see that the path is "/course/slashes:MITx+999+Robot_Super_Course" """ self.install_course_fixture() # Get the url, browser should land here after sign in. course_url = self.course_outline_sign_in_redirect_page.url self.course_outline_sign_in_redirect_page.visit() # Login self.course_outline_sign_in_redirect_page.login( self.user['email'], self.user['password']) self.course_outline_page.wait_for_page() # Verify that correct course is displayed after sign in. self.assertEqual(self.browser.current_url, course_url)
class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest, TestWithSearchIndexMixin): """ Test Library Content block in LMS """ shard = 17 def setUp(self): """ Install library with some content and a course using fixtures """ self._create_search_index() super(StudioLibraryContainerTest, self).setUp() # Also create a course: self.course_fixture = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'] ) self.populate_course_fixture(self.course_fixture) self.course_fixture.install() self.outline = CourseOutlinePage( self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run'] ) self.outline.visit() subsection = self.outline.section(SECTION_NAME).subsection(SUBSECTION_NAME) self.unit_page = subsection.expand_subsection().unit(UNIT_NAME).go_to() def tearDown(self): """ Tear down method: remove search index backing file """ self._cleanup_index_file() super(StudioLibraryContainerTest, self).tearDown() def populate_library_fixture(self, library_fixture): """ Populate the children of the test course fixture. """ library_fixture.add_children( XBlockFixtureDesc("html", "Html1"), XBlockFixtureDesc("html", "Html2"), XBlockFixtureDesc("html", "Html3"), ) def populate_course_fixture(self, course_fixture): """ Install a course with sections/problems, tabs, updates, and handouts """ library_content_metadata = { 'source_library_id': six.text_type(self.library_key), 'mode': 'random', 'max_count': 1, } course_fixture.add_children( XBlockFixtureDesc('chapter', SECTION_NAME).add_children( XBlockFixtureDesc('sequential', SUBSECTION_NAME).add_children( XBlockFixtureDesc('vertical', UNIT_NAME).add_children( XBlockFixtureDesc('library_content', "Library Content", metadata=library_content_metadata) ) ) ) ) def _get_library_xblock_wrapper(self, xblock): """ Wraps xblock into :class:`...pages.studio.library.StudioLibraryContainerXBlockWrapper` """ return StudioLibraryContainerXBlockWrapper.from_xblock_wrapper(xblock) @ddt.data(1, 2, 3) def test_can_edit_metadata(self, max_count): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block And I edit library content metadata and save it Then I can ensure that data is persisted """ library_name = self.library_info['display_name'] library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[1]) library_container.edit() edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator) edit_modal.library_name = library_name edit_modal.count = max_count library_container.save_settings() # saving settings # open edit window again to verify changes are persistent library_container.edit() edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator) self.assertEqual(edit_modal.library_name, library_name) self.assertEqual(edit_modal.count, max_count) def test_no_content_message(self): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block And I set Problem Type selector so that no libraries have matching content Then I can see that "No matching content" warning is shown When I set Problem Type selector so that there is matching content Then I can see that warning messages are not shown """ # Add a single "Dropdown" type problem to the library (which otherwise has only HTML blocks): self.library_fixture.create_xblock(self.library_fixture.library_location, XBlockFixtureDesc( "problem", "Dropdown", data=textwrap.dedent(""" <problem> <p>Dropdown</p> <optionresponse><optioninput label="Dropdown" options="('1', '2')" correct="'2'"></optioninput></optionresponse> </problem> """) )) expected_text = 'There are no matching problem types in the specified libraries. Select another problem type' library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[1]) # precondition check - assert library has children matching filter criteria self.assertFalse(library_container.has_validation_error) self.assertFalse(library_container.has_validation_warning) library_container.edit() edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator) self.assertEqual(edit_modal.capa_type, "Any Type") # precondition check edit_modal.capa_type = "Custom Evaluated Script" library_container.save_settings() self.assertTrue(library_container.has_validation_warning) self.assertIn(expected_text, library_container.validation_warning_text) library_container.edit() edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator) self.assertEqual(edit_modal.capa_type, "Custom Evaluated Script") # precondition check edit_modal.capa_type = "Dropdown" library_container.save_settings() # Library should contain single Dropdown problem, so now there should be no errors again self.assertFalse(library_container.has_validation_error) self.assertFalse(library_container.has_validation_warning) def test_cannot_manage(self): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block And when I click the "View" link Then I can see a preview of the blocks drawn from the library. And I do not see a duplicate button And I do not see a delete button """ block_wrapper_unit_page = self._get_library_xblock_wrapper(self.unit_page.xblocks[0].children[0]) container_page = block_wrapper_unit_page.go_to_container() for block in container_page.xblocks: self.assertFalse(block.has_duplicate_button) self.assertFalse(block.has_delete_button) self.assertFalse(block.has_edit_visibility_button)
def setUp(self): """ Initializes the components (page objects, courses, users) for this test suite """ # Some parameters are provided by the parent setUp() routine, such as the following: # self.course_id, self.course_info, self.unique_id super(BaseLmsDashboardTestMultiple, self).setUp() # Load page objects for use by the tests self.dashboard_page = DashboardPage(self.browser) # Configure some aspects of the test course and install the settings into the course self.courses = { 'A': { 'org': 'test_org', 'number': self.unique_id, 'run': 'test_run_A', 'display_name': 'Test Course A', 'enrollment_mode': 'audit', 'cert_name_long': 'Certificate of Audit Achievement' }, 'B': { 'org': 'test_org', 'number': self.unique_id, 'run': 'test_run_B', 'display_name': 'Test Course B', 'enrollment_mode': 'verified', 'cert_name_long': 'Certificate of Verified Achievement' }, 'C': { 'org': 'test_org', 'number': self.unique_id, 'run': 'test_run_C', 'display_name': 'Test Course C', 'enrollment_mode': 'credit', 'cert_name_long': 'Certificate of Credit Achievement' } } self.username = "******".format(uuid=self.unique_id[0:6]) self.email = "{user}@example.com".format(user=self.username) self.course_keys = {} self.course_fixtures = {} for key, value in self.courses.iteritems(): course_key = generate_course_key( value['org'], value['number'], value['run'], ) course_fixture = CourseFixture( value['org'], value['number'], value['run'], value['display_name'], ) course_fixture.add_advanced_settings({ u"social_sharing_url": {u"value": "http://custom/course/url"}, u"cert_name_long": {u"value": value['cert_name_long']} }) course_fixture.install() self.course_keys[key] = course_key self.course_fixtures[key] = course_fixture # Create the test user, register them for the course, and authenticate AutoAuthPage( self.browser, username=self.username, email=self.email, course_id=course_key, enrollment_mode=value['enrollment_mode'] ).visit() # Navigate the authenticated, enrolled user to the dashboard page and get testing! self.dashboard_page.visit()
class SignUpAndSignInTest(UniqueCourseTest): """ Test studio sign-up and sign-in """ shard = 21 def setUp(self): # pylint: disable=arguments-differ super(SignUpAndSignInTest, self).setUp() self.sign_up_page = SignupPage(self.browser) self.login_page = LoginPage(self.browser) self.course_outline_page = CourseOutlinePage( self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run'] ) self.course_outline_sign_in_redirect_page = CourseOutlineSignInRedirectPage( self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run'] ) self.course_fixture = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'], ) self.user = None def install_course_fixture(self): """ Install a course fixture """ self.course_fixture.install() self.user = self.course_fixture.user def test_sign_up_from_home(self): """ Scenario: Sign up from the homepage Given I visit the Studio homepage When I click the link with the text "Sign Up" And I fill in the registration form And I press the Create My Account button on the registration form Then I should see an email verification prompt """ index_page = IndexPage(self.browser) index_page.visit() index_page.click_sign_up() unique_number = uuid.uuid4().hex[:4] registration_dic = { '#email': '{}[email protected]'.format(unique_number), '#name': '{}-name'.format(unique_number), '#username': '******'.format(unique_number), '#password': '******'.format(unique_number), } # Register the user. self.sign_up_page.sign_up_user(registration_dic) home = HomePage(self.browser) home.wait_for_page() def test_sign_up_with_bad_password(self): """ Scenario: Sign up from the homepage Given I visit the Studio homepage When I click the link with the text "Sign Up" And I fill in the registration form When I enter an insufficient password and focus out I should see an error message """ index_page = IndexPage(self.browser) index_page.visit() index_page.click_sign_up() password_input = self.sign_up_page.input_password('a') # Arbitrary short password that will fail password_input.send_keys(Keys.TAB) # Focus out of the element index_page.wait_for_element_visibility('#password_error', 'Password Error Message') self.assertIsNotNone(index_page.q(css='#password_error').text) # Make sure there is an error message def test_login_with_valid_redirect(self): """ Scenario: Login with a valid redirect Given I have opened a new course in Studio And I am not logged in And I visit the url "/course/slashes:MITx+999+Robot_Super_Course" And I should see that the path is "/signin?next=/course/slashes%3AMITx%2B999%2BRobot_Super_Course" When I fill in and submit the signin form Then I should see that the path is "/course/slashes:MITx+999+Robot_Super_Course" """ self.install_course_fixture() # Get the url, browser should land here after sign in. course_url = self.course_outline_sign_in_redirect_page.url self.course_outline_sign_in_redirect_page.visit() # Login self.course_outline_sign_in_redirect_page.login(self.user['email'], self.user['password']) self.course_outline_page.wait_for_page() # Verify that correct course is displayed after sign in. self.assertEqual(self.browser.current_url, course_url) def test_login_with_invalid_redirect(self): """ Scenario: Login with an invalid redirect Given I have opened a new course in Studio And I am not logged in And I visit the url "/signin?next=http://www.google.com/" When I fill in and submit the signin form Then I should see that the path is "/home/" """ self.install_course_fixture() # Visit course self.course_outline_sign_in_redirect_page.visit() # Change redirect url self.browser.get(self.browser.current_url.split('=')[0] + '=http://www.google.com') # Login self.course_outline_sign_in_redirect_page.login(self.user['email'], self.user['password']) home = HomePage(self.browser) home.wait_for_page() self.assertEqual(self.browser.current_url, home.url) def test_login_with_mistyped_credentials(self): """ Given I have opened a new course in Studio And I am not logged in And I visit the Studio homepage When I click the link with the text "Sign In" Then I should see that the path is "/signin" And I should not see a login error message And I fill in and submit the signin form incorrectly Then I should see a login error message And I edit the password field Then I should not see a login error message And I submit the signin form And I wait for "2" seconds Then I should see that the path is "/course/slashes:MITx+999+Robot_Super_Course" """ self.install_course_fixture() self.course_outline_sign_in_redirect_page.visit() # Verify login_error is not present self.course_outline_sign_in_redirect_page.wait_for_element_absence( '#login_error', 'Login error not be present' ) # Login with wrong credentials self.course_outline_sign_in_redirect_page.login( self.user['email'], 'wrong_password', expect_success=False ) # Verify that login error is shown self.course_outline_sign_in_redirect_page.wait_for_element_visibility( '#login_error', 'Login error is visible' ) # Change the password self.course_outline_sign_in_redirect_page.fill_field('input#password', 'changed_password') # Login error should not be visible self.course_outline_sign_in_redirect_page.wait_for_element_invisibility( '#login_error', 'Login error is not visible' ) # Login with correct credentials self.course_outline_sign_in_redirect_page.login(self.user['email'], self.user['password']) self.course_outline_page.wait_for_page() # Verify that correct course is displayed after sign in. self.assertEqual(self.browser.current_url, self.course_outline_page.url)
class SignUpAndSignInTest(UniqueCourseTest): """ Test studio sign-up and sign-in """ shard = 21 def setUp(self): super(SignUpAndSignInTest, self).setUp() self.sign_up_page = SignupPage(self.browser) self.login_page = LoginPage(self.browser) self.course_outline_page = CourseOutlinePage( self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run'] ) self.course_outline_sign_in_redirect_page = CourseOutlineSignInRedirectPage( self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run'] ) self.course_fixture = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'], ) self.user = None def install_course_fixture(self): """ Install a course fixture """ self.course_fixture.install() self.user = self.course_fixture.user def test_sign_up_from_home(self): """ Scenario: Sign up from the homepage Given I visit the Studio homepage When I click the link with the text "Sign Up" And I fill in the registration form And I press the Create My Account button on the registration form Then I should see an email verification prompt """ index_page = IndexPage(self.browser) index_page.visit() index_page.click_sign_up() # Register the user. unique_number = uuid.uuid4().hex[:4] self.sign_up_page.sign_up_user( '{}[email protected]'.format(unique_number), '{}-name'.format(unique_number), '{}-username'.format(unique_number), '{}-password'.format(unique_number), ) home = HomePage(self.browser) home.wait_for_page() def test_sign_up_with_bad_password(self): """ Scenario: Sign up from the homepage Given I visit the Studio homepage When I click the link with the text "Sign Up" And I fill in the registration form When I enter an insufficient password and focus out I should see an error message """ index_page = IndexPage(self.browser) index_page.visit() index_page.click_sign_up() password_input = self.sign_up_page.input_password('a') # Arbitrary short password that will fail password_input.send_keys(Keys.TAB) # Focus out of the element index_page.wait_for_element_visibility('#register-password-validation-error', 'Password Error Message') self.assertIsNotNone(index_page.q(css='#register-password-validation-error-msg')) # Error message should exist def test_login_with_valid_redirect(self): """ Scenario: Login with a valid redirect Given I have opened a new course in Studio And I am not logged in And I visit the url "/course/slashes:MITx+999+Robot_Super_Course" And I should see that the path is "/signin?next=/course/slashes%3AMITx%2B999%2BRobot_Super_Course" When I fill in and submit the signin form Then I should see that the path is "/course/slashes:MITx+999+Robot_Super_Course" """ self.install_course_fixture() # Get the url, browser should land here after sign in. course_url = self.course_outline_sign_in_redirect_page.url self.course_outline_sign_in_redirect_page.visit() # Login self.course_outline_sign_in_redirect_page.login(self.user['email'], self.user['password']) self.course_outline_page.wait_for_page() # Verify that correct course is displayed after sign in. self.assertEqual(self.browser.current_url, course_url) def test_login_with_invalid_redirect(self): """ Scenario: Login with an invalid redirect Given I have opened a new course in Studio And I am not logged in And I visit the url "/signin?next=http://www.google.com/" When I fill in and submit the signin form Then I should see that the path is "/home/" """ self.install_course_fixture() # Visit course self.course_outline_sign_in_redirect_page.visit() # Change redirect url self.browser.get(self.browser.current_url.split('=')[0] + '=http://www.google.com') # Login self.course_outline_sign_in_redirect_page.login(self.user['email'], self.user['password']) # Verify that we land in LMS instead of the invalid redirect url self.assertEqual(self.browser.current_url, LMS_URL + "/dashboard") def test_login_with_mistyped_credentials(self): """ Given I have opened a new course in Studio And I am not logged in And I visit the Studio homepage When I click the link with the text "Sign In" Then I should see that the path is "/signin" And I should not see a login error message And I fill in and submit the signin form incorrectly Then I should see a login error message And I edit the password field Then I should not see a login error message And I submit the signin form And I wait for "2" seconds Then I should see that the path is "/course/slashes:MITx+999+Robot_Super_Course" """ self.install_course_fixture() self.course_outline_sign_in_redirect_page.visit() # Verify login_error is not present self.course_outline_sign_in_redirect_page.wait_for_element_absence( '#login_error', 'Login error not be present' ) # Login with wrong credentials self.course_outline_sign_in_redirect_page.login( self.user['email'], 'wrong_password', expect_success=False ) # Verify that login error is shown self.course_outline_sign_in_redirect_page.wait_for_element_visibility( ".js-form-errors.status.submission-error", 'Login error is visible' ) # Login with correct credentials self.course_outline_sign_in_redirect_page.login(self.user['email'], self.user['password']) self.course_outline_page.wait_for_page() # Verify that correct course is displayed after sign in. self.assertEqual(self.browser.current_url, self.course_outline_page.url)
class CertificateProgressPageTest(UniqueCourseTest): """ Tests for verifying Certificate info on Progress tab of course page. """ def setUp(self): super(CertificateProgressPageTest, self).setUp() # set same course number as we have in fixture json self.course_info['number'] = "3355358979513794782079645765720179311111" test_certificate_config = { 'id': 1, 'name': 'Certificate name', 'description': 'Certificate description', 'course_title': 'Course title override', 'signatories': [], 'version': 1, 'is_active': True } course_settings = {'certificates': test_certificate_config} self.course_fixture = CourseFixture( self.course_info["org"], self.course_info["number"], self.course_info["run"], self.course_info["display_name"], settings=course_settings ) self.course_fixture.add_advanced_settings({ "cert_html_view_enabled": {"value": "true"}, "certificates_show_before_end": {"value": "true"} }) self.course_fixture.add_update( CourseUpdateDesc(date='January 29, 2014', content='Test course update1') ) self.course_fixture.add_children( XBlockFixtureDesc('static_tab', 'Test Static Tab'), XBlockFixtureDesc('chapter', 'Test Section').add_children( XBlockFixtureDesc('sequential', 'Test Subsection', grader_type='Final Exam').add_children( XBlockFixtureDesc('problem', 'Test Problem 1', data=load_data_str('multiple_choice.xml')), XBlockFixtureDesc('html', 'Test HTML'), ) ), XBlockFixtureDesc('chapter', 'Test Section 2').add_children( XBlockFixtureDesc('sequential', 'Test Subsection 2', grader_type='Midterm Exam').add_children( XBlockFixtureDesc('problem', 'Test Problem 2', data=load_data_str('formula_problem.xml')), ) ) ) self.course_fixture.install() self.user_id = "99" # we have created a user with this id in fixture self.cert_fixture = CertificateConfigFixture(self.course_id, test_certificate_config) self.progress_page = ProgressPage(self.browser, self.course_id) self.courseware_page = CoursewarePage(self.browser, self.course_id) self.course_home_page = CourseHomePage(self.browser, self.course_id) self.tab_nav = TabNavPage(self.browser) def log_in_as_unique_user(self): """ Log in as a valid lms user. """ AutoAuthPage( self.browser, username="******", email="*****@*****.**", password="******", course_id=self.course_id ).visit() def test_progress_page_has_view_certificate_button(self): """ Scenario: View Certificate option should be present on Course Progress menu if the user is awarded a certificate. And there should be no padding around the box containing certificate info. (See SOL-1196 for details on this) As a Student Given there is a course with certificate configuration And I have passed the course and certificate is generated When I go on the Progress tab for the course Then I should see a 'View Certificate' button And their should be no padding around Certificate info box. """ self.cert_fixture.install() self.log_in_as_unique_user() self.complete_course_problems() self.course_home_page.visit() self.tab_nav.go_to_tab('Progress') self.assertTrue(self.progress_page.q(css='.auto-cert-message').first.visible) actual_padding = get_element_padding(self.progress_page, '.wrapper-msg.wrapper-auto-cert') actual_padding = [int(padding) for padding in actual_padding.itervalues()] expected_padding = [0, 0, 0, 0] # Verify that their is no padding around the box containing certificate info. self.assertEqual(actual_padding, expected_padding) def complete_course_problems(self): """ Complete Course Problems. Problems were added in the setUp """ self.course_home_page.visit() # Navigate to Test Subsection in Test Section Section self.course_home_page.outline.go_to_section('Test Section', 'Test Subsection') # Navigate to Test Problem 1 self.courseware_page.nav.go_to_vertical('Test Problem 1') # Select correct value for from select menu self.courseware_page.q(css='select option[value="{}"]'.format('blue')).first.click() # Select correct radio button for the answer self.courseware_page.q(css='fieldset div.field:nth-child(4) input').nth(0).click() # Select correct radio buttons for the answer self.courseware_page.q(css='fieldset div.field:nth-child(2) input').nth(1).click() self.courseware_page.q(css='fieldset div.field:nth-child(4) input').nth(1).click() # Submit the answer self.courseware_page.q(css='button.submit').click() self.courseware_page.wait_for_ajax() # Navigate to the 'Test Subsection 2' of 'Test Section 2' self.course_home_page.visit() self.course_home_page.outline.go_to_section('Test Section 2', 'Test Subsection 2') # Navigate to Test Problem 2 self.courseware_page.nav.go_to_vertical('Test Problem 2') # Fill in the answer of the problem self.courseware_page.q(css='input[id^=input_][id$=_2_1]').fill('A*x^2 + sqrt(y)') # Submit the answer self.courseware_page.q(css='button.submit').click() self.courseware_page.wait_for_ajax()
class StudioCourseTest(UniqueCourseTest): """ Base class for all Studio course tests. """ def setUp(self, is_staff=False, test_xss=True): # pylint: disable=arguments-differ """ Install a course with no content using a fixture. """ super(StudioCourseTest, self).setUp() self.test_xss = test_xss self.install_course_fixture(is_staff) def install_course_fixture(self, is_staff=False): """ Install a course fixture """ self.course_fixture = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'], ) if self.test_xss: xss_injected_unique_id = XSS_INJECTION + self.unique_id test_improper_escaping = {u"value": xss_injected_unique_id} self.course_fixture.add_advanced_settings({ "advertised_start": test_improper_escaping, "info_sidebar_name": test_improper_escaping, "cert_name_short": test_improper_escaping, "cert_name_long": test_improper_escaping, "display_organization": test_improper_escaping, "display_coursenumber": test_improper_escaping, }) self.course_info['display_organization'] = xss_injected_unique_id self.course_info['display_coursenumber'] = xss_injected_unique_id self.populate_course_fixture(self.course_fixture) self.course_fixture.install() self.user = self.course_fixture.user self.log_in(self.user, is_staff) def populate_course_fixture(self, course_fixture): """ Populate the children of the test course fixture. """ pass def log_in(self, user, is_staff=False): """ Log in as the user that created the course. The user will be given instructor access to the course and enrolled in it. By default the user will not have staff access unless is_staff is passed as True. Args: user(dict): dictionary containing user data: {'username': ..., 'email': ..., 'password': ...} is_staff(bool): register this user as staff """ self.auth_page = AutoAuthPage( self.browser, staff=is_staff, username=user.get('username'), email=user.get('email'), password=user.get('password') ) self.auth_page.visit()
class CertificateWebViewTest(EventsTestMixin, UniqueCourseTest): """ Tests for verifying certificate web view features """ shard = 5 def setUp(self): super(CertificateWebViewTest, self).setUp() # set same course number as we have in fixture json self.course_info['number'] = "335535897951379478207964576572017930000" test_certificate_config = { 'id': 1, 'name': 'Certificate name', 'description': 'Certificate description', 'course_title': 'Course title override', 'signatories': [], 'version': 1, 'is_active': True, } course_settings = {'certificates': test_certificate_config} self.course_fixture = CourseFixture( self.course_info["org"], self.course_info["number"], self.course_info["run"], self.course_info["display_name"], settings=course_settings ) self.course_fixture.add_advanced_settings({ "cert_html_view_enabled": {"value": "true"}, "certificates_display_behavior": {"value": "early_with_info"}, }) self.course_fixture.install() self.user_id = "99" # we have created a user with this id in fixture self.cert_fixture = CertificateConfigFixture(self.course_id, test_certificate_config) # Load certificate web view page for use by the tests self.certificate_page = CertificatePage(self.browser, self.user_id, self.course_id) def log_in_as_unique_user(self): """ Log in as a valid lms user. """ AutoAuthPage( self.browser, username="******", email="*****@*****.**", password="******", course_id=self.course_id ).visit() def test_page_has_accomplishments_banner(self): """ Scenario: User accomplishment banner should be present if logged in user is the one who is awarded the certificate Given there is a course with certificate configuration And I have passed the course and certificate is generated When I view the certificate web view page Then I should see the accomplishment banner. banner should have linked-in and facebook share buttons And When I click on `Add to Profile` button `edx.certificate.shared` event should be emitted """ self.cert_fixture.install() self.log_in_as_unique_user() self.certificate_page.visit() self.assertTrue(self.certificate_page.accomplishment_banner.visible) self.assertTrue(self.certificate_page.add_to_linkedin_profile_button.visible) self.assertTrue(self.certificate_page.add_to_facebook_profile_button.visible) self.certificate_page.add_to_linkedin_profile_button.click() actual_events = self.wait_for_events( event_filter={'event_type': 'edx.certificate.shared'}, number_of_matches=1 ) expected_events = [ { 'event': { 'user_id': self.user_id, 'course_id': self.course_id } } ] self.assert_events_match(expected_events, actual_events)
class SignUpAndSignInTest(UniqueCourseTest): """ Test studio sign-up and sign-in """ def setUp(self): # pylint: disable=arguments-differ super(SignUpAndSignInTest, self).setUp() self.sign_up_page = SignupPage(self.browser) self.login_page = LoginPage(self.browser) self.course_outline_page = CourseOutlinePage( self.browser, self.course_info["org"], self.course_info["number"], self.course_info["run"] ) self.course_outline_sign_in_redirect_page = CourseOutlineSignInRedirectPage( self.browser, self.course_info["org"], self.course_info["number"], self.course_info["run"] ) self.course_fixture = CourseFixture( self.course_info["org"], self.course_info["number"], self.course_info["run"], self.course_info["display_name"], ) self.user = None def install_course_fixture(self): """ Install a course fixture """ self.course_fixture.install() self.user = self.course_fixture.user def test_sign_up_from_home(self): """ Scenario: Sign up from the homepage Given I visit the Studio homepage When I click the link with the text "Sign Up" And I fill in the registration form And I press the Create My Account button on the registration form Then I should see an email verification prompt """ index_page = IndexPage(self.browser) index_page.visit() index_page.click_sign_up() unique_number = uuid.uuid4().hex[:4] registration_dic = { "#email": "{}[email protected]".format(unique_number), "#name": "{}-name".format(unique_number), "#username": "******".format(unique_number), "#password": "******".format(unique_number), } # Register the user. self.sign_up_page.sign_up_user(registration_dic) home = HomePage(self.browser) home.wait_for_page() def test_login_with_valid_redirect(self): """ Scenario: Login with a valid redirect Given I have opened a new course in Studio And I am not logged in And I visit the url "/course/slashes:MITx+999+Robot_Super_Course" And I should see that the path is "/signin?next=/course/slashes%3AMITx%2B999%2BRobot_Super_Course" When I fill in and submit the signin form Then I should see that the path is "/course/slashes:MITx+999+Robot_Super_Course" """ self.install_course_fixture() # Get the url, browser should land here after sign in. course_url = self.course_outline_sign_in_redirect_page.url self.course_outline_sign_in_redirect_page.visit() # Login self.course_outline_sign_in_redirect_page.login(self.user["email"], self.user["password"]) self.course_outline_page.wait_for_page() # Verify that correct course is displayed after sign in. self.assertEqual(self.browser.current_url, course_url) def test_login_with_invalid_redirect(self): """ Scenario: Login with an invalid redirect Given I have opened a new course in Studio And I am not logged in And I visit the url "/signin?next=http://www.google.com/" When I fill in and submit the signin form Then I should see that the path is "/home/" """ self.install_course_fixture() # Visit course self.course_outline_sign_in_redirect_page.visit() # Change redirect url self.browser.get(self.browser.current_url.split("=")[0] + "=http://www.google.com") # Login self.course_outline_sign_in_redirect_page.login(self.user["email"], self.user["password"]) home = HomePage(self.browser) home.wait_for_page() self.assertEqual(self.browser.current_url, home.url) def test_login_with_mistyped_credentials(self): """ Given I have opened a new course in Studio And I am not logged in And I visit the Studio homepage When I click the link with the text "Sign In" Then I should see that the path is "/signin" And I should not see a login error message And I fill in and submit the signin form incorrectly Then I should see a login error message And I edit the password field Then I should not see a login error message And I submit the signin form And I wait for "2" seconds Then I should see that the path is "/course/slashes:MITx+999+Robot_Super_Course" """ self.install_course_fixture() self.course_outline_sign_in_redirect_page.visit() # Verify login_error is not present self.course_outline_sign_in_redirect_page.wait_for_element_absence("#login_error", "Login error not be present") # Login with wrong credentials self.course_outline_sign_in_redirect_page.login(self.user["email"], "wrong_password", expect_success=False) # Verify that login error is shown self.course_outline_sign_in_redirect_page.wait_for_element_visibility("#login_error", "Login error is visible") # Change the password self.course_outline_sign_in_redirect_page.fill_field("input#password", "changed_password") # Login error should not be visible self.course_outline_sign_in_redirect_page.wait_for_element_invisibility( "#login_error", "Login error is not visible" ) # Login with correct credentials self.course_outline_sign_in_redirect_page.login(self.user["email"], self.user["password"]) self.course_outline_page.wait_for_page() # Verify that correct course is displayed after sign in. self.assertEqual(self.browser.current_url, self.course_outline_page.url)
class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest, TestWithSearchIndexMixin): """ Test Library Content block in LMS """ def setUp(self): """ Install library with some content and a course using fixtures """ self._create_search_index() super(StudioLibraryContainerTest, self).setUp() # Also create a course: self.course_fixture = CourseFixture(self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name']) self.populate_course_fixture(self.course_fixture) self.course_fixture.install() self.outline = CourseOutlinePage(self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run']) self.outline.visit() subsection = self.outline.section(SECTION_NAME).subsection( SUBSECTION_NAME) self.unit_page = subsection.expand_subsection().unit(UNIT_NAME).go_to() def tearDown(self): """ Tear down method: remove search index backing file """ self._cleanup_index_file() super(StudioLibraryContainerTest, self).tearDown() def populate_library_fixture(self, library_fixture): """ Populate the children of the test course fixture. """ library_fixture.add_children( XBlockFixtureDesc("html", "Html1"), XBlockFixtureDesc("html", "Html2"), XBlockFixtureDesc("html", "Html3"), ) def populate_course_fixture(self, course_fixture): """ Install a course with sections/problems, tabs, updates, and handouts """ library_content_metadata = { 'source_library_id': unicode(self.library_key), 'mode': 'random', 'max_count': 1, 'has_score': False } course_fixture.add_children( XBlockFixtureDesc('chapter', SECTION_NAME).add_children( XBlockFixtureDesc('sequential', SUBSECTION_NAME).add_children( XBlockFixtureDesc('vertical', UNIT_NAME).add_children( XBlockFixtureDesc( 'library_content', "Library Content", metadata=library_content_metadata))))) def _get_library_xblock_wrapper(self, xblock): """ Wraps xblock into :class:`...pages.studio.library.StudioLibraryContainerXBlockWrapper` """ return StudioLibraryContainerXBlockWrapper.from_xblock_wrapper(xblock) @ddt.data( (1, True), (2, False), (3, True), ) @ddt.unpack def test_can_edit_metadata(self, max_count, scored): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block And I edit library content metadata and save it Then I can ensure that data is persisted """ library_name = self.library_info['display_name'] library_container = self._get_library_xblock_wrapper( self.unit_page.xblocks[1]) library_container.edit() edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator) edit_modal.library_name = library_name edit_modal.count = max_count edit_modal.scored = scored library_container.save_settings() # saving settings # open edit window again to verify changes are persistent library_container.edit() edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator) self.assertEqual(edit_modal.library_name, library_name) self.assertEqual(edit_modal.count, max_count) self.assertEqual(edit_modal.scored, scored) def test_no_library_shows_library_not_configured(self): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block And I edit to select "No Library" Then I can see that library content block is misconfigured """ expected_text = 'A library has not yet been selected.' expected_action = 'Select a Library' library_container = self._get_library_xblock_wrapper( self.unit_page.xblocks[1]) # precondition check - the library block should be configured before we remove the library setting self.assertFalse( library_container.has_validation_not_configured_warning) library_container.edit() edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator) edit_modal.library_name = "No Library Selected" library_container.save_settings() self.assertTrue( library_container.has_validation_not_configured_warning) self.assertIn(expected_text, library_container.validation_not_configured_warning_text) self.assertIn(expected_action, library_container.validation_not_configured_warning_text) def test_out_of_date_message(self): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block Then I update the library being used Then I refresh the page Then I can see that library content block needs to be updated When I click on the update link Then I can see that the content no longer needs to be updated """ # Formerly flaky: see TE-745 expected_text = "This component is out of date. The library has new content." library_block = self._get_library_xblock_wrapper( self.unit_page.xblocks[1]) self.assertFalse(library_block.has_validation_warning) # Removed this assert until a summary message is added back to the author view (SOL-192) #self.assertIn("3 matching components", library_block.author_content) self.library_fixture.create_xblock( self.library_fixture.library_location, XBlockFixtureDesc("html", "Html4")) self.unit_page.visit() # Reload the page self.assertTrue(library_block.has_validation_warning) self.assertIn(expected_text, library_block.validation_warning_text) library_block.refresh_children() self.unit_page.wait_for_page() # Wait for the page to reload library_block = self._get_library_xblock_wrapper( self.unit_page.xblocks[1]) self.assertFalse(library_block.has_validation_message) # Removed this assert until a summary message is added back to the author view (SOL-192) #self.assertIn("4 matching components", library_block.author_content) def test_no_content_message(self): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block And I set Problem Type selector so that no libraries have matching content Then I can see that "No matching content" warning is shown When I set Problem Type selector so that there is matching content Then I can see that warning messages are not shown """ # Add a single "Dropdown" type problem to the library (which otherwise has only HTML blocks): self.library_fixture.create_xblock( self.library_fixture.library_location, XBlockFixtureDesc("problem", "Dropdown", data=textwrap.dedent(""" <problem> <p>Dropdown</p> <optionresponse><optioninput label="Dropdown" options="('1', '2')" correct="'2'"></optioninput></optionresponse> </problem> """))) expected_text = 'There are no matching problem types in the specified libraries. Select another problem type' library_container = self._get_library_xblock_wrapper( self.unit_page.xblocks[1]) # precondition check - assert library has children matching filter criteria self.assertFalse(library_container.has_validation_error) self.assertFalse(library_container.has_validation_warning) library_container.edit() edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator) self.assertEqual(edit_modal.capa_type, "Any Type") # precondition check edit_modal.capa_type = "Custom Evaluated Script" library_container.save_settings() self.assertTrue(library_container.has_validation_warning) self.assertIn(expected_text, library_container.validation_warning_text) library_container.edit() edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator) self.assertEqual(edit_modal.capa_type, "Custom Evaluated Script") # precondition check edit_modal.capa_type = "Dropdown" library_container.save_settings() # Library should contain single Dropdown problem, so now there should be no errors again self.assertFalse(library_container.has_validation_error) self.assertFalse(library_container.has_validation_warning) def test_not_enough_children_blocks(self): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block And I set Problem Type selector so "Any" Then I can see that "No matching content" warning is shown """ expected_tpl = "The specified library is configured to fetch {count} problems, " \ "but there are only {actual} matching problems." library_container = self._get_library_xblock_wrapper( self.unit_page.xblocks[1]) # precondition check - assert block is configured fine self.assertFalse(library_container.has_validation_error) self.assertFalse(library_container.has_validation_warning) library_container.edit() edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator) edit_modal.count = 50 library_container.save_settings() self.assertTrue(library_container.has_validation_warning) self.assertIn( expected_tpl.format(count=50, actual=len(self.library_fixture.children)), library_container.validation_warning_text) def test_settings_overrides(self): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block And when I click the "View" link Then I can see a preview of the blocks drawn from the library. When I edit one of the blocks to change a setting such as "display_name", Then I can see the new setting is overriding the library version. When I subsequently click to refresh the content with the latest from the library, Then I can see that the overrided version of the setting is preserved. When I click to edit the block and reset the setting, then I can see that the setting's field defaults back to the library version. """ block_wrapper_unit_page = self._get_library_xblock_wrapper( self.unit_page.xblocks[0].children[0]) container_page = block_wrapper_unit_page.go_to_container() library_block = self._get_library_xblock_wrapper( container_page.xblocks[0]) self.assertFalse(library_block.has_validation_message) self.assertEqual(len(library_block.children), 3) block = library_block.children[0] self.assertIn(block.name, ("Html1", "Html2", "Html3")) name_default = block.name block.edit() new_display_name = "A new name for this HTML block" block.set_field_val("Display Name", new_display_name) block.save_settings() self.assertEqual(block.name, new_display_name) # Create a new block, causing a new library version: self.library_fixture.create_xblock( self.library_fixture.library_location, XBlockFixtureDesc("html", "Html4")) container_page.visit() # Reload self.assertTrue(library_block.has_validation_warning) library_block.refresh_children() container_page.wait_for_page() # Wait for the page to reload self.assertEqual(len(library_block.children), 4) self.assertEqual(block.name, new_display_name) # Reset: block.edit() block.reset_field_val("Display Name") block.save_settings() self.assertEqual(block.name, name_default) def test_cannot_manage(self): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block And when I click the "View" link Then I can see a preview of the blocks drawn from the library. And I do not see a duplicate button And I do not see a delete button """ block_wrapper_unit_page = self._get_library_xblock_wrapper( self.unit_page.xblocks[0].children[0]) container_page = block_wrapper_unit_page.go_to_container() for block in container_page.xblocks: self.assertFalse(block.has_duplicate_button) self.assertFalse(block.has_delete_button) self.assertFalse(block.has_edit_visibility_button)
def setUp(self): """ Initializes the components (page objects, courses, users) for this test suite """ # Some parameters are provided by the parent setUp() routine, such as the following: # self.course_id, self.course_info, self.unique_id super(BaseLmsDashboardTestMultiple, self).setUp() # Load page objects for use by the tests self.dashboard_page = DashboardPage(self.browser) # Configure some aspects of the test course and install the settings into the course self.courses = { 'A': { 'org': 'test_org', 'number': self.unique_id, 'run': 'test_run_A', 'display_name': 'Test Course A', 'enrollment_mode': 'audit', 'cert_name_long': 'Certificate of Audit Achievement' }, 'B': { 'org': 'test_org', 'number': self.unique_id, 'run': 'test_run_B', 'display_name': 'Test Course B', 'enrollment_mode': 'verified', 'cert_name_long': 'Certificate of Verified Achievement' }, 'C': { 'org': 'test_org', 'number': self.unique_id, 'run': 'test_run_C', 'display_name': 'Test Course C', 'enrollment_mode': 'credit', 'cert_name_long': 'Certificate of Credit Achievement' } } self.username = "******".format(uuid=self.unique_id[0:6]) self.email = "{user}@example.com".format(user=self.username) self.course_keys = {} self.course_fixtures = {} for key, value in self.courses.iteritems(): course_key = generate_course_key( value['org'], value['number'], value['run'], ) course_fixture = CourseFixture( value['org'], value['number'], value['run'], value['display_name'], ) course_fixture.add_advanced_settings({ u"social_sharing_url": { u"value": "http://custom/course/url" }, u"cert_name_long": { u"value": value['cert_name_long'] } }) course_fixture.install() self.course_keys[key] = course_key self.course_fixtures[key] = course_fixture # Create the test user, register them for the course, and authenticate AutoAuthPage(self.browser, username=self.username, email=self.email, course_id=course_key, enrollment_mode=value['enrollment_mode']).visit() # Navigate the authenticated, enrolled user to the dashboard page and get testing! self.dashboard_page.visit()
def install_course_fixture(self, block_type='problem'): """ Install a course fixture """ course_fixture = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'], ) vertical = XBlockFixtureDesc('vertical', 'Test Unit') # populate the course fixture with the right conditional modules course_fixture.add_children( XBlockFixtureDesc('chapter', 'Test Section').add_children( XBlockFixtureDesc('sequential', 'Test Subsection').add_children( vertical ) ) ) course_fixture.install() # Construct conditional block source_block = None conditional_attr = None conditional_value = None if block_type == 'problem': problem_factory = StringResponseXMLFactory() problem_xml = problem_factory.build_xml( question_text='The answer is "correct string"', case_sensitive=False, answer='correct string', ), problem = XBlockFixtureDesc('problem', 'Test Problem', data=problem_xml[0]) source_block = problem conditional_attr = 'attempted' conditional_value = 'True' elif block_type == 'poll': poll = XBlockFixtureDesc( 'poll_question', 'Conditional Poll', question='Is this a good poll?', answers=[ {'id': 'yes', 'text': POLL_ANSWER}, {'id': 'no', 'text': 'Of course not!'} ], ) conditional_attr = 'poll_answer' conditional_value = 'yes' source_block = poll else: raise NotImplementedError() course_fixture.create_xblock(vertical.locator, source_block) # create conditional conditional = XBlockFixtureDesc( 'conditional', 'Test Conditional', sources_list=[source_block.locator], conditional_attr=conditional_attr, conditional_value=conditional_value ) result_block = XBlockFixtureDesc( 'html', 'Conditional Contents', data='<html><div class="hidden-contents">Hidden Contents</p></html>' ) course_fixture.create_xblock(vertical.locator, conditional) course_fixture.create_xblock(conditional.locator, result_block)
class SignUpAndSignInTest(UniqueCourseTest): """ Test studio sign-up and sign-in """ def setUp(self): # pylint: disable=arguments-differ super(SignUpAndSignInTest, self).setUp() self.sign_up_page = SignupPage(self.browser) self.login_page = LoginPage(self.browser) self.course_outline_page = CourseOutlinePage( self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run']) self.course_outline_sign_in_redirect_page = CourseOutlineSignInRedirectPage( self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run']) self.course_fixture = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'], ) self.user = None def install_course_fixture(self): """ Install a course fixture """ self.course_fixture.install() self.user = self.course_fixture.user def test_sign_up_from_home(self): """ Scenario: Sign up from the homepage Given I visit the Studio homepage When I click the link with the text "Sign Up" And I fill in the registration form And I press the Create My Account button on the registration form Then I should see an email verification prompt """ index_page = IndexPage(self.browser) index_page.visit() index_page.click_sign_up() unique_number = uuid.uuid4().hex[:4] registration_dic = { '#email': '{}[email protected]'.format(unique_number), '#name': '{}-name'.format(unique_number), '#username': '******'.format(unique_number), '#password': '******'.format(unique_number), } # Register the user. self.sign_up_page.sign_up_user(registration_dic) home = HomePage(self.browser) home.wait_for_page() def test_login_with_valid_redirect(self): """ Scenario: Login with a valid redirect Given I have opened a new course in Studio And I am not logged in And I visit the url "/course/slashes:MITx+999+Robot_Super_Course" And I should see that the path is "/signin?next=/course/slashes%3AMITx%2B999%2BRobot_Super_Course" When I fill in and submit the signin form Then I should see that the path is "/course/slashes:MITx+999+Robot_Super_Course" """ self.install_course_fixture() # Get the url, browser should land here after sign in. course_url = self.course_outline_sign_in_redirect_page.url self.course_outline_sign_in_redirect_page.visit() # Login self.course_outline_sign_in_redirect_page.login( self.user['email'], self.user['password']) self.course_outline_page.wait_for_page() # Verify that correct course is displayed after sign in. self.assertEqual(self.browser.current_url, course_url) def test_login_with_invalid_redirect(self): """ Scenario: Login with an invalid redirect Given I have opened a new course in Studio And I am not logged in And I visit the url "/signin?next=http://www.google.com/" When I fill in and submit the signin form Then I should see that the path is "/home/" """ self.install_course_fixture() # Visit course self.course_outline_sign_in_redirect_page.visit() # Change redirect url self.browser.get( self.browser.current_url.split('=')[0] + '=http://www.google.com') # Login self.course_outline_sign_in_redirect_page.login( self.user['email'], self.user['password']) home = HomePage(self.browser) home.wait_for_page() self.assertEqual(self.browser.current_url, home.url) def test_login_with_mistyped_credentials(self): """ Given I have opened a new course in Studio And I am not logged in And I visit the Studio homepage When I click the link with the text "Sign In" Then I should see that the path is "/signin" And I should not see a login error message And I fill in and submit the signin form incorrectly Then I should see a login error message And I edit the password field Then I should not see a login error message And I submit the signin form And I wait for "2" seconds Then I should see that the path is "/course/slashes:MITx+999+Robot_Super_Course" """ self.install_course_fixture() self.course_outline_sign_in_redirect_page.visit() # Verify login_error is not present self.course_outline_sign_in_redirect_page.wait_for_element_absence( '#login_error', 'Login error not be present') # Login with wrong credentials self.course_outline_sign_in_redirect_page.login(self.user['email'], 'wrong_password', expect_success=False) # Verify that login error is shown self.course_outline_sign_in_redirect_page.wait_for_element_visibility( '#login_error', 'Login error is visible') # Change the password self.course_outline_sign_in_redirect_page.fill_field( 'input#password', 'changed_password') # Login error should not be visible self.course_outline_sign_in_redirect_page.wait_for_element_invisibility( '#login_error', 'Login error is not visible') # Login with correct credentials self.course_outline_sign_in_redirect_page.login( self.user['email'], self.user['password']) self.course_outline_page.wait_for_page() # Verify that correct course is displayed after sign in. self.assertEqual(self.browser.current_url, self.course_outline_page.url)
def install_course_fixture(self, block_type='problem'): """ Install a course fixture """ course_fixture = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'], ) vertical = XBlockFixtureDesc('vertical', 'Test Unit') # populate the course fixture with the right conditional modules course_fixture.add_children( XBlockFixtureDesc('chapter', 'Test Section').add_children( XBlockFixtureDesc('sequential', 'Test Subsection').add_children(vertical))) course_fixture.install() # Construct conditional block source_block = None conditional_attr = None conditional_value = None if block_type == 'problem': problem_factory = StringResponseXMLFactory() problem_xml = problem_factory.build_xml( question_text='The answer is "correct string"', case_sensitive=False, answer='correct string', ), problem = XBlockFixtureDesc('problem', 'Test Problem', data=problem_xml[0]) source_block = problem conditional_attr = 'attempted' conditional_value = 'True' elif block_type == 'poll': poll = XBlockFixtureDesc( 'poll_question', 'Conditional Poll', question='Is this a good poll?', answers=[{ 'id': 'yes', 'text': POLL_ANSWER }, { 'id': 'no', 'text': 'Of course not!' }], ) conditional_attr = 'poll_answer' conditional_value = 'yes' source_block = poll else: raise NotImplementedError() course_fixture.create_xblock(vertical.locator, source_block) # create conditional conditional = XBlockFixtureDesc('conditional', 'Test Conditional', sources_list=[source_block.locator], conditional_attr=conditional_attr, conditional_value=conditional_value) result_block = XBlockFixtureDesc( 'html', 'Conditional Contents', data='<html><div class="hidden-contents">Hidden Contents</p></html>' ) course_fixture.create_xblock(vertical.locator, conditional) course_fixture.create_xblock(conditional.locator, result_block)
class VideoBaseTest(UniqueCourseTest): """ Base class for tests of the Video Player Sets up the course and provides helper functions for the Video tests. """ def setUp(self): """ Initialization of pages and course fixture for video tests """ super(VideoBaseTest, self).setUp() self.longMessage = True self.video = VideoPage(self.browser) self.tab_nav = TabNavPage(self.browser) self.courseware_page = CoursewarePage(self.browser, self.course_id) self.course_info_page = CourseInfoPage(self.browser, self.course_id) self.auth_page = AutoAuthPage(self.browser, course_id=self.course_id) self.course_fixture = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'] ) self.metadata = None self.assets = [] self.contents_of_verticals = None self.youtube_configuration = {} self.user_info = {} # reset youtube stub server self.addCleanup(YouTubeStubConfig.reset) def navigate_to_video(self): """ Prepare the course and get to the video and render it """ self._install_course_fixture() self._navigate_to_courseware_video_and_render() def navigate_to_video_no_render(self): """ Prepare the course and get to the video unit however do not wait for it to render, because the has been an error. """ self._install_course_fixture() self._navigate_to_courseware_video_no_render() def _install_course_fixture(self): """ Install the course fixture that has been defined """ if self.assets: self.course_fixture.add_asset(self.assets) chapter_sequential = XBlockFixtureDesc('sequential', 'Test Section') chapter_sequential.add_children(*self._add_course_verticals()) chapter = XBlockFixtureDesc('chapter', 'Test Chapter').add_children(chapter_sequential) self.course_fixture.add_children(chapter) self.course_fixture.install() if len(self.youtube_configuration) > 0: YouTubeStubConfig.configure(self.youtube_configuration) def _add_course_verticals(self): """ Create XBlockFixtureDesc verticals :return: a list of XBlockFixtureDesc """ xblock_verticals = [] _contents_of_verticals = self.contents_of_verticals # Video tests require at least one vertical with a single video. if not _contents_of_verticals: _contents_of_verticals = [[{'display_name': 'Video', 'metadata': self.metadata}]] for vertical_index, vertical in enumerate(_contents_of_verticals): xblock_verticals.append(self._create_single_vertical(vertical, vertical_index)) return xblock_verticals def _create_single_vertical(self, vertical_contents, vertical_index): """ Create a single course vertical of type XBlockFixtureDesc with category `vertical`. A single course vertical can contain single or multiple video modules. :param vertical_contents: a list of items for the vertical to contain :param vertical_index: index for the vertical display name :return: XBlockFixtureDesc """ xblock_course_vertical = XBlockFixtureDesc('vertical', u'Test Vertical-{0}'.format(vertical_index)) for video in vertical_contents: xblock_course_vertical.add_children( XBlockFixtureDesc('video', video['display_name'], metadata=video.get('metadata'))) return xblock_course_vertical def _navigate_to_courseware_video(self): """ Register for the course and navigate to the video unit """ self.auth_page.visit() self.user_info = self.auth_page.user_info self.courseware_page.visit() def _navigate_to_courseware_video_and_render(self): """ Wait for the video player to render """ self._navigate_to_courseware_video() self.video.wait_for_video_player_render() def _navigate_to_courseware_video_no_render(self): """ Wait for the video Xmodule but not for rendering """ self._navigate_to_courseware_video() self.video.wait_for_video_class() def metadata_for_mode(self, player_mode, additional_data=None): """ Create a dictionary for video player configuration according to `player_mode` :param player_mode (str): Video player mode :param additional_data (dict): Optional additional metadata. :return: dict """ metadata = {} youtube_ids = { 'youtube_id_1_0': '', 'youtube_id_0_75': '', 'youtube_id_1_25': '', 'youtube_id_1_5': '', } if player_mode == 'html5': metadata.update(youtube_ids) metadata.update({ 'html5_sources': HTML5_SOURCES }) if player_mode == 'youtube_html5': metadata.update({ 'html5_sources': HTML5_SOURCES, }) if player_mode == 'youtube_html5_unsupported_video': metadata.update({ 'html5_sources': HTML5_SOURCES_INCORRECT }) if player_mode == 'html5_unsupported_video': metadata.update(youtube_ids) metadata.update({ 'html5_sources': HTML5_SOURCES_INCORRECT }) if player_mode == 'hls': metadata.update(youtube_ids) metadata.update({ 'html5_sources': HLS_SOURCES, }) if player_mode == 'html5_and_hls': metadata.update(youtube_ids) metadata.update({ 'html5_sources': HTML5_SOURCES + HLS_SOURCES, }) if additional_data: metadata.update(additional_data) return metadata def go_to_sequential_position(self, position): """ Navigate to sequential specified by `video_display_name` """ self.courseware_page.go_to_sequential_position(position) self.video.wait_for_video_player_render()
class VideoBaseTest(UniqueCourseTest): """ Base class for tests of the Video Player Sets up the course and provides helper functions for the Video tests. """ def setUp(self): """ Initialization of pages and course fixture for video tests """ super(VideoBaseTest, self).setUp() self.longMessage = True self.video = VideoPage(self.browser) self.tab_nav = TabNavPage(self.browser) self.courseware_page = CoursewarePage(self.browser, self.course_id) self.course_info_page = CourseInfoPage(self.browser, self.course_id) self.auth_page = AutoAuthPage(self.browser, course_id=self.course_id) self.course_fixture = CourseFixture(self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name']) self.metadata = None self.assets = [] self.contents_of_verticals = None self.youtube_configuration = {} self.user_info = {} # reset youtube stub server self.addCleanup(YouTubeStubConfig.reset) def navigate_to_video(self): """ Prepare the course and get to the video and render it """ self._install_course_fixture() self._navigate_to_courseware_video_and_render() def navigate_to_video_no_render(self): """ Prepare the course and get to the video unit however do not wait for it to render, because the has been an error. """ self._install_course_fixture() self._navigate_to_courseware_video_no_render() def _install_course_fixture(self): """ Install the course fixture that has been defined """ if self.assets: self.course_fixture.add_asset(self.assets) chapter_sequential = XBlockFixtureDesc('sequential', 'Test Section') chapter_sequential.add_children(*self._add_course_verticals()) chapter = XBlockFixtureDesc( 'chapter', 'Test Chapter').add_children(chapter_sequential) self.course_fixture.add_children(chapter) self.course_fixture.install() if len(self.youtube_configuration) > 0: YouTubeStubConfig.configure(self.youtube_configuration) def _add_course_verticals(self): """ Create XBlockFixtureDesc verticals :return: a list of XBlockFixtureDesc """ xblock_verticals = [] _contents_of_verticals = self.contents_of_verticals # Video tests require at least one vertical with a single video. if not _contents_of_verticals: _contents_of_verticals = [[{ 'display_name': 'Video', 'metadata': self.metadata }]] for vertical_index, vertical in enumerate(_contents_of_verticals): xblock_verticals.append( self._create_single_vertical(vertical, vertical_index)) return xblock_verticals def _create_single_vertical(self, vertical_contents, vertical_index): """ Create a single course vertical of type XBlockFixtureDesc with category `vertical`. A single course vertical can contain single or multiple video modules. :param vertical_contents: a list of items for the vertical to contain :param vertical_index: index for the vertical display name :return: XBlockFixtureDesc """ xblock_course_vertical = XBlockFixtureDesc( 'vertical', u'Test Vertical-{0}'.format(vertical_index)) for video in vertical_contents: xblock_course_vertical.add_children( XBlockFixtureDesc('video', video['display_name'], metadata=video.get('metadata'))) return xblock_course_vertical def _navigate_to_courseware_video(self): """ Register for the course and navigate to the video unit """ self.auth_page.visit() self.user_info = self.auth_page.user_info self.courseware_page.visit() def _navigate_to_courseware_video_and_render(self): """ Wait for the video player to render """ self._navigate_to_courseware_video() self.video.wait_for_video_player_render() def _navigate_to_courseware_video_no_render(self): """ Wait for the video Xmodule but not for rendering """ self._navigate_to_courseware_video() self.video.wait_for_video_class() def metadata_for_mode(self, player_mode, additional_data=None): """ Create a dictionary for video player configuration according to `player_mode` :param player_mode (str): Video player mode :param additional_data (dict): Optional additional metadata. :return: dict """ metadata = {} youtube_ids = { 'youtube_id_1_0': '', 'youtube_id_0_75': '', 'youtube_id_1_25': '', 'youtube_id_1_5': '', } if player_mode == 'html5': metadata.update(youtube_ids) metadata.update({'html5_sources': HTML5_SOURCES}) if player_mode == 'youtube_html5': metadata.update({ 'html5_sources': HTML5_SOURCES, }) if player_mode == 'youtube_html5_unsupported_video': metadata.update({'html5_sources': HTML5_SOURCES_INCORRECT}) if player_mode == 'html5_unsupported_video': metadata.update(youtube_ids) metadata.update({'html5_sources': HTML5_SOURCES_INCORRECT}) if player_mode == 'hls': metadata.update(youtube_ids) metadata.update({ 'html5_sources': HLS_SOURCES, }) if player_mode == 'html5_and_hls': metadata.update(youtube_ids) metadata.update({ 'html5_sources': HTML5_SOURCES + HLS_SOURCES, }) if additional_data: metadata.update(additional_data) return metadata def go_to_sequential_position(self, position): """ Navigate to sequential specified by `video_display_name` """ self.courseware_page.go_to_sequential_position(position) self.video.wait_for_video_player_render()
class LibraryContentTestBase(UniqueCourseTest): """ Base class for library content block tests """ USERNAME = "******" EMAIL = "*****@*****.**" STAFF_USERNAME = "******" STAFF_EMAIL = "*****@*****.**" shard = 10 def populate_library_fixture(self, library_fixture): """ To be overwritten by subclassed tests. Used to install a library to run tests on. """ def setUp(self): """ Set up library, course and library content XBlock """ super(LibraryContentTestBase, self).setUp() self.courseware_page = CoursewarePage(self.browser, self.course_id) self.studio_course_outline = StudioCourseOutlinePage( self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run']) self.library_fixture = LibraryFixture( 'test_org', self.unique_id, u'Test Library {}'.format(self.unique_id)) self.populate_library_fixture(self.library_fixture) self.library_fixture.install() self.library_info = self.library_fixture.library_info self.library_key = self.library_fixture.library_key # Install a course with library content xblock self.course_fixture = CourseFixture(self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name']) library_content_metadata = { 'source_library_id': six.text_type(self.library_key), 'mode': 'random', 'max_count': 1, } self.lib_block = XBlockFixtureDesc('library_content', "Library Content", metadata=library_content_metadata) self.course_fixture.add_children( XBlockFixtureDesc('chapter', SECTION_NAME).add_children( XBlockFixtureDesc('sequential', SUBSECTION_NAME).add_children( XBlockFixtureDesc('vertical', UNIT_NAME).add_children( self.lib_block)))) self.course_fixture.install() def _change_library_content_settings(self, count=1, capa_type=None): """ Performs library block refresh in Studio, configuring it to show {count} children """ unit_page = self._go_to_unit_page(True) library_container_block = StudioLibraryContainerXBlockWrapper.from_xblock_wrapper( unit_page.xblocks[1]) library_container_block.edit() editor = StudioLibraryContentEditor(self.browser, library_container_block.locator) editor.count = count if capa_type is not None: editor.capa_type = capa_type editor.save() self._go_to_unit_page(change_login=False) unit_page.wait_for_page() unit_page.publish() self.assertIn("Published and Live", unit_page.publish_title) @property def library_xblocks_texts(self): """ Gets texts of all xblocks in library """ return frozenset(child.data for child in self.library_fixture.children) def _go_to_unit_page(self, change_login=True): """ Open unit page in Studio """ if change_login: LogoutPage(self.browser).visit() self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True) self.studio_course_outline.visit() subsection = self.studio_course_outline.section( SECTION_NAME).subsection(SUBSECTION_NAME) return subsection.expand_subsection().unit(UNIT_NAME).go_to() def _goto_library_block_page(self, block_id=None): """ Open library page in LMS """ self.courseware_page.visit() paragraphs = self.courseware_page.q(css='.course-content p').results if not paragraphs: course_home_page = CourseHomePage(self.browser, self.course_id) course_home_page.visit() course_home_page.outline.go_to_section_by_index(0, 0) block_id = block_id if block_id is not None else self.lib_block.locator #pylint: disable=attribute-defined-outside-init self.library_content_page = LibraryContentXBlockWrapper( self.browser, block_id) self.library_content_page.wait_for_page() def _auto_auth(self, username, email, staff): """ Logout and login with given credentials. """ AutoAuthPage(self.browser, username=username, email=email, course_id=self.course_id, staff=staff).visit()
class StudioLibraryContainerTest(StudioLibraryTest, UniqueCourseTest, TestWithSearchIndexMixin): """ Test Library Content block in LMS """ def setUp(self): """ Install library with some content and a course using fixtures """ self._create_search_index() super(StudioLibraryContainerTest, self).setUp() # Also create a course: self.course_fixture = CourseFixture( self.course_info['org'], self.course_info['number'], self.course_info['run'], self.course_info['display_name'] ) self.populate_course_fixture(self.course_fixture) self.course_fixture.install() self.outline = CourseOutlinePage( self.browser, self.course_info['org'], self.course_info['number'], self.course_info['run'] ) self.outline.visit() subsection = self.outline.section(SECTION_NAME).subsection(SUBSECTION_NAME) self.unit_page = subsection.expand_subsection().unit(UNIT_NAME).go_to() def tearDown(self): """ Tear down method: remove search index backing file """ self._cleanup_index_file() super(StudioLibraryContainerTest, self).tearDown() def populate_library_fixture(self, library_fixture): """ Populate the children of the test course fixture. """ library_fixture.add_children( XBlockFixtureDesc("html", "Html1"), XBlockFixtureDesc("html", "Html2"), XBlockFixtureDesc("html", "Html3"), ) def populate_course_fixture(self, course_fixture): """ Install a course with sections/problems, tabs, updates, and handouts """ library_content_metadata = { 'source_library_id': unicode(self.library_key), 'mode': 'random', 'max_count': 1, } course_fixture.add_children( XBlockFixtureDesc('chapter', SECTION_NAME).add_children( XBlockFixtureDesc('sequential', SUBSECTION_NAME).add_children( XBlockFixtureDesc('vertical', UNIT_NAME).add_children( XBlockFixtureDesc('library_content', "Library Content", metadata=library_content_metadata) ) ) ) ) def _get_library_xblock_wrapper(self, xblock): """ Wraps xblock into :class:`...pages.studio.library.StudioLibraryContainerXBlockWrapper` """ return StudioLibraryContainerXBlockWrapper.from_xblock_wrapper(xblock) @ddt.data(1, 2, 3) def test_can_edit_metadata(self, max_count): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block And I edit library content metadata and save it Then I can ensure that data is persisted """ library_name = self.library_info['display_name'] library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[1]) library_container.edit() edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator) edit_modal.library_name = library_name edit_modal.count = max_count library_container.save_settings() # saving settings # open edit window again to verify changes are persistent library_container.edit() edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator) self.assertEqual(edit_modal.library_name, library_name) self.assertEqual(edit_modal.count, max_count) def test_no_library_shows_library_not_configured(self): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block And I edit to select "No Library" Then I can see that library content block is misconfigured """ expected_text = 'A library has not yet been selected.' expected_action = 'Select a Library' library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[1]) # precondition check - the library block should be configured before we remove the library setting self.assertFalse(library_container.has_validation_not_configured_warning) library_container.edit() edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator) edit_modal.library_name = "No Library Selected" library_container.save_settings() self.assertTrue(library_container.has_validation_not_configured_warning) self.assertIn(expected_text, library_container.validation_not_configured_warning_text) self.assertIn(expected_action, library_container.validation_not_configured_warning_text) def test_out_of_date_message(self): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block Then I update the library being used Then I refresh the page Then I can see that library content block needs to be updated When I click on the update link Then I can see that the content no longer needs to be updated """ # Formerly flaky: see TE-745 expected_text = "This component is out of date. The library has new content." library_block = self._get_library_xblock_wrapper(self.unit_page.xblocks[1]) self.assertFalse(library_block.has_validation_warning) # Removed this assert until a summary message is added back to the author view (SOL-192) #self.assertIn("3 matching components", library_block.author_content) self.library_fixture.create_xblock(self.library_fixture.library_location, XBlockFixtureDesc("html", "Html4")) self.unit_page.visit() # Reload the page self.assertTrue(library_block.has_validation_warning) self.assertIn(expected_text, library_block.validation_warning_text) library_block.refresh_children() self.unit_page.wait_for_page() # Wait for the page to reload library_block = self._get_library_xblock_wrapper(self.unit_page.xblocks[1]) self.assertFalse(library_block.has_validation_message) # Removed this assert until a summary message is added back to the author view (SOL-192) #self.assertIn("4 matching components", library_block.author_content) def test_no_content_message(self): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block And I set Problem Type selector so that no libraries have matching content Then I can see that "No matching content" warning is shown When I set Problem Type selector so that there is matching content Then I can see that warning messages are not shown """ # Add a single "Dropdown" type problem to the library (which otherwise has only HTML blocks): self.library_fixture.create_xblock(self.library_fixture.library_location, XBlockFixtureDesc( "problem", "Dropdown", data=textwrap.dedent(""" <problem> <p>Dropdown</p> <optionresponse><optioninput label="Dropdown" options="('1', '2')" correct="'2'"></optioninput></optionresponse> </problem> """) )) expected_text = 'There are no matching problem types in the specified libraries. Select another problem type' library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[1]) # precondition check - assert library has children matching filter criteria self.assertFalse(library_container.has_validation_error) self.assertFalse(library_container.has_validation_warning) library_container.edit() edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator) self.assertEqual(edit_modal.capa_type, "Any Type") # precondition check edit_modal.capa_type = "Custom Evaluated Script" library_container.save_settings() self.assertTrue(library_container.has_validation_warning) self.assertIn(expected_text, library_container.validation_warning_text) library_container.edit() edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator) self.assertEqual(edit_modal.capa_type, "Custom Evaluated Script") # precondition check edit_modal.capa_type = "Dropdown" library_container.save_settings() # Library should contain single Dropdown problem, so now there should be no errors again self.assertFalse(library_container.has_validation_error) self.assertFalse(library_container.has_validation_warning) def test_not_enough_children_blocks(self): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block And I set Problem Type selector so "Any" Then I can see that "No matching content" warning is shown """ expected_tpl = "The specified library is configured to fetch {count} problems, " \ "but there are only {actual} matching problems." library_container = self._get_library_xblock_wrapper(self.unit_page.xblocks[1]) # precondition check - assert block is configured fine self.assertFalse(library_container.has_validation_error) self.assertFalse(library_container.has_validation_warning) library_container.edit() edit_modal = StudioLibraryContentEditor(self.browser, library_container.locator) edit_modal.count = 50 library_container.save_settings() self.assertTrue(library_container.has_validation_warning) self.assertIn( expected_tpl.format(count=50, actual=len(self.library_fixture.children)), library_container.validation_warning_text ) def test_settings_overrides(self): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block And when I click the "View" link Then I can see a preview of the blocks drawn from the library. When I edit one of the blocks to change a setting such as "display_name", Then I can see the new setting is overriding the library version. When I subsequently click to refresh the content with the latest from the library, Then I can see that the overrided version of the setting is preserved. When I click to edit the block and reset the setting, then I can see that the setting's field defaults back to the library version. """ block_wrapper_unit_page = self._get_library_xblock_wrapper(self.unit_page.xblocks[0].children[0]) container_page = block_wrapper_unit_page.go_to_container() library_block = self._get_library_xblock_wrapper(container_page.xblocks[0]) self.assertFalse(library_block.has_validation_message) self.assertEqual(len(library_block.children), 3) block = library_block.children[0] self.assertIn(block.name, ("Html1", "Html2", "Html3")) name_default = block.name block.edit() new_display_name = "A new name for this HTML block" block.set_field_val("Display Name", new_display_name) block.save_settings() self.assertEqual(block.name, new_display_name) # Create a new block, causing a new library version: self.library_fixture.create_xblock(self.library_fixture.library_location, XBlockFixtureDesc("html", "Html4")) container_page.visit() # Reload self.assertTrue(library_block.has_validation_warning) library_block.refresh_children() container_page.wait_for_page() # Wait for the page to reload self.assertEqual(len(library_block.children), 4) self.assertEqual(block.name, new_display_name) # Reset: block.edit() block.reset_field_val("Display Name") block.save_settings() self.assertEqual(block.name, name_default) def test_cannot_manage(self): """ Scenario: Given I have a library, a course and library content xblock in a course When I go to studio unit page for library content block And when I click the "View" link Then I can see a preview of the blocks drawn from the library. And I do not see a duplicate button And I do not see a delete button """ block_wrapper_unit_page = self._get_library_xblock_wrapper(self.unit_page.xblocks[0].children[0]) container_page = block_wrapper_unit_page.go_to_container() for block in container_page.xblocks: self.assertFalse(block.has_duplicate_button) self.assertFalse(block.has_delete_button) self.assertFalse(block.has_edit_visibility_button)