def test_action_is_callback(self): km = KeyMap() km.bind(key='a', action=lambda widget: widget.set_text('foo')) widget = km.wrap(Text)('Test Text') widget.keypress((80,), 'a') self.assertEqual(widget.text, 'foo')
def test_key_translation(self): km = KeyMap() km.bind(key='a', action=lambda widget: widget.set_text('Key pressed: a')) km.bind(key='b', action=Key('a')) widget = km.wrap(Text)('Test Text') widget.keypress((80,), 'b') self.assertEqual(widget.text, 'Key pressed: a')
def test_default_callback(self): def cb(action, widget): widget.set_text(action) km = KeyMap(callback=cb) km.bind(key='a', action='foo') widget = km.wrap(Text)('Test Text') widget.keypress((80,), 'a') self.assertEqual(widget.text, 'foo')
def test_widget_callback_overrides_default_callback(self): def default_cb(action, widget): widget.set_text(action) def widget_cb(action, widget): widget.set_text(action.upper()) km = KeyMap(callback=default_cb) km.bind(key='a', action='foo') widget = km.wrap(Text, callback=widget_cb)('Test Text') widget.keypress((80,), 'a') self.assertEqual(widget.text, 'FOO')
class TestKeyMap(unittest.TestCase): def setUp(self): self.km = KeyMap() def test_clear_all_contexts(self): self.km.bind(key='a', action='1', context='x') self.km.bind(key='b', action='2', context='y') self.km.bind(key='c', action='3', context='z') import sys if sys.hexversion < 0x03060000: # Python <= 3.6 dicts are not ordered yet self.assertEqual(set(self.km.keys()), {'a', 'b', 'c'}) else: self.assertEqual(tuple(self.km.keys()), ('a', 'b', 'c')) self.km.clear() self.assertEqual(tuple(self.km.keys()), ()) def test_clear_context(self): self.km.bind(key='a', action='1', context='x') self.km.bind(key='b', action='2', context='y') self.km.bind(key='c', action='3', context='z') import sys if sys.hexversion < 0x03060000: # Python <= 3.6 dicts are not ordered yet self.assertEqual(set(self.km.keys()), {'a', 'b', 'c'}) else: self.assertEqual(tuple(self.km.keys()), ('a', 'b', 'c')) self.km.clear(context='x') if sys.hexversion < 0x03060000: # Python <= 3.6 dicts are not ordered yet self.assertEqual(set(self.km.keys()), {'b', 'c'}) else: self.assertEqual(tuple(self.km.keys()), ('b', 'c')) self.km.clear(context='y') self.assertEqual(tuple(self.km.keys()), ('c',)) self.km.clear(context='z') self.assertEqual(tuple(self.km.keys()), ())
class TestKeyMap_with_nested_widgets(unittest.TestCase): def setUp(self): self.km = KeyMap(callback=self.handle_action) self.km.on_keychain(self.handle_keychain_changed) self.last_action = None self.action_widget = None self.action_counter = 0 self.active_keychains = set() # Create a Pile of two ListBoxes with some Text items def mk_item(widgetcls, i, context): return self.km.wrap(widgetcls, context=context)('%s: %s' % (context, i)) self.listw1 = self.km.wrap(urwid.ListBox, context='list1')( urwid.SimpleFocusListWalker([mk_item(urwid.Text, i, context='item1') for i in range(1, 10)]) ) self.listw2 = self.km.wrap(urwid.ListBox, context='list2')( urwid.SimpleFocusListWalker([mk_item(urwid.Edit, i, context='item2') for i in range(100, 1000, 100)]) ) self.mainw = self.km.wrap(urwid.Pile, context='main')([self.listw1, self.listw2]) self.km.bind('A', 'A in main', context='main') self.km.bind('B', 'B in main', context='main') self.km.bind('1 2 A', '12A in main', context='main') self.km.bind('1 2 B', '12B in main', context='main') self.km.bind('C', 'C in list1', context='list1') self.km.bind('D', 'D in list1', context='list1') self.km.bind('2 3 A', '23A in list1', context='list1') self.km.bind('2 3 B', '23B in list1', context='list1') self.km.bind('C', 'C in list2', context='list2') self.km.bind('D', 'D in list2', context='list2') self.km.bind('2 3 A', '23A in list2', context='list2') self.km.bind('2 3 B', '23B in list2', context='list2') self.km.bind('E', 'E in item1', context='item1') self.km.bind('F', 'F in item1', context='item1') self.km.bind('3 4 A', '34A in item1', context='item1') self.km.bind('3 4 B', '34B in item1', context='item1') self.km.bind('E', 'E in item2', context='item2') self.km.bind('F', 'F in item2', context='item2') self.km.bind('3 4 A', '34A in item2', context='item2') self.km.bind('3 4 B', '34B in item2', context='item2') def handle_action(self, action, widget): self.last_action = action self.action_counter += 1 self.action_widget = widget def handle_keychain_changed(self, keymap, context, active_keychains, keys_given): self.active_keychains = set(active_keychains) def press_key(self, key): self.mainw.keypress((80, 25), key) def assert_active_keychains(self, *active_keychains): self.assertEqual(self.active_keychains, set(active_keychains)) def assert_current_keychain(self, *keys): # If there are no active keychains, we don't want this test to pass if # given keys are expected. ak = tuple(self.active_keychains) if len(keys) < 1: self.assertEqual(ak, ()) else: self.assertEqual(self.km.current_keychain, keys) def assert_action(self, exp_action=None, exp_count=0, exp_widget=None): self.assertEqual(self.last_action, exp_action) self.assertEqual(self.action_counter, exp_count) self.assertEqual(self.action_widget, exp_widget) def test_mapped_singlekeys_in_main_context(self): self.press_key('A') self.assert_current_keychain() self.assert_action(exp_action='A in main', exp_count=1, exp_widget=self.mainw) self.assert_active_keychains() self.press_key('B') self.assert_current_keychain() self.assert_action(exp_action='B in main', exp_count=2, exp_widget=self.mainw) self.assert_active_keychains() def test_mapped_singlekeys_in_list_with_Text_widgets(self): self.mainw.focus_position = 0 self.press_key('C') self.assert_current_keychain() self.assert_action(exp_action='C in list1', exp_count=1, exp_widget=self.listw1) self.assert_active_keychains() self.press_key('D') self.assert_current_keychain() self.assert_action(exp_action='D in list1', exp_count=2, exp_widget=self.listw1) self.assert_active_keychains() def test_mapped_singlekeys_in_list_with_Edit_widgets(self): self.mainw.focus_position = 1 self.press_key('C') self.assertEqual(self.mainw.focus.focus.edit_text, 'C') self.assert_current_keychain() self.assert_action(exp_action=None, exp_count=0, exp_widget=None) self.assert_active_keychains() self.press_key('D') self.assertEqual(self.mainw.focus.focus.edit_text, 'CD') self.assert_current_keychain() self.assert_action(exp_action=None, exp_count=0, exp_widget=None) self.assert_active_keychains() def test_mapped_singlekeys_in_Text_widget(self): self.mainw.focus_position = 0 self.press_key('E') self.assert_current_keychain() self.assert_action(exp_action='E in item1', exp_count=1, exp_widget=self.listw1.focus) self.assert_active_keychains() self.press_key('F') self.assert_current_keychain() self.assert_action(exp_action='F in item1', exp_count=2, exp_widget=self.listw1.focus) self.assert_active_keychains() def test_mapped_singlekeys_in_Edit_widget(self): self.mainw.focus_position = 1 self.press_key('E') self.assertEqual(self.mainw.focus.focus.edit_text, 'E') self.assert_current_keychain() self.assert_action(exp_action=None, exp_count=0, exp_widget=None) self.assert_active_keychains() self.press_key('F') self.assertEqual(self.mainw.focus.focus.edit_text, 'EF') self.assert_current_keychain() self.assert_action(exp_action=None, exp_count=0, exp_widget=None) self.assert_active_keychains() def test_unmapped_singlekeys(self): self.press_key('Z') self.assert_current_keychain() self.assert_action(exp_action=None, exp_count=0, exp_widget=None) self.assert_active_keychains() def test_keychain_in_main_context(self): def start_keychain(exp_action, exp_count, exp_widget): self.press_key('1') self.assert_current_keychain('1') self.assert_action(exp_action=exp_action, exp_count=exp_count, exp_widget=exp_widget) self.assert_active_keychains((('1', '2', 'A'), '12A in main'), (('1', '2', 'B'), '12B in main')) self.press_key('2') self.assert_current_keychain('1', '2') self.assert_action(exp_action=exp_action, exp_count=exp_count, exp_widget=exp_widget) self.assert_active_keychains((('1', '2', 'A'), '12A in main'), (('1', '2', 'B'), '12B in main')) start_keychain(exp_action=None, exp_count=0, exp_widget=None) self.press_key('A') self.assert_action(exp_action='12A in main', exp_count=1, exp_widget=self.mainw) self.assert_current_keychain() self.assert_active_keychains() start_keychain(exp_action='12A in main', exp_count=1, exp_widget=self.mainw) self.press_key('B') self.assert_action(exp_action='12B in main', exp_count=2, exp_widget=self.mainw) self.assert_current_keychain() self.assert_active_keychains() def test_keychain_in_list1_context(self): self.mainw.focus_position = 0 def start_keychain(exp_action, exp_count, exp_widget): self.press_key('2') self.assert_current_keychain('2') self.assert_action(exp_action=exp_action, exp_count=exp_count, exp_widget=exp_widget) self.assert_active_keychains((('2', '3', 'A'), '23A in list1'), (('2', '3', 'B'), '23B in list1')) self.press_key('3') self.assert_current_keychain('2', '3') self.assert_action(exp_action=exp_action, exp_count=exp_count, exp_widget=exp_widget) self.assert_active_keychains((('2', '3', 'A'), '23A in list1'), (('2', '3', 'B'), '23B in list1')) start_keychain(exp_action=None, exp_count=0, exp_widget=None) self.press_key('A') self.assert_action(exp_action='23A in list1', exp_count=1, exp_widget=self.listw1) self.assert_current_keychain() self.assert_active_keychains() start_keychain(exp_action='23A in list1', exp_count=1, exp_widget=self.listw1) self.press_key('B') self.assert_action(exp_action='23B in list1', exp_count=2, exp_widget=self.listw1) self.assert_current_keychain() self.assert_active_keychains() def test_keychain_in_item1_context(self): self.mainw.focus_position = 0 def start_keychain(exp_action, exp_count, exp_widget): self.press_key('3') self.assert_current_keychain('3') self.assert_action(exp_action=exp_action, exp_count=exp_count, exp_widget=exp_widget) self.assert_active_keychains((('3', '4', 'A'), '34A in item1'), (('3', '4', 'B'), '34B in item1')) self.press_key('4') self.assert_current_keychain('3', '4') self.assert_action(exp_action=exp_action, exp_count=exp_count, exp_widget=exp_widget) self.assert_active_keychains((('3', '4', 'A'), '34A in item1'), (('3', '4', 'B'), '34B in item1')) start_keychain(exp_action=None, exp_count=0, exp_widget=None) self.press_key('A') self.assert_action(exp_action='34A in item1', exp_count=1, exp_widget=self.listw1.focus) self.assert_current_keychain() self.assert_active_keychains() start_keychain(exp_action='34A in item1', exp_count=1, exp_widget=self.listw1.focus) self.press_key('B') self.assert_action(exp_action='34B in item1', exp_count=2, exp_widget=self.listw1.focus) self.assert_current_keychain() self.assert_active_keychains() def test_keychain_in_list2_context(self): self.mainw.focus_position = 1 text = '' def press_key(key): nonlocal text text += key self.press_key(key) def start_keychain(): for key in ('2', '3'): press_key(key) self.assertEqual(self.listw2.focus.edit_text, text) self.assert_current_keychain() self.assert_action(exp_action=None, exp_count=0, exp_widget=None) self.assert_active_keychains() for key in ('A', 'B'): start_keychain() press_key(key) self.assertEqual(self.listw2.focus.edit_text, text) self.assert_current_keychain() self.assert_action(exp_action=None, exp_count=0, exp_widget=None) self.assert_active_keychains() def test_keychain_in_item2_context(self): self.mainw.focus_position = 1 text = '' def press_key(key): # noqa: E306 nonlocal text text += key self.press_key(key) def start_keychain(): for key in ('3', '4'): press_key(key) self.assertEqual(self.listw2.focus.edit_text, text) self.assert_current_keychain() self.assert_action(exp_action=None, exp_count=0, exp_widget=None) self.assert_active_keychains() for key in ('A', 'B'): start_keychain() press_key(key) self.assertEqual(self.listw2.focus.edit_text, text) self.assert_current_keychain() self.assert_action(exp_action=None, exp_count=0, exp_widget=None) self.assert_active_keychains() def test_abort_chain_with_builtin_key(self): def start_keychain(exp_action, exp_count, exp_widget): self.press_key('1') self.assert_current_keychain('1') self.assert_action(exp_action=exp_action, exp_count=exp_count, exp_widget=exp_widget) self.assert_active_keychains((('1', '2', 'A'), '12A in main'), (('1', '2', 'B'), '12B in main')) self.press_key('2') self.assert_current_keychain('1', '2') self.assert_action(exp_action=exp_action, exp_count=exp_count, exp_widget=exp_widget) self.assert_active_keychains((('1', '2', 'A'), '12A in main'), (('1', '2', 'B'), '12B in main')) start_keychain(exp_action=None, exp_count=0, exp_widget=None) self.press_key('down') self.assert_action(exp_action=None, exp_count=0, exp_widget=None) self.assert_current_keychain() self.assert_active_keychains() def test_abort_chain_with_mapped_key(self): self.km.bind('x', 'x in main', context='main') def start_keychain(exp_action, exp_count, exp_widget): self.press_key('1') self.assert_current_keychain('1') self.assert_action(exp_action=exp_action, exp_count=exp_count, exp_widget=exp_widget) self.assert_active_keychains((('1', '2', 'A'), '12A in main'), (('1', '2', 'B'), '12B in main')) self.press_key('2') self.assert_current_keychain('1', '2') self.assert_action(exp_action=exp_action, exp_count=exp_count, exp_widget=exp_widget) self.assert_active_keychains((('1', '2', 'A'), '12A in main'), (('1', '2', 'B'), '12B in main')) start_keychain(exp_action=None, exp_count=0, exp_widget=None) self.press_key('x') self.assert_action(exp_action=None, exp_count=0, exp_widget=None) self.assert_current_keychain() self.assert_active_keychains() def test_abort_chain_with_unmapped_key(self): def start_keychain(exp_action, exp_count, exp_widget): self.press_key('1') self.assert_current_keychain('1') self.assert_action(exp_action=exp_action, exp_count=exp_count, exp_widget=exp_widget) self.assert_active_keychains((('1', '2', 'A'), '12A in main'), (('1', '2', 'B'), '12B in main')) self.press_key('2') self.assert_current_keychain('1', '2') self.assert_action(exp_action=exp_action, exp_count=exp_count, exp_widget=exp_widget) self.assert_active_keychains((('1', '2', 'A'), '12A in main'), (('1', '2', 'B'), '12B in main')) start_keychain(exp_action=None, exp_count=0, exp_widget=None) self.press_key('x') self.assert_action(exp_action=None, exp_count=0, exp_widget=None) self.assert_current_keychain() self.assert_active_keychains() def test_builtin_keys_have_precedence_over_keychain_if_no_keychain_started(self): self.km.bind('z', 'impossible action', context='list2') # This should go to an Edit widget self.mainw.focus_position = 1 # Focus list2 self.press_key('z') self.assert_current_keychain() self.assert_action(exp_action=None, exp_count=0, exp_widget=None) self.assert_active_keychains() self.assertEqual(self.listw2.focus.edit_text, 'z') def test_chain_with_builtin_key_if_parent_needs_it(self): self.km.bind('up down', 'jump', context='list1') # Make sure parent uses 'up' key to move focus up self.listw1.focus_position = 1 self.press_key('up') self.assertEqual(self.listw1.focus_position, 0) self.assert_current_keychain() self.assert_action(exp_action=None, exp_count=0, exp_widget=None) self.assert_active_keychains() def test_chain_with_builtin_key_if_parent_doesnt_need_it(self): self.km.bind('up down', 'jump', context='list1') # Make sure parent doesn't need 'up' key self.listw1.focus_position = 0 self.press_key('up') self.assert_current_keychain('up') self.assert_action(exp_action=None, exp_count=0, exp_widget=None) self.assert_active_keychains((('up', 'down'), 'jump')) self.press_key('down') self.assert_current_keychain() self.assert_action(exp_action='jump', exp_count=1, exp_widget=self.listw1) self.assert_active_keychains()
class TestKeyMap_with_keychains(unittest.TestCase): def setUp(self): self.km = KeyMap(callback=self.handle_action) self.widget = self.km.wrap(urwid.Edit)('', 'Original Text') self.km.on_keychain(self.handle_keychain_changed) self._action_counter = 0 def handle_action(self, action, widget): self._action_counter += 1 widget.set_edit_text('%s%d' % (str(action), self._action_counter)) def handle_keychain_changed(self, keymap, context, active_keychains, keys_given): self.active_keychains = set(active_keychains) def assert_status(self, active_keychains=(), keys_given=(), widget_text=None): self.assertEqual(self.active_keychains, set(active_keychains)) self.assertEqual(self.km.current_keychain, tuple(keys_given)) self.assertEqual(self.widget.text, widget_text) def test_any_keychain_started(self): def cb(*args, **kwargs): pass self.km.bind(key='a+b+c', action='foo') self.assertFalse(self.km.any_keychain_started) self.km.evaluate('a', callback=cb) self.assertTrue(self.km.any_keychain_started) self.km.evaluate('b', callback=cb) self.assertTrue(self.km.any_keychain_started) self.km.evaluate('c', callback=cb) self.assertFalse(self.km.any_keychain_started) def test_unbind(self): kc = '1 2 3' for keys in ('1', '1 2', '1 2 3'): self.km.bind(kc, 'foo') self.assertIn(KeyChain('1', '2', '3'), self.km.keys()) self.km.unbind(keys) self.assertNotIn(KeyChain('1', '2', '3'), self.km.keys()) for keys in ('1 3', '1 2 4', '1 2 3 4', '2', '3', '2 1', '3 2 1'): self.km.bind(kc, 'foo') self.assertIn(KeyChain('1', '2', '3'), self.km.keys()) with self.assertRaises(ValueError) as cm: self.km.unbind(keys) self.assertIn('Key not mapped in context', str(cm.exception)) self.assertIn(str(self.km.mkkey(keys)), str(cm.exception)) def test_complete_chain(self): self.km.bind('alt-1 alt-2 alt-3', 'foo') self.widget.keypress((80,), 'alt-1') self.assert_status(keys_given=('alt-1',), widget_text='Original Text', active_keychains=((('alt-1', 'alt-2', 'alt-3'), 'foo'),)) self.widget.keypress((80,), 'alt-2') self.assert_status(keys_given=('alt-1', 'alt-2'), widget_text='Original Text', active_keychains=((('alt-1', 'alt-2', 'alt-3'), 'foo'),)) self.widget.keypress((80,), 'alt-3') self.assert_status(keys_given=(), widget_text='foo1', active_keychains=()) def test_reverse_chain_with_backspace(self): self.km.bind('alt-1 alt-f alt-x', 'foo') self.km.bind('alt-1 alt-b alt-x', 'bar') self.widget.keypress((80,), 'alt-1') self.assert_status(keys_given=('alt-1',), widget_text='Original Text', active_keychains=((('alt-1', 'alt-f', 'alt-x'), 'foo'), (('alt-1', 'alt-b', 'alt-x'), 'bar'))) self.widget.keypress((80,), 'alt-f') self.assert_status(keys_given=('alt-1', 'alt-f'), widget_text='Original Text', active_keychains=((('alt-1', 'alt-f', 'alt-x'), 'foo'),)) self.widget.keypress((80,), 'backspace') self.assert_status(keys_given=('alt-1',), widget_text='Original Text', active_keychains=((('alt-1', 'alt-f', 'alt-x'), 'foo'), (('alt-1', 'alt-b', 'alt-x'), 'bar'))) self.widget.keypress((80,), 'alt-b') self.assert_status(keys_given=('alt-1', 'alt-b'), widget_text='Original Text', active_keychains=((('alt-1', 'alt-b', 'alt-x'), 'bar'),)) self.widget.keypress((80,), 'alt-x') self.assert_status(keys_given=(), widget_text='bar1', active_keychains=()) def test_binding_backspace(self): self.km.bind('alt-1 backspace', 'foo') self.widget.keypress((80,), 'alt-1') self.assert_status(keys_given=('alt-1',), widget_text='Original Text', active_keychains=((('alt-1', 'backspace'), 'foo'),)) self.widget.keypress((80,), 'backspace') self.assert_status(keys_given=(), widget_text='foo1', active_keychains=()) def test_abort_chain_with_unmapped_key(self): self.km.bind('alt-1 alt-2 alt-3', 'foo') self.widget.keypress((80,), 'alt-1') self.assert_status(keys_given=('alt-1',), widget_text='Original Text', active_keychains=((('alt-1', 'alt-2', 'alt-3'), 'foo'),)) self.widget.keypress((80,), 'alt-x') self.assert_status(keys_given=(), widget_text='Original Text', active_keychains=()) def test_abort_chain_with_mapped_key(self): self.km.bind('alt-1 alt-2 alt-3', 'foo') self.km.bind('alt-x', 'bar') self.widget.keypress((80,), 'alt-1') self.assert_status(keys_given=('alt-1',), widget_text='Original Text', active_keychains=((('alt-1', 'alt-2', 'alt-3'), 'foo'),)) # Abort the started chain self.widget.keypress((80,), 'alt-x') self.assert_status(keys_given=(), widget_text='Original Text', active_keychains=()) # Mapped single-key evaluation works again self.widget.keypress((80,), 'alt-x') self.assert_status(keys_given=(), widget_text='bar1', active_keychains=()) def test_abort_chain_with_builtin_key(self): self.km.bind('alt-1 alt-2 alt-3', 'foo') self.widget.keypress((80,), 'alt-1') self.assert_status(keys_given=('alt-1',), widget_text='Original Text', active_keychains=((('alt-1', 'alt-2', 'alt-3'), 'foo'),)) # This would usually append 'x' to the Edit widget text, but we want it # to abort the started chain instead. self.widget.keypress((80,), 'x') self.assert_status(keys_given=(), widget_text='Original Text', active_keychains=()) # Now we can change the text again self.widget.keypress((80,), 'x') self.assert_status(keys_given=(), widget_text='Original Textx', active_keychains=()) def test_competing_chains(self): self.km.bind('alt-1 alt-2 alt-a', 'foo') self.km.bind('alt-1 alt-2 alt-b', 'bar') self.widget.keypress((80,), 'alt-1') self.assert_status(keys_given=('alt-1',), widget_text='Original Text', active_keychains=((('alt-1', 'alt-2', 'alt-a'), 'foo'), (('alt-1', 'alt-2', 'alt-b'), 'bar'))) self.widget.keypress((80,), 'alt-2') self.assert_status(keys_given=('alt-1', 'alt-2'), widget_text='Original Text', active_keychains=((('alt-1', 'alt-2', 'alt-a'), 'foo'), (('alt-1', 'alt-2', 'alt-b'), 'bar'))) self.widget.keypress((80,), 'alt-a') self.assert_status(keys_given=(), widget_text='foo1', active_keychains=()) self.widget.keypress((80,), 'alt-1') self.assert_status(keys_given=('alt-1',), widget_text='foo1', active_keychains=((('alt-1', 'alt-2', 'alt-a'), 'foo'), (('alt-1', 'alt-2', 'alt-b'), 'bar'))) self.widget.keypress((80,), 'alt-2') self.assert_status(keys_given=('alt-1', 'alt-2'), widget_text='foo1', active_keychains=((('alt-1', 'alt-2', 'alt-a'), 'foo'), (('alt-1', 'alt-2', 'alt-b'), 'bar'))) self.widget.keypress((80,), 'alt-b') self.assert_status(keys_given=(), widget_text='bar2', active_keychains=())
class TestKeyMap_with_single_keys(unittest.TestCase): def setUp(self): self.km = KeyMap() def test_unbind(self): self.km.bind(key='a', action='foo') self.assertIn(Key('a'), self.km.keys()) self.km.unbind(key='a') self.assertNotIn(Key('a'), self.km.keys()) self.km.bind(key='b', action='foo') with self.assertRaises(ValueError) as cm: self.km.unbind(key='c') self.assertIn("Key not mapped in context 'default'", str(cm.exception)) self.assertIn(str(Key('c')), str(cm.exception)) self.km.bind(key='d', action='foo') with self.assertRaises(ValueError) as cm: self.km.unbind(key='d', context='bar') self.assertIn("Unknown context: 'bar'", str(cm.exception)) self.km.bind(key='e', action='foo', context='kablam') with self.assertRaises(ValueError) as cm: self.km.unbind(key='e') self.assertIn("Key not mapped in context 'default'", str(cm.exception)) self.assertIn(str(Key('e')), str(cm.exception)) def test_mkkey(self): self.assertEqual(self.km.mkkey(Key('x')), Key('x')) self.assertEqual(self.km.mkkey(KeyChain('1', '2', '3')), KeyChain('1', '2', '3')) self.assertEqual(self.km.mkkey('x'), Key('x')) self.assertEqual(self.km.mkkey('x y z'), KeyChain('x', 'y', 'z')) self.assertEqual(self.km.mkkey('x+y+z'), KeyChain('x', 'y', 'z')) self.assertEqual(self.km.mkkey('x +'), KeyChain('x', '+')) self.assertEqual(self.km.mkkey('+ x'), KeyChain('+', 'x')) self.assertEqual(self.km.mkkey('x y +'), KeyChain('x', 'y', '+')) self.assertEqual(self.km.mkkey('+ y z'), KeyChain('+', 'y', 'z')) self.assertEqual(self.km.mkkey('+ + +'), KeyChain('+', '+', '+')) def test_circular_bind_detection(self): with self.assertRaises(ValueError) as cm: self.km.bind(key='a', action=self.km.mkkey('a')) self.assertIn('Circular', str(cm.exception)) self.assertIn('<a>', str(cm.exception)) def test_key_translation(self): widget = self.km.wrap(urwid.Text)('Test Text') self.km.bind(key='a', action=lambda widget: widget.set_text('Key pressed: a')) self.km.bind(key='b', action=Key('a')) widget.keypress((80,), 'b') self.assertEqual(widget.text, 'Key pressed: a') def test_key_translation_to_builtin_key(self): class SelectableText(urwid.Text): def selectable(self): return True def keypress(self, size, key): return key list_contents = [SelectableText(str(i)) for i in range(1, 10)] listw = self.km.wrap(urwid.ListBox, context='list')(urwid.SimpleFocusListWalker(list_contents)) self.km.bind(key='j', action=Key('down')) self.assertEqual(listw.focus_position, 0) listw.keypress((3, 3), 'j') self.assertEqual(listw.focus_position, 1) def test_action_is_callback(self): self.km.bind(key='a', action=lambda widget: widget.set_text('foo')) widget = self.km.wrap(urwid.Text)('Test Text') widget.keypress((80,), 'a') self.assertEqual(widget.text, 'foo') def test_widget_callback(self): def cb(action, widget): widget.set_text(action) self.km.bind(key='a', action='foo') widget = self.km.wrap(urwid.Text, callback=cb)('Test Text') widget.keypress((80,), 'a') self.assertEqual(widget.text, 'foo') def test_default_callback(self): def cb(action, widget): widget.set_text(action) self.km = KeyMap(callback=cb) self.km.bind(key='a', action='foo') widget = self.km.wrap(urwid.Text)('Test Text') widget.keypress((80,), 'a') self.assertEqual(widget.text, 'foo') def test_widget_callback_overrides_default_callback(self): def default_cb(action, widget): widget.set_text(action) def widget_cb(action, widget): widget.set_text(action.upper()) self.km = KeyMap(callback=default_cb) self.km.bind(key='a', action='foo') widget = self.km.wrap(urwid.Text, callback=widget_cb)('Test Text') widget.keypress((80,), 'a') self.assertEqual(widget.text, 'FOO')
class TestKeyMapped(unittest.TestCase): def setUp(self): self.keymap = KeyMap() def mk_widget(self, subcls, *args, context=None, callback=None, **kwargs): cls = self.keymap.wrap(subcls, context=context, callback=callback) return cls(*args, **kwargs) def assert_lines(self, widget, size, exp_lines, exp_focus_pos=None): canv = widget.render(size, focus=True) content = tuple(get_canvas_text(row) for row in canv.content()) self.assertEqual(content, tuple(exp_lines)) if exp_focus_pos is not None: self.assertEqual(widget.focus_position, exp_focus_pos) def test_hardcoded_keys_keep_working(self): list_contents = [urwid.Text(str(i)) for i in range(1, 10)] widget = self.mk_widget(urwid.ListBox, urwid.SimpleFocusListWalker(list_contents), context='list') size = (3, 3) self.assert_lines(widget, size, exp_lines=('1 ', '2 ', '3 ')) widget.keypress(size, 'down') self.assert_lines(widget, size, exp_lines=('2 ', '3 ', '4 ')) widget.keypress(size, 'page down') self.assert_lines(widget, size, exp_lines=('5 ', '6 ', '7 ')) widget.keypress(size, 'up') self.assert_lines(widget, size, exp_lines=('4 ', '5 ', '6 ')) widget.keypress(size, 'page up') self.assert_lines(widget, size, exp_lines=('1 ', '2 ', '3 ')) def test_non_hardcoded_keys_are_evaluated(self): list_contents = [urwid.Text(str(i)) for i in range(1, 10)] widget = self.mk_widget(urwid.ListBox, urwid.SimpleFocusListWalker(list_contents), context='list') action = FakeAction() self.keymap.bind('a', context='list', action=action) size = (3, 3) self.assert_lines(widget, size, exp_lines=('1 ', '2 ', '3 ')) widget.keypress(size, 'a') self.assert_lines(widget, size, exp_lines=('1 ', '2 ', '3 ')) self.assertEqual(action.callnum, 1) self.assertEqual(action.args, (widget,)) def test_evaluated_keys_are_offered_to_parent_again(self): list_contents = [urwid.Text(str(i)) for i in range(1, 10)] action = FakeAction() widget = self.mk_widget(urwid.ListBox, urwid.SimpleFocusListWalker(list_contents), context='list', callback=action) self.keymap.bind('j', context='list', action=Key('down')) size = (3, 3) self.assert_lines(widget, size, exp_lines=('1 ', '2 ', '3 ')) widget.keypress(size, 'j') self.assertEqual(action.callnum, 0) self.assert_lines(widget, size, exp_lines=('2 ', '3 ', '4 ')) def test_evaluated_key_does_not_replace_original_key(self): # Create a list of widgets that translate 'j' to 'down' in their # keypress() methods. lst_contents = [self.mk_widget(urwid.Text, str(i), context='item') for i in range(1, 10)] self.keymap.bind('j', context='item', action=Key('down')) # Create ListBox with separate key context. If the ListBox gets to # handle 'j', it just checks a mark we can look for. lst_widget = self.mk_widget(urwid.ListBox, urwid.SimpleFocusListWalker(lst_contents), context='list') lst_got_j = FakeAction() self.keymap.bind('j', context='list', action=lst_got_j) # Make sure everything works regularly size = (3, 3) self.assert_lines(lst_widget, size, exp_lines=('1 ', '2 ', '3 '), exp_focus_pos=0) lst_widget.keypress(size, 'down') self.assert_lines(lst_widget, size, exp_lines=('1 ', '2 ', '3 '), exp_focus_pos=1) # Do the actual test: Pressing 'j' should pass 'j' to the focused item, # which evaluates it to 'down'. But the list widget must get 'j'. lst_widget.keypress(size, 'j') self.assert_lines(lst_widget, size, exp_lines=('1 ', '2 ', '3 '), exp_focus_pos=1) self.assertEqual(lst_got_j.callnum, 1)
class TestKeyMap_with_key_chains(unittest.TestCase): def setUp(self): self.km = KeyMap(callback=self.handle_action) self.widget = self.km.wrap(Text)('Test Text') self.widgetA = self.km.wrap(Text, context='A')('Test Text A') self.widgetB = self.km.wrap(Text, context='B')('Test Text B') self._action_counter = 0 def handle_action(self, action, widget): self._action_counter += 1 widget.set_text('%s%d' % (str(action), self._action_counter)) def status(self): return (self.widget.text, self.widgetA.text, self.widgetB.text) def test_correct_chain(self): self.km.bind('1 2 3', 'foo') self.widget.keypress((80,), '1') self.assertEqual(self.widget.text, 'Test Text') self.widget.keypress((80,), '2') self.assertEqual(self.widget.text, 'Test Text') self.widget.keypress((80,), '3') self.assertEqual(self.widget.text, 'foo1') def test_incorrect_chain_then_correct_chain(self): self.km.bind('1 2 3', 'foo') self.widget.keypress((80,), '1') self.assertEqual(self.widget.text, 'Test Text') self.widget.keypress((80,), '2') self.assertEqual(self.widget.text, 'Test Text') self.widget.keypress((80,), 'x') self.assertEqual(self.widget.text, 'Test Text') self.widget.keypress((80,), '3') self.assertEqual(self.widget.text, 'Test Text') for c in ('1', '2', '3'): self.widget.keypress((80,), c) self.assertEqual(self.widget.text, 'foo1') def test_abort_with_bound_key_does_nothing(self): self.km.bind('1 2 3', 'foo') self.km.bind('x', 'bar') for c in ('1', '2', 'x', '3'): self.widget.keypress((80,), c) self.assertEqual(self.widget.text, 'Test Text') self.widget.keypress((80,), 'x') self.assertEqual(self.widget.text, 'bar1') def test_competing_chains_in_default_context(self): self.km.bind('1 2 3', 'foo') self.km.bind('1 2 0', 'bar') for c in ('1', '2', '3'): self.widget.keypress((80,), c) self.assertEqual(self.widget.text, 'foo1') for c in ('1', '2', '0'): self.widget.keypress((80,), c) self.assertEqual(self.widget.text, 'bar2') def test_competing_chains_in_default_context_with_different_lengths(self): self.km.bind('1 2 3', 'foo') self.km.bind('1 2 3 4', 'bar') for c in ('1', '2', '3'): self.widget.keypress((80,), c) self.assertEqual(self.widget.text, 'foo1') for c in ('1', '2', '3'): self.widget.keypress((80,), c) self.assertEqual(self.widget.text, 'foo2') self.widget.keypress((80,), '4') self.assertEqual(self.widget.text, 'foo2') def test_correct_contexts(self): self.km.bind('1 2 3', 'foo', context='A') self.km.bind('a b c', 'bar', context='B') for c in ('1', '2', '3'): self.widgetA.keypress((80,), c) self.assertEqual(self.widgetA.text, 'foo1') for c in ('a', 'b', 'c'): self.widgetB.keypress((80,), c) self.assertEqual(self.widgetB.text, 'bar2') self.assertEqual(self.widgetA.text, 'foo1') def test_wrong_contexts(self): self.km.bind('1 2 3', 'foo', context='A') self.km.bind('a b c', 'bar', context='B') before = self.status() for c in ('a', 'b', 'c'): self.widgetA.keypress((80,), c) self.assertEqual(before, self.status()) for c in ('1', '2', '3'): self.widgetB.keypress((80,), c) self.assertEqual(before, self.status()) def test_starting_one_chain_prevents_other_chains(self): self.km.bind('1 2 3', 'foo', context='A') self.km.bind('a b c', 'bar', context='B') before = self.status() self.widgetA.keypress((80,), '1') # Even though the 'a b c' is sent to the correct widget, the 'a' is # used up to abort the previously started '1 2 3' chain. for c in ('a', 'b', 'c'): self.widgetB.keypress((80,), c) self.assertEqual(before, self.status())