mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Add tests to screen modes.
This commit is contained in:
276
tests/test_screen_modes.py
Normal file
276
tests/test_screen_modes.py
Normal file
@@ -0,0 +1,276 @@
|
||||
from functools import partial
|
||||
from itertools import cycle
|
||||
from typing import Type
|
||||
|
||||
import pytest
|
||||
|
||||
from textual.app import (
|
||||
ActiveModeError,
|
||||
App,
|
||||
ComposeResult,
|
||||
InvalidModeError,
|
||||
UnknownModeError,
|
||||
)
|
||||
from textual.screen import ModalScreen, Screen
|
||||
from textual.widgets import Footer, Header, Label, TextLog
|
||||
|
||||
FRUITS = cycle("apple mango strawberry banana peach pear melon watermelon".split())
|
||||
|
||||
|
||||
class ScreenBindingsMixin(Screen[None]):
|
||||
BINDINGS = [
|
||||
("1", "one", "Mode 1"),
|
||||
("2", "two", "Mode 2"),
|
||||
("p", "push", "Push rnd scrn"),
|
||||
("o", "pop_screen", "Pop"),
|
||||
("r", "remove", "Remove mode 1"),
|
||||
]
|
||||
|
||||
def action_one(self) -> None:
|
||||
self.app.switch_mode("one")
|
||||
|
||||
def action_two(self) -> None:
|
||||
self.app.switch_mode("two")
|
||||
|
||||
def action_fruits(self) -> None:
|
||||
self.app.switch_mode("fruits")
|
||||
|
||||
def action_push(self) -> None:
|
||||
self.app.push_screen(FruitModal())
|
||||
|
||||
|
||||
class BaseScreen(ScreenBindingsMixin):
|
||||
def __init__(self, label):
|
||||
super().__init__()
|
||||
self.label = label
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Header()
|
||||
yield Label(self.label)
|
||||
yield Footer()
|
||||
|
||||
def action_remove(self) -> None:
|
||||
self.app.remove_mode("one")
|
||||
|
||||
|
||||
class FruitModal(ModalScreen[str], ScreenBindingsMixin):
|
||||
BINDINGS = [("d", "dismiss_fruit", "Dismiss")]
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Label(next(FRUITS))
|
||||
|
||||
|
||||
class FruitsScreen(ScreenBindingsMixin):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield TextLog()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ModesApp():
|
||||
class ModesApp(App[None]):
|
||||
MODES = {
|
||||
"one": lambda: BaseScreen("one"),
|
||||
"two": "screen_two",
|
||||
}
|
||||
|
||||
SCREENS = {
|
||||
"screen_two": lambda: BaseScreen("two"),
|
||||
}
|
||||
|
||||
def on_mount(self):
|
||||
self.switch_mode("one")
|
||||
|
||||
return ModesApp
|
||||
|
||||
|
||||
async def test_mode_setup(ModesApp: Type[App]):
|
||||
app = ModesApp()
|
||||
async with app.run_test():
|
||||
assert isinstance(app.screen, BaseScreen)
|
||||
assert str(app.screen.query_one(Label).renderable) == "one"
|
||||
|
||||
|
||||
async def test_switch_mode(ModesApp: Type[App]):
|
||||
app = ModesApp()
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.press("2")
|
||||
assert str(app.screen.query_one(Label).renderable) == "two"
|
||||
await pilot.press("1")
|
||||
assert str(app.screen.query_one(Label).renderable) == "one"
|
||||
|
||||
|
||||
async def test_switch_same_mode(ModesApp: Type[App]):
|
||||
app = ModesApp()
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.press("1")
|
||||
assert str(app.screen.query_one(Label).renderable) == "one"
|
||||
await pilot.press("1")
|
||||
assert str(app.screen.query_one(Label).renderable) == "one"
|
||||
|
||||
|
||||
async def test_switch_unknown_mode(ModesApp: Type[App]):
|
||||
app = ModesApp()
|
||||
async with app.run_test():
|
||||
with pytest.raises(UnknownModeError):
|
||||
app.switch_mode("unknown mode here")
|
||||
|
||||
|
||||
async def test_remove_mode(ModesApp: Type[App]):
|
||||
app = ModesApp()
|
||||
async with app.run_test() as pilot:
|
||||
app.switch_mode("two")
|
||||
await pilot.pause()
|
||||
assert str(app.screen.query_one(Label).renderable) == "two"
|
||||
app.remove_mode("one")
|
||||
assert "one" not in app.MODES
|
||||
|
||||
|
||||
async def test_remove_active_mode(ModesApp: Type[App]):
|
||||
app = ModesApp()
|
||||
async with app.run_test():
|
||||
with pytest.raises(ActiveModeError):
|
||||
app.remove_mode("one")
|
||||
|
||||
|
||||
async def test_add_mode(ModesApp: Type[App]):
|
||||
app = ModesApp()
|
||||
async with app.run_test() as pilot:
|
||||
app.add_mode("three", BaseScreen("three"))
|
||||
app.switch_mode("three")
|
||||
await pilot.pause()
|
||||
assert str(app.screen.query_one(Label).renderable) == "three"
|
||||
|
||||
|
||||
async def test_add_mode_duplicated(ModesApp: Type[App]):
|
||||
app = ModesApp()
|
||||
async with app.run_test():
|
||||
with pytest.raises(InvalidModeError):
|
||||
app.add_mode("one", BaseScreen("one"))
|
||||
|
||||
|
||||
async def test_screen_stack_preserved(ModesApp: Type[App]):
|
||||
fruits = []
|
||||
N = 5
|
||||
|
||||
app = ModesApp()
|
||||
async with app.run_test() as pilot:
|
||||
# Build the stack up.
|
||||
for _ in range(N):
|
||||
await pilot.press("p")
|
||||
fruits.append(str(app.query_one(Label).renderable))
|
||||
|
||||
assert len(app.screen_stack) == N + 1
|
||||
|
||||
# Switch out and back.
|
||||
await pilot.press("2")
|
||||
assert len(app.screen_stack) == 1
|
||||
await pilot.press("1")
|
||||
|
||||
# Check the stack.
|
||||
assert len(app.screen_stack) == N + 1
|
||||
for _ in range(N):
|
||||
assert str(app.query_one(Label).renderable) == fruits.pop()
|
||||
await pilot.press("o")
|
||||
|
||||
|
||||
async def test_inactive_stack_is_alive():
|
||||
class FastCounter(Screen[None]):
|
||||
def compose(self) -> ComposeResult:
|
||||
self.lbl = Label("0")
|
||||
yield self.lbl
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self.set_interval(0.01, self.increment)
|
||||
|
||||
def increment(self) -> None:
|
||||
self.lbl.update(str(int(str(self.lbl.renderable)) + 1))
|
||||
|
||||
def key_s(self):
|
||||
self.app.switch_mode("smile")
|
||||
|
||||
class SmileScreen(Screen[None]):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Label(":)")
|
||||
|
||||
def key_s(self):
|
||||
self.app.switch_mode("fast")
|
||||
|
||||
class ModesApp(App[None]):
|
||||
MODES = {
|
||||
"fast": FastCounter,
|
||||
"smile": SmileScreen,
|
||||
}
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self.switch_mode("fast")
|
||||
|
||||
app = ModesApp()
|
||||
async with app.run_test() as pilot:
|
||||
current = int(str(app.query_one(Label).renderable))
|
||||
await pilot.press("s")
|
||||
assert str(app.query_one(Label).renderable) == ":)"
|
||||
await pilot.press("s")
|
||||
assert int(str(app.query_one(Label).renderable)) > current
|
||||
|
||||
|
||||
async def test_multiple_mode_callbacks():
|
||||
written = []
|
||||
|
||||
class LogScreen(Screen[None]):
|
||||
def __init__(self, value):
|
||||
super().__init__()
|
||||
self.value = value
|
||||
|
||||
def key_p(self) -> None:
|
||||
self.app.push_screen(ResultScreen(self.value), written.append)
|
||||
|
||||
class ResultScreen(Screen[str]):
|
||||
def __init__(self, value):
|
||||
super().__init__()
|
||||
self.value = value
|
||||
|
||||
def key_p(self) -> None:
|
||||
self.dismiss(self.value)
|
||||
|
||||
def key_f(self) -> None:
|
||||
self.app.switch_mode("first")
|
||||
|
||||
def key_o(self) -> None:
|
||||
self.app.switch_mode("other")
|
||||
|
||||
class ModesApp(App[None]):
|
||||
MODES = {
|
||||
"first": lambda: LogScreen("first"),
|
||||
"other": lambda: LogScreen("other"),
|
||||
}
|
||||
|
||||
def on_mount(self) -> None:
|
||||
self.switch_mode("first")
|
||||
|
||||
def key_f(self) -> None:
|
||||
self.switch_mode("first")
|
||||
|
||||
def key_o(self) -> None:
|
||||
self.switch_mode("other")
|
||||
|
||||
app = ModesApp()
|
||||
async with app.run_test() as pilot:
|
||||
# Push and dismiss ResultScreen("first")
|
||||
await pilot.press("p")
|
||||
await pilot.press("p")
|
||||
assert written == ["first"]
|
||||
|
||||
# Push ResultScreen("first")
|
||||
await pilot.press("p")
|
||||
# Switch to LogScreen("other")
|
||||
await pilot.press("o")
|
||||
# Push and dismiss ResultScreen("other")
|
||||
await pilot.press("p")
|
||||
await pilot.press("p")
|
||||
assert written == ["first", "other"]
|
||||
|
||||
# Go back to ResultScreen("first")
|
||||
await pilot.press("f")
|
||||
# Dismiss ResultScreen("first")
|
||||
await pilot.press("p")
|
||||
assert written == ["first", "other", "first"]
|
||||
Reference in New Issue
Block a user