mirror of
https://github.com/1j01/textual-paint.git
synced 2024-12-22 14:21:33 +03:00
WIP on test recorder
This commit is contained in:
parent
0c43b7ff5a
commit
24b9b9986c
@ -1,20 +1,31 @@
|
||||
import re
|
||||
from rich.text import Text
|
||||
import os
|
||||
from textual.css.query import NoMatches, TooManyMatches
|
||||
from textual.dom import DOMNode
|
||||
from textual.events import Event, Key, MouseDown, MouseMove, MouseUp
|
||||
from textual.screen import Screen
|
||||
from textual_paint.paint import PaintApp
|
||||
|
||||
OUTPUT_FILE = "tests/test_paint_something.py"
|
||||
def unique_file(path: str) -> str:
|
||||
filename, extension = os.path.splitext(path)
|
||||
counter = 1
|
||||
|
||||
steps: list[tuple[Event, str]] = []
|
||||
while os.path.exists(path):
|
||||
# path = f"{filename} ({counter}){extension}"
|
||||
path = f"{filename}_{counter}{extension}"
|
||||
counter += 1
|
||||
|
||||
def get_selector(target: DOMNode) -> str:
|
||||
return path
|
||||
|
||||
OUTPUT_FILE = unique_file("tests/test_paint_something.py")
|
||||
|
||||
steps: list[tuple[Event, str, int|None]] = []
|
||||
|
||||
def get_selector(target: DOMNode) -> tuple[str, int|None]:
|
||||
"""Return a selector that can be used to find the widget."""
|
||||
assert app is not None, "app should be set by now"
|
||||
widget = target
|
||||
if widget.id:
|
||||
return f"#{widget.id}"
|
||||
return f"#{widget.id}", None
|
||||
selector = widget.css_identifier
|
||||
while widget.parent and not isinstance(widget.parent, Screen):
|
||||
widget = widget.parent
|
||||
@ -26,20 +37,23 @@ def get_selector(target: DOMNode) -> str:
|
||||
try:
|
||||
query_result = app.query_one(selector)
|
||||
except TooManyMatches:
|
||||
raise Exception(f"Selector {selector!r} matches more than one widget ({app.query(selector).nodes!r})")
|
||||
# raise Exception(f"Selector {selector!r} matches more than one widget ({app.query(selector).nodes!r})")
|
||||
return selector, app.query(selector).nodes.index(target)
|
||||
# smarter differentiators would be nice, like tooltip or text content,
|
||||
# but at least with indices, you'll know when you changed the tab order
|
||||
except NoMatches:
|
||||
raise Exception(f"Selector {selector!r} didn't match the target widget ({target!r})")
|
||||
if query_result is not target:
|
||||
raise Exception(f"Selector {selector!r} matched a different widget than the target ({query_result!r} rather than {target!r})")
|
||||
|
||||
return selector
|
||||
return selector, None
|
||||
|
||||
original_on_event = PaintApp.on_event
|
||||
async def on_event(self: PaintApp, event: Event) -> None:
|
||||
await original_on_event(self, event)
|
||||
if isinstance(event, (MouseDown, MouseMove, MouseUp)):
|
||||
widget, _ = self.get_widget_at(event.x, event.y)
|
||||
steps.append((event, get_selector(widget)))
|
||||
steps.append((event, *get_selector(widget)))
|
||||
elif isinstance(event, Key):
|
||||
if event.key == "ctrl+z":
|
||||
steps.pop()
|
||||
@ -57,20 +71,27 @@ def replay() -> None:
|
||||
app.on_event = on_event.__get__(app)
|
||||
async def replay_steps() -> None:
|
||||
assert app is not None, "app should be set by now"
|
||||
for event in steps:
|
||||
for event, selector, index in steps:
|
||||
await app.on_event(event)
|
||||
app.call_later(replay_steps)
|
||||
app.run() # blocking
|
||||
|
||||
def indent(text: str, spaces: int) -> str:
|
||||
return re.sub(r"^", " " * spaces, text, flags=re.MULTILINE)
|
||||
return "\n".join(" " * spaces + line for line in text.splitlines())
|
||||
|
||||
def save_replay() -> None:
|
||||
assert app is not None, "app should be set by now"
|
||||
helpers_code = ""
|
||||
steps_code = ""
|
||||
for event, selector in steps:
|
||||
for event, selector, index in steps:
|
||||
if isinstance(event, MouseDown):
|
||||
steps_code += f"await pilot.click({selector!r}, offset=Offset({event.x}, {event.y}))\n"
|
||||
if index is None:
|
||||
steps_code += f"await pilot.click({selector!r}, offset=Offset({event.x}, {event.y}))\n"
|
||||
else:
|
||||
steps_code += f"widget = pilot.app.query({selector!r})[{index!r}]\n"
|
||||
# steps_code += f"await pilot.click(widget, offset=Offset({event.x}, {event.y}))\n" # would be nice
|
||||
steps_code += f"await pilot.click(offset=Offset({event.x}, {event.y}) + widget.region.offset)\n"
|
||||
|
||||
|
||||
script = f"""\
|
||||
from pathlib import Path, PurePath
|
||||
@ -103,7 +124,7 @@ def test_paint_something(snap_compare: SnapCompareType):
|
||||
{indent(helpers_code, 8)}
|
||||
{indent(steps_code, 8)}
|
||||
|
||||
assert snap_compare(PAINT, run_before=test_paint_something_steps, size=({app.size.width}, {app.size.height}))
|
||||
assert snap_compare(PAINT, run_before=test_paint_something_steps, terminal_size=({app.size.width}, {app.size.height}))
|
||||
"""
|
||||
with open(OUTPUT_FILE, "w") as f:
|
||||
f.write(script)
|
||||
|
Loading…
Reference in New Issue
Block a user