# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations import inspect import tempfile import textwrap import traceback from pathlib import Path from typing import TYPE_CHECKING, Any, Callable from unittest.mock import MagicMock from urllib import parse from streamlit.runtime import Runtime from streamlit.runtime.caching.storage.dummy_cache_storage import ( MemoryCacheStorageManager, ) from streamlit.runtime.media_file_manager import MediaFileManager from streamlit.runtime.memory_media_file_storage import MemoryMediaFileStorage from streamlit.runtime.pages_manager import PagesManager from streamlit.runtime.scriptrunner.script_cache import ScriptCache from streamlit.runtime.secrets import Secrets from streamlit.runtime.state.common import TESTING_KEY from streamlit.runtime.state.safe_session_state import SafeSessionState from streamlit.runtime.state.session_state import SessionState from streamlit.source_util import page_icon_and_name from streamlit.testing.v1.element_tree import ( Block, Button, ButtonGroup, Caption, ChatInput, ChatMessage, Checkbox, Code, ColorPicker, Column, Dataframe, DateInput, Divider, ElementList, ElementTree, Error, Exception, # noqa: A004 Expander, Header, Info, Json, Latex, Markdown, Metric, Multiselect, Node, NumberInput, Radio, Selectbox, SelectSlider, Slider, Status, Subheader, Success, Tab, Table, Text, TextArea, TextInput, TimeInput, Title, Toast, Toggle, Warning, # noqa: A004 WidgetList, repr_, ) from streamlit.testing.v1.local_script_runner import LocalScriptRunner from streamlit.testing.v1.util import patch_config_options from streamlit.util import calc_md5 if TYPE_CHECKING: from collections.abc import Iterator, Sequence from streamlit.proto.WidgetStates_pb2 import WidgetStates TMP_DIR = tempfile.TemporaryDirectory() class AppTest: """ A simulated Streamlit app to check the correctness of displayed\ elements and outputs. An instance of ``AppTest`` simulates a running Streamlit app. This class provides methods to set up, manipulate, and inspect the app contents via API instead of a browser UI. It can be used to write automated tests of an app in various scenarios. These can then be run using a tool like pytest. ``AppTest`` can be initialized by one of three class methods: * |st.testing.v1.AppTest.from_file|_ (recommended) * |st.testing.v1.AppTest.from_string|_ * |st.testing.v1.AppTest.from_function|_ Once initialized, Session State and widget values can be updated and the script can be run. Unlike an actual live-running Streamlit app, you need to call ``AppTest.run()`` explicitly to re-run the app after changing a widget value. Switching pages also requires an explicit, follow-up call to ``AppTest.run()``. ``AppTest`` enables developers to build tests on their app as-is, in the familiar python test format, without major refactoring or abstracting out logic to be tested separately from the UI. Tests can run quickly with very low overhead. A typical pattern is to build a suite of tests for an app that ensure consistent functionality as the app evolves, and run the tests locally and/or in a CI environment like Github Actions. .. note:: ``AppTest`` only supports testing a single page of an app per instance. For multipage apps, each page will need to be tested separately. ``AppTest`` is not yet compatible with multipage apps using ``st.navigation`` and ``st.Page``. .. |st.testing.v1.AppTest.from_file| replace:: ``st.testing.v1.AppTest.from_file`` .. _st.testing.v1.AppTest.from_file: #apptestfrom_file .. |st.testing.v1.AppTest.from_string| replace:: ``st.testing.v1.AppTest.from_string`` .. _st.testing.v1.AppTest.from_string: #apptestfrom_string .. |st.testing.v1.AppTest.from_function| replace:: ``st.testing.v1.AppTest.from_function`` .. _st.testing.v1.AppTest.from_function: #apptestfrom_function Attributes ---------- secrets: dict[str, Any] Dictionary of secrets to be used the simulated app. Use dict-like syntax to set secret values for the simulated app. session_state: SafeSessionState Session State for the simulated app. SafeSessionState object supports read and write operations as usual for Streamlit apps. query_params: dict[str, Any] Dictionary of query parameters to be used by the simluated app. Use dict-like syntax to set ``query_params`` values for the simulated app. """ def __init__( self, script_path: str | Path, *, default_timeout: float, args: tuple[Any, ...] | None = None, kwargs: dict[str, Any] | None = None, ) -> None: self._script_path = str(script_path) self.default_timeout = default_timeout session_state = SessionState() session_state[TESTING_KEY] = {} self.session_state = SafeSessionState(session_state, lambda: None) self.query_params: dict[str, Any] = {} self.secrets: dict[str, Any] = {} self.args = args self.kwargs = kwargs self._page_hash = "" tree = ElementTree() tree._runner = self self._tree = tree @classmethod def from_string(cls, script: str, *, default_timeout: float = 3) -> AppTest: """ Create an instance of ``AppTest`` to simulate an app page defined\ within a string. This is useful for testing short scripts that fit comfortably as an inline string in the test itself, without having to create a separate file for it. The script must be executable on its own and so must contain all necessary imports. Parameters ---------- script: str The string contents of the script to be run. default_timeout: float Default time in seconds before a script run is timed out. Can be overridden for individual ``.run()`` calls. Returns ------- AppTest A simulated Streamlit app for testing. The simulated app can be executed via ``.run()``. """ return cls._from_string(script, default_timeout=default_timeout) @classmethod def _from_string( cls, script: str, *, default_timeout: float = 3, args: tuple[Any, ...] | None = None, kwargs: dict[str, Any] | None = None, ) -> AppTest: script_name = calc_md5(bytes(script, "utf-8")) path = Path(TMP_DIR.name, script_name) aligned_script = textwrap.dedent(script) path.write_text(aligned_script) return AppTest( str(path), default_timeout=default_timeout, args=args, kwargs=kwargs ) @classmethod def from_function( cls, script: Callable[..., Any], *, default_timeout: float = 3, args: tuple[Any, ...] | None = None, kwargs: dict[str, Any] | None = None, ) -> AppTest: """ Create an instance of ``AppTest`` to simulate an app page defined\ within a function. This is similar to ``AppTest.from_string()``, but more convenient to write with IDE assistance. The script must be executable on its own and so must contain all necessary imports. Parameters ---------- script: Callable A function whose body will be used as a script. Must be runnable in isolation, so it must include any necessary imports. default_timeout: float Default time in seconds before a script run is timed out. Can be overridden for individual ``.run()`` calls. args: tuple An optional tuple of args to pass to the script function. kwargs: dict An optional dict of kwargs to pass to the script function. Returns ------- AppTest A simulated Streamlit app for testing. The simulated app can be executed via ``.run()``. """ source_lines, _ = inspect.getsourcelines(script) source = textwrap.dedent("".join(source_lines)) module = source + f"\n{script.__name__}(*__args, **__kwargs)" return cls._from_string( module, default_timeout=default_timeout, args=args, kwargs=kwargs ) @classmethod def from_file( cls, script_path: str | Path, *, default_timeout: float = 3 ) -> AppTest: """ Create an instance of ``AppTest`` to simulate an app page defined\ within a file. This option is most convenient for CI workflows and testing of published apps. The script must be executable on its own and so must contain all necessary imports. Parameters ---------- script_path: str | Path Path to a script file. The path should be absolute or relative to the file calling ``.from_file``. default_timeout: float Default time in seconds before a script run is timed out. Can be overridden for individual ``.run()`` calls. Returns ------- AppTest A simulated Streamlit app for testing. The simulated app can be executed via ``.run()``. """ script_path = Path(script_path) if script_path.is_file(): path = script_path else: # TODO: Make this not super fragile # Attempt to find the test file calling this method, so the # path can be relative to there. stack = traceback.StackSummary.extract(traceback.walk_stack(None)) filepath = Path(stack[1].filename) path = filepath.parent / script_path return AppTest(path, default_timeout=default_timeout) def _run( self, widget_state: WidgetStates | None = None, timeout: float | None = None, ) -> AppTest: """Run the script, and parse the output messages for querying and interaction. Timeout is in seconds, or None to use the default timeout of the runner. """ # Have to import the streamlit module itself so replacing st.secrets # is visible to other modules. import streamlit as st if timeout is None: timeout = self.default_timeout # setup mock_runtime = MagicMock(spec=Runtime) mock_runtime.media_file_mgr = MediaFileManager( MemoryMediaFileStorage("/mock/media") ) mock_runtime.cache_storage_manager = MemoryCacheStorageManager() Runtime._instance = mock_runtime script_cache = ScriptCache() pages_manager = PagesManager( self._script_path, script_cache, setup_watcher=False ) saved_secrets: Secrets = st.secrets # Only modify global secrets stuff if we have been given secrets if self.secrets: new_secrets = Secrets() new_secrets._secrets = self.secrets st.secrets = new_secrets script_runner = LocalScriptRunner( self._script_path, self.session_state, pages_manager, args=self.args, kwargs=self.kwargs, ) with patch_config_options({"global.appTest": True}): self._tree = script_runner.run( widget_state, self.query_params, timeout, self._page_hash ) self._tree._runner = self # Last event is SHUTDOWN, so the corresponding data includes query string query_string = script_runner.event_data[-1]["client_state"].query_string self.query_params = parse.parse_qs(query_string) if self.secrets: if st.secrets._secrets is not None: self.secrets = dict(st.secrets._secrets) st.secrets = saved_secrets Runtime._instance = None return self def run(self, *, timeout: float | None = None) -> AppTest: """Run the script from the current state. This is equivalent to manually rerunning the app or the rerun that occurs upon user interaction. ``AppTest.run()`` must be manually called after updating a widget value or switching pages as script reruns do not occur automatically as they do for live-running Streamlit apps. Parameters ---------- timeout : float or None The maximum number of seconds to run the script. If ``timeout`` is ``None`` (default), Streamlit uses the default timeout set for the instance of ``AppTest``. Returns ------- AppTest self """ return self._tree.run(timeout=timeout) def switch_page(self, page_path: str) -> AppTest: """Switch to another page of the app. This method does not automatically rerun the app. Use a follow-up call to ``AppTest.run()`` to obtain the elements on the selected page. Parameters ---------- page_path: str Path of the page to switch to. The path must be relative to the main script's location (e.g. ``"pages/my_page.py"``). Returns ------- AppTest self """ main_dir = Path(self._script_path).parent full_page_path = main_dir / page_path if not full_page_path.is_file(): raise ValueError( f"Unable to find script at {page_path}, make sure the page given is relative to the main script." ) page_path_str = str(full_page_path.resolve()) _, page_name = page_icon_and_name(Path(page_path_str)) self._page_hash = calc_md5(page_name) return self @property def main(self) -> Block: """Sequence of elements within the main body of the app. Returns ------- Block A container of elements. Block can be queried for elements in the same manner as ``AppTest``. For example, ``Block.checkbox`` will return all ``st.checkbox`` within the associated container. """ return self._tree.main @property def sidebar(self) -> Block: """Sequence of all elements within ``st.sidebar``. Returns ------- Block A container of elements. Block can be queried for elements in the same manner as ``AppTest``. For example, ``Block.checkbox`` will return all ``st.checkbox`` within the associated container. """ return self._tree.sidebar @property def button(self) -> WidgetList[Button]: """Sequence of all ``st.button`` and ``st.form_submit_button`` widgets. Returns ------- WidgetList of Button Sequence of all ``st.button`` and ``st.form_submit_button`` widgets. Individual widgets can be accessed from a WidgetList by index (order on the page) or key. For example, ``at.button[0]`` for the first widget or ``at.button(key="my_key")`` for a widget with a given key. """ return self._tree.button @property def button_group(self) -> WidgetList[ButtonGroup[Any]]: """Sequence of all ``st.feedback`` widgets. Returns ------- WidgetList of ButtonGroup Sequence of all ``st.feedback`` widgets. Individual widgets can be accessed from a WidgetList by index (order on the page) or key. For example, ``at.button_group[0]`` for the first widget or ``at.button_group(key="my_key")`` for a widget with a given key. """ return self._tree.button_group @property def caption(self) -> ElementList[Caption]: """Sequence of all ``st.caption`` elements. Returns ------- ElementList of Caption Sequence of all ``st.caption`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.caption[0]`` for the first element. Caption is an extension of the Element class. """ return self._tree.caption @property def chat_input(self) -> WidgetList[ChatInput]: """Sequence of all ``st.chat_input`` widgets. Returns ------- WidgetList of ChatInput Sequence of all ``st.chat_input`` widgets. Individual widgets can be accessed from a WidgetList by index (order on the page) or key. For example, ``at.chat_input[0]`` for the first widget or ``at.chat_input(key="my_key")`` for a widget with a given key. """ return self._tree.chat_input @property def chat_message(self) -> Sequence[ChatMessage]: """Sequence of all ``st.chat_message`` elements. Returns ------- Sequence of ChatMessage Sequence of all ``st.chat_message`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.chat_message[0]`` for the first element. ChatMessage is an extension of the Block class. """ return self._tree.chat_message @property def checkbox(self) -> WidgetList[Checkbox]: """Sequence of all ``st.checkbox`` widgets. Returns ------- WidgetList of Checkbox Sequence of all ``st.checkbox`` widgets. Individual widgets can be accessed from a WidgetList by index (order on the page) or key. For example, ``at.checkbox[0]`` for the first widget or ``at.checkbox(key="my_key")`` for a widget with a given key. """ return self._tree.checkbox @property def code(self) -> ElementList[Code]: """Sequence of all ``st.code`` elements. Returns ------- ElementList of Code Sequence of all ``st.code`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.code[0]`` for the first element. Code is an extension of the Element class. """ return self._tree.code @property def color_picker(self) -> WidgetList[ColorPicker]: """Sequence of all ``st.color_picker`` widgets. Returns ------- WidgetList of ColorPicker Sequence of all ``st.color_picker`` widgets. Individual widgets can be accessed from a WidgetList by index (order on the page) or key. For example, ``at.color_picker[0]`` for the first widget or ``at.color_picker(key="my_key")`` for a widget with a given key. """ return self._tree.color_picker @property def columns(self) -> Sequence[Column]: """Sequence of all columns within ``st.columns`` elements. Each column within a single ``st.columns`` will be returned as a separate Column in the Sequence. Returns ------- Sequence of Column Sequence of all columns within ``st.columns`` elements. Individual columns can be accessed from an ElementList by index (order on the page). For example, ``at.columns[0]`` for the first column. Column is an extension of the Block class. """ return self._tree.columns @property def dataframe(self) -> ElementList[Dataframe]: """Sequence of all ``st.dataframe`` elements. Returns ------- ElementList of Dataframe Sequence of all ``st.dataframe`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.dataframe[0]`` for the first element. Dataframe is an extension of the Element class. """ return self._tree.dataframe @property def date_input(self) -> WidgetList[DateInput]: """Sequence of all ``st.date_input`` widgets. Returns ------- WidgetList of DateInput Sequence of all ``st.date_input`` widgets. Individual widgets can be accessed from a WidgetList by index (order on the page) or key. For example, ``at.date_input[0]`` for the first widget or ``at.date_input(key="my_key")`` for a widget with a given key. """ return self._tree.date_input @property def divider(self) -> ElementList[Divider]: """Sequence of all ``st.divider`` elements. Returns ------- ElementList of Divider Sequence of all ``st.divider`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.divider[0]`` for the first element. Divider is an extension of the Element class. """ return self._tree.divider @property def error(self) -> ElementList[Error]: """Sequence of all ``st.error`` elements. Returns ------- ElementList of Error Sequence of all ``st.error`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.error[0]`` for the first element. Error is an extension of the Element class. """ return self._tree.error @property def exception(self) -> ElementList[Exception]: """Sequence of all ``st.exception`` elements. Returns ------- ElementList of Exception Sequence of all ``st.exception`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.exception[0]`` for the first element. Exception is an extension of the Element class. """ return self._tree.exception @property def expander(self) -> Sequence[Expander]: """Sequence of all ``st.expander`` elements. Returns ------- Sequence of Expandable Sequence of all ``st.expander`` elements. Individual elements can be accessed from a Sequence by index (order on the page). For example, ``at.expander[0]`` for the first element. Expandable is an extension of the Block class. """ return self._tree.expander @property def header(self) -> ElementList[Header]: """Sequence of all ``st.header`` elements. Returns ------- ElementList of Header Sequence of all ``st.header`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.header[0]`` for the first element. Header is an extension of the Element class. """ return self._tree.header @property def info(self) -> ElementList[Info]: """Sequence of all ``st.info`` elements. Returns ------- ElementList of Info Sequence of all ``st.info`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.info[0]`` for the first element. Info is an extension of the Element class. """ return self._tree.info @property def json(self) -> ElementList[Json]: """Sequence of all ``st.json`` elements. Returns ------- ElementList of Json Sequence of all ``st.json`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.json[0]`` for the first element. Json is an extension of the Element class. """ return self._tree.json @property def latex(self) -> ElementList[Latex]: """Sequence of all ``st.latex`` elements. Returns ------- ElementList of Latex Sequence of all ``st.latex`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.latex[0]`` for the first element. Latex is an extension of the Element class. """ return self._tree.latex @property def markdown(self) -> ElementList[Markdown]: """Sequence of all ``st.markdown`` elements. Returns ------- ElementList of Markdown Sequence of all ``st.markdown`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.markdown[0]`` for the first element. Markdown is an extension of the Element class. """ return self._tree.markdown @property def metric(self) -> ElementList[Metric]: """Sequence of all ``st.metric`` elements. Returns ------- ElementList of Metric Sequence of all ``st.metric`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.metric[0]`` for the first element. Metric is an extension of the Element class. """ return self._tree.metric @property def multiselect(self) -> WidgetList[Multiselect[Any]]: """Sequence of all ``st.multiselect`` widgets. Returns ------- WidgetList of Multiselect Sequence of all ``st.multiselect`` widgets. Individual widgets can be accessed from a WidgetList by index (order on the page) or key. For example, ``at.multiselect[0]`` for the first widget or ``at.multiselect(key="my_key")`` for a widget with a given key. """ return self._tree.multiselect @property def number_input(self) -> WidgetList[NumberInput]: """Sequence of all ``st.number_input`` widgets. Returns ------- WidgetList of NumberInput Sequence of all ``st.number_input`` widgets. Individual widgets can be accessed from a WidgetList by index (order on the page) or key. For example, ``at.number_input[0]`` for the first widget or ``at.number_input(key="my_key")`` for a widget with a given key. """ return self._tree.number_input @property def radio(self) -> WidgetList[Radio[Any]]: """Sequence of all ``st.radio`` widgets. Returns ------- WidgetList of Radio Sequence of all ``st.radio`` widgets. Individual widgets can be accessed from a WidgetList by index (order on the page) or key. For example, ``at.radio[0]`` for the first widget or ``at.radio(key="my_key")`` for a widget with a given key. """ return self._tree.radio @property def select_slider(self) -> WidgetList[SelectSlider[Any]]: """Sequence of all ``st.select_slider`` widgets. Returns ------- WidgetList of SelectSlider Sequence of all ``st.select_slider`` widgets. Individual widgets can be accessed from a WidgetList by index (order on the page) or key. For example, ``at.select_slider[0]`` for the first widget or ``at.select_slider(key="my_key")`` for a widget with a given key. """ return self._tree.select_slider @property def selectbox(self) -> WidgetList[Selectbox[Any]]: """Sequence of all ``st.selectbox`` widgets. Returns ------- WidgetList of Selectbox Sequence of all ``st.selectbox`` widgets. Individual widgets can be accessed from a WidgetList by index (order on the page) or key. For example, ``at.selectbox[0]`` for the first widget or ``at.selectbox(key="my_key")`` for a widget with a given key. """ return self._tree.selectbox @property def slider(self) -> WidgetList[Slider[Any]]: """Sequence of all ``st.slider`` widgets. Returns ------- WidgetList of Slider Sequence of all ``st.slider`` widgets. Individual widgets can be accessed from a WidgetList by index (order on the page) or key. For example, ``at.slider[0]`` for the first widget or ``at.slider(key="my_key")`` for a widget with a given key. """ return self._tree.slider @property def subheader(self) -> ElementList[Subheader]: """Sequence of all ``st.subheader`` elements. Returns ------- ElementList of Subheader Sequence of all ``st.subheader`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.subheader[0]`` for the first element. Subheader is an extension of the Element class. """ return self._tree.subheader @property def success(self) -> ElementList[Success]: """Sequence of all ``st.success`` elements. Returns ------- ElementList of Success Sequence of all ``st.success`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.success[0]`` for the first element. Success is an extension of the Element class. """ return self._tree.success @property def status(self) -> Sequence[Status]: """Sequence of all ``st.status`` elements. Returns ------- Sequence of Status Sequence of all ``st.status`` elements. Individual elements can be accessed from a Sequence by index (order on the page). For example, ``at.status[0]`` for the first element. Status is an extension of the Block class. """ return self._tree.status @property def table(self) -> ElementList[Table]: """Sequence of all ``st.table`` elements. Returns ------- ElementList of Table Sequence of all ``st.table`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.table[0]`` for the first element. Table is an extension of the Element class. """ return self._tree.table @property def tabs(self) -> Sequence[Tab]: """Sequence of all tabs within ``st.tabs`` elements. Each tab within a single ``st.tabs`` will be returned as a separate Tab in the Sequence. Additionally, the tab labels are forwarded to each Tab element as a property. For example, ``st.tabs("A","B")`` will yield two Tab objects, with ``Tab.label`` returning "A" and "B", respectively. Returns ------- Sequence of Tab Sequence of all tabs within ``st.tabs`` elements. Individual tabs can be accessed from an ElementList by index (order on the page). For example, ``at.tabs[0]`` for the first tab. Tab is an extension of the Block class. """ return self._tree.tabs @property def text(self) -> ElementList[Text]: """Sequence of all ``st.text`` elements. Returns ------- ElementList of Text Sequence of all ``st.text`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.text[0]`` for the first element. Text is an extension of the Element class. """ return self._tree.text @property def text_area(self) -> WidgetList[TextArea]: """Sequence of all ``st.text_area`` widgets. Returns ------- WidgetList of TextArea Sequence of all ``st.text_area`` widgets. Individual widgets can be accessed from a WidgetList by index (order on the page) or key. For example, ``at.text_area[0]`` for the first widget or ``at.text_area(key="my_key")`` for a widget with a given key. """ return self._tree.text_area @property def text_input(self) -> WidgetList[TextInput]: """Sequence of all ``st.text_input`` widgets. Returns ------- WidgetList of TextInput Sequence of all ``st.text_input`` widgets. Individual widgets can be accessed from a WidgetList by index (order on the page) or key. For example, ``at.text_input[0]`` for the first widget or ``at.text_input(key="my_key")`` for a widget with a given key. """ return self._tree.text_input @property def time_input(self) -> WidgetList[TimeInput]: """Sequence of all ``st.time_input`` widgets. Returns ------- WidgetList of TimeInput Sequence of all ``st.time_input`` widgets. Individual widgets can be accessed from a WidgetList by index (order on the page) or key. For example, ``at.time_input[0]`` for the first widget or ``at.time_input(key="my_key")`` for a widget with a given key. """ return self._tree.time_input @property def title(self) -> ElementList[Title]: """Sequence of all ``st.title`` elements. Returns ------- ElementList of Title Sequence of all ``st.title`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.title[0]`` for the first element. Title is an extension of the Element class. """ return self._tree.title @property def toast(self) -> ElementList[Toast]: """Sequence of all ``st.toast`` elements. Returns ------- ElementList of Toast Sequence of all ``st.toast`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.toast[0]`` for the first element. Toast is an extension of the Element class. """ return self._tree.toast @property def toggle(self) -> WidgetList[Toggle]: """Sequence of all ``st.toggle`` widgets. Returns ------- WidgetList of Toggle Sequence of all ``st.toggle`` widgets. Individual widgets can be accessed from a WidgetList by index (order on the page) or key. For example, ``at.toggle[0]`` for the first widget or ``at.toggle(key="my_key")`` for a widget with a given key. """ return self._tree.toggle @property def warning(self) -> ElementList[Warning]: """Sequence of all ``st.warning`` elements. Returns ------- ElementList of Warning Sequence of all ``st.warning`` elements. Individual elements can be accessed from an ElementList by index (order on the page). For example, ``at.warning[0]`` for the first element. Warning is an extension of the Element class. """ return self._tree.warning def __len__(self) -> int: return len(self._tree) def __iter__(self) -> Iterator[Node]: yield from self._tree def __getitem__(self, idx: int) -> Node: return self._tree[idx] def get(self, element_type: str) -> Sequence[Node]: """Get elements or widgets of the specified type. This method returns the collection of all elements or widgets of the specified type on the current page. Retrieve a specific element by using its index (order on page) or key lookup. Parameters ---------- element_type: str An element attribute of ``AppTest``. For example, "button", "caption", or "chat_input". Returns ------- Sequence of Elements Sequence of elements of the given type. Individual elements can be accessed from a Sequence by index (order on the page). When getting and ``element_type`` that is a widget, individual widgets can be accessed by key. For example, ``at.get("text")[0]`` for the first ``st.text`` element or ``at.get("slider")(key="my_key")`` for the ``st.slider`` widget with a given key. """ return self._tree.get(element_type) def __repr__(self) -> str: return repr_(self)