Различить исходы страницы#
После действия страница может подтвердить успех, показать отказ или не изменить наблюдаемое
состояние. outcomes(...) связывает признаки страницы с типизированными результатами:
# examples/mail/browser/send.py
class ComposerPage:
recipient: Annotated[Element, css('input[name="recipient"]')]
subject: Annotated[Element, css('input[name="subject"]')]
body: Annotated[Element, css('textarea[name="body"]')]
submit: Annotated[Element, css('button[type="submit"]')]
rejected: Annotated[Element, css('[data-outcome="rejected"]')]
@dataclass(frozen=True, slots=True)
class Sent:
pass
@dataclass(frozen=True, slots=True)
class Rejected:
pass
@dataclass(frozen=True, slots=True)
class Silent:
pass
async def _sent(content: ComposerPage) -> Sent:
_ = content
return Sent()
async def _rejected(content: ComposerPage) -> Rejected:
_ = content
return Rejected()
async def _silent(content: ComposerPage) -> Silent:
_ = content
return Silent()
@dataclass(frozen=True, slots=True, kw_only=True)
class OpenComposer(BrowserOperation[None, None]):
__browser__ = Browser.goto("/compose/", at=css("[data-composer]"))
@dataclass(frozen=True, slots=True, kw_only=True)
class SubmitMessage(BrowserOperation[ComposerPage, Sent | Rejected | Silent]):
__browser__ = Browser.act(
ComposerPage,
at=css("[data-composer]"),
outcomes=outcomes(
when(visible(css('[role="status"]')), then=_sent),
when(visible(css('[data-outcome="rejected"]')), then=_rejected),
otherwise=_silent,
),
)
recipient: str
subject: str
body: str
async def act(self, content: ComposerPage) -> None:
if await content.rejected.visible():
await content.rejected.press("Escape")
await content.recipient.fill(self.recipient)
await content.subject.fill(self.subject)
await content.body.fill(self.body)
await content.submit.click()
class ComposerApi(AsyncBrowserApi):
open = op(OpenComposer)
send = op(SubmitMessage)
Один запуск проходит все три состояния настоящей страницы:
# examples/docs/integration_browser_outcomes.py
sent, rejected, silent = await send_three_messages()
outcomes = type(sent).__name__, type(rejected).__name__, type(silent).__name__
Файл целиком
"""Run one declared browser operation through all composer outcomes."""
from __future__ import annotations
import asyncio
from examples.mail.browser.send import send_three_messages
async def main() -> None:
# docs:integration-browser-outcomes:start
# examples/docs/integration_browser_outcomes.py
sent, rejected, silent = await send_three_messages()
outcomes = type(sent).__name__, type(rejected).__name__, type(silent).__name__
# docs:integration-browser-outcomes:end
print(f"outcomes: {', '.join(outcomes)}")
if __name__ == "__main__":
asyncio.run(main())
outcomes: Sent, Rejected, Silent
Что наблюдает клиент |
Что срабатывает |
Что получает вызывающий |
|---|---|---|
Видимая строка состояния |
первый |
|
Видимый диалог отказа |
второй |
|
Ни один признак до дедлайна |
|
|
Правила Failure(...) проверяются перед успешными исходами и выбрасывают PageError. Помехи
Handle(...) выполняются ещё раньше, чтобы баннер или диалог не скрывал настоящий результат.
Первое совпавшее правило завершает ожидание.
Примечание
otherwise должен отражать реальный контракт сайта. Верните отдельный тип для допустимого
молчания или выбросьте исключение, если отсутствие признака означает сбой.