def get_page_content(self, index=0): """ Get the contents of a page present at the index passed. Arguments: index (int): Index of page Returns: str: Content of page. """ click_css( page=self, css='.action-button-text', source_index=index, require_notification=False ) content = self.browser.execute_script( 'return tinyMCE.activeEditor.getContent()' ) click_css( page=self, css='.button.action-cancel', source_index=0, require_notification=False ) return content
def cancel_upload(self): """ Click 'cancel' on the file upload dialog. """ click_css( self, '.button.action-cancel', 0, False )
def upload_new_file(page, file_names): """ Upload file(s). Arguments: page (PageObject): Page to upload file to. file_names (list): file name(s) we want to upload. """ # Make file input field visible. file_input_css = '.file-input' page.browser.execute_script( '$("{}").css("display","block");'.format(file_input_css)) page.wait_for_element_visibility( file_input_css, "Upload button is visible.") # Loop through each file and upload. for file_name in file_names: page.q(css=file_input_css).results[0].send_keys( UPLOAD_FILE_DIR + "/" + file_name) page.wait_for_element_visibility( '.progress-bar', 'Upload progress bar is visible.') page.wait_for( lambda: page.q( css='.progress-fill').text[0] == 'Upload completed', description='Upload complete.') # Close the upload prompt. click_css(page, '.close-button', 0, False) page.wait_for_element_invisibility( page.UPLOAD_FORM_CSS, 'New file upload prompt has been closed.')
def add_advanced_component(page, menu_index, name): """ Adds an instance of the advanced component with the specified name. menu_index specifies which instance of the menus should be used (based on vertical placement within the page). """ # Click on the Advanced icon. page.wait_for_component_menu() click_css(page, 'button>span.large-advanced-icon', menu_index, require_notification=False) # This does an animation to hide the first level of buttons # and instead show the Advanced buttons that are available. # We should be OK though because click_css turns off jQuery animations # Make sure that the menu of advanced components is visible before clicking (the HTML is always on the # page, but will have display none until the large-advanced-icon is clicked). page.wait_for_element_visibility('.new-component-advanced', 'Advanced component menu is visible') # Now click on the component to add it. component_css = u'button[data-category={}]'.format(name) page.wait_for_element_visibility( component_css, u'Advanced component {} is visible'.format(name)) # Adding some components, e.g. the Discussion component, will make an ajax call # but we should be OK because the click_css method is written to handle that. click_css(page, component_css, 0)
def view_live(self): """ Clicks the "View Live" link and switches to the new tab """ click_css(self, '.view-live-button', require_notification=False) self.wait_for_page() self.browser.switch_to_window(self.browser.window_handles[-1])
def add_html_component(page, menu_index, boilerplate=None): """ Adds an instance of the HTML component with the specified name. menu_index specifies which instance of the menus should be used (based on vertical placement within the page). """ # Click on the HTML icon. page.wait_for_component_menu() click_css(page, 'button>span.large-html-icon', menu_index, require_notification=False) # Make sure that the menu of HTML components is visible before clicking page.wait_for_element_visibility('.new-component-html', 'HTML component menu is visible') # Now click on the component to add it. component_css = u'button[data-category=html]' if boilerplate: component_css += u'[data-boilerplate={}]'.format(boilerplate) else: component_css += u':not([data-boilerplate])' page.wait_for_element_visibility( component_css, u'HTML component {} is visible'.format(boilerplate)) # Adding some components will make an ajax call but we should be OK because # the click_css method is written to handle that. click_css(page, component_css, 0)
def publish(self): """ Publish the unit. """ click_css(self, self._bounded_selector('.action-publish'), require_notification=False) modal = CourseOutlineModal(self) EmptyPromise(lambda: modal.is_shown(), 'Modal is shown.') # pylint: disable=unnecessary-lambda modal.publish()
def write_update_and_save(self, new_update): """ Write new update and save it. """ type_in_codemirror(self, 0, new_update) click_css(self, '.save-button', 0, True) self.wait_for_element_invisibility('.new-update-form', 'Update form is not visible')
def discard_changes(self): """ Discards draft changes (which will then re-render the page). """ self.scroll_to_element('a.action-discard') click_css(self, 'a.action-discard', 0, require_notification=False) confirm_prompt(self) self.wait_for_ajax()
def edit_course_update(self, course_update_edit_text, index=0): """ Edit course update """ click_css(self, '#course-update-view .edit-button', index, False) self.wait_for_element_presence( '.new-update-form', 'Update form has been opened') self.write_update_and_save(course_update_edit_text)
def write_update_and_save(self, new_update): """ Write new update and save it. """ type_in_codemirror(self, 0, new_update) click_css(self, '.save-button', 0, True) self.wait_for_element_invisibility( '.new-update-form', 'Update form is not visible')
def click_delete_update_button(self): """ Clicks the delete update post button and confirms the delete notification. """ click_css(self, '.post-preview .delete-button', require_notification=False) confirm_prompt(self)
def edit_course_update(self, course_update_edit_text, index=0): """ Edit course update """ click_css(self, '#course-update-view .edit-button', index, False) self.wait_for_element_presence('.new-update-form', 'Update form has been opened') self.write_update_and_save(course_update_edit_text)
def click_take_me_there_link(self): """ Click take me there link. """ click_css( self, '#page-alert .alert.confirmation .nav-actions .action-secondary', require_notification=False)
def add_section_from_bottom_button(self, click_child_icon=False): """ Clicks the button for adding a section which resides at the bottom of the screen. """ element_css = self.BOTTOM_ADD_SECTION_BUTTON if click_child_icon: element_css += " .fa-plus" click_css(self, element_css)
def click_hint(self, hint_index=0): """ Click the Hint button. Arguments: hint_index (int): Index of a displayed hint """ click_css(self, '.problem .hint-button', require_notification=False) self.wait_for_focus_on_hint_notification(hint_index)
def open_new_update_form(self): """ Open update form """ self.wait_for_element_visibility( '.button.new-button.new-update-button', 'Update form visibility') click_css(self, '.button.new-button.new-update-button', 0, False) self.wait_for_element_presence('.new-update-form', 'Update form has been opened')
def add_child(self, require_notification=True): """ Adds a child to this xblock, waiting for notifications. """ click_css( self, self._bounded_selector(self.ADD_BUTTON_SELECTOR), require_notification=require_notification, )
def click_edit_update_button(self): """ Clicks the edit update post button. """ click_css(self, '.post-preview .edit-button', require_notification=False) self.wait_for_element_visibility('.CodeMirror', 'Waiting for .CodeMirror')
def add_discussion(page, menu_index=0): """ Add a new instance of the discussion category. menu_index specifies which instance of the menus should be used (based on vertical placement within the page). """ page.wait_for_component_menu() click_css(page, 'button>span.large-discussion-icon', menu_index)
def open_new_update_form(self): """ Open update form """ self.wait_for_element_visibility( '.button.new-button.new-update-button', 'Update form visibility' ) click_css(self, '.button.new-button.new-update-button', 0, False) self.wait_for_element_presence( '.new-update-form', 'Update form has been opened')
def add_page(self): """ Adds a new empty page. """ click_css(page=self, css='.button.new-button.new-tab', source_index=0, require_notification=False) self.wait_for_element_visibility('.component.course-tab.is-movable', 'New page is not visible')
def delete_course_update(self, index=0): """ Delete a course update. """ if self.get_course_update_count() > 0: click_css(self, '#course-update-view .delete-button', index, False) click_css(self, '.prompt.warning.has-actions .action-primary', 0, True) self.wait_for_element_invisibility( '.prompt.warning.has-actions .action-primary', 'Delete prompt is not visible')
def edit_course_handout(self, update_handout_text): """ Edit course handout. """ click_css(self, '#course-handouts-view .edit-button', 0, False) self.wait_for_element_visibility('.edit-handouts-form', 'Handout edit form visible.') type_in_codemirror(self, 0, update_handout_text) click_css(self, '.save-button', 0, True) self.wait_for_element_invisibility('.edit-handouts-form', 'Handout edit form is not visible')
def edit_course_handout(self, update_handout_text): """ Edit course handout. """ click_css( self, '#course-handouts-view .edit-button', 0, False) self.wait_for_element_visibility( '.edit-handouts-form', 'Handout edit form visible.') type_in_codemirror(self, 0, update_handout_text) click_css(self, '.save-button', 0, True) self.wait_for_element_invisibility( '.edit-handouts-form', 'Handout edit form is not visible')
def delete_page(self, index=0): """ Deletes the page present at the index passed. Arguments: index (int): Index of page """ click_css(page=self, css='.delete-button.action-button', source_index=index, require_notification=False) self.q(css='.prompt.warning button.action-primary ').first.click() sync_on_notification(self)
def delete(self, source_index): """ Delete the item with index source_index (based on vertical placement in page). Only visible items are counted in the source_index. The index of the first item is 0. """ # Click the delete button click_css(self, '.delete-button', source_index, require_notification=False) # Click the confirmation dialog button confirm_prompt(self)
def delete_course_update(self, index=0): """ Delete a course update. """ if self.get_course_update_count() > 0: click_css(self, '#course-update-view .delete-button', index, False) click_css( self, '.prompt.warning.has-actions .action-primary', 0, True ) self.wait_for_element_invisibility( '.prompt.warning.has-actions .action-primary', 'Delete prompt is not visible' )
def add_static_page(self): """ Adds a static page """ total_tabs = len(self.q(css='.course-nav-list>li')) click_css(self, '.add-pages .new-tab', require_notification=False) self.wait_for( lambda: len(self.q(css='.course-nav-list>li')) == total_tabs + 1, description="Static tab is added" ) self.wait_for_element_visibility( u'.tab-list :nth-child({}) .xblock-student_view'.format(total_tabs), 'Static tab is visible' )
def click_leave_team_link(self, remaining_members=0, cancel=False): """ Click on Leave Team link""" leave_team_css = '.leave-team-link' self.scroll_to_element(leave_team_css) self.wait_for_element_visibility(leave_team_css, 'Leave Team link is visible.') click_css(self, leave_team_css, require_notification=False) confirm_prompt(self, cancel, require_notification=False) if cancel is False: self.wait_for( lambda: self.join_team_button_present, description="Join Team button did not become present") self.wait_for_capacity_text(remaining_members)
def add_page(self): """ Adds a new empty page. """ self.wait_for_add_page_click_handler() click_css( page=self, css='.button.new-button.new-tab', source_index=0, require_notification=False ) self.wait_for_element_visibility( '.component.course-tab.is-movable', 'New page is not visible' )
def delete_page(self, index=0): """ Deletes the page present at the index passed. Arguments: index (int): Index of page """ click_css( page=self, css='.delete-button.action-button', source_index=index, require_notification=False ) self.q(css='.prompt.warning button.action-primary ').first.click() sync_on_notification(self)
def upload_image(self, file_name): """ Upload image and add description and click save to upload image via TinyMCE editor. """ file_input_css = "[type='file']" # select file input element and change visibility to add file. self.browser.execute_script( '$("{}").css("display","block");'.format(file_input_css)) self.wait_for_element_visibility(file_input_css, "Input is visible") self.q(css=file_input_css).results[0].send_keys(file_name) self.wait_for_element_visibility('#imageDescription', 'Upload form is visible.') self.q(css='#imageDescription').results[0].send_keys('test image') click_css(self, '.modal-footer .btn-primary')
def click_new_update_button(self): """ Clicks the new-update button. """ def is_update_button_enabled(): """ Checks if the New Update button is enabled """ return self.q( css='.new-update-button').attrs('disabled')[0] is None self.wait_for( promise_check_func=is_update_button_enabled, description='Waiting for the New update button to be enabled.') click_css(self, '.new-update-button', require_notification=False) self.wait_for_element_visibility('.CodeMirror', 'Waiting for .CodeMirror')
def edit_page(self, new_content, index=0): """ Edits the page present at the index passed. Arguments: new_content (str): New content to set. index (int): Index of page """ click_css(page=self, css='.action-button-text', source_index=index, require_notification=False) self.browser.execute_script( 'tinyMCE.activeEditor.setContent("{}")'.format(new_content)) self.browser.execute_script( 'document.querySelectorAll(".button.action-primary' '.action-save")[0].click();') sync_on_notification(self)
def toggle_staff_lock(self, inherits_staff_lock=False): """ Toggles "hide from students" which enables or disables a staff-only lock. Returns True if the lock is now enabled, else False. """ was_locked_initially = self.is_staff_locked if not was_locked_initially: self.q(css='a.action-staff-lock').first.click() else: click_css(self, 'a.action-staff-lock', 0, require_notification=False) if not inherits_staff_lock: confirm_prompt(self) self.wait_for_ajax() return not was_locked_initially
def upload_tarball(self, tarball_filename): """ Upload a tarball to be imported. """ asset_file_path = self.file_path(tarball_filename) # Make the upload elements visible to the WebDriver. self.browser.execute_script( '$(".file-name-block").show();$(".file-input").show()') # Upload the file. self.q(css='input[type="file"]')[0].send_keys(asset_file_path) # Upload the same file again. Reason behind this is to decrease the # probability or fraction of times the failure occur. Please be # noted this doesn't eradicate the root cause of the error, it # just decreases to failure rate to minimal. # Jira ticket reference: TNL-4191. self.q(css='input[type="file"]')[0].send_keys(asset_file_path) # Some of the tests need these lines to pass so don't remove them. self._wait_for_button() click_css(self, '.submit-button', require_notification=True)
def get_page_content(self, index=0): """ Get the contents of a page present at the index passed. Arguments: index (int): Index of page Returns: str: Content of page. """ click_css(page=self, css='.action-button-text', source_index=index, require_notification=False) content = self.browser.execute_script( 'return tinyMCE.activeEditor.getContent()') click_css(page=self, css='.button.action-cancel', source_index=0, require_notification=False) return content
def edit_page(self, new_content, index=0): """ Edits the page present at the index passed. Arguments: new_content (str): New content to set. index (int): Index of page """ click_css( page=self, css='.action-button-text', source_index=index, require_notification=False ) self.browser.execute_script( 'tinyMCE.activeEditor.setContent("{}")'.format(new_content) ) self.browser.execute_script( 'document.querySelectorAll(".button.action-primary' '.action-save")[0].click();' ) sync_on_notification(self)