run test context manager

This commit is contained in:
Will McGugan
2022-10-29 10:53:50 +01:00
parent 269ff4883e
commit 264b4fe733
3 changed files with 60 additions and 2 deletions

View File

@@ -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

View File

@@ -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()

View File

@@ -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)