mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
@@ -191,10 +191,9 @@ def pytest_terminal_summary(
|
||||
console = Console(legacy_windows=False, force_terminal=True)
|
||||
if diffs:
|
||||
snapshot_report_location = config._textual_snapshot_html_report
|
||||
console.rule("[b red]Textual Snapshot Report", style="red")
|
||||
console.print("[b red]Textual Snapshot Report", style="red")
|
||||
console.print(
|
||||
f"\n[black on red]{len(diffs)} mismatched snapshots[/]\n"
|
||||
f"\n[b]View the [link=file://{snapshot_report_location}]failure report[/].\n"
|
||||
)
|
||||
console.print(f"[dim]{snapshot_report_location}\n")
|
||||
console.rule(style="red")
|
||||
|
||||
@@ -2,7 +2,8 @@ from string import ascii_lowercase
|
||||
|
||||
import pytest
|
||||
|
||||
from textual.binding import Bindings, Binding, BindingError, NoBinding
|
||||
from textual.app import App
|
||||
from textual.binding import Bindings, Binding, BindingError, NoBinding, InvalidBinding
|
||||
|
||||
BINDING1 = Binding("a,b", action="action1", description="description1")
|
||||
BINDING2 = Binding("c", action="action2", description="description2")
|
||||
@@ -13,45 +14,78 @@ BINDING3 = Binding(" d , e ", action="action3", description="description3")
|
||||
def bindings():
|
||||
yield Bindings([BINDING1, BINDING2])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def more_bindings():
|
||||
yield Bindings([BINDING1, BINDING2, BINDING3])
|
||||
|
||||
|
||||
def test_bindings_get_key(bindings):
|
||||
assert bindings.get_key("b") == Binding("b", action="action1", description="description1")
|
||||
assert bindings.get_key("b") == Binding(
|
||||
"b", action="action1", description="description1"
|
||||
)
|
||||
assert bindings.get_key("c") == BINDING2
|
||||
with pytest.raises(NoBinding):
|
||||
bindings.get_key("control+meta+alt+shift+super+hyper+t")
|
||||
|
||||
|
||||
def test_bindings_get_key_spaced_list(more_bindings):
|
||||
assert more_bindings.get_key("d").action == more_bindings.get_key("e").action
|
||||
|
||||
|
||||
def test_bindings_merge_simple(bindings):
|
||||
left = Bindings([BINDING1])
|
||||
right = Bindings([BINDING2])
|
||||
assert Bindings.merge([left, right]).keys == bindings.keys
|
||||
|
||||
|
||||
def test_bindings_merge_overlap():
|
||||
left = Bindings([BINDING1])
|
||||
another_binding = Binding("a", action="another_action", description="another_description")
|
||||
another_binding = Binding(
|
||||
"a", action="another_action", description="another_description"
|
||||
)
|
||||
assert Bindings.merge([left, Bindings([another_binding])]).keys == {
|
||||
"a": another_binding,
|
||||
"b": Binding("b", action="action1", description="description1"),
|
||||
}
|
||||
|
||||
|
||||
def test_bad_binding_tuple():
|
||||
with pytest.raises(BindingError):
|
||||
_ = Bindings((("a", "action"),))
|
||||
with pytest.raises(BindingError):
|
||||
_ = Bindings((("a", "action", "description","too much"),))
|
||||
_ = Bindings((("a", "action", "description", "too much"),))
|
||||
|
||||
|
||||
def test_binding_from_tuples():
|
||||
assert Bindings((( BINDING2.key, BINDING2.action, BINDING2.description),)).get_key("c") == BINDING2
|
||||
assert (
|
||||
Bindings(((BINDING2.key, BINDING2.action, BINDING2.description),)).get_key("c")
|
||||
== BINDING2
|
||||
)
|
||||
|
||||
|
||||
def test_shown():
|
||||
bindings = Bindings([
|
||||
Binding(
|
||||
key, action=f"action_{key}", description=f"Emits {key}",show=bool(ord(key)%2)
|
||||
) for key in ascii_lowercase
|
||||
])
|
||||
assert len(bindings.shown_keys)==(len(ascii_lowercase)/2)
|
||||
bindings = Bindings(
|
||||
[
|
||||
Binding(
|
||||
key,
|
||||
action=f"action_{key}",
|
||||
description=f"Emits {key}",
|
||||
show=bool(ord(key) % 2),
|
||||
)
|
||||
for key in ascii_lowercase
|
||||
]
|
||||
)
|
||||
assert len(bindings.shown_keys) == (len(ascii_lowercase) / 2)
|
||||
|
||||
|
||||
def test_invalid_binding():
|
||||
with pytest.raises(InvalidBinding):
|
||||
|
||||
class BrokenApp(App):
|
||||
BINDINGS = [(",,,", "foo", "Broken")]
|
||||
|
||||
with pytest.raises(InvalidBinding):
|
||||
|
||||
class BrokenApp(App):
|
||||
BINDINGS = [(", ,", "foo", "Broken")]
|
||||
|
||||
@@ -57,7 +57,7 @@ async def test_just_app_no_bindings() -> None:
|
||||
class AlphaBinding(App[None]):
|
||||
"""An app with a simple alpha key binding."""
|
||||
|
||||
BINDINGS = [Binding("a", "a", "a")]
|
||||
BINDINGS = [Binding("a", "a", "a", priority=True)]
|
||||
|
||||
|
||||
async def test_just_app_alpha_binding() -> None:
|
||||
@@ -81,8 +81,7 @@ async def test_just_app_alpha_binding() -> None:
|
||||
class LowAlphaBinding(App[None]):
|
||||
"""An app with a simple low-priority alpha key binding."""
|
||||
|
||||
PRIORITY_BINDINGS = False
|
||||
BINDINGS = [Binding("a", "a", "a")]
|
||||
BINDINGS = [Binding("a", "a", "a", priority=False)]
|
||||
|
||||
|
||||
async def test_just_app_low_priority_alpha_binding() -> None:
|
||||
@@ -106,7 +105,7 @@ async def test_just_app_low_priority_alpha_binding() -> None:
|
||||
class ScreenWithBindings(Screen):
|
||||
"""A screen with a simple alpha key binding."""
|
||||
|
||||
BINDINGS = [Binding("a", "a", "a")]
|
||||
BINDINGS = [Binding("a", "a", "a", priority=True)]
|
||||
|
||||
|
||||
class AppWithScreenThatHasABinding(App[None]):
|
||||
@@ -144,8 +143,7 @@ async def test_app_screen_with_bindings() -> None:
|
||||
class ScreenWithLowBindings(Screen):
|
||||
"""A screen with a simple low-priority alpha key binding."""
|
||||
|
||||
PRIORITY_BINDINGS = False
|
||||
BINDINGS = [Binding("a", "a", "a")]
|
||||
BINDINGS = [Binding("a", "a", "a", priority=False)]
|
||||
|
||||
|
||||
class AppWithScreenThatHasALowBinding(App[None]):
|
||||
|
||||
50
tests/test_keys.py
Normal file
50
tests/test_keys.py
Normal file
@@ -0,0 +1,50 @@
|
||||
import pytest
|
||||
|
||||
from textual.app import App
|
||||
from textual.keys import _character_to_key
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"character,key",
|
||||
[
|
||||
("1", "1"),
|
||||
("2", "2"),
|
||||
("a", "a"),
|
||||
("z", "z"),
|
||||
("_", "underscore"),
|
||||
(" ", "space"),
|
||||
("~", "tilde"),
|
||||
("?", "question_mark"),
|
||||
("£", "pound_sign"),
|
||||
(",", "comma"),
|
||||
],
|
||||
)
|
||||
def test_character_to_key(character: str, key: str) -> None:
|
||||
assert _character_to_key(character) == key
|
||||
|
||||
|
||||
async def test_character_bindings():
|
||||
"""Test you can bind to a character as well as a longer key name."""
|
||||
counter = 0
|
||||
|
||||
class BindApp(App):
|
||||
BINDINGS = [(".,~,space", "increment", "foo")]
|
||||
|
||||
def action_increment(self) -> None:
|
||||
nonlocal counter
|
||||
counter += 1
|
||||
|
||||
app = BindApp()
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.press(".")
|
||||
await pilot.pause()
|
||||
assert counter == 1
|
||||
await pilot.press("~")
|
||||
await pilot.pause()
|
||||
assert counter == 2
|
||||
await pilot.press(" ")
|
||||
await pilot.pause()
|
||||
assert counter == 3
|
||||
await pilot.press("x")
|
||||
await pilot.pause()
|
||||
assert counter == 3
|
||||
@@ -17,7 +17,7 @@ class ValidWidget(Widget):
|
||||
|
||||
async def test_dispatch_key_valid_key():
|
||||
widget = ValidWidget()
|
||||
result = await widget.dispatch_key(Key(widget, key="x", char="x"))
|
||||
result = await widget.dispatch_key(Key(widget, key="x", character="x"))
|
||||
assert result is True
|
||||
assert widget.called_by == widget.key_x
|
||||
|
||||
@@ -26,7 +26,7 @@ async def test_dispatch_key_valid_key_alias():
|
||||
"""When you press tab or ctrl+i, it comes through as a tab key event, but handlers for
|
||||
tab and ctrl+i are both considered valid."""
|
||||
widget = ValidWidget()
|
||||
result = await widget.dispatch_key(Key(widget, key="tab", char="\t"))
|
||||
result = await widget.dispatch_key(Key(widget, key="tab", character="\t"))
|
||||
assert result is True
|
||||
assert widget.called_by == widget.key_ctrl_i
|
||||
|
||||
@@ -52,5 +52,5 @@ async def test_dispatch_key_raises_when_conflicting_handler_aliases():
|
||||
In the terminal, they're the same thing, so we fail fast via exception here."""
|
||||
widget = DuplicateHandlersWidget()
|
||||
with pytest.raises(DuplicateKeyHandlers):
|
||||
await widget.dispatch_key(Key(widget, key="tab", char="\t"))
|
||||
await widget.dispatch_key(Key(widget, key="tab", character="\t"))
|
||||
assert widget.called_by == widget.key_tab
|
||||
|
||||
@@ -106,7 +106,7 @@ def test_cant_match_escape_sequence_too_long(parser):
|
||||
# The rest of the characters correspond to the expected key presses
|
||||
events = events[1:]
|
||||
for index, character in enumerate(sequence[1:]):
|
||||
assert events[index].char == character
|
||||
assert events[index].character == character
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
Reference in New Issue
Block a user