mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
run test context manager
This commit is contained in:
@@ -10,10 +10,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
### Changed
|
||||
|
||||
- DOMQuery now raises InvalidQueryFormat in response to invalid query strings, rather than cryptic CSS error
|
||||
- Dropped quit_after, screenshot, and screenshot_title from App.run, which can all be done via auto_pilot
|
||||
- Widgets are now closed in reversed DOM order
|
||||
|
||||
### Added
|
||||
|
||||
- Added Unmount event
|
||||
- Added App.run_async method
|
||||
- Added App.run_test context manager
|
||||
- Added auto_pilot to App.run and App.run_async
|
||||
|
||||
## [0.2.1] - 2022-10-23
|
||||
|
||||
|
||||
@@ -633,6 +633,38 @@ class App(Generic[ReturnType], DOMNode):
|
||||
await asyncio.sleep(0.02)
|
||||
await app._animator.wait_for_idle()
|
||||
|
||||
@asynccontextmanager
|
||||
async def run_test(self, *, headless: bool = True):
|
||||
"""An asynchronous context manager for testing app.
|
||||
|
||||
Args:
|
||||
headless (bool, optional): Run in headless mode (no output or input). Defaults to True.
|
||||
|
||||
"""
|
||||
from .pilot import Pilot
|
||||
|
||||
app = self
|
||||
app_ready_event = asyncio.Event()
|
||||
|
||||
def on_app_ready() -> None:
|
||||
"""Called when app is ready to process events."""
|
||||
app_ready_event.set()
|
||||
|
||||
async def run_app(app) -> None:
|
||||
await app._process_messages(ready_callback=on_app_ready, headless=headless)
|
||||
|
||||
# Launch the app in the "background"
|
||||
asyncio.create_task(run_app(app))
|
||||
|
||||
# Wait until the app has performed all startup routines.
|
||||
await app_ready_event.wait()
|
||||
|
||||
# Context manager returns pilot object to manipulate the app
|
||||
yield Pilot(app)
|
||||
|
||||
# Shutdown the app cleanly
|
||||
await app._shutdown()
|
||||
|
||||
async def run_async(
|
||||
self,
|
||||
*,
|
||||
@@ -655,8 +687,16 @@ class App(Generic[ReturnType], DOMNode):
|
||||
async def app_ready() -> None:
|
||||
"""Called by the message loop when the app is ready."""
|
||||
if auto_pilot is not None:
|
||||
|
||||
async def run_auto_pilot(pilot) -> None:
|
||||
try:
|
||||
await auto_pilot(pilot)
|
||||
except Exception:
|
||||
app.exit()
|
||||
raise
|
||||
|
||||
pilot = Pilot(app)
|
||||
asyncio.create_task(auto_pilot(pilot))
|
||||
asyncio.create_task(run_auto_pilot(pilot))
|
||||
|
||||
await app._process_messages(ready_callback=app_ready, headless=headless)
|
||||
await app._shutdown()
|
||||
|
||||
@@ -17,10 +17,15 @@ class Pilot:
|
||||
self._app = app
|
||||
|
||||
def __rich_repr__(self) -> rich.repr.Result:
|
||||
yield "app", "self._app"
|
||||
yield "app", self._app
|
||||
|
||||
@property
|
||||
def app(self) -> App:
|
||||
"""Get a reference to the application.
|
||||
|
||||
Returns:
|
||||
App: The App instance.
|
||||
"""
|
||||
return self._app
|
||||
|
||||
async def press(self, *keys: str) -> None:
|
||||
@@ -39,3 +44,11 @@ class Pilot:
|
||||
delay (float, optional): Seconds to pause. Defaults to 50ms.
|
||||
"""
|
||||
await asyncio.sleep(delay)
|
||||
|
||||
async def exit(self, result: object) -> None:
|
||||
"""Exit the app with the given result.
|
||||
|
||||
Args:
|
||||
result (object): The app result returned by `run` or `run_async`.
|
||||
"""
|
||||
self.app.exit(result)
|
||||
|
||||
Reference in New Issue
Block a user