Skip to content

test(focus): update tests with app.run_test #5687

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 107 additions & 93 deletions tests/test_focus.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import pytest

from textual.app import App, ComposeResult
from textual.containers import Container, ScrollableContainer
from textual.screen import Screen
from textual.widget import Widget
from textual.widgets import Button, Label

Expand All @@ -19,34 +16,23 @@ class ChildrenFocusableOnly(Widget, can_focus=False, can_focus_children=True):
pass


@pytest.fixture
def screen() -> Screen:
app = App()

with app._context():
app.push_screen(Screen())

screen = app.screen
class FocusTestApp(App):
AUTO_FOCUS = None

def compose(self) -> ComposeResult:
# 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")),
)
yield Focusable(id="foo", classes="a")
yield NonFocusable(id="bar")
yield Focusable(Focusable(id="Paul", classes="c"), id="container1", classes="b")
yield NonFocusable(Focusable(id="Jessica", classes="a"), id="container2")
yield Focusable(id="baz", classes="b")
yield ChildrenFocusableOnly(Focusable(id="child", classes="c"))

return screen


def test_focus_chain():
async def test_focus_chain():
app = App()
with app._context():
app.push_screen(Screen())

async with app.run_test():
screen = app.screen

# Check empty focus chain
Expand All @@ -65,7 +51,7 @@ def test_focus_chain():
assert focus_chain == ["foo", "container1", "Paul", "baz", "child"]


def test_allow_focus():
async def test_allow_focus():
"""Test allow_focus and allow_focus_children are called and the result used."""
focusable_allow_focus_called = False
non_focusable_allow_focus_called = False
Expand All @@ -92,9 +78,7 @@ def allow_focus_children(self) -> bool:

app = App()

with app._context():
app.push_screen(Screen())

async with app.run_test():
app.screen._add_children(
Focusable(id="foo"),
NonFocusable(id="bar"),
Expand All @@ -106,93 +90,119 @@ def allow_focus_children(self) -> bool:
assert non_focusable_allow_focus_called


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"
assert screen.focus_next().id == "baz"
assert screen.focus_next().id == "child"
async def test_focus_next_and_previous():
app = FocusTestApp()
async with app.run_test():
screen = app.screen

assert screen.focus_next().id == "foo"
assert screen.focus_next().id == "container1"
assert screen.focus_next().id == "Paul"
assert screen.focus_next().id == "baz"
assert screen.focus_next().id == "child"

assert screen.focus_previous().id == "baz"
assert screen.focus_previous().id == "Paul"
assert screen.focus_previous().id == "container1"
assert screen.focus_previous().id == "foo"
assert screen.focus_previous().id == "baz"
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):
async def test_focus_next_wrap_around():
"""Ensure focusing the next widget wraps around the focus chain."""
screen.set_focus(screen.query_one("#child"))
assert screen.focused.id == "child"
app = FocusTestApp()
async with app.run_test():
screen = app.screen

assert screen.focus_next().id == "foo"
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):

async def test_focus_previous_wrap_around():
"""Ensure focusing the previous widget wraps around the focus chain."""
screen.set_focus(screen.query_one("#foo"))
assert screen.focused.id == "foo"
app = FocusTestApp()
async with app.run_test():
screen = app.screen

screen.set_focus(screen.query_one("#foo"))
assert screen.focused.id == "foo"

assert screen.focus_previous().id == "child"
assert screen.focus_previous().id == "child"


def test_wrap_around_selector(screen: Screen):
async def test_wrap_around_selector():
"""Ensure moving focus in both directions wraps around the focus chain."""
screen.set_focus(screen.query_one("#foo"))
assert screen.focused.id == "foo"
app = FocusTestApp()
async with app.run_test():
screen = app.screen

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"
assert screen.focus_previous("#Paul").id == "Paul"
assert screen.focus_next("#foo").id == "foo"


def test_no_focus_empty_selector(screen: Screen):
async def test_no_focus_empty_selector():
"""Ensure focus is cleared when selector matches nothing."""
assert screen.focus_next("#bananas") is None
assert screen.focus_previous("#bananas") is None
app = FocusTestApp()
async with app.run_test():
screen = app.screen

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_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
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):
async def test_focus_next_and_previous_with_type_selector():
"""Move focus with a selector that matches the currently focused node."""
screen.set_focus(screen.query_one("#Paul"))
assert screen.focused.id == "Paul"
app = FocusTestApp()
async with app.run_test():
screen = app.screen

assert screen.focus_next(Focusable).id == "baz"
assert screen.focus_next(Focusable).id == "child"
screen.set_focus(screen.query_one("#Paul"))
assert screen.focused.id == "Paul"

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"
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):

async def test_focus_next_and_previous_with_str_selector():
"""Move focus with a selector that matches the currently focused node."""
screen.set_focus(screen.query_one("#foo"))
assert screen.focused.id == "foo"
app = FocusTestApp()
async with app.run_test():
screen = app.screen

assert screen.focus_next(".a").id == "foo"
assert screen.focus_next(".c").id == "Paul"
assert screen.focus_next(".c").id == "child"
screen.set_focus(screen.query_one("#foo"))
assert screen.focused.id == "foo"

assert screen.focus_previous(".c").id == "Paul"
assert screen.focus_previous(".a").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():

async 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()
with app._context():
app.push_screen(Screen())

async with app.run_test():
screen = app.screen

from textual.containers import Horizontal, VerticalScroll
Expand Down Expand Up @@ -233,18 +243,22 @@ def test_focus_next_and_previous_with_type_selector_without_self():
assert screen.focus_previous(Input).id == "w5"


def test_focus_next_and_previous_with_str_selector_without_self(screen: Screen):
async def test_focus_next_and_previous_with_str_selector_without_self():
"""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"
app = FocusTestApp()
async with app.run_test():
screen = app.screen

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_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"
assert screen.focus_previous(".a").id == "foo"
assert screen.focus_previous(".a").id == "foo"
assert screen.focus_previous(".b").id == "baz"


async def test_focus_does_not_move_to_invisible_widgets():
Expand Down
Loading