Skip to content

Commit 93e75f3

Browse files
authored
Merge pull request #969 from Textualize/more-testing
More testing
2 parents 93ccf70 + 6be7895 commit 93e75f3

File tree

10 files changed

+5133
-131
lines changed

10 files changed

+5133
-131
lines changed

docs/examples/styles/grid.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,3 @@ def compose(self):
1414

1515

1616
app = GridApp(css_path="grid.css")
17-
if __name__ == "__main__":
18-
app.run()

docs/examples/styles/links.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,3 @@ def compose(self) -> ComposeResult:
1313

1414

1515
app = LinksApp(css_path="links.css")
16-
17-
if __name__ == "__main__":
18-
app.run()

docs/examples/widgets/checkbox.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ class CheckboxApp(App):
77
def compose(self) -> ComposeResult:
88
yield Static("[b]Example checkboxes\n", classes="label")
99
yield Horizontal(
10-
Static("off: ", classes="label"), Checkbox(), classes="container"
10+
Static("off: ", classes="label"),
11+
Checkbox(animate=False),
12+
classes="container",
1113
)
1214
yield Horizontal(
1315
Static("on: ", classes="label"),
File renamed without changes.

docs/examples/widgets/footer.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,16 @@
44

55

66
class FooterApp(App):
7-
BINDINGS = [Binding(key="q", action="quit", description="Quit the app")]
7+
BINDINGS = [
8+
Binding(key="q", action="quit", description="Quit the app"),
9+
Binding(
10+
key="question_mark",
11+
action="help",
12+
description="Show help screen",
13+
key_display="?",
14+
),
15+
Binding(key="j", action="down", description="Scroll down", show=False),
16+
]
817

918
def compose(self) -> ComposeResult:
1019
yield Footer()

docs/widgets/data_table.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ The example below populates a table with CSV data.
1111

1212
=== "Output"
1313

14-
```{.textual path="docs/examples/widgets/table.py"}
14+
```{.textual path="docs/examples/widgets/data_table.py"}
1515
```
1616

17-
=== "table.py"
17+
=== "data_table.py"
1818

1919
```python
20-
--8<-- "docs/examples/widgets/table.py"
20+
--8<-- "docs/examples/widgets/data_table.py"
2121
```
2222

2323

src/textual/app.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ async def press_keys() -> None:
605605
assert press
606606
driver = app._driver
607607
assert driver is not None
608-
await asyncio.sleep(0.01)
608+
await asyncio.sleep(0.02)
609609
for key in press:
610610
if key == "_":
611611
print("(pause 50ms)")
@@ -632,7 +632,13 @@ async def press_keys() -> None:
632632
print(f"press {key!r} (char={char!r})")
633633
key_event = events.Key(self, key, char)
634634
driver.send_event(key_event)
635-
await asyncio.sleep(0.01)
635+
# TODO: A bit of a fudge - extra sleep after tabbing to help guard against race
636+
# condition between widget-level key handling and app/screen level handling.
637+
# More information here: https://github.com/Textualize/textual/issues/1009
638+
# This conditional sleep can be removed after that issue is closed.
639+
if key == "tab":
640+
await asyncio.sleep(0.05)
641+
await asyncio.sleep(0.02)
636642

637643
await app._animator.wait_for_idle()
638644

tests/snapshot_tests/__snapshots__/test_snapshots.ambr

Lines changed: 5031 additions & 118 deletions
Large diffs are not rendered by default.

tests/snapshot_tests/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ def compare(
5959
"""
6060
node = request.node
6161
app = import_app(app_path)
62+
compare.app = app
6263
actual_screenshot = take_svg_screenshot(
6364
app=app,
6465
press=press,

tests/snapshot_tests/test_snapshots.py

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
from pathlib import Path, PurePosixPath
2+
3+
import pytest
4+
5+
from textual.app import App
6+
from textual.widgets import Input, Button
7+
8+
9+
# --- Layout related stuff ---
10+
111
def test_grid_layout_basic(snap_compare):
212
assert snap_compare("docs/examples/guide/layout/grid_layout1.py")
313

@@ -26,7 +36,73 @@ def test_dock_layout_sidebar(snap_compare):
2636
assert snap_compare("docs/examples/guide/layout/dock_layout2_sidebar.py")
2737

2838

39+
# --- Widgets - rendering and basic interactions ---
40+
# Each widget should have a canonical example that is display in the docs.
41+
# When adding a new widget, ideally we should also create a snapshot test
42+
# from these examples which test rendering and simple interactions with it.
43+
2944
def test_checkboxes(snap_compare):
3045
"""Tests checkboxes but also acts a regression test for using
3146
width: auto in a Horizontal layout context."""
32-
assert snap_compare("docs/examples/widgets/checkbox.py")
47+
press = [
48+
"shift+tab",
49+
"enter", # toggle off
50+
"shift+tab",
51+
"wait:20",
52+
"enter", # toggle on
53+
"wait:20",
54+
]
55+
assert snap_compare("docs/examples/widgets/checkbox.py", press=press)
56+
57+
58+
def test_input_and_focus(snap_compare):
59+
press = [
60+
"tab",
61+
*"Darren", # Focus first input, write "Darren"
62+
"tab",
63+
*"Burns", # Tab focus to second input, write "Burns"
64+
]
65+
assert snap_compare("docs/examples/widgets/input.py", press=press)
66+
67+
# Assert that the state of the Input is what we'd expect
68+
app: App = snap_compare.app
69+
input: Input = app.query_one(Input)
70+
assert input.value == "Darren"
71+
assert input.cursor_position == 6
72+
assert input.view_position == 0
73+
74+
75+
def test_buttons_render(snap_compare):
76+
# Testing button rendering. We press tab to focus the first button too.
77+
assert snap_compare("docs/examples/widgets/button.py", press=["tab"])
78+
79+
app = snap_compare.app
80+
button: Button = app.query_one(Button)
81+
assert app.focused is button
82+
83+
84+
def test_datatable_render(snap_compare):
85+
press = ["tab", "down", "down", "right", "up", "left"]
86+
assert snap_compare("docs/examples/widgets/data_table.py", press=press)
87+
88+
89+
def test_footer_render(snap_compare):
90+
assert snap_compare("docs/examples/widgets/footer.py")
91+
92+
93+
def test_header_render(snap_compare):
94+
assert snap_compare("docs/examples/widgets/header.py")
95+
96+
97+
# --- CSS properties ---
98+
# We have a canonical example for each CSS property that is shown in their docs.
99+
# If any of these change, something has likely broken, so snapshot each of them.
100+
101+
PATHS = [
102+
str(PurePosixPath(path)) for path in Path("docs/examples/styles").iterdir() if path.suffix == ".py"
103+
]
104+
105+
106+
@pytest.mark.parametrize("path", PATHS)
107+
def test_css_property_snapshot(path, snap_compare):
108+
assert snap_compare(path)

0 commit comments

Comments
 (0)