mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge branch 'main' into datatable-events
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import pytest
|
||||
|
||||
from textual.app import App
|
||||
from textual.screen import Screen
|
||||
from textual.widget import Widget
|
||||
@@ -15,6 +17,28 @@ class ChildrenFocusableOnly(Widget, can_focus=False, can_focus_children=True):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def screen() -> Screen:
|
||||
app = App()
|
||||
app._set_active()
|
||||
app.push_screen(Screen())
|
||||
|
||||
screen = app.screen
|
||||
|
||||
# The classes even/odd alternate along the focus chain.
|
||||
# The classes in/out identify nested widgets.
|
||||
screen._add_children(
|
||||
Focusable(id="foo", classes="a"),
|
||||
NonFocusable(id="bar"),
|
||||
Focusable(Focusable(id="Paul", classes="c"), id="container1", classes="b"),
|
||||
NonFocusable(Focusable(id="Jessica", classes="a"), id="container2"),
|
||||
Focusable(id="baz", classes="b"),
|
||||
ChildrenFocusableOnly(Focusable(id="child", classes="c")),
|
||||
)
|
||||
|
||||
return screen
|
||||
|
||||
|
||||
def test_focus_chain():
|
||||
app = App()
|
||||
app._set_active()
|
||||
@@ -38,22 +62,7 @@ def test_focus_chain():
|
||||
assert focus_chain == ["foo", "container1", "Paul", "baz", "child"]
|
||||
|
||||
|
||||
def test_focus_next_and_previous():
|
||||
app = App()
|
||||
app._set_active()
|
||||
app.push_screen(Screen())
|
||||
|
||||
screen = app.screen
|
||||
|
||||
screen._add_children(
|
||||
Focusable(id="foo"),
|
||||
NonFocusable(id="bar"),
|
||||
Focusable(Focusable(id="Paul"), id="container1"),
|
||||
NonFocusable(Focusable(id="Jessica"), id="container2"),
|
||||
Focusable(id="baz"),
|
||||
ChildrenFocusableOnly(Focusable(id="child")),
|
||||
)
|
||||
|
||||
def test_focus_next_and_previous(screen: Screen):
|
||||
assert screen.focus_next().id == "foo"
|
||||
assert screen.focus_next().id == "container1"
|
||||
assert screen.focus_next().id == "Paul"
|
||||
@@ -64,3 +73,131 @@ def test_focus_next_and_previous():
|
||||
assert screen.focus_previous().id == "Paul"
|
||||
assert screen.focus_previous().id == "container1"
|
||||
assert screen.focus_previous().id == "foo"
|
||||
|
||||
|
||||
def test_focus_next_wrap_around(screen: Screen):
|
||||
"""Ensure focusing the next widget wraps around the focus chain."""
|
||||
screen.set_focus(screen.query_one("#child"))
|
||||
assert screen.focused.id == "child"
|
||||
|
||||
assert screen.focus_next().id == "foo"
|
||||
|
||||
|
||||
def test_focus_previous_wrap_around(screen: Screen):
|
||||
"""Ensure focusing the previous widget wraps around the focus chain."""
|
||||
screen.set_focus(screen.query_one("#foo"))
|
||||
assert screen.focused.id == "foo"
|
||||
|
||||
assert screen.focus_previous().id == "child"
|
||||
|
||||
|
||||
def test_wrap_around_selector(screen: Screen):
|
||||
"""Ensure moving focus in both directions wraps around the focus chain."""
|
||||
screen.set_focus(screen.query_one("#foo"))
|
||||
assert screen.focused.id == "foo"
|
||||
|
||||
assert screen.focus_previous("#Paul").id == "Paul"
|
||||
assert screen.focus_next("#foo").id == "foo"
|
||||
|
||||
|
||||
def test_no_focus_empty_selector(screen: Screen):
|
||||
"""Ensure focus is cleared when selector matches nothing."""
|
||||
assert screen.focus_next("#bananas") is None
|
||||
assert screen.focus_previous("#bananas") is None
|
||||
|
||||
screen.set_focus(screen.query_one("#foo"))
|
||||
assert screen.focused is not None
|
||||
assert screen.focus_next("bananas") is None
|
||||
assert screen.focused is None
|
||||
|
||||
screen.set_focus(screen.query_one("#foo"))
|
||||
assert screen.focused is not None
|
||||
assert screen.focus_previous("bananas") is None
|
||||
assert screen.focused is None
|
||||
|
||||
|
||||
def test_focus_next_and_previous_with_type_selector(screen: Screen):
|
||||
"""Move focus with a selector that matches the currently focused node."""
|
||||
screen.set_focus(screen.query_one("#Paul"))
|
||||
assert screen.focused.id == "Paul"
|
||||
|
||||
assert screen.focus_next(Focusable).id == "baz"
|
||||
assert screen.focus_next(Focusable).id == "child"
|
||||
|
||||
assert screen.focus_previous(Focusable).id == "baz"
|
||||
assert screen.focus_previous(Focusable).id == "Paul"
|
||||
assert screen.focus_previous(Focusable).id == "container1"
|
||||
assert screen.focus_previous(Focusable).id == "foo"
|
||||
|
||||
|
||||
def test_focus_next_and_previous_with_str_selector(screen: Screen):
|
||||
"""Move focus with a selector that matches the currently focused node."""
|
||||
screen.set_focus(screen.query_one("#foo"))
|
||||
assert screen.focused.id == "foo"
|
||||
|
||||
assert screen.focus_next(".a").id == "foo"
|
||||
assert screen.focus_next(".c").id == "Paul"
|
||||
assert screen.focus_next(".c").id == "child"
|
||||
|
||||
assert screen.focus_previous(".c").id == "Paul"
|
||||
assert screen.focus_previous(".a").id == "foo"
|
||||
|
||||
|
||||
def test_focus_next_and_previous_with_type_selector_without_self():
|
||||
"""Test moving the focus with a selector that does not match the currently focused node."""
|
||||
app = App()
|
||||
app._set_active()
|
||||
app.push_screen(Screen())
|
||||
|
||||
screen = app.screen
|
||||
|
||||
from textual.containers import Horizontal, Vertical
|
||||
from textual.widgets import Button, Checkbox, Input
|
||||
|
||||
screen._add_children(
|
||||
Vertical(
|
||||
Horizontal(
|
||||
Input(id="w3"),
|
||||
Checkbox(id="w4"),
|
||||
Input(id="w5"),
|
||||
Button(id="w6"),
|
||||
Checkbox(id="w7"),
|
||||
id="w2",
|
||||
),
|
||||
Horizontal(
|
||||
Button(id="w9"),
|
||||
Checkbox(id="w10"),
|
||||
Button(id="w11"),
|
||||
Input(id="w12"),
|
||||
Input(id="w13"),
|
||||
id="w8",
|
||||
),
|
||||
id="w1",
|
||||
)
|
||||
)
|
||||
|
||||
screen.set_focus(screen.query_one("#w3"))
|
||||
assert screen.focused.id == "w3"
|
||||
|
||||
assert screen.focus_next(Button).id == "w6"
|
||||
assert screen.focus_next(Checkbox).id == "w7"
|
||||
assert screen.focus_next(Input).id == "w12"
|
||||
|
||||
assert screen.focus_previous(Button).id == "w11"
|
||||
assert screen.focus_previous(Checkbox).id == "w10"
|
||||
assert screen.focus_previous(Button).id == "w9"
|
||||
assert screen.focus_previous(Input).id == "w5"
|
||||
|
||||
|
||||
def test_focus_next_and_previous_with_str_selector_without_self(screen: Screen):
|
||||
"""Test moving the focus with a selector that does not match the currently focused node."""
|
||||
screen.set_focus(screen.query_one("#foo"))
|
||||
assert screen.focused.id == "foo"
|
||||
|
||||
assert screen.focus_next(".c").id == "Paul"
|
||||
assert screen.focus_next(".b").id == "baz"
|
||||
assert screen.focus_next(".c").id == "child"
|
||||
|
||||
assert screen.focus_previous(".a").id == "foo"
|
||||
assert screen.focus_previous(".a").id == "foo"
|
||||
assert screen.focus_previous(".b").id == "baz"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
|
||||
from textual.app import App
|
||||
from textual.keys import _character_to_key
|
||||
from textual.keys import _character_to_key, _get_key_display
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -48,3 +48,7 @@ async def test_character_bindings():
|
||||
await pilot.press("x")
|
||||
await pilot.pause()
|
||||
assert counter == 3
|
||||
|
||||
|
||||
def test_get_key_display():
|
||||
assert _get_key_display("minus") == "-"
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import pytest
|
||||
from rich.style import Style
|
||||
|
||||
from textual.color import Color
|
||||
from textual.css.errors import StyleValueError
|
||||
from textual.css.styles import Styles
|
||||
|
||||
|
||||
@@ -7,3 +11,22 @@ def test_box_normalization():
|
||||
styles = Styles()
|
||||
styles.border_left = ("none", "red")
|
||||
assert styles.border_left == ("", Color.parse("red"))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("style_attr", ["text_style", "link_style"])
|
||||
def test_text_style_none_with_others(style_attr):
|
||||
"""Style "none" mixed with others should give custom Textual exception."""
|
||||
styles = Styles()
|
||||
|
||||
with pytest.raises(StyleValueError):
|
||||
setattr(styles, style_attr, "bold none underline italic")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("style_attr", ["text_style", "link_style"])
|
||||
def test_text_style_set_to_none(style_attr):
|
||||
"""Setting text style to "none" should clear the styles."""
|
||||
styles = Styles()
|
||||
setattr(styles, style_attr, "bold underline italic")
|
||||
assert getattr(styles, style_attr) != Style.null()
|
||||
setattr(styles, style_attr, "none")
|
||||
assert getattr(styles, style_attr) == Style.null()
|
||||
|
||||
Reference in New Issue
Block a user