This repository has been archived on 2024-12-22. You can view files and clone it, but cannot push or open issues or pull requests.
PyBitmessage-2024-12-22/mockenv/lib/python3.6/site-packages/kivy/tests/test_uix_textinput.py
2022-07-22 16:13:59 +05:30

565 lines
17 KiB
Python

'''
uix.textinput tests
========================
'''
import unittest
from itertools import count
from kivy.core.window import Window
from kivy.tests.common import GraphicUnitTest, UTMotionEvent
from kivy.uix.textinput import TextInput
from kivy.uix.widget import Widget
from kivy.clock import Clock
touch_id = count()
class TextInputTest(unittest.TestCase):
def test_focusable_when_disabled(self):
ti = TextInput()
ti.disabled = True
ti.focused = True
ti.bind(focus=self.on_focused)
def on_focused(self, instance, value):
self.assertTrue(instance.focused, value)
def test_wordbreak(self):
self.test_txt = "Firstlongline\n\nSecondveryverylongline"
ti = TextInput(width='30dp', size_hint_x=None)
ti.bind(text=self.on_text)
ti.text = self.test_txt
def on_text(self, instance, value):
# Check if text is modified while recreating from lines and lines_flags
self.assertEqual(instance.text, self.test_txt)
# Check if wordbreaking is correctly done
# If so Secondvery... should start from the 7th line
pos_S = self.test_txt.index('S')
self.assertEqual(instance.get_cursor_from_index(pos_S), (0, 6))
class TextInputIMETest(unittest.TestCase):
def test_ime(self):
empty_ti = TextInput()
empty_ti.focused = True
ti = TextInput(text='abc')
Window.dispatch('on_textedit', '')
self.assertEqual(empty_ti.text, '')
self.assertEqual(ti.text, 'abc')
ti.focused = True
Window.dispatch('on_textedit', '')
self.assertEqual(ti.text, 'abcㅎ')
Window.dispatch('on_textedit', '')
self.assertEqual(ti.text, 'abc하')
Window.dispatch('on_textedit', '')
Window.dispatch('on_textedit', '')
Window.dispatch('on_textinput', '')
Window.dispatch('on_textedit', '')
Window.dispatch('on_textedit', '')
self.assertEqual(ti.text, 'abc하세')
class TextInputGraphicTest(GraphicUnitTest):
def test_text_validate(self):
ti = TextInput(multiline=False)
ti.focus = True
self.render(ti)
self.assertFalse(ti.multiline)
self.assertTrue(ti.focus)
self.assertTrue(ti.text_validate_unfocus)
ti.validate_test = None
ti.bind(on_text_validate=lambda *_: setattr(
ti, 'validate_test', True
))
ti._key_down(
(
None, # displayed_str
None, # internal_str
'enter', # internal_action
1 # scale
),
repeat=False
)
self.assertTrue(ti.validate_test)
self.assertFalse(ti.focus)
ti.validate_test = None
ti.text_validate_unfocus = False
ti.focus = True
self.assertTrue(ti.focus)
ti._key_down(
(None, None, 'enter', 1),
repeat=False
)
self.assertTrue(ti.validate_test)
self.assertTrue(ti.focus)
def test_selection_enter_multiline(self):
text = 'multiline\ntext'
ti = TextInput(multiline=True, text=text)
ti.focus = True
self.render(ti)
self.assertTrue(ti.focus)
# assert cursor is here:
# multiline
# text$
self.assertEqual(
ti.cursor, (
len(text.split('\n')[-1]),
len(text.split('\n')) - 1
)
)
# move and check position
# mult$iline
# text
ti._key_down( # push selection
(
None, # displayed_str
None, # internal_str
'shift', # internal_action
1 # scale
),
repeat=False
)
ti._key_down(
(None, None, 'cursor_up', 1),
repeat=False
)
# pop selection
ti._key_up(
(None, None, 'shift', 1),
repeat=False
)
self.assertEqual(
ti.cursor, (
len(text.split('\n')[-1]),
len(text.split('\n')) - 2
)
)
self.assertEqual(ti.text, text)
# overwrite selection with \n
ti._key_down(
(None, None, 'enter', 1),
repeat=False
)
self.assertEqual(ti.text, text[:4] + '\n')
def test_selection_enter_singleline(self):
text = 'singleline'
ti = TextInput(multiline=False, text=text)
ti.focus = True
self.render(ti)
self.assertTrue(ti.focus)
# assert cursor is here:
# singleline$
self.assertEqual(ti.cursor, (len(text), 0))
# move and check position
# single$line
steps = 4
options = ((
'enter',
text
), (
'backspace',
text[:len(text) - steps]
))
for key, txt in options:
# push selection
ti._key_down((None, None, 'shift', 1), repeat=False)
for _ in range(steps):
ti._key_down(
(None, None, 'cursor_left', 1),
repeat=False
)
# pop selection
ti._key_up((None, None, 'shift', 1), repeat=False)
self.assertEqual(
ti.cursor, (len(text[:-steps]), 0)
)
self.assertEqual(ti.text, text)
# try to overwrite selection with \n
# (shouldn't work because single line)
ti._key_down(
(None, None, key, 1),
repeat=False
)
self.assertEqual(ti.text, txt)
ti._key_down((None, None, 'cursor_end', 1), repeat=False)
def test_del(self):
text = 'some_random_text'
ti = TextInput(multiline=False, text=text)
ti.focus = True
self.render(ti)
self.assertTrue(ti.focus)
# assert cursor is here:
self.assertEqual(ti.cursor, (len(text), 0))
steps_skip = 2
steps_select = 4
del_key = 'del'
for _ in range(steps_skip):
ti._key_down(
(None, None, 'cursor_left', 1),
repeat=False
)
# cursor at the place of ^
# some_random_te^xt
# push selection
ti._key_down((None, None, 'shift', 1), repeat=False)
for _ in range(steps_select):
ti._key_down(
(None, None, 'cursor_left', 1),
repeat=False
)
# pop selection
ti._key_up((None, None, 'shift', 1), repeat=False)
# cursor at the place of ^, selection between * chars
# some_rando*^m_te*xt
self.assertEqual(
ti.cursor, (len(text[:-steps_select - steps_skip]), 0)
)
self.assertEqual(ti.text, text)
ti._key_down(
(None, None, del_key, 1),
repeat=False
)
# cursor now at: some_rando^xt
self.assertEqual(ti.text, 'some_randoxt')
ti._key_down(
(None, None, del_key, 1),
repeat=False
)
self.assertEqual(ti.text, 'some_randot')
def test_escape(self):
text = 'some_random_text'
escape_key = 'escape'
ti = TextInput(multiline=False, text=text)
ti.focus = True
self.render(ti)
self.assertTrue(ti.focus)
ti._key_down(
(None, None, escape_key, 1),
repeat=False
)
self.assertFalse(ti.focus)
self.assertEqual(ti.text, text)
def test_no_shift_cursor_arrow_on_selection(self):
text = 'some_random_text'
ti = TextInput(multiline=False, text=text)
ti.focus = True
self.render(ti)
self.assertTrue(ti.focus)
# assert cursor is here:
self.assertEqual(ti.cursor, (len(text), 0))
steps_skip = 2
steps_select = 4
for _ in range(steps_skip):
ti._key_down(
(None, None, 'cursor_left', 1),
repeat=False
)
# cursor at the place of ^
# some_random_te^xt
# push selection
ti._key_down((None, None, 'shift', 1), repeat=False)
for _ in range(steps_select):
ti._key_down(
(None, None, 'cursor_left', 1),
repeat=False
)
# pop selection
ti._key_up((None, None, 'shift', 1), repeat=False)
# cursor at the place of ^, selection between * chars
# some_rando*^m_te*xt
ti._key_down(
(None, None, 'cursor_right', 1),
repeat=False
)
self.assertEqual(ti.cursor, (len(text) - steps_skip, 0))
def test_cursor_movement_control(self):
text = "these are\nmany words"
ti = TextInput(multiline=True, text=text)
ti.focus = True
self.render(ti)
self.assertTrue(ti.focus)
# assert cursor is here:
self.assertEqual(
ti.cursor, (
len(text.split('\n')[-1]),
len(text.split('\n')) - 1
)
)
options = (
('cursor_left', (5, 1)),
('cursor_left', (0, 1)),
('cursor_left', (6, 0)),
('cursor_right', (9, 0)),
('cursor_right', (4, 1)))
for key, pos in options:
ti._key_down((None, None, 'ctrl_L', 1), repeat=False)
ti._key_down((None, None, key, 1), repeat=False)
self.assertEqual(ti.cursor, pos)
ti._key_up((None, None, 'ctrl_L', 1), repeat=False)
def test_cursor_blink(self):
ti = TextInput(cursor_blink=True)
ti.focus = True
# overwrite blinking event, because too long delay
ti._do_blink_cursor_ev = Clock.create_trigger(
ti._do_blink_cursor, 0.01, interval=True
)
self.render(ti)
# from kwargs cursor_blink == True
self.assertTrue(ti.cursor_blink)
self.assertTrue(ti._do_blink_cursor_ev.is_triggered)
# set whether to blink & check if resets
ti.cursor_blink = False
for i in range(30):
self.advance_frames(int(0.01 * Clock._max_fps) + 1)
self.assertFalse(ti._do_blink_cursor_ev.is_triggered)
# no blinking, cursor visible
self.assertFalse(ti._cursor_blink)
ti.cursor_blink = True
self.assertTrue(ti.cursor_blink)
for i in range(30):
self.advance_frames(int(0.01 * Clock._max_fps) + 1)
self.assertTrue(ti._do_blink_cursor_ev.is_triggered)
def test_visible_lines_range(self):
ti = self.make_scrollable_text_input()
assert ti._visible_lines_range == (20, 30)
ti.height = ti_height_for_x_lines(ti, 2.5)
ti.do_cursor_movement('cursor_home', control=True)
self.advance_frames(1)
assert ti._visible_lines_range == (0, 3)
ti.height = ti_height_for_x_lines(ti, 0)
self.advance_frames(1)
assert ti._visible_lines_range == (0, 0)
def test_keyboard_scroll(self):
ti = self.make_scrollable_text_input()
prev_cursor = ti.cursor
ti.do_cursor_movement('cursor_home', control=True)
self.advance_frames(1)
assert ti._visible_lines_range == (0, 10)
assert prev_cursor != ti.cursor
prev_cursor = ti.cursor
ti.do_cursor_movement('cursor_down', control=True)
self.advance_frames(1)
assert ti._visible_lines_range == (1, 11)
# cursor position (col and row) should not be
# changed by "ctrl + cursor_down" and "ctrl + cursor_up"
assert prev_cursor == ti.cursor
prev_cursor = ti.cursor
ti.do_cursor_movement('cursor_up', control=True)
self.advance_frames(1)
assert ti._visible_lines_range == (0, 10)
assert prev_cursor == ti.cursor
prev_cursor = ti.cursor
ti.do_cursor_movement('cursor_end', control=True)
self.advance_frames(1)
assert ti._visible_lines_range == (20, 30)
assert prev_cursor != ti.cursor
def test_scroll_doesnt_move_cursor(self):
ti = self.make_scrollable_text_input()
from kivy.base import EventLoop
win = EventLoop.window
touch = UTMotionEvent("unittest", next(touch_id), {
"x": ti.center_x / float(win.width),
"y": ti.center_y / float(win.height),
})
touch.profile.append('button')
touch.button = 'scrolldown'
prev_cursor = ti.cursor
assert ti._visible_lines_range == (20, 30)
EventLoop.post_dispatch_input("begin", touch)
EventLoop.post_dispatch_input("end", touch)
self.advance_frames(1)
assert ti._visible_lines_range == (19, 29)
assert ti.cursor == prev_cursor
def test_vertical_scroll_doesnt_depend_on_lines_rendering(self):
# TextInput.on_touch_down was checking the possibility to scroll_up
# using the positions of the rendered lines' rects. These positions
# don't change when the lines are skipped (e.g. during fast scroll
# or ctrl+cursor_home) which lead to scroll freeze
ti = self.make_scrollable_text_input()
# move viewport to the first line
ti.do_cursor_movement('cursor_home', control=True)
self.advance_frames(1)
assert ti._visible_lines_range == (0, 10)
from kivy.base import EventLoop
win = EventLoop.window
# slowly scroll to the last line to render all lines at least once
for _ in range(30): # little overscroll is important for detection
touch = UTMotionEvent("unittest", next(touch_id), {
"x": ti.center_x / float(win.width),
"y": ti.center_y / float(win.height),
})
touch.profile.append('button')
touch.button = 'scrollup'
EventLoop.post_dispatch_input("begin", touch)
EventLoop.post_dispatch_input("end", touch)
self.advance_frames(1)
assert ti._visible_lines_range == (20, 30)
# jump to the first line again
ti.do_cursor_movement('cursor_home', control=True)
# temp fix: only change of cursor position triggers update as for now
ti._trigger_update_graphics()
self.advance_frames(1)
assert ti._visible_lines_range == (0, 10)
# scrolling up should work now
touch = UTMotionEvent("unittest", next(touch_id), {
"x": ti.center_x / float(win.width),
"y": ti.center_y / float(win.height),
})
touch.profile.append('button')
touch.button = 'scrollup'
EventLoop.post_dispatch_input("begin", touch)
EventLoop.post_dispatch_input("end", touch)
self.advance_frames(1)
assert ti._visible_lines_range == (1, 11)
def test_selectall_copy_paste(self):
text = 'test'
ti = TextInput(multiline=False, text=text)
ti.focus = True
self.render(ti)
from kivy.base import EventLoop
win = EventLoop.window
# select all
# win.dispatch(event_name, key, scancode, kstr, modifiers)
win.dispatch('on_key_down', 97, 4, 'a', ['capslock', 'ctrl'])
win.dispatch('on_key_up', 97, 4)
self.advance_frames(1)
# copy
win.dispatch('on_key_down', 99, 6, 'c',
['capslock', 'numlock', 'ctrl'])
win.dispatch('on_key_up', 99, 6)
self.advance_frames(1)
# home
win.dispatch('on_key_down', 278, 74, None, ['capslock'])
win.dispatch('on_key_up', 278, 74)
self.advance_frames(1)
# paste
win.dispatch('on_key_down', 118, 25, 'v', ['numlock', 'ctrl'])
win.dispatch('on_key_up', 118, 25)
self.advance_frames(1)
assert ti.text == 'testtest'
def make_scrollable_text_input(self, num_of_lines=30, n_lines_to_show=10):
"""Prepare and start rendering the scrollable text input.
num_of_lines -- amount of dummy lines used as contents
n_lines_to_show -- amount of lines to fit in viewport
"""
# create TextInput instance with dummy contents
text = '\n'.join(map(str, range(num_of_lines)))
ti = TextInput(text=text)
ti.focus = True
# use container to have flexible TextInput size
container = Widget()
container.add_widget(ti)
self.render(container)
# change TextInput's size to contain the needed amount of lines
ti.height = ti_height_for_x_lines(ti, n_lines_to_show)
self.advance_frames(1)
return ti
def ti_height_for_x_lines(ti, x):
"""Calculate TextInput height required to display x lines in viewport.
ti -- TextInput object being used
x -- number of lines to display
"""
padding_top = ti.padding[1]
padding_bottom = ti.padding[3]
return int((ti.line_height + ti.line_spacing) * x
+ padding_top + padding_bottom)
if __name__ == '__main__':
import unittest
unittest.main()