Notifications and toasts¶
Examples are in Python
Kotlin has the same methods in camelCase (await_toast → awaitToast), with named
arguments where Python uses keywords. Kotlin + JUnit 5
shows this page's examples in Kotlin.
Toasts¶
device.await_toast(text=None, mode=EXACT, *, package_name=None, timeout=None) returns a
Toast(text, package_name). A toast shown in the last 3.5 s counts (the longest one stays up),
so calling it right after the action that raises the toast cannot miss it. It does not consume
the toast: two calls in a row can both match it.
Without package_name, a toast from any package matches; app.await_toast(text, mode) matches
only that app's (on Android 11+ a text toast is drawn by SystemUI but still reported under the
app that posted it). mode is a selector MatchMode (CONTAINS, REGEX, …). Nothing within
the timeout is a WaitTimeoutError with reason NO_TOAST.
app.element(res("save")).tap()
assert app.await_toast().text == "Saved"
app.await_toast("Saved") # the same check, as a wait for that text
Custom-view toasts posted from the background are blocked by Android itself (11+) and never appear.
Notifications¶
Tap reads notifications as data, from a notification listener in its driver app (it gets
notification access for the session and gives it back on detach); the shade stays closed. A
Notification has package_name, title, text, actions (its button titles), clearable
and posted_at.
| Call | Does |
|---|---|
await_notification(title, text, mode, *, package_name, timeout) |
waits until a matching notification is showing (one already posted counts) and returns the newest; nothing within the timeout is a wait timeout with reason NO_NOTIFICATION. app.await_notification(...) matches only that app's |
notifications() |
every notification showing, newest first |
open_notification(title, text, mode, *, package_name, action) |
opens it as a tap in the shade does (its content intent; an auto-cancel notification then goes away), or with action presses the button with that title |
dismiss_notification(title, text, mode, *, package_name) |
swipes it away; an ongoing one is refused (ACTION_REJECTED / NOT_CLEARABLE) |
title and text match under mode (EXACT, CONTAINS, REGEX, …); every argument given
must match. Open and dismiss act on exactly one notification: none or several fail with
NOT_FOUND / AMBIGUOUS before anything happens. Group summaries are not listed. On Android
13+ an app needs the POST_NOTIFICATIONS permission to post at all.
app.element(res("send")).tap()
message = app.await_notification("New message")
assert message.actions == ["Mark as read"]
device.open_notification("New message", package_name=app.package_name)
assert device.foreground_activity().class_name == "com.example.chat.ConversationActivity"
device.open_notification("New message", package_name=app.package_name, action="Mark as read")
device.dismiss_notification("Syncing", package_name=app.package_name) # ongoing: NOT_CLEARABLE
device.foreground_activity() (Where am I?) tells you where
opening a notification landed.