textual/tests/test_widget.py
Will McGugan b0f5c35782
tabs widget (#2020)
* tabs widget

* click underline

* color tweak

* docs

* docs update

* expose Tab

* added remove_tab and clear

* fix cycling

* add animation

* docs

* changelog

* remove recompose

* docstrings

* Update docs/guide/actions.md

Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>

* Rodrigoed the tabs

* Update docs/widgets/tabs.md

Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>

* Update docs/widgets/tabs.md

Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>

* copy

* docstrings

* docstring

* docstring

* Apply suggestions from code review

Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>

* stop click

* docstring

* auto assign consistent IDs

* Apply suggestions from code review

Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>

* Document bindings

* document bindings

* Apply suggestions from code review

Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>

---------

Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>
2023-03-13 14:39:15 +00:00

183 lines
5.1 KiB
Python

import pytest
from textual._node_list import DuplicateIds
from textual.app import App, ComposeResult
from textual.css.errors import StyleValueError
from textual.css.query import NoMatches
from textual.geometry import Size
from textual.widget import MountError, Widget
from textual.widgets import Label
@pytest.mark.parametrize(
"set_val, get_val, style_str",
[
[True, True, "visible"],
[False, False, "hidden"],
["hidden", False, "hidden"],
["visible", True, "visible"],
],
)
def test_widget_set_visible_true(set_val, get_val, style_str):
widget = Widget()
widget.visible = set_val
assert widget.visible is get_val
assert widget.styles.visibility == style_str
def test_widget_set_visible_invalid_string():
widget = Widget()
with pytest.raises(StyleValueError):
widget.visible = "nope! no widget for me!"
assert widget.visible
def test_widget_content_width():
class TextWidget(Widget):
def __init__(self, text: str, id: str) -> None:
self.text = text
super().__init__(id=id)
self.expand = False
self.shrink = True
def render(self) -> str:
return self.text
widget1 = TextWidget("foo", id="widget1")
widget2 = TextWidget("foo\nbar", id="widget2")
widget3 = TextWidget("foo\nbar\nbaz", id="widget3")
app = App()
app._set_active()
width = widget1.get_content_width(Size(20, 20), Size(80, 24))
height = widget1.get_content_height(Size(20, 20), Size(80, 24), width)
assert width == 3
assert height == 1
width = widget2.get_content_width(Size(20, 20), Size(80, 24))
height = widget2.get_content_height(Size(20, 20), Size(80, 24), width)
assert width == 3
assert height == 2
width = widget3.get_content_width(Size(20, 20), Size(80, 24))
height = widget3.get_content_height(Size(20, 20), Size(80, 24), width)
assert width == 3
assert height == 3
class GetByIdApp(App):
def compose(self) -> ComposeResult:
grandchild1 = Widget(id="grandchild1")
child1 = Widget(grandchild1, id="child1")
child2 = Widget(id="child2")
yield Widget(
child1,
child2,
id="parent",
)
@pytest.fixture
async def hierarchy_app():
app = GetByIdApp()
async with app.run_test():
yield app
@pytest.fixture
async def parent(hierarchy_app):
yield hierarchy_app.get_widget_by_id("parent")
def test_get_child_by_id_gets_first_child(parent):
child = parent.get_child_by_id(id="child1")
assert child.id == "child1"
assert child.get_child_by_id(id="grandchild1").id == "grandchild1"
assert parent.get_child_by_id(id="child2").id == "child2"
def test_get_child_by_id_no_matching_child(parent):
with pytest.raises(NoMatches):
parent.get_child_by_id(id="doesnt-exist")
def test_get_child_by_id_only_immediate_descendents(parent):
with pytest.raises(NoMatches):
parent.get_child_by_id(id="grandchild1")
def test_get_widget_by_id_no_matching_child(parent):
with pytest.raises(NoMatches):
parent.get_widget_by_id(id="i-dont-exist")
def test_get_widget_by_id_non_immediate_descendants(parent):
result = parent.get_widget_by_id("grandchild1")
assert result.id == "grandchild1"
def test_get_widget_by_id_immediate_descendants(parent):
result = parent.get_widget_by_id("child1")
assert result.id == "child1"
def test_get_widget_by_id_doesnt_return_self(parent):
with pytest.raises(NoMatches):
parent.get_widget_by_id("parent")
def test_get_widgets_app_delegated(hierarchy_app, parent):
# Check that get_child_by_id finds the parent, which is a child of the default Screen
queried_parent = hierarchy_app.get_child_by_id("parent")
assert queried_parent is parent
# Check that the grandchild (descendant of the default screen) is found
grandchild = hierarchy_app.get_widget_by_id("grandchild1")
assert grandchild.id == "grandchild1"
def test_widget_mount_ids_must_be_unique_mounting_all_in_one_go(parent):
widget1 = Widget(id="hello")
widget2 = Widget(id="hello")
with pytest.raises(MountError):
parent.mount(widget1, widget2)
def test_widget_mount_ids_must_be_unique_mounting_multiple_calls(parent):
widget1 = Widget(id="hello")
widget2 = Widget(id="hello")
parent.mount(widget1)
with pytest.raises(DuplicateIds):
parent.mount(widget2)
# Regression test for https://github.com/Textualize/textual/issues/1634
async def test_remove():
class RemoveMeLabel(Label):
async def on_mount(self) -> None:
await self.run_action("app.remove_all")
class Container(Widget):
async def clear(self) -> None:
await self.query("*").remove()
class RemoveApp(App):
def compose(self) -> ComposeResult:
yield Container(RemoveMeLabel())
async def action_remove_all(self) -> None:
await self.query_one(Container).clear()
self.exit(123)
app = RemoveApp()
async with app.run_test() as pilot:
await pilot.press("r")
await pilot.pause()
assert app.return_value == 123