def chain_flick(marionette, wait_for_condition, expected1, expected2): testAction = marionette.absolute_url("testAction.html") marionette.navigate(testAction) button = marionette.find_element(By.ID, "button1") action = Actions(marionette) action.flick(button, 0, 0, 0, 200).perform() wait_for_condition_else_raise(marionette, wait_for_condition, expected1,"return document.getElementById('button1').innerHTML;") wait_for_condition_else_raise(marionette, wait_for_condition, expected2,"return document.getElementById('buttonFlick').innerHTML;")
def _flick_to_image(self, direction): image = self.marionette.find_element(*self._current_image_locator) action = Actions(self.marionette) x_start = (image.size['width'] / 100) * (direction == 'next' and 90 or 10) x_end = (image.size['width'] / 100) * (direction == 'next' and 10 or 90) y_start = image.size['height'] / 4 y_end = image.size['height'] / 4 action.flick(image, x_start, y_start, x_end, y_end, 200).perform() Wait(self.marionette).until( lambda m: abs(image.location['x']) >= image.size['width'])
def chain_flick(marionette, wait_for_condition, expected1, expected2): testAction = marionette.absolute_url("testAction.html") marionette.navigate(testAction) button = marionette.find_element(By.ID, "button1") action = Actions(marionette) action.flick(button, 0, 0, 0, 200).perform() wait_for_condition_else_raise( marionette, wait_for_condition, expected1, "return document.getElementById('button1').innerHTML;") wait_for_condition_else_raise( marionette, wait_for_condition, expected2, "return document.getElementById('buttonFlick').innerHTML;")
def _flick_to_image(self, direction): image = self.marionette.find_element(*self._current_image_locator) action = Actions(self.marionette) x_start = (image.size['width'] / 100) * (direction == 'next' and 90 or 10) x_end = (image.size['width'] / 100) * (direction == 'next' and 10 or 90) y_start = image.size['height'] / 4 y_end = image.size['height'] / 4 action.flick(image, x_start, y_start, x_end, y_end, 200).perform() Wait(self.marionette).until( lambda m: abs(image.location['x']) >= image.size['width']) # Workaround for bug 1161441, the transitionend event is not firing in this # case with a Marionette flick action, as opposed to a manual flick action self.marionette.execute_script(""" arguments[0].dispatchEvent(new CustomEvent("transitionend")); """, [self.current_image_frame])
def _flick_to_image(self, direction): image = self.marionette.find_element(*self._current_image_locator) action = Actions(self.marionette) x_start = (image.size['width'] / 100) * (direction == 'next' and 90 or 10) x_end = (image.size['width'] / 100) * (direction == 'next' and 10 or 90) y_start = image.size['height'] / 4 y_end = image.size['height'] / 4 action.flick(image, x_start, y_start, x_end, y_end, 200).perform() Wait(self.marionette).until( lambda m: abs(image.location['x']) >= image.size['width']) # Workaround for bug 1161441, the transitionend event is not firing in this # case with a Marionette flick action, as opposed to a manual flick action self.marionette.execute_script( """ arguments[0].dispatchEvent(new CustomEvent("transitionend")); """, [self.current_image_frame])
def _flick_to_month(self, direction): """Flick current monthly calendar to next or previous month. @param direction: flick to next month if direction='next', else flick to previous month """ action = Actions(self.marionette) month = self.marionette.find_element( *self._current_monthly_calendar_locator) month_year = self.current_month_year x_start = (month.size['width'] / 100) * (direction == 'next' and 90 or 10) x_end = (month.size['width'] / 100) * (direction == 'next' and 10 or 90) y_start = month.size['height'] / 4 y_end = month.size['height'] / 4 action.flick(month, x_start, y_start, x_end, y_end, 200).perform() Wait(self.marionette).until(lambda m: self.current_month_year != month_year)
def _flick_to_month(self, direction): """Flick current monthly calendar to next or previous month. @param direction: flick to next month if direction='next', else flick to previous month """ action = Actions(self.marionette) month = self.marionette.find_element( *self._current_monthly_calendar_locator) month_year = self.current_month_year x_start = (month.size['width'] / 100) * (direction == 'next' and 90 or 10) x_end = (month.size['width'] / 100) * (direction == 'next' and 10 or 90) y_start = month.size['height'] / 4 y_end = month.size['height'] / 4 action.flick(month, x_start, y_start, x_end, y_end, 200).perform() Wait(self.marionette).until( lambda m: self.current_month_year != month_year)
class CommonCaretsTestCase(object): '''Common test cases for a selection with a two carets. To run these test cases, a subclass must inherit from both this class and MarionetteTestCase. ''' _long_press_time = 1 # 1 second _input_selector = (By.ID, 'input') _textarea_selector = (By.ID, 'textarea') _textarea_rtl_selector = (By.ID, 'textarea_rtl') _contenteditable_selector = (By.ID, 'contenteditable') _content_selector = (By.ID, 'content') _textarea2_selector = (By.ID, 'textarea2') _contenteditable2_selector = (By.ID, 'contenteditable2') _content2_selector = (By.ID, 'content2') def setUp(self): # Code to execute before a tests are run. super(CommonCaretsTestCase, self).setUp() self.actions = Actions(self.marionette) # The carets to be tested. self.carets_tested_pref = None # The carets to be disabled in this test suite. self.carets_disabled_pref = None def set_pref(self, pref_name, value): '''Set a preference to value. For example: >>> set_pref('layout.accessiblecaret.enabled', True) ''' pref_name = repr(pref_name) if isinstance(value, bool): value = 'true' if value else 'false' func = 'setBoolPref' elif isinstance(value, int): value = str(value) func = 'setIntPref' else: value = repr(value) func = 'setCharPref' with self.marionette.using_context('chrome'): script = 'Services.prefs.%s(%s, %s)' % (func, pref_name, value) self.marionette.execute_script(script) def open_test_html(self, enabled=True): '''Open html for testing and locate elements, and enable/disable touch caret.''' self.set_pref(self.carets_tested_pref, enabled) self.set_pref(self.carets_disabled_pref, False) test_html = self.marionette.absolute_url('test_selectioncarets.html') self.marionette.navigate(test_html) self._input = self.marionette.find_element(*self._input_selector) self._textarea = self.marionette.find_element(*self._textarea_selector) self._textarea_rtl = self.marionette.find_element( *self._textarea_rtl_selector) self._contenteditable = self.marionette.find_element( *self._contenteditable_selector) self._content = self.marionette.find_element(*self._content_selector) def open_test_html2(self, enabled=True): '''Open html for testing and locate elements, and enable/disable touch caret.''' self.set_pref(self.carets_tested_pref, enabled) self.set_pref(self.carets_disabled_pref, False) test_html2 = self.marionette.absolute_url( 'test_selectioncarets_multipleline.html') self.marionette.navigate(test_html2) self._textarea2 = self.marionette.find_element( *self._textarea2_selector) self._contenteditable2 = self.marionette.find_element( *self._contenteditable2_selector) self._content2 = self.marionette.find_element(*self._content2_selector) def _first_word_location(self, el): '''Get the location (x, y) of the first word in el. Note: this function has a side effect which changes focus to the target element el. ''' sel = SelectionManager(el) # Move caret behind the first character to get the location of the first # word. el.tap() sel.move_caret_to_front() sel.move_caret_by_offset(1) return sel.caret_location() def _long_press_to_select(self, el, x, y): '''Long press the location (x, y) to select a word. SelectionCarets should appear. On Windows, those spaces after the word will also be selected. ''' long_press_without_contextmenu(self.marionette, el, self._long_press_time, x, y) def _test_long_press_to_select_a_word(self, el, assertFunc): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue( len(words) >= 2, 'Expect at least two words in the content.') target_content = words[0] # Goal: Select the first word. x, y = self._first_word_location(el) self._long_press_to_select(el, x, y) # Ignore extra spaces selected after the word. assertFunc(target_content, sel.selected_content.rstrip()) def _test_move_selection_carets(self, el, assertFunc): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue( len(words) >= 1, 'Expect at least one word in the content.') # Goal: Select all text after the first word. target_content = original_content[len(words[0]):] # Get the location of the selection carets at the end of the content for # later use. el.tap() sel.select_all() (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() x, y = self._first_word_location(el) self._long_press_to_select(el, x, y) # Move the right caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Move the left caret to the previous position of the right caret. self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() # Ignore extra spaces at the beginning of the content in comparison. assertFunc(target_content.lstrip(), sel.selected_content.lstrip()) def _test_minimum_select_one_character(self, el, assertFunc, x=None, y=None): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue( len(words) >= 1, 'Expect at least one word in the content.') # Get the location of the selection carets at the end of the content for # later use. sel.select_all() (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() el.tap() # Goal: Select the first character. target_content = original_content[0] if x and y: # If we got x and y from the arguments, use it as a hint of the # location of the first word pass else: x, y = self._first_word_location(el) self._long_press_to_select(el, x, y) # Move the right caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Move the right caret to the position of the left caret. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform() assertFunc(target_content, sel.selected_content) def _test_focus_obtained_by_long_press(self, el1, el2): '''Test the focus could be changed from el1 to el2 by long press. If the focus is changed to e2 successfully, SelectionCarets should appear and could be dragged. ''' # Goal: Tap to focus el1, and then select the first character on # el2. # We want to collect the location of the first word in el2 here # since self._first_word_location() has the side effect which would # change the focus. x, y = self._first_word_location(el2) el1.tap() self._test_minimum_select_one_character(el2, self.assertEqual, x=x, y=y) def _test_handle_tilt_when_carets_overlap_to_each_other( self, el, assertFunc): '''Test tilt handling when carets overlap to each other. Let SelectionCarets overlap to each other. If SelectionCarets are set to tilted successfully, tapping the tilted carets should not cause the selection to be collapsed and the carets should be draggable. ''' sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue( len(words) >= 1, 'Expect at least one word in the content.') # Goal: Select the first word. x, y = self._first_word_location(el) self._long_press_to_select(el, x, y) target_content = sel.selected_content # Move the left caret to the position of the right caret to trigger # carets overlapping. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() # We make two hit tests targeting the left edge of the left tilted caret # and the right edge of the right tilted caret. If either of the hits is # missed, selection would be collapsed and both carets should not be # draggable. (caret3_x, caret3_y), (caret4_x, caret4_y) = sel.selection_carets_location() # The following values are from ua.css. caret_width = 44 caret_margin_left = -23 tilt_right_margin_left = 18 tilt_left_margin_left = -17 left_caret_left_edge_x = caret3_x + caret_margin_left + tilt_left_margin_left el.tap(left_caret_left_edge_x + 2, caret3_y) right_caret_right_edge_x = (caret4_x + caret_margin_left + tilt_right_margin_left + caret_width) el.tap(right_caret_right_edge_x - 2, caret4_y) # Drag the left caret back to the initial selection, the first word. self.actions.flick(el, caret3_x, caret3_y, caret1_x, caret1_y).perform() assertFunc(target_content, sel.selected_content) ######################################################################## # <input> test cases with selection carets enabled ######################################################################## def test_input_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._input, self.assertEqual) def test_input_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._input, self.assertEqual) def test_input_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._input, self.assertEqual) def test_input_focus_obtained_by_long_press_from_textarea(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._input) def test_input_focus_obtained_by_long_press_from_contenteditable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._input) def test_input_focus_obtained_by_long_press_from_content_non_editable( self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._input) def test_input_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other( self._input, self.assertEqual) ######################################################################## # <input> test cases with selection carets disabled ######################################################################## def test_input_long_press_to_select_a_word_disabled(self): self.open_test_html(enabled=False) self._test_long_press_to_select_a_word(self._input, self.assertNotEqual) def test_input_move_selection_carets_disabled(self): self.open_test_html(enabled=False) self._test_move_selection_carets(self._input, self.assertNotEqual) ######################################################################## # <textarea> test cases with selection carets enabled ######################################################################## def test_textarea_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea, self.assertEqual) def test_textarea_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._textarea, self.assertEqual) def test_textarea_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._textarea, self.assertEqual) def test_textarea_focus_obtained_by_long_press_from_input(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._textarea) def test_textarea_focus_obtained_by_long_press_from_contenteditable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._textarea) def test_textarea_focus_obtained_by_long_press_from_content_non_editable( self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._textarea) def test_textarea_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other( self._textarea, self.assertEqual) ######################################################################## # <textarea> test cases with selection carets disabled ######################################################################## def test_textarea_long_press_to_select_a_word_disabled(self): self.open_test_html(enabled=False) self._test_long_press_to_select_a_word(self._textarea, self.assertNotEqual) def test_textarea_move_selection_carets_disable(self): self.open_test_html(enabled=False) self._test_move_selection_carets(self._textarea, self.assertNotEqual) ######################################################################## # <textarea> right-to-left test cases with selection carets enabled ######################################################################## def test_textarea_rtl_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._textarea_rtl, self.assertEqual) ######################################################################## # <textarea> right-to-left test cases with selection carets disabled ######################################################################## def test_textarea_rtl_long_press_to_select_a_word_disabled(self): self.open_test_html(enabled=False) self._test_long_press_to_select_a_word(self._textarea_rtl, self.assertNotEqual) def test_textarea_rtl_move_selection_carets_disabled(self): self.open_test_html(enabled=False) self._test_move_selection_carets(self._textarea_rtl, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with selection carets enabled ######################################################################## def test_contenteditable_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._contenteditable, self.assertEqual) def test_contenteditable_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._contenteditable, self.assertEqual) def test_contenteditable_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._contenteditable, self.assertEqual) def test_contenteditable_focus_obtained_by_long_press_from_input(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._contenteditable) def test_contenteditable_focus_obtained_by_long_press_from_textarea(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._contenteditable) def test_contenteditable_focus_obtained_by_long_press_from_content_non_editable( self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._contenteditable) def test_contenteditable_handle_tilt_when_carets_overlap_to_each_other( self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other( self._contenteditable, self.assertEqual) ######################################################################## # <div> contenteditable test cases with selection carets disabled ######################################################################## def test_contenteditable_long_press_to_select_a_word_disabled(self): self.open_test_html(enabled=False) self._test_long_press_to_select_a_word(self._contenteditable, self.assertNotEqual) def test_contenteditable_move_selection_carets_disabled(self): self.open_test_html(enabled=False) self._test_move_selection_carets(self._contenteditable, self.assertNotEqual) ######################################################################## # <div> non-editable test cases with selection carets enabled ######################################################################## def test_content_non_editable_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._content, self.assertEqual) def test_content_non_editable_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._content, self.assertEqual) def test_content_non_editable_minimum_select_one_character_by_selection( self): self.open_test_html() self._test_minimum_select_one_character(self._content, self.assertEqual) def test_content_non_editable_focus_obtained_by_long_press_from_input( self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._content) def test_content_non_editable_focus_obtained_by_long_press_from_textarea( self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._content) def test_content_non_editable_focus_obtained_by_long_press_from_contenteditable( self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._content) def test_content_non_editable_handle_tilt_when_carets_overlap_to_each_other( self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other( self._content, self.assertEqual) ######################################################################## # <textarea> (multi-lines) test cases with selection carets enabled ######################################################################## def test_textarea2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._textarea2, self.assertEqual) ######################################################################## # <div> contenteditable2 (multi-lines) test cases with selection carets enabled ######################################################################## def test_contenteditable2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._contenteditable2, self.assertEqual) ######################################################################## # <div> non-editable2 (multi-lines) test cases with selection carets enabled ######################################################################## def test_content_non_editable2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._content2, self.assertEqual)
class AccessibleCaretCursorModeTestCase(MarionetteTestCase): '''Test cases for AccessibleCaret under cursor mode. We call the blinking cursor (nsCaret) as cursor, and call AccessibleCaret as caret for short. ''' # Element IDs. _input_id = 'input' _input_padding_id = 'input-padding' _textarea_id = 'textarea' _textarea_one_line_id = 'textarea-one-line' _contenteditable_id = 'contenteditable' # Test html files. _cursor_html = 'test_carets_cursor.html' def setUp(self): # Code to execute before every test is running. super(AccessibleCaretCursorModeTestCase, self).setUp() self.caret_tested_pref = 'layout.accessiblecaret.enabled' self.hide_carets_for_mouse = 'layout.accessiblecaret.hide_carets_for_mouse_input' self.prefs = { self.caret_tested_pref: True, self.hide_carets_for_mouse: False, } self.marionette.set_prefs(self.prefs) self.actions = Actions(self.marionette) def open_test_html(self, test_html): self.marionette.navigate(self.marionette.absolute_url(test_html)) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) def test_move_cursor_to_the_right_by_one_character(self, el_id): self.open_test_html(self._cursor_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) content_to_add = '!' target_content = sel.content target_content = target_content[:1] + content_to_add + target_content[ 1:] # Get first caret (x, y) at position 1 and 2. el.tap() sel.move_cursor_to_front() cursor0_x, cursor0_y = sel.cursor_location() first_caret0_x, first_caret0_y = sel.first_caret_location() sel.move_cursor_by_offset(1) first_caret1_x, first_caret1_y = sel.first_caret_location() # Tap the front of the input to make first caret appear. el.tap(cursor0_x, cursor0_y) # Move first caret. self.actions.flick(el, first_caret0_x, first_caret0_y, first_caret1_x, first_caret1_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() self.assertEqual(target_content, sel.content) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) def test_move_cursor_to_end_by_dragging_caret_to_bottom_right_corner( self, el_id): self.open_test_html(self._cursor_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) content_to_add = '!' target_content = sel.content + content_to_add # Tap the front of the input to make first caret appear. el.tap() sel.move_cursor_to_front() el.tap(*sel.cursor_location()) # Move first caret to the bottom-right corner of the element. src_x, src_y = sel.first_caret_location() dest_x, dest_y = el.size['width'], el.size['height'] self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() self.assertEqual(target_content, sel.content) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) def test_move_cursor_to_front_by_dragging_caret_to_front(self, el_id): self.open_test_html(self._cursor_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) content_to_add = '!' target_content = content_to_add + sel.content # Get first caret location at the front. el.tap() sel.move_cursor_to_front() dest_x, dest_y = sel.first_caret_location() # Tap to make first caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_cursor_to_end() sel.move_cursor_by_offset(1, backward=True) el.tap(*sel.cursor_location()) src_x, src_y = sel.first_caret_location() # Move first caret to the front of the input box. self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() self.assertEqual(target_content, sel.content) def test_caret_not_appear_when_typing_in_scrollable_content(self): self.open_test_html(self._cursor_html) el = self.marionette.find_element(By.ID, self._input_id) sel = SelectionManager(el) content_to_add = '!' non_target_content = content_to_add + sel.content + string.ascii_letters el.tap() sel.move_cursor_to_end() # Insert a long string to the end of the <input>, which triggers # ScrollPositionChanged event. el.send_keys(string.ascii_letters) # The caret should not be visible. If it does appear wrongly due to the # ScrollPositionChanged event, we can drag it to the front of the # <input> to change the cursor position. src_x, src_y = sel.first_caret_location() dest_x, dest_y = 0, 0 self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() # The content should not be inserted at the front of the <input>. el.send_keys(content_to_add) self.assertNotEqual(non_target_content, sel.content) @parameterized(_input_id, el_id=_input_id) @parameterized(_input_padding_id, el_id=_input_padding_id) @parameterized(_textarea_one_line_id, el_id=_textarea_one_line_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) def test_caret_not_jump_when_dragging_to_editable_content_boundary( self, el_id): self.open_test_html(self._cursor_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) content_to_add = '!' non_target_content = sel.content + content_to_add # Goal: the cursor position is not changed after dragging the caret down # on the Y-axis. el.tap() sel.move_cursor_to_front() el.tap(*sel.cursor_location()) x, y = sel.first_caret_location() # Drag the caret down by 50px, and insert '!'. self.actions.flick(el, x, y, x, y + 50).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() self.assertNotEqual(non_target_content, sel.content) @parameterized(_input_id, el_id=_input_id) @parameterized(_input_padding_id, el_id=_input_padding_id) @parameterized(_textarea_one_line_id, el_id=_textarea_one_line_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) def test_caret_not_jump_to_front_when_dragging_up_to_editable_content_boundary( self, el_id): self.open_test_html(self._cursor_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) content_to_add = '!' non_target_content = content_to_add + sel.content # Goal: the cursor position is not changed after dragging the caret down # on the Y-axis. el.tap() sel.move_cursor_to_end() sel.move_cursor_by_offset(1, backward=True) el.tap(*sel.cursor_location()) x, y = sel.first_caret_location() # Drag the caret up by 50px, and insert '!'. self.actions.flick(el, x, y, x, y - 50).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() self.assertNotEqual(non_target_content, sel.content) def test_drag_caret_from_front_to_end_across_columns(self): self.open_test_html('test_carets_columns.html') el = self.marionette.find_element(By.ID, 'columns-inner') sel = SelectionManager(el) content_to_add = '!' target_content = sel.content + content_to_add # Goal: the cursor position can be changed by dragging the caret from # the front to the end of the content. # Tap to make the cursor appear. before_image_1 = self.marionette.find_element(By.ID, 'before-image-1') before_image_1.tap() # Tap the front of the content to make first caret appear. sel.move_cursor_to_front() el.tap(*sel.cursor_location()) src_x, src_y = sel.first_caret_location() dest_x, dest_y = el.size['width'], el.size['height'] # Drag the first caret to the bottom-right corner of the element. self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() self.assertEqual(target_content, sel.content) def test_move_cursor_to_front_by_dragging_caret_to_front_br_element(self): self.open_test_html(self._cursor_html) el = self.marionette.find_element(By.ID, self._contenteditable_id) sel = SelectionManager(el) content_to_add_1 = '!' content_to_add_2 = '\n\n' target_content = content_to_add_1 + content_to_add_2 + sel.content # Goal: the cursor position can be changed by dragging the caret from # the end of the content to the front br element. Because we cannot get # caret location if it's on a br element, we need to get the first caret # location then adding the new lines. # Get first caret location at the front. el.tap() sel.move_cursor_to_front() dest_x, dest_y = sel.first_caret_location() # Append new line to the front of the content. el.send_keys(content_to_add_2) # Tap to make first caret appear. el.tap() sel.move_cursor_to_end() sel.move_cursor_by_offset(1, backward=True) el.tap(*sel.cursor_location()) src_x, src_y = sel.first_caret_location() # Move first caret to the front of the input box. self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add_1).key_up( content_to_add_1).perform() self.assertEqual(target_content, sel.content)
class SelectionCaretsTest(MarionetteTestCase): _long_press_time = 1 # 1 second _input_selector = (By.ID, "input") _textarea_selector = (By.ID, "textarea") _textarea_rtl_selector = (By.ID, "textarea_rtl") _contenteditable_selector = (By.ID, "contenteditable") _content_selector = (By.ID, "content") _textarea2_selector = (By.ID, "textarea2") _contenteditable2_selector = (By.ID, "contenteditable2") _content2_selector = (By.ID, "content2") def setUp(self): # Code to execute before a tests are run. MarionetteTestCase.setUp(self) self.actions = Actions(self.marionette) def openTestHtml(self, enabled=True): """Open html for testing and locate elements, and enable/disable touch caret.""" self.marionette.execute_script( 'SpecialPowers.setBoolPref("selectioncaret.enabled", %s);' % ("true" if enabled else "false") ) test_html = self.marionette.absolute_url("test_selectioncarets.html") self.marionette.navigate(test_html) self._input = self.marionette.find_element(*self._input_selector) self._textarea = self.marionette.find_element(*self._textarea_selector) self._textarea_rtl = self.marionette.find_element(*self._textarea_rtl_selector) self._contenteditable = self.marionette.find_element(*self._contenteditable_selector) self._content = self.marionette.find_element(*self._content_selector) def openTestHtml2(self, enabled=True): """Open html for testing and locate elements, and enable/disable touch caret.""" self.marionette.execute_script( 'SpecialPowers.setBoolPref("selectioncaret.enabled", %s);' % ("true" if enabled else "false") ) test_html2 = self.marionette.absolute_url("test_selectioncarets_multipleline.html") self.marionette.navigate(test_html2) self._textarea2 = self.marionette.find_element(*self._textarea2_selector) self._contenteditable2 = self.marionette.find_element(*self._contenteditable2_selector) self._content2 = self.marionette.find_element(*self._content2_selector) def _first_word_location(self, el): """Get the location (x, y) of the first word in el. Note: this function has a side effect which changes focus to the target element el. """ sel = SelectionManager(el) # Move caret behind the first character to get the location of the first # word. el.tap() sel.move_caret_to_front() sel.move_caret_by_offset(1) return sel.caret_location() def _long_press_to_select(self, el, x, y): """Long press the location (x, y) to select a word. SelectionCarets should appear. On Windows, those spaces after the word will also be selected. """ long_press_without_contextmenu(self.marionette, el, self._long_press_time, x, y) def _test_long_press_to_select_a_word(self, el, assertFunc): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 2, "Expect at least two words in the content.") target_content = words[0] # Goal: Select the first word. x, y = self._first_word_location(el) self._long_press_to_select(el, x, y) # Ignore extra spaces selected after the word. assertFunc(target_content, sel.selected_content.rstrip()) def _test_move_selection_carets(self, el, assertFunc): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, "Expect at least one word in the content.") # Goal: Select all text after the first word. target_content = original_content[len(words[0]) :] # Get the location of the selection carets at the end of the content for # later use. el.tap() sel.select_all() (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() x, y = self._first_word_location(el) self._long_press_to_select(el, x, y) # Move the right caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Move the left caret to the previous position of the right caret. self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() # Ignore extra spaces at the beginning of the content in comparison. assertFunc(target_content.lstrip(), sel.selected_content.lstrip()) def _test_minimum_select_one_character(self, el, assertFunc, x=None, y=None): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, "Expect at least one word in the content.") # Get the location of the selection carets at the end of the content for # later use. sel.select_all() (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() el.tap() # Goal: Select the first character. target_content = original_content[0] if x and y: # If we got x and y from the arguments, use it as a hint of the # location of the first word pass else: x, y = self._first_word_location(el) self._long_press_to_select(el, x, y) # Move the right caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Move the right caret to the position of the left caret. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform() assertFunc(target_content, sel.selected_content) def _test_focus_obtained_by_long_press(self, el1, el2): """Test the focus could be changed from el1 to el2 by long press. If the focus is changed to e2 successfully, SelectionCarets should appear and could be dragged. """ # Goal: Tap to focus el1, and then select the first character on # el2. # We want to collect the location of the first word in el2 here # since self._first_word_location() has the side effect which would # change the focus. x, y = self._first_word_location(el2) el1.tap() self._test_minimum_select_one_character(el2, self.assertEqual, x=x, y=y) def _test_handle_tilt_when_carets_overlap_to_each_other(self, el, assertFunc): """Test tilt handling when carets overlap to each other. Let SelectionCarets overlap to each other. If SelectionCarets are set to tilted successfully, tapping the tilted carets should not cause the selection to be collapsed and the carets should be draggable. """ sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, "Expect at least one word in the content.") # Goal: Select the first word. x, y = self._first_word_location(el) self._long_press_to_select(el, x, y) target_content = sel.selected_content # Move the left caret to the position of the right caret to trigger # carets overlapping. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() # We make two hit tests targeting the left edge of the left tilted caret # and the right edge of the right tilted caret. If either of the hits is # missed, selection would be collapsed and both carets should not be # draggable. (caret3_x, caret3_y), (caret4_x, caret4_y) = sel.selection_carets_location() # The following values are from ua.css. caret_width = 44 caret_margin_left = -23 tilt_right_margin_left = 18 tilt_left_margin_left = -17 left_caret_left_edge_x = caret3_x + caret_margin_left + tilt_left_margin_left el.tap(ceil(left_caret_left_edge_x), caret3_y) right_caret_right_edge_x = caret4_x + caret_margin_left + tilt_right_margin_left + caret_width el.tap(floor(right_caret_right_edge_x), caret4_y) # Drag the left caret back to the initial selection, the first word. self.actions.flick(el, caret3_x, caret3_y, caret1_x, caret1_y).perform() assertFunc(target_content, sel.selected_content) ######################################################################## # <input> test cases with selection carets enabled ######################################################################## def test_input_long_press_to_select_a_word(self): self.openTestHtml(enabled=True) self._test_long_press_to_select_a_word(self._input, self.assertEqual) def test_input_move_selection_carets(self): self.openTestHtml(enabled=True) self._test_move_selection_carets(self._input, self.assertEqual) def test_input_minimum_select_one_character(self): self.openTestHtml(enabled=True) self._test_minimum_select_one_character(self._input, self.assertEqual) def test_input_focus_obtained_by_long_press_from_textarea(self): self.openTestHtml(enabled=True) self._test_focus_obtained_by_long_press(self._textarea, self._input) def test_input_focus_obtained_by_long_press_from_contenteditable(self): self.openTestHtml(enabled=True) self._test_focus_obtained_by_long_press(self._contenteditable, self._input) def test_input_focus_obtained_by_long_press_from_content_non_editable(self): self.openTestHtml(enabled=True) self._test_focus_obtained_by_long_press(self._content, self._input) def test_input_handle_tilt_when_carets_overlap_to_each_other(self): self.openTestHtml(enabled=True) self._test_handle_tilt_when_carets_overlap_to_each_other(self._input, self.assertEqual) ######################################################################## # <input> test cases with selection carets disabled ######################################################################## def test_input_long_press_to_select_a_word_disabled(self): self.openTestHtml(enabled=False) self._test_long_press_to_select_a_word(self._input, self.assertNotEqual) def test_input_move_selection_carets_disabled(self): self.openTestHtml(enabled=False) self._test_move_selection_carets(self._input, self.assertNotEqual) ######################################################################## # <textarea> test cases with selection carets enabled ######################################################################## def test_textarea_long_press_to_select_a_word(self): self.openTestHtml(enabled=True) self._test_long_press_to_select_a_word(self._textarea, self.assertEqual) def test_textarea_move_selection_carets(self): self.openTestHtml(enabled=True) self._test_move_selection_carets(self._textarea, self.assertEqual) def test_textarea_minimum_select_one_character(self): self.openTestHtml(enabled=True) self._test_minimum_select_one_character(self._textarea, self.assertEqual) def test_textarea_focus_obtained_by_long_press_from_input(self): self.openTestHtml(enabled=True) self._test_focus_obtained_by_long_press(self._input, self._textarea) def test_textarea_focus_obtained_by_long_press_from_contenteditable(self): self.openTestHtml(enabled=True) self._test_focus_obtained_by_long_press(self._contenteditable, self._textarea) def test_textarea_focus_obtained_by_long_press_from_content_non_editable(self): self.openTestHtml(enabled=True) self._test_focus_obtained_by_long_press(self._content, self._textarea) def test_textarea_handle_tilt_when_carets_overlap_to_each_other(self): self.openTestHtml(enabled=True) self._test_handle_tilt_when_carets_overlap_to_each_other(self._textarea, self.assertEqual) ######################################################################## # <textarea> test cases with selection carets disabled ######################################################################## def test_textarea_long_press_to_select_a_word_disabled(self): self.openTestHtml(enabled=False) self._test_long_press_to_select_a_word(self._textarea, self.assertNotEqual) def test_textarea_move_selection_carets_disable(self): self.openTestHtml(enabled=False) self._test_move_selection_carets(self._textarea, self.assertNotEqual) ######################################################################## # <textarea> right-to-left test cases with selection carets enabled ######################################################################## def test_textarea_rtl_long_press_to_select_a_word(self): self.openTestHtml(enabled=True) self._test_long_press_to_select_a_word(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_move_selection_carets(self): self.openTestHtml(enabled=True) self._test_move_selection_carets(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_minimum_select_one_character(self): self.openTestHtml(enabled=True) self._test_minimum_select_one_character(self._textarea_rtl, self.assertEqual) ######################################################################## # <textarea> right-to-left test cases with selection carets disabled ######################################################################## def test_textarea_rtl_long_press_to_select_a_word_disabled(self): self.openTestHtml(enabled=False) self._test_long_press_to_select_a_word(self._textarea_rtl, self.assertNotEqual) def test_textarea_rtl_move_selection_carets_disabled(self): self.openTestHtml(enabled=False) self._test_move_selection_carets(self._textarea_rtl, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with selection carets enabled ######################################################################## def test_contenteditable_long_press_to_select_a_word(self): self.openTestHtml(enabled=True) self._test_long_press_to_select_a_word(self._contenteditable, self.assertEqual) def test_contenteditable_move_selection_carets(self): self.openTestHtml(enabled=True) self._test_move_selection_carets(self._contenteditable, self.assertEqual) def test_contenteditable_minimum_select_one_character(self): self.openTestHtml(enabled=True) self._test_minimum_select_one_character(self._contenteditable, self.assertEqual) def test_contenteditable_focus_obtained_by_long_press_from_input(self): self.openTestHtml(enabled=True) self._test_focus_obtained_by_long_press(self._input, self._contenteditable) def test_contenteditable_focus_obtained_by_long_press_from_textarea(self): self.openTestHtml(enabled=True) self._test_focus_obtained_by_long_press(self._textarea, self._contenteditable) def test_contenteditable_focus_obtained_by_long_press_from_content_non_editable(self): self.openTestHtml(enabled=True) self._test_focus_obtained_by_long_press(self._content, self._contenteditable) def test_contenteditable_handle_tilt_when_carets_overlap_to_each_other(self): self.openTestHtml(enabled=True) self._test_handle_tilt_when_carets_overlap_to_each_other(self._contenteditable, self.assertEqual) ######################################################################## # <div> contenteditable test cases with selection carets disabled ######################################################################## def test_contenteditable_long_press_to_select_a_word_disabled(self): self.openTestHtml(enabled=False) self._test_long_press_to_select_a_word(self._contenteditable, self.assertNotEqual) def test_contenteditable_move_selection_carets_disabled(self): self.openTestHtml(enabled=False) self._test_move_selection_carets(self._contenteditable, self.assertNotEqual) ######################################################################## # <div> non-editable test cases with selection carets enabled ######################################################################## def test_content_non_editable_long_press_to_select_a_word(self): self.openTestHtml(enabled=True) self._test_long_press_to_select_a_word(self._content, self.assertEqual) def test_content_non_editable_move_selection_carets(self): self.openTestHtml(enabled=True) self._test_move_selection_carets(self._content, self.assertEqual) def test_content_non_editable_minimum_select_one_character_by_selection(self): self.openTestHtml(enabled=True) self._test_minimum_select_one_character(self._content, self.assertEqual) def test_content_non_editable_focus_obtained_by_long_press_from_input(self): self.openTestHtml(enabled=True) self._test_focus_obtained_by_long_press(self._input, self._content) def test_content_non_editable_focus_obtained_by_long_press_from_textarea(self): self.openTestHtml(enabled=True) self._test_focus_obtained_by_long_press(self._textarea, self._content) def test_content_non_editable_focus_obtained_by_long_press_from_contenteditable(self): self.openTestHtml(enabled=True) self._test_focus_obtained_by_long_press(self._contenteditable, self._content) def test_content_non_editable_handle_tilt_when_carets_overlap_to_each_other(self): self.openTestHtml(enabled=True) self._test_handle_tilt_when_carets_overlap_to_each_other(self._content, self.assertEqual) ######################################################################## # <textarea> (multi-lines) test cases with selection carets enabled ######################################################################## def test_textarea2_minimum_select_one_character(self): self.openTestHtml2(enabled=True) self._test_minimum_select_one_character(self._textarea2, self.assertEqual) ######################################################################## # <div> contenteditable2 (multi-lines) test cases with selection carets enabled ######################################################################## def test_contenteditable2_minimum_select_one_character(self): self.openTestHtml2(enabled=True) self._test_minimum_select_one_character(self._contenteditable2, self.assertEqual) ######################################################################## # <div> non-editable2 (multi-lines) test cases with selection carets enabled ######################################################################## def test_content_non_editable2_minimum_select_one_character(self): self.openTestHtml2(enabled=True) self._test_minimum_select_one_character(self._content2, self.assertEqual)
class Loop(object): """Object representing the Loop application. """ def __init__(self, parent): self.apps = parent.apps self.data_layer = parent.data_layer self.parent = parent self.marionette = parent.marionette self.UTILS = parent.UTILS self.actions = Actions(self.marionette) self.browser = Browser(self.parent) self.app_name = "Firefox Hello" self.market_url = "https://owd.tid.es/B3lg1r89n/market/appList.html" self.persistent_directory = "/data/local/storage/persistent" self.loop_dir = self.UTILS.general.get_config_variable( "install_dir", "loop") def launch(self): """ Launch the app. """ self.app = self.apps.launch(self.app_name) self.UTILS.element.waitForNotElements( DOM.GLOBAL.loading_overlay, self.__class__.__name__ + " app - loading overlay") return self.app def is_installed(self): return self.apps.is_app_installed(self.app_name) def install(self): via = self.UTILS.general.get_config_variable("install_via", "loop") if via == "Grunt": self.install_via_grunt() elif via == "Market": self.install_via_marketplace() else: self.UTILS.test.test(False, "Not valid way to install Loop") def install_via_grunt(self, version="1.1"): self.UTILS.reporting.logResult('info', 'Installing via grunt....') c1 = 'cd {}'.format(self.loop_dir) c2 = 'git checkout {}'.format(version) c3 = 'git fetch && git merge origin/{}'.format(version) c4 = 'grunt build' with open(os.devnull, 'w') as DEVNULL: result = subprocess32.check_output("; ".join([c1, c2, c3, c4]), shell=True, stderr=DEVNULL) self.marionette.switch_to_frame() msg = "{} installed".format(self.app_name) installed_app_msg = (DOM.GLOBAL.system_banner_msg[0], DOM.GLOBAL.system_banner_msg[1].format(msg)) self.UTILS.element.waitForElements(installed_app_msg, "App installed", timeout=30) install_ok_msg = "Done, without errors." self.UTILS.reporting.logResult( 'info', "Result of this test script: {}".format(result)) self.UTILS.test.test(install_ok_msg in result, "Install via grunt is OK") def install_via_marketplace(self): self.UTILS.reporting.logResult('info', 'Installing via marketplace....') # Make sure we install the latest version self.update_and_publish() self.browser.launch() time.sleep(1) self.browser.open_url(self.market_url) loop_link = self.UTILS.element.getElement( ('xpath', '//p[contains(text(), "{}")]'.format(self.app_name)), "App link") loop_link.tap() self.marionette.switch_to_frame() install_ok = self.UTILS.element.getElement(DOM.GLOBAL.app_install_ok, "Install button") install_ok.tap() msg = "{} installed".format(self.app_name) installed_app_msg = (DOM.GLOBAL.system_banner_msg[0], DOM.GLOBAL.system_banner_msg[1].format(msg)) self.UTILS.element.waitForElements(installed_app_msg, "App installed", timeout=30) def reinstall(self): self.uninstall() time.sleep(2) self.install() def uninstall(self): self.UTILS.reporting.logResult('info', "uninstalling.........") self.apps.uninstall(self.app_name) self.parent.wait_for_condition( lambda m: not self.apps.is_app_installed(self.app_name), timeout=20, message="{} is not installed".format(self.app_name)) def update_and_publish(self): self.publish_loop_dir = self.UTILS.general.get_config_variable( "aux_files", "loop") c1 = 'cd {}'.fomrat(self.publish_loop_dir) c2 = './publish_app.sh {}'.format(self.loop_dir) with open(os.devnull, 'w') as DEVNULL: result = subprocess32.check_output("; ".join([c1, c2]), shell=True, stderr=DEVNULL) chops = result.split("\n") self.UTILS.reporting.logResult('info', "result: {}".format(chops)) self.UTILS.test.test("And all done, hopefully." in chops, "The script to publish an app is OK", True) def _fill_fxa_field(self, field_locator, text): """ Auxiliary method to fill "Firefox account login" fields """ self.UTILS.reporting.logResult( 'info', '[firefox_login] Filling fxa field with text: {}'.format(text)) self.parent.wait_for_element_displayed(*field_locator) fxa_input = self.marionette.find_element(*field_locator) fxa_input.send_keys(text) time.sleep(2) self.parent.wait_for_condition( lambda m: m.find_element(*DOM.Loop.ffox_account_login_next ).get_attribute("disabled") != "disabled") next_btn = self.marionette.find_element( *DOM.Loop.ffox_account_login_next) next_btn.tap() self.parent.wait_for_element_not_displayed( *DOM.Loop.ffox_account_login_overlay) def tap_on_firefox_login_button(self): ffox_btn = self.marionette.find_element( *DOM.Loop.wizard_login_ffox_account) self.UTILS.element.simulateClick(ffox_btn) def _tap_on_phone_login_button(self): phone_btn = self.marionette.find_element( *DOM.Loop.wizard_login_phone_number) self.UTILS.element.simulateClick(phone_btn) def _get_mobile_id_options(self): return self.marionette.find_elements(*DOM.Loop.mobile_id_sim_list_item) def wizard_or_login(self): """ Checks if we have to skip the Wizard, log in, or if we're already at the main screen of Loop For the first two scenarios, it returns True. If we are already inside Loop, it returns False. """ # TODO: switch try-except code -> first check login instead of wizard # see if it works, when the wizard is first try: self.parent.wait_for_element_displayed(*DOM.Loop.wizard_header) self.UTILS.reporting.logResult('info', '[wizard_or_login] Wizard') self.skip_wizard() return True except: self.UTILS.reporting.logResult('info', '[wizard_or_login] Login') try: self.parent.wait_for_element_displayed(*DOM.Loop.wizard_login) return True except: self.UTILS.reporting.logResult('info', '[wizard_or_login] Loop') try: self.parent.wait_for_element_displayed( *DOM.Loop.app_header) return False except: self.UTILS.test.test(False, "Ooops. Something went wrong", True) def get_wizard_steps(self): """ Returns the number of steps of the wizard """ return len( self.marionette.find_elements(*DOM.Loop.wizard_slideshow_step)) def skip_wizard(self): """ Skips first time use wizard by flicking the screen """ time.sleep(1) wizard_steps = self.get_wizard_steps() current_frame = self.apps.displayed_app.frame x_start = current_frame.size['width'] // 2 x_end = x_start // 4 y_start = current_frame.size['height'] // 2 for i in range(wizard_steps): self.actions.flick(current_frame, x_start, y_start, x_end, y_start, duration=600).perform() time.sleep(1) self.marionette.switch_to_frame(self.apps.displayed_app.frame_id) self.parent.wait_for_element_displayed(DOM.Loop.wizard_login[0], DOM.Loop.wizard_login[1], timeout=10) def firefox_login(self, email, password, is_wrong=False): """ Logs in using Firefox account """ self.tap_on_firefox_login_button() self.UTILS.iframe.switchToFrame(*DOM.Loop.ffox_account_frame_locator) self.parent.wait_for_element_displayed( DOM.Loop.ffox_account_login_title[0], DOM.Loop.ffox_account_login_title[1], timeout=20) self._fill_fxa_field(DOM.Loop.ffox_account_login_mail, email) self._fill_fxa_field(DOM.Loop.ffox_account_login_pass, password) if not is_wrong: done_btn = self.marionette.find_element( *DOM.Loop.ffox_account_login_done) done_btn.tap() def phone_login_auto(self, phone_number, option_number=1): """Wrapper to log in using phone number, either the already selected or entering it manually""" try: self.UTILS.reporting.info("Trying phone login using Mobile ID") self.phone_login() except: self.UTILS.reporting.info( "Mobile ID login failed, falling back to manual") self.UTILS.iframe.switchToFrame(*DOM.Loop.mobile_id_frame_locator) self.marionette.find_element('id', 'header').tap(25, 25) self.UTILS.iframe.switchToFrame(*DOM.Loop.frame_locator) self.phone_login_manually(phone_number) @retry(3, context=("OWDTestToolkit.apps.loop", "Loop"), aux_func_name="retry_phone_login") def phone_login(self, option_number=1): """ Logs in using mobile id """ self._tap_on_phone_login_button() self.UTILS.iframe.switchToFrame(*DOM.Loop.mobile_id_frame_locator) self.parent.wait_for_element_not_displayed( *DOM.Loop.ffox_account_login_overlay) mobile_id_header = ("xpath", DOM.GLOBAL.app_head_specific.format( _("Mobile ID"))) self.parent.wait_for_element_displayed(*mobile_id_header) options = self._get_mobile_id_options() if len(options) > 1: # Option number refers to the SIM number (1 or 2), not to the position in the array options[option_number - 1].tap() allow_button = self.marionette.find_element( *DOM.Loop.mobile_id_allow_button) allow_button.tap() try: self.parent.wait_for_element_displayed( DOM.Loop.mobile_id_verified_button[0], DOM.Loop.mobile_id_verified_button[1], timeout=30) verified_button = self.marionette.find_element( *DOM.Loop.mobile_id_verified_button) self.UTILS.element.simulateClick(verified_button) except: self.parent.wait_for_condition( lambda m: "state-sending" in m.find_element( *DOM.Loop.mobile_id_allow_button).get_attribute("class"), timeout=5, message="Button is still sending") # Make @retry do its work raise self.apps.switch_to_displayed_app() @retry(3, context=("OWDTestToolkit.apps.loop", "Loop"), aux_func_name="retry_phone_login") def phone_login_manually(self, phone_number_without_prefix): """ Logs in using mobile id, but instead of using the automatically provided by the app selecting the manual option @phone_number_without_prefix str Phone number to register into Loop NOTE: for the shake of simplicity, we assume the prefix is the spanish one (+34) by default """ self._tap_on_phone_login_button() self.UTILS.iframe.switchToFrame(*DOM.Loop.mobile_id_frame_locator) self.parent.wait_for_element_not_displayed( *DOM.Loop.ffox_account_login_overlay) mobile_id_header = ("xpath", DOM.GLOBAL.app_head_specific.format( _("Mobile ID"))) self.parent.wait_for_element_displayed(*mobile_id_header) # Manually! manually_link = self.marionette.find_element( *DOM.Loop.mobile_id_add_phone_number) manually_link.tap() self.parent.wait_for_element_displayed( *DOM.Loop.mobile_id_add_phone_number_number) phone_input = self.marionette.find_element( *DOM.Loop.mobile_id_add_phone_number_number) phone_input.send_keys(phone_number_without_prefix) # NOTE: before you virtually kill me, I cannot take this duplicated code into another # separated method due to the @reply decorator. Just letting you know :). allow_button = self.marionette.find_element( *DOM.Loop.mobile_id_allow_button) allow_button.tap() try: self.parent.wait_for_element_displayed( DOM.Loop.mobile_id_verified_button[0], DOM.Loop.mobile_id_verified_button[1], timeout=30) verified_button = self.marionette.find_element( *DOM.Loop.mobile_id_verified_button) self.UTILS.element.simulateClick(verified_button) except: self.parent.wait_for_condition( lambda m: "state-sending" in m.find_element( *DOM.Loop.mobile_id_allow_button).get_attribute("class"), timeout=5, message="Button is still sending") # Make @retry do its work raise self.apps.switch_to_displayed_app() @retry(5, context=("OWDTestToolkit.apps.loop", "Loop"), aux_func_name="retry_ffox_login") def allow_permission_ffox_login(self): """ Allows Loop to read our contacts This method checks whether is necessary to allow extra permissions for loop or not ater loggin in with Firefox accounts Also, since this is is the last step before connecting to the Loop server, it checks that no error has been raised. If that happens, it retries the connection up to 5 times. """ self.marionette.switch_to_frame() try: self.UTILS.reporting.debug("Looking for permission panel....") self.parent.wait_for_element_displayed( DOM.GLOBAL.app_permission_dialog[0], DOM.GLOBAL.app_permission_dialog[1], timeout=10) except: try: self.UTILS.reporting.debug( "Now looking for permission Loop main view....") self.apps.switch_to_displayed_app() self.parent.wait_for_element_displayed(*DOM.Loop.app_header) return except: self.UTILS.reporting.debug("And Now looking for error....") self.marionette.switch_to_frame() self.parent.wait_for_element_displayed( *DOM.GLOBAL.modal_dialog_alert_title) self.UTILS.reporting.logResult('info', "Error connecting...") # Make @retry do its work raise msg_text = self.marionette.find_element( *DOM.GLOBAL.app_permission_msg).text self.UTILS.test.test(self.app_name in msg_text, "Permissions for loop") allow_btn = self.marionette.find_element( *DOM.GLOBAL.app_permission_btn_yes) self.UTILS.element.simulateClick(allow_btn) self.apps.switch_to_displayed_app() def allow_permission_phone_login(self): """ Allows Loop to read our contacts This method checks whether is necessary to allow extra permissions for loop or not ater loggin in with Mobile ID Also, since this is is the last step before connecting to the Loop server, it checks that no error has been raised. If that happens, it retries the connection up to 5 times. """ self.marionette.switch_to_frame() try: self.UTILS.reporting.debug("Looking for permission panel....") self.parent.wait_for_element_displayed( DOM.GLOBAL.app_permission_dialog[0], DOM.GLOBAL.app_permission_dialog[1], timeout=10) except: self.UTILS.reporting.debug( "Now looking for permission Loop main view....") self.apps.switch_to_displayed_app() self.parent.wait_for_element_displayed(*DOM.Loop.app_header) return msg_text = self.marionette.find_element( *DOM.GLOBAL.app_permission_msg).text self.UTILS.test.test(self.app_name in msg_text, "Permissions for loop") allow_btn = self.marionette.find_element( *DOM.GLOBAL.app_permission_btn_yes) self.UTILS.element.simulateClick(allow_btn) self.apps.switch_to_displayed_app() def retry_ffox_login(self): """ Retry Ffox account login if it has failed. This method is called as the aux_func for our brand new retry decorator """ self.UTILS.reporting.logResult('info', "Retrying FxA login...") self.apps.switch_to_displayed_app() time.sleep(2) self.parent.wait_for_element_displayed(*DOM.Loop.error_screen_ok) ok_btn = self.marionette.find_element(*DOM.Loop.error_screen_ok) self.UTILS.element.simulateClick(ok_btn) self.parent.wait_for_element_displayed(*DOM.Loop.wizard_login) self.tap_on_firefox_login_button() def retry_phone_login(self): """ Retry phone login if it has failed """ self.UTILS.reporting.logResult('info', "Retrying phone login...") self.parent.wait_for_element_displayed(*DOM.Loop.mobile_id_back) back_btn = self.marionette.find_element(*DOM.Loop.mobile_id_back) self.UTILS.element.simulateClick(back_btn) self.apps.switch_to_displayed_app() time.sleep(2) self._tap_on_phone_login_button() def open_settings(self): """ Open settings panel from call log """ self.parent.wait_for_element_displayed(*DOM.Loop.open_settings_btn) settings_btn = self.marionette.find_element( *DOM.Loop.open_settings_btn) self.UTILS.element.simulateClick(settings_btn) self.parent.wait_for_element_displayed(*DOM.Loop.settings_panel_header) def logout(self, confirm=True): """ This methods logs us out from Loop. It assumes we already are in the Loop Settings panel """ try: self.parent.wait_for_element_displayed(*DOM.Loop.settings_logout) except: self.UTILS.reporting.logResult('info', "Already logged out") return logout_btn = self.marionette.find_element(*DOM.Loop.settings_logout) self.UTILS.element.simulateClick(logout_btn) self.parent.wait_for_element_displayed(*DOM.Loop.form_confirm_logout) if confirm: confirm_btn = self.marionette.find_element( *DOM.Loop.form_confirm_logout) self.UTILS.element.simulateClick(confirm_btn) self.parent.wait_for_element_not_displayed( *DOM.Loop.loading_overlay) self.parent.wait_for_element_displayed(*DOM.Loop.wizard_login) else: cancel_btn = self.marionette.find_element( *DOM.Loop.form_confirm_cancel) self.parent.wait_for_element_displayed( *DOM.Loop.settings_panel_header) def switch_to_urls(self): self.parent.wait_for_element_displayed( *DOM.Loop.call_log_shared_links_tab) tab = self.marionette.find_element(*DOM.Loop.call_log_shared_links_tab) tab.tap() self.parent.wait_for_condition( lambda m: tab.get_attribute("aria-selected") == "true", timeout=10, message="Checking that 'Shared links' is selected") def _get_number_of_urls(self, locator): try: entries = self.marionette.find_elements(*locator) return len(entries) except: return 0 def get_number_of_all_urls(self): return self._get_number_of_urls(DOM.Loop.shared_links_entry) def get_number_of_available_urls(self): return self._get_number_of_urls(DOM.Loop.shared_links_entry_available) def get_number_of_revoked_urls(self): return self._get_number_of_urls(DOM.Loop.shared_links_entry_revoked) def _select_action(self, action): elem = (DOM.Loop.form_action_action[0], DOM.Loop.form_action_action[1].format(action)) self.parent.wait_for_element_displayed(*elem) self.marionette.find_element(*elem).tap() def _confirm(self, decission): elem = DOM.Loop.form_confirm_delete if decission else DOM.Loop.form_confirm_cancel self.parent.wait_for_element_displayed(*elem) self.marionette.find_element(*elem).tap() def _is_entry_revoked(self, entry): try: entry.find_element(*DOM.Loop.shared_links_entry_revoked_nested) return True except Exception, e: self.UTILS.reporting.logResult('info', "What happened here? {}".format(e)) return False
class AccessibleCaretSelectionModeTestCase(MarionetteTestCase): '''Test cases for AccessibleCaret under selection mode.''' # Element IDs. _input_id = 'input' _input_padding_id = 'input-padding' _input_size_id = 'input-size' _textarea_id = 'textarea' _textarea2_id = 'textarea2' _textarea_one_line_id = 'textarea-one-line' _textarea_rtl_id = 'textarea-rtl' _contenteditable_id = 'contenteditable' _contenteditable2_id = 'contenteditable2' _content_id = 'content' _content2_id = 'content2' _non_selectable_id = 'non-selectable' # Test html files. _selection_html = 'test_carets_selection.html' _multipleline_html = 'test_carets_multipleline.html' _multiplerange_html = 'test_carets_multiplerange.html' _longtext_html = 'test_carets_longtext.html' _iframe_html = 'test_carets_iframe.html' _display_none_html = 'test_carets_display_none.html' def setUp(self): # Code to execute before every test is running. super(AccessibleCaretSelectionModeTestCase, self).setUp() self.carets_tested_pref = 'layout.accessiblecaret.enabled' self.prefs = { 'layout.word_select.eat_space_to_next_word': False, self.carets_tested_pref: True, } self.marionette.set_prefs(self.prefs) self.actions = Actions(self.marionette) def open_test_html(self, test_html): self.marionette.navigate(self.marionette.absolute_url(test_html)) def word_offset(self, text, ordinal): 'Get the character offset of the ordinal-th word in text.' tokens = re.split(r'(\S+)', text) # both words and spaces spaces = tokens[0::2] # collect spaces at odd indices words = tokens[1::2] # collect word at even indices if ordinal >= len(words): raise IndexError('Only %d words in text, but got ordinal %d' % (len(words), ordinal)) # Cursor position of the targeting word is behind the the first # character in the word. For example, offset to 'def' in 'abc def' is # between 'd' and 'e'. offset = len(spaces[0]) + 1 offset += sum(len(words[i]) + len(spaces[i + 1]) for i in range(ordinal)) return offset def test_word_offset(self): text = ' ' * 3 + 'abc' + ' ' * 3 + 'def' self.assertTrue(self.word_offset(text, 0), 4) self.assertTrue(self.word_offset(text, 1), 10) with self.assertRaises(IndexError): self.word_offset(text, 2) def word_location(self, el, ordinal): '''Get the location (x, y) of the ordinal-th word in el. The ordinal starts from 0. Note: this function has a side effect which changes focus to the target element el. ''' sel = SelectionManager(el) offset = self.word_offset(sel.content, ordinal) # Move the blinking cursor to the word. el.tap() sel.move_cursor_to_front() sel.move_cursor_by_offset(offset) x, y = sel.cursor_location() return x, y def rect_relative_to_window(self, el): '''Get element's bounding rectangle. This function is similar to el.rect, but the coordinate is relative to the top left corner of the window instead of the document. ''' return self.marionette.execute_script(''' let rect = arguments[0].getBoundingClientRect(); return {x: rect.x, y:rect.y, width: rect.width, height: rect.height}; ''', script_args=[el]) def long_press_on_location(self, el, x=None, y=None): '''Long press the location (x, y) to select a word. If no (x, y) are given, it will be targeted at the center of the element. On Windows, those spaces after the word will also be selected. This function sends synthesized eMouseLongTap to gecko. ''' rect = self.rect_relative_to_window(el) target_x = rect['x'] + (x if x is not None else rect['width'] // 2) target_y = rect['y'] + (y if y is not None else rect['height'] // 2) self.marionette.execute_script(''' let utils = window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); utils.sendTouchEventToWindow('touchstart', [0], [arguments[0]], [arguments[1]], [1], [1], [0], [1], 1, 0); utils.sendMouseEventToWindow('mouselongtap', arguments[0], arguments[1], 0, 1, 0); utils.sendTouchEventToWindow('touchend', [0], [arguments[0]], [arguments[1]], [1], [1], [0], [1], 1, 0); ''', script_args=[target_x, target_y], sandbox='system') def long_press_on_word(self, el, wordOrdinal): x, y = self.word_location(el, wordOrdinal) self.long_press_on_location(el, x, y) def to_unix_line_ending(self, s): """Changes all Windows/Mac line endings in s to UNIX line endings.""" return s.replace('\r\n', '\n').replace('\r', '\n') @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) @parameterized(_content_id, el_id=_content_id) def test_long_press_to_select_a_word(self, el_id): self.open_test_html(self._selection_html) el = self.marionette.find_element(By.ID, el_id) self._test_long_press_to_select_a_word(el) def _test_long_press_to_select_a_word(self, el): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 2, 'Expect at least two words in the content.') target_content = words[0] # Goal: Select the first word. self.long_press_on_word(el, 0) # Ignore extra spaces selected after the word. self.assertEqual(target_content, sel.selected_content) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) @parameterized(_content_id, el_id=_content_id) def test_drag_carets(self, el_id): self.open_test_html(self._selection_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.') # Goal: Select all text after the first word. target_content = original_content[len(words[0]):] # Get the location of the carets at the end of the content for later # use. el.tap() sel.select_all() end_caret_x, end_caret_y = sel.second_caret_location() self.long_press_on_word(el, 0) # Drag the second caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Drag the first caret to the previous position of the second caret. self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() self.assertEqual(target_content, sel.selected_content) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) @parameterized(_content_id, el_id=_content_id) def test_drag_swappable_carets(self, el_id): self.open_test_html(self._selection_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.') target_content1 = words[0] target_content2 = original_content[len(words[0]):] # Get the location of the carets at the end of the content for later # use. el.tap() sel.select_all() end_caret_x, end_caret_y = sel.second_caret_location() self.long_press_on_word(el, 0) # Drag the first caret to the end and back to where it was # immediately. The selection range should not be collapsed. caret1_x, caret1_y = sel.first_caret_location() self.actions.flick(el, caret1_x, caret1_y, end_caret_x, end_caret_y)\ .flick(el, end_caret_x, end_caret_y, caret1_x, caret1_y).perform() self.assertEqual(target_content1, sel.selected_content) # Drag the first caret to the end. caret1_x, caret1_y = sel.first_caret_location() self.actions.flick(el, caret1_x, caret1_y, end_caret_x, end_caret_y).perform() self.assertEqual(target_content2, sel.selected_content) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) @parameterized(_content_id, el_id=_content_id) def test_minimum_select_one_character(self, el_id): self.open_test_html(self._selection_html) el = self.marionette.find_element(By.ID, el_id) self._test_minimum_select_one_character(el) @parameterized(_textarea2_id, el_id=_textarea2_id) @parameterized(_contenteditable2_id, el_id=_contenteditable2_id) @parameterized(_content2_id, el_id=_content2_id) def test_minimum_select_one_character2(self, el_id): self.open_test_html(self._multipleline_html) el = self.marionette.find_element(By.ID, el_id) self._test_minimum_select_one_character(el) def _test_minimum_select_one_character(self, el): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.') # Get the location of the carets at the end of the content for later # use. sel.select_all() end_caret_x, end_caret_y = sel.second_caret_location() el.tap() # Goal: Select the first character. target_content = original_content[0] self.long_press_on_word(el, 0) # Drag the second caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Drag the second caret to the position of the first caret. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform() self.assertEqual(target_content, sel.selected_content) @parameterized(_input_id + '_to_' + _textarea_id, el1_id=_input_id, el2_id=_textarea_id) @parameterized(_input_id + '_to_' + _contenteditable_id, el1_id=_input_id, el2_id=_contenteditable_id) @parameterized(_input_id + '_to_' + _content_id, el1_id=_input_id, el2_id=_content_id) @parameterized(_textarea_id + '_to_' + _input_id, el1_id=_textarea_id, el2_id=_input_id) @parameterized(_textarea_id + '_to_' + _contenteditable_id, el1_id=_textarea_id, el2_id=_contenteditable_id) @parameterized(_textarea_id + '_to_' + _content_id, el1_id=_textarea_id, el2_id=_content_id) @parameterized(_contenteditable_id + '_to_' + _input_id, el1_id=_contenteditable_id, el2_id=_input_id) @parameterized(_contenteditable_id + '_to_' + _textarea_id, el1_id=_contenteditable_id, el2_id=_textarea_id) @parameterized(_contenteditable_id + '_to_' + _content_id, el1_id=_contenteditable_id, el2_id=_content_id) @parameterized(_content_id + '_to_' + _input_id, el1_id=_content_id, el2_id=_input_id) @parameterized(_content_id + '_to_' + _textarea_id, el1_id=_content_id, el2_id=_textarea_id) @parameterized(_content_id + '_to_' + _contenteditable_id, el1_id=_content_id, el2_id=_contenteditable_id) def test_long_press_changes_focus_from(self, el1_id, el2_id): self.open_test_html(self._selection_html) el1 = self.marionette.find_element(By.ID, el1_id) el2 = self.marionette.find_element(By.ID, el2_id) # Compute the content of the first word in el2. sel = SelectionManager(el2) original_content = sel.content words = original_content.split() target_content = words[0] # Goal: Tap to focus el1, and then select the first word on el2. # We want to collect the location of the first word in el2 here # since self.word_location() has the side effect which would # change the focus. x, y = self.word_location(el2, 0) el1.tap() self.long_press_on_location(el2, x, y) self.assertEqual(target_content, sel.selected_content) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) def test_focus_not_changed_by_long_press_on_non_selectable(self, el_id): self.open_test_html(self._selection_html) el = self.marionette.find_element(By.ID, el_id) non_selectable = self.marionette.find_element(By.ID, self._non_selectable_id) # Goal: Focus remains on the editable element el after long pressing on # the non-selectable element. sel = SelectionManager(el) self.long_press_on_word(el, 0) self.long_press_on_location(non_selectable) active_sel = SelectionManager(self.marionette.get_active_element()) self.assertEqual(sel.content, active_sel.content) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) @parameterized(_content_id, el_id=_content_id) def test_handle_tilt_when_carets_overlap_each_other(self, el_id): '''Test tilt handling when carets overlap to each other. Let the two carets overlap each other. If they are set to tilted successfully, tapping the tilted carets should not cause the selection to be collapsed and the carets should be draggable. ''' self.open_test_html(self._selection_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.') # Goal: Select the first word. self.long_press_on_word(el, 0) target_content = sel.selected_content # Drag the first caret to the position of the second caret to trigger # carets overlapping. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() # We make two hit tests targeting the left edge of the left tilted caret # and the right edge of the right tilted caret. If either of the hits is # missed, selection would be collapsed and both carets should not be # draggable. (caret3_x, caret3_y), (caret4_x, caret4_y) = sel.carets_location() # The following values are from ua.css and all.js caret_width = float(self.marionette.get_pref('layout.accessiblecaret.width')) caret_margin_left = float(self.marionette.get_pref('layout.accessiblecaret.margin-left')) tilt_right_margin_left = 0.41 * caret_width tilt_left_margin_left = -0.39 * caret_width left_caret_left_edge_x = caret3_x + caret_margin_left + tilt_left_margin_left el.tap(left_caret_left_edge_x + 2, caret3_y) right_caret_right_edge_x = (caret4_x + caret_margin_left + tilt_right_margin_left + caret_width) el.tap(right_caret_right_edge_x - 2, caret4_y) # Drag the first caret back to the initial selection, the first word. self.actions.flick(el, caret3_x, caret3_y, caret1_x, caret1_y).perform() self.assertEqual(target_content, sel.selected_content) def test_drag_caret_over_non_selectable_field(self): '''Test dragging the caret over a non-selectable field. The selected content should exclude non-selectable elements and the second caret should appear in last range's position. ''' self.open_test_html(self._multiplerange_html) body = self.marionette.find_element(By.ID, 'bd') sel3 = self.marionette.find_element(By.ID, 'sel3') sel4 = self.marionette.find_element(By.ID, 'sel4') sel6 = self.marionette.find_element(By.ID, 'sel6') # Select target element and get target caret location self.long_press_on_word(sel4, 3) sel = SelectionManager(body) end_caret_x, end_caret_y = sel.second_caret_location() self.long_press_on_word(sel6, 0) end_caret2_x, end_caret2_y = sel.second_caret_location() # Select start element self.long_press_on_word(sel3, 3) # Drag end caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(body, caret2_x, caret2_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), 'this 3\nuser can select this') (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(body, caret2_x, caret2_y, end_caret2_x, end_caret2_y, 1).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), 'this 3\nuser can select this 4\nuser can select this 5\nuser') # Drag first caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(body, caret1_x, caret1_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), '4\nuser can select this 5\nuser') def test_drag_swappable_caret_over_non_selectable_field(self): self.open_test_html(self._multiplerange_html) body = self.marionette.find_element(By.ID, 'bd') sel3 = self.marionette.find_element(By.ID, 'sel3') sel4 = self.marionette.find_element(By.ID, 'sel4') sel = SelectionManager(body) self.long_press_on_word(sel4, 3) (end_caret1_x, end_caret1_y), (end_caret2_x, end_caret2_y) = sel.carets_location() self.long_press_on_word(sel3, 3) (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() # Drag the first caret down, which will across the second caret. self.actions.flick(body, caret1_x, caret1_y, end_caret1_x, end_caret1_y).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), '3\nuser can select') # The old second caret becomes the first caret. Drag it down again. self.actions.flick(body, caret2_x, caret2_y, end_caret2_x, end_caret2_y).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), 'this') def test_drag_caret_to_beginning_of_a_line(self): '''Bug 1094056 Test caret visibility when caret is dragged to beginning of a line ''' self.open_test_html(self._multiplerange_html) body = self.marionette.find_element(By.ID, 'bd') sel1 = self.marionette.find_element(By.ID, 'sel1') sel2 = self.marionette.find_element(By.ID, 'sel2') # Select the first word in the second line self.long_press_on_word(sel2, 0) sel = SelectionManager(body) (start_caret_x, start_caret_y), (end_caret_x, end_caret_y) = sel.carets_location() # Select target word in the first line self.long_press_on_word(sel1, 2) # Drag end caret to the beginning of the second line (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(body, caret2_x, caret2_y, start_caret_x, start_caret_y).perform() # Drag end caret back to the target word self.actions.flick(body, start_caret_x, start_caret_y, caret2_x, caret2_y).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content), 'select') @skip_if_not_rotatable def test_caret_position_after_changing_orientation_of_device(self): '''Bug 1094072 If positions of carets are updated correctly, they should be draggable. ''' self.open_test_html(self._longtext_html) body = self.marionette.find_element(By.ID, 'bd') longtext = self.marionette.find_element(By.ID, 'longtext') # Select word in portrait mode, then change to landscape mode self.marionette.set_orientation('portrait') self.long_press_on_word(longtext, 12) sel = SelectionManager(body) (p_start_caret_x, p_start_caret_y), (p_end_caret_x, p_end_caret_y) = sel.carets_location() self.marionette.set_orientation('landscape') (l_start_caret_x, l_start_caret_y), (l_end_caret_x, l_end_caret_y) = sel.carets_location() # Drag end caret to the start caret to change the selected content self.actions.flick(body, l_end_caret_x, l_end_caret_y, l_start_caret_x, l_start_caret_y).perform() # Change orientation back to portrait mode to prevent affecting # other tests self.marionette.set_orientation('portrait') self.assertEqual(self.to_unix_line_ending(sel.selected_content), 'o') def test_select_word_inside_an_iframe(self): '''Bug 1088552 The scroll offset in iframe should be taken into consideration properly. In this test, we scroll content in the iframe to the bottom to cause a huge offset. If we use the right coordinate system, selection should work. Otherwise, it would be hard to trigger select word. ''' self.open_test_html(self._iframe_html) iframe = self.marionette.find_element(By.ID, 'frame') # switch to inner iframe and scroll to the bottom self.marionette.switch_to_frame(iframe) self.marionette.execute_script( 'document.getElementById("bd").scrollTop += 999') # long press to select bottom text body = self.marionette.find_element(By.ID, 'bd') sel = SelectionManager(body) self._bottomtext = self.marionette.find_element(By.ID, 'bottomtext') self.long_press_on_location(self._bottomtext) self.assertNotEqual(self.to_unix_line_ending(sel.selected_content), '') def test_carets_initialized_in_display_none(self): '''Test AccessibleCaretEventHub is properly initialized on a <html> with display: none. ''' self.open_test_html(self._display_none_html) html = self.marionette.find_element(By.ID, 'html') content = self.marionette.find_element(By.ID, 'content') # Remove 'display: none' from <html> self.marionette.execute_script( 'arguments[0].style.display = "unset";', script_args=[html] ) # If AccessibleCaretEventHub is initialized successfully, select a word # should work. self._test_long_press_to_select_a_word(content) def test_long_press_to_select_when_partial_visible_word_is_selected(self): self.open_test_html(self._selection_html) el = self.marionette.find_element(By.ID, self._input_size_id) sel = SelectionManager(el) original_content = sel.content words = original_content.split() # We cannot use self.long_press_on_word() for the second long press # on the first word because it has side effect that changes the # cursor position. We need to save the location of the first word to # be used later. word0_x, word0_y = self.word_location(el, 0) # Long press on the second word. self.long_press_on_word(el, 1) self.assertEqual(words[1], sel.selected_content) # Long press on the first word. self.long_press_on_location(el, word0_x, word0_y) self.assertEqual(words[0], sel.selected_content) # If the second caret is visible, it can be dragged to the position # of the first caret. After that, selection will contain only the # first character. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform() self.assertEqual(words[0][0], sel.selected_content) @parameterized(_input_id, el_id=_input_id) @parameterized(_input_padding_id, el_id=_input_padding_id) @parameterized(_textarea_one_line_id, el_id=_textarea_one_line_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) def test_carets_not_jump_when_dragging_to_editable_content_boundary(self, el_id): self.open_test_html(self._selection_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 3, 'Expect at least three words in the content.') # Goal: the selection is not changed after dragging the caret on the # Y-axis. target_content = words[1] self.long_press_on_word(el, 1) (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() # Drag the first caret up by 50px. self.actions.flick(el, caret1_x, caret1_y, caret1_x, caret1_y - 50).perform() self.assertEqual(target_content, sel.selected_content) # Drag the second caret down by 50px. self.actions.flick(el, caret2_x, caret2_y, caret2_x, caret2_y + 50).perform() self.assertEqual(target_content, sel.selected_content)
class TouchCaretTest(MarionetteTestCase): _input_selector = (By.ID, 'input') _textarea_selector = (By.ID, 'textarea') _contenteditable_selector = (By.ID, 'contenteditable') _large_expiration_time = 3000 * 20 # 60 seconds def setUp(self): # Code to execute before a test is being run. MarionetteTestCase.setUp(self) self.actions = Actions(self.marionette) self.original_expiration_time = self.expiration_time def tearDown(self): # Code to execute after a test is being run. self.expiration_time = self.original_expiration_time MarionetteTestCase.tearDown(self) @property def expiration_time(self): 'Return touch caret expiration time in milliseconds.' return self.marionette.execute_script( 'return SpecialPowers.getIntPref("touchcaret.expiration.time");') @expiration_time.setter def expiration_time(self, expiration_time): 'Set touch caret expiration time in milliseconds.' self.marionette.execute_script( 'SpecialPowers.setIntPref("touchcaret.expiration.time", arguments[0]);', script_args=[expiration_time]) def openTestHtml(self, enabled=True, expiration_time=None): '''Open html for testing and locate elements, enable/disable touch caret, and set touch caret expiration time in milliseconds). ''' self.marionette.execute_async_script( 'SpecialPowers.pushPrefEnv({"set": [["touchcaret.enabled", %s]]}, marionetteScriptFinished);' % ('true' if enabled else 'false')) # Set a larger expiration time to avoid intermittent test failures. if expiration_time is not None: self.expiration_time = expiration_time test_html = self.marionette.absolute_url('test_touchcaret.html') self.marionette.navigate(test_html) self._input = self.marionette.find_element(*self._input_selector) self._textarea = self.marionette.find_element(*self._textarea_selector) self._contenteditable = self.marionette.find_element(*self._contenteditable_selector) def _test_move_caret_to_the_right_by_one_character(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = sel.content target_content = target_content[:1] + content_to_add + target_content[1:] # Get touch caret (x, y) at position 1 and 2. el.tap() sel.move_caret_to_front() caret0_x, caret0_y = sel.caret_location() touch_caret0_x, touch_caret0_y = sel.touch_caret_location() sel.move_caret_by_offset(1) touch_caret1_x, touch_caret1_y = sel.touch_caret_location() # Tap the front of the input to make touch caret appear. el.tap(caret0_x, caret0_y) # Move touch caret self.actions.flick(el, touch_caret0_x, touch_caret0_y, touch_caret1_x, touch_caret1_y).perform() el.send_keys(content_to_add) assertFunc(target_content, sel.content) def _test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = sel.content + content_to_add # Tap the front of the input to make touch caret appear. el.tap() sel.move_caret_to_front() el.tap(*sel.caret_location()) # Move touch caret to the bottom-right corner of the element. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = el.size['width'], el.size['height'] self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() el.send_keys(content_to_add) assertFunc(target_content, sel.content) def _test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = content_to_add + sel.content # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) # Move touch caret to the top-left corner of the input box. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() el.send_keys(content_to_add) assertFunc(target_content, sel.content) def _test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' non_target_content = content_to_add + sel.content # Get touch caret expiration time in millisecond, and convert it to second. timeout = self.expiration_time / 1000.0 # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) # Wait until touch caret disappears, then pretend to move it to the # top-left corner of the input box. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 self.actions.wait(timeout).flick(el, src_x, src_y, dest_x, dest_y).perform() el.send_keys(content_to_add) assertFunc(non_target_content, sel.content) def _test_touch_caret_hides_after_receiving_wheel_event(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' non_target_content = content_to_add + sel.content # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) # Send an arbitrary scroll-down-10px wheel event to the center of the # input box to hide touch caret. Then pretend to move it to the top-left # corner of the input box. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 el_center_x, el_center_y = el.rect['x'], el.rect['y'] self.marionette.execute_script( '''var utils = SpecialPowers.getDOMWindowUtils(window); utils.sendWheelEvent(arguments[0], arguments[1], 0, 10, 0, WheelEvent.DOM_DELTA_PIXEL, 0, 0, 0, 0);''', script_args=[el_center_x, el_center_y] ) self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() el.send_keys(content_to_add) assertFunc(non_target_content, sel.content) ######################################################################## # <input> test cases with touch caret enabled ######################################################################## def test_input_move_caret_to_the_right_by_one_character(self): self.openTestHtml(enabled=True, expiration_time=self._large_expiration_time) self._test_move_caret_to_the_right_by_one_character(self._input, self.assertEqual) def test_input_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self): self.openTestHtml(enabled=True, expiration_time=self._large_expiration_time) self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._input, self.assertEqual) def test_input_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self): self.openTestHtml(enabled=True, expiration_time=self._large_expiration_time) self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._input, self.assertEqual) def test_input_touch_caret_timeout(self): self.openTestHtml(enabled=True) self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self._input, self.assertNotEqual) def test_input_touch_caret_hides_after_receiving_wheel_event(self): self.openTestHtml(enabled=True, expiration_time=0) self._test_touch_caret_hides_after_receiving_wheel_event(self._input, self.assertNotEqual) ######################################################################## # <input> test cases with touch caret disabled ######################################################################## def test_input_move_caret_to_the_right_by_one_character_disabled(self): self.openTestHtml(enabled=False) self._test_move_caret_to_the_right_by_one_character(self._input, self.assertNotEqual) def test_input_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self): self.openTestHtml(enabled=False) self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._input, self.assertNotEqual) ######################################################################## # <textarea> test cases with touch caret enabled ######################################################################## def test_textarea_move_caret_to_the_right_by_one_character(self): self.openTestHtml(enabled=True, expiration_time=self._large_expiration_time) self._test_move_caret_to_the_right_by_one_character(self._textarea, self.assertEqual) def test_textarea_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self): self.openTestHtml(enabled=True, expiration_time=self._large_expiration_time) self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._textarea, self.assertEqual) def test_textarea_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self): self.openTestHtml(enabled=True, expiration_time=self._large_expiration_time) self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._textarea, self.assertEqual) def test_textarea_touch_caret_timeout(self): self.openTestHtml(enabled=True) self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self._textarea, self.assertNotEqual) def test_textarea_touch_caret_hides_after_receiving_wheel_event(self): self.openTestHtml(enabled=True, expiration_time=0) self._test_touch_caret_hides_after_receiving_wheel_event(self._textarea, self.assertNotEqual) ######################################################################## # <textarea> test cases with touch caret disabled ######################################################################## def test_textarea_move_caret_to_the_right_by_one_character_disabled(self): self.openTestHtml(enabled=False) self._test_move_caret_to_the_right_by_one_character(self._textarea, self.assertNotEqual) def test_textarea_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self): self.openTestHtml(enabled=False) self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._textarea, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with touch caret enabled ######################################################################## def test_contenteditable_move_caret_to_the_right_by_one_character(self): self.openTestHtml(enabled=True, expiration_time=self._large_expiration_time) self._test_move_caret_to_the_right_by_one_character(self._contenteditable, self.assertEqual) def test_contenteditable_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self): self.openTestHtml(enabled=True, expiration_time=self._large_expiration_time) self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._contenteditable, self.assertEqual) def test_contenteditable_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self): self.openTestHtml(enabled=True, expiration_time=self._large_expiration_time) self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._contenteditable, self.assertEqual) def test_contenteditable_touch_caret_timeout(self): self.openTestHtml(enabled=True) self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self._contenteditable, self.assertNotEqual) def test_contenteditable_touch_caret_hides_after_receiving_wheel_event(self): self.openTestHtml(enabled=True, expiration_time=0) self._test_touch_caret_hides_after_receiving_wheel_event(self._contenteditable, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with touch caret disabled ######################################################################## def test_contenteditable_move_caret_to_the_right_by_one_character_disabled(self): self.openTestHtml(enabled=False) self._test_move_caret_to_the_right_by_one_character(self._contenteditable, self.assertNotEqual) def test_contenteditable_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self): self.openTestHtml(enabled=False) self._test_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self._contenteditable, self.assertNotEqual)
class Calendar(object): def __init__(self, parent): self.apps = parent.apps self.data_layer = parent.data_layer self.parent = parent self.marionette = parent.marionette self.UTILS = parent.UTILS self.actions = Actions(self.marionette) def launch(self): self.app = self.apps.launch(self.__class__.__name__) self.UTILS.element.waitForNotElements(DOM.GLOBAL.loading_overlay, self.__class__.__name__ + " app - loading overlay") return self.app def moveDayViewBy(self, num): """ Switches to week view, then moves 'p_num' weeks in the future or past (if the p_num is positive or negative) relative to today. """ self.UTILS.reporting.logResult("info", "<b>Adjusting day view by {} screens ...</b>".format(num)) self.setView("day") self.setView("today") if num == 0: return """ Set the y-coordinate offset, depending on which direction we need to flick the display. """ numMoves = num if numMoves > 0: _moveEl = 1 else: _moveEl = 2 numMoves = numMoves * -1 # Now move to the desired screen. for i in range(numMoves): # Flick the screen to move it (tricky to find the element we can flick!). _el = self.marionette.find_elements(*DOM.Calendar.dview_events) _num = 0 for i in range(len(_el)): if _el[i].is_displayed(): _num = i break x_pos1 = 0 x_pos2 = 0 if _moveEl == 1: x_pos1 = _el[_num].size["width"] if _moveEl == 2: x_pos2 = _el[_num].size["width"] self.actions.flick(_el[_num], x_pos1, 0, x_pos2, 0).perform() time.sleep(0.3) # Check this is the expected day. _new_epoch = int(time.time()) + (num * 24 * 60 * 60) _new_now = self.UTILS.date_and_time.getDateTimeFromEpochSecs(_new_epoch) _expected_str = "{} {}, {}".format(_new_now.month_name[:3], _new_now.mday, _new_now.day_name) x = self.UTILS.element.getElement(DOM.Calendar.current_view_header, "Current view header") self.UTILS.test.test(x.text == _expected_str, "Header is '<b>%s</b>' (it was '%s')." % (_expected_str, x.text)) x = self.UTILS.debug.screenShotOnErr() self.UTILS.reporting.logResult("info", "Day view screen after moving {} pages: ".format(num), x) def getEventPreview(self, p_view, p_hour24, p_title, p_location=False): """ Return object for an event in month / week or day view. The tag identifiers aren't consistent, so set them here. <type>: (<event preview identifier>, <event title identifier>) """ event_view = { "month": (DOM.Calendar.view_events_block_m % p_hour24, DOM.Calendar.view_events_title_month), "week": (DOM.Calendar.view_events_block_w % p_hour24, DOM.Calendar.view_events_title_week), "day": (DOM.Calendar.view_events_block_d % p_hour24, DOM.Calendar.view_events_title_day) } viewStr = event_view[p_view] """ Switch to the desired view. For the life of me I can't get 'wait_for_element' ... to work in day view, so I'm just waiting a few seconds then checking with .is_displayed() instead. """ self.setView(p_view) time.sleep(2) # Start by getting the parent element objects, which could contain event details. event_objects = self.UTILS.element.getElements(('xpath', viewStr[0]), "'" + p_view + "' event details list", False, 20, False) if not event_objects: return False if len(event_objects) <= 0: return False for event_object in event_objects: if p_title in event_object.text: return event_object # If we get to here we failed to return the element we're after. return False def createEvent(self): x = self.UTILS.element.getElement(DOM.Calendar.add_event_btn, "Add event button") x.tap() title = ("xpath", "//input[@name='title']") where = ("xpath", "//input[@name='location']") # allday checkbox, True False (use tap()) allday = ("xpath", "//li[@class='allday']") x = self.UTILS.element.getElement(title, "Title", True, 10) x.send_keys("hello title") x = self.UTILS.element.getElement(where, "Where") x.send_keys("hello where") x = self.UTILS.element.getElement(allday, "All day") x.tap() self.marionette.execute_script("document.getElementById('start-date-locale').click()") def changeDay(self, numDays, viewType): """ Changes the calendar day to a different day relative to 'today' - <b>uses the month view to do this, then switches back to whichever view you want (month, week, day)</b>.<br> <b>numDays</b> is a number (can be negative to go back, i.e. -5,-2,1,3,5 etc...).<br> <b>viewType</b> is the calendar view to return to (today / day / week / month)<br> Returns a modified DateTime object from <i>UTILS.getDateTimeFromEpochSecs()</i>. """ self.setView("month") self.setView("today") now_secs = time.time() now_diff = int(now_secs) + (86400 * numDays) now_today = self.UTILS.date_and_time.getDateTimeFromEpochSecs(now_secs) new_today = self.UTILS.date_and_time.getDateTimeFromEpochSecs(now_diff) # Switch to month view and tap this day, then switch back to our view. if now_today.mon != new_today.mon: x = new_today.mon - now_today.mon self.moveMonthViewBy(x) el_id_str = "d-%s-%s-%s" % (new_today.year, new_today.mon - 1, new_today.mday) x = self.UTILS.element.getElement(("xpath", "//li[@data-date='{}']".format(el_id_str)), "Cell for day {}/{}/{}".format(new_today.mday, new_today.mon, new_today.year)) x.tap() self.setView(viewType.lower()) return new_today def moveMonthViewBy(self, num): """ Switches to month view, then moves 'num' months in the future or past (if the num is positive or negative) relative to today. """ self.UTILS.reporting.logResult("info", "<b>Adjusting month view by {} months ...</b>".format(num)) self.setView("month") self.setView("today") if num == 0: return """ Set the y-coordinate offset, depending on which direction we need to flick the display. """ numMoves = num x2 = 0 if numMoves > 0: el_num = -1 x2 = -500 if numMoves < 0: el_num = 0 x2 = 500 numMoves = numMoves * -1 now = time.localtime(int(time.time())) month = now.tm_mon year = now.tm_year for i in range(numMoves): # Flick the display to show the date we're aiming for. el = self.marionette.find_elements(*DOM.Calendar.mview_first_row_for_flick)[el_num] self.actions.flick(el, 0, 0, x2, 0).perform() # Increment the month and year so we keep track of what's expected. if num < 0: month = month - 1 else: month = month + 1 if month <= 0: month = 12 year = year - 1 elif month >= 13: month = 1 year = year + 1 time.sleep(0.3) # Work out what the header should now be. month_names = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] expect = "{} {}".format(month_names[month - 1], year) actual = self.UTILS.element.getElement(DOM.Calendar.current_view_header, "Header").text self.UTILS.test.test(expect.lower() in actual.lower(), "Expecting header to contain '{}' (received '{}')" .format(expect, actual)) def moveWeekViewBy(self, num): """ Switches to week view, then moves 'num' weeks in the future or past (if the num is positive or negative) relative to today. """ self.UTILS.reporting.logResult("info", "<b>Adjusting week view by {} screens ...</b>".format(num)) self.setView("week") self.setView("today") if num == 0: return """ Set the y-coordinate offset, depending on which direction we need to flick the display. """ numMoves = num x2 = 0 if numMoves > 0: el = -1 x2 = -500 if numMoves < 0: el = 0 x2 = 500 numMoves = numMoves * -1 """ Keep track of how many days we're adjusting the display by (so we can check we're looking at the correct display at the end). """ days_offset = 0 now_epoch = int(time.time()) now = self.UTILS.date_and_time.getDateTimeFromEpochSecs(now_epoch) now_str = "{} {}".format(now.day_name[:3].upper(), now.mday) displayed_days = self.UTILS.element.getElements(DOM.Calendar.wview_active_days, "Active days") startpos = 0 for i in range(len(displayed_days)): x = displayed_days[i].text if x: if now_str in x: startpos = i - 1 break if num < 0: days_offset = startpos else: days_offset = len(displayed_days) - startpos - 2 # Now move to the desired screen. for i in range(numMoves): # Flick the screen to move it. self.actions.flick(displayed_days[el], 0, 0, x2, 0).perform() # Get the count of days we're adjusting (so we can check later). displayed_days = self.UTILS.element.getElements(DOM.Calendar.wview_active_days, "Active days") days_offset = days_offset + len(displayed_days) time.sleep(0.3) """ Work out what the display should now be: 1. Today + days_offset should be displayed. 2. Header should be month + year, now + days_offset should be in active days. """ if num < 0: new_epoch = now_epoch - (days_offset * 24 * 60 * 60) else: new_epoch = now_epoch + (days_offset * 24 * 60 * 60) new_now = self.UTILS.date_and_time.getDateTimeFromEpochSecs(new_epoch) new_now_str = "{} {}".format(new_now.day_name[:3].upper(), new_now.mday) x = self.UTILS.element.getElements(DOM.Calendar.wview_active_days, "Active days") boolOK = False for i in range(len(x)): x = displayed_days[i].text if x: if new_now_str in x: boolOK = True break self.UTILS.test.test(boolOK, "The column for date '<b>{}</b>' displayed.".format(new_now_str)) x = self.UTILS.element.getElement(DOM.Calendar.current_view_header, "Current view header") self.UTILS.test.test(new_now.month_name in x.text, "'<b>{}</b>' is in header ('{}')." .format(new_now.month_name, x.text)) self.UTILS.test.test(str(new_now.year) in x.text, "'<b>{}</b>' is in header ('{}').".format(new_now.year, x.text)) x = self.UTILS.debug.screenShotOnErr() self.UTILS.reporting.logResult("info", "Week view screen after moving {} pages: ".format(num), x) def setView(self, typ): """ Set to view typ (today / day / week / month). """ x = self.UTILS.element.getElement((DOM.Calendar.view_type[0], DOM.Calendar.view_type[1] % typ.lower()), "'" + typ + "' view type selector") x.tap() if typ.lower() != 'today': viewTypes = {"month": DOM.Calendar.mview_container, "week": DOM.Calendar.wview_container, "day": DOM.Calendar.dview_container} self.UTILS.element.waitForElements(viewTypes[typ], "Container for '{}' view".format(typ)) else: time.sleep(0.5)
class CommonCaretsTestCase(object): '''Common test cases for a selection with a two carets. To run these test cases, a subclass must inherit from both this class and MarionetteTestCase. ''' _input_selector = (By.ID, 'input') _textarea_selector = (By.ID, 'textarea') _textarea_rtl_selector = (By.ID, 'textarea_rtl') _contenteditable_selector = (By.ID, 'contenteditable') _content_selector = (By.ID, 'content') _textarea2_selector = (By.ID, 'textarea2') _contenteditable2_selector = (By.ID, 'contenteditable2') _content2_selector = (By.ID, 'content2') def setUp(self): # Code to execute before a tests are run. super(CommonCaretsTestCase, self).setUp() self.actions = Actions(self.marionette) # The carets to be tested. self.carets_tested_pref = None # The carets to be disabled in this test suite. self.carets_disabled_pref = None def set_pref(self, pref_name, value): '''Set a preference to value. For example: >>> set_pref('layout.accessiblecaret.enabled', True) ''' pref_name = repr(pref_name) if isinstance(value, bool): value = 'true' if value else 'false' func = 'setBoolPref' elif isinstance(value, int): value = str(value) func = 'setIntPref' else: value = repr(value) func = 'setCharPref' with self.marionette.using_context('chrome'): script = 'Services.prefs.%s(%s, %s)' % (func, pref_name, value) self.marionette.execute_script(script) def open_test_html(self, enabled=True): '''Open html for testing and locate elements, and enable/disable touch caret.''' self.set_pref(self.carets_tested_pref, enabled) self.set_pref(self.carets_disabled_pref, False) test_html = self.marionette.absolute_url('test_selectioncarets.html') self.marionette.navigate(test_html) self._input = self.marionette.find_element(*self._input_selector) self._textarea = self.marionette.find_element(*self._textarea_selector) self._textarea_rtl = self.marionette.find_element(*self._textarea_rtl_selector) self._contenteditable = self.marionette.find_element(*self._contenteditable_selector) self._content = self.marionette.find_element(*self._content_selector) def open_test_html2(self, enabled=True): '''Open html for testing and locate elements, and enable/disable touch caret.''' self.set_pref(self.carets_tested_pref, enabled) self.set_pref(self.carets_disabled_pref, False) test_html2 = self.marionette.absolute_url('test_selectioncarets_multipleline.html') self.marionette.navigate(test_html2) self._textarea2 = self.marionette.find_element(*self._textarea2_selector) self._contenteditable2 = self.marionette.find_element(*self._contenteditable2_selector) self._content2 = self.marionette.find_element(*self._content2_selector) def open_test_html_multirange(self, enabled=True): 'Open html for testing and enable selectioncaret and non-editable support' self.set_pref(self.carets_tested_pref, enabled) self.set_pref(self.carets_disabled_pref, False) test_html = self.marionette.absolute_url('test_selectioncarets_multiplerange.html') self.marionette.navigate(test_html) self._body = self.marionette.find_element(By.ID, 'bd') self._sel1 = self.marionette.find_element(By.ID, 'sel1') self._sel2 = self.marionette.find_element(By.ID, 'sel2') self._sel3 = self.marionette.find_element(By.ID, 'sel3') self._sel4 = self.marionette.find_element(By.ID, 'sel4') self._sel6 = self.marionette.find_element(By.ID, 'sel6') self._nonsel1 = self.marionette.find_element(By.ID, 'nonsel1') def open_test_html_long_text(self, enabled=True): 'Open html for testing and enable selectioncaret' self.set_pref(self.carets_tested_pref, enabled) self.set_pref(self.carets_disabled_pref, False) test_html = self.marionette.absolute_url('test_selectioncarets_longtext.html') self.marionette.navigate(test_html) self._body = self.marionette.find_element(By.ID, 'bd') self._longtext = self.marionette.find_element(By.ID, 'longtext') def open_test_html_iframe(self, enabled=True): 'Open html for testing and enable selectioncaret' self.set_pref(self.carets_tested_pref, enabled) self.set_pref(self.carets_disabled_pref, False) test_html = self.marionette.absolute_url('test_selectioncarets_iframe.html') self.marionette.navigate(test_html) self._iframe = self.marionette.find_element(By.ID, 'frame') def word_location(self, el, ordinal): '''Get the location (x, y) of the ordinal-th word in el. The ordinal starts from 0. Note: this function has a side effect which changes focus to the target element el. ''' sel = SelectionManager(el) tokens = re.split(r'(\S+)', sel.content) # both words and spaces words = tokens[0::2] # collect words at even indices spaces = tokens[1::2] # collect spaces at odd indices self.assertTrue(ordinal < len(words), 'Expect at least %d words in the content.' % ordinal) # Cursor position of the targeting word is behind the the first # character in the word. For example, offset to 'def' in 'abc def' is # between 'd' and 'e'. offset = sum(len(words[i]) + len(spaces[i]) for i in range(ordinal)) + 1 # Move caret to the word. el.tap() sel.move_caret_to_front() sel.move_caret_by_offset(offset) x, y = sel.caret_location() return x, y def rect_relative_to_window(self, el): '''Get element's bounding rectangle. This function is similar to el.rect, but the coordinate is relative to the top left corner of the window instead of the document. ''' return self.marionette.execute_script(''' let rect = arguments[0].getBoundingClientRect(); return {x: rect.x, y:rect.y, width: rect.width, height: rect.height}; ''', script_args=[el]) def long_press_on_location(self, el, x=None, y=None): '''Long press the location (x, y) to select a word. If no (x, y) are given, it will be targeted at the center of the element. On Windows, those spaces after the word will also be selected. This function sends synthesized eMouseLongTap to gecko. ''' rect = self.rect_relative_to_window(el) target_x = rect['x'] + (x if x is not None else rect['width'] // 2) target_y = rect['y'] + (y if y is not None else rect['height'] // 2) self.marionette.execute_script(''' let Ci = Components.interfaces; let utils = window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); utils.sendTouchEventToWindow('touchstart', [0], [arguments[0]], [arguments[1]], [1], [1], [0], [1], 1, 0); utils.sendMouseEventToWindow('mouselongtap', arguments[0], arguments[1], 0, 1, 0); utils.sendTouchEventToWindow('touchend', [0], [arguments[0]], [arguments[1]], [1], [1], [0], [1], 1, 0); ''', script_args=[target_x, target_y], sandbox='system') def long_press_on_word(self, el, wordOrdinal): x, y = self.word_location(el, wordOrdinal) self.long_press_on_location(el, x, y) def to_unix_line_ending(self, s): """Changes all Windows/Mac line endings in s to UNIX line endings.""" return s.replace('\r\n', '\n').replace('\r', '\n') def _test_long_press_to_select_a_word(self, el, assertFunc): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 2, 'Expect at least two words in the content.') target_content = words[0] # Goal: Select the first word. self.long_press_on_word(el, 0) # Ignore extra spaces selected after the word. assertFunc(target_content, sel.selected_content.rstrip()) def _test_move_selection_carets(self, el, assertFunc): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.') # Goal: Select all text after the first word. target_content = original_content[len(words[0]):] # Get the location of the selection carets at the end of the content for # later use. el.tap() sel.select_all() (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() self.long_press_on_word(el, 0) # Move the right caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Move the left caret to the previous position of the right caret. self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() # Ignore extra spaces at the beginning of the content in comparison. assertFunc(target_content.lstrip(), sel.selected_content.lstrip()) def _test_minimum_select_one_character(self, el, assertFunc, x=None, y=None): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.') # Get the location of the selection carets at the end of the content for # later use. sel.select_all() (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() el.tap() # Goal: Select the first character. target_content = original_content[0] if x and y: # If we got x and y from the arguments, use it as a hint of the # location of the first word pass else: x, y = self.word_location(el, 0) self.long_press_on_location(el, x, y) # Move the right caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Move the right caret to the position of the left caret. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform() assertFunc(target_content, sel.selected_content) def _test_focus_obtained_by_long_press(self, el1, el2): '''Test the focus could be changed from el1 to el2 by long press. If the focus is changed to e2 successfully, SelectionCarets should appear and could be dragged. ''' # Goal: Tap to focus el1, and then select the first character on # el2. # We want to collect the location of the first word in el2 here # since self.word_location() has the side effect which would # change the focus. x, y = self.word_location(el2, 0) el1.tap() self._test_minimum_select_one_character(el2, self.assertEqual, x=x, y=y) def _test_handle_tilt_when_carets_overlap_to_each_other(self, el, assertFunc): '''Test tilt handling when carets overlap to each other. Let SelectionCarets overlap to each other. If SelectionCarets are set to tilted successfully, tapping the tilted carets should not cause the selection to be collapsed and the carets should be draggable. ''' sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.') # Goal: Select the first word. self.long_press_on_word(el, 0) target_content = sel.selected_content # Move the left caret to the position of the right caret to trigger # carets overlapping. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() # We make two hit tests targeting the left edge of the left tilted caret # and the right edge of the right tilted caret. If either of the hits is # missed, selection would be collapsed and both carets should not be # draggable. (caret3_x, caret3_y), (caret4_x, caret4_y) = sel.selection_carets_location() # The following values are from ua.css. caret_width = 44 caret_margin_left = -23 tilt_right_margin_left = 18 tilt_left_margin_left = -17 left_caret_left_edge_x = caret3_x + caret_margin_left + tilt_left_margin_left el.tap(left_caret_left_edge_x + 2, caret3_y) right_caret_right_edge_x = (caret4_x + caret_margin_left + tilt_right_margin_left + caret_width) el.tap(right_caret_right_edge_x - 2, caret4_y) # Drag the left caret back to the initial selection, the first word. self.actions.flick(el, caret3_x, caret3_y, caret1_x, caret1_y).perform() assertFunc(target_content, sel.selected_content) def test_long_press_to_select_non_selectable_word(self): '''Testing long press on non selectable field. We should not select anything when long press on non selectable fields.''' self.open_test_html_multirange() halfY = self._nonsel1.size['height'] / 2 self.long_press_on_location(self._nonsel1, 0, halfY) sel = SelectionManager(self._nonsel1) range_count = sel.range_count() self.assertEqual(range_count, 0) def test_drag_caret_over_non_selectable_field(self): '''Testing drag caret over non selectable field. So that the selected content should exclude non selectable field and end selection caret should appear in last range's position.''' self.open_test_html_multirange() # Select target element and get target caret location self.long_press_on_word(self._sel4, 3) sel = SelectionManager(self._body) (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() self.long_press_on_word(self._sel6, 0) (_, _), (end_caret2_x, end_caret2_y) = sel.selection_carets_location() # Select start element self.long_press_on_word(self._sel3, 3) # Drag end caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), 'this 3\nuser can select this') (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, end_caret2_x, end_caret2_y, 1).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), 'this 3\nuser can select this 4\nuser can select this 5\nuser') # Drag first caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret1_x, caret1_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), '4\nuser can select this 5\nuser') def test_drag_caret_to_beginning_of_a_line(self): '''Bug 1094056 Test caret visibility when caret is dragged to beginning of a line ''' self.open_test_html_multirange() # Select the first word in the second line self.long_press_on_word(self._sel2, 0) sel = SelectionManager(self._body) (start_caret_x, start_caret_y), (end_caret_x, end_caret_y) = sel.selection_carets_location() # Select target word in the first line self.long_press_on_word(self._sel1, 2) # Drag end caret to the beginning of the second line (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, start_caret_x, start_caret_y).perform() # Drag end caret back to the target word self.actions.flick(self._body, start_caret_x, start_caret_y, caret2_x, caret2_y).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), 'select') @skip_if_not_rotatable def test_caret_position_after_changing_orientation_of_device(self): '''Bug 1094072 If positions of carets are updated correctly, they should be draggable. ''' self.open_test_html_long_text() # Select word in portrait mode, then change to landscape mode self.marionette.set_orientation('portrait') self.long_press_on_word(self._longtext, 12) sel = SelectionManager(self._body) (p_start_caret_x, p_start_caret_y), (p_end_caret_x, p_end_caret_y) = sel.selection_carets_location() self.marionette.set_orientation('landscape') (l_start_caret_x, l_start_caret_y), (l_end_caret_x, l_end_caret_y) = sel.selection_carets_location() # Drag end caret to the start caret to change the selected content self.actions.flick(self._body, l_end_caret_x, l_end_caret_y, l_start_caret_x, l_start_caret_y).perform() # Change orientation back to portrait mode to prevent affecting # other tests self.marionette.set_orientation('portrait') self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), 'o') def test_select_word_inside_an_iframe(self): '''Bug 1088552 The scroll offset in iframe should be taken into consideration properly. In this test, we scroll content in the iframe to the bottom to cause a huge offset. If we use the right coordinate system, selection should work. Otherwise, it would be hard to trigger select word. ''' self.open_test_html_iframe() # switch to inner iframe and scroll to the bottom self.marionette.switch_to_frame(self._iframe) self.marionette.execute_script( 'document.getElementById("bd").scrollTop += 999') # long press to select bottom text self._body = self.marionette.find_element(By.ID, 'bd') sel = SelectionManager(self._body) self._bottomtext = self.marionette.find_element(By.ID, 'bottomtext') self.long_press_on_location(self._bottomtext) self.assertNotEqual(self.to_unix_line_ending(sel.selected_content.strip()), '') ######################################################################## # <input> test cases with selection carets enabled ######################################################################## def test_input_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._input, self.assertEqual) def test_input_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._input, self.assertEqual) def test_input_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._input, self.assertEqual) def test_input_focus_obtained_by_long_press_from_textarea(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._input) def test_input_focus_obtained_by_long_press_from_contenteditable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._input) def test_input_focus_obtained_by_long_press_from_content_non_editable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._input) def test_input_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other(self._input, self.assertEqual) ######################################################################## # <input> test cases with selection carets disabled ######################################################################## def test_input_long_press_to_select_a_word_disabled(self): self.open_test_html(enabled=False) self._test_long_press_to_select_a_word(self._input, self.assertNotEqual) def test_input_move_selection_carets_disabled(self): self.open_test_html(enabled=False) self._test_move_selection_carets(self._input, self.assertNotEqual) ######################################################################## # <textarea> test cases with selection carets enabled ######################################################################## def test_textarea_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea, self.assertEqual) def test_textarea_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._textarea, self.assertEqual) def test_textarea_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._textarea, self.assertEqual) def test_textarea_focus_obtained_by_long_press_from_input(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._textarea) def test_textarea_focus_obtained_by_long_press_from_contenteditable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._textarea) def test_textarea_focus_obtained_by_long_press_from_content_non_editable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._textarea) def test_textarea_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other(self._textarea, self.assertEqual) ######################################################################## # <textarea> test cases with selection carets disabled ######################################################################## def test_textarea_long_press_to_select_a_word_disabled(self): self.open_test_html(enabled=False) self._test_long_press_to_select_a_word(self._textarea, self.assertNotEqual) def test_textarea_move_selection_carets_disable(self): self.open_test_html(enabled=False) self._test_move_selection_carets(self._textarea, self.assertNotEqual) ######################################################################## # <textarea> right-to-left test cases with selection carets enabled ######################################################################## def test_textarea_rtl_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._textarea_rtl, self.assertEqual) ######################################################################## # <textarea> right-to-left test cases with selection carets disabled ######################################################################## def test_textarea_rtl_long_press_to_select_a_word_disabled(self): self.open_test_html(enabled=False) self._test_long_press_to_select_a_word(self._textarea_rtl, self.assertNotEqual) def test_textarea_rtl_move_selection_carets_disabled(self): self.open_test_html(enabled=False) self._test_move_selection_carets(self._textarea_rtl, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with selection carets enabled ######################################################################## def test_contenteditable_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._contenteditable, self.assertEqual) def test_contenteditable_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._contenteditable, self.assertEqual) def test_contenteditable_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._contenteditable, self.assertEqual) def test_contenteditable_focus_obtained_by_long_press_from_input(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._contenteditable) def test_contenteditable_focus_obtained_by_long_press_from_textarea(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._contenteditable) def test_contenteditable_focus_obtained_by_long_press_from_content_non_editable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._contenteditable) def test_contenteditable_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other(self._contenteditable, self.assertEqual) ######################################################################## # <div> contenteditable test cases with selection carets disabled ######################################################################## def test_contenteditable_long_press_to_select_a_word_disabled(self): self.open_test_html(enabled=False) self._test_long_press_to_select_a_word(self._contenteditable, self.assertNotEqual) def test_contenteditable_move_selection_carets_disabled(self): self.open_test_html(enabled=False) self._test_move_selection_carets(self._contenteditable, self.assertNotEqual) ######################################################################## # <div> non-editable test cases with selection carets enabled ######################################################################## def test_content_non_editable_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._content, self.assertEqual) def test_content_non_editable_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._content, self.assertEqual) def test_content_non_editable_minimum_select_one_character_by_selection(self): self.open_test_html() self._test_minimum_select_one_character(self._content, self.assertEqual) def test_content_non_editable_focus_obtained_by_long_press_from_input(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._content) def test_content_non_editable_focus_obtained_by_long_press_from_textarea(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._content) def test_content_non_editable_focus_obtained_by_long_press_from_contenteditable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._content) def test_content_non_editable_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other(self._content, self.assertEqual) ######################################################################## # <textarea> (multi-lines) test cases with selection carets enabled ######################################################################## def test_textarea2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._textarea2, self.assertEqual) ######################################################################## # <div> contenteditable2 (multi-lines) test cases with selection carets enabled ######################################################################## def test_contenteditable2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._contenteditable2, self.assertEqual) ######################################################################## # <div> non-editable2 (multi-lines) test cases with selection carets enabled ######################################################################## def test_content_non_editable2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._content2, self.assertEqual)
class AccessibleCaretCursorModeTestCase(MarionetteTestCase): '''Test cases for AccessibleCaret under cursor mode. We call the blinking cursor (nsCaret) as cursor, and call AccessibleCaret as caret for short. ''' # Element IDs. _input_id = 'input' _input_padding_id = 'input-padding' _textarea_id = 'textarea' _textarea_one_line_id = 'textarea-one-line' _contenteditable_id = 'contenteditable' # Test html files. _cursor_html = 'test_carets_cursor.html' def setUp(self): # Code to execute before every test is running. super(AccessibleCaretCursorModeTestCase, self).setUp() self.caret_tested_pref = 'layout.accessiblecaret.enabled' self.caret_timeout_ms_pref = 'layout.accessiblecaret.timeout_ms' self.hide_carets_for_mouse = 'layout.accessiblecaret.hide_carets_for_mouse_input' self.prefs = { self.caret_tested_pref: True, self.caret_timeout_ms_pref: 0, self.hide_carets_for_mouse: False, } self.marionette.set_prefs(self.prefs) self.actions = Actions(self.marionette) def open_test_html(self, test_html): self.marionette.navigate(self.marionette.absolute_url(test_html)) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) def test_move_cursor_to_the_right_by_one_character(self, el_id): self.open_test_html(self._cursor_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) content_to_add = '!' target_content = sel.content target_content = target_content[:1] + content_to_add + target_content[1:] # Get first caret (x, y) at position 1 and 2. el.tap() sel.move_cursor_to_front() cursor0_x, cursor0_y = sel.cursor_location() first_caret0_x, first_caret0_y = sel.first_caret_location() sel.move_cursor_by_offset(1) first_caret1_x, first_caret1_y = sel.first_caret_location() # Tap the front of the input to make first caret appear. el.tap(cursor0_x, cursor0_y) # Move first caret. self.actions.flick(el, first_caret0_x, first_caret0_y, first_caret1_x, first_caret1_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() self.assertEqual(target_content, sel.content) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) def test_move_cursor_to_end_by_dragging_caret_to_bottom_right_corner(self, el_id): self.open_test_html(self._cursor_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) content_to_add = '!' target_content = sel.content + content_to_add # Tap the front of the input to make first caret appear. el.tap() sel.move_cursor_to_front() el.tap(*sel.cursor_location()) # Move first caret to the bottom-right corner of the element. src_x, src_y = sel.first_caret_location() dest_x, dest_y = el.size['width'], el.size['height'] self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() self.assertEqual(target_content, sel.content) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) def test_move_cursor_to_front_by_dragging_caret_to_front(self, el_id): self.open_test_html(self._cursor_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) content_to_add = '!' target_content = content_to_add + sel.content # Get first caret location at the front. el.tap() sel.move_cursor_to_front() dest_x, dest_y = sel.first_caret_location() # Tap to make first caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_cursor_to_end() sel.move_cursor_by_offset(1, backward=True) el.tap(*sel.cursor_location()) src_x, src_y = sel.first_caret_location() # Move first caret to the front of the input box. self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() self.assertEqual(target_content, sel.content) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) def test_dragging_caret_to_top_left_corner_after_timeout(self, el_id): self.open_test_html(self._cursor_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) content_to_add = '!' non_target_content = content_to_add + sel.content # Set caret timeout to be 1 second. timeout = 1 self.marionette.set_pref(self.caret_timeout_ms_pref, timeout * 1000) # Set a 3x timeout margin to prevent intermittent test failures. timeout *= 3 # Tap to make first caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_cursor_to_end() sel.move_cursor_by_offset(1, backward=True) el.tap(*sel.cursor_location()) # Wait until first caret disappears, then pretend to move it to the # top-left corner of the input box. src_x, src_y = sel.first_caret_location() dest_x, dest_y = 0, 0 self.actions.wait(timeout).flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() self.assertNotEqual(non_target_content, sel.content) def test_caret_not_appear_when_typing_in_scrollable_content(self): self.open_test_html(self._cursor_html) el = self.marionette.find_element(By.ID, self._input_id) sel = SelectionManager(el) content_to_add = '!' target_content = sel.content + string.ascii_letters + content_to_add el.tap() sel.move_cursor_to_end() # Insert a long string to the end of the <input>, which triggers # ScrollPositionChanged event. el.send_keys(string.ascii_letters) # The caret should not be visible. If it does appear wrongly due to the # ScrollPositionChanged event, we can drag it to the front of the # <input> to change the cursor position. src_x, src_y = sel.first_caret_location() dest_x, dest_y = 0, 0 self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() # The content should be inserted at the end of the <input>. el.send_keys(content_to_add) self.assertEqual(target_content, sel.content) @parameterized(_input_id, el_id=_input_id) @parameterized(_input_padding_id, el_id=_input_padding_id) @parameterized(_textarea_one_line_id, el_id=_textarea_one_line_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) def test_caret_not_jump_when_dragging_to_editable_content_boundary(self, el_id): self.open_test_html(self._cursor_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) content_to_add = '!' non_target_content = sel.content + content_to_add # Goal: the cursor position is not changed after dragging the caret down # on the Y-axis. el.tap() sel.move_cursor_to_front() el.tap(*sel.cursor_location()) x, y = sel.first_caret_location() # Drag the caret down by 50px, and insert '!'. self.actions.flick(el, x, y, x, y + 50).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() self.assertNotEqual(non_target_content, sel.content) @parameterized(_input_id, el_id=_input_id) @parameterized(_input_padding_id, el_id=_input_padding_id) @parameterized(_textarea_one_line_id, el_id=_textarea_one_line_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) def test_caret_not_jump_to_front_when_dragging_up_to_editable_content_boundary(self, el_id): self.open_test_html(self._cursor_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) content_to_add = '!' non_target_content = content_to_add + sel.content # Goal: the cursor position is not changed after dragging the caret down # on the Y-axis. el.tap() sel.move_cursor_to_end() sel.move_cursor_by_offset(1, backward=True) el.tap(*sel.cursor_location()) x, y = sel.first_caret_location() # Drag the caret up by 50px, and insert '!'. self.actions.flick(el, x, y, x, y - 50).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() self.assertNotEqual(non_target_content, sel.content) def test_drag_caret_from_front_to_end_across_columns(self): self.open_test_html('test_carets_columns.html') el = self.marionette.find_element(By.ID, 'columns-inner') sel = SelectionManager(el) content_to_add = '!' target_content = sel.content + content_to_add # Goal: the cursor position can be changed by dragging the caret from # the front to the end of the content. # Tap to make the cursor appear. before_image_1 = self.marionette.find_element(By.ID, 'before-image-1') before_image_1.tap() # Tap the front of the content to make first caret appear. sel.move_cursor_to_front() el.tap(*sel.cursor_location()) src_x, src_y = sel.first_caret_location() dest_x, dest_y = el.size['width'], el.size['height'] # Drag the first caret to the bottom-right corner of the element. self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() self.assertEqual(target_content, sel.content) def test_move_cursor_to_front_by_dragging_caret_to_front_br_element(self): self.open_test_html(self._cursor_html) el = self.marionette.find_element(By.ID, self._contenteditable_id) sel = SelectionManager(el) content_to_add_1 = '!' content_to_add_2 = '\n\n' target_content = content_to_add_1 + content_to_add_2 + sel.content # Goal: the cursor position can be changed by dragging the caret from # the end of the content to the front br element. Because we cannot get # caret location if it's on a br element, we need to get the first caret # location then adding the new lines. # Get first caret location at the front. el.tap() sel.move_cursor_to_front() dest_x, dest_y = sel.first_caret_location() # Append new line to the front of the content. el.send_keys(content_to_add_2); # Tap to make first caret appear. el.tap() sel.move_cursor_to_end() sel.move_cursor_by_offset(1, backward=True) el.tap(*sel.cursor_location()) src_x, src_y = sel.first_caret_location() # Move first caret to the front of the input box. self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add_1).key_up(content_to_add_1).perform() self.assertEqual(target_content, sel.content)
class CommonCaretTestCase(object): '''Common test cases for a collapsed selection with a single caret. To run these test cases, a subclass must inherit from both this class and MarionetteTestCase. ''' _input_selector = (By.ID, 'input') _textarea_selector = (By.ID, 'textarea') _contenteditable_selector = (By.ID, 'contenteditable') def setUp(self): # Code to execute before a test is being run. super(CommonCaretTestCase, self).setUp() self.actions = Actions(self.marionette) # The caret to be tested. self.caret_tested_pref = None # The caret to be disabled in this test suite. self.caret_disabled_pref = None self.caret_timeout_ms_pref = None def set_pref(self, pref_name, value): '''Set a preference to value. For example: >>> set_pref('layout.accessiblecaret.enabled', True) ''' pref_name = repr(pref_name) if isinstance(value, bool): value = 'true' if value else 'false' func = 'setBoolPref' elif isinstance(value, int): value = str(value) func = 'setIntPref' else: value = repr(value) func = 'setCharPref' with self.marionette.using_context('chrome'): script = 'Services.prefs.%s(%s, %s)' % (func, pref_name, value) self.marionette.execute_script(script) def timeout_ms(self): 'Return touch caret expiration time in milliseconds.' with self.marionette.using_context('chrome'): return self.marionette.execute_script( 'return Services.prefs.getIntPref("%s");' % self.caret_timeout_ms_pref) def open_test_html(self, enabled=True, timeout_ms=0): '''Open html for testing and locate elements, enable/disable touch caret, and set touch caret expiration time in milliseconds). ''' self.set_pref(self.caret_tested_pref, enabled) self.set_pref(self.caret_disabled_pref, False) self.set_pref(self.caret_timeout_ms_pref, timeout_ms) test_html = self.marionette.absolute_url('test_touchcaret.html') self.marionette.navigate(test_html) self._input = self.marionette.find_element(*self._input_selector) self._textarea = self.marionette.find_element(*self._textarea_selector) self._contenteditable = self.marionette.find_element(*self._contenteditable_selector) def _test_move_caret_to_the_right_by_one_character(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = sel.content target_content = target_content[:1] + content_to_add + target_content[1:] # Get touch caret (x, y) at position 1 and 2. el.tap() sel.move_caret_to_front() caret0_x, caret0_y = sel.caret_location() touch_caret0_x, touch_caret0_y = sel.touch_caret_location() sel.move_caret_by_offset(1) touch_caret1_x, touch_caret1_y = sel.touch_caret_location() # Tap the front of the input to make touch caret appear. el.tap(caret0_x, caret0_y) # Move touch caret self.actions.flick(el, touch_caret0_x, touch_caret0_y, touch_caret1_x, touch_caret1_y).perform() el.send_keys(content_to_add) assertFunc(target_content, sel.content) def _test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = sel.content + content_to_add # Tap the front of the input to make touch caret appear. el.tap() sel.move_caret_to_front() el.tap(*sel.caret_location()) # Move touch caret to the bottom-right corner of the element. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = el.size['width'], el.size['height'] self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() el.send_keys(content_to_add) assertFunc(target_content, sel.content) def _test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = content_to_add + sel.content # Get touch caret location at the front. el.tap() sel.move_caret_to_front() dest_x, dest_y = sel.touch_caret_location() # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) src_x, src_y = sel.touch_caret_location() # Move touch caret to the front of the input box. self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() el.send_keys(content_to_add) assertFunc(target_content, sel.content) def _test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' non_target_content = content_to_add + sel.content # Get touch caret expiration time in millisecond, and convert it to second. timeout = self.timeout_ms() / 1000.0 # Set a 3x timeout margin to prevent intermittent test failures. timeout *= 3 # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) # Wait until touch caret disappears, then pretend to move it to the # top-left corner of the input box. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 self.actions.wait(timeout).flick(el, src_x, src_y, dest_x, dest_y).perform() el.send_keys(content_to_add) assertFunc(non_target_content, sel.content) def _test_touch_caret_hides_after_receiving_wheel_event(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' non_target_content = content_to_add + sel.content # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) # Send an arbitrary scroll-down-10px wheel event to the center of the # input box to hide touch caret. Then pretend to move it to the top-left # corner of the input box. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 el_center_x, el_center_y = el.rect['x'], el.rect['y'] self.marionette.execute_script( ''' var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindowUtils); utils.sendWheelEvent(arguments[0], arguments[1], 0, 10, 0, WheelEvent.DOM_DELTA_PIXEL, 0, 0, 0, 0); ''', script_args=[el_center_x, el_center_y], sandbox='system' ) self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() el.send_keys(content_to_add) assertFunc(non_target_content, sel.content) ######################################################################## # <input> test cases with touch caret enabled ######################################################################## def test_input_move_caret_to_the_right_by_one_character(self): self.open_test_html() self._test_move_caret_to_the_right_by_one_character(self._input, self.assertEqual) def test_input_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self): self.open_test_html() self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._input, self.assertEqual) def test_input_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self._input, self.assertEqual) def test_input_touch_caret_timeout(self): self.open_test_html(timeout_ms=1000) self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self._input, self.assertNotEqual) def test_input_touch_caret_hides_after_receiving_wheel_event(self): self.open_test_html() self._test_touch_caret_hides_after_receiving_wheel_event(self._input, self.assertNotEqual) ######################################################################## # <input> test cases with touch caret disabled ######################################################################## def test_input_move_caret_to_the_right_by_one_character_disabled(self): self.open_test_html(enabled=False) self._test_move_caret_to_the_right_by_one_character(self._input, self.assertNotEqual) def test_input_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self): self.open_test_html(enabled=False) self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self._input, self.assertNotEqual) ######################################################################## # <textarea> test cases with touch caret enabled ######################################################################## def test_textarea_move_caret_to_the_right_by_one_character(self): self.open_test_html() self._test_move_caret_to_the_right_by_one_character(self._textarea, self.assertEqual) def test_textarea_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self): self.open_test_html() self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._textarea, self.assertEqual) def test_textarea_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self._textarea, self.assertEqual) def test_textarea_touch_caret_timeout(self): self.open_test_html(timeout_ms=1000) self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self._textarea, self.assertNotEqual) def test_textarea_touch_caret_hides_after_receiving_wheel_event(self): self.open_test_html() self._test_touch_caret_hides_after_receiving_wheel_event(self._textarea, self.assertNotEqual) ######################################################################## # <textarea> test cases with touch caret disabled ######################################################################## def test_textarea_move_caret_to_the_right_by_one_character_disabled(self): self.open_test_html(enabled=False) self._test_move_caret_to_the_right_by_one_character(self._textarea, self.assertNotEqual) def test_textarea_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self): self.open_test_html(enabled=False) self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self._textarea, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with touch caret enabled ######################################################################## def test_contenteditable_move_caret_to_the_right_by_one_character(self): self.open_test_html() self._test_move_caret_to_the_right_by_one_character(self._contenteditable, self.assertEqual) def test_contenteditable_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self): self.open_test_html() self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._contenteditable, self.assertEqual) def test_contenteditable_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self._contenteditable, self.assertEqual) def test_contenteditable_touch_caret_timeout(self): self.open_test_html(timeout_ms=1000) self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self._contenteditable, self.assertNotEqual) def test_contenteditable_touch_caret_hides_after_receiving_wheel_event(self): self.open_test_html() self._test_touch_caret_hides_after_receiving_wheel_event(self._contenteditable, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with touch caret disabled ######################################################################## def test_contenteditable_move_caret_to_the_right_by_one_character_disabled(self): self.open_test_html(enabled=False) self._test_move_caret_to_the_right_by_one_character(self._contenteditable, self.assertNotEqual) def test_contenteditable_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self): self.open_test_html(enabled=False) self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self._contenteditable, self.assertNotEqual)
class CommonCaretsTestCase2(object): '''Common test cases for a selection with a two carets. To run these test cases, a subclass must inherit from both this class and MarionetteTestCase. ''' _long_press_time = 1 # 1 second def setUp(self): # Code to execute before a tests are run. MarionetteTestCase.setUp(self) self.actions = Actions(self.marionette) # The carets to be tested. self.carets_tested_pref = None # The carets to be disabled in this test suite. self.carets_disabled_pref = None def set_pref(self, pref_name, value): '''Set a preference to value. For example: >>> set_pref('layout.accessiblecaret.enabled', True) ''' pref_name = repr(pref_name) if isinstance(value, bool): value = 'true' if value else 'false' func = 'setBoolPref' elif isinstance(value, int): value = str(value) func = 'setIntPref' else: value = repr(value) func = 'setCharPref' with self.marionette.using_context('chrome'): script = 'Services.prefs.%s(%s, %s)' % (func, pref_name, value) self.marionette.execute_script(script) def open_test_html(self, enabled=True): 'Open html for testing and enable selectioncaret and non-editable support' self.set_pref(self.carets_tested_pref, enabled) self.set_pref(self.carets_disabled_pref, False) test_html = self.marionette.absolute_url('test_selectioncarets_multiplerange.html') self.marionette.navigate(test_html) self._body = self.marionette.find_element(By.ID, 'bd') self._sel1 = self.marionette.find_element(By.ID, 'sel1') self._sel2 = self.marionette.find_element(By.ID, 'sel2') self._sel3 = self.marionette.find_element(By.ID, 'sel3') self._sel4 = self.marionette.find_element(By.ID, 'sel4') self._sel6 = self.marionette.find_element(By.ID, 'sel6') self._nonsel1 = self.marionette.find_element(By.ID, 'nonsel1') def open_test_html_long_text(self, enabled=True): 'Open html for testing and enable selectioncaret' self.set_pref(self.carets_tested_pref, enabled) self.set_pref(self.carets_disabled_pref, False) test_html = self.marionette.absolute_url('test_selectioncarets_longtext.html') self.marionette.navigate(test_html) self._body = self.marionette.find_element(By.ID, 'bd') self._longtext = self.marionette.find_element(By.ID, 'longtext') def open_test_html_iframe(self, enabled=True): 'Open html for testing and enable selectioncaret' self.set_pref(self.carets_tested_pref, enabled) self.set_pref(self.carets_disabled_pref, False) test_html = self.marionette.absolute_url('test_selectioncarets_iframe.html') self.marionette.navigate(test_html) self._iframe = self.marionette.find_element(By.ID, 'frame') def _long_press_to_select_word(self, el, wordOrdinal): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(wordOrdinal < len(words), 'Expect at least %d words in the content.' % wordOrdinal) # Calc offset offset = 0 for i in range(wordOrdinal): offset += (len(words[i]) + 1) # Move caret inside the word. el.tap() sel.move_caret_to_front() sel.move_caret_by_offset(offset) x, y = sel.caret_location() # Long press the caret position. Selection carets should appear, and the # word will be selected. On Windows, those spaces after the word # will also be selected. long_press_without_contextmenu(self.marionette, el, self._long_press_time, x, y) def _to_unix_line_ending(self, s): """Changes all Windows/Mac line endings in s to UNIX line endings.""" return s.replace('\r\n', '\n').replace('\r', '\n') def test_long_press_to_select_non_selectable_word(self): '''Testing long press on non selectable field. We should not select anything when long press on non selectable fields.''' self.open_test_html() halfY = self._nonsel1.size['height'] / 2 long_press_without_contextmenu(self.marionette, self._nonsel1, self._long_press_time, 0, halfY) sel = SelectionManager(self._nonsel1) range_count = sel.range_count() self.assertEqual(range_count, 0) def test_drag_caret_over_non_selectable_field(self): '''Testing drag caret over non selectable field. So that the selected content should exclude non selectable field and end selection caret should appear in last range's position.''' self.open_test_html() # Select target element and get target caret location self._long_press_to_select_word(self._sel4, 3) sel = SelectionManager(self._body) (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() self._long_press_to_select_word(self._sel6, 0) (_, _), (end_caret2_x, end_caret2_y) = sel.selection_carets_location() # Select start element self._long_press_to_select_word(self._sel3, 3) # Drag end caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()), 'this 3\nuser can select this') (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, end_caret2_x, end_caret2_y, 1).perform() self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()), 'this 3\nuser can select this 4\nuser can select this 5\nuser') # Drag first caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret1_x, caret1_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()), '4\nuser can select this 5\nuser') def test_drag_caret_to_beginning_of_a_line(self): '''Bug 1094056 Test caret visibility when caret is dragged to beginning of a line ''' self.open_test_html() # Select the first word in the second line self._long_press_to_select_word(self._sel2, 0) sel = SelectionManager(self._body) (start_caret_x, start_caret_y), (end_caret_x, end_caret_y) = sel.selection_carets_location() # Select target word in the first line self._long_press_to_select_word(self._sel1, 2) # Drag end caret to the beginning of the second line (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, start_caret_x, start_caret_y).perform() # Drag end caret back to the target word self.actions.flick(self._body, start_caret_x, start_caret_y, caret2_x, caret2_y).perform() self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()), 'select') @skip_if_not_rotatable def test_caret_position_after_changing_orientation_of_device(self): '''Bug 1094072 If positions of carets are updated correctly, they should be draggable. ''' self.open_test_html_long_text() # Select word in portrait mode, then change to landscape mode self.marionette.set_orientation('portrait') self._long_press_to_select_word(self._longtext, 12) sel = SelectionManager(self._body) (p_start_caret_x, p_start_caret_y), (p_end_caret_x, p_end_caret_y) = sel.selection_carets_location() self.marionette.set_orientation('landscape') (l_start_caret_x, l_start_caret_y), (l_end_caret_x, l_end_caret_y) = sel.selection_carets_location() # Drag end caret to the start caret to change the selected content self.actions.flick(self._body, l_end_caret_x, l_end_caret_y, l_start_caret_x, l_start_caret_y).perform() # Change orientation back to portrait mode to prevent affecting # other tests self.marionette.set_orientation('portrait') self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()), 'o') def test_select_word_inside_an_iframe(self): '''Bug 1088552 The scroll offset in iframe should be taken into consideration properly. In this test, we scroll content in the iframe to the bottom to cause a huge offset. If we use the right coordinate system, selection should work. Otherwise, it would be hard to trigger select word. ''' self.open_test_html_iframe() # switch to inner iframe and scroll to the bottom self.marionette.switch_to_frame(self._iframe) self.marionette.execute_script( 'document.getElementById("bd").scrollTop += 999') # long press to select bottom text self._body = self.marionette.find_element(By.ID, 'bd') sel = SelectionManager(self._body) self._bottomtext = self.marionette.find_element(By.ID, 'bottomtext') long_press_without_contextmenu(self.marionette, self._bottomtext, self._long_press_time) self.assertNotEqual(self._to_unix_line_ending(sel.selected_content.strip()), '')
class AccessibleCaretSelectionModeTestCase(MarionetteTestCase): """Test cases for AccessibleCaret under selection mode.""" # Element IDs. _input_id = "input" _input_padding_id = "input-padding" _textarea_id = "textarea" _textarea2_id = "textarea2" _textarea_one_line_id = "textarea-one-line" _textarea_rtl_id = "textarea-rtl" _contenteditable_id = "contenteditable" _contenteditable2_id = "contenteditable2" _content_id = "content" _content2_id = "content2" _non_selectable_id = "non-selectable" # Test html files. _selection_html = "test_carets_selection.html" _multipleline_html = "test_carets_multipleline.html" _multiplerange_html = "test_carets_multiplerange.html" _longtext_html = "test_carets_longtext.html" _iframe_html = "test_carets_iframe.html" _display_none_html = "test_carets_display_none.html" def setUp(self): # Code to execute before every test is running. super(AccessibleCaretSelectionModeTestCase, self).setUp() self.carets_tested_pref = "layout.accessiblecaret.enabled" self.prefs = {"layout.word_select.eat_space_to_next_word": False, self.carets_tested_pref: True} self.marionette.set_prefs(self.prefs) self.actions = Actions(self.marionette) def open_test_html(self, test_html): self.marionette.navigate(self.marionette.absolute_url(test_html)) def word_offset(self, text, ordinal): "Get the character offset of the ordinal-th word in text." tokens = re.split(r"(\S+)", text) # both words and spaces spaces = tokens[0::2] # collect spaces at odd indices words = tokens[1::2] # collect word at even indices if ordinal >= len(words): raise IndexError("Only %d words in text, but got ordinal %d" % (len(words), ordinal)) # Cursor position of the targeting word is behind the the first # character in the word. For example, offset to 'def' in 'abc def' is # between 'd' and 'e'. offset = len(spaces[0]) + 1 offset += sum(len(words[i]) + len(spaces[i + 1]) for i in range(ordinal)) return offset def test_word_offset(self): text = " " * 3 + "abc" + " " * 3 + "def" self.assertTrue(self.word_offset(text, 0), 4) self.assertTrue(self.word_offset(text, 1), 10) with self.assertRaises(IndexError): self.word_offset(text, 2) def word_location(self, el, ordinal): """Get the location (x, y) of the ordinal-th word in el. The ordinal starts from 0. Note: this function has a side effect which changes focus to the target element el. """ sel = SelectionManager(el) offset = self.word_offset(sel.content, ordinal) # Move the blinking cursor to the word. el.tap() sel.move_cursor_to_front() sel.move_cursor_by_offset(offset) x, y = sel.cursor_location() return x, y def rect_relative_to_window(self, el): """Get element's bounding rectangle. This function is similar to el.rect, but the coordinate is relative to the top left corner of the window instead of the document. """ return self.marionette.execute_script( """ let rect = arguments[0].getBoundingClientRect(); return {x: rect.x, y:rect.y, width: rect.width, height: rect.height}; """, script_args=[el], ) def long_press_on_location(self, el, x=None, y=None): """Long press the location (x, y) to select a word. If no (x, y) are given, it will be targeted at the center of the element. On Windows, those spaces after the word will also be selected. This function sends synthesized eMouseLongTap to gecko. """ rect = self.rect_relative_to_window(el) target_x = rect["x"] + (x if x is not None else rect["width"] // 2) target_y = rect["y"] + (y if y is not None else rect["height"] // 2) self.marionette.execute_script( """ let Ci = Components.interfaces; let utils = window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); utils.sendTouchEventToWindow('touchstart', [0], [arguments[0]], [arguments[1]], [1], [1], [0], [1], 1, 0); utils.sendMouseEventToWindow('mouselongtap', arguments[0], arguments[1], 0, 1, 0); utils.sendTouchEventToWindow('touchend', [0], [arguments[0]], [arguments[1]], [1], [1], [0], [1], 1, 0); """, script_args=[target_x, target_y], sandbox="system", ) def long_press_on_word(self, el, wordOrdinal): x, y = self.word_location(el, wordOrdinal) self.long_press_on_location(el, x, y) def to_unix_line_ending(self, s): """Changes all Windows/Mac line endings in s to UNIX line endings.""" return s.replace("\r\n", "\n").replace("\r", "\n") @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) @parameterized(_content_id, el_id=_content_id) def test_long_press_to_select_a_word(self, el_id): self.open_test_html(self._selection_html) el = self.marionette.find_element(By.ID, el_id) self._test_long_press_to_select_a_word(el) def _test_long_press_to_select_a_word(self, el): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 2, "Expect at least two words in the content.") target_content = words[0] # Goal: Select the first word. self.long_press_on_word(el, 0) # Ignore extra spaces selected after the word. self.assertEqual(target_content, sel.selected_content) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) @parameterized(_content_id, el_id=_content_id) def test_drag_carets(self, el_id): self.open_test_html(self._selection_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, "Expect at least one word in the content.") # Goal: Select all text after the first word. target_content = original_content[len(words[0]) :] # Get the location of the carets at the end of the content for later # use. el.tap() sel.select_all() end_caret_x, end_caret_y = sel.second_caret_location() self.long_press_on_word(el, 0) # Drag the second caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Drag the first caret to the previous position of the second caret. self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() self.assertEqual(target_content, sel.selected_content) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) @parameterized(_content_id, el_id=_content_id) def test_drag_swappable_carets(self, el_id): self.open_test_html(self._selection_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, "Expect at least one word in the content.") target_content1 = words[0] target_content2 = original_content[len(words[0]) :] # Get the location of the carets at the end of the content for later # use. el.tap() sel.select_all() end_caret_x, end_caret_y = sel.second_caret_location() self.long_press_on_word(el, 0) # Drag the first caret to the end and back to where it was # immediately. The selection range should not be collapsed. caret1_x, caret1_y = sel.first_caret_location() self.actions.flick(el, caret1_x, caret1_y, end_caret_x, end_caret_y).flick( el, end_caret_x, end_caret_y, caret1_x, caret1_y ).perform() self.assertEqual(target_content1, sel.selected_content) # Drag the first caret to the end. caret1_x, caret1_y = sel.first_caret_location() self.actions.flick(el, caret1_x, caret1_y, end_caret_x, end_caret_y).perform() self.assertEqual(target_content2, sel.selected_content) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) @parameterized(_content_id, el_id=_content_id) def test_minimum_select_one_character(self, el_id): self.open_test_html(self._selection_html) el = self.marionette.find_element(By.ID, el_id) self._test_minimum_select_one_character(el) @parameterized(_textarea2_id, el_id=_textarea2_id) @parameterized(_contenteditable2_id, el_id=_contenteditable2_id) @parameterized(_content2_id, el_id=_content2_id) def test_minimum_select_one_character2(self, el_id): self.open_test_html(self._multipleline_html) el = self.marionette.find_element(By.ID, el_id) self._test_minimum_select_one_character(el) def _test_minimum_select_one_character(self, el, x=None, y=None): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, "Expect at least one word in the content.") # Get the location of the carets at the end of the content for later # use. sel.select_all() end_caret_x, end_caret_y = sel.second_caret_location() el.tap() # Goal: Select the first character. target_content = original_content[0] if x and y: # If we got x and y from the arguments, use it as a hint of the # location of the first word pass else: x, y = self.word_location(el, 0) self.long_press_on_location(el, x, y) # Drag the second caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Drag the second caret to the position of the first caret. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform() self.assertEqual(target_content, sel.selected_content) @parameterized(_input_id + "_to_" + _textarea_id, el1_id=_input_id, el2_id=_textarea_id) @parameterized(_input_id + "_to_" + _contenteditable_id, el1_id=_input_id, el2_id=_contenteditable_id) @parameterized(_input_id + "_to_" + _content_id, el1_id=_input_id, el2_id=_content_id) @parameterized(_textarea_id + "_to_" + _input_id, el1_id=_textarea_id, el2_id=_input_id) @parameterized(_textarea_id + "_to_" + _contenteditable_id, el1_id=_textarea_id, el2_id=_contenteditable_id) @parameterized(_textarea_id + "_to_" + _content_id, el1_id=_textarea_id, el2_id=_content_id) @parameterized(_contenteditable_id + "_to_" + _input_id, el1_id=_contenteditable_id, el2_id=_input_id) @parameterized(_contenteditable_id + "_to_" + _textarea_id, el1_id=_contenteditable_id, el2_id=_textarea_id) @parameterized(_contenteditable_id + "_to_" + _content_id, el1_id=_contenteditable_id, el2_id=_content_id) @parameterized(_content_id + "_to_" + _input_id, el1_id=_content_id, el2_id=_input_id) @parameterized(_content_id + "_to_" + _textarea_id, el1_id=_content_id, el2_id=_textarea_id) @parameterized(_content_id + "_to_" + _contenteditable_id, el1_id=_content_id, el2_id=_contenteditable_id) def test_long_press_changes_focus_from(self, el1_id, el2_id): """Test the focus could be changed from el1 to el2 by long press. If the focus is changed to e2 successfully, the carets should appear and could be dragged. """ # Goal: Tap to focus el1, and then select the first character on # el2. # We want to collect the location of the first word in el2 here # since self.word_location() has the side effect which would # change the focus. self.open_test_html(self._selection_html) el1 = self.marionette.find_element(By.ID, el1_id) el2 = self.marionette.find_element(By.ID, el2_id) x, y = self.word_location(el2, 0) el1.tap() self._test_minimum_select_one_character(el2, x=x, y=y) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) def test_focus_not_changed_by_long_press_on_non_selectable(self, el_id): self.open_test_html(self._selection_html) el = self.marionette.find_element(By.ID, el_id) non_selectable = self.marionette.find_element(By.ID, self._non_selectable_id) # Goal: Focus remains on the editable element el after long pressing on # the non-selectable element. sel = SelectionManager(el) self.long_press_on_word(el, 0) self.long_press_on_location(non_selectable) active_sel = SelectionManager(self.marionette.get_active_element()) self.assertEqual(sel.content, active_sel.content) @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) @parameterized(_content_id, el_id=_content_id) def test_handle_tilt_when_carets_overlap_each_other(self, el_id): """Test tilt handling when carets overlap to each other. Let the two carets overlap each other. If they are set to tilted successfully, tapping the tilted carets should not cause the selection to be collapsed and the carets should be draggable. """ self.open_test_html(self._selection_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, "Expect at least one word in the content.") # Goal: Select the first word. self.long_press_on_word(el, 0) target_content = sel.selected_content # Drag the first caret to the position of the second caret to trigger # carets overlapping. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() # We make two hit tests targeting the left edge of the left tilted caret # and the right edge of the right tilted caret. If either of the hits is # missed, selection would be collapsed and both carets should not be # draggable. (caret3_x, caret3_y), (caret4_x, caret4_y) = sel.carets_location() # The following values are from ua.css and all.js caret_width = float(self.marionette.get_pref("layout.accessiblecaret.width")) caret_margin_left = float(self.marionette.get_pref("layout.accessiblecaret.margin-left")) tilt_right_margin_left = 0.41 * caret_width tilt_left_margin_left = -0.39 * caret_width left_caret_left_edge_x = caret3_x + caret_margin_left + tilt_left_margin_left el.tap(left_caret_left_edge_x + 2, caret3_y) right_caret_right_edge_x = caret4_x + caret_margin_left + tilt_right_margin_left + caret_width el.tap(right_caret_right_edge_x - 2, caret4_y) # Drag the first caret back to the initial selection, the first word. self.actions.flick(el, caret3_x, caret3_y, caret1_x, caret1_y).perform() self.assertEqual(target_content, sel.selected_content) def test_drag_caret_over_non_selectable_field(self): """Test dragging the caret over a non-selectable field. The selected content should exclude non-selectable elements and the second caret should appear in last range's position. """ self.open_test_html(self._multiplerange_html) body = self.marionette.find_element(By.ID, "bd") sel3 = self.marionette.find_element(By.ID, "sel3") sel4 = self.marionette.find_element(By.ID, "sel4") sel6 = self.marionette.find_element(By.ID, "sel6") # Select target element and get target caret location self.long_press_on_word(sel4, 3) sel = SelectionManager(body) end_caret_x, end_caret_y = sel.second_caret_location() self.long_press_on_word(sel6, 0) end_caret2_x, end_caret2_y = sel.second_caret_location() # Select start element self.long_press_on_word(sel3, 3) # Drag end caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(body, caret2_x, caret2_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), "this 3\nuser can select this") (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(body, caret2_x, caret2_y, end_caret2_x, end_caret2_y, 1).perform() self.assertEqual( self.to_unix_line_ending(sel.selected_content.strip()), "this 3\nuser can select this 4\nuser can select this 5\nuser", ) # Drag first caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(body, caret1_x, caret1_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), "4\nuser can select this 5\nuser") def test_drag_swappable_caret_over_non_selectable_field(self): self.open_test_html(self._multiplerange_html) body = self.marionette.find_element(By.ID, "bd") sel3 = self.marionette.find_element(By.ID, "sel3") sel4 = self.marionette.find_element(By.ID, "sel4") sel = SelectionManager(body) self.long_press_on_word(sel4, 3) (end_caret1_x, end_caret1_y), (end_caret2_x, end_caret2_y) = sel.carets_location() self.long_press_on_word(sel3, 3) (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() # Drag the first caret down, which will across the second caret. self.actions.flick(body, caret1_x, caret1_y, end_caret1_x, end_caret1_y).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), "3\nuser can select") # The old second caret becomes the first caret. Drag it down again. self.actions.flick(body, caret2_x, caret2_y, end_caret2_x, end_caret2_y).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), "this") def test_drag_caret_to_beginning_of_a_line(self): """Bug 1094056 Test caret visibility when caret is dragged to beginning of a line """ self.open_test_html(self._multiplerange_html) body = self.marionette.find_element(By.ID, "bd") sel1 = self.marionette.find_element(By.ID, "sel1") sel2 = self.marionette.find_element(By.ID, "sel2") # Select the first word in the second line self.long_press_on_word(sel2, 0) sel = SelectionManager(body) (start_caret_x, start_caret_y), (end_caret_x, end_caret_y) = sel.carets_location() # Select target word in the first line self.long_press_on_word(sel1, 2) # Drag end caret to the beginning of the second line (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(body, caret2_x, caret2_y, start_caret_x, start_caret_y).perform() # Drag end caret back to the target word self.actions.flick(body, start_caret_x, start_caret_y, caret2_x, caret2_y).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content), "select") @skip_if_not_rotatable def test_caret_position_after_changing_orientation_of_device(self): """Bug 1094072 If positions of carets are updated correctly, they should be draggable. """ self.open_test_html(self._longtext_html) body = self.marionette.find_element(By.ID, "bd") longtext = self.marionette.find_element(By.ID, "longtext") # Select word in portrait mode, then change to landscape mode self.marionette.set_orientation("portrait") self.long_press_on_word(longtext, 12) sel = SelectionManager(body) (p_start_caret_x, p_start_caret_y), (p_end_caret_x, p_end_caret_y) = sel.carets_location() self.marionette.set_orientation("landscape") (l_start_caret_x, l_start_caret_y), (l_end_caret_x, l_end_caret_y) = sel.carets_location() # Drag end caret to the start caret to change the selected content self.actions.flick(body, l_end_caret_x, l_end_caret_y, l_start_caret_x, l_start_caret_y).perform() # Change orientation back to portrait mode to prevent affecting # other tests self.marionette.set_orientation("portrait") self.assertEqual(self.to_unix_line_ending(sel.selected_content), "o") def test_select_word_inside_an_iframe(self): """Bug 1088552 The scroll offset in iframe should be taken into consideration properly. In this test, we scroll content in the iframe to the bottom to cause a huge offset. If we use the right coordinate system, selection should work. Otherwise, it would be hard to trigger select word. """ self.open_test_html(self._iframe_html) iframe = self.marionette.find_element(By.ID, "frame") # switch to inner iframe and scroll to the bottom self.marionette.switch_to_frame(iframe) self.marionette.execute_script('document.getElementById("bd").scrollTop += 999') # long press to select bottom text body = self.marionette.find_element(By.ID, "bd") sel = SelectionManager(body) self._bottomtext = self.marionette.find_element(By.ID, "bottomtext") self.long_press_on_location(self._bottomtext) self.assertNotEqual(self.to_unix_line_ending(sel.selected_content), "") def test_carets_initialized_in_display_none(self): """Test AccessibleCaretEventHub is properly initialized on a <html> with display: none. """ self.open_test_html(self._display_none_html) html = self.marionette.find_element(By.ID, "html") content = self.marionette.find_element(By.ID, "content") # Remove 'display: none' from <html> self.marionette.execute_script('arguments[0].style.display = "unset";', script_args=[html]) # If AccessibleCaretEventHub is initialized successfully, select a word # should work. self._test_long_press_to_select_a_word(content) def test_long_press_to_select_when_partial_visible_word_is_selected(self): self.open_test_html(self._selection_html) el = self.marionette.find_element(By.ID, self._input_id) sel = SelectionManager(el) # To successfully select the second word while the first word is being # selected, use sufficient spaces between 'a' and 'b' to avoid the # second caret covers on the second word. original_content = "aaaaaaaa bbbbbbbb" el.clear() el.send_keys(original_content) words = original_content.split() # We cannot use self.long_press_on_word() directly since it has will # change the cursor position which affects this test. We have to store # the position of word 0 and word 1 before long-pressing to select the # word. word0_x, word0_y = self.word_location(el, 0) word1_x, word1_y = self.word_location(el, 1) self.long_press_on_location(el, word0_x, word0_y) self.assertEqual(words[0], sel.selected_content) self.long_press_on_location(el, word1_x, word1_y) self.assertEqual(words[1], sel.selected_content) self.long_press_on_location(el, word0_x, word0_y) self.assertEqual(words[0], sel.selected_content) # If the second carets is visible, it can be dragged to the position of # the first caret. After that, selection will contain only the first # character. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform() self.assertEqual(words[0][0], sel.selected_content) @parameterized(_input_id, el_id=_input_id) @parameterized(_input_padding_id, el_id=_input_padding_id) @parameterized(_textarea_one_line_id, el_id=_textarea_one_line_id) @parameterized(_contenteditable_id, el_id=_contenteditable_id) def test_carets_not_jump_when_dragging_to_editable_content_boundary(self, el_id): self.open_test_html(self._selection_html) el = self.marionette.find_element(By.ID, el_id) sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 3, "Expect at least three words in the content.") # Goal: the selection is not changed after dragging the caret on the # Y-axis. target_content = words[1] self.long_press_on_word(el, 1) (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() # Drag the first caret up by 50px. self.actions.flick(el, caret1_x, caret1_y, caret1_x, caret1_y - 50).perform() self.assertEqual(target_content, sel.selected_content) # Drag the second caret down by 50px. self.actions.flick(el, caret2_x, caret2_y, caret2_x, caret2_y + 50).perform() self.assertEqual(target_content, sel.selected_content)
class Loop(object): """Object representing the Loop application. """ def __init__(self, parent): self.apps = parent.apps self.data_layer = parent.data_layer self.parent = parent self.marionette = parent.marionette self.UTILS = parent.UTILS self.actions = Actions(self.marionette) self.browser = Browser(self.parent) self.app_name = "Firefox Hello" self.market_url = "https://owd.tid.es/B3lg1r89n/market/appList.html" self.persistent_directory = "/data/local/storage/persistent" self.loop_dir = self.UTILS.general.get_config_variable("install_dir", "loop") def launch(self): """ Launch the app. """ self.app = self.apps.launch(self.app_name) self.UTILS.element.waitForNotElements(DOM.GLOBAL.loading_overlay, self.__class__.__name__ + " app - loading overlay") return self.app def is_installed(self): return self.apps.is_app_installed(self.app_name) def install(self): via = self.UTILS.general.get_config_variable("install_via", "loop") if via == "Grunt": self.install_via_grunt() elif via == "Market": self.install_via_marketplace() else: self.UTILS.test.test(False, "Not valid way to install Loop") def install_via_grunt(self, version="1.1"): self.UTILS.reporting.logResult('info', 'Installing via grunt....') c1 = 'cd {}'.format(self.loop_dir) c2 = 'git checkout {}'.format(version) c3 = 'git fetch && git merge origin/{}'.format(version) c4 = 'grunt build' with open(os.devnull, 'w') as DEVNULL: result = subprocess32.check_output("; ".join([c1, c2, c3, c4]), shell=True, stderr=DEVNULL) self.marionette.switch_to_frame() msg = "{} installed".format(self.app_name) installed_app_msg = (DOM.GLOBAL.system_banner_msg[0], DOM.GLOBAL.system_banner_msg[1].format(msg)) self.UTILS.element.waitForElements(installed_app_msg, "App installed", timeout=30) install_ok_msg = "Done, without errors." self.UTILS.reporting.logResult('info', "Result of this test script: {}".format(result)) self.UTILS.test.test(install_ok_msg in result, "Install via grunt is OK") def install_via_marketplace(self): self.UTILS.reporting.logResult('info', 'Installing via marketplace....') # Make sure we install the latest version self.update_and_publish() self.browser.launch() time.sleep(1) self.browser.open_url(self.market_url) loop_link = self.UTILS.element.getElement( ('xpath', '//p[contains(text(), "{}")]'.format(self.app_name)), "App link") loop_link.tap() self.marionette.switch_to_frame() install_ok = self.UTILS.element.getElement(DOM.GLOBAL.app_install_ok, "Install button") install_ok.tap() msg = "{} installed".format(self.app_name) installed_app_msg = (DOM.GLOBAL.system_banner_msg[0], DOM.GLOBAL.system_banner_msg[1].format(msg)) self.UTILS.element.waitForElements(installed_app_msg, "App installed", timeout=30) def reinstall(self): self.uninstall() time.sleep(2) self.install() def uninstall(self): self.UTILS.reporting.logResult('info', "uninstalling.........") self.apps.uninstall(self.app_name) self.parent.wait_for_condition(lambda m: not self.apps.is_app_installed( self.app_name), timeout=20, message="{} is not installed".format(self.app_name)) def update_and_publish(self): self.publish_loop_dir = self.UTILS.general.get_config_variable("aux_files", "loop") c1 = 'cd {}'.fomrat(self.publish_loop_dir) c2 = './publish_app.sh {}'.format(self.loop_dir) with open(os.devnull, 'w') as DEVNULL: result = subprocess32.check_output("; ".join([c1, c2]), shell=True, stderr=DEVNULL) chops = result.split("\n") self.UTILS.reporting.logResult('info', "result: {}".format(chops)) self.UTILS.test.test("And all done, hopefully." in chops, "The script to publish an app is OK", True) def _fill_fxa_field(self, field_locator, text): """ Auxiliary method to fill "Firefox account login" fields """ self.UTILS.reporting.logResult('info', '[firefox_login] Filling fxa field with text: {}'.format(text)) self.parent.wait_for_element_displayed(*field_locator) fxa_input = self.marionette.find_element(*field_locator) fxa_input.send_keys(text) time.sleep(2) self.parent.wait_for_condition( lambda m: m.find_element(*DOM.Loop.ffox_account_login_next).get_attribute("disabled") != "disabled") next_btn = self.marionette.find_element(*DOM.Loop.ffox_account_login_next) next_btn.tap() self.parent.wait_for_element_not_displayed(*DOM.Loop.ffox_account_login_overlay) def tap_on_firefox_login_button(self): ffox_btn = self.marionette.find_element(*DOM.Loop.wizard_login_ffox_account) self.UTILS.element.simulateClick(ffox_btn) def _tap_on_phone_login_button(self): phone_btn = self.marionette.find_element(*DOM.Loop.wizard_login_phone_number) self.UTILS.element.simulateClick(phone_btn) def _get_mobile_id_options(self): return self.marionette.find_elements(*DOM.Loop.mobile_id_sim_list_item) def wizard_or_login(self): """ Checks if we have to skip the Wizard, log in, or if we're already at the main screen of Loop For the first two scenarios, it returns True. If we are already inside Loop, it returns False. """ # TODO: switch try-except code -> first check login instead of wizard # see if it works, when the wizard is first try: self.parent.wait_for_element_displayed(*DOM.Loop.wizard_header) self.UTILS.reporting.logResult('info', '[wizard_or_login] Wizard') self.skip_wizard() return True except: self.UTILS.reporting.logResult('info', '[wizard_or_login] Login') try: self.parent.wait_for_element_displayed(*DOM.Loop.wizard_login) return True except: self.UTILS.reporting.logResult('info', '[wizard_or_login] Loop') try: self.parent.wait_for_element_displayed(*DOM.Loop.app_header) return False except: self.UTILS.test.test(False, "Ooops. Something went wrong", True) def get_wizard_steps(self): """ Returns the number of steps of the wizard """ return len(self.marionette.find_elements(*DOM.Loop.wizard_slideshow_step)) def skip_wizard(self): """ Skips first time use wizard by flicking the screen """ time.sleep(1) wizard_steps = self.get_wizard_steps() current_frame = self.apps.displayed_app.frame x_start = current_frame.size['width'] // 2 x_end = x_start // 4 y_start = current_frame.size['height'] // 2 for i in range(wizard_steps): self.actions.flick(current_frame, x_start, y_start, x_end, y_start, duration=600).perform() time.sleep(1) self.marionette.switch_to_frame(self.apps.displayed_app.frame_id) self.parent.wait_for_element_displayed(DOM.Loop.wizard_login[0], DOM.Loop.wizard_login[1], timeout=10) def firefox_login(self, email, password, is_wrong=False): """ Logs in using Firefox account """ self.tap_on_firefox_login_button() self.UTILS.iframe.switchToFrame(*DOM.Loop.ffox_account_frame_locator) self.parent.wait_for_element_displayed( DOM.Loop.ffox_account_login_title[0], DOM.Loop.ffox_account_login_title[1], timeout=20) self._fill_fxa_field(DOM.Loop.ffox_account_login_mail, email) self._fill_fxa_field(DOM.Loop.ffox_account_login_pass, password) if not is_wrong: done_btn = self.marionette.find_element(*DOM.Loop.ffox_account_login_done) done_btn.tap() def phone_login_auto(self, phone_number, option_number=1): """Wrapper to log in using phone number, either the already selected or entering it manually""" try: self.UTILS.reporting.info("Trying phone login using Mobile ID") self.phone_login() except: self.UTILS.reporting.info("Mobile ID login failed, falling back to manual") self.UTILS.iframe.switchToFrame(*DOM.Loop.mobile_id_frame_locator) self.marionette.find_element('id', 'header').tap(25, 25) self.UTILS.iframe.switchToFrame(*DOM.Loop.frame_locator) self.phone_login_manually(phone_number) @retry(3, context=("OWDTestToolkit.apps.loop", "Loop"), aux_func_name="retry_phone_login") def phone_login(self, option_number=1): """ Logs in using mobile id """ self._tap_on_phone_login_button() self.UTILS.iframe.switchToFrame(*DOM.Loop.mobile_id_frame_locator) self.parent.wait_for_element_not_displayed(*DOM.Loop.ffox_account_login_overlay) mobile_id_header = ("xpath", DOM.GLOBAL.app_head_specific.format(_("Mobile ID"))) self.parent.wait_for_element_displayed(*mobile_id_header) options = self._get_mobile_id_options() if len(options) > 1: # Option number refers to the SIM number (1 or 2), not to the position in the array options[option_number - 1].tap() allow_button = self.marionette.find_element(*DOM.Loop.mobile_id_allow_button) allow_button.tap() try: self.parent.wait_for_element_displayed( DOM.Loop.mobile_id_verified_button[0], DOM.Loop.mobile_id_verified_button[1], timeout=30) verified_button = self.marionette.find_element(*DOM.Loop.mobile_id_verified_button) self.UTILS.element.simulateClick(verified_button) except: self.parent.wait_for_condition(lambda m: "state-sending" in m.find_element( *DOM.Loop.mobile_id_allow_button).get_attribute("class"), timeout=5, message="Button is still sending") # Make @retry do its work raise self.apps.switch_to_displayed_app() @retry(3, context=("OWDTestToolkit.apps.loop", "Loop"), aux_func_name="retry_phone_login") def phone_login_manually(self, phone_number_without_prefix): """ Logs in using mobile id, but instead of using the automatically provided by the app selecting the manual option @phone_number_without_prefix str Phone number to register into Loop NOTE: for the shake of simplicity, we assume the prefix is the spanish one (+34) by default """ self._tap_on_phone_login_button() self.UTILS.iframe.switchToFrame(*DOM.Loop.mobile_id_frame_locator) self.parent.wait_for_element_not_displayed(*DOM.Loop.ffox_account_login_overlay) mobile_id_header = ("xpath", DOM.GLOBAL.app_head_specific.format(_("Mobile ID"))) self.parent.wait_for_element_displayed(*mobile_id_header) # Manually! manually_link = self.marionette.find_element(*DOM.Loop.mobile_id_add_phone_number) manually_link.tap() self.parent.wait_for_element_displayed(*DOM.Loop.mobile_id_add_phone_number_number) phone_input = self.marionette.find_element(*DOM.Loop.mobile_id_add_phone_number_number) phone_input.send_keys(phone_number_without_prefix) # NOTE: before you virtually kill me, I cannot take this duplicated code into another # separated method due to the @reply decorator. Just letting you know :). allow_button = self.marionette.find_element(*DOM.Loop.mobile_id_allow_button) allow_button.tap() try: self.parent.wait_for_element_displayed( DOM.Loop.mobile_id_verified_button[0], DOM.Loop.mobile_id_verified_button[1], timeout=30) verified_button = self.marionette.find_element(*DOM.Loop.mobile_id_verified_button) self.UTILS.element.simulateClick(verified_button) except: self.parent.wait_for_condition(lambda m: "state-sending" in m.find_element( *DOM.Loop.mobile_id_allow_button).get_attribute("class"), timeout=5, message="Button is still sending") # Make @retry do its work raise self.apps.switch_to_displayed_app() @retry(5, context=("OWDTestToolkit.apps.loop", "Loop"), aux_func_name="retry_ffox_login") def allow_permission_ffox_login(self): """ Allows Loop to read our contacts This method checks whether is necessary to allow extra permissions for loop or not ater loggin in with Firefox accounts Also, since this is is the last step before connecting to the Loop server, it checks that no error has been raised. If that happens, it retries the connection up to 5 times. """ self.marionette.switch_to_frame() try: self.UTILS.reporting.debug("Looking for permission panel....") self.parent.wait_for_element_displayed( DOM.GLOBAL.app_permission_dialog[0], DOM.GLOBAL.app_permission_dialog[1], timeout=10) except: try: self.UTILS.reporting.debug("Now looking for permission Loop main view....") self.apps.switch_to_displayed_app() self.parent.wait_for_element_displayed(*DOM.Loop.app_header) return except: self.UTILS.reporting.debug("And Now looking for error....") self.marionette.switch_to_frame() self.parent.wait_for_element_displayed(*DOM.GLOBAL.modal_dialog_alert_title) self.UTILS.reporting.logResult('info', "Error connecting...") # Make @retry do its work raise msg_text = self.marionette.find_element(*DOM.GLOBAL.app_permission_msg).text self.UTILS.test.test(self.app_name in msg_text, "Permissions for loop") allow_btn = self.marionette.find_element(*DOM.GLOBAL.app_permission_btn_yes) self.UTILS.element.simulateClick(allow_btn) self.apps.switch_to_displayed_app() def allow_permission_phone_login(self): """ Allows Loop to read our contacts This method checks whether is necessary to allow extra permissions for loop or not ater loggin in with Mobile ID Also, since this is is the last step before connecting to the Loop server, it checks that no error has been raised. If that happens, it retries the connection up to 5 times. """ self.marionette.switch_to_frame() try: self.UTILS.reporting.debug("Looking for permission panel....") self.parent.wait_for_element_displayed( DOM.GLOBAL.app_permission_dialog[0], DOM.GLOBAL.app_permission_dialog[1], timeout=10) except: self.UTILS.reporting.debug("Now looking for permission Loop main view....") self.apps.switch_to_displayed_app() self.parent.wait_for_element_displayed(*DOM.Loop.app_header) return msg_text = self.marionette.find_element(*DOM.GLOBAL.app_permission_msg).text self.UTILS.test.test(self.app_name in msg_text, "Permissions for loop") allow_btn = self.marionette.find_element(*DOM.GLOBAL.app_permission_btn_yes) self.UTILS.element.simulateClick(allow_btn) self.apps.switch_to_displayed_app() def retry_ffox_login(self): """ Retry Ffox account login if it has failed. This method is called as the aux_func for our brand new retry decorator """ self.UTILS.reporting.logResult('info', "Retrying FxA login...") self.apps.switch_to_displayed_app() time.sleep(2) self.parent.wait_for_element_displayed(*DOM.Loop.error_screen_ok) ok_btn = self.marionette.find_element(*DOM.Loop.error_screen_ok) self.UTILS.element.simulateClick(ok_btn) self.parent.wait_for_element_displayed(*DOM.Loop.wizard_login) self.tap_on_firefox_login_button() def retry_phone_login(self): """ Retry phone login if it has failed """ self.UTILS.reporting.logResult('info', "Retrying phone login...") self.parent.wait_for_element_displayed(*DOM.Loop.mobile_id_back) back_btn = self.marionette.find_element(*DOM.Loop.mobile_id_back) self.UTILS.element.simulateClick(back_btn) self.apps.switch_to_displayed_app() time.sleep(2) self._tap_on_phone_login_button() def open_settings(self): """ Open settings panel from call log """ self.parent.wait_for_element_displayed(*DOM.Loop.open_settings_btn) settings_btn = self.marionette.find_element(*DOM.Loop.open_settings_btn) self.UTILS.element.simulateClick(settings_btn) self.parent.wait_for_element_displayed(*DOM.Loop.settings_panel_header) def logout(self, confirm=True): """ This methods logs us out from Loop. It assumes we already are in the Loop Settings panel """ try: self.parent.wait_for_element_displayed(*DOM.Loop.settings_logout) except: self.UTILS.reporting.logResult('info', "Already logged out") return logout_btn = self.marionette.find_element(*DOM.Loop.settings_logout) self.UTILS.element.simulateClick(logout_btn) self.parent.wait_for_element_displayed(*DOM.Loop.form_confirm_logout) if confirm: confirm_btn = self.marionette.find_element(*DOM.Loop.form_confirm_logout) self.UTILS.element.simulateClick(confirm_btn) self.parent.wait_for_element_not_displayed(*DOM.Loop.loading_overlay) self.parent.wait_for_element_displayed(*DOM.Loop.wizard_login) else: cancel_btn = self.marionette.find_element(*DOM.Loop.form_confirm_cancel) self.parent.wait_for_element_displayed(*DOM.Loop.settings_panel_header) def switch_to_urls(self): self.parent.wait_for_element_displayed(*DOM.Loop.call_log_shared_links_tab) tab = self.marionette.find_element(*DOM.Loop.call_log_shared_links_tab) tab.tap() self.parent.wait_for_condition(lambda m: tab.get_attribute( "aria-selected") == "true", timeout=10, message="Checking that 'Shared links' is selected") def _get_number_of_urls(self, locator): try: entries = self.marionette.find_elements(*locator) return len(entries) except: return 0 def get_number_of_all_urls(self): return self._get_number_of_urls(DOM.Loop.shared_links_entry) def get_number_of_available_urls(self): return self._get_number_of_urls(DOM.Loop.shared_links_entry_available) def get_number_of_revoked_urls(self): return self._get_number_of_urls(DOM.Loop.shared_links_entry_revoked) def _select_action(self, action): elem = (DOM.Loop.form_action_action[0], DOM.Loop.form_action_action[1].format(action)) self.parent.wait_for_element_displayed(*elem) self.marionette.find_element(*elem).tap() def _confirm(self, decission): elem = DOM.Loop.form_confirm_delete if decission else DOM.Loop.form_confirm_cancel self.parent.wait_for_element_displayed(*elem) self.marionette.find_element(*elem).tap() def _is_entry_revoked(self, entry): try: entry.find_element(*DOM.Loop.shared_links_entry_revoked_nested) return True except Exception, e: self.UTILS.reporting.logResult('info', "What happened here? {}".format(e)) return False
class AccessibleCaretCursorModeTestCase(MarionetteTestCase): '''Test cases for AccessibleCaret under cursor mode, aka touch caret. ''' def setUp(self): # Code to execute before a test is being run. super(AccessibleCaretCursorModeTestCase, self).setUp() self.caret_tested_pref = 'layout.accessiblecaret.enabled' self.caret_timeout_ms_pref = 'layout.accessiblecaret.timeout_ms' self.prefs = { self.caret_tested_pref: True, self.caret_timeout_ms_pref: 0, } self.marionette.set_prefs(self.prefs) self.actions = Actions(self.marionette) def timeout_ms(self): 'Return touch caret expiration time in milliseconds.' return self.marionette.get_pref(self.caret_timeout_ms_pref) def open_test_html(self): 'Open html for testing and locate elements.' test_html = self.marionette.absolute_url('test_touchcaret.html') self.marionette.navigate(test_html) self._input = self.marionette.find_element(By.ID, 'input') self._textarea = self.marionette.find_element(By.ID, 'textarea') self._contenteditable = self.marionette.find_element(By.ID, 'contenteditable') def _test_move_caret_to_the_right_by_one_character(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = sel.content target_content = target_content[:1] + content_to_add + target_content[1:] # Get touch caret (x, y) at position 1 and 2. el.tap() sel.move_caret_to_front() caret0_x, caret0_y = sel.caret_location() touch_caret0_x, touch_caret0_y = sel.touch_caret_location() sel.move_caret_by_offset(1) touch_caret1_x, touch_caret1_y = sel.touch_caret_location() # Tap the front of the input to make touch caret appear. el.tap(caret0_x, caret0_y) # Move touch caret self.actions.flick(el, touch_caret0_x, touch_caret0_y, touch_caret1_x, touch_caret1_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(target_content, sel.content) def _test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = sel.content + content_to_add # Tap the front of the input to make touch caret appear. el.tap() sel.move_caret_to_front() el.tap(*sel.caret_location()) # Move touch caret to the bottom-right corner of the element. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = el.size['width'], el.size['height'] self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(target_content, sel.content) def _test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = content_to_add + sel.content # Get touch caret location at the front. el.tap() sel.move_caret_to_front() dest_x, dest_y = sel.touch_caret_location() # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) src_x, src_y = sel.touch_caret_location() # Move touch caret to the front of the input box. self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(target_content, sel.content) def _test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' non_target_content = content_to_add + sel.content # Get touch caret expiration time in millisecond, and convert it to second. timeout = self.timeout_ms() / 1000.0 # Set a 3x timeout margin to prevent intermittent test failures. timeout *= 3 # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) # Wait until touch caret disappears, then pretend to move it to the # top-left corner of the input box. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 self.actions.wait(timeout).flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(non_target_content, sel.content) def _test_touch_caret_hides_after_receiving_wheel_event(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' non_target_content = content_to_add + sel.content # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) # Send an arbitrary scroll-down-10px wheel event to the center of the # input box to hide touch caret. Then pretend to move it to the top-left # corner of the input box. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 el_center_x, el_center_y = el.rect['x'], el.rect['y'] self.marionette.execute_script( ''' var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindowUtils); utils.sendWheelEvent(arguments[0], arguments[1], 0, 10, 0, WheelEvent.DOM_DELTA_PIXEL, 0, 0, 0, 0); ''', script_args=[el_center_x, el_center_y], sandbox='system' ) self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(non_target_content, sel.content) def _test_caret_not_appear_when_typing_in_scrollable_content(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = sel.content + string.ascii_letters + content_to_add el.tap() sel.move_caret_to_end() # Insert a long string to the end of the <input>, which triggers # ScrollPositionChanged event. el.send_keys(string.ascii_letters) # The caret should not be visible. If it does appear wrongly due to the # ScrollPositionChanged event, we can drag it to the front of the # <input> to change the cursor position. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() # The content should be inserted at the end of the <input>. el.send_keys(content_to_add) assertFunc(target_content, sel.content) ######################################################################## # <input> test cases with touch caret enabled ######################################################################## def test_input_move_caret_to_the_right_by_one_character(self): self.open_test_html() self._test_move_caret_to_the_right_by_one_character(self._input, self.assertEqual) def test_input_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self): self.open_test_html() self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._input, self.assertEqual) def test_input_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self._input, self.assertEqual) def test_input_caret_not_appear_when_typing_in_scrollable_content(self): self.open_test_html() self._test_caret_not_appear_when_typing_in_scrollable_content(self._input, self.assertEqual) def test_input_touch_caret_timeout(self): with self.marionette.using_prefs({self.caret_timeout_ms_pref: 1000}): self.open_test_html() self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self._input, self.assertNotEqual) ######################################################################## # <input> test cases with touch caret disabled ######################################################################## def test_input_move_caret_to_the_right_by_one_character_disabled(self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_the_right_by_one_character(self._input, self.assertNotEqual) def test_input_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self._input, self.assertNotEqual) ######################################################################## # <textarea> test cases with touch caret enabled ######################################################################## def test_textarea_move_caret_to_the_right_by_one_character(self): self.open_test_html() self._test_move_caret_to_the_right_by_one_character(self._textarea, self.assertEqual) def test_textarea_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self): self.open_test_html() self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._textarea, self.assertEqual) def test_textarea_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self._textarea, self.assertEqual) def test_textarea_touch_caret_timeout(self): with self.marionette.using_prefs({self.caret_timeout_ms_pref: 1000}): self.open_test_html() self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self._textarea, self.assertNotEqual) ######################################################################## # <textarea> test cases with touch caret disabled ######################################################################## def test_textarea_move_caret_to_the_right_by_one_character_disabled(self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_the_right_by_one_character(self._textarea, self.assertNotEqual) def test_textarea_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self._textarea, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with touch caret enabled ######################################################################## def test_contenteditable_move_caret_to_the_right_by_one_character(self): self.open_test_html() self._test_move_caret_to_the_right_by_one_character(self._contenteditable, self.assertEqual) def test_contenteditable_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self): self.open_test_html() self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._contenteditable, self.assertEqual) def test_contenteditable_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self._contenteditable, self.assertEqual) def test_contenteditable_touch_caret_timeout(self): with self.marionette.using_prefs({self.caret_timeout_ms_pref: 1000}): self.open_test_html() self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self._contenteditable, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with touch caret disabled ######################################################################## def test_contenteditable_move_caret_to_the_right_by_one_character_disabled(self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_the_right_by_one_character(self._contenteditable, self.assertNotEqual) def test_contenteditable_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self._contenteditable, self.assertNotEqual) def test_caret_does_not_jump_when_dragging_to_editable_content_boundary(self): self.open_test_html() el = self._input sel = SelectionManager(el) content_to_add = '!' non_target_content = sel.content + content_to_add # Goal: the cursor position does not being changed after dragging the # caret down on the Y-axis. el.tap() sel.move_caret_to_front() el.tap(*sel.caret_location()) x, y = sel.touch_caret_location() # Drag the caret down by 50px, and insert '!'. self.actions.flick(el, x, y, x, y + 50).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() self.assertNotEqual(non_target_content, sel.content)
class CommonCaretsTestCase2(object): '''Common test cases for a selection with a two carets. To run these test cases, a subclass must inherit from both this class and MarionetteTestCase. ''' _long_press_time = 1 # 1 second def setUp(self): # Code to execute before a tests are run. MarionetteTestCase.setUp(self) self.actions = Actions(self.marionette) # The carets to be tested. self.carets_tested_pref = None # The carets to be disabled in this test suite. self.carets_disabled_pref = None def set_pref(self, pref_name, value): '''Set a preference to value. For example: >>> set_pref('layout.accessiblecaret.enabled', True) ''' pref_name = repr(pref_name) if isinstance(value, bool): value = 'true' if value else 'false' elif isinstance(value, int): value = str(value) else: value = repr(value) script = '''SpecialPowers.pushPrefEnv({"set": [[%s, %s]]}, marionetteScriptFinished);''' % ( pref_name, value) self.marionette.execute_async_script(script) def open_test_html(self, enabled=True): 'Open html for testing and enable selectioncaret and non-editable support' self.set_pref(self.carets_tested_pref, enabled) self.set_pref(self.carets_disabled_pref, False) test_html = self.marionette.absolute_url( 'test_selectioncarets_multiplerange.html') self.marionette.navigate(test_html) self._body = self.marionette.find_element(By.ID, 'bd') self._sel1 = self.marionette.find_element(By.ID, 'sel1') self._sel2 = self.marionette.find_element(By.ID, 'sel2') self._sel3 = self.marionette.find_element(By.ID, 'sel3') self._sel4 = self.marionette.find_element(By.ID, 'sel4') self._sel6 = self.marionette.find_element(By.ID, 'sel6') self._nonsel1 = self.marionette.find_element(By.ID, 'nonsel1') def open_test_html_long_text(self, enabled=True): 'Open html for testing and enable selectioncaret' self.set_pref(self.carets_tested_pref, enabled) self.set_pref(self.carets_disabled_pref, False) test_html = self.marionette.absolute_url( 'test_selectioncarets_longtext.html') self.marionette.navigate(test_html) self._body = self.marionette.find_element(By.ID, 'bd') self._longtext = self.marionette.find_element(By.ID, 'longtext') def open_test_html_iframe(self, enabled=True): 'Open html for testing and enable selectioncaret' self.set_pref(self.carets_tested_pref, enabled) self.set_pref(self.carets_disabled_pref, False) test_html = self.marionette.absolute_url( 'test_selectioncarets_iframe.html') self.marionette.navigate(test_html) self._iframe = self.marionette.find_element(By.ID, 'frame') def _long_press_to_select_word(self, el, wordOrdinal): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue( wordOrdinal < len(words), 'Expect at least %d words in the content.' % wordOrdinal) # Calc offset offset = 0 for i in range(wordOrdinal): offset += (len(words[i]) + 1) # Move caret inside the word. el.tap() sel.move_caret_to_front() sel.move_caret_by_offset(offset) x, y = sel.caret_location() # Long press the caret position. Selection carets should appear, and the # word will be selected. On Windows, those spaces after the word # will also be selected. long_press_without_contextmenu(self.marionette, el, self._long_press_time, x, y) def _to_unix_line_ending(self, s): """Changes all Windows/Mac line endings in s to UNIX line endings.""" return s.replace('\r\n', '\n').replace('\r', '\n') def test_long_press_to_select_non_selectable_word(self): '''Testing long press on non selectable field. We should not select anything when long press on non selectable fields.''' self.open_test_html() halfY = self._nonsel1.size['height'] / 2 long_press_without_contextmenu(self.marionette, self._nonsel1, self._long_press_time, 0, halfY) sel = SelectionManager(self._nonsel1) range_count = sel.range_count() self.assertEqual(range_count, 0) def test_drag_caret_over_non_selectable_field(self): '''Testing drag caret over non selectable field. So that the selected content should exclude non selectable field and end selection caret should appear in last range's position.''' self.open_test_html() # Select target element and get target caret location self._long_press_to_select_word(self._sel4, 3) sel = SelectionManager(self._body) (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() self._long_press_to_select_word(self._sel6, 0) (_, _), (end_caret2_x, end_caret2_y) = sel.selection_carets_location() # Select start element self._long_press_to_select_word(self._sel3, 3) # Drag end caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual( self._to_unix_line_ending(sel.selected_content.strip()), 'this 3\nuser can select this') (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, end_caret2_x, end_caret2_y, 1).perform() self.assertEqual( self._to_unix_line_ending(sel.selected_content.strip()), 'this 3\nuser can select this 4\nuser can select this 5\nuser') # Drag first caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret1_x, caret1_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual( self._to_unix_line_ending(sel.selected_content.strip()), '4\nuser can select this 5\nuser') def test_drag_caret_to_beginning_of_a_line(self): '''Bug 1094056 Test caret visibility when caret is dragged to beginning of a line ''' self.open_test_html() # Select the first word in the second line self._long_press_to_select_word(self._sel2, 0) sel = SelectionManager(self._body) (start_caret_x, start_caret_y), (end_caret_x, end_caret_y) = sel.selection_carets_location() # Select target word in the first line self._long_press_to_select_word(self._sel1, 2) # Drag end caret to the beginning of the second line (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, start_caret_x, start_caret_y).perform() # Drag end caret back to the target word self.actions.flick(self._body, start_caret_x, start_caret_y, caret2_x, caret2_y).perform() self.assertEqual( self._to_unix_line_ending(sel.selected_content.strip()), 'select') @skip_if_not_rotatable def test_caret_position_after_changing_orientation_of_device(self): '''Bug 1094072 If positions of carets are updated correctly, they should be draggable. ''' self.open_test_html_long_text() # Select word in portrait mode, then change to landscape mode self.marionette.set_orientation('portrait') self._long_press_to_select_word(self._longtext, 12) sel = SelectionManager(self._body) (p_start_caret_x, p_start_caret_y), (p_end_caret_x, p_end_caret_y) = sel.selection_carets_location() self.marionette.set_orientation('landscape') (l_start_caret_x, l_start_caret_y), (l_end_caret_x, l_end_caret_y) = sel.selection_carets_location() # Drag end caret to the start caret to change the selected content self.actions.flick(self._body, l_end_caret_x, l_end_caret_y, l_start_caret_x, l_start_caret_y).perform() # Change orientation back to portrait mode to prevent affecting # other tests self.marionette.set_orientation('portrait') self.assertEqual( self._to_unix_line_ending(sel.selected_content.strip()), 'o') def test_select_word_inside_an_iframe(self): '''Bug 1088552 The scroll offset in iframe should be taken into consideration properly. In this test, we scroll content in the iframe to the bottom to cause a huge offset. If we use the right coordinate system, selection should work. Otherwise, it would be hard to trigger select word. ''' self.open_test_html_iframe() # switch to inner iframe and scroll to the bottom self.marionette.switch_to_frame(self._iframe) self.marionette.execute_script( 'document.getElementById("bd").scrollTop += 999') # long press to select bottom text self._body = self.marionette.find_element(By.ID, 'bd') sel = SelectionManager(self._body) self._bottomtext = self.marionette.find_element(By.ID, 'bottomtext') long_press_without_contextmenu(self.marionette, self._bottomtext, self._long_press_time) self.assertNotEqual( self._to_unix_line_ending(sel.selected_content.strip()), '')
class CommonCaretsTestCase(object): '''Common test cases for a selection with a two carets. To run these test cases, a subclass must inherit from both this class and MarionetteTestCase. ''' _long_press_time = 1 # 1 second _input_selector = (By.ID, 'input') _textarea_selector = (By.ID, 'textarea') _textarea_rtl_selector = (By.ID, 'textarea_rtl') _contenteditable_selector = (By.ID, 'contenteditable') _content_selector = (By.ID, 'content') _textarea2_selector = (By.ID, 'textarea2') _contenteditable2_selector = (By.ID, 'contenteditable2') _content2_selector = (By.ID, 'content2') def setUp(self): # Code to execute before a tests are run. super(CommonCaretsTestCase, self).setUp() self.actions = Actions(self.marionette) # The carets to be tested. self.carets_tested_pref = None # The carets to be disabled in this test suite. self.carets_disabled_pref = None def set_pref(self, pref_name, value): '''Set a preference to value. For example: >>> set_pref('layout.accessiblecaret.enabled', True) ''' pref_name = repr(pref_name) if isinstance(value, bool): value = 'true' if value else 'false' func = 'setBoolPref' elif isinstance(value, int): value = str(value) func = 'setIntPref' else: value = repr(value) func = 'setCharPref' with self.marionette.using_context('chrome'): script = 'Services.prefs.%s(%s, %s)' % (func, pref_name, value) self.marionette.execute_script(script) def open_test_html(self, enabled=True): '''Open html for testing and locate elements, and enable/disable touch caret.''' self.set_pref(self.carets_tested_pref, enabled) self.set_pref(self.carets_disabled_pref, False) test_html = self.marionette.absolute_url('test_selectioncarets.html') self.marionette.navigate(test_html) self._input = self.marionette.find_element(*self._input_selector) self._textarea = self.marionette.find_element(*self._textarea_selector) self._textarea_rtl = self.marionette.find_element(*self._textarea_rtl_selector) self._contenteditable = self.marionette.find_element(*self._contenteditable_selector) self._content = self.marionette.find_element(*self._content_selector) def open_test_html2(self, enabled=True): '''Open html for testing and locate elements, and enable/disable touch caret.''' self.set_pref(self.carets_tested_pref, enabled) self.set_pref(self.carets_disabled_pref, False) test_html2 = self.marionette.absolute_url('test_selectioncarets_multipleline.html') self.marionette.navigate(test_html2) self._textarea2 = self.marionette.find_element(*self._textarea2_selector) self._contenteditable2 = self.marionette.find_element(*self._contenteditable2_selector) self._content2 = self.marionette.find_element(*self._content2_selector) def _first_word_location(self, el): '''Get the location (x, y) of the first word in el. Note: this function has a side effect which changes focus to the target element el. ''' sel = SelectionManager(el) # Move caret behind the first character to get the location of the first # word. el.tap() sel.move_caret_to_front() sel.move_caret_by_offset(1) return sel.caret_location() def _long_press_to_select(self, el, x, y): '''Long press the location (x, y) to select a word. SelectionCarets should appear. On Windows, those spaces after the word will also be selected. ''' long_press_without_contextmenu(self.marionette, el, self._long_press_time, x, y) def _test_long_press_to_select_a_word(self, el, assertFunc): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 2, 'Expect at least two words in the content.') target_content = words[0] # Goal: Select the first word. x, y = self._first_word_location(el) self._long_press_to_select(el, x, y) # Ignore extra spaces selected after the word. assertFunc(target_content, sel.selected_content.rstrip()) def _test_move_selection_carets(self, el, assertFunc): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.') # Goal: Select all text after the first word. target_content = original_content[len(words[0]):] # Get the location of the selection carets at the end of the content for # later use. el.tap() sel.select_all() (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() x, y = self._first_word_location(el) self._long_press_to_select(el, x, y) # Move the right caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Move the left caret to the previous position of the right caret. self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() # Ignore extra spaces at the beginning of the content in comparison. assertFunc(target_content.lstrip(), sel.selected_content.lstrip()) def _test_minimum_select_one_character(self, el, assertFunc, x=None, y=None): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.') # Get the location of the selection carets at the end of the content for # later use. sel.select_all() (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() el.tap() # Goal: Select the first character. target_content = original_content[0] if x and y: # If we got x and y from the arguments, use it as a hint of the # location of the first word pass else: x, y = self._first_word_location(el) self._long_press_to_select(el, x, y) # Move the right caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Move the right caret to the position of the left caret. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform() assertFunc(target_content, sel.selected_content) def _test_focus_obtained_by_long_press(self, el1, el2): '''Test the focus could be changed from el1 to el2 by long press. If the focus is changed to e2 successfully, SelectionCarets should appear and could be dragged. ''' # Goal: Tap to focus el1, and then select the first character on # el2. # We want to collect the location of the first word in el2 here # since self._first_word_location() has the side effect which would # change the focus. x, y = self._first_word_location(el2) el1.tap() self._test_minimum_select_one_character(el2, self.assertEqual, x=x, y=y) def _test_handle_tilt_when_carets_overlap_to_each_other(self, el, assertFunc): '''Test tilt handling when carets overlap to each other. Let SelectionCarets overlap to each other. If SelectionCarets are set to tilted successfully, tapping the tilted carets should not cause the selection to be collapsed and the carets should be draggable. ''' sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.') # Goal: Select the first word. x, y = self._first_word_location(el) self._long_press_to_select(el, x, y) target_content = sel.selected_content # Move the left caret to the position of the right caret to trigger # carets overlapping. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() # We make two hit tests targeting the left edge of the left tilted caret # and the right edge of the right tilted caret. If either of the hits is # missed, selection would be collapsed and both carets should not be # draggable. (caret3_x, caret3_y), (caret4_x, caret4_y) = sel.selection_carets_location() # The following values are from ua.css. caret_width = 44 caret_margin_left = -23 tilt_right_margin_left = 18 tilt_left_margin_left = -17 left_caret_left_edge_x = caret3_x + caret_margin_left + tilt_left_margin_left el.tap(left_caret_left_edge_x + 2, caret3_y) right_caret_right_edge_x = (caret4_x + caret_margin_left + tilt_right_margin_left + caret_width) el.tap(right_caret_right_edge_x - 2, caret4_y) # Drag the left caret back to the initial selection, the first word. self.actions.flick(el, caret3_x, caret3_y, caret1_x, caret1_y).perform() assertFunc(target_content, sel.selected_content) ######################################################################## # <input> test cases with selection carets enabled ######################################################################## def test_input_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._input, self.assertEqual) def test_input_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._input, self.assertEqual) def test_input_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._input, self.assertEqual) def test_input_focus_obtained_by_long_press_from_textarea(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._input) def test_input_focus_obtained_by_long_press_from_contenteditable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._input) def test_input_focus_obtained_by_long_press_from_content_non_editable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._input) def test_input_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other(self._input, self.assertEqual) ######################################################################## # <input> test cases with selection carets disabled ######################################################################## def test_input_long_press_to_select_a_word_disabled(self): self.open_test_html(enabled=False) self._test_long_press_to_select_a_word(self._input, self.assertNotEqual) def test_input_move_selection_carets_disabled(self): self.open_test_html(enabled=False) self._test_move_selection_carets(self._input, self.assertNotEqual) ######################################################################## # <textarea> test cases with selection carets enabled ######################################################################## def test_textarea_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea, self.assertEqual) def test_textarea_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._textarea, self.assertEqual) def test_textarea_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._textarea, self.assertEqual) def test_textarea_focus_obtained_by_long_press_from_input(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._textarea) def test_textarea_focus_obtained_by_long_press_from_contenteditable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._textarea) def test_textarea_focus_obtained_by_long_press_from_content_non_editable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._textarea) def test_textarea_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other(self._textarea, self.assertEqual) ######################################################################## # <textarea> test cases with selection carets disabled ######################################################################## def test_textarea_long_press_to_select_a_word_disabled(self): self.open_test_html(enabled=False) self._test_long_press_to_select_a_word(self._textarea, self.assertNotEqual) def test_textarea_move_selection_carets_disable(self): self.open_test_html(enabled=False) self._test_move_selection_carets(self._textarea, self.assertNotEqual) ######################################################################## # <textarea> right-to-left test cases with selection carets enabled ######################################################################## def test_textarea_rtl_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._textarea_rtl, self.assertEqual) ######################################################################## # <textarea> right-to-left test cases with selection carets disabled ######################################################################## def test_textarea_rtl_long_press_to_select_a_word_disabled(self): self.open_test_html(enabled=False) self._test_long_press_to_select_a_word(self._textarea_rtl, self.assertNotEqual) def test_textarea_rtl_move_selection_carets_disabled(self): self.open_test_html(enabled=False) self._test_move_selection_carets(self._textarea_rtl, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with selection carets enabled ######################################################################## def test_contenteditable_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._contenteditable, self.assertEqual) def test_contenteditable_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._contenteditable, self.assertEqual) def test_contenteditable_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._contenteditable, self.assertEqual) def test_contenteditable_focus_obtained_by_long_press_from_input(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._contenteditable) def test_contenteditable_focus_obtained_by_long_press_from_textarea(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._contenteditable) def test_contenteditable_focus_obtained_by_long_press_from_content_non_editable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._contenteditable) def test_contenteditable_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other(self._contenteditable, self.assertEqual) ######################################################################## # <div> contenteditable test cases with selection carets disabled ######################################################################## def test_contenteditable_long_press_to_select_a_word_disabled(self): self.open_test_html(enabled=False) self._test_long_press_to_select_a_word(self._contenteditable, self.assertNotEqual) def test_contenteditable_move_selection_carets_disabled(self): self.open_test_html(enabled=False) self._test_move_selection_carets(self._contenteditable, self.assertNotEqual) ######################################################################## # <div> non-editable test cases with selection carets enabled ######################################################################## def test_content_non_editable_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._content, self.assertEqual) def test_content_non_editable_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._content, self.assertEqual) def test_content_non_editable_minimum_select_one_character_by_selection(self): self.open_test_html() self._test_minimum_select_one_character(self._content, self.assertEqual) def test_content_non_editable_focus_obtained_by_long_press_from_input(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._content) def test_content_non_editable_focus_obtained_by_long_press_from_textarea(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._content) def test_content_non_editable_focus_obtained_by_long_press_from_contenteditable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._content) def test_content_non_editable_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other(self._content, self.assertEqual) ######################################################################## # <textarea> (multi-lines) test cases with selection carets enabled ######################################################################## def test_textarea2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._textarea2, self.assertEqual) ######################################################################## # <div> contenteditable2 (multi-lines) test cases with selection carets enabled ######################################################################## def test_contenteditable2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._contenteditable2, self.assertEqual) ######################################################################## # <div> non-editable2 (multi-lines) test cases with selection carets enabled ######################################################################## def test_content_non_editable2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._content2, self.assertEqual)
class CommonCaretsTestCase(object): """Common test cases for a selection with a two carets. To run these test cases, a subclass must inherit from both this class and MarionetteTestCase. """ def setUp(self): # Code to execute before a tests are run. super(CommonCaretsTestCase, self).setUp() self.actions = Actions(self.marionette) def open_test_html(self): "Open html for testing and locate elements." test_html = self.marionette.absolute_url("test_selectioncarets.html") self.marionette.navigate(test_html) self._input = self.marionette.find_element(By.ID, "input") self._textarea = self.marionette.find_element(By.ID, "textarea") self._textarea_rtl = self.marionette.find_element(By.ID, "textarea_rtl") self._contenteditable = self.marionette.find_element(By.ID, "contenteditable") self._content = self.marionette.find_element(By.ID, "content") self._non_selectable = self.marionette.find_element(By.ID, "non_selectable") def open_test_html2(self): "Open html for testing and locate elements." test_html2 = self.marionette.absolute_url("test_selectioncarets_multipleline.html") self.marionette.navigate(test_html2) self._textarea2 = self.marionette.find_element(By.ID, "textarea2") self._contenteditable2 = self.marionette.find_element(By.ID, "contenteditable2") self._content2 = self.marionette.find_element(By.ID, "content2") def open_test_html_multirange(self): "Open html for testing non-editable support." test_html = self.marionette.absolute_url("test_selectioncarets_multiplerange.html") self.marionette.navigate(test_html) self._body = self.marionette.find_element(By.ID, "bd") self._sel1 = self.marionette.find_element(By.ID, "sel1") self._sel2 = self.marionette.find_element(By.ID, "sel2") self._sel3 = self.marionette.find_element(By.ID, "sel3") self._sel4 = self.marionette.find_element(By.ID, "sel4") self._sel6 = self.marionette.find_element(By.ID, "sel6") self._nonsel1 = self.marionette.find_element(By.ID, "nonsel1") def open_test_html_long_text(self): "Open html for testing long text." test_html = self.marionette.absolute_url("test_selectioncarets_longtext.html") self.marionette.navigate(test_html) self._body = self.marionette.find_element(By.ID, "bd") self._longtext = self.marionette.find_element(By.ID, "longtext") def open_test_html_iframe(self): "Open html for testing iframe." test_html = self.marionette.absolute_url("test_selectioncarets_iframe.html") self.marionette.navigate(test_html) self._iframe = self.marionette.find_element(By.ID, "frame") def open_test_html_display_none(self): "Open html for testing html with display: none." test_html = self.marionette.absolute_url("test_carets_display_none.html") self.marionette.navigate(test_html) self._html = self.marionette.find_element(By.ID, "html") self._content = self.marionette.find_element(By.ID, "content") def word_offset(self, text, ordinal): "Get the character offset of the ordinal-th word in text." tokens = re.split(r"(\S+)", text) # both words and spaces spaces = tokens[0::2] # collect spaces at odd indices words = tokens[1::2] # collect word at even indices if ordinal >= len(words): raise IndexError("Only %d words in text, but got ordinal %d" % (len(words), ordinal)) # Cursor position of the targeting word is behind the the first # character in the word. For example, offset to 'def' in 'abc def' is # between 'd' and 'e'. offset = len(spaces[0]) + 1 offset += sum(len(words[i]) + len(spaces[i + 1]) for i in range(ordinal)) return offset def test_word_offset(self): text = " " * 3 + "abc" + " " * 3 + "def" self.assertTrue(self.word_offset(text, 0), 4) self.assertTrue(self.word_offset(text, 1), 10) with self.assertRaises(IndexError): self.word_offset(text, 2) def word_location(self, el, ordinal): """Get the location (x, y) of the ordinal-th word in el. The ordinal starts from 0. Note: this function has a side effect which changes focus to the target element el. """ sel = SelectionManager(el) offset = self.word_offset(sel.content, ordinal) # Move caret to the word. el.tap() sel.move_caret_to_front() sel.move_caret_by_offset(offset) x, y = sel.caret_location() return x, y def rect_relative_to_window(self, el): """Get element's bounding rectangle. This function is similar to el.rect, but the coordinate is relative to the top left corner of the window instead of the document. """ return self.marionette.execute_script( """ let rect = arguments[0].getBoundingClientRect(); return {x: rect.x, y:rect.y, width: rect.width, height: rect.height}; """, script_args=[el], ) def long_press_on_location(self, el, x=None, y=None): """Long press the location (x, y) to select a word. If no (x, y) are given, it will be targeted at the center of the element. On Windows, those spaces after the word will also be selected. This function sends synthesized eMouseLongTap to gecko. """ rect = self.rect_relative_to_window(el) target_x = rect["x"] + (x if x is not None else rect["width"] // 2) target_y = rect["y"] + (y if y is not None else rect["height"] // 2) self.marionette.execute_script( """ let Ci = Components.interfaces; let utils = window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); utils.sendTouchEventToWindow('touchstart', [0], [arguments[0]], [arguments[1]], [1], [1], [0], [1], 1, 0); utils.sendMouseEventToWindow('mouselongtap', arguments[0], arguments[1], 0, 1, 0); utils.sendTouchEventToWindow('touchend', [0], [arguments[0]], [arguments[1]], [1], [1], [0], [1], 1, 0); """, script_args=[target_x, target_y], sandbox="system", ) def long_press_on_word(self, el, wordOrdinal): x, y = self.word_location(el, wordOrdinal) self.long_press_on_location(el, x, y) def to_unix_line_ending(self, s): """Changes all Windows/Mac line endings in s to UNIX line endings.""" return s.replace("\r\n", "\n").replace("\r", "\n") def _test_long_press_to_select_a_word(self, el, assertFunc): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 2, "Expect at least two words in the content.") target_content = words[0] # Goal: Select the first word. self.long_press_on_word(el, 0) # Ignore extra spaces selected after the word. assertFunc(target_content, sel.selected_content) def _test_move_selection_carets(self, el, assertFunc): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, "Expect at least one word in the content.") # Goal: Select all text after the first word. target_content = original_content[len(words[0]) :] # Get the location of the selection carets at the end of the content for # later use. el.tap() sel.select_all() (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() self.long_press_on_word(el, 0) # Move the right caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Move the left caret to the previous position of the right caret. self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() assertFunc(target_content, sel.selected_content) def _test_minimum_select_one_character(self, el, assertFunc, x=None, y=None): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, "Expect at least one word in the content.") # Get the location of the selection carets at the end of the content for # later use. sel.select_all() (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() el.tap() # Goal: Select the first character. target_content = original_content[0] if x and y: # If we got x and y from the arguments, use it as a hint of the # location of the first word pass else: x, y = self.word_location(el, 0) self.long_press_on_location(el, x, y) # Move the right caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Move the right caret to the position of the left caret. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform() assertFunc(target_content, sel.selected_content) def _test_focus_obtained_by_long_press(self, el1, el2): """Test the focus could be changed from el1 to el2 by long press. If the focus is changed to e2 successfully, SelectionCarets should appear and could be dragged. """ # Goal: Tap to focus el1, and then select the first character on # el2. # We want to collect the location of the first word in el2 here # since self.word_location() has the side effect which would # change the focus. x, y = self.word_location(el2, 0) el1.tap() self._test_minimum_select_one_character(el2, self.assertEqual, x=x, y=y) def _test_focus_not_being_changed_by_long_press_on_non_selectable(self, el): # Goal: Focus remains on the editable element el after long pressing on # the non-selectable element. sel = SelectionManager(el) self.long_press_on_word(el, 0) self.long_press_on_location(self._non_selectable) active_sel = SelectionManager(self.marionette.get_active_element()) self.assertEqual(sel.content, active_sel.content) def _test_handle_tilt_when_carets_overlap_to_each_other(self, el, assertFunc): """Test tilt handling when carets overlap to each other. Let SelectionCarets overlap to each other. If SelectionCarets are set to tilted successfully, tapping the tilted carets should not cause the selection to be collapsed and the carets should be draggable. """ sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, "Expect at least one word in the content.") # Goal: Select the first word. self.long_press_on_word(el, 0) target_content = sel.selected_content # Move the left caret to the position of the right caret to trigger # carets overlapping. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() # We make two hit tests targeting the left edge of the left tilted caret # and the right edge of the right tilted caret. If either of the hits is # missed, selection would be collapsed and both carets should not be # draggable. (caret3_x, caret3_y), (caret4_x, caret4_y) = sel.selection_carets_location() # The following values are from ua.css and all.js if self.carets_tested_pref == "selectioncaret.enabled": caret_width = 44 caret_margin_left = -23 tilt_right_margin_left = 18 tilt_left_margin_left = -17 elif self.carets_tested_pref == "layout.accessiblecaret.enabled": caret_width = float(self.marionette.get_pref("layout.accessiblecaret.width")) caret_margin_left = float(self.marionette.get_pref("layout.accessiblecaret.margin-left")) tilt_right_margin_left = 0.41 * caret_width tilt_left_margin_left = -0.39 * caret_width left_caret_left_edge_x = caret3_x + caret_margin_left + tilt_left_margin_left el.tap(left_caret_left_edge_x + 2, caret3_y) right_caret_right_edge_x = caret4_x + caret_margin_left + tilt_right_margin_left + caret_width el.tap(right_caret_right_edge_x - 2, caret4_y) # Drag the left caret back to the initial selection, the first word. self.actions.flick(el, caret3_x, caret3_y, caret1_x, caret1_y).perform() assertFunc(target_content, sel.selected_content) def test_long_press_to_select_non_selectable_word(self): """Testing long press on non selectable field. We should not select anything when long press on non selectable fields.""" self.open_test_html_multirange() halfY = self._nonsel1.size["height"] / 2 self.long_press_on_location(self._nonsel1, 0, halfY) sel = SelectionManager(self._nonsel1) range_count = sel.range_count() self.assertEqual(range_count, 0) def test_drag_caret_over_non_selectable_field(self): """Testing drag caret over non selectable field. So that the selected content should exclude non selectable field and end selection caret should appear in last range's position.""" self.open_test_html_multirange() # Select target element and get target caret location self.long_press_on_word(self._sel4, 3) sel = SelectionManager(self._body) (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() self.long_press_on_word(self._sel6, 0) (_, _), (end_caret2_x, end_caret2_y) = sel.selection_carets_location() # Select start element self.long_press_on_word(self._sel3, 3) # Drag end caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), "this 3\nuser can select this") (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, end_caret2_x, end_caret2_y, 1).perform() self.assertEqual( self.to_unix_line_ending(sel.selected_content.strip()), "this 3\nuser can select this 4\nuser can select this 5\nuser", ) # Drag first caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret1_x, caret1_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), "4\nuser can select this 5\nuser") def test_drag_caret_to_beginning_of_a_line(self): """Bug 1094056 Test caret visibility when caret is dragged to beginning of a line """ self.open_test_html_multirange() # Select the first word in the second line self.long_press_on_word(self._sel2, 0) sel = SelectionManager(self._body) (start_caret_x, start_caret_y), (end_caret_x, end_caret_y) = sel.selection_carets_location() # Select target word in the first line self.long_press_on_word(self._sel1, 2) # Drag end caret to the beginning of the second line (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, start_caret_x, start_caret_y).perform() # Drag end caret back to the target word self.actions.flick(self._body, start_caret_x, start_caret_y, caret2_x, caret2_y).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content), "select") @skip_if_not_rotatable def test_caret_position_after_changing_orientation_of_device(self): """Bug 1094072 If positions of carets are updated correctly, they should be draggable. """ self.open_test_html_long_text() # Select word in portrait mode, then change to landscape mode self.marionette.set_orientation("portrait") self.long_press_on_word(self._longtext, 12) sel = SelectionManager(self._body) (p_start_caret_x, p_start_caret_y), (p_end_caret_x, p_end_caret_y) = sel.selection_carets_location() self.marionette.set_orientation("landscape") (l_start_caret_x, l_start_caret_y), (l_end_caret_x, l_end_caret_y) = sel.selection_carets_location() # Drag end caret to the start caret to change the selected content self.actions.flick(self._body, l_end_caret_x, l_end_caret_y, l_start_caret_x, l_start_caret_y).perform() # Change orientation back to portrait mode to prevent affecting # other tests self.marionette.set_orientation("portrait") self.assertEqual(self.to_unix_line_ending(sel.selected_content), "o") def test_select_word_inside_an_iframe(self): """Bug 1088552 The scroll offset in iframe should be taken into consideration properly. In this test, we scroll content in the iframe to the bottom to cause a huge offset. If we use the right coordinate system, selection should work. Otherwise, it would be hard to trigger select word. """ self.open_test_html_iframe() # switch to inner iframe and scroll to the bottom self.marionette.switch_to_frame(self._iframe) self.marionette.execute_script('document.getElementById("bd").scrollTop += 999') # long press to select bottom text self._body = self.marionette.find_element(By.ID, "bd") sel = SelectionManager(self._body) self._bottomtext = self.marionette.find_element(By.ID, "bottomtext") self.long_press_on_location(self._bottomtext) self.assertNotEqual(self.to_unix_line_ending(sel.selected_content), "") def test_carets_initialized_in_display_none(self): """Test AccessibleCaretEventHub is properly initialized on a <html> with display: none. """ self.open_test_html_display_none() # Remove 'display: none' from <html> self.marionette.execute_script('arguments[0].style.display = "unset";', script_args=[self._html]) # If AccessibleCaretEventHub is initialized successfully, select a word # should work. self._test_long_press_to_select_a_word(self._content, self.assertEqual) ######################################################################## # <input> test cases with selection carets enabled ######################################################################## def test_input_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._input, self.assertEqual) def test_input_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._input, self.assertEqual) def test_input_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._input, self.assertEqual) def test_input_focus_obtained_by_long_press_from_textarea(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._input) def test_input_focus_obtained_by_long_press_from_contenteditable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._input) def test_input_focus_obtained_by_long_press_from_content_non_editable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._input) def test_input_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other(self._input, self.assertEqual) def test_input_focus_not_changed_by_long_press_on_non_selectable(self): self.open_test_html() self._test_focus_not_being_changed_by_long_press_on_non_selectable(self._input) ######################################################################## # <input> test cases with selection carets disabled ######################################################################## def test_input_long_press_to_select_a_word_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_long_press_to_select_a_word(self._input, self.assertNotEqual) def test_input_move_selection_carets_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_move_selection_carets(self._input, self.assertNotEqual) ######################################################################## # <textarea> test cases with selection carets enabled ######################################################################## def test_textarea_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea, self.assertEqual) def test_textarea_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._textarea, self.assertEqual) def test_textarea_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._textarea, self.assertEqual) def test_textarea_focus_obtained_by_long_press_from_input(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._textarea) def test_textarea_focus_obtained_by_long_press_from_contenteditable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._textarea) def test_textarea_focus_obtained_by_long_press_from_content_non_editable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._textarea) def test_textarea_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other(self._textarea, self.assertEqual) def test_textarea_focus_not_changed_by_long_press_on_non_selectable(self): self.open_test_html() self._test_focus_not_being_changed_by_long_press_on_non_selectable(self._textarea) ######################################################################## # <textarea> test cases with selection carets disabled ######################################################################## def test_textarea_long_press_to_select_a_word_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea, self.assertNotEqual) def test_textarea_move_selection_carets_disable(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_move_selection_carets(self._textarea, self.assertNotEqual) ######################################################################## # <textarea> right-to-left test cases with selection carets enabled ######################################################################## def test_textarea_rtl_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_focus_not_changed_by_long_press_on_non_selectable(self): self.open_test_html() self._test_focus_not_being_changed_by_long_press_on_non_selectable(self._textarea_rtl) ######################################################################## # <textarea> right-to-left test cases with selection carets disabled ######################################################################## def test_textarea_rtl_long_press_to_select_a_word_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea_rtl, self.assertNotEqual) def test_textarea_rtl_move_selection_carets_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_move_selection_carets(self._textarea_rtl, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with selection carets enabled ######################################################################## def test_contenteditable_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._contenteditable, self.assertEqual) def test_contenteditable_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._contenteditable, self.assertEqual) def test_contenteditable_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._contenteditable, self.assertEqual) def test_contenteditable_focus_obtained_by_long_press_from_input(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._contenteditable) def test_contenteditable_focus_obtained_by_long_press_from_textarea(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._contenteditable) def test_contenteditable_focus_obtained_by_long_press_from_content_non_editable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._contenteditable) def test_contenteditable_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other(self._contenteditable, self.assertEqual) def test_contenteditable_focus_not_changed_by_long_press_on_non_selectable(self): self.open_test_html() self._test_focus_not_being_changed_by_long_press_on_non_selectable(self._contenteditable) ######################################################################## # <div> contenteditable test cases with selection carets disabled ######################################################################## def test_contenteditable_long_press_to_select_a_word_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_long_press_to_select_a_word(self._contenteditable, self.assertNotEqual) def test_contenteditable_move_selection_carets_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_move_selection_carets(self._contenteditable, self.assertNotEqual) ######################################################################## # <div> non-editable test cases with selection carets enabled ######################################################################## def test_content_non_editable_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._content, self.assertEqual) def test_content_non_editable_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._content, self.assertEqual) def test_content_non_editable_minimum_select_one_character_by_selection(self): self.open_test_html() self._test_minimum_select_one_character(self._content, self.assertEqual) def test_content_non_editable_focus_obtained_by_long_press_from_input(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._content) def test_content_non_editable_focus_obtained_by_long_press_from_textarea(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._content) def test_content_non_editable_focus_obtained_by_long_press_from_contenteditable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._content) def test_content_non_editable_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other(self._content, self.assertEqual) ######################################################################## # <textarea> (multi-lines) test cases with selection carets enabled ######################################################################## def test_textarea2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._textarea2, self.assertEqual) ######################################################################## # <div> contenteditable2 (multi-lines) test cases with selection carets enabled ######################################################################## def test_contenteditable2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._contenteditable2, self.assertEqual) ######################################################################## # <div> non-editable2 (multi-lines) test cases with selection carets enabled ######################################################################## def test_content_non_editable2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._content2, self.assertEqual)
class element(object): def __init__(self, parent): self.parent = parent self.marionette = parent.marionette self.actions = Actions(self.marionette) def getElement(self, elem, msg, is_displayed=True, timeout=5, stop_on_error=True): """ Returns an element, or False it it's not found """ x = self.getElements(elem, msg, is_displayed, timeout, stop_on_error) if x: # We're expecting ONE element back (it has different methods if it's one). return x[0] else: return False def getElements(self, elem, msg, is_displayed=True, timeout=5, stop_on_error=True): """ Returns a list of matching elements, or False if none are found """ boolEl = self.waitForElements(elem, msg, is_displayed, timeout, stop_on_error) if boolEl: el = self.marionette.find_elements(*elem) return el else: return False def headerCheck(self, value): """ Returns the header that matches a string. NOTE: ALL headers in this iframe return true for ".is_displayed()"! """ is_ok = False try: self.parent.parent.wait_for_element_present(*DOM.GLOBAL.app_head, timeout=1) headerNames = self.marionette.find_elements(*DOM.GLOBAL.app_head) for i in headerNames: if i.text == value: if i.is_displayed(): is_ok = True break except: is_ok = False self.parent.test.test(is_ok, "Header is \"" + value + "\".") return is_ok def move_scroller(self, scroller, forward=True): """Move the scroller one item forward or backwards. """ x_pos = scroller.size['width'] / 2 y_start = scroller.size['height'] / 2 y_end = -scroller.size['height'] if forward else scroller.size['height'] self.actions.flick(scroller, x_pos, y_start, x_pos, y_end, 100) self.actions.perform() def set_scroller_val(self, scroller_elem, number): """ Set the numeric value of a scroller (only works with numbers right now). """ current_value = int(scroller_elem.find_element(*DOM.GLOBAL.scroller_curr_val).text) # Now flick the scroller as many times as required n = int(number) while n != current_value: # Do we need to go forwards or backwards? if n > int(current_value): self.move_scroller(scroller_elem, True) if n < int(current_value): self.move_scroller(scroller_elem, False) # Get the new 'current_value'. current_value = int(scroller_elem.find_element(*DOM.GLOBAL.scroller_curr_val).text) # From gaiatest Clock -> regions -> alarm.py def _flick_menu_up(self, locator): self.parent.parent.wait_for_element_displayed(*self._current_element(*locator), timeout=2) current_element = self.marionette.find_element(*self._current_element(*locator)) next_element = self.marionette.find_element(*self._next_element(*locator)) # TODO: update this with more accurate Actions action = Actions(self.marionette) action.press(next_element) action.move(current_element) action.release() action.perform() def _flick_menu_down(self, locator): self.parent.parent.wait_for_element_displayed(*self._current_element(*locator), timeout=2) current_element = self.marionette.find_element(*self._current_element(*locator)) next_element = self.marionette.find_element(*self._next_element(*locator)) # TODO: update this with more accurate Actions action = Actions(self.marionette) action.press(current_element) action.move(next_element) action.release() action.perform() def _current_element(self, method, target): self.parent.reporting.debug("*** Finding current element for target {}".format(target)) return (method, '{}.picker-unit.selected'.format(target)) def _next_element(self, method, target): self.parent.reporting.debug("*** Finding next element for target {}".format(target)) return (method, '{}.picker-unit.selected + div'.format(target)) def simulateClick(self, element): self.marionette.execute_script(""" /** * Helper method to simulate clicks on iFrames which is not currently * working in the Marionette JS Runner. * @param {Marionette.Element} element The element to simulate the click on. **/ var event = new MouseEvent('click', { 'view': window, 'bubbles': true, 'cancelable': true }); arguments[0].dispatchEvent(event); """, script_args=[element]) def simulateClickAtPos(self, element, posX, posY): self.parent.reporting.logResult('info', 'Simulating click....') self.marionette.execute_script(""" /** * Helper method to simulate clicks on iFrames which is not currently * working in the Marionette JS Runner. * @param {Marionette.Element} element The element to simulate the click on. **/ var event = document.createEvent('MouseEvents'); event.initMouseEvent( 'click', true, true, window, 0, 0, 0, arguments[1], arguments[2], false, false, false, false, 0, null ); arguments[0].dispatchEvent(event); """, script_args=[element, posX, posY]) def waitForElements(self, elem, msg, is_displayed=True, timeout=5, stop_on_error=True): """ Waits for an element to be displayed and captures the error if not """ is_ok = True msg = u"" + msg try: if is_displayed: msg = u"{} displayed within {} seconds.|{}".format(msg, timeout, elem) self.parent.parent.wait_for_element_displayed(*elem, timeout=timeout) else: msg = u"{} present within {} seconds.|{}".format(msg, timeout, elem) self.parent.parent.wait_for_element_present(*elem, timeout=timeout) except Exception: is_ok = False self.parent.test.test(is_ok, msg, stop_on_error) return is_ok def waitForNotElements(self, elem, msg, is_displayed=True, timeout=5, stop_on_error=True): """ Waits for an element to be displayed and captures the error if not """ is_ok = True try: if is_displayed: msg = "{} no longer displayed within {} seconds.|{}".format(msg, timeout, elem) self.parent.parent.wait_for_element_not_displayed(*elem, timeout=timeout) else: msg = "{} no longer present within {} seconds.|{}".format(msg, timeout, elem) self.parent.parent.wait_for_element_not_present(*elem, timeout=timeout) except: is_ok = False self.parent.test.test(is_ok, msg, stop_on_error) return is_ok def getElementByXpath(self, path): """ Use this function when normal getElement did not work """ return self.marionette.execute_script(""" return document.evaluate(arguments[0], document, null, 9, null).singleNodeValue; """, script_args=[path]) def getParent(self, element): """ Gets the element's parent. Can be called recursively """ return self.marionette.execute_script(""" return arguments[0].parentNode; """, script_args=[element]) def getChildren(self, element): """ Gets the element's children """ return self.marionette.execute_script(""" return arguments[0].children; """, script_args=[element]) def find_nested(self, context, css_selector): return self.marionette.execute_script(""" return arguments[0].querySelector(arguments[1]) """, script_args=[context, css_selector]) def get_css_value(self, element, css_property): """ Gets the value of a certain css property. """ return self.marionette.execute_script(""" function getStyle (el, styleProp) { if (el.currentStyle) var y = x.currentStyle[styleProp]; else if (window.getComputedStyle) var y = document.defaultView.getComputedStyle(el,null) .getPropertyValue(styleProp); return y; } return getStyle(arguments[0], arguments[1]) """, script_args=[element, css_property]) def is_ellipsis_active(self, element): """ Checks whether a certain element is really ellipsed when its content overflows its width """ return self.marionette.execute_script(""" function isEllipsisActive(element) { return (element.offsetWidth < element.scrollWidth); } return isEllipsisActive(arguments[0]) """, script_args=[element]) def scroll_into_view(self, element): self.marionette.execute_script(""" arguments[0].scrollIntoView(); """, script_args=[element]) def perform_action_over_element(self, locator, action, position=None): script = """ var _locatorMap = { "id": document.getElementById, "class name": document.getElementsByClassName, "css selector": document.querySelector, "xpath": function () { return document.evaluate(arguments[0], document, null, 9, null).singleNodeValue }, "tag name": document.getElementsByTagName }; // TODO - Add more events here var _actionMap = { "click": new MouseEvent('click', { 'view': window, 'bubbles': true, 'cancelable': true }), //HTMLElement.prototype.click } var location_method = arguments[0][0]; var locator = arguments[0][1]; var action = arguments[1]; var position = arguments[2]; if (position) { var element = _locatorMap[location_method].call(document, locator)[position]; } else { if ((locator === "class name") || (locator === "tag name")) { var e = 'JavaScriptException: InvalidParametersException: ' var msg = 'If using "class name" or "tag name", it is mandatory to specify a position' throw e + msg } var element = _locatorMap[location_method].call(document, locator); } if (element) { if (_actionMap.hasOwnProperty(action)) { element.dispatchEvent(_actionMap[action]) } else { var e = 'JavaScriptException: InvalidParametersException: ' var msg = 'Specified action <' + action + '> not supported'; throw e + msg } } """ self.marionette.execute_script(script, script_args=[list(locator), action, position])
class SelectionCaretsMultipleRangeTest(MarionetteTestCase): _long_press_time = 1 # 1 second def setUp(self): # Code to execute before a tests are run. MarionetteTestCase.setUp(self) self.actions = Actions(self.marionette) def openTestHtml(self, enabled=True): # Open html for testing and enable selectioncaret and # non-editable support self.marionette.execute_async_script( 'SpecialPowers.pushPrefEnv({"set": [["selectioncaret.enabled", %s],["selectioncaret.noneditable", %s]]}, marionetteScriptFinished);' % ( ('true' if enabled else 'false'), ('true' if enabled else 'false'))) test_html = self.marionette.absolute_url('test_selectioncarets_multiplerange.html') self.marionette.navigate(test_html) self._body = self.marionette.find_element(By.ID, 'bd') self._sel1 = self.marionette.find_element(By.ID, 'sel1') self._sel2 = self.marionette.find_element(By.ID, 'sel2') self._sel3 = self.marionette.find_element(By.ID, 'sel3') self._sel4 = self.marionette.find_element(By.ID, 'sel4') self._sel6 = self.marionette.find_element(By.ID, 'sel6') self._nonsel1 = self.marionette.find_element(By.ID, 'nonsel1') def openTestHtmlLongText(self, enabled=True): # Open html for testing and enable selectioncaret self.marionette.execute_script( 'SpecialPowers.setBoolPref("selectioncaret.enabled", %s);' % ('true' if enabled else 'false')) test_html = self.marionette.absolute_url('test_selectioncarets_longtext.html') self.marionette.navigate(test_html) self._body = self.marionette.find_element(By.ID, 'bd') self._longtext = self.marionette.find_element(By.ID, 'longtext') def openTestHtmlIframe(self, enabled=True): # Open html for testing and enable selectioncaret self.marionette.execute_script( 'SpecialPowers.setBoolPref("selectioncaret.enabled", %s);' % ('true' if enabled else 'false')) test_html = self.marionette.absolute_url('test_selectioncarets_iframe.html') self.marionette.navigate(test_html) self._iframe = self.marionette.find_element(By.ID, 'frame') def _long_press_to_select_word(self, el, wordOrdinal): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(wordOrdinal < len(words), 'Expect at least %d words in the content.' % wordOrdinal) # Calc offset offset = 0 for i in range(wordOrdinal): offset += (len(words[i]) + 1) # Move caret inside the word. el.tap() sel.move_caret_to_front() sel.move_caret_by_offset(offset) x, y = sel.caret_location() # Long press the caret position. Selection carets should appear, and the # word will be selected. On Windows, those spaces after the word # will also be selected. long_press_without_contextmenu(self.marionette, el, self._long_press_time, x, y) def _to_unix_line_ending(self, s): """Changes all Windows/Mac line endings in s to UNIX line endings.""" return s.replace('\r\n', '\n').replace('\r', '\n') def test_long_press_to_select_non_selectable_word(self): '''Testing long press on non selectable field. We should not select anything when long press on non selectable fields.''' self.openTestHtml(enabled=True) halfY = self._nonsel1.size['height'] / 2 long_press_without_contextmenu(self.marionette, self._nonsel1, self._long_press_time, 0, halfY) sel = SelectionManager(self._nonsel1) range_count = sel.range_count() self.assertEqual(range_count, 0) def test_drag_caret_over_non_selectable_field(self): '''Testing drag caret over non selectable field. So that the selected content should exclude non selectable field and end selection caret should appear in last range's position.''' self.openTestHtml(enabled=True) # Select target element and get target caret location self._long_press_to_select_word(self._sel4, 3) sel = SelectionManager(self._body) (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() self._long_press_to_select_word(self._sel6, 0) (_, _), (end_caret2_x, end_caret2_y) = sel.selection_carets_location() # Select start element self._long_press_to_select_word(self._sel3, 3) # Drag end caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()), 'this 3\nuser can select this') (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, end_caret2_x, end_caret2_y, 1).perform() self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()), 'this 3\nuser can select this 4\nuser can select this 5\nuser') # Drag first caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret1_x, caret1_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()), '4\nuser can select this 5\nuser') def test_drag_caret_to_beginning_of_a_line(self): '''Bug 1094056 Test caret visibility when caret is dragged to beginning of a line ''' self.openTestHtml(enabled=True) # Select the first word in the second line self._long_press_to_select_word(self._sel2, 0) sel = SelectionManager(self._body) (start_caret_x, start_caret_y), (end_caret_x, end_caret_y) = sel.selection_carets_location() # Select target word in the first line self._long_press_to_select_word(self._sel1, 2) # Drag end caret to the beginning of the second line (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, start_caret_x, start_caret_y).perform() # Drag end caret back to the target word self.actions.flick(self._body, start_caret_x, start_caret_y, caret2_x, caret2_y).perform() self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()), 'select') def test_caret_position_after_changing_orientation_of_device(self): '''Bug 1094072 If positions of carets are updated correctly, they should be draggable. ''' # Skip running test on non-rotatable device ex.desktop browser if not self.marionette.session_capabilities['rotatable']: return self.openTestHtmlLongText(enabled=True) # Select word in portrait mode, then change to landscape mode self.marionette.set_orientation('portrait') self._long_press_to_select_word(self._longtext, 12) sel = SelectionManager(self._body) (p_start_caret_x, p_start_caret_y), (p_end_caret_x, p_end_caret_y) = sel.selection_carets_location() self.marionette.set_orientation('landscape') (l_start_caret_x, l_start_caret_y), (l_end_caret_x, l_end_caret_y) = sel.selection_carets_location() # Drag end caret to the start caret to change the selected content self.actions.flick(self._body, l_end_caret_x, l_end_caret_y, l_start_caret_x, l_start_caret_y).perform() # Change orientation back to portrait mode to prevent affecting # other tests self.marionette.set_orientation('portrait') self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()), 'o') def test_select_word_inside_an_iframe(self): '''Bug 1088552 The scroll offset in iframe should be taken into consideration properly. In this test, we scroll content in the iframe to the bottom to cause a huge offset. If we use the right coordinate system, selection should work. Otherwise, it would be hard to trigger select word. ''' self.openTestHtmlIframe(enabled=True) # switch to inner iframe and scroll to the bottom self.marionette.switch_to_frame(self._iframe) self.marionette.execute_script( 'document.getElementById("bd").scrollTop += 999') # long press to select bottom text self._body = self.marionette.find_element(By.ID, 'bd') sel = SelectionManager(self._body) self._bottomtext = self.marionette.find_element(By.ID, 'bottomtext') long_press_without_contextmenu(self.marionette, self._bottomtext, self._long_press_time) self.assertNotEqual(self._to_unix_line_ending(sel.selected_content.strip()), '')
class SelectionCaretsMultipleRangeTest(MarionetteTestCase): _long_press_time = 1 # 1 second def setUp(self): # Code to execute before a tests are run. MarionetteTestCase.setUp(self) self.actions = Actions(self.marionette) def openTestHtml(self, enabled=True): # Open html for testing and enable selectioncaret and # non-editable support self.marionette.execute_script( 'SpecialPowers.setBoolPref("selectioncaret.enabled", %s);' % ('true' if enabled else 'false')) self.marionette.execute_script( 'SpecialPowers.setBoolPref("selectioncaret.noneditable", %s);' % ('true' if enabled else 'false')) test_html = self.marionette.absolute_url('test_selectioncarets_multiplerange.html') self.marionette.navigate(test_html) self._body = self.marionette.find_element(By.ID, 'bd') self._sel1 = self.marionette.find_element(By.ID, 'sel1') self._sel2 = self.marionette.find_element(By.ID, 'sel2') self._sel3 = self.marionette.find_element(By.ID, 'sel3') self._sel4 = self.marionette.find_element(By.ID, 'sel4') self._sel6 = self.marionette.find_element(By.ID, 'sel6') self._nonsel1 = self.marionette.find_element(By.ID, 'nonsel1') def _long_press_to_select_word(self, el, wordOrdinal): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(wordOrdinal < len(words), 'Expect at least %d words in the content.' % wordOrdinal) # Calc offset offset = 0 for i in range(wordOrdinal): offset += (len(words[i]) + 1) # Move caret inside the word. el.tap() sel.move_caret_to_front() sel.move_caret_by_offset(offset) x, y = sel.caret_location() # Long press the caret position. Selection carets should appear, and the # word will be selected. On Windows, those spaces after the word # will also be selected. long_press_without_contextmenu(self.marionette, el, self._long_press_time, x, y) def _to_unix_line_ending(self, s): """Changes all Windows/Mac line endings in s to UNIX line endings.""" return s.replace('\r\n', '\n').replace('\r', '\n') def test_long_press_to_select_non_selectable_word(self): '''Testing long press on non selectable field. We should not select anything when long press on non selectable fields.''' self.openTestHtml(enabled=True) halfY = self._nonsel1.size['height'] / 2 long_press_without_contextmenu(self.marionette, self._nonsel1, self._long_press_time, 0, halfY) sel = SelectionManager(self._nonsel1) range_count = sel.range_count() self.assertEqual(range_count, 0) def test_drag_caret_over_non_selectable_field(self): '''Testing drag caret over non selectable field. So that the selected content should exclude non selectable field and end selection caret should appear in last range's position.''' self.openTestHtml(enabled=True) # Select target element and get target caret location self._long_press_to_select_word(self._sel4, 3) sel = SelectionManager(self._body) (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() self._long_press_to_select_word(self._sel6, 0) (_, _), (end_caret2_x, end_caret2_y) = sel.selection_carets_location() # Select start element self._long_press_to_select_word(self._sel3, 3) # Drag end caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()), 'this 3\nuser can select this') (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, end_caret2_x, end_caret2_y, 1).perform() self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()), 'this 3\nuser can select this 4\nuser can select this 5\nuser') def test_drag_caret_to_beginning_of_a_line(self): '''Bug 1094056 Test caret visibility when caret is dragged to beginning of a line ''' self.openTestHtml(enabled=True) # Select the first word in the second line self._long_press_to_select_word(self._sel2, 0) sel = SelectionManager(self._body) (start_caret_x, start_caret_y), (end_caret_x, end_caret_y) = sel.selection_carets_location() # Select target word in the first line self._long_press_to_select_word(self._sel1, 2) # Drag end caret to the beginning of the second line (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, start_caret_x, start_caret_y).perform() # Drag end caret back to the target word self.actions.flick(self._body, start_caret_x, start_caret_y, caret2_x, caret2_y).perform() self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()), 'select')
class AccessibleCaretSelectionModeTestCase(MarionetteTestCase): '''Test cases for AccessibleCaret under selection mode, aka selection carets. ''' def setUp(self): # Code to execute before a tests are run. super(AccessibleCaretSelectionModeTestCase, self).setUp() self.carets_tested_pref = 'layout.accessiblecaret.enabled' self.prefs = { 'layout.word_select.eat_space_to_next_word': False, 'layout.accessiblecaret.use_long_tap_injector': False, self.carets_tested_pref: True, } self.marionette.set_prefs(self.prefs) self.actions = Actions(self.marionette) def open_test_html(self): 'Open html for testing and locate elements.' test_html = self.marionette.absolute_url('test_selectioncarets.html') self.marionette.navigate(test_html) self._input = self.marionette.find_element(By.ID, 'input') self._textarea = self.marionette.find_element(By.ID, 'textarea') self._textarea_rtl = self.marionette.find_element(By.ID, 'textarea_rtl') self._contenteditable = self.marionette.find_element(By.ID, 'contenteditable') self._content = self.marionette.find_element(By.ID, 'content') self._non_selectable = self.marionette.find_element(By.ID, 'non_selectable') def open_test_html2(self): 'Open html for testing and locate elements.' test_html2 = self.marionette.absolute_url('test_selectioncarets_multipleline.html') self.marionette.navigate(test_html2) self._textarea2 = self.marionette.find_element(By.ID, 'textarea2') self._contenteditable2 = self.marionette.find_element(By.ID, 'contenteditable2') self._content2 = self.marionette.find_element(By.ID, 'content2') def open_test_html_multirange(self): 'Open html for testing non-editable support.' test_html = self.marionette.absolute_url('test_selectioncarets_multiplerange.html') self.marionette.navigate(test_html) self._body = self.marionette.find_element(By.ID, 'bd') self._sel1 = self.marionette.find_element(By.ID, 'sel1') self._sel2 = self.marionette.find_element(By.ID, 'sel2') self._sel3 = self.marionette.find_element(By.ID, 'sel3') self._sel4 = self.marionette.find_element(By.ID, 'sel4') self._sel6 = self.marionette.find_element(By.ID, 'sel6') self._nonsel1 = self.marionette.find_element(By.ID, 'nonsel1') def open_test_html_long_text(self): 'Open html for testing long text.' test_html = self.marionette.absolute_url('test_selectioncarets_longtext.html') self.marionette.navigate(test_html) self._body = self.marionette.find_element(By.ID, 'bd') self._longtext = self.marionette.find_element(By.ID, 'longtext') def open_test_html_iframe(self): 'Open html for testing iframe.' test_html = self.marionette.absolute_url('test_selectioncarets_iframe.html') self.marionette.navigate(test_html) self._iframe = self.marionette.find_element(By.ID, 'frame') def open_test_html_display_none(self): 'Open html for testing html with display: none.' test_html = self.marionette.absolute_url('test_carets_display_none.html') self.marionette.navigate(test_html) self._html = self.marionette.find_element(By.ID, 'html') self._content = self.marionette.find_element(By.ID, 'content') def word_offset(self, text, ordinal): 'Get the character offset of the ordinal-th word in text.' tokens = re.split(r'(\S+)', text) # both words and spaces spaces = tokens[0::2] # collect spaces at odd indices words = tokens[1::2] # collect word at even indices if ordinal >= len(words): raise IndexError('Only %d words in text, but got ordinal %d' % (len(words), ordinal)) # Cursor position of the targeting word is behind the the first # character in the word. For example, offset to 'def' in 'abc def' is # between 'd' and 'e'. offset = len(spaces[0]) + 1 offset += sum(len(words[i]) + len(spaces[i + 1]) for i in range(ordinal)) return offset def test_word_offset(self): text = ' ' * 3 + 'abc' + ' ' * 3 + 'def' self.assertTrue(self.word_offset(text, 0), 4) self.assertTrue(self.word_offset(text, 1), 10) with self.assertRaises(IndexError): self.word_offset(text, 2) def word_location(self, el, ordinal): '''Get the location (x, y) of the ordinal-th word in el. The ordinal starts from 0. Note: this function has a side effect which changes focus to the target element el. ''' sel = SelectionManager(el) offset = self.word_offset(sel.content, ordinal) # Move caret to the word. el.tap() sel.move_caret_to_front() sel.move_caret_by_offset(offset) x, y = sel.caret_location() return x, y def rect_relative_to_window(self, el): '''Get element's bounding rectangle. This function is similar to el.rect, but the coordinate is relative to the top left corner of the window instead of the document. ''' return self.marionette.execute_script(''' let rect = arguments[0].getBoundingClientRect(); return {x: rect.x, y:rect.y, width: rect.width, height: rect.height}; ''', script_args=[el]) def long_press_on_location(self, el, x=None, y=None): '''Long press the location (x, y) to select a word. If no (x, y) are given, it will be targeted at the center of the element. On Windows, those spaces after the word will also be selected. This function sends synthesized eMouseLongTap to gecko. ''' rect = self.rect_relative_to_window(el) target_x = rect['x'] + (x if x is not None else rect['width'] // 2) target_y = rect['y'] + (y if y is not None else rect['height'] // 2) self.marionette.execute_script(''' let Ci = Components.interfaces; let utils = window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); utils.sendTouchEventToWindow('touchstart', [0], [arguments[0]], [arguments[1]], [1], [1], [0], [1], 1, 0); utils.sendMouseEventToWindow('mouselongtap', arguments[0], arguments[1], 0, 1, 0); utils.sendTouchEventToWindow('touchend', [0], [arguments[0]], [arguments[1]], [1], [1], [0], [1], 1, 0); ''', script_args=[target_x, target_y], sandbox='system') def long_press_on_word(self, el, wordOrdinal): x, y = self.word_location(el, wordOrdinal) self.long_press_on_location(el, x, y) def to_unix_line_ending(self, s): """Changes all Windows/Mac line endings in s to UNIX line endings.""" return s.replace('\r\n', '\n').replace('\r', '\n') def _test_long_press_to_select_a_word(self, el, assertFunc): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 2, 'Expect at least two words in the content.') target_content = words[0] # Goal: Select the first word. self.long_press_on_word(el, 0) # Ignore extra spaces selected after the word. assertFunc(target_content, sel.selected_content) def _test_move_selection_carets(self, el, assertFunc): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.') # Goal: Select all text after the first word. target_content = original_content[len(words[0]):] # Get the location of the selection carets at the end of the content for # later use. el.tap() sel.select_all() (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() self.long_press_on_word(el, 0) # Move the right caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Move the left caret to the previous position of the right caret. self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() assertFunc(target_content, sel.selected_content) def _test_minimum_select_one_character(self, el, assertFunc, x=None, y=None): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.') # Get the location of the selection carets at the end of the content for # later use. sel.select_all() (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() el.tap() # Goal: Select the first character. target_content = original_content[0] if x and y: # If we got x and y from the arguments, use it as a hint of the # location of the first word pass else: x, y = self.word_location(el, 0) self.long_press_on_location(el, x, y) # Move the right caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Move the right caret to the position of the left caret. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform() assertFunc(target_content, sel.selected_content) def _test_focus_obtained_by_long_press(self, el1, el2): '''Test the focus could be changed from el1 to el2 by long press. If the focus is changed to e2 successfully, SelectionCarets should appear and could be dragged. ''' # Goal: Tap to focus el1, and then select the first character on # el2. # We want to collect the location of the first word in el2 here # since self.word_location() has the side effect which would # change the focus. x, y = self.word_location(el2, 0) el1.tap() self._test_minimum_select_one_character(el2, self.assertEqual, x=x, y=y) def _test_focus_not_being_changed_by_long_press_on_non_selectable(self, el): # Goal: Focus remains on the editable element el after long pressing on # the non-selectable element. sel = SelectionManager(el) self.long_press_on_word(el, 0) self.long_press_on_location(self._non_selectable) active_sel = SelectionManager(self.marionette.get_active_element()) self.assertEqual(sel.content, active_sel.content) def _test_handle_tilt_when_carets_overlap_to_each_other(self, el, assertFunc): '''Test tilt handling when carets overlap to each other. Let SelectionCarets overlap to each other. If SelectionCarets are set to tilted successfully, tapping the tilted carets should not cause the selection to be collapsed and the carets should be draggable. ''' sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.') # Goal: Select the first word. self.long_press_on_word(el, 0) target_content = sel.selected_content # Move the left caret to the position of the right caret to trigger # carets overlapping. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() # We make two hit tests targeting the left edge of the left tilted caret # and the right edge of the right tilted caret. If either of the hits is # missed, selection would be collapsed and both carets should not be # draggable. (caret3_x, caret3_y), (caret4_x, caret4_y) = sel.selection_carets_location() # The following values are from ua.css and all.js if self.carets_tested_pref == 'selectioncaret.enabled': caret_width = 44 caret_margin_left = -23 tilt_right_margin_left = 18 tilt_left_margin_left = -17 elif self.carets_tested_pref == 'layout.accessiblecaret.enabled': caret_width = float(self.marionette.get_pref('layout.accessiblecaret.width')) caret_margin_left = float(self.marionette.get_pref('layout.accessiblecaret.margin-left')) tilt_right_margin_left = 0.41 * caret_width; tilt_left_margin_left = -0.39 * caret_width; left_caret_left_edge_x = caret3_x + caret_margin_left + tilt_left_margin_left el.tap(left_caret_left_edge_x + 2, caret3_y) right_caret_right_edge_x = (caret4_x + caret_margin_left + tilt_right_margin_left + caret_width) el.tap(right_caret_right_edge_x - 2, caret4_y) # Drag the left caret back to the initial selection, the first word. self.actions.flick(el, caret3_x, caret3_y, caret1_x, caret1_y).perform() assertFunc(target_content, sel.selected_content) def test_long_press_to_select_non_selectable_word(self): '''Testing long press on non selectable field. We should not select anything when long press on non selectable fields.''' self.open_test_html_multirange() halfY = self._nonsel1.size['height'] / 2 self.long_press_on_location(self._nonsel1, 0, halfY) sel = SelectionManager(self._nonsel1) range_count = sel.range_count() self.assertEqual(range_count, 0) def test_drag_caret_over_non_selectable_field(self): '''Testing drag caret over non selectable field. So that the selected content should exclude non selectable field and end selection caret should appear in last range's position.''' self.open_test_html_multirange() # Select target element and get target caret location self.long_press_on_word(self._sel4, 3) sel = SelectionManager(self._body) (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() self.long_press_on_word(self._sel6, 0) (_, _), (end_caret2_x, end_caret2_y) = sel.selection_carets_location() # Select start element self.long_press_on_word(self._sel3, 3) # Drag end caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), 'this 3\nuser can select this') (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, end_caret2_x, end_caret2_y, 1).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), 'this 3\nuser can select this 4\nuser can select this 5\nuser') # Drag first caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret1_x, caret1_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), '4\nuser can select this 5\nuser') def test_drag_caret_to_beginning_of_a_line(self): '''Bug 1094056 Test caret visibility when caret is dragged to beginning of a line ''' self.open_test_html_multirange() # Select the first word in the second line self.long_press_on_word(self._sel2, 0) sel = SelectionManager(self._body) (start_caret_x, start_caret_y), (end_caret_x, end_caret_y) = sel.selection_carets_location() # Select target word in the first line self.long_press_on_word(self._sel1, 2) # Drag end caret to the beginning of the second line (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, start_caret_x, start_caret_y).perform() # Drag end caret back to the target word self.actions.flick(self._body, start_caret_x, start_caret_y, caret2_x, caret2_y).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content), 'select') @skip_if_not_rotatable def test_caret_position_after_changing_orientation_of_device(self): '''Bug 1094072 If positions of carets are updated correctly, they should be draggable. ''' self.open_test_html_long_text() # Select word in portrait mode, then change to landscape mode self.marionette.set_orientation('portrait') self.long_press_on_word(self._longtext, 12) sel = SelectionManager(self._body) (p_start_caret_x, p_start_caret_y), (p_end_caret_x, p_end_caret_y) = sel.selection_carets_location() self.marionette.set_orientation('landscape') (l_start_caret_x, l_start_caret_y), (l_end_caret_x, l_end_caret_y) = sel.selection_carets_location() # Drag end caret to the start caret to change the selected content self.actions.flick(self._body, l_end_caret_x, l_end_caret_y, l_start_caret_x, l_start_caret_y).perform() # Change orientation back to portrait mode to prevent affecting # other tests self.marionette.set_orientation('portrait') self.assertEqual(self.to_unix_line_ending(sel.selected_content), 'o') def test_select_word_inside_an_iframe(self): '''Bug 1088552 The scroll offset in iframe should be taken into consideration properly. In this test, we scroll content in the iframe to the bottom to cause a huge offset. If we use the right coordinate system, selection should work. Otherwise, it would be hard to trigger select word. ''' self.open_test_html_iframe() # switch to inner iframe and scroll to the bottom self.marionette.switch_to_frame(self._iframe) self.marionette.execute_script( 'document.getElementById("bd").scrollTop += 999') # long press to select bottom text self._body = self.marionette.find_element(By.ID, 'bd') sel = SelectionManager(self._body) self._bottomtext = self.marionette.find_element(By.ID, 'bottomtext') self.long_press_on_location(self._bottomtext) self.assertNotEqual(self.to_unix_line_ending(sel.selected_content), '') def test_carets_initialized_in_display_none(self): '''Test AccessibleCaretEventHub is properly initialized on a <html> with display: none. ''' self.open_test_html_display_none() # Remove 'display: none' from <html> self.marionette.execute_script( 'arguments[0].style.display = "unset";', script_args=[self._html] ) # If AccessibleCaretEventHub is initialized successfully, select a word # should work. self._test_long_press_to_select_a_word(self._content, self.assertEqual) ######################################################################## # <input> test cases with selection carets enabled ######################################################################## def test_input_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._input, self.assertEqual) def test_input_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._input, self.assertEqual) def test_input_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._input, self.assertEqual) def test_input_focus_obtained_by_long_press_from_textarea(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._input) def test_input_focus_obtained_by_long_press_from_contenteditable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._input) def test_input_focus_obtained_by_long_press_from_content_non_editable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._input) def test_input_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other(self._input, self.assertEqual) def test_input_focus_not_changed_by_long_press_on_non_selectable(self): self.open_test_html() self._test_focus_not_being_changed_by_long_press_on_non_selectable(self._input) ######################################################################## # <input> test cases with selection carets disabled ######################################################################## def test_input_long_press_to_select_a_word_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_long_press_to_select_a_word(self._input, self.assertNotEqual) def test_input_move_selection_carets_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_move_selection_carets(self._input, self.assertNotEqual) ######################################################################## # <textarea> test cases with selection carets enabled ######################################################################## def test_textarea_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea, self.assertEqual) def test_textarea_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._textarea, self.assertEqual) def test_textarea_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._textarea, self.assertEqual) def test_textarea_focus_obtained_by_long_press_from_input(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._textarea) def test_textarea_focus_obtained_by_long_press_from_contenteditable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._textarea) def test_textarea_focus_obtained_by_long_press_from_content_non_editable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._textarea) def test_textarea_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other(self._textarea, self.assertEqual) def test_textarea_focus_not_changed_by_long_press_on_non_selectable(self): self.open_test_html() self._test_focus_not_being_changed_by_long_press_on_non_selectable(self._textarea) ######################################################################## # <textarea> test cases with selection carets disabled ######################################################################## def test_textarea_long_press_to_select_a_word_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea, self.assertNotEqual) def test_textarea_move_selection_carets_disable(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_move_selection_carets(self._textarea, self.assertNotEqual) ######################################################################## # <textarea> right-to-left test cases with selection carets enabled ######################################################################## def test_textarea_rtl_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_focus_not_changed_by_long_press_on_non_selectable(self): self.open_test_html() self._test_focus_not_being_changed_by_long_press_on_non_selectable(self._textarea_rtl) ######################################################################## # <textarea> right-to-left test cases with selection carets disabled ######################################################################## def test_textarea_rtl_long_press_to_select_a_word_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea_rtl, self.assertNotEqual) def test_textarea_rtl_move_selection_carets_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_move_selection_carets(self._textarea_rtl, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with selection carets enabled ######################################################################## def test_contenteditable_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._contenteditable, self.assertEqual) def test_contenteditable_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._contenteditable, self.assertEqual) def test_contenteditable_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._contenteditable, self.assertEqual) def test_contenteditable_focus_obtained_by_long_press_from_input(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._contenteditable) def test_contenteditable_focus_obtained_by_long_press_from_textarea(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._contenteditable) def test_contenteditable_focus_obtained_by_long_press_from_content_non_editable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._contenteditable) def test_contenteditable_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other(self._contenteditable, self.assertEqual) def test_contenteditable_focus_not_changed_by_long_press_on_non_selectable(self): self.open_test_html() self._test_focus_not_being_changed_by_long_press_on_non_selectable(self._contenteditable) ######################################################################## # <div> contenteditable test cases with selection carets disabled ######################################################################## def test_contenteditable_long_press_to_select_a_word_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_long_press_to_select_a_word(self._contenteditable, self.assertNotEqual) def test_contenteditable_move_selection_carets_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_move_selection_carets(self._contenteditable, self.assertNotEqual) ######################################################################## # <div> non-editable test cases with selection carets enabled ######################################################################## def test_content_non_editable_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._content, self.assertEqual) def test_content_non_editable_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._content, self.assertEqual) def test_content_non_editable_minimum_select_one_character_by_selection(self): self.open_test_html() self._test_minimum_select_one_character(self._content, self.assertEqual) def test_content_non_editable_focus_obtained_by_long_press_from_input(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._content) def test_content_non_editable_focus_obtained_by_long_press_from_textarea(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._content) def test_content_non_editable_focus_obtained_by_long_press_from_contenteditable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._content) def test_content_non_editable_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other(self._content, self.assertEqual) ######################################################################## # <textarea> (multi-lines) test cases with selection carets enabled ######################################################################## def test_textarea2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._textarea2, self.assertEqual) ######################################################################## # <div> contenteditable2 (multi-lines) test cases with selection carets enabled ######################################################################## def test_contenteditable2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._contenteditable2, self.assertEqual) ######################################################################## # <div> non-editable2 (multi-lines) test cases with selection carets enabled ######################################################################## def test_content_non_editable2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._content2, self.assertEqual) def test_long_press_to_select_when_partial_visible_word_is_selected(self): self.open_test_html() el = self._input sel = SelectionManager(el) # To successfully select the second word while the first word is being # selected, use sufficient spaces between 'a' and 'b' to avoid the # second caret covers on the second word. original_content = 'aaaaaaaa bbbbbbbb' el.clear() el.send_keys(original_content) words = original_content.split() # We cannot use self.long_press_on_word() directly since it has will # change the cursor position which affects this test. We have to store # the position of word 0 and word 1 before long-pressing to select the # word. word0_x, word0_y = self.word_location(el, 0) word1_x, word1_y = self.word_location(el, 1) self.long_press_on_location(el, word0_x, word0_y) self.assertEqual(words[0], sel.selected_content) self.long_press_on_location(el, word1_x, word1_y) self.assertEqual(words[1], sel.selected_content) self.long_press_on_location(el, word0_x, word0_y) self.assertEqual(words[0], sel.selected_content) # If the second carets is visible, it can be dragged to the position of # the first caret. After that, selection will contain only the first # character. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform() self.assertEqual(words[0][0], sel.selected_content) def test_carets_do_not_jump_when_dragging_to_editable_content_boundary(self): self.open_test_html() el = self._input sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue(len(words) >= 3, 'Expect at least three words in the content.') # Goal: the selection does not being changed after dragging the caret # on the Y-axis only. target_content = words[1] self.long_press_on_word(el, 1) (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() # Drag the first caret up by 50px. self.actions.flick(el, caret1_x, caret1_y, caret1_x, caret1_y - 50).perform() self.assertEqual(target_content, sel.selected_content) # Drag the first caret down by 50px. self.actions.flick(el, caret2_x, caret2_y, caret2_x, caret2_y + 50).perform() self.assertEqual(target_content, sel.selected_content)
class Calendar(object): def __init__(self, parent): self.apps = parent.apps self.data_layer = parent.data_layer self.parent = parent self.marionette = parent.marionette self.UTILS = parent.UTILS self.actions = Actions(self.marionette) def launch(self): self.app = self.apps.launch(self.__class__.__name__) self.UTILS.element.waitForNotElements( DOM.GLOBAL.loading_overlay, self.__class__.__name__ + " app - loading overlay") return self.app def moveDayViewBy(self, num): """ Switches to week view, then moves 'p_num' weeks in the future or past (if the p_num is positive or negative) relative to today. """ self.UTILS.reporting.logResult( "info", "<b>Adjusting day view by {} screens ...</b>".format(num)) self.setView("day") self.setView("today") if num == 0: return """ Set the y-coordinate offset, depending on which direction we need to flick the display. """ numMoves = num if numMoves > 0: _moveEl = 1 else: _moveEl = 2 numMoves = numMoves * -1 # Now move to the desired screen. for i in range(numMoves): # Flick the screen to move it (tricky to find the element we can flick!). _el = self.marionette.find_elements(*DOM.Calendar.dview_events) _num = 0 for i in range(len(_el)): if _el[i].is_displayed(): _num = i break x_pos1 = 0 x_pos2 = 0 if _moveEl == 1: x_pos1 = _el[_num].size["width"] if _moveEl == 2: x_pos2 = _el[_num].size["width"] self.actions.flick(_el[_num], x_pos1, 0, x_pos2, 0).perform() time.sleep(0.3) # Check this is the expected day. _new_epoch = int(time.time()) + (num * 24 * 60 * 60) _new_now = self.UTILS.date_and_time.getDateTimeFromEpochSecs( _new_epoch) _expected_str = "{} {}, {}".format(_new_now.month_name[:3], _new_now.mday, _new_now.day_name) x = self.UTILS.element.getElement(DOM.Calendar.current_view_header, "Current view header") self.UTILS.test.test( x.text == _expected_str, "Header is '<b>%s</b>' (it was '%s')." % (_expected_str, x.text)) x = self.UTILS.debug.screenShotOnErr() self.UTILS.reporting.logResult( "info", "Day view screen after moving {} pages: ".format(num), x) def getEventPreview(self, p_view, p_hour24, p_title, p_location=False): """ Return object for an event in month / week or day view. The tag identifiers aren't consistent, so set them here. <type>: (<event preview identifier>, <event title identifier>) """ event_view = { "month": (DOM.Calendar.view_events_block_m % p_hour24, DOM.Calendar.view_events_title_month), "week": (DOM.Calendar.view_events_block_w % p_hour24, DOM.Calendar.view_events_title_week), "day": (DOM.Calendar.view_events_block_d % p_hour24, DOM.Calendar.view_events_title_day) } viewStr = event_view[p_view] """ Switch to the desired view. For the life of me I can't get 'wait_for_element' ... to work in day view, so I'm just waiting a few seconds then checking with .is_displayed() instead. """ self.setView(p_view) time.sleep(2) # Start by getting the parent element objects, which could contain event details. event_objects = self.UTILS.element.getElements( ('xpath', viewStr[0]), "'" + p_view + "' event details list", False, 20, False) if not event_objects: return False if len(event_objects) <= 0: return False for event_object in event_objects: if p_title in event_object.text: return event_object # If we get to here we failed to return the element we're after. return False def createEvent(self): x = self.UTILS.element.getElement(DOM.Calendar.add_event_btn, "Add event button") x.tap() title = ("xpath", "//input[@name='title']") where = ("xpath", "//input[@name='location']") # allday checkbox, True False (use tap()) allday = ("xpath", "//li[@class='allday']") x = self.UTILS.element.getElement(title, "Title", True, 10) x.send_keys("hello title") x = self.UTILS.element.getElement(where, "Where") x.send_keys("hello where") x = self.UTILS.element.getElement(allday, "All day") x.tap() self.marionette.execute_script( "document.getElementById('start-date-locale').click()") def changeDay(self, numDays, viewType): """ Changes the calendar day to a different day relative to 'today' - <b>uses the month view to do this, then switches back to whichever view you want (month, week, day)</b>.<br> <b>numDays</b> is a number (can be negative to go back, i.e. -5,-2,1,3,5 etc...).<br> <b>viewType</b> is the calendar view to return to (today / day / week / month)<br> Returns a modified DateTime object from <i>UTILS.getDateTimeFromEpochSecs()</i>. """ self.setView("month") self.setView("today") now_secs = time.time() now_diff = int(now_secs) + (86400 * numDays) now_today = self.UTILS.date_and_time.getDateTimeFromEpochSecs(now_secs) new_today = self.UTILS.date_and_time.getDateTimeFromEpochSecs(now_diff) # Switch to month view and tap this day, then switch back to our view. if now_today.mon != new_today.mon: x = new_today.mon - now_today.mon self.moveMonthViewBy(x) el_id_str = "d-%s-%s-%s" % (new_today.year, new_today.mon - 1, new_today.mday) x = self.UTILS.element.getElement( ("xpath", "//li[@data-date='{}']".format(el_id_str)), "Cell for day {}/{}/{}".format(new_today.mday, new_today.mon, new_today.year)) x.tap() self.setView(viewType.lower()) return new_today def moveMonthViewBy(self, num): """ Switches to month view, then moves 'num' months in the future or past (if the num is positive or negative) relative to today. """ self.UTILS.reporting.logResult( "info", "<b>Adjusting month view by {} months ...</b>".format(num)) self.setView("month") self.setView("today") if num == 0: return """ Set the y-coordinate offset, depending on which direction we need to flick the display. """ numMoves = num x2 = 0 if numMoves > 0: el_num = -1 x2 = -500 if numMoves < 0: el_num = 0 x2 = 500 numMoves = numMoves * -1 now = time.localtime(int(time.time())) month = now.tm_mon year = now.tm_year for i in range(numMoves): # Flick the display to show the date we're aiming for. el = self.marionette.find_elements( *DOM.Calendar.mview_first_row_for_flick)[el_num] self.actions.flick(el, 0, 0, x2, 0).perform() # Increment the month and year so we keep track of what's expected. if num < 0: month = month - 1 else: month = month + 1 if month <= 0: month = 12 year = year - 1 elif month >= 13: month = 1 year = year + 1 time.sleep(0.3) # Work out what the header should now be. month_names = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ] expect = "{} {}".format(month_names[month - 1], year) actual = self.UTILS.element.getElement( DOM.Calendar.current_view_header, "Header").text self.UTILS.test.test( expect.lower() in actual.lower(), "Expecting header to contain '{}' (received '{}')".format( expect, actual)) def moveWeekViewBy(self, num): """ Switches to week view, then moves 'num' weeks in the future or past (if the num is positive or negative) relative to today. """ self.UTILS.reporting.logResult( "info", "<b>Adjusting week view by {} screens ...</b>".format(num)) self.setView("week") self.setView("today") if num == 0: return """ Set the y-coordinate offset, depending on which direction we need to flick the display. """ numMoves = num x2 = 0 if numMoves > 0: el = -1 x2 = -500 if numMoves < 0: el = 0 x2 = 500 numMoves = numMoves * -1 """ Keep track of how many days we're adjusting the display by (so we can check we're looking at the correct display at the end). """ days_offset = 0 now_epoch = int(time.time()) now = self.UTILS.date_and_time.getDateTimeFromEpochSecs(now_epoch) now_str = "{} {}".format(now.day_name[:3].upper(), now.mday) displayed_days = self.UTILS.element.getElements( DOM.Calendar.wview_active_days, "Active days") startpos = 0 for i in range(len(displayed_days)): x = displayed_days[i].text if x: if now_str in x: startpos = i - 1 break if num < 0: days_offset = startpos else: days_offset = len(displayed_days) - startpos - 2 # Now move to the desired screen. for i in range(numMoves): # Flick the screen to move it. self.actions.flick(displayed_days[el], 0, 0, x2, 0).perform() # Get the count of days we're adjusting (so we can check later). displayed_days = self.UTILS.element.getElements( DOM.Calendar.wview_active_days, "Active days") days_offset = days_offset + len(displayed_days) time.sleep(0.3) """ Work out what the display should now be: 1. Today + days_offset should be displayed. 2. Header should be month + year, now + days_offset should be in active days. """ if num < 0: new_epoch = now_epoch - (days_offset * 24 * 60 * 60) else: new_epoch = now_epoch + (days_offset * 24 * 60 * 60) new_now = self.UTILS.date_and_time.getDateTimeFromEpochSecs(new_epoch) new_now_str = "{} {}".format(new_now.day_name[:3].upper(), new_now.mday) x = self.UTILS.element.getElements(DOM.Calendar.wview_active_days, "Active days") boolOK = False for i in range(len(x)): x = displayed_days[i].text if x: if new_now_str in x: boolOK = True break self.UTILS.test.test( boolOK, "The column for date '<b>{}</b>' displayed.".format(new_now_str)) x = self.UTILS.element.getElement(DOM.Calendar.current_view_header, "Current view header") self.UTILS.test.test( new_now.month_name in x.text, "'<b>{}</b>' is in header ('{}').".format(new_now.month_name, x.text)) self.UTILS.test.test( str(new_now.year) in x.text, "'<b>{}</b>' is in header ('{}').".format(new_now.year, x.text)) x = self.UTILS.debug.screenShotOnErr() self.UTILS.reporting.logResult( "info", "Week view screen after moving {} pages: ".format(num), x) def setView(self, typ): """ Set to view typ (today / day / week / month). """ x = self.UTILS.element.getElement( (DOM.Calendar.view_type[0], DOM.Calendar.view_type[1] % typ.lower()), "'" + typ + "' view type selector") x.tap() if typ.lower() != 'today': viewTypes = { "month": DOM.Calendar.mview_container, "week": DOM.Calendar.wview_container, "day": DOM.Calendar.dview_container } self.UTILS.element.waitForElements( viewTypes[typ], "Container for '{}' view".format(typ)) else: time.sleep(0.5)
class CommonCaretTestCase(object): """Common test cases for a collapsed selection with a single caret. To run these test cases, a subclass must inherit from both this class and MarionetteTestCase. """ def setUp(self): # Code to execute before a test is being run. super(CommonCaretTestCase, self).setUp() self.actions = Actions(self.marionette) def timeout_ms(self): "Return touch caret expiration time in milliseconds." return self.marionette.get_pref(self.caret_timeout_ms_pref) def open_test_html(self): "Open html for testing and locate elements." test_html = self.marionette.absolute_url("test_touchcaret.html") self.marionette.navigate(test_html) self._input = self.marionette.find_element(By.ID, "input") self._textarea = self.marionette.find_element(By.ID, "textarea") self._contenteditable = self.marionette.find_element(By.ID, "contenteditable") def _test_move_caret_to_the_right_by_one_character(self, el, assertFunc): sel = SelectionManager(el) content_to_add = "!" target_content = sel.content target_content = target_content[:1] + content_to_add + target_content[1:] # Get touch caret (x, y) at position 1 and 2. el.tap() sel.move_caret_to_front() caret0_x, caret0_y = sel.caret_location() touch_caret0_x, touch_caret0_y = sel.touch_caret_location() sel.move_caret_by_offset(1) touch_caret1_x, touch_caret1_y = sel.touch_caret_location() # Tap the front of the input to make touch caret appear. el.tap(caret0_x, caret0_y) # Move touch caret self.actions.flick(el, touch_caret0_x, touch_caret0_y, touch_caret1_x, touch_caret1_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(target_content, sel.content) def _test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self, el, assertFunc): sel = SelectionManager(el) content_to_add = "!" target_content = sel.content + content_to_add # Tap the front of the input to make touch caret appear. el.tap() sel.move_caret_to_front() el.tap(*sel.caret_location()) # Move touch caret to the bottom-right corner of the element. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = el.size["width"], el.size["height"] self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(target_content, sel.content) def _test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self, el, assertFunc): sel = SelectionManager(el) content_to_add = "!" target_content = content_to_add + sel.content # Get touch caret location at the front. el.tap() sel.move_caret_to_front() dest_x, dest_y = sel.touch_caret_location() # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) src_x, src_y = sel.touch_caret_location() # Move touch caret to the front of the input box. self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(target_content, sel.content) def _test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout(self, el, assertFunc): sel = SelectionManager(el) content_to_add = "!" non_target_content = content_to_add + sel.content # Get touch caret expiration time in millisecond, and convert it to second. timeout = self.timeout_ms() / 1000.0 # Set a 3x timeout margin to prevent intermittent test failures. timeout *= 3 # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) # Wait until touch caret disappears, then pretend to move it to the # top-left corner of the input box. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 self.actions.wait(timeout).flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(non_target_content, sel.content) def _test_touch_caret_hides_after_receiving_wheel_event(self, el, assertFunc): sel = SelectionManager(el) content_to_add = "!" non_target_content = content_to_add + sel.content # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) # Send an arbitrary scroll-down-10px wheel event to the center of the # input box to hide touch caret. Then pretend to move it to the top-left # corner of the input box. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 el_center_x, el_center_y = el.rect["x"], el.rect["y"] self.marionette.execute_script( """ var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindowUtils); utils.sendWheelEvent(arguments[0], arguments[1], 0, 10, 0, WheelEvent.DOM_DELTA_PIXEL, 0, 0, 0, 0); """, script_args=[el_center_x, el_center_y], sandbox="system", ) self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(non_target_content, sel.content) def _test_caret_not_appear_when_typing_in_scrollable_content(self, el, assertFunc): sel = SelectionManager(el) content_to_add = "!" target_content = sel.content + string.ascii_letters + content_to_add el.tap() sel.move_caret_to_end() # Insert a long string to the end of the <input>, which triggers # ScrollPositionChanged event. el.send_keys(string.ascii_letters) # The caret should not be visible. If it does appear wrongly due to the # ScrollPositionChanged event, we can drag it to the front of the # <input> to change the cursor position. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() # The content should be inserted at the end of the <input>. el.send_keys(content_to_add) assertFunc(target_content, sel.content) ######################################################################## # <input> test cases with touch caret enabled ######################################################################## def test_input_move_caret_to_the_right_by_one_character(self): self.open_test_html() self._test_move_caret_to_the_right_by_one_character(self._input, self.assertEqual) def test_input_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self): self.open_test_html() self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._input, self.assertEqual) def test_input_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self._input, self.assertEqual) def test_input_caret_not_appear_when_typing_in_scrollable_content(self): self.open_test_html() self._test_caret_not_appear_when_typing_in_scrollable_content(self._input, self.assertEqual) def test_input_touch_caret_timeout(self): with self.marionette.using_prefs({self.caret_timeout_ms_pref: 1000}): self.open_test_html() self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout( self._input, self.assertNotEqual ) ######################################################################## # <input> test cases with touch caret disabled ######################################################################## def test_input_move_caret_to_the_right_by_one_character_disabled(self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_the_right_by_one_character(self._input, self.assertNotEqual) def test_input_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self._input, self.assertNotEqual) ######################################################################## # <textarea> test cases with touch caret enabled ######################################################################## def test_textarea_move_caret_to_the_right_by_one_character(self): self.open_test_html() self._test_move_caret_to_the_right_by_one_character(self._textarea, self.assertEqual) def test_textarea_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self): self.open_test_html() self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self._textarea, self.assertEqual) def test_textarea_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content(self._textarea, self.assertEqual) def test_textarea_touch_caret_timeout(self): with self.marionette.using_prefs({self.caret_timeout_ms_pref: 1000}): self.open_test_html() self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout( self._textarea, self.assertNotEqual ) ######################################################################## # <textarea> test cases with touch caret disabled ######################################################################## def test_textarea_move_caret_to_the_right_by_one_character_disabled(self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_the_right_by_one_character(self._textarea, self.assertNotEqual) def test_textarea_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self._textarea, self.assertNotEqual ) ######################################################################## # <div> contenteditable test cases with touch caret enabled ######################################################################## def test_contenteditable_move_caret_to_the_right_by_one_character(self): self.open_test_html() self._test_move_caret_to_the_right_by_one_character(self._contenteditable, self.assertEqual) def test_contenteditable_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner(self): self.open_test_html() self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner( self._contenteditable, self.assertEqual ) def test_contenteditable_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner(self): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self._contenteditable, self.assertEqual ) def test_contenteditable_touch_caret_timeout(self): with self.marionette.using_prefs({self.caret_timeout_ms_pref: 1000}): self.open_test_html() self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout( self._contenteditable, self.assertNotEqual ) ######################################################################## # <div> contenteditable test cases with touch caret disabled ######################################################################## def test_contenteditable_move_caret_to_the_right_by_one_character_disabled(self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_the_right_by_one_character(self._contenteditable, self.assertNotEqual) def test_contenteditable_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled(self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self._contenteditable, self.assertNotEqual )
class CommonCaretTestCase(object): '''Common test cases for a collapsed selection with a single caret. To run these test cases, a subclass must inherit from both this class and MarionetteTestCase. ''' _input_selector = (By.ID, 'input') _textarea_selector = (By.ID, 'textarea') _contenteditable_selector = (By.ID, 'contenteditable') def setUp(self): # Code to execute before a test is being run. super(CommonCaretTestCase, self).setUp() self.actions = Actions(self.marionette) # The caret to be tested. self.caret_tested_pref = None # The caret to be disabled in this test suite. self.caret_disabled_pref = None self.caret_timeout_ms_pref = None def set_pref(self, pref_name, value): '''Set a preference to value. For example: >>> set_pref('layout.accessiblecaret.enabled', True) ''' pref_name = repr(pref_name) if isinstance(value, bool): value = 'true' if value else 'false' func = 'setBoolPref' elif isinstance(value, int): value = str(value) func = 'setIntPref' else: value = repr(value) func = 'setCharPref' with self.marionette.using_context('chrome'): script = 'Services.prefs.%s(%s, %s)' % (func, pref_name, value) self.marionette.execute_script(script) def timeout_ms(self): 'Return touch caret expiration time in milliseconds.' with self.marionette.using_context('chrome'): return self.marionette.execute_script( 'return Services.prefs.getIntPref("%s");' % self.caret_timeout_ms_pref) def open_test_html(self, enabled=True, timeout_ms=0): '''Open html for testing and locate elements, enable/disable touch caret, and set touch caret expiration time in milliseconds). ''' self.set_pref(self.caret_tested_pref, enabled) self.set_pref(self.caret_disabled_pref, False) self.set_pref(self.caret_timeout_ms_pref, timeout_ms) test_html = self.marionette.absolute_url('test_touchcaret.html') self.marionette.navigate(test_html) self._input = self.marionette.find_element(*self._input_selector) self._textarea = self.marionette.find_element(*self._textarea_selector) self._contenteditable = self.marionette.find_element( *self._contenteditable_selector) def _test_move_caret_to_the_right_by_one_character(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = sel.content target_content = target_content[:1] + content_to_add + target_content[ 1:] # Get touch caret (x, y) at position 1 and 2. el.tap() sel.move_caret_to_front() caret0_x, caret0_y = sel.caret_location() touch_caret0_x, touch_caret0_y = sel.touch_caret_location() sel.move_caret_by_offset(1) touch_caret1_x, touch_caret1_y = sel.touch_caret_location() # Tap the front of the input to make touch caret appear. el.tap(caret0_x, caret0_y) # Move touch caret self.actions.flick(el, touch_caret0_x, touch_caret0_y, touch_caret1_x, touch_caret1_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(target_content, sel.content) def _test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner( self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = sel.content + content_to_add # Tap the front of the input to make touch caret appear. el.tap() sel.move_caret_to_front() el.tap(*sel.caret_location()) # Move touch caret to the bottom-right corner of the element. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = el.size['width'], el.size['height'] self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(target_content, sel.content) def _test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = content_to_add + sel.content # Get touch caret location at the front. el.tap() sel.move_caret_to_front() dest_x, dest_y = sel.touch_caret_location() # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) src_x, src_y = sel.touch_caret_location() # Move touch caret to the front of the input box. self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(target_content, sel.content) def _test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout( self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' non_target_content = content_to_add + sel.content # Get touch caret expiration time in millisecond, and convert it to second. timeout = self.timeout_ms() / 1000.0 # Set a 3x timeout margin to prevent intermittent test failures. timeout *= 3 # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) # Wait until touch caret disappears, then pretend to move it to the # top-left corner of the input box. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 self.actions.wait(timeout).flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(non_target_content, sel.content) def _test_touch_caret_hides_after_receiving_wheel_event( self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' non_target_content = content_to_add + sel.content # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) # Send an arbitrary scroll-down-10px wheel event to the center of the # input box to hide touch caret. Then pretend to move it to the top-left # corner of the input box. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 el_center_x, el_center_y = el.rect['x'], el.rect['y'] self.marionette.execute_script(''' var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindowUtils); utils.sendWheelEvent(arguments[0], arguments[1], 0, 10, 0, WheelEvent.DOM_DELTA_PIXEL, 0, 0, 0, 0); ''', script_args=[el_center_x, el_center_y], sandbox='system') self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(non_target_content, sel.content) def _test_caret_not_appear_when_typing_in_scrollable_content( self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = sel.content + string.ascii_letters + content_to_add el.tap() sel.move_caret_to_end() # Insert a long string to the end of the <input>, which triggers # ScrollPositionChanged event. el.send_keys(string.ascii_letters) # The caret should not be visible. If it does appear wrongly due to the # ScrollPositionChanged event, we can drag it to the front of the # <input> to change the cursor position. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() # The content should be inserted at the end of the <input>. el.send_keys(content_to_add) assertFunc(target_content, sel.content) ######################################################################## # <input> test cases with touch caret enabled ######################################################################## def test_input_move_caret_to_the_right_by_one_character(self): self.open_test_html() self._test_move_caret_to_the_right_by_one_character( self._input, self.assertEqual) def test_input_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner( self): self.open_test_html() self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner( self._input, self.assertEqual) def test_input_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner( self): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self._input, self.assertEqual) def test_input_caret_not_appear_when_typing_in_scrollable_content(self): self.open_test_html() self._test_caret_not_appear_when_typing_in_scrollable_content( self._input, self.assertEqual) def test_input_touch_caret_timeout(self): self.open_test_html(timeout_ms=1000) self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout( self._input, self.assertNotEqual) ######################################################################## # <input> test cases with touch caret disabled ######################################################################## def test_input_move_caret_to_the_right_by_one_character_disabled(self): self.open_test_html(enabled=False) self._test_move_caret_to_the_right_by_one_character( self._input, self.assertNotEqual) def test_input_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled( self): self.open_test_html(enabled=False) self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self._input, self.assertNotEqual) ######################################################################## # <textarea> test cases with touch caret enabled ######################################################################## def test_textarea_move_caret_to_the_right_by_one_character(self): self.open_test_html() self._test_move_caret_to_the_right_by_one_character( self._textarea, self.assertEqual) def test_textarea_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner( self): self.open_test_html() self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner( self._textarea, self.assertEqual) def test_textarea_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner( self): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self._textarea, self.assertEqual) def test_textarea_touch_caret_timeout(self): self.open_test_html(timeout_ms=1000) self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout( self._textarea, self.assertNotEqual) ######################################################################## # <textarea> test cases with touch caret disabled ######################################################################## def test_textarea_move_caret_to_the_right_by_one_character_disabled(self): self.open_test_html(enabled=False) self._test_move_caret_to_the_right_by_one_character( self._textarea, self.assertNotEqual) def test_textarea_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled( self): self.open_test_html(enabled=False) self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self._textarea, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with touch caret enabled ######################################################################## def test_contenteditable_move_caret_to_the_right_by_one_character(self): self.open_test_html() self._test_move_caret_to_the_right_by_one_character( self._contenteditable, self.assertEqual) def test_contenteditable_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner( self): self.open_test_html() self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner( self._contenteditable, self.assertEqual) def test_contenteditable_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner( self): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self._contenteditable, self.assertEqual) def test_contenteditable_touch_caret_timeout(self): self.open_test_html(timeout_ms=1000) self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout( self._contenteditable, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with touch caret disabled ######################################################################## def test_contenteditable_move_caret_to_the_right_by_one_character_disabled( self): self.open_test_html(enabled=False) self._test_move_caret_to_the_right_by_one_character( self._contenteditable, self.assertNotEqual) def test_contenteditable_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled( self): self.open_test_html(enabled=False) self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self._contenteditable, self.assertNotEqual)
class Gallery(object): def __init__(self, parent): self.apps = parent.apps self.data_layer = parent.data_layer self.parent = parent self.marionette = parent.marionette self.UTILS = parent.UTILS self.actions = Actions(self.marionette) def launch(self): self.app = self.apps.launch(self.__class__.__name__) self.UTILS.element.waitForNotElements( DOM.GLOBAL.loading_overlay, self.__class__.__name__ + " app - loading overlay") self.UTILS.element.waitForNotElements(DOM.Gallery.loading_bar, "Loading bar", True, 10) return self.app def convert_str_to_seconds(self, the_string): """ Converts a str of the form "aa:bb" into seconds """ processed = time.strptime(the_string, '%M:%S') return (int( datetime.timedelta(minutes=processed.tm_min, seconds=processed.tm_sec).total_seconds())) def check_video_length(self, expected_duration, margin=2): """ This method asserts that the video has the desired duration @param int expected_duration specifies the video duration in seconds """ # Play the video and get total duration self.play_current_video() video_length = self.UTILS.element.getElement( DOM.Gallery.preview_current_video_duration, "Video length") real_duration = self.convert_str_to_seconds(video_length.text) # Note: we give 1 second margin in case the things went a little bit slower when recording the video interval = range(expected_duration - margin, expected_duration + margin + 1, 1) self.UTILS.test.test(real_duration in interval, "Duration matches") def click_on_thumbnail_at_position(self, position, preview=True): """ Clicks on a thumbnail at a certain position from the gallery. @param boolean preview specifies whether we have to check for the preview screen or not """ self.parent.wait_for_element_displayed(*DOM.Gallery.thumbnail_items) thumb_list = self.marionette.find_elements( *DOM.Gallery.thumbnail_items) time.sleep(1) thumb = thumb_list[position] thumb.tap() self.UTILS.element.waitForNotElements(DOM.Gallery.thumbnail_items, "Thumbnail list", True, 10) if preview: self.UTILS.element.waitForElements(DOM.Gallery.preview, "Thumbnail preview", True, 10) def _click_on_thumb_external(self, position, frame_to_change): """ Private method which handles image selection and image cropping @param int position thumbnail to click @param tuple frame_to_change frame to switch once the image has been cropped """ time.sleep(1) self.click_on_thumbnail_at_position(position, preview=False) time.sleep(2) crop = self.UTILS.element.getElement(DOM.Gallery.crop_done, "Crop Done") crop.tap() self.UTILS.iframe.switchToFrame(*frame_to_change) def click_on_thumbnail_at_position_mms(self, position): """ Clicks a thumbnail from the gallery in order to attach it to a MMS """ self._click_on_thumb_external(position, DOM.Messages.frame_locator) def click_on_thumbnail_at_position_email(self, position): """ Clicks a thumbnail from the gallery in order to attach it to an email """ self._click_on_thumb_external(position, DOM.Email.frame_locator) def delete_thumbnails(self, num_array): """ Deletes the thumbnails listed in num_array (following an index starting at number 0) The list must be numeric, i.e "delete_thumbnails([0,1,2])". """ # Get the amount of thumbnails we currently have. before_thumbcount = self.get_number_of_thumbnails() delete_thumbcount = len(num_array) target_thumbcount = before_thumbcount - delete_thumbcount select_mode_btn = self.UTILS.element.getElement( DOM.Gallery.thumbnail_select_mode, "Select button") select_mode_btn.tap() # Select the target ones thumbs = self.UTILS.element.getElements(DOM.Gallery.thumbnail_items, "Thumbnails") for position in num_array: thumbs[position].tap() selected = self.UTILS.element.getElement( DOM.Gallery.thumbnail_number_selected, "Number selected header") self.UTILS.test.test( str(delete_thumbcount) in selected.text, "Right number of thumbs selected") trash_btn = self.UTILS.element.getElement( DOM.Gallery.thumbnail_trash_icon, "Trash icon") trash_btn.tap() confirm = self.UTILS.element.getElement(DOM.GLOBAL.modal_confirm_ok, "Delete") confirm.tap() if target_thumbcount < 1: self.UTILS.element.waitForElements( DOM.Gallery.no_thumbnails_message, "Message saying there are no thumbnails", False, 5) else: # Come out of 'select' mode. exit_select_mode_header = self.UTILS.element.getElement( DOM.Gallery.exit_select_mode_header, "Exit select mode button") exit_select_mode_header.tap(25, 25) current_thumbs = self.get_number_of_thumbnails() self.UTILS.test.test(current_thumbs == target_thumbcount, "After deleting [{}] pics, we have the expected number: {}".\ format(delete_thumbcount, target_thumbcount)) def get_gallery_items(self): """ Returns a list of gallery item objects, with RAW info (date, metadata, size...) """ self.UTILS.element.waitForElements(DOM.Gallery.thumbnail_items, "Thumbnails", True, 20, False) return self.marionette.execute_script( "return window.wrappedJSObject.files;") def play_current_video(self): """ Plays the video that has previously been loaded (by pressing its thumbnail first), then press a play button. """ play_btn = self.UTILS.element.getElement( DOM.Gallery.preview_current_video_play, "Video play button") time.sleep(1) play_btn.tap() self.UTILS.element.waitForElements( DOM.Gallery.preview_current_video_pause, "Pause button", True, 20, False) def get_number_of_thumbnails(self): """ Returns the number of thumbnails. """ try: self.parent.wait_for_element_displayed( *DOM.Gallery.thumbnail_items) return len( self.marionette.find_elements(*DOM.Gallery.thumbnail_items)) except: return 0 def wait_for_thumbnails_number(self, number, timeout=10): """ Waits untils @number thumbnails are present in the thumbnails screen """ msg = "Waiting until we have the expected number of thumbnails" self.parent.wait_for_condition(lambda m: len( m.find_elements(*DOM.Gallery.thumbnail_items)) == number, timeout=timeout, message=msg) def swipe_between_gallery_items(self, steps): """ Swipes over the gallery items (the preview screen must be displayed) a certain number of steps Important: there is no way of checking that the image is being shown each time we swipe, """ current_frame = self.apps.displayed_app.frame x_start = current_frame.size['width'] x_end = x_start // 4 y_start = current_frame.size['height'] // 2 for i in range(steps): self.actions.flick(current_frame, x_start, y_start, x_end, y_start, duration=600).perform() time.sleep(1)
class AccessibleCaretSelectionModeTestCase(MarionetteTestCase): '''Test cases for AccessibleCaret under selection mode, aka selection carets. ''' def setUp(self): # Code to execute before a tests are run. super(AccessibleCaretSelectionModeTestCase, self).setUp() self.carets_tested_pref = 'layout.accessiblecaret.enabled' self.prefs = { 'layout.word_select.eat_space_to_next_word': False, 'layout.accessiblecaret.use_long_tap_injector': False, self.carets_tested_pref: True, } self.marionette.set_prefs(self.prefs) self.actions = Actions(self.marionette) def open_test_html(self): 'Open html for testing and locate elements.' test_html = self.marionette.absolute_url('test_selectioncarets.html') self.marionette.navigate(test_html) self._input = self.marionette.find_element(By.ID, 'input') self._textarea = self.marionette.find_element(By.ID, 'textarea') self._textarea_rtl = self.marionette.find_element( By.ID, 'textarea_rtl') self._contenteditable = self.marionette.find_element( By.ID, 'contenteditable') self._content = self.marionette.find_element(By.ID, 'content') self._non_selectable = self.marionette.find_element( By.ID, 'non_selectable') def open_test_html2(self): 'Open html for testing and locate elements.' test_html2 = self.marionette.absolute_url( 'test_selectioncarets_multipleline.html') self.marionette.navigate(test_html2) self._textarea2 = self.marionette.find_element(By.ID, 'textarea2') self._contenteditable2 = self.marionette.find_element( By.ID, 'contenteditable2') self._content2 = self.marionette.find_element(By.ID, 'content2') def open_test_html_multirange(self): 'Open html for testing non-editable support.' test_html = self.marionette.absolute_url( 'test_selectioncarets_multiplerange.html') self.marionette.navigate(test_html) self._body = self.marionette.find_element(By.ID, 'bd') self._sel1 = self.marionette.find_element(By.ID, 'sel1') self._sel2 = self.marionette.find_element(By.ID, 'sel2') self._sel3 = self.marionette.find_element(By.ID, 'sel3') self._sel4 = self.marionette.find_element(By.ID, 'sel4') self._sel6 = self.marionette.find_element(By.ID, 'sel6') self._nonsel1 = self.marionette.find_element(By.ID, 'nonsel1') def open_test_html_long_text(self): 'Open html for testing long text.' test_html = self.marionette.absolute_url( 'test_selectioncarets_longtext.html') self.marionette.navigate(test_html) self._body = self.marionette.find_element(By.ID, 'bd') self._longtext = self.marionette.find_element(By.ID, 'longtext') def open_test_html_iframe(self): 'Open html for testing iframe.' test_html = self.marionette.absolute_url( 'test_selectioncarets_iframe.html') self.marionette.navigate(test_html) self._iframe = self.marionette.find_element(By.ID, 'frame') def open_test_html_display_none(self): 'Open html for testing html with display: none.' test_html = self.marionette.absolute_url( 'test_carets_display_none.html') self.marionette.navigate(test_html) self._html = self.marionette.find_element(By.ID, 'html') self._content = self.marionette.find_element(By.ID, 'content') def word_offset(self, text, ordinal): 'Get the character offset of the ordinal-th word in text.' tokens = re.split(r'(\S+)', text) # both words and spaces spaces = tokens[0::2] # collect spaces at odd indices words = tokens[1::2] # collect word at even indices if ordinal >= len(words): raise IndexError('Only %d words in text, but got ordinal %d' % (len(words), ordinal)) # Cursor position of the targeting word is behind the the first # character in the word. For example, offset to 'def' in 'abc def' is # between 'd' and 'e'. offset = len(spaces[0]) + 1 offset += sum( len(words[i]) + len(spaces[i + 1]) for i in range(ordinal)) return offset def test_word_offset(self): text = ' ' * 3 + 'abc' + ' ' * 3 + 'def' self.assertTrue(self.word_offset(text, 0), 4) self.assertTrue(self.word_offset(text, 1), 10) with self.assertRaises(IndexError): self.word_offset(text, 2) def word_location(self, el, ordinal): '''Get the location (x, y) of the ordinal-th word in el. The ordinal starts from 0. Note: this function has a side effect which changes focus to the target element el. ''' sel = SelectionManager(el) offset = self.word_offset(sel.content, ordinal) # Move caret to the word. el.tap() sel.move_caret_to_front() sel.move_caret_by_offset(offset) x, y = sel.caret_location() return x, y def rect_relative_to_window(self, el): '''Get element's bounding rectangle. This function is similar to el.rect, but the coordinate is relative to the top left corner of the window instead of the document. ''' return self.marionette.execute_script(''' let rect = arguments[0].getBoundingClientRect(); return {x: rect.x, y:rect.y, width: rect.width, height: rect.height}; ''', script_args=[el]) def long_press_on_location(self, el, x=None, y=None): '''Long press the location (x, y) to select a word. If no (x, y) are given, it will be targeted at the center of the element. On Windows, those spaces after the word will also be selected. This function sends synthesized eMouseLongTap to gecko. ''' rect = self.rect_relative_to_window(el) target_x = rect['x'] + (x if x is not None else rect['width'] // 2) target_y = rect['y'] + (y if y is not None else rect['height'] // 2) self.marionette.execute_script(''' let Ci = Components.interfaces; let utils = window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); utils.sendTouchEventToWindow('touchstart', [0], [arguments[0]], [arguments[1]], [1], [1], [0], [1], 1, 0); utils.sendMouseEventToWindow('mouselongtap', arguments[0], arguments[1], 0, 1, 0); utils.sendTouchEventToWindow('touchend', [0], [arguments[0]], [arguments[1]], [1], [1], [0], [1], 1, 0); ''', script_args=[target_x, target_y], sandbox='system') def long_press_on_word(self, el, wordOrdinal): x, y = self.word_location(el, wordOrdinal) self.long_press_on_location(el, x, y) def to_unix_line_ending(self, s): """Changes all Windows/Mac line endings in s to UNIX line endings.""" return s.replace('\r\n', '\n').replace('\r', '\n') def _test_long_press_to_select_a_word(self, el, assertFunc): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue( len(words) >= 2, 'Expect at least two words in the content.') target_content = words[0] # Goal: Select the first word. self.long_press_on_word(el, 0) # Ignore extra spaces selected after the word. assertFunc(target_content, sel.selected_content) def _test_move_selection_carets(self, el, assertFunc): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue( len(words) >= 1, 'Expect at least one word in the content.') # Goal: Select all text after the first word. target_content = original_content[len(words[0]):] # Get the location of the selection carets at the end of the content for # later use. el.tap() sel.select_all() (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() self.long_press_on_word(el, 0) # Move the right caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Move the left caret to the previous position of the right caret. self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() assertFunc(target_content, sel.selected_content) def _test_minimum_select_one_character(self, el, assertFunc, x=None, y=None): sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue( len(words) >= 1, 'Expect at least one word in the content.') # Get the location of the selection carets at the end of the content for # later use. sel.select_all() (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() el.tap() # Goal: Select the first character. target_content = original_content[0] if x and y: # If we got x and y from the arguments, use it as a hint of the # location of the first word pass else: x, y = self.word_location(el, 0) self.long_press_on_location(el, x, y) # Move the right caret to the end of the content. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform() # Move the right caret to the position of the left caret. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform() assertFunc(target_content, sel.selected_content) def _test_focus_obtained_by_long_press(self, el1, el2): '''Test the focus could be changed from el1 to el2 by long press. If the focus is changed to e2 successfully, SelectionCarets should appear and could be dragged. ''' # Goal: Tap to focus el1, and then select the first character on # el2. # We want to collect the location of the first word in el2 here # since self.word_location() has the side effect which would # change the focus. x, y = self.word_location(el2, 0) el1.tap() self._test_minimum_select_one_character(el2, self.assertEqual, x=x, y=y) def _test_focus_not_being_changed_by_long_press_on_non_selectable( self, el): # Goal: Focus remains on the editable element el after long pressing on # the non-selectable element. sel = SelectionManager(el) self.long_press_on_word(el, 0) self.long_press_on_location(self._non_selectable) active_sel = SelectionManager(self.marionette.get_active_element()) self.assertEqual(sel.content, active_sel.content) def _test_handle_tilt_when_carets_overlap_to_each_other( self, el, assertFunc): '''Test tilt handling when carets overlap to each other. Let SelectionCarets overlap to each other. If SelectionCarets are set to tilted successfully, tapping the tilted carets should not cause the selection to be collapsed and the carets should be draggable. ''' sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue( len(words) >= 1, 'Expect at least one word in the content.') # Goal: Select the first word. self.long_press_on_word(el, 0) target_content = sel.selected_content # Move the left caret to the position of the right caret to trigger # carets overlapping. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform() # We make two hit tests targeting the left edge of the left tilted caret # and the right edge of the right tilted caret. If either of the hits is # missed, selection would be collapsed and both carets should not be # draggable. (caret3_x, caret3_y), (caret4_x, caret4_y) = sel.selection_carets_location() # The following values are from ua.css and all.js if self.carets_tested_pref == 'selectioncaret.enabled': caret_width = 44 caret_margin_left = -23 tilt_right_margin_left = 18 tilt_left_margin_left = -17 elif self.carets_tested_pref == 'layout.accessiblecaret.enabled': caret_width = float( self.marionette.get_pref('layout.accessiblecaret.width')) caret_margin_left = float( self.marionette.get_pref('layout.accessiblecaret.margin-left')) tilt_right_margin_left = 0.41 * caret_width tilt_left_margin_left = -0.39 * caret_width left_caret_left_edge_x = caret3_x + caret_margin_left + tilt_left_margin_left el.tap(left_caret_left_edge_x + 2, caret3_y) right_caret_right_edge_x = (caret4_x + caret_margin_left + tilt_right_margin_left + caret_width) el.tap(right_caret_right_edge_x - 2, caret4_y) # Drag the left caret back to the initial selection, the first word. self.actions.flick(el, caret3_x, caret3_y, caret1_x, caret1_y).perform() assertFunc(target_content, sel.selected_content) def test_long_press_to_select_non_selectable_word(self): '''Testing long press on non selectable field. We should not select anything when long press on non selectable fields.''' self.open_test_html_multirange() halfY = self._nonsel1.size['height'] / 2 self.long_press_on_location(self._nonsel1, 0, halfY) sel = SelectionManager(self._nonsel1) range_count = sel.range_count() self.assertEqual(range_count, 0) def test_drag_caret_over_non_selectable_field(self): '''Testing drag caret over non selectable field. So that the selected content should exclude non selectable field and end selection caret should appear in last range's position.''' self.open_test_html_multirange() # Select target element and get target caret location self.long_press_on_word(self._sel4, 3) sel = SelectionManager(self._body) (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location() self.long_press_on_word(self._sel6, 0) (_, _), (end_caret2_x, end_caret2_y) = sel.selection_carets_location() # Select start element self.long_press_on_word(self._sel3, 3) # Drag end caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual( self.to_unix_line_ending(sel.selected_content.strip()), 'this 3\nuser can select this') (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, end_caret2_x, end_caret2_y, 1).perform() self.assertEqual( self.to_unix_line_ending(sel.selected_content.strip()), 'this 3\nuser can select this 4\nuser can select this 5\nuser') # Drag first caret to target location (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret1_x, caret1_y, end_caret_x, end_caret_y, 1).perform() self.assertEqual( self.to_unix_line_ending(sel.selected_content.strip()), '4\nuser can select this 5\nuser') def test_drag_caret_to_beginning_of_a_line(self): '''Bug 1094056 Test caret visibility when caret is dragged to beginning of a line ''' self.open_test_html_multirange() # Select the first word in the second line self.long_press_on_word(self._sel2, 0) sel = SelectionManager(self._body) (start_caret_x, start_caret_y), (end_caret_x, end_caret_y) = sel.selection_carets_location() # Select target word in the first line self.long_press_on_word(self._sel1, 2) # Drag end caret to the beginning of the second line (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(self._body, caret2_x, caret2_y, start_caret_x, start_caret_y).perform() # Drag end caret back to the target word self.actions.flick(self._body, start_caret_x, start_caret_y, caret2_x, caret2_y).perform() self.assertEqual(self.to_unix_line_ending(sel.selected_content), 'select') @skip_if_not_rotatable def test_caret_position_after_changing_orientation_of_device(self): '''Bug 1094072 If positions of carets are updated correctly, they should be draggable. ''' self.open_test_html_long_text() # Select word in portrait mode, then change to landscape mode self.marionette.set_orientation('portrait') self.long_press_on_word(self._longtext, 12) sel = SelectionManager(self._body) (p_start_caret_x, p_start_caret_y), (p_end_caret_x, p_end_caret_y) = sel.selection_carets_location() self.marionette.set_orientation('landscape') (l_start_caret_x, l_start_caret_y), (l_end_caret_x, l_end_caret_y) = sel.selection_carets_location() # Drag end caret to the start caret to change the selected content self.actions.flick(self._body, l_end_caret_x, l_end_caret_y, l_start_caret_x, l_start_caret_y).perform() # Change orientation back to portrait mode to prevent affecting # other tests self.marionette.set_orientation('portrait') self.assertEqual(self.to_unix_line_ending(sel.selected_content), 'o') def test_select_word_inside_an_iframe(self): '''Bug 1088552 The scroll offset in iframe should be taken into consideration properly. In this test, we scroll content in the iframe to the bottom to cause a huge offset. If we use the right coordinate system, selection should work. Otherwise, it would be hard to trigger select word. ''' self.open_test_html_iframe() # switch to inner iframe and scroll to the bottom self.marionette.switch_to_frame(self._iframe) self.marionette.execute_script( 'document.getElementById("bd").scrollTop += 999') # long press to select bottom text self._body = self.marionette.find_element(By.ID, 'bd') sel = SelectionManager(self._body) self._bottomtext = self.marionette.find_element(By.ID, 'bottomtext') self.long_press_on_location(self._bottomtext) self.assertNotEqual(self.to_unix_line_ending(sel.selected_content), '') def test_carets_initialized_in_display_none(self): '''Test AccessibleCaretEventHub is properly initialized on a <html> with display: none. ''' self.open_test_html_display_none() # Remove 'display: none' from <html> self.marionette.execute_script('arguments[0].style.display = "unset";', script_args=[self._html]) # If AccessibleCaretEventHub is initialized successfully, select a word # should work. self._test_long_press_to_select_a_word(self._content, self.assertEqual) ######################################################################## # <input> test cases with selection carets enabled ######################################################################## def test_input_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._input, self.assertEqual) def test_input_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._input, self.assertEqual) def test_input_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._input, self.assertEqual) def test_input_focus_obtained_by_long_press_from_textarea(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._input) def test_input_focus_obtained_by_long_press_from_contenteditable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._input) def test_input_focus_obtained_by_long_press_from_content_non_editable( self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._input) def test_input_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other( self._input, self.assertEqual) def test_input_focus_not_changed_by_long_press_on_non_selectable(self): self.open_test_html() self._test_focus_not_being_changed_by_long_press_on_non_selectable( self._input) ######################################################################## # <input> test cases with selection carets disabled ######################################################################## def test_input_long_press_to_select_a_word_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_long_press_to_select_a_word(self._input, self.assertNotEqual) def test_input_move_selection_carets_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_move_selection_carets(self._input, self.assertNotEqual) ######################################################################## # <textarea> test cases with selection carets enabled ######################################################################## def test_textarea_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea, self.assertEqual) def test_textarea_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._textarea, self.assertEqual) def test_textarea_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._textarea, self.assertEqual) def test_textarea_focus_obtained_by_long_press_from_input(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._textarea) def test_textarea_focus_obtained_by_long_press_from_contenteditable(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._textarea) def test_textarea_focus_obtained_by_long_press_from_content_non_editable( self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._textarea) def test_textarea_handle_tilt_when_carets_overlap_to_each_other(self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other( self._textarea, self.assertEqual) def test_textarea_focus_not_changed_by_long_press_on_non_selectable(self): self.open_test_html() self._test_focus_not_being_changed_by_long_press_on_non_selectable( self._textarea) ######################################################################## # <textarea> test cases with selection carets disabled ######################################################################## def test_textarea_long_press_to_select_a_word_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea, self.assertNotEqual) def test_textarea_move_selection_carets_disable(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_move_selection_carets(self._textarea, self.assertNotEqual) ######################################################################## # <textarea> right-to-left test cases with selection carets enabled ######################################################################## def test_textarea_rtl_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._textarea_rtl, self.assertEqual) def test_textarea_rtl_focus_not_changed_by_long_press_on_non_selectable( self): self.open_test_html() self._test_focus_not_being_changed_by_long_press_on_non_selectable( self._textarea_rtl) ######################################################################## # <textarea> right-to-left test cases with selection carets disabled ######################################################################## def test_textarea_rtl_long_press_to_select_a_word_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_long_press_to_select_a_word(self._textarea_rtl, self.assertNotEqual) def test_textarea_rtl_move_selection_carets_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_move_selection_carets(self._textarea_rtl, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with selection carets enabled ######################################################################## def test_contenteditable_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._contenteditable, self.assertEqual) def test_contenteditable_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._contenteditable, self.assertEqual) def test_contenteditable_minimum_select_one_character(self): self.open_test_html() self._test_minimum_select_one_character(self._contenteditable, self.assertEqual) def test_contenteditable_focus_obtained_by_long_press_from_input(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._contenteditable) def test_contenteditable_focus_obtained_by_long_press_from_textarea(self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._contenteditable) def test_contenteditable_focus_obtained_by_long_press_from_content_non_editable( self): self.open_test_html() self._test_focus_obtained_by_long_press(self._content, self._contenteditable) def test_contenteditable_handle_tilt_when_carets_overlap_to_each_other( self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other( self._contenteditable, self.assertEqual) def test_contenteditable_focus_not_changed_by_long_press_on_non_selectable( self): self.open_test_html() self._test_focus_not_being_changed_by_long_press_on_non_selectable( self._contenteditable) ######################################################################## # <div> contenteditable test cases with selection carets disabled ######################################################################## def test_contenteditable_long_press_to_select_a_word_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_long_press_to_select_a_word(self._contenteditable, self.assertNotEqual) def test_contenteditable_move_selection_carets_disabled(self): with self.marionette.using_prefs({self.carets_tested_pref: False}): self.open_test_html() self._test_move_selection_carets(self._contenteditable, self.assertNotEqual) ######################################################################## # <div> non-editable test cases with selection carets enabled ######################################################################## def test_content_non_editable_long_press_to_select_a_word(self): self.open_test_html() self._test_long_press_to_select_a_word(self._content, self.assertEqual) def test_content_non_editable_move_selection_carets(self): self.open_test_html() self._test_move_selection_carets(self._content, self.assertEqual) def test_content_non_editable_minimum_select_one_character_by_selection( self): self.open_test_html() self._test_minimum_select_one_character(self._content, self.assertEqual) def test_content_non_editable_focus_obtained_by_long_press_from_input( self): self.open_test_html() self._test_focus_obtained_by_long_press(self._input, self._content) def test_content_non_editable_focus_obtained_by_long_press_from_textarea( self): self.open_test_html() self._test_focus_obtained_by_long_press(self._textarea, self._content) def test_content_non_editable_focus_obtained_by_long_press_from_contenteditable( self): self.open_test_html() self._test_focus_obtained_by_long_press(self._contenteditable, self._content) def test_content_non_editable_handle_tilt_when_carets_overlap_to_each_other( self): self.open_test_html() self._test_handle_tilt_when_carets_overlap_to_each_other( self._content, self.assertEqual) ######################################################################## # <textarea> (multi-lines) test cases with selection carets enabled ######################################################################## def test_textarea2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._textarea2, self.assertEqual) ######################################################################## # <div> contenteditable2 (multi-lines) test cases with selection carets enabled ######################################################################## def test_contenteditable2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._contenteditable2, self.assertEqual) ######################################################################## # <div> non-editable2 (multi-lines) test cases with selection carets enabled ######################################################################## def test_content_non_editable2_minimum_select_one_character(self): self.open_test_html2() self._test_minimum_select_one_character(self._content2, self.assertEqual) def test_long_press_to_select_when_partial_visible_word_is_selected(self): self.open_test_html() el = self._input sel = SelectionManager(el) # To successfully select the second word while the first word is being # selected, use sufficient spaces between 'a' and 'b' to avoid the # second caret covers on the second word. original_content = 'aaaaaaaa bbbbbbbb' el.clear() el.send_keys(original_content) words = original_content.split() # We cannot use self.long_press_on_word() directly since it has will # change the cursor position which affects this test. We have to store # the position of word 0 and word 1 before long-pressing to select the # word. word0_x, word0_y = self.word_location(el, 0) word1_x, word1_y = self.word_location(el, 1) self.long_press_on_location(el, word0_x, word0_y) self.assertEqual(words[0], sel.selected_content) self.long_press_on_location(el, word1_x, word1_y) self.assertEqual(words[1], sel.selected_content) self.long_press_on_location(el, word0_x, word0_y) self.assertEqual(words[0], sel.selected_content) # If the second carets is visible, it can be dragged to the position of # the first caret. After that, selection will contain only the first # character. (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform() self.assertEqual(words[0][0], sel.selected_content) def test_carets_do_not_jump_when_dragging_to_editable_content_boundary( self): self.open_test_html() el = self._input sel = SelectionManager(el) original_content = sel.content words = original_content.split() self.assertTrue( len(words) >= 3, 'Expect at least three words in the content.') # Goal: the selection does not being changed after dragging the caret # on the Y-axis only. target_content = words[1] self.long_press_on_word(el, 1) (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location() # Drag the first caret up by 50px. self.actions.flick(el, caret1_x, caret1_y, caret1_x, caret1_y - 50).perform() self.assertEqual(target_content, sel.selected_content) # Drag the first caret down by 50px. self.actions.flick(el, caret2_x, caret2_y, caret2_x, caret2_y + 50).perform() self.assertEqual(target_content, sel.selected_content)
class AccessibleCaretCursorModeTestCase(MarionetteTestCase): '''Test cases for AccessibleCaret under cursor mode, aka touch caret. ''' def setUp(self): # Code to execute before a test is being run. super(AccessibleCaretCursorModeTestCase, self).setUp() self.caret_tested_pref = 'layout.accessiblecaret.enabled' self.caret_timeout_ms_pref = 'layout.accessiblecaret.timeout_ms' self.prefs = { self.caret_tested_pref: True, self.caret_timeout_ms_pref: 0, } self.marionette.set_prefs(self.prefs) self.actions = Actions(self.marionette) def timeout_ms(self): 'Return touch caret expiration time in milliseconds.' return self.marionette.get_pref(self.caret_timeout_ms_pref) def open_test_html(self): 'Open html for testing and locate elements.' test_html = self.marionette.absolute_url('test_touchcaret.html') self.marionette.navigate(test_html) self._input = self.marionette.find_element(By.ID, 'input') self._textarea = self.marionette.find_element(By.ID, 'textarea') self._contenteditable = self.marionette.find_element( By.ID, 'contenteditable') def _test_move_caret_to_the_right_by_one_character(self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = sel.content target_content = target_content[:1] + content_to_add + target_content[ 1:] # Get touch caret (x, y) at position 1 and 2. el.tap() sel.move_caret_to_front() caret0_x, caret0_y = sel.caret_location() touch_caret0_x, touch_caret0_y = sel.touch_caret_location() sel.move_caret_by_offset(1) touch_caret1_x, touch_caret1_y = sel.touch_caret_location() # Tap the front of the input to make touch caret appear. el.tap(caret0_x, caret0_y) # Move touch caret self.actions.flick(el, touch_caret0_x, touch_caret0_y, touch_caret1_x, touch_caret1_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(target_content, sel.content) def _test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner( self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = sel.content + content_to_add # Tap the front of the input to make touch caret appear. el.tap() sel.move_caret_to_front() el.tap(*sel.caret_location()) # Move touch caret to the bottom-right corner of the element. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = el.size['width'], el.size['height'] self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(target_content, sel.content) def _test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = content_to_add + sel.content # Get touch caret location at the front. el.tap() sel.move_caret_to_front() dest_x, dest_y = sel.touch_caret_location() # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) src_x, src_y = sel.touch_caret_location() # Move touch caret to the front of the input box. self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(target_content, sel.content) def _test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout( self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' non_target_content = content_to_add + sel.content # Get touch caret expiration time in millisecond, and convert it to second. timeout = self.timeout_ms() / 1000.0 # Set a 3x timeout margin to prevent intermittent test failures. timeout *= 3 # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) # Wait until touch caret disappears, then pretend to move it to the # top-left corner of the input box. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 self.actions.wait(timeout).flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(non_target_content, sel.content) def _test_touch_caret_hides_after_receiving_wheel_event( self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' non_target_content = content_to_add + sel.content # Tap to make touch caret appear. Note: it's strange that when the caret # is at the end, the rect of the caret in <textarea> cannot be obtained. # A bug perhaps. el.tap() sel.move_caret_to_end() sel.move_caret_by_offset(1, backward=True) el.tap(*sel.caret_location()) # Send an arbitrary scroll-down-10px wheel event to the center of the # input box to hide touch caret. Then pretend to move it to the top-left # corner of the input box. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 el_center_x, el_center_y = el.rect['x'], el.rect['y'] self.marionette.execute_script(''' var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindowUtils); utils.sendWheelEvent(arguments[0], arguments[1], 0, 10, 0, WheelEvent.DOM_DELTA_PIXEL, 0, 0, 0, 0); ''', script_args=[el_center_x, el_center_y], sandbox='system') self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() assertFunc(non_target_content, sel.content) def _test_caret_not_appear_when_typing_in_scrollable_content( self, el, assertFunc): sel = SelectionManager(el) content_to_add = '!' target_content = sel.content + string.ascii_letters + content_to_add el.tap() sel.move_caret_to_end() # Insert a long string to the end of the <input>, which triggers # ScrollPositionChanged event. el.send_keys(string.ascii_letters) # The caret should not be visible. If it does appear wrongly due to the # ScrollPositionChanged event, we can drag it to the front of the # <input> to change the cursor position. src_x, src_y = sel.touch_caret_location() dest_x, dest_y = 0, 0 self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform() # The content should be inserted at the end of the <input>. el.send_keys(content_to_add) assertFunc(target_content, sel.content) ######################################################################## # <input> test cases with touch caret enabled ######################################################################## def test_input_move_caret_to_the_right_by_one_character(self): self.open_test_html() self._test_move_caret_to_the_right_by_one_character( self._input, self.assertEqual) def test_input_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner( self): self.open_test_html() self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner( self._input, self.assertEqual) def test_input_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner( self): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self._input, self.assertEqual) def test_input_caret_not_appear_when_typing_in_scrollable_content(self): self.open_test_html() self._test_caret_not_appear_when_typing_in_scrollable_content( self._input, self.assertEqual) def test_input_touch_caret_timeout(self): with self.marionette.using_prefs({self.caret_timeout_ms_pref: 1000}): self.open_test_html() self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout( self._input, self.assertNotEqual) ######################################################################## # <input> test cases with touch caret disabled ######################################################################## def test_input_move_caret_to_the_right_by_one_character_disabled(self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_the_right_by_one_character( self._input, self.assertNotEqual) def test_input_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled( self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self._input, self.assertNotEqual) ######################################################################## # <textarea> test cases with touch caret enabled ######################################################################## def test_textarea_move_caret_to_the_right_by_one_character(self): self.open_test_html() self._test_move_caret_to_the_right_by_one_character( self._textarea, self.assertEqual) def test_textarea_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner( self): self.open_test_html() self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner( self._textarea, self.assertEqual) def test_textarea_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner( self): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self._textarea, self.assertEqual) def test_textarea_touch_caret_timeout(self): with self.marionette.using_prefs({self.caret_timeout_ms_pref: 1000}): self.open_test_html() self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout( self._textarea, self.assertNotEqual) ######################################################################## # <textarea> test cases with touch caret disabled ######################################################################## def test_textarea_move_caret_to_the_right_by_one_character_disabled(self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_the_right_by_one_character( self._textarea, self.assertNotEqual) def test_textarea_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled( self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self._textarea, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with touch caret enabled ######################################################################## def test_contenteditable_move_caret_to_the_right_by_one_character(self): self.open_test_html() self._test_move_caret_to_the_right_by_one_character( self._contenteditable, self.assertEqual) def test_contenteditable_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner( self): self.open_test_html() self._test_move_caret_to_end_by_dragging_touch_caret_to_bottom_right_corner( self._contenteditable, self.assertEqual) def test_contenteditable_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner( self): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self._contenteditable, self.assertEqual) def test_contenteditable_touch_caret_timeout(self): with self.marionette.using_prefs({self.caret_timeout_ms_pref: 1000}): self.open_test_html() self._test_touch_caret_timeout_by_dragging_it_to_top_left_corner_after_timout( self._contenteditable, self.assertNotEqual) ######################################################################## # <div> contenteditable test cases with touch caret disabled ######################################################################## def test_contenteditable_move_caret_to_the_right_by_one_character_disabled( self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_the_right_by_one_character( self._contenteditable, self.assertNotEqual) def test_contenteditable_move_caret_to_front_by_dragging_touch_caret_to_top_left_corner_disabled( self): with self.marionette.using_prefs({self.caret_tested_pref: False}): self.open_test_html() self._test_move_caret_to_front_by_dragging_touch_caret_to_front_of_content( self._contenteditable, self.assertNotEqual) def test_caret_does_not_jump_when_dragging_to_editable_content_boundary( self): self.open_test_html() el = self._input sel = SelectionManager(el) content_to_add = '!' non_target_content = sel.content + content_to_add # Goal: the cursor position does not being changed after dragging the # caret down on the Y-axis. el.tap() sel.move_caret_to_front() el.tap(*sel.caret_location()) x, y = sel.touch_caret_location() # Drag the caret down by 50px, and insert '!'. self.actions.flick(el, x, y, x, y + 50).perform() self.actions.key_down(content_to_add).key_up(content_to_add).perform() self.assertNotEqual(non_target_content, sel.content)