2014-06-11 14:28:00 +04:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
import re
|
|
|
|
|
2015-02-23 17:43:37 +03:00
|
|
|
from marionette_driver.by import By
|
|
|
|
from marionette_driver.marionette import Actions
|
|
|
|
from marionette_driver.selection import SelectionManager
|
2016-12-12 15:05:34 +03:00
|
|
|
from marionette_harness.marionette_test import (
|
|
|
|
MarionetteTestCase,
|
|
|
|
SkipTest,
|
|
|
|
parameterized
|
|
|
|
)
|
2015-02-23 17:43:37 +03:00
|
|
|
|
2014-06-11 14:28:00 +04:00
|
|
|
|
2015-09-30 13:29:34 +03:00
|
|
|
def skip_if_not_rotatable(target):
|
|
|
|
def wrapper(self, *args, **kwargs):
|
|
|
|
if not self.marionette.session_capabilities.get('rotatable'):
|
|
|
|
raise SkipTest('skipping due to device not rotatable')
|
|
|
|
return target(self, *args, **kwargs)
|
|
|
|
return wrapper
|
|
|
|
|
|
|
|
|
2015-12-22 09:14:12 +03:00
|
|
|
class AccessibleCaretSelectionModeTestCase(MarionetteTestCase):
|
2016-03-08 17:21:31 +03:00
|
|
|
'''Test cases for AccessibleCaret under selection mode.'''
|
2016-03-08 17:45:47 +03:00
|
|
|
# Element IDs.
|
2016-03-08 17:21:31 +03:00
|
|
|
_input_id = 'input'
|
2016-03-10 12:38:32 +03:00
|
|
|
_input_padding_id = 'input-padding'
|
2017-10-12 11:41:48 +03:00
|
|
|
_input_size_id = 'input-size'
|
2016-03-08 17:21:31 +03:00
|
|
|
_textarea_id = 'textarea'
|
|
|
|
_textarea2_id = 'textarea2'
|
2016-03-10 12:38:32 +03:00
|
|
|
_textarea_one_line_id = 'textarea-one-line'
|
2016-03-08 17:21:31 +03:00
|
|
|
_textarea_rtl_id = 'textarea-rtl'
|
|
|
|
_contenteditable_id = 'contenteditable'
|
|
|
|
_contenteditable2_id = 'contenteditable2'
|
|
|
|
_content_id = 'content'
|
|
|
|
_content2_id = 'content2'
|
|
|
|
_non_selectable_id = 'non-selectable'
|
2015-12-22 09:14:12 +03:00
|
|
|
|
2016-03-08 17:45:47 +03:00
|
|
|
# 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'
|
|
|
|
|
2014-06-11 14:28:00 +04:00
|
|
|
def setUp(self):
|
2016-03-08 17:21:31 +03:00
|
|
|
# Code to execute before every test is running.
|
2015-12-22 09:14:12 +03:00
|
|
|
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)
|
2014-06-11 14:28:00 +04:00
|
|
|
self.actions = Actions(self.marionette)
|
|
|
|
|
2016-03-08 17:45:47 +03:00
|
|
|
def open_test_html(self, test_html):
|
|
|
|
self.marionette.navigate(self.marionette.absolute_url(test_html))
|
2015-09-30 13:29:34 +03:00
|
|
|
|
2015-10-07 13:09:03 +03:00
|
|
|
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)
|
|
|
|
|
2015-09-30 13:29:35 +03:00
|
|
|
def word_location(self, el, ordinal):
|
|
|
|
'''Get the location (x, y) of the ordinal-th word in el.
|
|
|
|
|
|
|
|
The ordinal starts from 0.
|
2014-12-31 09:40:39 +03:00
|
|
|
|
|
|
|
Note: this function has a side effect which changes focus to the
|
|
|
|
target element el.
|
|
|
|
|
|
|
|
'''
|
|
|
|
sel = SelectionManager(el)
|
2015-10-07 13:09:03 +03:00
|
|
|
offset = self.word_offset(sel.content, ordinal)
|
2015-09-30 13:29:35 +03:00
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
# Move the blinking cursor to the word.
|
2014-06-11 14:28:00 +04:00
|
|
|
el.tap()
|
2016-03-08 17:45:47 +03:00
|
|
|
sel.move_cursor_to_front()
|
|
|
|
sel.move_cursor_by_offset(offset)
|
|
|
|
x, y = sel.cursor_location()
|
2014-06-11 14:28:00 +04:00
|
|
|
|
2015-09-30 13:29:35 +03:00
|
|
|
return x, y
|
2014-12-31 09:40:39 +03:00
|
|
|
|
2015-09-30 13:29:35 +03:00
|
|
|
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])
|
|
|
|
|
2015-09-30 13:29:35 +03:00
|
|
|
def long_press_on_location(self, el, x=None, y=None):
|
2014-12-31 09:40:39 +03:00
|
|
|
'''Long press the location (x, y) to select a word.
|
|
|
|
|
2015-09-30 13:29:35 +03:00
|
|
|
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.
|
2015-09-30 13:29:35 +03:00
|
|
|
This function sends synthesized eMouseLongTap to gecko.
|
2014-12-31 09:40:39 +03:00
|
|
|
|
|
|
|
'''
|
2015-09-30 13:29:35 +03:00
|
|
|
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')
|
2014-06-11 14:28:00 +04:00
|
|
|
|
2015-09-30 13:29:35 +03:00
|
|
|
def long_press_on_word(self, el, wordOrdinal):
|
|
|
|
x, y = self.word_location(el, wordOrdinal)
|
|
|
|
self.long_press_on_location(el, x, y)
|
2015-09-30 13:29:34 +03:00
|
|
|
|
2015-09-30 13:29:35 +03:00
|
|
|
def to_unix_line_ending(self, s):
|
2015-09-30 13:29:34 +03:00
|
|
|
"""Changes all Windows/Mac line endings in s to UNIX line endings."""
|
|
|
|
|
|
|
|
return s.replace('\r\n', '\n').replace('\r', '\n')
|
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
@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):
|
2016-03-08 17:45:47 +03:00
|
|
|
self.open_test_html(self._selection_html)
|
2016-03-08 17:21:31 +03:00
|
|
|
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):
|
2014-06-11 14:28:00 +04:00
|
|
|
sel = SelectionManager(el)
|
|
|
|
original_content = sel.content
|
|
|
|
words = original_content.split()
|
|
|
|
self.assertTrue(len(words) >= 2, 'Expect at least two words in the content.')
|
2014-06-13 04:33:00 +04:00
|
|
|
target_content = words[0]
|
2014-06-11 14:28:00 +04:00
|
|
|
|
2014-06-13 04:33:00 +04:00
|
|
|
# Goal: Select the first word.
|
2015-09-30 13:29:35 +03:00
|
|
|
self.long_press_on_word(el, 0)
|
2014-06-11 14:28:00 +04:00
|
|
|
|
2014-06-13 04:33:00 +04:00
|
|
|
# Ignore extra spaces selected after the word.
|
2016-03-08 17:21:31 +03:00
|
|
|
self.assertEqual(target_content, sel.selected_content)
|
2014-06-11 14:28:00 +04:00
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
@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):
|
2016-03-08 17:45:47 +03:00
|
|
|
self.open_test_html(self._selection_html)
|
2016-03-08 17:21:31 +03:00
|
|
|
el = self.marionette.find_element(By.ID, el_id)
|
2014-06-11 14:28:00 +04:00
|
|
|
sel = SelectionManager(el)
|
|
|
|
original_content = sel.content
|
|
|
|
words = original_content.split()
|
|
|
|
self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.')
|
|
|
|
|
2014-06-13 04:33:00 +04:00
|
|
|
# Goal: Select all text after the first word.
|
|
|
|
target_content = original_content[len(words[0]):]
|
2014-06-11 14:28:00 +04:00
|
|
|
|
2016-03-08 17:45:47 +03:00
|
|
|
# Get the location of the carets at the end of the content for later
|
|
|
|
# use.
|
2014-06-11 14:28:00 +04:00
|
|
|
el.tap()
|
|
|
|
sel.select_all()
|
2016-03-08 17:45:47 +03:00
|
|
|
end_caret_x, end_caret_y = sel.second_caret_location()
|
2014-06-11 14:28:00 +04:00
|
|
|
|
2015-09-30 13:29:35 +03:00
|
|
|
self.long_press_on_word(el, 0)
|
2014-06-11 14:28:00 +04:00
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
# Drag the second caret to the end of the content.
|
2016-03-08 17:45:47 +03:00
|
|
|
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
|
2014-06-11 14:28:00 +04:00
|
|
|
self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform()
|
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
# Drag the first caret to the previous position of the second caret.
|
2015-02-10 02:46:00 +03:00
|
|
|
self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform()
|
2014-06-11 14:28:00 +04:00
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
self.assertEqual(target_content, sel.selected_content)
|
2014-06-11 14:28:00 +04:00
|
|
|
|
2016-04-11 12:57:29 +03:00
|
|
|
@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)
|
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
@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):
|
2016-03-08 17:45:47 +03:00
|
|
|
self.open_test_html(self._selection_html)
|
2016-03-08 17:21:31 +03:00
|
|
|
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):
|
2016-03-08 17:45:47 +03:00
|
|
|
self.open_test_html(self._multipleline_html)
|
2016-03-08 17:21:31 +03:00
|
|
|
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):
|
2014-06-11 14:28:00 +04:00
|
|
|
sel = SelectionManager(el)
|
|
|
|
original_content = sel.content
|
|
|
|
words = original_content.split()
|
|
|
|
self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.')
|
|
|
|
|
2016-03-08 17:45:47 +03:00
|
|
|
# Get the location of the carets at the end of the content for later
|
|
|
|
# use.
|
2015-01-25 21:36:00 +03:00
|
|
|
sel.select_all()
|
2016-03-08 17:45:47 +03:00
|
|
|
end_caret_x, end_caret_y = sel.second_caret_location()
|
2015-01-25 21:36:00 +03:00
|
|
|
el.tap()
|
|
|
|
|
2014-06-13 04:33:00 +04:00
|
|
|
# Goal: Select the first character.
|
|
|
|
target_content = original_content[0]
|
2014-06-11 14:28:00 +04:00
|
|
|
|
2014-12-31 09:40:39 +03:00
|
|
|
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:
|
2015-09-30 13:29:35 +03:00
|
|
|
x, y = self.word_location(el, 0)
|
|
|
|
self.long_press_on_location(el, x, y)
|
2014-06-11 14:28:00 +04:00
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
# Drag the second caret to the end of the content.
|
2016-03-08 17:45:47 +03:00
|
|
|
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
|
2015-01-25 21:36:00 +03:00
|
|
|
self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform()
|
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
# Drag the second caret to the position of the first caret.
|
2016-03-08 17:45:47 +03:00
|
|
|
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
|
2015-02-10 02:46:00 +03:00
|
|
|
self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform()
|
2014-06-11 14:28:00 +04:00
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
self.assertEqual(target_content, sel.selected_content)
|
2014-06-11 14:28:00 +04:00
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
@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):
|
2017-10-16 12:58:41 +03:00
|
|
|
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)
|
2014-12-31 09:40:39 +03:00
|
|
|
|
2017-10-16 12:58:41 +03:00
|
|
|
# Compute the content of the first word in el2.
|
|
|
|
sel = SelectionManager(el2)
|
|
|
|
original_content = sel.content
|
|
|
|
words = original_content.split()
|
|
|
|
target_content = words[0]
|
2014-12-31 09:40:39 +03:00
|
|
|
|
2017-10-16 12:58:41 +03:00
|
|
|
# Goal: Tap to focus el1, and then select the first word on el2.
|
2014-12-31 09:40:39 +03:00
|
|
|
|
|
|
|
# We want to collect the location of the first word in el2 here
|
2015-09-30 13:29:35 +03:00
|
|
|
# since self.word_location() has the side effect which would
|
2014-12-31 09:40:39 +03:00
|
|
|
# change the focus.
|
2015-09-30 13:29:35 +03:00
|
|
|
x, y = self.word_location(el2, 0)
|
2017-10-16 12:58:41 +03:00
|
|
|
|
2014-12-31 09:40:39 +03:00
|
|
|
el1.tap()
|
2017-10-16 12:58:41 +03:00
|
|
|
self.long_press_on_location(el2, x, y)
|
|
|
|
self.assertEqual(target_content, sel.selected_content)
|
2016-03-08 17:21:31 +03:00
|
|
|
|
|
|
|
@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):
|
2016-03-08 17:45:47 +03:00
|
|
|
self.open_test_html(self._selection_html)
|
2016-03-08 17:21:31 +03:00
|
|
|
el = self.marionette.find_element(By.ID, el_id)
|
|
|
|
non_selectable = self.marionette.find_element(By.ID, self._non_selectable_id)
|
2014-12-31 09:40:39 +03:00
|
|
|
|
2015-10-13 12:26:29 +03:00
|
|
|
# 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)
|
2016-03-08 17:21:31 +03:00
|
|
|
self.long_press_on_location(non_selectable)
|
2015-10-13 12:26:29 +03:00
|
|
|
active_sel = SelectionManager(self.marionette.get_active_element())
|
|
|
|
self.assertEqual(sel.content, active_sel.content)
|
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
@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):
|
2015-02-11 00:03:00 +03:00
|
|
|
'''Test tilt handling when carets overlap to each other.
|
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
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.
|
2015-02-11 00:03:00 +03:00
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
'''
|
2016-03-08 17:45:47 +03:00
|
|
|
self.open_test_html(self._selection_html)
|
2016-03-08 17:21:31 +03:00
|
|
|
el = self.marionette.find_element(By.ID, el_id)
|
2015-02-11 00:03:00 +03:00
|
|
|
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.
|
2015-09-30 13:29:35 +03:00
|
|
|
self.long_press_on_word(el, 0)
|
2015-02-11 00:03:00 +03:00
|
|
|
target_content = sel.selected_content
|
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
# Drag the first caret to the position of the second caret to trigger
|
2015-02-11 00:03:00 +03:00
|
|
|
# carets overlapping.
|
2016-03-08 17:45:47 +03:00
|
|
|
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
|
2015-02-11 00:03:00 +03:00
|
|
|
self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform()
|
|
|
|
|
2015-02-12 03:05:00 +03:00
|
|
|
# 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.
|
2016-03-08 17:45:47 +03:00
|
|
|
(caret3_x, caret3_y), (caret4_x, caret4_y) = sel.carets_location()
|
2015-02-11 00:03:00 +03:00
|
|
|
|
2015-10-19 02:53:00 +03:00
|
|
|
# The following values are from ua.css and all.js
|
2016-03-08 17:21:31 +03:00
|
|
|
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
|
2015-02-12 03:05:00 +03:00
|
|
|
|
|
|
|
left_caret_left_edge_x = caret3_x + caret_margin_left + tilt_left_margin_left
|
2015-05-07 08:55:00 +03:00
|
|
|
el.tap(left_caret_left_edge_x + 2, caret3_y)
|
2015-02-12 03:05:00 +03:00
|
|
|
|
|
|
|
right_caret_right_edge_x = (caret4_x + caret_margin_left +
|
|
|
|
tilt_right_margin_left + caret_width)
|
2015-05-07 08:55:00 +03:00
|
|
|
el.tap(right_caret_right_edge_x - 2, caret4_y)
|
2015-02-11 00:03:00 +03:00
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
# Drag the first caret back to the initial selection, the first word.
|
2015-02-11 00:03:00 +03:00
|
|
|
self.actions.flick(el, caret3_x, caret3_y, caret1_x, caret1_y).perform()
|
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
self.assertEqual(target_content, sel.selected_content)
|
2015-09-30 13:29:34 +03:00
|
|
|
|
|
|
|
def test_drag_caret_over_non_selectable_field(self):
|
2016-03-08 17:45:47 +03:00
|
|
|
'''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.
|
|
|
|
|
|
|
|
'''
|
2016-03-08 17:45:47 +03:00
|
|
|
self.open_test_html(self._multiplerange_html)
|
2016-03-08 17:21:31 +03:00
|
|
|
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')
|
2015-09-30 13:29:34 +03:00
|
|
|
|
|
|
|
# Select target element and get target caret location
|
2016-03-08 17:21:31 +03:00
|
|
|
self.long_press_on_word(sel4, 3)
|
|
|
|
sel = SelectionManager(body)
|
2016-03-08 17:45:47 +03:00
|
|
|
end_caret_x, end_caret_y = sel.second_caret_location()
|
2015-09-30 13:29:34 +03:00
|
|
|
|
2016-03-08 17:21:31 +03:00
|
|
|
self.long_press_on_word(sel6, 0)
|
2016-03-08 17:45:47 +03:00
|
|
|
end_caret2_x, end_caret2_y = sel.second_caret_location()
|
2015-09-30 13:29:34 +03:00
|
|
|
|
|
|
|
# Select start element
|
2016-03-08 17:21:31 +03:00
|
|
|
self.long_press_on_word(sel3, 3)
|
2015-09-30 13:29:34 +03:00
|
|
|
|
|
|
|
# Drag end caret to target location
|
2016-03-08 17:45:47 +03:00
|
|
|
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
|
2016-03-08 17:21:31 +03:00
|
|
|
self.actions.flick(body, caret2_x, caret2_y, end_caret_x, end_caret_y, 1).perform()
|
2015-09-30 13:29:35 +03:00
|
|
|
self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()),
|
2015-09-30 13:29:34 +03:00
|
|
|
'this 3\nuser can select this')
|
|
|
|
|
2016-03-08 17:45:47 +03:00
|
|
|
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
|
2016-03-08 17:21:31 +03:00
|
|
|
self.actions.flick(body, caret2_x, caret2_y, end_caret2_x, end_caret2_y, 1).perform()
|
2015-09-30 13:29:35 +03:00
|
|
|
self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()),
|
2015-09-30 13:29:34 +03:00
|
|
|
'this 3\nuser can select this 4\nuser can select this 5\nuser')
|
|
|
|
|
|
|
|
# Drag first caret to target location
|
2016-03-08 17:45:47 +03:00
|
|
|
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
|
2016-03-08 17:21:31 +03:00
|
|
|
self.actions.flick(body, caret1_x, caret1_y, end_caret_x, end_caret_y, 1).perform()
|
2015-09-30 13:29:35 +03:00
|
|
|
self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()),
|
2015-09-30 13:29:34 +03:00
|
|
|
'4\nuser can select this 5\nuser')
|
|
|
|
|
2016-04-11 12:57:29 +03:00
|
|
|
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')
|
|
|
|
|
2015-09-30 13:29:34 +03:00
|
|
|
def test_drag_caret_to_beginning_of_a_line(self):
|
|
|
|
'''Bug 1094056
|
|
|
|
Test caret visibility when caret is dragged to beginning of a line
|
|
|
|
'''
|
2016-03-08 17:45:47 +03:00
|
|
|
self.open_test_html(self._multiplerange_html)
|
2016-03-08 17:21:31 +03:00
|
|
|
body = self.marionette.find_element(By.ID, 'bd')
|
|
|
|
sel1 = self.marionette.find_element(By.ID, 'sel1')
|
|
|
|
sel2 = self.marionette.find_element(By.ID, 'sel2')
|
2015-09-30 13:29:34 +03:00
|
|
|
|
|
|
|
# Select the first word in the second line
|
2016-03-08 17:21:31 +03:00
|
|
|
self.long_press_on_word(sel2, 0)
|
|
|
|
sel = SelectionManager(body)
|
2016-03-08 17:45:47 +03:00
|
|
|
(start_caret_x, start_caret_y), (end_caret_x, end_caret_y) = sel.carets_location()
|
2015-09-30 13:29:34 +03:00
|
|
|
|
|
|
|
# Select target word in the first line
|
2016-03-08 17:21:31 +03:00
|
|
|
self.long_press_on_word(sel1, 2)
|
2015-09-30 13:29:34 +03:00
|
|
|
|
|
|
|
# Drag end caret to the beginning of the second line
|
2016-03-08 17:45:47 +03:00
|
|
|
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
|
2016-03-08 17:21:31 +03:00
|
|
|
self.actions.flick(body, caret2_x, caret2_y, start_caret_x, start_caret_y).perform()
|
2015-09-30 13:29:34 +03:00
|
|
|
|
|
|
|
# Drag end caret back to the target word
|
2016-03-08 17:21:31 +03:00
|
|
|
self.actions.flick(body, start_caret_x, start_caret_y, caret2_x, caret2_y).perform()
|
2015-09-30 13:29:34 +03:00
|
|
|
|
2015-10-07 13:09:03 +03:00
|
|
|
self.assertEqual(self.to_unix_line_ending(sel.selected_content), 'select')
|
2015-09-30 13:29:34 +03:00
|
|
|
|
|
|
|
@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.
|
|
|
|
'''
|
2016-03-08 17:45:47 +03:00
|
|
|
self.open_test_html(self._longtext_html)
|
2016-03-08 17:21:31 +03:00
|
|
|
body = self.marionette.find_element(By.ID, 'bd')
|
|
|
|
longtext = self.marionette.find_element(By.ID, 'longtext')
|
2015-09-30 13:29:34 +03:00
|
|
|
|
|
|
|
# Select word in portrait mode, then change to landscape mode
|
|
|
|
self.marionette.set_orientation('portrait')
|
2016-03-08 17:21:31 +03:00
|
|
|
self.long_press_on_word(longtext, 12)
|
|
|
|
sel = SelectionManager(body)
|
2016-03-08 17:45:47 +03:00
|
|
|
(p_start_caret_x, p_start_caret_y), (p_end_caret_x, p_end_caret_y) = sel.carets_location()
|
2015-09-30 13:29:34 +03:00
|
|
|
self.marionette.set_orientation('landscape')
|
2016-03-08 17:45:47 +03:00
|
|
|
(l_start_caret_x, l_start_caret_y), (l_end_caret_x, l_end_caret_y) = sel.carets_location()
|
2015-09-30 13:29:34 +03:00
|
|
|
|
|
|
|
# Drag end caret to the start caret to change the selected content
|
2016-03-08 17:21:31 +03:00
|
|
|
self.actions.flick(body, l_end_caret_x, l_end_caret_y,
|
|
|
|
l_start_caret_x, l_start_caret_y).perform()
|
2015-09-30 13:29:34 +03:00
|
|
|
|
|
|
|
# Change orientation back to portrait mode to prevent affecting
|
|
|
|
# other tests
|
|
|
|
self.marionette.set_orientation('portrait')
|
|
|
|
|
2015-10-07 13:09:03 +03:00
|
|
|
self.assertEqual(self.to_unix_line_ending(sel.selected_content), 'o')
|
2015-09-30 13:29:34 +03:00
|
|
|
|
|
|
|
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.
|
|
|
|
'''
|
2016-03-08 17:45:47 +03:00
|
|
|
self.open_test_html(self._iframe_html)
|
2016-03-08 17:21:31 +03:00
|
|
|
iframe = self.marionette.find_element(By.ID, 'frame')
|
2015-09-30 13:29:34 +03:00
|
|
|
|
|
|
|
# switch to inner iframe and scroll to the bottom
|
2016-03-08 17:21:31 +03:00
|
|
|
self.marionette.switch_to_frame(iframe)
|
2015-09-30 13:29:34 +03:00
|
|
|
self.marionette.execute_script(
|
|
|
|
'document.getElementById("bd").scrollTop += 999')
|
|
|
|
|
|
|
|
# long press to select bottom text
|
2016-03-08 17:21:31 +03:00
|
|
|
body = self.marionette.find_element(By.ID, 'bd')
|
|
|
|
sel = SelectionManager(body)
|
2015-09-30 13:29:34 +03:00
|
|
|
self._bottomtext = self.marionette.find_element(By.ID, 'bottomtext')
|
2015-09-30 13:29:35 +03:00
|
|
|
self.long_press_on_location(self._bottomtext)
|
2015-09-30 13:29:34 +03:00
|
|
|
|
2015-10-07 13:09:03 +03:00
|
|
|
self.assertNotEqual(self.to_unix_line_ending(sel.selected_content), '')
|
2015-09-30 13:29:34 +03:00
|
|
|
|
2015-11-16 13:16:43 +03:00
|
|
|
def test_carets_initialized_in_display_none(self):
|
|
|
|
'''Test AccessibleCaretEventHub is properly initialized on a <html> with
|
|
|
|
display: none.
|
|
|
|
|
|
|
|
'''
|
2016-03-08 17:45:47 +03:00
|
|
|
self.open_test_html(self._display_none_html)
|
2016-03-08 17:21:31 +03:00
|
|
|
html = self.marionette.find_element(By.ID, 'html')
|
|
|
|
content = self.marionette.find_element(By.ID, 'content')
|
2015-11-16 13:16:43 +03:00
|
|
|
|
|
|
|
# Remove 'display: none' from <html>
|
|
|
|
self.marionette.execute_script(
|
|
|
|
'arguments[0].style.display = "unset";',
|
2016-03-08 17:21:31 +03:00
|
|
|
script_args=[html]
|
2015-11-16 13:16:43 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
# If AccessibleCaretEventHub is initialized successfully, select a word
|
|
|
|
# should work.
|
2016-03-08 17:21:31 +03:00
|
|
|
self._test_long_press_to_select_a_word(content)
|
2015-05-07 08:55:00 +03:00
|
|
|
|
2015-10-07 13:09:03 +03:00
|
|
|
def test_long_press_to_select_when_partial_visible_word_is_selected(self):
|
2016-03-08 17:45:47 +03:00
|
|
|
self.open_test_html(self._selection_html)
|
2017-10-12 11:41:48 +03:00
|
|
|
el = self.marionette.find_element(By.ID, self._input_size_id)
|
2015-10-07 13:09:03 +03:00
|
|
|
sel = SelectionManager(el)
|
|
|
|
|
2017-10-12 11:41:48 +03:00
|
|
|
original_content = sel.content
|
2015-10-07 13:09:03 +03:00
|
|
|
words = original_content.split()
|
|
|
|
|
2017-10-12 11:41:48 +03:00
|
|
|
# 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.
|
2015-10-07 13:09:03 +03:00
|
|
|
word0_x, word0_y = self.word_location(el, 0)
|
|
|
|
|
2017-10-12 11:41:48 +03:00
|
|
|
# Long press on the second word.
|
|
|
|
self.long_press_on_word(el, 1)
|
2015-10-07 13:09:03 +03:00
|
|
|
self.assertEqual(words[1], sel.selected_content)
|
|
|
|
|
2017-10-12 11:41:48 +03:00
|
|
|
# Long press on the first word.
|
2015-10-07 13:09:03 +03:00
|
|
|
self.long_press_on_location(el, word0_x, word0_y)
|
|
|
|
self.assertEqual(words[0], sel.selected_content)
|
|
|
|
|
2017-10-12 11:41:48 +03:00
|
|
|
# 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.
|
2016-03-08 17:45:47 +03:00
|
|
|
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
|
2015-10-07 13:09:03 +03:00
|
|
|
self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform()
|
|
|
|
self.assertEqual(words[0][0], sel.selected_content)
|
2015-10-07 13:09:04 +03:00
|
|
|
|
2016-03-10 12:38:32 +03:00
|
|
|
@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):
|
2016-03-08 17:45:47 +03:00
|
|
|
self.open_test_html(self._selection_html)
|
2016-03-10 12:38:32 +03:00
|
|
|
el = self.marionette.find_element(By.ID, el_id)
|
2015-10-07 13:09:04 +03:00
|
|
|
sel = SelectionManager(el)
|
|
|
|
original_content = sel.content
|
|
|
|
words = original_content.split()
|
|
|
|
self.assertTrue(len(words) >= 3, 'Expect at least three words in the content.')
|
|
|
|
|
2016-03-10 12:38:32 +03:00
|
|
|
# Goal: the selection is not changed after dragging the caret on the
|
|
|
|
# Y-axis.
|
2015-10-07 13:09:04 +03:00
|
|
|
target_content = words[1]
|
|
|
|
|
|
|
|
self.long_press_on_word(el, 1)
|
2016-03-08 17:45:47 +03:00
|
|
|
(caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
|
2015-10-07 13:09:04 +03:00
|
|
|
|
|
|
|
# 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)
|
|
|
|
|
2016-03-10 12:38:32 +03:00
|
|
|
# Drag the second caret down by 50px.
|
2015-10-07 13:09:04 +03:00
|
|
|
self.actions.flick(el, caret2_x, caret2_y, caret2_x, caret2_y + 50).perform()
|
|
|
|
self.assertEqual(target_content, sel.selected_content)
|