diff --git a/CHANGELOG.md b/CHANGELOG.md index c01bbc04d..c012a65f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### New features +* Expose shiny.pytest, shiny.run and shiny.playwright modules that allow users to testing their Shiny apps. (#1448, #1456) + * Added busy indicators to provide users with a visual cue when the server is busy calculating outputs or otherwise serving requests to the client. More specifically, a spinner is shown on each calculating/recalculating output, and a pulsing banner is shown at the top of the page when the app is otherwise busy. Use the new `ui.busy_indicator.options()` function to customize the appearance of the busy indicators and `ui.busy_indicator.use()` to disable/enable them. (#918) * Added support for creating modules using Shiny Express syntax, and using modules in Shiny Express apps. (#1220) diff --git a/shiny/playwright/__init__.py b/shiny/playwright/__init__.py index 815c64b17..bc6e14e21 100644 --- a/shiny/playwright/__init__.py +++ b/shiny/playwright/__init__.py @@ -5,12 +5,24 @@ "The shiny.playwright module requires the playwright package to be installed." " Please install it with this command:" "\n\n pip install playwright" - "\n\n", - "If you are using pytest to test your code," - " you can install the pytest-playwright shim package with this command:", - "\n\n pip install pytest-playwright", ) +# If `pytest` is installed... +try: + import pytest # noqa: F401 # pyright: ignore[reportUnusedImport, reportMissingTypeStubs] + + # At this point, `playwright` and `pytest` are installed. + # Try to make sure `pytest-playwright` is installed + try: + import pytest_playwright # noqa: F401 # pyright: ignore[reportUnusedImport, reportMissingTypeStubs] + except ImportError: + raise ImportError( + "If you are using pytest to test your app," + " you can install the pytest-playwright shim package with this command:", + "\n\n pip install pytest-playwright", + ) +except ImportError: + pass from . import controls, expect __all__ = ["expect", "controls"] diff --git a/shiny/playwright/controls/_controls.py b/shiny/playwright/controls/_controls.py index b7032ea85..a86cd721b 100644 --- a/shiny/playwright/controls/_controls.py +++ b/shiny/playwright/controls/_controls.py @@ -111,6 +111,20 @@ def set_text( delay: OptionalFloat = None, timeout: Timeout = None, ) -> None: + """ + Sets the text of a DOM element. + + Parameters + ---------- + loc + Playwright `Locator` of the element. + text + The text to set. + delay + The delay between key presses in milliseconds. Defaults to `None`. + timeout + The maximum time to wait for the text to be set. Defaults to `None`. + """ # TODO-future; Composable set() method loc.fill("", timeout=timeout) # Reset the value loc.type(text, delay=delay, timeout=timeout) # Type the value @@ -126,21 +140,37 @@ def _expect_multiple(loc: Locator, multiple: bool, timeout: Timeout = None) -> N ###################################################### -class _InputBaseP(Protocol): +class _UiBaseP(Protocol): id: str loc: Locator page: Page -class _InputWithContainerP(_InputBaseP, Protocol): +class _UiWithContainerP(_UiBaseP, Protocol): + """A protocol class representing UI with a container.""" + loc_container: Locator + """ + Playwright `Locator` for the container of the UI element. + """ + +class _UiBase: + """A base class representing shiny UI components.""" -class _InputBase: # timeout: Timeout id: str + """ + The browser DOM `id` of the UI element. + """ loc: Locator + """ + Playwright `Locator` of the UI element. + """ page: Page + """ + Playwright `Page` of the Shiny app. + """ def __init__( self, @@ -159,17 +189,19 @@ def __init__( @property # TODO; Can not publicly find `LocatorAssertions` in `playwright` def expect(self): + """Expectation method equivalent to `playwright.expect(self.loc)`.""" + # TODO-karan-test: Search for `.loc)` and convert `expect(FOO.loc)` to `FOO.expect`. If we don't like the helper API, we should remove it. return playwright_expect(self.loc) -class _InputWithContainer(_InputBase): +class _UiWithContainer(_UiBase): """ - A mixin class representing inputs with a container. + A mixin class representing UI with a container. """ loc_container: Locator """ - `loc_container` is the locator of the container of the input. + Playwright `Locator` for the container of the UI element. """ def __init__( @@ -186,13 +218,13 @@ def __init__( Parameters ---------- page - The page where the input is located. + Playwright `Page` of the Shiny app. id - The id of the input. + The id of the UI element. loc - The locator of the input. + Playwright `Locator` of the UI element. loc_container - The locator of the container of the input. + Playwright `Locator` of the container of the UI element. """ loc_is_str = isinstance(loc, str) loc_container_is_str = isinstance(loc_container, str) @@ -225,7 +257,14 @@ def __init__( self.loc_container = loc_container -class _InputWithLabel(_InputWithContainer): +class _UiWithLabel(_UiWithContainer): + """A mixin class representing UI components with a label.""" + + loc_label: Locator + """ + Playwright `Locator` for the label of the UI element. + """ + def __init__( self, page: Page, @@ -235,6 +274,22 @@ def __init__( loc_container: InitLocator = "div.shiny-input-container", loc_label: InitLocator | None = None, ) -> None: + """ + Initializes the input with a label. + + Parameters + ---------- + page + The page where the input is located. + id + The id of the UI element. + loc + Playwright `Locator` of the UI element. + loc_container + Playwright `Locator` of the container of the UI element. + loc_label + Playwright `Locator` of the label of the UI element. Defaults to `None`. + """ super().__init__( page, id=id, @@ -254,31 +309,41 @@ def expect_label( *, timeout: Timeout = None, ) -> None: + """ + Expect the label of the input to have a specific text. + + Parameters + ---------- + value + The expected text value of the label. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ playwright_expect(self.loc_label).to_have_text(value, timeout=timeout) class _WidthLocM: """ - A mixin class representing the `loc`'s width. - This class provides methods to expect the width attribute of an element. + A mixin class representing the `.loc`'s width. + + This class provides methods to expect the width attribute of a DOM element. """ def expect_width( - self: _InputBaseP, + self: _UiBaseP, value: AttrValue, *, timeout: Timeout = None, ) -> None: """ - Expect the width attribute of an element to have a specific value. + Expect the `width` attribute of a DOM element to have a specific value. Parameters ---------- value - The expected value of the width attribute. + The expected value of the `width` attribute. timeout - The maximum time to wait for the expectation to be fulfilled. - Defaults to None. + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. """ expect_attribute_to_have_value(self.loc, "width", value=value, timeout=timeout) @@ -286,25 +351,25 @@ def expect_width( class _WidthContainerM: """ A mixin class representing the container's width. - This class provides methods to expect the width attribute of an element's container. + + This class provides methods to expect the width attribute of a DOM element's container. """ def expect_width( - self: _InputWithContainerP, + self: _UiWithContainerP, value: AttrValue, *, timeout: Timeout = None, ) -> None: """ - Expect the width attribute of an element's container to have a specific value. + Expect the `width` attribute of a input's container to have a specific value. Parameters ---------- value - The expected value of the width attribute. + The expected value of the `width` attribute. timeout - The maximum time to wait for the expectation to be fulfilled. - Defaults to None. + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. """ expect_attribute_to_have_value( self.loc_container, "width", value=value, timeout=timeout @@ -312,17 +377,39 @@ def expect_width( class _SetTextM: - def set(self: _InputBaseP, value: str, *, timeout: Timeout = None) -> None: + def set(self: _UiBaseP, value: str, *, timeout: Timeout = None) -> None: + """ + Sets the text value + + Parameters + ---------- + value + The text to set. + timeout + The maximum time to wait for the text to be set. Defaults to `None`. + """ set_text(self.loc, value, timeout=timeout) class _ExpectTextInputValueM: + """A mixin class for text input values.""" + def expect_value( - self: _InputBaseP, + self: _UiBaseP, value: PatternOrStr, *, timeout: Timeout = None, ) -> None: + """ + Expect the value of the text input to have a specific value. + + Parameters + ---------- + value + The expected value of the text input. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ playwright_expect(self.loc).to_have_value(value, timeout=timeout) @@ -330,17 +417,21 @@ class InputNumeric( _SetTextM, _ExpectTextInputValueM, _WidthLocM, - _InputWithLabel, + _UiWithLabel, ): - # id: str, - # label: TagChild, - # value: float, - # *, - # min: Optional[float] = None, - # max: Optional[float] = None, - # step: Optional[float] = None, - # width: Optional[str] = None, + """Input numeric control for :func:`~shiny.ui.input_numeric`.""" + def __init__(self, page: Page, id: str) -> None: + """ + Initializes the input numeric Playwright control. + + Parameters + ---------- + page + The page where the input numeric is located. + id + The id of the input numeric. + """ super().__init__( page, id=id, @@ -353,6 +444,16 @@ def expect_min( *, timeout: Timeout = None, ) -> None: + """ + Expect the minimum numeric value to be a specific value. + + Parameters + ---------- + value + The expected minimum numeric value. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ expect_attribute_to_have_value(self.loc, "min", value=value, timeout=timeout) def expect_max( @@ -361,6 +462,16 @@ def expect_max( *, timeout: Timeout = None, ) -> None: + """ + Expect the maximum numeric value to be a specific value. + + Parameters + ---------- + value + The expected maximum numeric value. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ expect_attribute_to_have_value(self.loc, "max", value=value, timeout=timeout) def expect_step( @@ -369,16 +480,40 @@ def expect_step( *, timeout: Timeout = None, ) -> None: + """ + Expect step value when incrementing/decrementing the numeric input. + + Parameters + ---------- + value + The expected step value for the numeric input. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ expect_attribute_to_have_value(self.loc, "step", value=value, timeout=timeout) class _ExpectSpellcheckAttrM: + """ + A mixin class for the spellcheck attribute. + """ + def expect_spellcheck( - self: _InputBaseP, + self: _UiBaseP, value: Literal["true", "false"] | None, *, timeout: Timeout = None, ) -> None: + """ + Expect the `spellcheck` attribute of the input to have a specific value. + + Parameters + ---------- + value + The expected value of the `spellcheck` attribute. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ # self.spellcheck.expect_to_have_value(value, timeout=timeout) expect_attribute_to_have_value( self.loc, "spellcheck", value=value, timeout=timeout @@ -387,11 +522,21 @@ def expect_spellcheck( class _ExpectPlaceholderAttrM: def expect_placeholder( - self: _InputBaseP, + self: _UiBaseP, value: AttrValue, *, timeout: Timeout = None, ) -> None: + """ + Expect the `placeholder` attribute of the input to have a specific value. + + Parameters + ---------- + value + The expected value of the `placeholder` attribute. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "placeholder", value=value, timeout=timeout ) @@ -399,11 +544,21 @@ def expect_placeholder( class _ExpectAutocompleteAttrM: def expect_autocomplete( - self: _InputBaseP, + self: _UiBaseP, value: AttrValue, *, timeout: Timeout = None, ) -> None: + """ + Expect the `autocomplete` attribute of the input to have a specific value. + + Parameters + ---------- + value + The expected value of the `autocomplete` attribute. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "autocomplete", value=value, timeout=timeout ) @@ -416,17 +571,21 @@ class InputText( _ExpectPlaceholderAttrM, _ExpectAutocompleteAttrM, _ExpectSpellcheckAttrM, - _InputWithLabel, + _UiWithLabel, ): - # id: str, - # label: TagChild, - # value: str = "", - # *, - # width: Optional[str] = None, - # placeholder: Optional[str] = None, - # autocomplete: Optional[str] = "off", - # spellcheck: Optional[Literal["true", "false"]] = None, + """Input text control for :func:`~shiny.ui.input_text`.""" + def __init__(self, page: Page, id: str) -> None: + """ + Initializes the input text. + + Parameters + ---------- + page + The page where the input text is located. + id + The id of the input text. + """ super().__init__( page, id=id, @@ -438,17 +597,21 @@ class InputPassword( _SetTextM, _ExpectTextInputValueM, _ExpectPlaceholderAttrM, - _InputWithLabel, + _UiWithLabel, ): - # id: str, - # label: TagChild, - # value: str = "", - # *, - # width: Optional[str] = None, - # placeholder: Optional[str] = None, - ... + """Input password control for :func:`~shiny.ui.input_password`.""" def __init__(self, page: Page, id: str) -> None: + """ + Initializes the input password. + + Parameters + ---------- + page + The page where the input password is located. + id + The id of the input password. + """ super().__init__( page, id=id, @@ -463,6 +626,16 @@ def expect_width( *, timeout: Timeout = None, ) -> None: + """ + Expect the `width` attribute of the input password to have a specific value. + + Parameters + ---------- + value + The expected value of the `width` attribute. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ expect_to_have_style(self.loc_container, "width", value, timeout=timeout) @@ -475,22 +648,21 @@ class InputTextArea( _ExpectPlaceholderAttrM, _ExpectAutocompleteAttrM, _ExpectSpellcheckAttrM, - _InputWithLabel, + _UiWithLabel, ): - # id: str, - # label: TagChild, - # value: str = "", - # width: Optional[str] = None, - # height: Optional[str] = None, - # cols: Optional[int] = None, - # rows: Optional[int] = None, - # placeholder: Optional[str] = None, - # resize: Optional[ - # Literal["none", "both", "horizontal", "vertical"] - # ] = None, - # autocomplete: Optional[str] = None, - # spellcheck: Optional[Literal["true", "false"]] = None, + """Input text area control for :func:`~shiny.ui.input_text_area`.""" + def __init__(self, page: Page, id: str) -> None: + """ + Initializes the input text area. + + Parameters + ---------- + page + The page where the input text area is located. + id + The id of the input text area. + """ super().__init__( page, id=id, @@ -498,6 +670,16 @@ def __init__(self, page: Page, id: str) -> None: ) def expect_width(self, value: StyleValue, *, timeout: Timeout = None) -> None: + """ + Expect the `width` attribute of the input text area to have a specific value. + + Parameters + ---------- + value + The expected value of the `width` attribute. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ if value is None: expect_to_have_style(self.loc_container, "width", None, timeout=timeout) expect_to_have_style(self.loc, "width", "100%", timeout=timeout) @@ -506,12 +688,42 @@ def expect_width(self, value: StyleValue, *, timeout: Timeout = None) -> None: expect_to_have_style(self.loc, "width", None, timeout=timeout) def expect_height(self, value: StyleValue, *, timeout: Timeout = None) -> None: + """ + Expect the `height` attribute of the input text area to have a specific value. + + Parameters + ---------- + value + The expected value of the `height` attribute. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ expect_to_have_style(self.loc, "height", value, timeout=timeout) def expect_cols(self, value: AttrValue, *, timeout: Timeout = None) -> None: + """ + Expect the `cols` attribute of the input text area to have a specific value. + + Parameters + ---------- + value + The expected value of the `cols` attribute. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ expect_attribute_to_have_value(self.loc, "cols", value=value, timeout=timeout) def expect_rows(self, value: AttrValue, *, timeout: Timeout = None) -> None: + """ + Expect the `rows` attribute of the input text area to have a specific value. + + Parameters + ---------- + value + The expected value of the `rows` attribute. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ expect_attribute_to_have_value(self.loc, "rows", value=value, timeout=timeout) def expect_resize( @@ -520,6 +732,16 @@ def expect_resize( *, timeout: Timeout = None, ) -> None: + """ + Expect the `resize` attribute of the input text area to have a specific value. + + Parameters + ---------- + value + The expected value of the `resize` attribute. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ expect_attribute_to_have_value(self.loc, "resize", value=value, timeout=timeout) def expect_autoresize( @@ -528,6 +750,16 @@ def expect_autoresize( *, timeout: Timeout = None, ) -> None: + """ + Expect the `autoresize` attribute of the input text area to have a specific value. + + Parameters + ---------- + value + The expected value of the `autoresize` attribute. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ _expect_class_value( self.loc, "textarea-autoresize", @@ -538,11 +770,20 @@ def expect_autoresize( class _InputSelectBase( _WidthLocM, - _InputWithLabel, + _UiWithLabel, ): loc_selected: Locator + """ + Playwright `Locator` for the selected option of the input select. + """ loc_choices: Locator + """ + Playwright `Locator` for the choices of the input select. + """ loc_choice_groups: Locator + """ + Playwright `Locator` for the choice groups of the input select. + """ def __init__( self, @@ -551,6 +792,18 @@ def __init__( *, select_class: str = "", ) -> None: + """ + Initializes the input select. + + Parameters + ---------- + page + The page where the input select is located. + id + The id of the input select. + select_class + The class of the select element. Defaults to "". + """ super().__init__( page, id=id, @@ -566,6 +819,16 @@ def set( *, timeout: Timeout = None, ) -> None: + """ + Sets the selected option(s) of the input select. + + Parameters + ---------- + selected + The value(s) of the selected option(s). + timeout + The maximum time to wait for the selection to be set. Defaults to `None`. + """ if isinstance(selected, str): selected = [selected] self.loc.select_option(value=selected, timeout=timeout) @@ -577,7 +840,17 @@ def expect_choices( *, timeout: Timeout = None, ) -> None: - """Expect choices to be in order""" + """ + Expect the available options of the input select to be an exact match. + + Parameters + ---------- + choices + The expected choices of the input select. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ + # Playwright doesn't like lists of size 0. Instead, check for empty locator if len(choices) == 0: playwright_expect(self.loc_choices).to_have_count(0, timeout=timeout) @@ -594,20 +867,29 @@ def expect_choices( def expect_selected( self, - selected: PatternOrStr | ListPatternOrStr, + value: PatternOrStr | ListPatternOrStr, *, timeout: Timeout = None, ) -> None: - """Expect choices to be in order""" + """ + Expect the selected option(s) of the input select to be an exact match. + + Parameters + ---------- + value + The expected value(s) of the selected option(s). + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ # Playwright doesn't like lists of size 0 - if isinstance(selected, list) and len(selected) == 0: + if isinstance(value, list) and len(value) == 0: playwright_expect(self.loc_selected).to_have_count(0, timeout=timeout) return - if isinstance(selected, list): - self.expect.to_have_values(selected, timeout=timeout) + if isinstance(value, list): + self.expect.to_have_values(value, timeout=timeout) else: - self.expect.to_have_value(selected, timeout=timeout) + self.expect.to_have_value(value, timeout=timeout) # _MultipleDomItems.expect_locator_values_in_list( # page=self.page, @@ -626,7 +908,17 @@ def expect_choice_groups( *, timeout: Timeout = None, ) -> None: - """Expect choices to be in order""" + """ + Expect the choice groups of the input select to be an exact match. + + Parameters + ---------- + choice_groups + The expected choice groups of the input select. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ + # Playwright doesn't like lists of size 0. Instead, use `None` if len(choice_groups) == 0: playwright_expect(self.loc_choice_groups).to_have_count(0, timeout=timeout) @@ -644,21 +936,51 @@ def expect_choice_groups( def expect_choice_labels( self, - choice_labels: ListPatternOrStr, + value: ListPatternOrStr, *, timeout: Timeout = None, ) -> None: + """ + Expect the choice labels of the input select to be an exact match. + + Parameters + ---------- + value + The expected choice labels of the input select. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ # Playwright doesn't like lists of size 0. Instead, use `None` - if len(choice_labels) == 0: + if len(value) == 0: playwright_expect(self.loc_choices).to_have_count(0, timeout=timeout) return - playwright_expect(self.loc_choices).to_have_text(choice_labels, timeout=timeout) + playwright_expect(self.loc_choices).to_have_text(value, timeout=timeout) # multiple: bool = False, - def expect_multiple(self, multiple: bool, *, timeout: Timeout = None) -> None: - _expect_multiple(self.loc, multiple, timeout=timeout) + def expect_multiple(self, value: bool, *, timeout: Timeout = None) -> None: + """ + Expect the input select to allow multiple selections. + + Parameters + ---------- + value + Whether the input select allows multiple selections. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ + _expect_multiple(self.loc, value, timeout=timeout) def expect_size(self, value: AttrValue, *, timeout: Timeout = None) -> None: + """ + Expect the size attribute of the input select to have a specific value. + + Parameters + ---------- + value + The expected value of the `size` attribute. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "size", @@ -668,15 +990,19 @@ def expect_size(self, value: AttrValue, *, timeout: Timeout = None) -> None: class InputSelect(_InputSelectBase): - # id: str, - # label: TagChild, - # choices: SelectChoicesArg, - # selected: Optional[Union[str, list[str]]] = None, - # multiple: bool = False, - # selectize: bool = False, - # width: Optional[str] = None, - # size: Optional[str] = None, + """Input select control for :func:`~shiny.ui.input_select`.""" + def __init__(self, page: Page, id: str) -> None: + """ + Initializes the input select. + + Parameters + ---------- + page + The page where the input select is located. + id + The id of the input select. + """ super().__init__( page, id=id, @@ -684,17 +1010,29 @@ def __init__(self, page: Page, id: str) -> None: ) # selectize: bool = False, - def expect_selectize(self, selectize: bool, *, timeout: Timeout = None) -> None: + def expect_selectize(self, value: bool, *, timeout: Timeout = None) -> None: + """ + Expect the input select to be selectize. + + Parameters + ---------- + value + Whether the input select is selectize. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ # class_=None if selectize else "form-select", _expect_class_value( self.loc, "form-select", - has_class=not selectize, + has_class=not value, timeout=timeout, ) class InputSelectize(_InputSelectBase): + """Input selectize control for :func:`~shiny.ui.input_selectize`.""" + def __init__(self, page: Page, id: str) -> None: super().__init__( page, @@ -703,18 +1041,37 @@ def __init__(self, page: Page, id: str) -> None: ) -class _InputActionBase(_InputBase): +class _InputActionBase(_UiBase): def expect_label( self, value: PatternOrStr, *, timeout: Timeout = None, ) -> None: - """Must include icon if present""" + """ + Expect the label of the input button to have a specific value. + + Note: This must include the icon if it is present! + + Parameters + ---------- + value + The expected value of the label. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ self.expect.to_have_text(value, timeout=timeout) def click(self, *, timeout: Timeout = None, **kwargs: object) -> None: + """ + Clicks the input action. + + Parameters + ---------- + timeout + The maximum time to wait for the input action to be clicked. Defaults to `None`. + """ self.loc.click(timeout=timeout, **kwargs) # pyright: ignore[reportArgumentType] @@ -722,15 +1079,23 @@ class InputActionButton( _WidthLocM, _InputActionBase, ): - # label: TagChild, - # icon: TagChild = None, - # width: Optional[str] = None, + """Input action button control for :func:`~shiny.ui.input_action_button`.""" def __init__( self, page: Page, id: str, ) -> None: + """ + Initializes the input action button. + + Parameters + ---------- + page + The page where the input action button is located. + id + The id of the input action button. + """ super().__init__( page, id=id, @@ -738,12 +1103,24 @@ def __init__( ) -class InputDarkMode(_InputBase): +class InputDarkMode(_UiBase): + """Input dark mode control for :func:`~shiny.ui.input_dark_mode`.""" + def __init__( self, page: Page, id: Optional[str] | None, ) -> None: + """ + Initializes the input dark mode. + + Parameters + ---------- + page + The page where the input dark mode is located. + id + The id of the input dark mode. + """ id_selector = "" if id is None else f"#{id}" super().__init__( @@ -753,21 +1130,59 @@ def __init__( ) def click(self, *, timeout: Timeout = None): + """ + Clicks the input dark mode. + + Parameters + ---------- + timeout + The maximum time to wait for the input dark mode to be clicked. Defaults to `None`. + """ self.loc.click(timeout=timeout) return self def expect_mode(self, value: str, *, timeout: Timeout = None): + """ + Expect the `mode` attribute of the input dark mode to have a specific value. + + Parameters + ---------- + value + The expected value of the `mode` attribute. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ expect_attribute_to_have_value(self.loc, "mode", value=value, timeout=timeout) self.expect_page_mode(value, timeout=timeout) return self def expect_page_mode(self, value: str, *, timeout: Timeout = None): + """ + Expect the page to have a specific dark mode value. + + Parameters + ---------- + value + The expected value of the page's dark mode. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ expect_attribute_to_have_value( self.page.locator("html"), "data-bs-theme", value=value, timeout=timeout ) return self def expect_wc_attribute(self, value: str, *, timeout: Timeout = None): + """ + Expect the `wc` attribute of the input dark mode to have a specific value. + + Parameters + ---------- + value + The expected value of the `wc` attribute. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "attribute", value=value, timeout=timeout ) @@ -778,22 +1193,24 @@ class InputTaskButton( _WidthLocM, _InputActionBase, ): + """Input task button control for :func:`~shiny.ui.input_task_button`.""" + # TODO-Karan: Test auto_reset functionality - # id: str, - # label: TagChild, - # *args: TagChild, - # icon: TagChild = None, - # label_busy: TagChild = "Processing...", - # icon_busy: TagChild | MISSING_TYPE = MISSING, - # width: Optional[str] = None, - # type: Optional[str] = "primary", - # auto_reset: bool = True, - # **kwargs: TagAttrValue, def __init__( self, page: Page, id: str, ) -> None: + """ + Initializes the input task button. + + Parameters + ---------- + page + The page where the input task button is located. + id + The id of the input task button. + """ super().__init__( page, id=id, @@ -801,8 +1218,21 @@ def __init__( ) def expect_state( - self, value: Literal["ready", "busy"] | str, *, timeout: Timeout = None + self, + value: Literal["ready", "busy"] | str, + *, + timeout: Timeout = None, ): + """ + Expect the state of the input task button to have a specific value. + + Parameters + ---------- + value + The expected value of the state of the input task button. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc.locator("> bslib-switch-inline"), name="case", @@ -811,22 +1241,78 @@ def expect_state( ) def expect_label(self, value: PatternOrStr, *, timeout: Timeout = None) -> None: + """ + Expect the label of the input task button to have a specific value. + + Parameters + ---------- + value + The expected value of the label. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ self.expect_label_ready(value, timeout=timeout) def expect_label_ready(self, value: PatternOrStr, *, timeout: Timeout = None): + """ + Expect the label of a ready input task button to have a specific value. + + Parameters + ---------- + value + The expected value of the label. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ self.expect_label_state("ready", value, timeout=timeout) def expect_label_busy(self, value: PatternOrStr, *, timeout: Timeout = None): + """ + Expect the label of a busy input task button to have a specific value. + + Parameters + ---------- + value + The expected value of the label. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ self.expect_label_state("busy", value, timeout=timeout) def expect_label_state( - self, state: str, value: PatternOrStr, *, timeout: Timeout = None + self, + state: str, + value: PatternOrStr, + *, + timeout: Timeout = None, ): + """ + Expect the label of the input task button to have a specific value in a specific state. + + Parameters + ---------- + state + The state of the input task button. + value + The expected value of the label. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ playwright_expect( self.loc.locator(f"> bslib-switch-inline > span[slot='{state}']") ).to_have_text(value, timeout=timeout) def expect_auto_reset(self, value: bool, timeout: Timeout = None): + """ + Expect the `auto-reset` attribute of the input task button to have a specific value. + + Parameters + ---------- + value + The expected value of the `auto-reset` attribute. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, name="data-auto-reset", @@ -836,14 +1322,23 @@ def expect_auto_reset(self, value: bool, timeout: Timeout = None): class InputActionLink(_InputActionBase): - # label: TagChild, - # icon: TagChild = None, + """Input action link control for :func:`~shiny.ui.input_action_link`.""" def __init__( self, page: Page, id: str, ) -> None: + """ + Initializes the input action link. + + Parameters + ---------- + page + The page where the input action link is located. + id + The id of the input action link. + """ super().__init__( page, id=id, @@ -858,14 +1353,25 @@ def __init__( class _InputCheckboxBase( _WidthContainerM, - _InputWithLabel, + _UiWithLabel, ): - # label: TagChild - # value: bool = False - # width: Optional[str] = None def __init__( self, page: Page, id: str, loc: InitLocator, loc_label: str | None ) -> None: + """ + Initializes the input checkbox. + + Parameters + ---------- + page + The page where the input checkbox is located. + id + The id of the input checkbox. + loc + Playwright `Locator` of the input checkbox. + loc_label + Playwright `Locator` of the label of the input checkbox. + """ super().__init__( page, id=id, @@ -874,6 +1380,16 @@ def __init__( ) def set(self, value: bool, *, timeout: Timeout = None, **kwargs: object) -> None: + """ + Sets the input checkbox. + + Parameters + ---------- + value + The value of the input checkbox. + timeout + The maximum time to wait for the input checkbox to be set. Defaults to `None`. + """ self.loc.wait_for(state="visible", timeout=timeout) self.loc.scroll_into_view_if_needed(timeout=timeout) self.loc.set_checked( @@ -881,11 +1397,29 @@ def set(self, value: bool, *, timeout: Timeout = None, **kwargs: object) -> None ) def toggle(self, *, timeout: Timeout = None, **kwargs: object) -> None: + """ + Toggles the input checkbox. + + Parameters + ---------- + timeout + The maximum time to wait for the input checkbox to be toggled. Defaults to `None`. + """ self.loc.wait_for(state="visible", timeout=timeout) self.loc.scroll_into_view_if_needed(timeout=timeout) self.loc.click(timeout=timeout, **kwargs) # pyright: ignore[reportArgumentType] def expect_checked(self, value: bool, *, timeout: Timeout = None) -> None: + """ + Expect the input checkbox to be checked. + + Parameters + ---------- + value + Whether the input checkbox is checked. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ if value: self.expect.to_be_checked(timeout=timeout) else: @@ -893,11 +1427,23 @@ def expect_checked(self, value: bool, *, timeout: Timeout = None) -> None: class InputCheckbox(_InputCheckboxBase): + """Input checkbox control for :func:`~shiny.ui.input_checkbox`.""" + def __init__( self, page: Page, id: str, ) -> None: + """ + Initializes the input checkbox. + + Parameters + ---------- + page + The page where the input checkbox is located. + id + The id of the input checkbox. + """ super().__init__( page, id=id, @@ -907,11 +1453,23 @@ def __init__( class InputSwitch(_InputCheckboxBase): + """Input switch control for :func:`~shiny.ui.input_switch`.""" + def __init__( self, page: Page, id: str, ) -> None: + """ + Initializes the input switch. + + Parameters + ---------- + page + The page where the input switch is located. + id + The id of the input switch. + """ super().__init__( page, id=id, @@ -926,12 +1484,30 @@ def assert_arr_is_unique( arr: ListPatternOrStr, msg: str, ) -> None: + """ + Assert that the array is unique. + + Parameters + ---------- + arr + The array to check. + msg + The error message. + """ assert len(arr) == len(list(dict.fromkeys(arr))), msg @staticmethod def checked_css_str( is_checked: bool | MISSING_TYPE = MISSING, ) -> str: + """ + Get the CSS string for checked elements. + + Parameters + ---------- + is_checked + Whether the elements are checked. Defaults to `MISSING`. + """ if is_missing(is_checked): return "" if is_checked: @@ -952,6 +1528,31 @@ def expect_locator_contains_values_in_list( timeout: Timeout = None, key: str = "value", ) -> None: + """ + Expect the locator to contain the values in the list. + + The matching values must exist and be in order, but other values may also exist + within the container. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + loc_container + The container locator. + el_type + The element type. + arr_name + The variable name. + arr + The expected values. + is_checked + Whether the elements are checked. Defaults to `MISSING`. + timeout + The timeout for the expectation. Defaults to `None`. + key + The key. Defaults to `"value"`. + """ # Make sure the locator contains all of `arr` assert_type(arr, typing.List[str]) @@ -1015,6 +1616,31 @@ def expect_locator_values_in_list( timeout: Timeout = None, key: str = "value", ) -> None: + """ + Expect the locator to contain the values in the list. + + The matching values must exist and be in order. No other matching values will be + allowed within the container. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + loc_container + The container locator. + el_type + The element type locator. + arr_name + The array name. + arr + The expected values. + is_checked + Whether the elements are checked. Defaults to `MISSING`. + timeout + The timeout for the expectation. Defaults to `None`. + key + The key. Defaults to `"value"`. + """ # Make sure the locator has exactly `arr` values # Make sure the locator has len(uniq_arr) input elements @@ -1083,29 +1709,52 @@ def expect_locator_values_in_list( raise e -class _RadioButtonCheckboxGroupBase(_InputWithLabel): +# TODO-barret; continue from here + + +class _RadioButtonCheckboxGroupBase(_UiWithLabel): loc_choice_labels: Locator def expect_choice_labels( self, - labels: ListPatternOrStr, + value: ListPatternOrStr, *, timeout: Timeout = None, ) -> None: - if len(labels) == 1: - labels_val = labels[0] + """ + Expect the labels of the choices. + + Parameters + ---------- + value + The expected labels. + timeout + The timeout for the expectation. Defaults to `None`. + """ + if len(value) == 1: + labels_val = value[0] else: - labels_val = labels + labels_val = value playwright_expect(self.loc_choice_labels).to_have_text( labels_val, timeout=timeout, ) - def expect_inline(self, inline: bool, *, timeout: Timeout = None) -> None: + def expect_inline(self, value: bool, *, timeout: Timeout = None) -> None: + """ + Expect the input to be inline. + + Parameters + ---------- + value + Whether the input is inline. + timeout + The timeout for the expectation. Defaults to `None`. + """ _expect_class_value( self.loc_container, "shiny-input-container-inline", - has_class=inline, + has_class=value, timeout=timeout, ) @@ -1124,6 +1773,16 @@ def __init__( page: Page, id: str, ) -> None: + """ + Initialize the InputCheckboxGroup. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The id of the checkbox group. + """ super().__init__( page, id=id, @@ -1159,6 +1818,16 @@ def set( timeout: Timeout = None, **kwargs: object, ) -> None: + """ + Set the selected checkboxes. + + Parameters + ---------- + selected + The values of the selected checkboxes. + timeout + The timeout for the action. Defaults to `None`. + """ # Having an arr of size 0 is allowed. Will uncheck everything assert_type(selected, typing.List[str]) @@ -1197,27 +1866,47 @@ def in_selected(value: str) -> bool: def expect_choices( self, - choices: ListPatternOrStr, + value: ListPatternOrStr, *, timeout: Timeout = None, ) -> None: + """ + Expect the checkbox choices. + + Parameters + ---------- + value + The expected choices. + timeout + The timeout for the expectation. Defaults to `None`. + """ _MultipleDomItems.expect_locator_values_in_list( page=self.page, loc_container=self.loc_container, el_type="input[type=checkbox]", arr_name="choices", - arr=choices, + arr=value, timeout=timeout, ) def expect_selected( self, - selected: ListPatternOrStr, + value: ListPatternOrStr, *, timeout: Timeout = None, ) -> None: + """ + Expect the selected checkboxes. + + Parameters + ---------- + value + The expected values of the selected checkboxes. + timeout + The timeout for the expectation. Defaults to `None`. + """ # Playwright doesn't like lists of size 0 - if len(selected) == 0: + if len(value) == 0: playwright_expect(self.loc_selected).to_have_count(0, timeout=timeout) return @@ -1226,7 +1915,7 @@ def expect_selected( loc_container=self.loc_container, el_type="input[type=checkbox]", arr_name="selected", - arr=selected, + arr=value, timeout=timeout, is_checked=True, ) @@ -1236,17 +1925,35 @@ class InputRadioButtons( _WidthContainerM, _RadioButtonCheckboxGroupBase, ): - # id: str, - # label: TagChild, - # choices: ChoicesArg, - # selected: Optional[str] = None, - # inline: bool = False, - # width: Optional[str] = None, + """Input radio buttons control for :func:`~shiny.ui.input_radio_buttons`.""" + + loc_selected: Locator + """ + Playwright `Locator` of the selected radio button. + """ + loc_choices: Locator + """ + Playwright `Locator` of the radio button choices. + """ + loc_choice_labels: Locator + """ + Playwright `Locator` of the labels of the radio button choices. + """ + def __init__( self, page: Page, id: str, ) -> None: + """Initialize the InputRadioButtons. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The id of the radio buttons. + """ super().__init__( page, id=id, @@ -1281,6 +1988,16 @@ def set( timeout: Timeout = None, **kwargs: object, ) -> None: + """ + Set the selected radio button. + + Parameters + ---------- + selected + The value of the selected radio button. + timeout + The timeout for the action. Defaults to `None`. + """ assert_type(selected, str) # Only need to set. # The Browser will _unset_ the previously selected radio button @@ -1290,40 +2007,71 @@ def set( def expect_choices( self, - choices: ListPatternOrStr, + value: ListPatternOrStr, *, timeout: Timeout = None, ) -> None: + """ + Expect the radio button choices. + + Parameters + ---------- + value + The expected choices. + timeout + The timeout for the expectation. Defaults to `None`. + """ _MultipleDomItems.expect_locator_values_in_list( page=self.page, loc_container=self.loc_container, el_type="input[type=radio]", arr_name="choices", - arr=choices, + arr=value, timeout=timeout, ) def expect_selected( self, - selected: PatternOrStr | None, + value: PatternOrStr | None, *, timeout: Timeout = None, ) -> None: + """ + Expect the selected radio button. + + Parameters + ---------- + value + The expected value of the selected radio button. + timeout + The timeout for the expectation. Defaults to `None`. + """ # Playwright doesn't like lists of size 0. Instead, use `None` - if selected is None: + if value is None: playwright_expect(self.loc_selected).to_have_count(0, timeout=timeout) return - playwright_expect(self.loc_selected).to_have_value(selected, timeout=timeout) + playwright_expect(self.loc_selected).to_have_value(value, timeout=timeout) class InputFile( # _ExpectPlaceholderAttrM, - _InputWithLabel, + _UiWithLabel, ): + """Input file control for :func:`~shiny.ui.input_file`.""" + loc_button: Locator + """ + Playwright `Locator` of the button. + """ loc_file_display: Locator + """ + Playwright `Locator` of the file display. + """ loc_progress: Locator + """ + Playwright `Locator` of the progress bar. + """ # id: str, # label: TagChild, @@ -1343,6 +2091,16 @@ def __init__( page: Page, id: str, ) -> None: + """ + Initialize the InputFile. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The id of the file input. + """ super().__init__( page, id=id, @@ -1365,6 +2123,18 @@ def set( timeout: Timeout = None, expect_complete_timeout: Timeout = 30 * 1000, ) -> None: + """ + Set the file upload. + + Parameters + ---------- + file_path + The path to the file to upload. + timeout + The timeout for the action. Defaults to `None`. + expect_complete_timeout + The timeout for the expectation that the upload is complete. Defaults to `30 * 1000`. + """ self.loc.wait_for(state="visible", timeout=timeout) self.loc.scroll_into_view_if_needed(timeout=timeout) self.loc.set_input_files(file_path, timeout=timeout) @@ -1377,40 +2147,98 @@ def expect_complete( *, timeout: Timeout = None, ) -> None: + """ + Expect the file upload to be complete. + + Parameters + ---------- + timeout + The timeout for the expectation. Defaults to `None`. + """ expect_to_have_style(self.loc_progress, "width", "100%", timeout=timeout) # TODO-future; Test multiple file upload - def expect_multiple(self, multiple: bool, *, timeout: Timeout = None) -> None: - _expect_multiple(self.loc, multiple, timeout=timeout) + def expect_multiple(self, value: bool, *, timeout: Timeout = None) -> None: + """ + Expect the `multiple` attribute to have a specific value. + + Parameters + ---------- + value + The expected value of the `multiple` attribute. + timeout + The timeout for the expectation. Defaults to `None`. + """ + _expect_multiple(self.loc, value, timeout=timeout) def expect_accept( self, - accept: list[str] | AttrValue, + value: list[str] | AttrValue, *, timeout: Timeout = None, ) -> None: - if isinstance(accept, list): - accept = ",".join(accept) - expect_attribute_to_have_value(self.loc, "accept", accept, timeout=timeout) + """ + Expect the `accept` attribute to have a specific value. + + Parameters + ---------- + value + The expected value of the `accept` attribute. + timeout + The timeout for the expectation. Defaults to `None`. + """ + if isinstance(value, list): + value = ",".join(value) + expect_attribute_to_have_value(self.loc, "accept", value, timeout=timeout) + + def expect_width(self, value: StyleValue, *, timeout: Timeout = None) -> None: + """ + Expect the width of the input file to have a specific value. - def expect_width(self, width: StyleValue, *, timeout: Timeout = None) -> None: - expect_to_have_style(self.loc_container, "width", width, timeout=timeout) + Parameters + ---------- + value + The expected value of the width. + timeout + The timeout for the expectation. Defaults to `None`. + """ + expect_to_have_style(self.loc_container, "width", value, timeout=timeout) def expect_button_label( self, - button_label: PatternOrStr, + value: PatternOrStr, *, timeout: Timeout = None, ) -> None: - playwright_expect(self.loc_button).to_have_text(button_label, timeout=timeout) + """ + Expect the button label to have a specific value. + + Parameters + ---------- + value + The expected value of the button label. + timeout + The timeout for the expectation. Defaults to `None`. + """ + playwright_expect(self.loc_button).to_have_text(value, timeout=timeout) def expect_capture( self, - capture: Literal["environment", "user"] | None, + value: Literal["environment", "user"] | None, *, timeout: Timeout = None, ) -> None: - expect_attribute_to_have_value(self.loc, "capture", capture, timeout=timeout) + """ + Expect the `capture` attribute to have a specific value. + + Parameters + ---------- + value + The expected value of the `capture` attribute. + timeout + The timeout for the expectation. Defaults to `None`. + """ + expect_attribute_to_have_value(self.loc, "capture", value, timeout=timeout) def expect_placeholder(self, value: AttrValue, *, timeout: Timeout = None) -> None: expect_attribute_to_have_value( @@ -1418,32 +2246,36 @@ def expect_placeholder(self, value: AttrValue, *, timeout: Timeout = None) -> No ) -class _InputSliderBase(_WidthLocM, _InputWithLabel): - # id: str, - # label: TagChild, - # min: SliderValueArg, - # max: SliderValueArg, - # value: Union[SliderValueArg, Iterable[SliderValueArg]], - # step: Optional[SliderStepArg] = None, - # ticks: bool = True, - # animate: Union[bool, AnimationOptions] = False, - # width: Optional[str] = None, - # sep: str = ",", - # pre: Optional[str] = None, - # post: Optional[str] = None, - # time_format: Optional[str] = None, - # timezone: Optional[str] = None, - # drag_range: bool = True, +class _InputSliderBase(_WidthLocM, _UiWithLabel): loc_irs: Locator + """ + Playwright `Locator` of the input slider. + """ loc_irs_ticks: Locator + """ + Playwright `Locator` of the input slider ticks. + """ loc_play_pause: Locator + """ + Playwright `Locator` of the play/pause button. + """ def __init__( self, page: Page, id: str, ) -> None: + """ + Initialize the InputSlider. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The id of the slider. + """ super().__init__( page, id=id, @@ -1461,6 +2293,16 @@ def expect_tick_labels( *, timeout: Timeout = None, ) -> None: + """ + Expect the tick labels of the input slider. + + Parameters + ---------- + value + The expected tick labels. + timeout + The timeout for the expectation. Defaults to `None`. + """ if value is None: playwright_expect(self.loc_irs_ticks).to_have_count(0) return @@ -1468,6 +2310,16 @@ def expect_tick_labels( playwright_expect(self.loc_irs_ticks).to_have_text(value, timeout=timeout) def expect_animate(self, exists: bool, *, timeout: Timeout = None) -> None: + """ + Expect the animate button to exist. + + Parameters + ---------- + exists + Whether the animate button should exist. + timeout + The timeout for the expectation. Defaults to `None`. + """ animate_count = 1 if exists else 0 playwright_expect(self.loc_play_pause).to_have_count(animate_count) @@ -1503,6 +2355,14 @@ def expect_animate_options( # become `play` over and over again. Instead, have explicit `play` and `pause` # methods. def click_play(self, *, timeout: Timeout = None) -> None: + """ + Click the play button. + + Parameters + ---------- + timeout + The timeout for the action. Defaults to `None`. + """ self.loc_container.wait_for(state="visible", timeout=timeout) self.loc_container.scroll_into_view_if_needed(timeout=timeout) _expect_class_value( @@ -1511,6 +2371,14 @@ def click_play(self, *, timeout: Timeout = None) -> None: self.loc_play_pause.click() def click_pause(self, *, timeout: Timeout = None) -> None: + """ + Click the pause button. + + Parameters + ---------- + timeout + The timeout for the action. Defaults to `None`. + """ self.loc_container.wait_for(state="visible", timeout=timeout) self.loc_container.scroll_into_view_if_needed(timeout=timeout) _expect_class_value( @@ -1519,36 +2387,106 @@ def click_pause(self, *, timeout: Timeout = None) -> None: self.loc_play_pause.click() def expect_min(self, value: AttrValue, *, timeout: Timeout = None) -> None: + """ + Expect the input element to have the expected `min` attribute value. + + Parameters + ---------- + value + The expected value. + timeout + The timeout for the expectation. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "data-min", value=value, timeout=timeout ) def expect_max(self, value: AttrValue, *, timeout: Timeout = None) -> None: + """ + Expect the input element to have the expected `max` attribute value. + + Parameters + ---------- + value + The expected value. + timeout + The timeout for the expectation. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "data-max", value=value, timeout=timeout ) def expect_step(self, value: AttrValue, *, timeout: Timeout = None) -> None: + """ + Expect the input element to have the expected `step` attribute value. + + Parameters + ---------- + value + The expected value. + timeout + The timeout for the expectation. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "data-step", value=value, timeout=timeout ) def expect_ticks(self, value: AttrValue, *, timeout: Timeout = None) -> None: + """ + Expect the input element to have the expected `data-ticks` attribute value. + + Parameters + ---------- + value + The expected value. + timeout + The timeout for the expectation. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "data-grid", value=value, timeout=timeout ) def expect_sep(self, value: AttrValue, *, timeout: Timeout = None) -> None: + """ + Expect the input element to have the expected `data-sep` attribute value. + + Parameters + ---------- + value + The expected value. + timeout + The timeout for the expectation. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "data-prettify-separator", value=value, timeout=timeout ) def expect_pre(self, value: AttrValue, *, timeout: Timeout = None) -> None: + """ + Expect the input element to have the expected `data-pre` attribute value. + + Parameters + ---------- + value + The expected value. + timeout + The timeout for the expectation. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "data-prefix", value=value, timeout=timeout ) def expect_post(self, value: AttrValue, *, timeout: Timeout = None) -> None: + """ + Expect the input element to have the expected `data-post` attribute value. + + Parameters + ---------- + value + The expected value. + timeout + The timeout for the expectation. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "data-postfix", value=value, timeout=timeout ) @@ -1559,16 +2497,46 @@ def expect_post(self, value: AttrValue, *, timeout: Timeout = None) -> None: # expect_attr(self.loc, "data-data-type", value=value, timeout=timeout) def expect_time_format(self, value: AttrValue, *, timeout: Timeout = None) -> None: + """ + Asserts that the input element has the expected `data-time-format` attribute value. + + Parameters + ---------- + value + The expected value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "data-time-format", value=value, timeout=timeout ) def expect_timezone(self, value: AttrValue, *, timeout: Timeout = None) -> None: + """ + Asserts that the input element has the expected `data-timezone` attribute value. + + Parameters + ---------- + value + The expected value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "data-timezone", value=value, timeout=timeout ) def expect_drag_range(self, value: AttrValue, *, timeout: Timeout = None) -> None: + """ + Asserts that the input element has the expected `data-drag-range` attribute value. + + Parameters + ---------- + value + The expected value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "data-drag-interval", value=value, timeout=timeout ) @@ -1617,6 +2585,18 @@ def should_continue(cur_pxls: float) -> bool: sleep_time = 0.05 def slow_move(x: float, y: float, delay: float = sleep_time) -> None: + """ + Slowly move the mouse to the given coordinates. + + Parameters + ---------- + x + The x-coordinate. + y + The y-coordinate. + delay + The delay between each move. Defaults to `sleep_time`. + """ mouse.move(x, y) time.sleep(delay) @@ -1685,17 +2665,42 @@ def _handle_center( class InputSlider(_InputSliderBase): + """Input slider control for :func:`~shiny.ui.input_slider`.""" + loc_irs_label: Locator + """ + Playwright `Locator` of the input slider label. + """ def __init__( self, page: Page, id: str, ) -> None: + """ + Initialize the InputSlider object. + + Parameters + ---------- + page + The Playwright Page object. + id + The id of the input element. + """ super().__init__(page, id=id) self.loc_irs_label = self.loc_irs.locator("> .irs > .irs-single") def expect_value(self, value: PatternOrStr, *, timeout: Timeout = None) -> None: + """ + Asserts that the input element has the expected value. + + Parameters + ---------- + value + The expected value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ playwright_expect(self.loc_irs_label).to_have_text(value, timeout=timeout) def set( @@ -1705,6 +2710,18 @@ def set( max_err_values: int = 15, timeout: Timeout = None, ) -> None: + """ + Set the value of the slider. + + Parameters + ---------- + value + The value to set the slider to. + max_err_values + The maximum number of error values to display if the value is not found. Defaults to 15. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ self._wait_for_container(timeout=timeout) handle = self.loc_irs.locator("> .irs-handle") @@ -1723,14 +2740,32 @@ def set( class InputSliderRange(_InputSliderBase): + """Input slider range control for :func:`~shiny.ui.input_slider_range`.""" + loc_irs_label_from: Locator + """ + Playwright `Locator` of the input slider label for the `from` handle. + """ loc_irs_label_to: Locator + """ + Playwright `Locator` of the input slider label for the `to` handle. + """ def __init__( self, page: Page, id: str, ) -> None: + """ + Initialize the InputSliderRange object. + + Parameters + ---------- + page + The Playwright Page object. + id + The id of the input element. + """ super().__init__(page, id=id) self.loc_irs_label_from = self.loc_irs.locator("> .irs > .irs-from") self.loc_irs_label_to = self.loc_irs.locator("> .irs > .irs-to") @@ -1745,6 +2780,16 @@ def expect_value( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected value. + + Parameters + ---------- + value + The expected value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ if all_missing(*value): raise ValueError("Both `value` tuple entries cannot be `MISSING_TYPE`") from_val = value[0] @@ -1793,6 +2838,18 @@ def set( max_err_values: int = 15, timeout: Timeout = None, ) -> None: + """ + Set the value of the slider. + + Parameters + ---------- + value + The value to set the slider to. + max_err_values + The maximum number of error values to display if the value is not found. Defaults to 15. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ if all_missing(*value): raise ValueError("Both `value` tuple entries cannot be `MISSING_TYPE`") @@ -1850,21 +2907,8 @@ def set( class _DateBase( _SetTextM, _WidthContainerM, - _InputWithLabel, + _UiWithLabel, ): - # id: str, - # label: TagChild, - # value: Optional[Union[date, str]] = None, - # min: Optional[Union[date, str]] = None, - # max: Optional[Union[date, str]] = None, - # format: str = "yyyy-mm-dd", - # startview: str = "month", - # weekstart: int = 0, - # language: str = "en", - # width: Optional[str] = None, - # autoclose: bool = True, - # datesdisabled: Optional[list[str]] = None, - # daysofweekdisabled: Optional[list[int]] = None, # Due to the `language` parameter, we can't use `datetime.date` as a value type @@ -1874,6 +2918,16 @@ def expect_value( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected value. + + Parameters + ---------- + value + The expected value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ # if value is None: # self.expect.to_be_empty(timeout=timeout) # else: @@ -1885,6 +2939,16 @@ def expect_min_date( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected `data-min-date` attribute value. + + Parameters + ---------- + value + The expected `data-min-date` attribute value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "data-min-date", value=value, timeout=timeout ) @@ -1895,6 +2959,16 @@ def expect_max_date( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected `data-max-date` attribute value. + + Parameters + ---------- + value + The expected `data-max-date` attribute value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "data-max-date", value=value, timeout=timeout ) @@ -1905,6 +2979,16 @@ def expect_format( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected `data-date-format` attribute value. + + Parameters + ---------- + value + The expected `data-date-format` attribute value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "data-date-format", value=value, timeout=timeout ) @@ -1915,6 +2999,16 @@ def expect_startview( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected `data-date-start-view` attribute value. + + Parameters + ---------- + value + The expected `data-date-start-view` attribute value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "data-date-start-view", value=value, timeout=timeout ) @@ -1925,6 +3019,16 @@ def expect_weekstart( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected `data-date-week-start` attribute value. + + Parameters + ---------- + value + The expected `data-date-week-start` attribute value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ if isinstance(value, int): value = str(value) expect_attribute_to_have_value( @@ -1937,6 +3041,16 @@ def expect_language( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected `data-date-language` attribute value. + + Parameters + ---------- + value + The expected `data-date-language` attribute value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "data-date-language", value=value, timeout=timeout ) @@ -1948,6 +3062,16 @@ def expect_autoclose( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected `data-date-autoclose` attribute value. + + Parameters + ---------- + value + The expected `data-date-autoclose` attribute value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ expect_attribute_to_have_value( self.loc, "data-date-autoclose", value=value, timeout=timeout ) @@ -1958,6 +3082,16 @@ def expect_datesdisabled( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected `data-date-dates-disabled` attribute value. + + Parameters + ---------- + value + The expected `data-date-dates-disabled` attribute value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ if isinstance(value, list): assert len(value) > 0, "`value` must be `None` or a non-empty list" value_str = "null" if value is None else json.dumps(value) @@ -1974,6 +3108,16 @@ def expect_daysofweekdisabled( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected `data-date-days-of-week-disabled` attribute value. + + Parameters + ---------- + value + The expected `data-date-days-of-week-disabled` attribute value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ if isinstance(value, list): assert len(value) > 0, "`value` must be `None` or a non-empty list" value_str = "null" if value is None else json.dumps(value) @@ -1987,6 +3131,16 @@ def expect_daysofweekdisabled( class InputDate(_DateBase): def __init__(self, page: Page, id: str) -> None: + """ + Initialize an InputDate object. + + Parameters + ---------- + page + The page object. + id + The id of the input element. + """ super().__init__( page, id=id, @@ -1995,29 +3149,41 @@ def __init__(self, page: Page, id: str) -> None: ) -class InputDateRange(_WidthContainerM, _InputWithLabel): - # id: str, - # label: TagChild, - # *, - # start: Optional[Union[date, str]] = None, - # end: Optional[Union[date, str]] = None, - # min: Optional[Union[date, str]] = None, - # max: Optional[Union[date, str]] = None, - # format: str = "yyyy-mm-dd", - # startview: str = "month", - # weekstart: int = 0, - # language: str = "en", - # separator: str = " to ", - # width: Optional[str] = None, - # autoclose: bool = True, +class InputDateRange(_WidthContainerM, _UiWithLabel): + """Input date range control for :func:`~shiny.ui.input_date_range`.""" loc_separator: Locator + """ + Playwright `Locator` of the separator between the two input elements. + """ loc_start: Locator + """ + Playwright `Locator` of the start date input element. + """ loc_end: Locator + """ + Playwright `Locator` of the end date input element. + """ date_start: _DateBase + """ + The start date input element. + """ date_end: _DateBase + """ + The end date input element. + """ def __init__(self, page: Page, id: str) -> None: + """ + Initialize an InputDateRange object. + + Parameters + ---------- + page + The page object. + id + The id of the input element. + """ super().__init__( page, id=id, @@ -2051,6 +3217,16 @@ def set( *, timeout: Timeout = None, ) -> None: + """ + Sets the value of the input element. + + Parameters + ---------- + value + The value to set. The first element is the start date and the second element is the end date. + timeout + The maximum time to wait for the value to be set. Defaults to `None`. + """ start = value[0] end = value[1] # TODO-future; Composable set() methods? @@ -2069,6 +3245,16 @@ def expect_value( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected value. + + Parameters + ---------- + value + The expected value. The first element is the start date and the second element is the end date. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ if all_missing(*value): raise ValueError("Both `start_val` and `end_val` can not be `MISSING_TYPE`") start_val = value[0] @@ -2089,6 +3275,16 @@ def expect_min_date( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected minimum date. + + Parameters + ---------- + value + The expected minimum date. + timeout + The maximum time to wait for the minimum date to appear. Defaults to `None`. + """ # TODO-future; Composable expectations self.date_start.expect_min_date(value, timeout=timeout) self.date_end.expect_min_date(value, timeout=timeout) @@ -2100,6 +3296,16 @@ def expect_max_date( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected maximum date. + + Parameters + ---------- + value + The expected maximum date. + timeout + The maximum time to wait for the maximum date to appear. Defaults to `None`. + """ # TODO-future; Composable expectations self.date_start.expect_max_date(value, timeout=timeout) self.date_end.expect_max_date(value, timeout=timeout) @@ -2111,6 +3317,16 @@ def expect_format( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected format. + + Parameters + ---------- + value + The expected format. + timeout + The maximum time to wait for the format to appear. Defaults to `None`. + """ # TODO-future; Composable expectations self.date_start.expect_format(value, timeout=timeout) self.date_end.expect_format(value, timeout=timeout) @@ -2122,6 +3338,16 @@ def expect_startview( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected start view. + + Parameters + ---------- + value + The expected start view. + timeout + The maximum time to wait for the start view to appear. Defaults to `None`. + """ # TODO-future; Composable expectations self.date_start.expect_startview(value, timeout=timeout) self.date_end.expect_startview(value, timeout=timeout) @@ -2133,6 +3359,16 @@ def expect_weekstart( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected week start. + + Parameters + ---------- + value + The expected week start. + timeout + The maximum time to wait for the week start to appear. Defaults to `None`. + """ # TODO-future; Composable expectations self.date_start.expect_weekstart(value, timeout=timeout) self.date_end.expect_weekstart(value, timeout=timeout) @@ -2144,6 +3380,16 @@ def expect_language( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected language. + + Parameters + ---------- + value + The expected language. + timeout + The maximum time to wait for the language to appear. Defaults to `None`. + """ # TODO-future; Composable expectations self.date_start.expect_language(value, timeout=timeout) self.date_end.expect_language(value, timeout=timeout) @@ -2155,6 +3401,16 @@ def expect_separator( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected separator. + + Parameters + ---------- + value + The expected separator. + timeout + The maximum time to wait for the separator to appear. Defaults to `None`. + """ playwright_expect(self.loc_separator).to_have_text(value, timeout=timeout) # width: Optional[str] = None, @@ -2166,6 +3422,16 @@ def expect_autoclose( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the input element has the expected autoclose value. + + Parameters + ---------- + value + The expected autoclose value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ # TODO-future; Composable expectations self.date_start.expect_autoclose(value, timeout=timeout) self.date_end.expect_autoclose(value, timeout=timeout) @@ -2183,9 +3449,22 @@ class _OutputBaseP(Protocol): class _OutputBase: + """ + Base class for output controls. + """ + id: str + """ + The ID of the output control. + """ loc: Locator + """ + Playwright `Locator` of the output control. + """ page: Page + """ + Playwright `Page` of the Shiny app. + """ def __init__( self, @@ -2217,6 +3496,16 @@ def expect_value( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the output has the expected value. + + Parameters + ---------- + value + The expected value. + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ """Note this function will trim value and output text value before comparing them""" self.expect.to_have_text(value, timeout=timeout) @@ -2224,7 +3513,7 @@ def expect_value( class _OutputContainerP(_OutputBaseP, Protocol): def expect_container_tag( self: _OutputBaseP, - tag_name: Literal["span", "div"] | str, + value: Literal["span", "div"] | str, *, timeout: Timeout = None, ) -> None: ... @@ -2233,73 +3522,195 @@ def expect_container_tag( class _OutputContainerM: def expect_container_tag( self: _OutputBaseP, - tag_name: Literal["span", "div"] | str, + value: Literal["span", "div"] | str, *, timeout: Timeout = None, ) -> None: - loc = self.loc.locator(f"xpath=self::{tag_name}") + """ + Asserts that the output has the expected container tag. + + Parameters + ---------- + value + The expected container tag. + timeout + The maximum time to wait for the container tag to appear. Defaults to `None`. + """ + loc = self.loc.locator(f"xpath=self::{value}") playwright_expect(loc).to_have_count(1, timeout=timeout) class _OutputInlineContainerM(_OutputContainerM): def expect_inline( - self: _OutputContainerP, inline: bool = False, *, timeout: Timeout = None + self: _OutputContainerP, + value: bool = False, + *, + timeout: Timeout = None, ) -> None: - tag_name = "span" if inline else "div" + """ + Asserts that the output is inline. + + Parameters + ---------- + value + Whether the output is inline. + timeout + The maximum time to wait for the output to appear. Defaults to `None`. + """ + tag_name = "span" if value else "div" self.expect_container_tag(tag_name, timeout=timeout) -class OutputText(_OutputInlineContainerM, _OutputTextValue): +class OutputText( + _OutputInlineContainerM, + _OutputTextValue, +): + """Text output control for :func:`~shiny.ui.text_output`.""" + + loc: Locator + """ + Playwright `Locator` of the text output. + """ + def __init__( self, page: Page, id: str, ) -> None: + """ + Initializes a text output. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the text output. + """ super().__init__(page, id=id, loc=f"#{id}.shiny-text-output") def get_value(self, *, timeout: Timeout = None) -> str: + """ + Gets the text value of the output. + + Parameters + ---------- + timeout + The maximum time to wait for the value to appear. Defaults to `None`. + """ return self.loc.inner_text(timeout=timeout) class OutputCode(_OutputTextValue): + """Code output control for :func:`~shiny.ui.code_output`.""" + + loc: Locator + """ + Playwright `Locator` of the code output. + """ + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a code output. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the code output. + """ super().__init__(page, id=id, loc=f"pre#{id}.shiny-text-output") def expect_has_placeholder( - self, placeholder: bool = False, *, timeout: Timeout = None + self, + value: bool = False, + *, + timeout: Timeout = None, ) -> None: + """ + Asserts that the code output has the expected placeholder. + + Parameters + ---------- + value + Whether the code output has a placeholder. + timeout + The maximum time to wait for the placeholder to appear. Defaults to `None`. + """ _expect_class_value( self.loc, cls="noplaceholder", - has_class=not placeholder, + has_class=not value, timeout=timeout, ) class OutputTextVerbatim(_OutputTextValue): + """Verbatim text output control for :func:`~shiny.ui.text_output_verbatim`.""" + + loc: Locator + """ + Playwright `Locator` of the verbatim text output. + """ + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a verbatim text output. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the verbatim text output. + """ super().__init__(page, id=id, loc=f"pre#{id}.shiny-text-output") def expect_has_placeholder( - self, placeholder: bool = False, *, timeout: Timeout = None + self, + value: bool = False, + *, + timeout: Timeout = None, ) -> None: + """ + Asserts that the verbatim text output has the expected placeholder. + + Parameters + ---------- + value + Whether the verbatim text output has a placeholder. + timeout + The maximum time to wait for the placeholder to appear. Defaults to `None`. + """ _expect_class_value( self.loc, cls="noplaceholder", - has_class=not placeholder, + has_class=not value, timeout=timeout, ) class _OutputImageBase(_OutputInlineContainerM, _OutputBase): - # id: str - # width: str = "100%" - # height: str = "400px" - # inline: bool = False loc_img: Locator + """ + Playwright `Locator` of the image. + """ def __init__(self, page: Page, id: str, loc_classes: str = "") -> None: + """ + Initializes an image output. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the image. + loc_classes + Additional classes to locate the image. Defaults to "". + """ super().__init__( page, id=id, @@ -2313,6 +3724,16 @@ def expect_height( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the image has the expected height. + + Parameters + ---------- + value + The expected height. + timeout + The maximum time to wait for the height to appear. Defaults to `None`. + """ expect_to_have_style(self.loc, "height", value, timeout=timeout) def expect_width( @@ -2321,6 +3742,16 @@ def expect_width( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the image has the expected width. + + Parameters + ---------- + value + The expected width. + timeout + The maximum time to wait for the width to appear. Defaults to `None`. + """ expect_to_have_style(self.loc, "width", value, timeout=timeout) def expect_img_src( @@ -2329,6 +3760,16 @@ def expect_img_src( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the image has the expected src. + + Parameters + ---------- + value + The expected src. + timeout + The maximum time to wait for the src to appear. Defaults to `None`. + """ expect_attribute_to_have_value(self.loc_img, "src", value, timeout=timeout) def expect_img_width( @@ -2337,6 +3778,16 @@ def expect_img_width( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the image has the expected width. + + Parameters + ---------- + value + The expected width. + timeout + The maximum time to wait for the width to appear. Defaults to `None`. + """ expect_attribute_to_have_value(self.loc_img, "width", value, timeout=timeout) def expect_img_height( @@ -2345,6 +3796,16 @@ def expect_img_height( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the image has the expected height. + + Parameters + ---------- + value + The expected height. + timeout + The maximum time to wait for the height to appear. Defaults to `None`. + """ expect_attribute_to_have_value(self.loc_img, "height", value, timeout=timeout) def expect_img_alt( @@ -2353,6 +3814,16 @@ def expect_img_alt( *, timeout: Timeout = None, ) -> None: + """ + Asserts that the image has the expected alt text. + + Parameters + ---------- + value + The expected alt text. + timeout + The maximum time to wait for the alt text to appear. Defaults to `None`. + """ expect_attribute_to_have_value(self.loc_img, "alt", value, timeout=timeout) # def expect_img_style( @@ -2366,135 +3837,272 @@ def expect_img_alt( class OutputImage(_OutputImageBase): def __init__(self, page: Page, id: str) -> None: + """ + Initializes an image output. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the image. + """ super().__init__(page, id=id) class OutputPlot(_OutputImageBase): - # shiny-plot-output - # id: str - # width: str = "100%" - # height: str = "400px" - # inline: bool = False + """Plot output control for :func:`~shiny.ui.plot_output`.""" + + loc: Locator + """ + Playwright `Locator` of the plot output. + """ + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a plot output. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the plot. + """ super().__init__(page, id=id, loc_classes=".shiny-plot-output") class OutputUi(_OutputInlineContainerM, _OutputBase): - # id: str, - # inline: bool = False, - # container: Optional[TagFunction] = None, + """UI output control for :func:`~shiny.ui.ui_output`.""" + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a UI output. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the UI output. + """ super().__init__(page, id=id, loc=f"#{id}") # TODO-future; Should we try verify that `recalculating` class is not present? Do this for all outputs! - def expect_empty(self, empty: bool, *, timeout: Timeout = None) -> None: - if empty: + def expect_empty(self, value: bool, *, timeout: Timeout = None) -> None: + """ + Asserts that the output is empty. + + Parameters + ---------- + value + Whether the output is empty. + timeout + The maximum time to wait for the output to be empty. Defaults to `None`. + """ + if value: self.expect.to_be_empty(timeout=timeout) else: self.expect.not_to_be_empty(timeout=timeout) - def expect_text(self, text: str, *, timeout: Timeout = None) -> None: - self.expect.to_have_text(text, timeout=timeout) + def expect_text(self, value: str, *, timeout: Timeout = None) -> None: + """ + Asserts that the output has the expected text. + + Parameters + ---------- + value + The expected text. + timeout + The maximum time to wait for the text to appear. Defaults to `None`. + """ + + self.expect.to_have_text(value, timeout=timeout) # When making selectors, use `xpath` so that direct decendents can be checked class OutputTable(_OutputBase): - # id: str, - # **kwargs: TagAttrArg + """Table output control for :func:`~shiny.ui.table_output`.""" + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a table output. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the table. + """ super().__init__(page, id=id, loc=f"#{id}") def expect_cell( self, - text: PatternOrStr, + value: PatternOrStr, row: int, col: int, *, timeout: Timeout = None, ) -> None: + """ + Asserts that the table cell has the expected text. + + Parameters + ---------- + value + The expected text in the cell. + row + The row number. + col + The column number. + timeout + The maximum time to wait for the text to appear. Defaults to `None`. + """ assert_type(row, int) assert_type(col, int) playwright_expect( self.loc.locator( f"xpath=./table/tbody/tr[{row}]/td[{col}] | ./table/tbody/tr[{row}]/th[{col}]" ) - ).to_have_text(text, timeout=timeout) + ).to_have_text(value, timeout=timeout) def expect_column_labels( self, - labels: ListPatternOrStr | None, + value: ListPatternOrStr | None, *, timeout: Timeout = None, ) -> None: - if isinstance(labels, list) and len(labels) == 0: - labels = None + """ + Asserts that the table has the expected column labels. + + Parameters + ---------- + value + The expected column labels. If None, it asserts that the table has no column labels. + timeout + The maximum time to wait for the column labels to appear. Defaults to `None`. + """ + if isinstance(value, list) and len(value) == 0: + value = None - if labels is None: + if value is None: playwright_expect( self.loc.locator("xpath=./table/thead/tr/th") ).to_have_count(0, timeout=timeout) else: playwright_expect( self.loc.locator("xpath=./table/thead/tr/th") - ).to_have_text(labels, timeout=timeout) + ).to_have_text(value, timeout=timeout) def expect_column_text( self, col: int, # Can't use `None` as we don't know how many rows exist - text: ListPatternOrStr, + value: ListPatternOrStr, *, timeout: Timeout = None, ) -> None: + """ + Asserts that the column has the expected text. + + Parameters + ---------- + col + The column number. + value + The expected text in the column. + timeout + The maximum time to wait for the text to appear. Defaults to `None`. + """ assert_type(col, int) playwright_expect( self.loc.locator(f"xpath=./table/tbody/tr/td[{col}]") ).to_have_text( - text, + value, timeout=timeout, ) def expect_n_col( self, - n: int, + value: int, *, timeout: Timeout = None, ) -> None: + """ + Asserts that the table has the expected number of columns. + + Parameters + ---------- + value + The expected number of columns in the table. + timeout + The maximum time to wait for the table to have the expected number of columns. Defaults to `None`. + """ playwright_expect( # self.loc.locator("xpath=./table/thead/tr[1]/(td|th)") self.loc.locator("xpath=./table/thead/tr[1]/td | ./table/thead/tr[1]/th") ).to_have_count( - n, + value, timeout=timeout, ) def expect_n_row( self, - n: int, + value: int, *, timeout: Timeout = None, ) -> None: + """ + Asserts that the table has the expected number of rows. + + Parameters + ---------- + value + The expected number of rows in the table. + timeout + The maximum time to wait for the table to have the expected number of rows. Defaults to `None`. + """ playwright_expect(self.loc.locator("xpath=./table/tbody/tr")).to_have_count( - n, + value, timeout=timeout, ) class Sidebar( _WidthLocM, - _InputWithContainer, + _UiWithContainer, ): - # *args: TagChild | TagAttrs, - # width: CssUnit = 250, - # position: Literal["left", "right"] = "left", - # open: Literal["desktop", "open", "closed", "always"] = "desktop", - # id: Optional[str] = None, - # title: TagChild | str = None, - # bg: Optional[str] = None, - # fg: Optional[str] = None, - # class_: Optional[str] = None, # TODO-future; Consider using `**kwargs` instead - # max_height_mobile: Optional[str | float] = None, + """Sidebar control for func: `~shiny.ui.sidebar`.""" + + loc_container: Locator + """ + Playwright `Locator` for the sidebar layout. + """ + loc: Locator + """ + Playwright `Locator` for the sidebar. + """ + loc_handle: Locator + """ + Playwright `Locator` for the open/close handle of the sidebar. + """ + loc_position: Locator + """ + Playwright `Locator` for the position of the sidebar. + """ + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a sidebar control. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the sidebar. + """ super().__init__( page, id=id, @@ -2505,45 +4113,106 @@ def __init__(self, page: Page, id: str) -> None: self.loc_position = self.loc.locator("..") def expect_text(self, value: PatternOrStr, *, timeout: Timeout = None) -> None: + """ + Asserts that the sidebar has the expected text. + + Parameters + ---------- + value + The expected text in the sidebar. + timeout + The maximum time to wait for the text to appear. Defaults to `None`. + """ playwright_expect(self.loc).to_have_text(value, timeout=timeout) def expect_position( - self, position: Literal["left", "right"], *, timeout: Timeout = None + self, + value: Literal["left", "right"], + *, + timeout: Timeout = None, ) -> None: - is_right_sidebar = position == "right" + """ + Asserts that the sidebar is in the expected position. + + Parameters + ---------- + value + The expected position of the sidebar. + timeout + The maximum time to wait for the sidebar to appear. Defaults to `None`. + """ + is_right_sidebar = value == "right" _expect_class_value( self.loc_position, - f"sidebar-{position}", + f"sidebar-{value}", is_right_sidebar, timeout=timeout, ) def expect_handle(self, exists: bool, *, timeout: Timeout = None) -> None: + """ + Asserts that the sidebar handle exists or does not exist. + + Parameters + ---------- + exists + `True` if the sidebar open/close handle should exist, `False` otherwise. + timeout + The maximum time to wait for the sidebar handle to appear. Defaults to `None`. + """ playwright_expect(self.loc_handle).to_have_count(int(exists), timeout=timeout) - def expect_open(self, open: bool, *, timeout: Timeout = None) -> None: + def expect_open(self, value: bool, *, timeout: Timeout = None) -> None: + """ + Expect the sidebar to be open or closed. + + Parameters + ---------- + value + `True` if the sidebar should be open, `False` to be closed. + timeout + The maximum time to wait for the sidebar to open or close. Defaults to `None`. + """ playwright_expect(self.loc_handle).to_have_attribute( - "aria-expanded", str(open).lower(), timeout=timeout + "aria-expanded", str(value).lower(), timeout=timeout ) def set(self, open: bool, *, timeout: Timeout = None) -> None: + """ + Sets the sidebar to be open or closed. + + Parameters + ---------- + open + `True` to open the sidebar and `False` to close it. + timeout + The maximum time to wait for the sidebar to open or close. Defaults to `None`. + """ if open ^ (self.loc_handle.get_attribute("aria-expanded") == "true"): self.toggle(timeout=timeout) def toggle(self, *, timeout: Timeout = None) -> None: + """ + Toggles the sidebar open or closed. + + Parameters + ---------- + timeout + The maximum time to wait for the sidebar to toggle. Defaults to `None`. + """ self.loc_handle.wait_for(state="visible", timeout=timeout) self.loc_handle.scroll_into_view_if_needed(timeout=timeout) self.loc_handle.click(timeout=timeout) -class _CardBodyP(_InputBaseP, Protocol): +class _CardBodyP(_UiBaseP, Protocol): """ Represents the body of a card control. """ loc_body: Locator """ - The locator for the body element of the card control. + Playwright `Locator` for the body element of the card control. """ @@ -2552,7 +4221,7 @@ class _CardBodyM: def expect_body( self: _CardBodyP, - text: PatternOrStr | list[PatternOrStr], + value: PatternOrStr | list[PatternOrStr], *, timeout: Timeout = None, ) -> None: @@ -2563,22 +4232,22 @@ def expect_body( text The expected text or a list of expected texts. timeout - The maximum time to wait for the text to appear. Defaults to None. + The maximum time to wait for the text to appear. Defaults to `None`. """ playwright_expect(self.loc).to_have_text( - text, + value, timeout=timeout, ) -class _CardFooterLayoutP(_InputBaseP, Protocol): +class _CardFooterLayoutP(_UiBaseP, Protocol): """ Represents the layout of the footer in a card. """ loc_footer: Locator """ - The locator for the footer element. + Playwright `Locator` for the footer element. """ @@ -2589,7 +4258,7 @@ class _CardFooterM: def expect_footer( self: _CardFooterLayoutP, - text: PatternOrStr, + value: PatternOrStr, *, timeout: Timeout = None, ) -> None: @@ -2598,138 +4267,142 @@ def expect_footer( Parameters ---------- - text + value The expected text in the footer section. timeout - The maximum time to wait for the footer text to appear. Defaults to None. + The maximum time to wait for the footer text to appear. Defaults to `None`. """ playwright_expect(self.loc_footer).to_have_text( - text, + value, timeout=timeout, ) -class _CardFullScreenLayoutP(_OutputBaseP, Protocol): +class _CardValueBoxFullScreenLayoutP(_OutputBaseP, Protocol): """ - Represents a card full-screen layout for the Playwright controls. + Represents a card / Value Box full-screen layout for the Playwright controls. """ loc_title: Locator """ - The locator for the title element. + Playwright `Locator` for the title element. """ _loc_fullscreen: Locator """ - The locator for the full-screen element. + Playwright `Locator` for the full-screen element. """ _loc_close_button: Locator """ - The locator for the close button element. + Playwright `Locator` for the close button element. """ -class _CardFullScreenM: +class _CardValueBoxFullScreenM: """ - Represents a class for managing full screen functionality of a card. + Represents a class for managing full screen functionality of a Card or Value Box. """ + # TODO-karan-test: Convert `open_full_screen` and `close_full_screen` to `set_full_screen(open:bool)` def open_full_screen( - self: _CardFullScreenLayoutP, *, timeout: Timeout = None + self: _CardValueBoxFullScreenLayoutP, *, timeout: Timeout = None ) -> None: """ - Opens the card in full screen mode. + Opens the element in full screen mode. Parameters ---------- timeout - The maximum time to wait for the card to open in full screen mode. Defaults to None. + The maximum time to wait for full screen mode to open. Defaults to `None`. """ self.loc_title.hover(timeout=timeout) self._loc_fullscreen.wait_for(state="visible", timeout=timeout) self._loc_fullscreen.click(timeout=timeout) def close_full_screen( - self: _CardFullScreenLayoutP, *, timeout: Timeout = None + self: _CardValueBoxFullScreenLayoutP, *, timeout: Timeout = None ) -> None: """ - Closes the card from full screen mode. + Exits full screen mode. Parameters ---------- timeout - The maximum time to wait for the card to close from full screen mode. Defaults to None. + The maximum time to wait to wait for full screen mode to exit. Defaults to `None`. """ self._loc_close_button.click(timeout=timeout) - def expect_full_screen_open( - self: _CardFullScreenLayoutP, open: bool, *, timeout: Timeout = None + def expect_full_screen( + self: _CardValueBoxFullScreenLayoutP, value: bool, *, timeout: Timeout = None ) -> None: """ - Verifies if the card is expected to be in full screen mode. + Verifies if the full screen mode is currently open. Parameters ---------- - open - True if the card is expected to be in full screen mode, False otherwise. + value + `True` if the item is to be in full screen mode, `False` otherwise. timeout - The maximum time to wait for the verification. Defaults to None. + The maximum time to wait for the verification. Defaults to `None`. """ playwright_expect(self._loc_close_button).to_have_count( - int(open), timeout=timeout + int(value), timeout=timeout ) def expect_full_screen_available( - self: _CardFullScreenLayoutP, available: bool, *, timeout: Timeout = None + self: _CardValueBoxFullScreenLayoutP, + value: bool, + *, + timeout: Timeout = None, ) -> None: """ - Expects the card to be available for full screen mode. + Expects whether full screen mode is available for the element. Parameters ---------- - available - True if the value box is expected to be available for full screen mode, False otherwise. + value + `True` if the element is expected to be available for full screen mode, False otherwise. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ playwright_expect(self._loc_fullscreen).to_have_count( - int(available), timeout=timeout + int(value), timeout=timeout ) class ValueBox( _WidthLocM, - _CardFullScreenM, - _InputWithContainer, + _CardValueBoxFullScreenM, + _UiWithContainer, ): """ - ValueBox control for :func:`~shiny.ui.value_box` + Value Box control for :func:`~shiny.ui.value_box`. """ loc: Locator """ - `Locator` for the value box's value + Playwright `Locator` for the value box's value. """ loc_showcase: Locator """ - `Locator` for the value box showcase + Playwright `Locator` for the value box showcase. """ loc_title: Locator """ - `Locator` for the value box title + Playwright `Locator` for the value box title. """ loc_body: Locator """ - `Locator` for the value box body + Playwright `Locator` for the value box body. """ def __init__(self, page: Page, id: str) -> None: """ - Initializes a new instance of the ValueBox class. + Initializes a new instance of the `ValueBox` class. Parameters ---------- page - The Playwright page object. + Playwright `Page` of the Shiny app. id The ID of the value box. @@ -2768,13 +4441,13 @@ def expect_height(self, value: StyleValue, *, timeout: Timeout = None) -> None: value The expected height value. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ expect_to_have_style(self.loc_container, "max-height", value, timeout=timeout) def expect_title( self, - text: PatternOrStr, + value: PatternOrStr, *, timeout: Timeout = None, ) -> None: @@ -2783,20 +4456,20 @@ def expect_title( Parameters ---------- - text + value The expected text pattern or string. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ playwright_expect(self.loc_title).to_have_text( - text, + value, timeout=timeout, ) def expect_value( self, - text: PatternOrStr, + value: PatternOrStr, *, timeout: Timeout = None, ) -> None: @@ -2805,19 +4478,19 @@ def expect_value( Parameters ---------- - text + value The expected text pattern or string. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ playwright_expect(self.loc).to_have_text( - text, + value, timeout=timeout, ) def expect_body( self, - text: PatternOrStr | list[PatternOrStr], + value: PatternOrStr | list[PatternOrStr], *, timeout: Timeout = None, ) -> None: @@ -2826,48 +4499,59 @@ def expect_body( Parameters ---------- - text + value The expected text pattern or list of patterns/strings. - Note: If testing against multiple elements, text should be an array + + Note: If testing against multiple elements, text should be an array. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ playwright_expect(self.loc_body).to_have_text( - text, + value, timeout=timeout, ) -class Card(_WidthLocM, _CardFooterM, _CardBodyM, _CardFullScreenM, _InputWithContainer): +class Card( + _WidthLocM, + _CardFooterM, + _CardBodyM, + _CardValueBoxFullScreenM, + _UiWithContainer, +): """ - Card control for :func:`~shiny.ui.card` + Card control for :func:`~shiny.ui.card`. """ + loc_container: Locator + """ + Playwright `Locator` for the card container. + """ loc: Locator """ - `Locator` for the card's value + Playwright `Locator` for the card's value. """ loc_title: Locator """ - `Locator` for the card title + Playwright `Locator` for the card title. """ loc_footer: Locator """ - `Locator` for the card footer + Playwright `Locator` for the card footer. """ loc_body: Locator """ - `Locator` for the card body + Playwright `Locator` for the card body. """ def __init__(self, page: Page, id: str) -> None: """ - Initializes a new instance of the Card class. + Initializes a new instance of the `Card` class. Parameters ---------- page - The Playwright page object. + Playwright `Page` of the Shiny app. id The ID of the card. """ @@ -2893,7 +4577,7 @@ def __init__(self, page: Page, id: str) -> None: def expect_header( self, - text: PatternOrStr | None, + value: PatternOrStr | None, *, timeout: Timeout = None, ) -> None: @@ -2902,16 +4586,17 @@ def expect_header( Parameters ---------- - text - The expected text pattern or string - Note: None if the header is expected to not exist. + value + The expected text pattern or string. + + Note: `None` if the header is expected to not exist. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ - if text is None: + if value is None: playwright_expect(self.loc_title).to_have_count(0, timeout=timeout) else: - playwright_expect(self.loc_title).to_have_text(text, timeout=timeout) + playwright_expect(self.loc_title).to_have_text(value, timeout=timeout) # def expect_body( # self, @@ -2928,7 +4613,7 @@ def expect_header( def expect_footer( self, - text: PatternOrStr | None, + value: PatternOrStr | None, *, timeout: Timeout = None, ) -> None: @@ -2937,16 +4622,16 @@ def expect_footer( Parameters ---------- - text + value The expected text pattern or string Note: None if the footer is expected to not exist. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ - if text is None: + if value is None: playwright_expect(self.loc_footer).to_have_count(0, timeout=timeout) else: - playwright_expect(self.loc_footer).to_have_text(text, timeout=timeout) + playwright_expect(self.loc_footer).to_have_text(value, timeout=timeout) def expect_max_height(self, value: StyleValue, *, timeout: Timeout = None) -> None: """ @@ -2957,7 +4642,7 @@ def expect_max_height(self, value: StyleValue, *, timeout: Timeout = None) -> No value The expected maximum height value. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ expect_to_have_style(self.loc_container, "max-height", value, timeout=timeout) @@ -2970,7 +4655,7 @@ def expect_min_height(self, value: StyleValue, *, timeout: Timeout = None) -> No value The expected minimum height value. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ expect_to_have_style(self.loc_container, "min-height", value, timeout=timeout) @@ -2983,43 +4668,80 @@ def expect_height(self, value: StyleValue, *, timeout: Timeout = None) -> None: value The expected height value. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ expect_to_have_style(self.loc_container, "height", value, timeout=timeout) class Accordion( _WidthLocM, - _InputWithContainer, + _UiWithContainer, ): - # *args: AccordionPanel | TagAttrs, - # id: Optional[str] = None, - # open: Optional[bool | str | list[str]] = None, - # multiple: bool = True, - # class_: Optional[str] = None, - # width: Optional[CssUnit] = None, - # height: Optional[CssUnit] = None, - # **kwargs: TagAttrValue, + """Accordion control for :func:`~shiny.ui.accordion`.""" + + loc: Locator + """ + Playwright `Locator` for each accordion items. + """ + loc_container: Locator + """ + Playwright `Locator` for the accordion container. + """ + # loc_open: Locator + # """ + # `Locator` for the open accordion panel + # """ + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a new instance of the `Accordion` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the accordion. + """ super().__init__( page, id=id, loc="> div.accordion-item", loc_container=f"div#{id}.accordion.shiny-bound-input", ) - self.loc_open = self.loc.locator( - # Return self - "xpath=.", - # Simple approach as position is not needed - has=page.locator( - "> div.accordion-collapse.show", - ), - ) + # self.loc_open = self.loc.locator( + # # Return self + # "xpath=.", + # # Simple approach as position is not needed + # has=page.locator( + # "> div.accordion-collapse.show", + # ), + # ) def expect_height(self, value: StyleValue, *, timeout: Timeout = None) -> None: + """ + Expects the accordion to have the specified height. + + Parameters + ---------- + value + The expected height. + timeout + The maximum time to wait for the height to be visible and interactable. Defaults to `None`. + """ expect_to_have_style(self.loc_container, "height", value, timeout=timeout) def expect_width(self, value: StyleValue, *, timeout: Timeout = None) -> None: + """ + Expects the accordion to have the specified width. + + Parameters + ---------- + value + The expected width. + timeout + The maximum time to wait for the width to be visible and interactable. Defaults to `None`. + """ expect_to_have_style(self.loc_container, "width", value, timeout=timeout) def expect_open( @@ -3048,6 +4770,16 @@ def expect_panels( *, timeout: Timeout = None, ) -> None: + """ + Expects the accordion to have the specified panels. + + Parameters + ---------- + value + The expected panels. + timeout + The maximum time to wait for the panels to be visible and interactable. Defaults to `None`. + """ _MultipleDomItems.expect_locator_values_in_list( page=self.page, loc_container=self.loc_container, @@ -3064,6 +4796,16 @@ def set( *, timeout: Timeout = None, ) -> None: + """ + Sets the state of the accordion panel. + + Parameters + ---------- + selected + The selected accordion panel(s). + timeout + The maximum time to wait for the accordion panel to be visible and interactable. Defaults to `None`. + """ if isinstance(selected, str): selected = [selected] for element in self.loc.element_handles(): @@ -3082,21 +4824,59 @@ def accordion_panel( self, data_value: str, ) -> AccordionPanel: + """ + Returns the accordion panel with the specified data value. + + Parameters + ---------- + data_value + The data value of the accordion panel. + """ return AccordionPanel(self.page, self.id, data_value) class AccordionPanel( _WidthLocM, - _InputWithContainer, + _UiWithContainer, ): - # self, - # *args: TagChild | TagAttrs, - # data_value: str, - # icon: TagChild | None, - # title: TagChild | None, - # id: str | None, - # **kwargs: TagAttrValue, + """ + AccordionPanel control for :func:`~shiny.ui.accordion_panel`. + """ + + loc_label: Locator + """ + Playwright `Locator` for the accordion panel's label. + """ + loc_icon: Locator + """ + Playwright `Locator` for the accordion panel's icon. + """ + loc_body: Locator + """ + Playwright `Locator` for the accordion panel's body. + """ + loc_header: Locator + """ + Playwright `Locator` for the accordion panel's header. + """ + # loc_body_visible: Locator + # """ + # Playwright `Locator` for the visible accordion panel body + # """ + def __init__(self, page: Page, id: str, data_value: str) -> None: + """ + Initializes a new instance of the `AccordionPanel` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the accordion panel. + data_value + The data value of the accordion panel. + """ super().__init__( page, id=id, @@ -3114,35 +4894,108 @@ def __init__(self, page: Page, id: str, data_value: str) -> None: self.loc_body = self.loc.locator("> .accordion-collapse") self.loc_header = self.loc.locator("> .accordion-header") - self.loc_body_visible = self.loc.locator("> .accordion-collapse.show") + self._loc_body_visible = self.loc.locator("> .accordion-collapse.show") def expect_label(self, value: PatternOrStr, *, timeout: Timeout = None) -> None: + """ + Expects the accordion panel label to have the specified text. + + Parameters + ---------- + value + The expected text pattern or string. + timeout + The maximum time to wait for the label to appear. Defaults to `None`. + """ playwright_expect(self.loc_label).to_have_text(value, timeout=timeout) def expect_body(self, value: PatternOrStr, *, timeout: Timeout = None) -> None: + """ + Expects the accordion panel body to have the specified text. + + Parameters + ---------- + value + The expected text pattern or string. + timeout + The maximum time to wait for the body to appear. Defaults to `None`. + """ playwright_expect(self.loc_body).to_have_text(value, timeout=timeout) def expect_icon(self, value: PatternOrStr, *, timeout: Timeout = None) -> None: + """ + Expects the accordion panel icon to have the specified text. + + Parameters + ---------- + value + The expected text pattern or string. + timeout + The maximum time to wait for the icon to appear. Defaults to `None`. + """ playwright_expect(self.loc_icon).to_have_text(value, timeout=timeout) - def expect_open(self, is_open: bool, *, timeout: Timeout = None) -> None: - _expect_class_value(self.loc_body, "show", is_open, timeout=timeout) + def expect_open(self, value: bool, *, timeout: Timeout = None) -> None: + """ + Expects the accordion panel to be open or closed. + + Parameters + ---------- + value + `True` if the accordion panel is expected to be open, `False` otherwise. + timeout + The maximum time to wait for the expectation to pass. Defaults to `None`. + """ + _expect_class_value(self.loc_body, "show", value, timeout=timeout) # user sends value of Open: true | false def set(self, open: bool, *, timeout: Timeout = None) -> None: + """ + Sets the state of the control to open or closed. + + Parameters + ---------- + open + `True` to open the accordion panel, False to close it. + timeout + The maximum time to wait for the control to be visible and interactable. Defaults to `None`. + """ self.loc.wait_for(state="visible", timeout=timeout) self.loc.scroll_into_view_if_needed(timeout=timeout) expect_not_to_have_class(self.loc_body, "collapsing", timeout=timeout) - if self.loc_body_visible.count() != int(open): + if self._loc_body_visible.count() != int(open): self.toggle(timeout=timeout) def toggle(self, *, timeout: Timeout = None) -> None: + """ + Toggles the state of the control. + + Parameters + ---------- + timeout + The maximum time to wait for the control to be visible and interactable. Defaults to `None`. + """ self.loc.wait_for(state="visible", timeout=timeout) self.loc.scroll_into_view_if_needed(timeout=timeout) self.loc_header.click(timeout=timeout) -class _OverlayBase(_InputBase): +class _OverlayBase(_UiBase): + """Base class for overlay controls""" + + loc_trigger: Locator + """ + Playwright `Locator` for the trigger element. + """ + loc_overlay_body: Locator + """ + Playwright `Locator` for the overlay body. + """ + loc_overlay_container: Locator + """ + Playwright `Locator` for of the overlay container. + """ + def __init__( self, page: Page, @@ -3152,6 +5005,22 @@ def __init__( overlay_name: str, overlay_selector: str, ) -> None: + """ + Initializes a new instance of the `OverlayBase` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the overlay. + loc + Playwright `Locator` of the overlay. + overlay_name + The name of the overlay. + overlay_selector + The selector of the overlay. + """ super().__init__(page, id=id, loc=loc) self._overlay_name = overlay_name self._overlay_selector = overlay_selector @@ -3186,23 +5055,61 @@ def get_loc_overlay_body(self, *, timeout: Timeout = None) -> Locator: ) def get_loc_overlay_container(self, *, timeout: Timeout = None) -> Locator: + """ + Returns the locator for the overlay container. + + Parameters + ---------- + timeout + The maximum time to wait for the overlay container to appear. Defaults to `None`. + """ return self.page.locator(f"#{self._get_overlay_id(timeout=timeout)}") def expect_body(self, value: PatternOrStr, *, timeout: Timeout = None) -> None: + """ + Expects the overlay body to have the specified text. + + Parameters + ---------- + value + The expected text pattern or string. + timeout + The maximum time to wait for the overlay body to appear. Defaults to `None`. + """ playwright_expect(self.get_loc_overlay_body(timeout=timeout)).to_have_text( value, timeout=timeout ) - def expect_active(self, active: bool, *, timeout: Timeout = None) -> None: - value = re.compile(r".*") if active else None + def expect_active(self, value: bool, *, timeout: Timeout = None) -> None: + """ + Expects the overlay to be active or inactive. + + Parameters + ---------- + value + `True` if the overlay is expected to be active, False otherwise. + timeout + The maximum time to wait for the expectation to pass. Defaults to `None`. + """ + attr_value = re.compile(r".*") if value else None return expect_attribute_to_have_value( loc=self.loc_trigger, timeout=timeout, name="aria-describedby", - value=value, + value=attr_value, ) def expect_placement(self, value: str, *, timeout: Timeout = None) -> None: + """ + Expects the overlay to have the specified placement. + + Parameters + ---------- + value + The expected placement value. + timeout + The maximum time to wait for the expectation to pass. Defaults to `None`. + """ return expect_attribute_to_have_value( loc=self.get_loc_overlay_container(timeout=timeout), timeout=timeout, @@ -3212,14 +5119,32 @@ def expect_placement(self, value: str, *, timeout: Timeout = None) -> None: class Popover(_OverlayBase): - # trigger: TagChild, - # *args: TagChild | TagAttrs, - # title: Optional[TagChild] = None, - # id: Optional[str] = None, - # placement: Literal["auto", "top", "right", "bottom", "left"] = "auto", - # options: Optional[dict[str, Any]] = None, - # **kwargs: TagAttrValue, + """Popover control for :func:`~shiny.ui.popover`.""" + + loc_trigger: Locator + """ + Playwright `Locator` for the trigger element that opens/closes the popover. + """ + loc_overlay_body: Locator + """ + Playwright `Locator` for the popover body. + """ + loc_overlay_container: Locator + """ + Playwright `Locator` for the popover container. + """ + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a new instance of the `Popover` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the popover. + """ super().__init__( page, id=id, @@ -3229,23 +5154,68 @@ def __init__(self, page: Page, id: str) -> None: ) def set(self, open: bool, timeout: Timeout = None) -> None: + """ + Sets the state of the popover. + + Parameters + ---------- + open + `True` to open the popover and `False` to close it. + timeout + The maximum time to wait for the popover to be visible and interactable. Defaults to `None`. + """ if open ^ self.get_loc_overlay_body(timeout=timeout).count() > 0: self.toggle() def toggle(self, timeout: Timeout = None) -> None: + """ + Toggles the state of the popover. + + Parameters + ---------- + timeout + The maximum time to wait for the popover to be visible and interactable. Defaults to `None`. + """ self.loc_trigger.wait_for(state="visible", timeout=timeout) self.loc_trigger.scroll_into_view_if_needed(timeout=timeout) self.loc_trigger.click(timeout=timeout) -class Tooltip(_OverlayBase): - # trigger: TagChild, - # *args: TagChild | TagAttrs, - # id: Optional[str] = None, - # placement: Literal["auto", "top", "right", "bottom", "left"] = "auto", - # options: Optional[dict[str, object]] = None, - # **kwargs: TagAttrValue, +class Tooltip(_OverlayBase): + """Tooltip control for :func:`~shiny.ui.tooltip`.""" + + loc_container: Locator + """ + Playwright `Locator` for the container tooltip. + """ + loc: Locator + """ + Playwright `Locator` for the tooltip content. + """ + loc_trigger: Locator + """ + Playwright `Locator` for the trigger element. + """ + loc_overlay_body: Locator + """ + Playwright `Locator` for the overlay body. + """ + loc_overlay_container: Locator + """ + Playwright `Locator` for the overlay container. + """ + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a new instance of the `Tooltip` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the tooltip. + """ super().__init__( page, id=id, @@ -3255,18 +5225,38 @@ def __init__(self, page: Page, id: str) -> None: ) def set(self, open: bool, timeout: Timeout = None) -> None: + """ + Sets the state of the tooltip. + + Parameters + ---------- + open + `True` to open the tooltip and `False` to close it. + timeout + The maximum time to wait for the tooltip to be visible and interactable. Defaults to `None`. + """ if open ^ self.get_loc_overlay_body(timeout=timeout).count() > 0: self.toggle(timeout=timeout) if not open: self.get_loc_overlay_body(timeout=timeout).click() def toggle(self, timeout: Timeout = None) -> None: + """ + Toggles the state of the tooltip. + + Parameters + ---------- + timeout + The maximum time to wait for the tooltip to be visible and interactable. Defaults to `None`. + """ self.loc_trigger.wait_for(state="visible", timeout=timeout) self.loc_trigger.scroll_into_view_if_needed(timeout=timeout) self.loc_trigger.hover(timeout=timeout) -class _NavItemBase(_InputWithContainer): +class _NavItemBase(_UiWithContainer): + """A Base mixin class for Nav and NavItem controls""" + def nav_item( self, value: str, @@ -3274,9 +5264,27 @@ def nav_item( return NavItem(self.page, self.id, value) def set(self, value: str, *, timeout: Timeout = None) -> None: + """ + Sets the state of the control to open or closed. + + Parameters + ---------- + value + The selected nav item. + """ self.nav_item(value).click(timeout=timeout) def expect_value(self, value: PatternOrStr, *, timeout: Timeout = None) -> None: + """ + Expects the control to have the specified value. + + Parameters + ---------- + value + The expected value. + timeout + The maximum time to wait for the expectation to pass. Defaults to `None`. + """ # data attribute of active tab and compare with value playwright_expect( self.loc_container.locator('a[role="tab"].active') @@ -3292,12 +5300,30 @@ def expect_value(self, value: PatternOrStr, *, timeout: Timeout = None) -> None: # ) def get_loc_active_content(self, *, timeout: Timeout = None) -> Locator: + """ + Returns the locator for the active content. + + Parameters + ---------- + timeout + The maximum time to wait for the locator to appear. Defaults to `None`. + """ datatab_id = self.loc_container.get_attribute("data-tabsetid", timeout=timeout) return self.page.locator( f"div.tab-content[data-tabsetid='{datatab_id}'] > div.tab-pane.active" ) def expect_content(self, value: PatternOrStr, *, timeout: Timeout = None) -> None: + """ + Expects the control to have the specified content. + + Parameters + ---------- + value + The expected content. + timeout + The maximum time to wait for the expectation to pass. Defaults to `None`. + """ playwright_expect(self.get_loc_active_content()).to_have_text( value, timeout=timeout ) @@ -3308,6 +5334,16 @@ def expect_nav_values( *, timeout: Timeout = None, ) -> None: + """ + Expects the control to have the specified nav values. + + Parameters + ---------- + value + The expected nav values. + timeout + The maximum time to wait for the expectation to pass. Defaults to `None`. + """ _MultipleDomItems.expect_locator_values_in_list( page=self.page, loc_container=self.loc_container, @@ -3324,16 +5360,47 @@ def expect_nav_titles( *, timeout: Timeout = None, ) -> None: + """ + Expects the control to have the specified nav titles. + + Parameters + ---------- + value + The expected nav titles. + timeout + The maximum time to wait for the expectation to pass. Defaults to `None`. + """ self.expect.to_have_text(value, timeout=timeout) -class NavItem(_InputWithContainer): - # *args: NavsetArg, - # id: Optional[str] = None, - # selected: Optional[str] = None, - # header: TagChild = None, - # footer: TagChild = None, +class NavItem(_UiWithContainer): + """Navigation item control for :func:`~shiny.ui.nav_item`.""" + + """ + Playwright `Locator` for the content of the nav item. + """ + loc: Locator + """ + Playwright `Locator` for the nav item. + """ + loc_container: Locator + """ + Playwright `Locator` for the nav item container. + """ + def __init__(self, page: Page, id: str, data_value: str) -> None: + """ + Initializes a new instance of the `NavItem` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the nav item. + data_value + The data value of the nav item. + """ super().__init__( page, id=id, @@ -3347,29 +5414,77 @@ def __init__(self, page: Page, id: str, data_value: str) -> None: # get active content instead of assertion @property def loc_content(self) -> Locator: - """Note. This requires 2 steps. Will not work if the overlay element is rapidly created during locator fetch""" + """ + Returns the locator for the content of the nav item. + + Note: This requires 2 steps. Will not work if the overlay element is rapidly created during locator fetch + """ datatab_id = self.loc_container.get_attribute("data-tabsetid") return self.page.locator( f"div.tab-content[data-tabsetid='{datatab_id}'] > div.tab-pane[data-value='{self._data_value}']" ) def click(self, *, timeout: Timeout = None) -> None: + """ + Clicks the nav item. + + Parameters + ---------- + timeout + The maximum time to wait for the nav item to be visible and interactable. Defaults to `None`. + """ self.loc.click(timeout=timeout) - def expect_active(self, active: bool, *, timeout: Timeout = None) -> None: - _expect_class_value(self.loc, "active", active, timeout=timeout) + def expect_active(self, value: bool, *, timeout: Timeout = None) -> None: + """ + Expects the nav item to be active or inactive. + + Parameters + ---------- + active + `True` if the nav item is expected to be active, False otherwise. + timeout + The maximum time to wait for the expectation to pass. Defaults to `None`. + """ + _expect_class_value(self.loc, "active", value, timeout=timeout) def expect_content(self, value: PatternOrStr, *, timeout: Timeout = None) -> None: + """ + Expects the nav item content to have the specified text. + + Parameters + ---------- + value + The expected text pattern or string. + timeout + The maximum time to wait for the expectation to pass. Defaults to `None`. + """ playwright_expect(self.loc_content).to_have_text(value, timeout=timeout) class NavsetTab(_NavItemBase): - # *args: NavsetArg, - # id: Optional[str] = None, - # selected: Optional[str] = None, - # header: TagChild = None, - # footer: TagChild = None, + """NavsetTab control for :func:`~shiny.ui.navset_tab`.""" + + loc: Locator + """ + Playwright `Locator` for the nav set tab. + """ + loc_container: Locator + """ + Playwright `Locator` for the nav set tab container. + """ + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a new instance of the `NavsetTab` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the nav set tab. + """ super().__init__( page, id=id, @@ -3379,7 +5494,19 @@ def __init__(self, page: Page, id: str) -> None: class NavsetPill(_NavItemBase): + """NavsetPill control for :func:`~shiny.ui.navset_pill`.""" + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a new instance of the `NavsetPill` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the nav set pill. + """ super().__init__( page, id=id, @@ -3389,7 +5516,19 @@ def __init__(self, page: Page, id: str) -> None: class NavsetUnderline(_NavItemBase): + """NavsetUnderline control for :func:`~shiny.ui.navset_underline`.""" + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a new instance of the `NavsetUnderline` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the nav set underline. + """ super().__init__( page, id=id, @@ -3399,7 +5538,19 @@ def __init__(self, page: Page, id: str) -> None: class NavsetPillList(_NavItemBase): + """NavsetPillList control for :func:`~shiny.ui.navset_pill_list`.""" + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a new instance of the `NavsetPillList` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the nav set pill list. + """ super().__init__( page, id=id, @@ -3409,7 +5560,19 @@ def __init__(self, page: Page, id: str) -> None: class NavsetCardTab(_NavItemBase): + """NavsetCardTab control for :func:`~shiny.ui.navset_card_tab`.""" + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a new instance of the `NavsetCardTab` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the nav set card tab. + """ super().__init__( page, id=id, @@ -3419,7 +5582,19 @@ def __init__(self, page: Page, id: str) -> None: class NavsetCardPill(_NavItemBase): + """NavsetCardPill control for :func:`~shiny.ui.navset_card_pill`.""" + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a new instance of the `NavsetCardPill` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the nav set card pill. + """ super().__init__( page, id=id, @@ -3429,7 +5604,19 @@ def __init__(self, page: Page, id: str) -> None: class NavsetCardUnderline(_NavItemBase): + """NavsetCardUnderline control for :func:`~shiny.ui.navset_card_underline`.""" + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a new instance of the `NavsetCardUnderline` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the nav set card underline. + """ super().__init__( page, id=id, @@ -3439,7 +5626,19 @@ def __init__(self, page: Page, id: str) -> None: class NavsetHidden(_NavItemBase): + """NavsetHidden control for :func:`~shiny.ui.navset_hidden`.""" + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a new instance of the `NavsetHidden` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the nav set hidden. + """ super().__init__( page, id=id, @@ -3449,7 +5648,19 @@ def __init__(self, page: Page, id: str) -> None: class NavsetBar(_NavItemBase): + """NavsetBar control for :func:`~shiny.ui.navset_bar`.""" + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a new instance of the `NavsetBar` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the nav set bar. + """ super().__init__( page, id=id, @@ -3458,32 +5669,36 @@ def __init__(self, page: Page, id: str) -> None: ) -class OutputDataFrame(_InputWithContainer): +class OutputDataFrame(_UiWithContainer): """ - OutputDataFrame control for :func:`~shiny.ui.output_data_frame` + OutputDataFrame control for :func:`~shiny.ui.output_data_frame`. """ + loc_container: Locator + """ + Playwright `Locator` for the data frame container. + """ loc: Locator """ - `Locator` for the data frame + Playwright `Locator` for the data frame. """ loc_head: Locator """ - `Locator` for the data frame columns + Playwright `Locator` for the head of the data frame table. """ loc_body: Locator """ - `Locator` for the data frame rows + Playwright `Locator` for the body of the data frame table. """ def __init__(self, page: Page, id: str) -> None: """ - Initializes a new instance of the OutputDataFrame class. + Initializes a new instance of the `OutputDataFrame` class. Parameters ---------- page - The Playwright page object. + Playwright `Page` of the Shiny app. id The ID of the data frame. """ @@ -3514,24 +5729,24 @@ def cell_locator(self, row: int, col: int) -> Locator: f"> td:nth-child({col + 1}), > th:nth-child({col + 1})" ) - def expect_n_row(self, row_number: int, *, timeout: Timeout = None): + def expect_n_row(self, value: int, *, timeout: Timeout = None): """ Expects the number of rows in the data frame. Parameters ---------- - row_number + value The expected number of rows. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ playwright_expect(self.loc_body.locator("> tr")).to_have_count( - row_number, timeout=timeout + value, timeout=timeout ) def expect_cell( self, - text: PatternOrStr, + value: PatternOrStr, *, row: int, col: int, @@ -3542,25 +5757,25 @@ def expect_cell( Parameters ---------- - text + value The expected text in the cell. row The row number of the cell. col The column number of the cell. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ assert_type(row, int) assert_type(col, int) self._cell_scroll_if_needed(row=row, col=col, timeout=timeout) playwright_expect(self.cell_locator(row, col)).to_have_text( - text, timeout=timeout + value, timeout=timeout ) def expect_column_labels( self, - labels: ListPatternOrStr | None, + value: ListPatternOrStr | None, *, timeout: Timeout = None, ) -> None: @@ -3569,22 +5784,23 @@ def expect_column_labels( Parameters ---------- - labels + value The expected column labels. + Note: None if the column labels are expected to not exist. edit - True if the data frame is in edit mode, False otherwise. + `True` if the data frame is to be in edit mode, `False` otherwise. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ - if isinstance(labels, list) and len(labels) == 0: - labels = None + if isinstance(value, list) and len(value) == 0: + value = None - if labels is None: + if value is None: playwright_expect(self.loc_column_label).to_have_count(0, timeout=timeout) else: playwright_expect(self.loc_column_label).to_have_text( - labels, timeout=timeout + value, timeout=timeout ) def _cell_scroll_if_needed(self, *, row: int, col: int, timeout: Timeout): @@ -3630,7 +5846,7 @@ def _cell_scroll_if_needed(self, *, row: int, col: int, timeout: Timeout): def expect_column_label( self, - text: ListPatternOrStr, + value: ListPatternOrStr, *, col: int, timeout: Timeout = None, @@ -3640,23 +5856,23 @@ def expect_column_label( Parameters ---------- + value + The expected text in the column. col The column number. - text - The expected text in the column. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ assert_type(col, int) # It's zero based, nth(0) selects the first element. playwright_expect(self.loc_column_label.nth(col - 1)).to_have_text( - text, + value, timeout=timeout, ) def expect_n_col( self, - n: int, + value: int, *, timeout: Timeout = None, ) -> None: @@ -3665,19 +5881,19 @@ def expect_n_col( Parameters ---------- - n + value The expected number of columns. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ playwright_expect(self.loc_column_label).to_have_count( - n, + value, timeout=timeout, ) def expect_cell_class( self, - class_: str, + value: str, *, row: int, col: int, @@ -3688,24 +5904,24 @@ def expect_cell_class( Parameters ---------- + value + The expected class of the cell. row The row number of the cell. col The column number of the cell. - class_ - The expected class of the cell. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ expect_to_have_class( self.cell_locator(row=row, col=col), - class_, + value, timeout=timeout, ) def select_rows( self, - rows: list[int], + value: list[int], *, timeout: Timeout = None, ) -> None: @@ -3714,18 +5930,18 @@ def select_rows( Parameters ---------- - rows + value The list of row numbers to select. timeout - The maximum time to wait for the action to complete. Defaults to None. + The maximum time to wait for the action to complete. Defaults to `None`. """ - if len(rows) > 1: - rows = sorted(rows) + if len(value) > 1: + value = sorted(value) # check if the items in the row contain all numbers from index 0 to index -1 - if rows == list(range(rows[0], rows[-1] + 1)): + if value == list(range(value[0], value[-1] + 1)): self.page.keyboard.down("Shift") - self.cell_locator(row=rows[0], col=0).click(timeout=timeout) - self.cell_locator(row=rows[-1], col=0).click(timeout=timeout) + self.cell_locator(row=value[0], col=0).click(timeout=timeout) + self.cell_locator(row=value[-1], col=0).click(timeout=timeout) self.page.keyboard.up("Shift") else: # if operating system is MacOs use Meta (Cmd) else use Ctrl key @@ -3733,7 +5949,7 @@ def select_rows( self.page.keyboard.down("Meta") else: self.page.keyboard.down("Control") - for row in rows: + for row in value: self._cell_scroll_if_needed(row=row, col=0, timeout=timeout) self.cell_locator(row=row, col=0).click(timeout=timeout) if platform.system() == "Darwin": @@ -3741,11 +5957,11 @@ def select_rows( else: self.page.keyboard.up("Control") else: - self.cell_locator(row=rows[0], col=0).click(timeout=timeout) + self.cell_locator(row=value[0], col=0).click(timeout=timeout) def expect_class_state( self, - state: str, + value: str, *, row: int, col: int, @@ -3753,18 +5969,29 @@ def expect_class_state( ): """ Expects the state of the class in the data frame. + + Parameters + ---------- + value + The expected state of the class. + row + The row number of the cell. + col + The column number of the cell. + timeout + The maximum time to wait for the expectation to pass. Defaults to `None`. """ - if state == "ready": + if value == "ready": playwright_expect(self.cell_locator(row=row, col=col)).not_to_have_class( "cell-edit-editing", timeout=timeout ) - elif state == "editing": + elif value == "editing": self.expect_cell_class("cell-edit-editing", row=row, col=col) - elif state == "saving": + elif value == "saving": self.expect_cell_class("cell-edit-saving", row=row, col=col) - elif state == "failure": + elif value == "failure": self.expect_cell_class("cell-edit-failure", row=row, col=col) - elif state == "success": + elif value == "success": self.expect_cell_class("cell-edit-success", row=row, col=col) else: raise ValueError( @@ -3791,7 +6018,7 @@ def edit_cell( col The column number of the cell. timeout - The maximum time to wait for the action to complete. Defaults to None. + The maximum time to wait for the action to complete. Defaults to `None`. """ cell = self.cell_locator(row=row, col=col) @@ -3799,9 +6026,10 @@ def edit_cell( cell.click(timeout=timeout) cell.locator("> textarea").fill(text) - # TODO-karan-test: Rename to `set_column_sorting?` + # TODO-karan-test: Rename to `set_sort?` # TODO-karan-test: Add support for a list of columns # TODO-karan-test: Add support for direction + # TODO-karan-test: Add method for testing direction def set_column_sort( self, col: int, @@ -3816,13 +6044,14 @@ def set_column_sort( col The column number to sort. timeout - The maximum time to wait for the action to complete. Defaults to None. + The maximum time to wait for the action to complete. Defaults to `None`. """ self.loc_column_label.nth(col).click(timeout=timeout) - # TODO-karan-test: Rename to `set_column_filters?` + # TODO-karan-test: Rename to `set_filter?` # TODO-karan-test: Add support for a list of columns ? If so, all other columns should be reset # TODO-karan-test: Add support for a None value reset all filters + # TODO-karan-test: Add method for testing direction def set_column_filter( self, col: int, @@ -3840,7 +6069,7 @@ def set_column_filter( text The text to filter the column. timeout - The maximum time to wait for the action to complete. Defaults to None. + The maximum time to wait for the action to complete. Defaults to `None`. """ if isinstance(text, str): self.loc_column_filter.nth(col).locator("> input").fill( @@ -3880,14 +6109,14 @@ def save_cell( col The column number of the cell. timeout - The maximum time to wait for the action to complete. Defaults to None. + The maximum time to wait for the action to complete. Defaults to `None`. """ self.edit_cell(text, row=row, col=col, timeout=timeout) self.cell_locator(row=row, col=col).locator("> textarea").press(save_key) def expect_cell_title( self, - message: str, + value: str, *, row: int, col: int, @@ -3898,24 +6127,37 @@ def expect_cell_title( Parameters ---------- + value + The expected validation message of the cell. row The row number of the cell. col The column number of the cell. - message - The expected validation message of the cell. timeout - The maximum time to wait for the expectation to pass. Defaults to None. + The maximum time to wait for the expectation to pass. Defaults to `None`. """ - playwright_expect(self.cell_locator(row=row, col=col)).to_have_attribute( - name="title", value=message, timeout=timeout + name="title", value=value, timeout=timeout ) # TODO: Use mixin for dowloadlink and download button class DownloadLink(_InputActionBase): + """ + DownloadLink control for :func:`~shiny.ui.download_link`. + """ + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a new instance of the `DownloadLink` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the download link. + """ super().__init__( page, id=id, @@ -3927,7 +6169,21 @@ class DownloadButton( _WidthLocM, _InputActionBase, ): + """ + DownloadButton control for :func:`~shiny.ui.download_button` + """ + def __init__(self, page: Page, id: str) -> None: + """ + Initializes a new instance of the `DownloadButton` class. + + Parameters + ---------- + page + Playwright `Page` of the Shiny app. + id + The ID of the download button. + """ super().__init__( page, id=id, diff --git a/tests/playwright/shiny/components/card-input/test_card-input.py b/tests/playwright/shiny/components/card-input/test_card-input.py index 4130b6003..7b14da909 100644 --- a/tests/playwright/shiny/components/card-input/test_card-input.py +++ b/tests/playwright/shiny/components/card-input/test_card-input.py @@ -27,25 +27,25 @@ def test_card_input(page: Page, app_path: str, sel_card: str, sel_vb: str) -> No out_vb = OutputCode(page, "out_value_box") # Open and close card full screen, check input value ------ - card.expect_full_screen_open(False) + card.expect_full_screen(False) out_card.expect_value("False") card.open_full_screen() - card.expect_full_screen_open(True) + card.expect_full_screen(True) out_card.expect_value("True") card.close_full_screen() - card.expect_full_screen_open(False) + card.expect_full_screen(False) out_card.expect_value("False") # Open and close value box full screen, check input value ------ - vb.expect_full_screen_open(False) + vb.expect_full_screen(False) out_vb.expect_value("False") vb.open_full_screen() - vb.expect_full_screen_open(True) + vb.expect_full_screen(True) out_vb.expect_value("True") vb.close_full_screen() - vb.expect_full_screen_open(False) + vb.expect_full_screen(False) out_vb.expect_value("False") diff --git a/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox_ks.py b/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox_ks.py index f5d72e24b..16fb9b07a 100644 --- a/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox_ks.py +++ b/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox_ks.py @@ -60,12 +60,12 @@ def test_valuebox(page: Page, local_app: ShinyAppProc) -> None: assert get_value_box_bg_color(value_box1) == "rgb(193, 0, 0)" assert get_value_box_fg_color(value_box1) == "rgb(255, 255, 255)" value_box1.expect_full_screen_available(True) - value_box1.expect_full_screen_open(False) + value_box1.expect_full_screen(False) value_box1.open_full_screen() - value_box1.expect_full_screen_open(True) + value_box1.expect_full_screen(True) value_box1.expect_body(["Inside the fullscreen"]) value_box1.close_full_screen() - value_box1.expect_full_screen_open(False) + value_box1.expect_full_screen(False) value_box2 = ValueBox(page, "valuebox2") value_box2.expect_height(None) diff --git a/tests/playwright/shiny/components/value_box/smoke/test_valuebox.py b/tests/playwright/shiny/components/value_box/smoke/test_valuebox.py index badae5341..c2715289a 100644 --- a/tests/playwright/shiny/components/value_box/smoke/test_valuebox.py +++ b/tests/playwright/shiny/components/value_box/smoke/test_valuebox.py @@ -10,9 +10,9 @@ def test_valuebox(page: Page, local_app: ShinyAppProc, value_box_id: str) -> Non page.goto(local_app.url) value_box = ValueBox(page, value_box_id) - value_box.expect_full_screen_open(False) + value_box.expect_full_screen(False) value_box.open_full_screen() - value_box.expect_full_screen_open(True) + value_box.expect_full_screen(True) if value_box_id == "valuebox1": value_box.expect_height(None) value_box.expect_title("KPI Title") @@ -30,4 +30,4 @@ def test_valuebox(page: Page, local_app: ShinyAppProc, value_box_id: str) -> Non ) assert title_tag_name == "p" value_box.close_full_screen() - value_box.expect_full_screen_open(False) + value_box.expect_full_screen(False) diff --git a/tests/playwright/shiny/experimental/card/kitchensink/test_card_ks.py b/tests/playwright/shiny/experimental/card/kitchensink/test_card_ks.py index a16476c5e..276c9df10 100644 --- a/tests/playwright/shiny/experimental/card/kitchensink/test_card_ks.py +++ b/tests/playwright/shiny/experimental/card/kitchensink/test_card_ks.py @@ -34,11 +34,11 @@ def test_card_kitchensink(page: Page, local_app: ShinyAppProc) -> None: "\nThis is the body of a card with default height w/ fullscreen", ] ) - card.expect_full_screen_open(False) + card.expect_full_screen(False) card.open_full_screen() - card.expect_full_screen_open(True) + card.expect_full_screen(True) card.close_full_screen() - card.expect_full_screen_open(False) + card.expect_full_screen(False) card = Card(page, "card2") card.expect_max_height(None) @@ -50,7 +50,7 @@ def test_card_kitchensink(page: Page, local_app: ShinyAppProc) -> None: ["\nThis is the body without a header of a footer - No Fullscreen\n"] ) assert get_body_tag_name(card) == "p" - card.expect_full_screen_open(False) + card.expect_full_screen(False) card.expect_full_screen_available(False) card = Card(page, "card3") diff --git a/tests/playwright/shiny/experimental/card/test_card.py b/tests/playwright/shiny/experimental/card/test_card.py index 7f06948fc..cc18b137e 100644 --- a/tests/playwright/shiny/experimental/card/test_card.py +++ b/tests/playwright/shiny/experimental/card/test_card.py @@ -20,8 +20,8 @@ def test_card(page: Page, local_app: ShinyAppProc) -> None: "\nThis is still the body.\n", ] ) - card.expect_full_screen_open(False) + card.expect_full_screen(False) card.open_full_screen() - card.expect_full_screen_open(True) + card.expect_full_screen(True) card.close_full_screen() - card.expect_full_screen_open(False) + card.expect_full_screen(False) diff --git a/tests/playwright/shiny/outputs/test_output_image.py b/tests/playwright/shiny/outputs/test_output_image.py index 071352a8e..f1c3c0e06 100644 --- a/tests/playwright/shiny/outputs/test_output_image.py +++ b/tests/playwright/shiny/outputs/test_output_image.py @@ -14,7 +14,7 @@ def test_output_image_kitchen(page: Page, app: ShinyAppProc) -> None: img = OutputImage(page, "image") - img.expect_inline(inline=False) + img.expect_inline(False) img.expect_img_src(re.compile(r"data:image/png;base64")) img.expect_img_height(None) diff --git a/tests/playwright/shiny/outputs/test_output_plot.py b/tests/playwright/shiny/outputs/test_output_plot.py index 67ae9664a..e2cfb59f3 100644 --- a/tests/playwright/shiny/outputs/test_output_plot.py +++ b/tests/playwright/shiny/outputs/test_output_plot.py @@ -14,7 +14,7 @@ def test_output_plot_kitchen(page: Page, app: ShinyAppProc) -> None: plot = OutputPlot(page, "p") - plot.expect_inline(inline=False) + plot.expect_inline(False) plot.expect_height("400px") plot.expect_width("100%") diff --git a/tests/playwright/shiny/plot-sizing/test_plot_sizing.py b/tests/playwright/shiny/plot-sizing/test_plot_sizing.py index cb9afc08b..ae1fa7664 100644 --- a/tests/playwright/shiny/plot-sizing/test_plot_sizing.py +++ b/tests/playwright/shiny/plot-sizing/test_plot_sizing.py @@ -41,7 +41,7 @@ def test_output_image_kitchen(page: Page, local_app: ShinyAppProc) -> None: img = OutputPlot(page, plotid) # These assertions are mostly to ensure that the plots load before we # evaluate their sizes - img.expect_inline(inline=False) + img.expect_inline(False) img.expect_img_src(re.compile(r"data:image/png;base64"), timeout=20000) rect = page.evaluate(