mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
228 lines
8.2 KiB
Python
228 lines
8.2 KiB
Python
from textual.app import App, ComposeResult
|
|
from textual.containers import Container, Vertical
|
|
from textual.widgets import Button, Label, Static
|
|
|
|
|
|
async def test_remove_single_widget():
|
|
"""It should be possible to the only widget on a screen."""
|
|
async with App().run_test() as pilot:
|
|
widget = Static()
|
|
assert not widget.is_attached
|
|
await pilot.app.mount(widget)
|
|
assert widget.is_attached
|
|
assert len(pilot.app.screen._nodes) == 1
|
|
await pilot.app.query_one(Static).remove()
|
|
assert not widget.is_attached
|
|
assert len(pilot.app.screen._nodes) == 0
|
|
|
|
|
|
async def test_many_remove_all_widgets():
|
|
"""It should be possible to remove all widgets on a multi-widget screen."""
|
|
async with App().run_test() as pilot:
|
|
await pilot.app.mount(*[Static() for _ in range(10)])
|
|
assert len(pilot.app.screen._nodes) == 10
|
|
await pilot.app.query(Static).remove()
|
|
assert len(pilot.app.screen._nodes) == 0
|
|
|
|
|
|
async def test_many_remove_some_widgets():
|
|
"""It should be possible to remove some widgets on a multi-widget screen."""
|
|
async with App().run_test() as pilot:
|
|
await pilot.app.mount(*[Static(classes=f"is-{n % 2}") for n in range(10)])
|
|
assert len(pilot.app.screen._nodes) == 10
|
|
await pilot.app.query(".is-0").remove()
|
|
assert len(pilot.app.screen._nodes) == 5
|
|
|
|
|
|
async def test_remove_branch():
|
|
"""It should be possible to remove a whole branch in the DOM."""
|
|
async with App().run_test() as pilot:
|
|
await pilot.app.mount(
|
|
Container(Container(Container(Container(Container(Static()))))),
|
|
Static(),
|
|
Container(Container(Container(Container(Container(Static()))))),
|
|
)
|
|
assert len(pilot.app.screen.walk_children(with_self=False)) == 13
|
|
await pilot.app.screen._nodes[0].remove()
|
|
assert len(pilot.app.screen.walk_children(with_self=False)) == 7
|
|
|
|
|
|
async def test_remove_overlap():
|
|
"""It should be possible to remove an overlapping collection of widgets."""
|
|
async with App().run_test() as pilot:
|
|
await pilot.app.mount(
|
|
Container(Container(Container(Container(Container(Static()))))),
|
|
Static(),
|
|
Container(Container(Container(Container(Container(Static()))))),
|
|
)
|
|
assert len(pilot.app.screen.walk_children(with_self=False)) == 13
|
|
await pilot.app.query(Container).remove()
|
|
assert len(pilot.app.screen.walk_children(with_self=False)) == 1
|
|
|
|
|
|
async def test_remove_move_focus():
|
|
"""Removing a focused widget should settle focus elsewhere."""
|
|
async with App().run_test() as pilot:
|
|
buttons = [Button(str(n)) for n in range(10)]
|
|
await pilot.app.mount(Container(*buttons[:5]), Container(*buttons[5:]))
|
|
assert len(pilot.app.screen._nodes) == 2
|
|
assert len(pilot.app.screen.walk_children(with_self=False)) == 12
|
|
assert pilot.app.focused is None
|
|
await pilot.press("tab")
|
|
assert pilot.app.focused is not None
|
|
assert pilot.app.focused == buttons[0]
|
|
await pilot.app.screen._nodes[0].remove()
|
|
assert len(pilot.app.screen._nodes) == 1
|
|
assert len(pilot.app.screen.walk_children(with_self=False)) == 6
|
|
assert pilot.app.focused is not None
|
|
assert pilot.app.focused == buttons[9]
|
|
|
|
|
|
async def test_widget_remove_order() -> None:
|
|
"""A Widget.remove of a top-level widget should cause bottom-first removal."""
|
|
|
|
removals: list[str] = []
|
|
|
|
class Removable(Container):
|
|
def on_unmount(self, _):
|
|
removals.append(self.id if self.id is not None else "unknown")
|
|
|
|
async with App().run_test() as pilot:
|
|
await pilot.app.mount(
|
|
Removable(Removable(Removable(id="grandchild"), id="child"), id="parent")
|
|
)
|
|
assert len(pilot.app.screen.walk_children(with_self=False)) == 3
|
|
await pilot.app.screen._nodes[0].remove()
|
|
assert len(pilot.app.screen.walk_children(with_self=False)) == 0
|
|
assert removals == ["grandchild", "child", "parent"]
|
|
|
|
|
|
async def test_query_remove_order():
|
|
"""A DOMQuery.remove of a top-level widget should cause bottom-first removal."""
|
|
|
|
removals: list[str] = []
|
|
|
|
class Removable(Container):
|
|
def on_unmount(self, _):
|
|
removals.append(self.id if self.id is not None else "unknown")
|
|
|
|
async with App().run_test() as pilot:
|
|
await pilot.app.mount(
|
|
Removable(Removable(Removable(id="grandchild"), id="child"), id="parent")
|
|
)
|
|
assert len(pilot.app.screen.walk_children(with_self=False)) == 3
|
|
await pilot.app.query(Removable).remove()
|
|
assert len(pilot.app.screen.walk_children(with_self=False)) == 0
|
|
assert removals == ["grandchild", "child", "parent"]
|
|
|
|
|
|
class ExampleApp(App):
|
|
def compose(self) -> ComposeResult:
|
|
yield Button("ABC")
|
|
yield Label("Outside of vertical.")
|
|
with Vertical():
|
|
for index in range(5):
|
|
yield Label(str(index))
|
|
|
|
|
|
async def test_widget_remove_children_container():
|
|
app = ExampleApp()
|
|
async with app.run_test():
|
|
container = app.query_one(Vertical)
|
|
|
|
# 6 labels in total, with 5 of them inside the container.
|
|
assert len(app.query(Label)) == 6
|
|
assert len(container.children) == 5
|
|
|
|
await container.remove_children()
|
|
|
|
# The labels inside the container are gone, and the 1 outside remains.
|
|
assert len(app.query(Label)) == 1
|
|
assert len(container.children) == 0
|
|
|
|
|
|
async def test_widget_remove_children_with_star_selector():
|
|
app = ExampleApp()
|
|
async with app.run_test():
|
|
container = app.query_one(Vertical)
|
|
|
|
# 6 labels in total, with 5 of them inside the container.
|
|
assert len(app.query(Label)) == 6
|
|
assert len(container.children) == 5
|
|
|
|
await container.remove_children("*")
|
|
|
|
# The labels inside the container are gone, and the 1 outside remains.
|
|
assert len(app.query(Label)) == 1
|
|
assert len(container.children) == 0
|
|
|
|
|
|
async def test_widget_remove_children_with_string_selector():
|
|
app = ExampleApp()
|
|
async with app.run_test():
|
|
container = app.query_one(Vertical)
|
|
|
|
# 6 labels in total, with 5 of them inside the container.
|
|
assert len(app.query(Label)) == 6
|
|
assert len(container.children) == 5
|
|
|
|
await app.screen.remove_children("Label")
|
|
|
|
# Only the Screen > Label widget is gone, everything else remains.
|
|
assert len(app.query(Button)) == 1
|
|
assert len(app.query(Vertical)) == 1
|
|
assert len(app.query(Label)) == 5
|
|
|
|
|
|
async def test_widget_remove_children_with_type_selector():
|
|
app = ExampleApp()
|
|
async with app.run_test():
|
|
assert len(app.query(Button)) == 1 # Sanity check.
|
|
await app.screen.remove_children(Button)
|
|
assert len(app.query(Button)) == 0
|
|
|
|
|
|
async def test_widget_remove_children_with_selector_does_not_leak():
|
|
app = ExampleApp()
|
|
async with app.run_test():
|
|
container = app.query_one(Vertical)
|
|
|
|
# 6 labels in total, with 5 of them inside the container.
|
|
assert len(app.query(Label)) == 6
|
|
assert len(container.children) == 5
|
|
|
|
await container.remove_children("Label")
|
|
|
|
# The labels inside the container are gone, and the 1 outside remains.
|
|
assert len(app.query(Label)) == 1
|
|
assert len(container.children) == 0
|
|
|
|
|
|
async def test_widget_remove_children_no_children():
|
|
app = ExampleApp()
|
|
async with app.run_test():
|
|
button = app.query_one(Button)
|
|
|
|
count_before = len(app.query("*"))
|
|
await button.remove_children()
|
|
count_after = len(app.query("*"))
|
|
|
|
assert len(app.query(Button)) == 1 # The button still remains.
|
|
assert (
|
|
count_before == count_after
|
|
) # No widgets have been removed, since Button has no children.
|
|
|
|
|
|
async def test_widget_remove_children_no_children_match_selector():
|
|
app = ExampleApp()
|
|
async with app.run_test():
|
|
container = app.query_one(Vertical)
|
|
assert len(container.query("Button")) == 0 # Sanity check.
|
|
|
|
count_before = len(app.query("*"))
|
|
container_children_before = list(container.children)
|
|
await container.remove_children("Button")
|
|
|
|
assert count_before == len(app.query("*"))
|
|
assert container_children_before == list(container.children)
|