diff --git a/CHANGELOG.md b/CHANGELOG.md index a0f08d428..0850651f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added `.expect_widths()` to `NavsetPillList` in `shiny.playwright.controllers` for testing `ui.navset_pill_list(widths=)`. (#1668) -* Added `.expect_title()` for `Popover` controller (#1683) +* Added `.expect_title()` for `Popover` in `shiny.playwright.controllers` (#1683) + +* Added `.expect_disabled()` for `InputActionButton` in `shiny.playwright.controllers` (#1705) * Small improvements to the default pulse busy indicator to better blend with any background. It's also now slightly smaller by default.(#1707) @@ -37,11 +39,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `ui.Theme()` now works correctly on Windows when the theme requires Sass compilation. (thanks @yuuuxt, #1684) -* Fixed the `InputSlider` playwright controller's `.expect_width()` to check the `width` property within the `style` attribute. (#1691) +* Fixed multiple input controllers (`InputSlider`, `InputDate`, `InputDateRange`, `InputCheckbox`, and `InputCheckboxGroup`) in `shiny.playwright.controller` to check the `width` property within the `style` attribute. (#1691, #1696, #1702) + +* Fixed multiple input controllers (`InputSwitch`, `InputRadioButtons`, `InputNumeric`, `InputText`, `InputTextArea`, `InputPassword`, `InputActionButton`, and `InputActionLink`) in `shiny.playwright.controller` to correctly validate the `width` style property in `.expect_width(). (#1705) -* Fixed the `InputDate` and `InputDateRange` playwright controllers to check the `width` property within the `style` attribute. (#1696) +* Fixed input controller `InputTextArea` in `shiny.playwright.controller` to correctly validate the `resize` style property in `.expect_resize()`. (#1705) -* Fixed the `InputCheckbox` and `InputCheckboxGroup` playwright controllers' `.expect_width()` to check the `width` property within the `style` attribute. (#1702) * Fixed a bug in `ui.conditional_panel()` that would cause the panel to repeatedly show/hide itself when the provided condition did not evaluate to a boolean value. (#1707) diff --git a/shiny/playwright/controller/_base.py b/shiny/playwright/controller/_base.py index 329a47324..bd4433ab7 100644 --- a/shiny/playwright/controller/_base.py +++ b/shiny/playwright/controller/_base.py @@ -333,11 +333,10 @@ def expect_label( playwright_expect(self.loc_label).to_have_text(value, timeout=timeout) -class WidthLocM: +class WidthLocStlyeM: """ - A mixin class representing the `.loc`'s width. + A mixin class that provides methods to control the width of input action buttons and action links. - This class provides methods to expect the width attribute of a DOM element. """ def expect_width( @@ -356,24 +355,24 @@ def expect_width( timeout 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) + _expect_style_to_have_value(self.loc, "width", value, timeout=timeout) -class WidthContainerM: +class WidthLocM: """ - A mixin class representing the container's width. + A mixin class representing the `.loc`'s width. - This class provides methods to expect the width attribute of a DOM element's container. + This class provides methods to expect the width attribute of a DOM element. """ def expect_width( - self: UiWithContainerP, + self: UiBaseP, value: AttrValue, *, timeout: Timeout = None, ) -> None: """ - Expect the `width` attribute of a input's container to have a specific value. + Expect the `width` attribute of a DOM element to have a specific value. Parameters ---------- @@ -382,9 +381,36 @@ def expect_width( timeout 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 - ) + _expect_attribute_to_have_value(self.loc, "width", value=value, timeout=timeout) + + +# # Currently not being used, hence commenting +# class WidthContainerM: +# """ +# A mixin class representing the container's width. + +# This class provides methods to expect the width attribute of a DOM element's container. +# """ + +# def expect_width( +# self: UiWithContainerP, +# value: AttrValue, +# *, +# timeout: Timeout = None, +# ) -> None: +# """ +# Expect the `width` attribute of a input's container 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_attribute_to_have_value( +# self.loc_container, "width", value=value, timeout=timeout +# ) class WidthContainerStyleM: diff --git a/shiny/playwright/controller/_input_buttons.py b/shiny/playwright/controller/_input_buttons.py index 59326ee62..0b287a563 100644 --- a/shiny/playwright/controller/_input_buttons.py +++ b/shiny/playwright/controller/_input_buttons.py @@ -13,11 +13,17 @@ expect_attribute_to_have_value as _expect_attribute_to_have_value, ) from ..expect._internal import expect_style_to_have_value as _expect_style_to_have_value -from ._base import InputActionBase, UiBase, UiWithLabel, WidthLocM, _expect_multiple +from ._base import ( + InputActionBase, + UiBase, + UiWithLabel, + WidthLocStlyeM, + _expect_multiple, +) class InputActionButton( - WidthLocM, + WidthLocStlyeM, InputActionBase, ): """Controller for :func:`shiny.ui.input_action_button`.""" @@ -43,6 +49,21 @@ def __init__( loc=f"button#{id}.action-button.shiny-bound-input", ) + def expect_disabled(self, value: bool, *, timeout: Timeout = None): + """ + Expect the input action button to be disabled. + + Parameters + ---------- + value + The expected value of the `disabled` attribute. + timeout + The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. + """ + _expect_attribute_to_have_value( + self.loc, "disabled", "" if value else None, timeout=timeout + ) + class InputDarkMode(UiBase): """Controller for :func:`shiny.ui.input_dark_mode`.""" @@ -131,7 +152,7 @@ def expect_attribute(self, value: str, *, timeout: Timeout = None): class InputTaskButton( - WidthLocM, + WidthLocStlyeM, InputActionBase, ): """Controller for :func:`shiny.ui.input_task_button`.""" diff --git a/shiny/playwright/controller/_input_fields.py b/shiny/playwright/controller/_input_fields.py index affa9cee7..a22a173e1 100644 --- a/shiny/playwright/controller/_input_fields.py +++ b/shiny/playwright/controller/_input_fields.py @@ -17,40 +17,14 @@ from ._base import ( Resize, UiBaseP, - UiWithContainerP, UiWithLabel, - WidthLocM, + WidthContainerStyleM, all_missing, not_is_missing, set_text, ) -class InputDateWidthM: - """ - A mixin class for input date width. - This mixin class provides methods to expect the width of input date elements. - """ - - def expect_width( - self: UiWithContainerP, - value: AttrValue, - *, - timeout: Timeout = None, - ) -> None: - """ - Expect the input select to have a specific width. - - Parameters - ---------- - value - The expected width. - timeout - The maximum time to wait for the expectation to be fulfilled. Defaults to `None`. - """ - _expect_style_to_have_value(self.loc_container, "width", value, timeout=timeout) - - class _SetTextM: def set(self: UiBaseP, value: str, *, timeout: Timeout = None) -> None: """ @@ -91,7 +65,7 @@ def expect_value( class InputNumeric( _SetTextM, _ExpectTextInputValueM, - WidthLocM, + WidthContainerStyleM, UiWithLabel, ): """Controller for :func:`shiny.ui.input_numeric`.""" @@ -242,7 +216,7 @@ def expect_autocomplete( class InputText( _SetTextM, _ExpectTextInputValueM, - WidthLocM, + WidthContainerStyleM, _ExpectPlaceholderAttrM, _ExpectAutocompleteAttrM, _ExpectSpellcheckAttrM, @@ -271,6 +245,7 @@ def __init__(self, page: Page, id: str) -> None: class InputPassword( _SetTextM, _ExpectTextInputValueM, + WidthContainerStyleM, _ExpectPlaceholderAttrM, UiWithLabel, ): @@ -316,6 +291,7 @@ def expect_width( class InputTextArea( _SetTextM, + WidthContainerStyleM, _ExpectTextInputValueM, _ExpectPlaceholderAttrM, _ExpectAutocompleteAttrM, @@ -421,9 +397,7 @@ def expect_resize( 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 - ) + _expect_style_to_have_value(self.loc, "resize", value=value, timeout=timeout) def expect_autoresize( self, @@ -450,7 +424,7 @@ def expect_autoresize( class _DateBase( - InputDateWidthM, + WidthContainerStyleM, _SetTextM, UiWithLabel, ): @@ -694,7 +668,7 @@ def __init__(self, page: Page, id: str) -> None: ) -class InputDateRange(InputDateWidthM, UiWithLabel): +class InputDateRange(WidthContainerStyleM, UiWithLabel): """Controller for :func:`shiny.ui.input_date_range`.""" loc_separator: Locator diff --git a/tests/playwright/shiny/inputs/input_kitchensink/input_action_button_kitchensink/app.py b/tests/playwright/shiny/inputs/input_kitchensink/input_action_button_kitchensink/app.py new file mode 100644 index 000000000..1c3fdd985 --- /dev/null +++ b/tests/playwright/shiny/inputs/input_kitchensink/input_action_button_kitchensink/app.py @@ -0,0 +1,40 @@ +from faicons import icon_svg + +from shiny.express import input, render, ui + +ui.page_opts(title="Kitchen Sink: ui.input_action_button()", fillable=True) + +with ui.layout_columns(): + with ui.card(): + ui.h3("Default Action Button") + ui.input_action_button("default", label="Default button") + + @render.code + def default_txt(): + return f"Button clicked {input.default()} times" + + with ui.card(): + ui.h3("With Custom Width") + ui.input_action_button("width", "Wide button", width="200px") + + @render.code + def width_txt(): + return f"Button clicked {input.width()} times" + + with ui.card(): + ui.h3("With Icon") + ui.input_action_button( + "icon", "Button with icon", icon=icon_svg("trash-arrow-up") + ) + + @render.code + def icon_txt(): + return f"Button clicked {input.icon()} times" + + with ui.card(): + ui.h3("Disabled Button") + ui.input_action_button("disabled", "Disabled button", disabled=True) + + @render.code + def disabled_txt(): + return f"Button clicked {input.disabled()} times" diff --git a/tests/playwright/shiny/inputs/input_kitchensink/input_action_button_kitchensink/test_input_action_button_kitchensink.py b/tests/playwright/shiny/inputs/input_kitchensink/input_action_button_kitchensink/test_input_action_button_kitchensink.py new file mode 100644 index 000000000..e026e1e95 --- /dev/null +++ b/tests/playwright/shiny/inputs/input_kitchensink/input_action_button_kitchensink/test_input_action_button_kitchensink.py @@ -0,0 +1,27 @@ +from playwright.sync_api import Page +from playwright.sync_api import expect as playwright_expect + +from shiny.playwright import controller +from shiny.run import ShinyAppProc + + +def test_input_action_button_kitchen(page: Page, local_app: ShinyAppProc) -> None: + page.goto(local_app.url) + + default = controller.InputActionButton(page, "default") + default.expect_label("Default button") + default.expect_disabled(False) + controller.OutputCode(page, "default_txt").expect_value("Button clicked 0 times") + default.click() + controller.OutputCode(page, "default_txt").expect_value("Button clicked 1 times") + + width = controller.InputActionButton(page, "width") + width.expect_width("200px") + + disabled = controller.InputActionButton(page, "disabled") + disabled.expect_disabled(True) + # Disabled button should not have an icon + playwright_expect(disabled.loc.locator("svg.fa")).to_have_count(0) + + icon = controller.InputActionButton(page, "icon") + playwright_expect(icon.loc.locator("svg.fa")).to_have_count(1) diff --git a/tests/playwright/shiny/inputs/input_kitchensink/input_action_link_kitchensink/app.py b/tests/playwright/shiny/inputs/input_kitchensink/input_action_link_kitchensink/app.py new file mode 100644 index 000000000..d43bbe1e2 --- /dev/null +++ b/tests/playwright/shiny/inputs/input_kitchensink/input_action_link_kitchensink/app.py @@ -0,0 +1,20 @@ +from faicons import icon_svg + +from shiny.express import input, render, ui + +ui.page_opts(title="Kitchen Sink: ui.input_action_link()", fillable=True) + +with ui.layout_columns(): + with ui.card(): + ui.input_action_link("default", label="Default action link") + + @render.code + def default_txt(): + return f"Link clicked {input.default()} times" + + with ui.card(): + ui.input_action_link("icon", "Link with icon", icon=icon_svg("trash-arrow-up")) + + @render.code + def icon_txt(): + return f"Link clicked {input.icon()} times" diff --git a/tests/playwright/shiny/inputs/input_kitchensink/input_action_link_kitchensink/test_input_action_link_kitchensink.py b/tests/playwright/shiny/inputs/input_kitchensink/input_action_link_kitchensink/test_input_action_link_kitchensink.py new file mode 100644 index 000000000..f5dcca56b --- /dev/null +++ b/tests/playwright/shiny/inputs/input_kitchensink/input_action_link_kitchensink/test_input_action_link_kitchensink.py @@ -0,0 +1,19 @@ +from playwright.sync_api import Page +from playwright.sync_api import expect as playwright_expect + +from shiny.playwright import controller +from shiny.run import ShinyAppProc + + +def test_input_action_link_kitchen(page: Page, local_app: ShinyAppProc) -> None: + page.goto(local_app.url) + + default = controller.InputActionLink(page, "default") + default.expect_label("Default action link") + playwright_expect(default.loc.locator("svg.fa")).to_have_count(0) + controller.OutputCode(page, "default_txt").expect_value("Link clicked 0 times") + default.click() + controller.OutputCode(page, "default_txt").expect_value("Link clicked 1 times") + + icon = controller.InputActionLink(page, "icon") + playwright_expect(icon.loc.locator("svg.fa")).to_have_count(1) diff --git a/tests/playwright/shiny/inputs/input_controls_kitchensink/input_checkbox/app.py b/tests/playwright/shiny/inputs/input_kitchensink/input_checkbox/app.py similarity index 100% rename from tests/playwright/shiny/inputs/input_controls_kitchensink/input_checkbox/app.py rename to tests/playwright/shiny/inputs/input_kitchensink/input_checkbox/app.py diff --git a/tests/playwright/shiny/inputs/input_controls_kitchensink/input_checkbox/test_input_checkbox_kitchensink.py b/tests/playwright/shiny/inputs/input_kitchensink/input_checkbox/test_input_checkbox_kitchensink.py similarity index 100% rename from tests/playwright/shiny/inputs/input_controls_kitchensink/input_checkbox/test_input_checkbox_kitchensink.py rename to tests/playwright/shiny/inputs/input_kitchensink/input_checkbox/test_input_checkbox_kitchensink.py diff --git a/tests/playwright/shiny/inputs/input_controls_kitchensink/input_checkbox_group/app.py b/tests/playwright/shiny/inputs/input_kitchensink/input_checkbox_group/app.py similarity index 100% rename from tests/playwright/shiny/inputs/input_controls_kitchensink/input_checkbox_group/app.py rename to tests/playwright/shiny/inputs/input_kitchensink/input_checkbox_group/app.py diff --git a/tests/playwright/shiny/inputs/input_controls_kitchensink/input_checkbox_group/test_input_checkbox_group_kitchensink.py b/tests/playwright/shiny/inputs/input_kitchensink/input_checkbox_group/test_input_checkbox_group_kitchensink.py similarity index 100% rename from tests/playwright/shiny/inputs/input_controls_kitchensink/input_checkbox_group/test_input_checkbox_group_kitchensink.py rename to tests/playwright/shiny/inputs/input_kitchensink/input_checkbox_group/test_input_checkbox_group_kitchensink.py diff --git a/tests/playwright/shiny/inputs/input_kitchensink/input_numeric_kitchensink/app.py b/tests/playwright/shiny/inputs/input_kitchensink/input_numeric_kitchensink/app.py new file mode 100644 index 000000000..72916aa7c --- /dev/null +++ b/tests/playwright/shiny/inputs/input_kitchensink/input_numeric_kitchensink/app.py @@ -0,0 +1,38 @@ +from shiny.express import input, render, ui + +ui.page_opts(title="Kitchen Sink: ui.input_numeric()", fillable=True) + +with ui.layout_columns(): + with ui.card(): + ui.h3("Default Numeric Input") + ui.input_numeric("default", label="Default numeric input", value=10) + + @render.code + def default_txt(): + return str(input.default()) + + with ui.card(): + ui.h3("With Min and Max") + ui.input_numeric("min_max", "Min and Max", min=0, max=100, value=50) + + @render.code + def min_max_txt(): + return str(input.min_max()) + + +with ui.layout_columns(): + with ui.card(): + ui.h3("With Step") + ui.input_numeric("step", "Step of 0.5", step=0.5, value=2.5) + + @render.code + def step_txt(): + return str(input.step()) + + with ui.card(): + ui.h3("Custom Width") + ui.input_numeric("width", "Custom width", width="200px", value=15) + + @render.code + def width_txt(): + return str(input.width()) diff --git a/tests/playwright/shiny/inputs/input_kitchensink/input_numeric_kitchensink/test_input_numeric_kitchensink.py b/tests/playwright/shiny/inputs/input_kitchensink/input_numeric_kitchensink/test_input_numeric_kitchensink.py new file mode 100644 index 000000000..050272e7f --- /dev/null +++ b/tests/playwright/shiny/inputs/input_kitchensink/input_numeric_kitchensink/test_input_numeric_kitchensink.py @@ -0,0 +1,28 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.run import ShinyAppProc + + +def test_input_numeric_kitchen(page: Page, local_app: ShinyAppProc) -> None: + page.goto(local_app.url) + + default = controller.InputNumeric(page, "default") + default.expect_label("Default numeric input") + default.expect_value("10") + controller.OutputUi(page, "default_txt").expect.to_have_text("10") + + min_max = controller.InputNumeric(page, "min_max") + min_max.expect_max("100") + min_max.expect_min("0") + min_max.expect_value("50") + controller.OutputUi(page, "min_max_txt").expect.to_have_text("50") + + step = controller.InputNumeric(page, "step") + step.expect_step("0.5") + + width = controller.InputNumeric(page, "width") + width.expect_width("200px") + width.set("20") + width.expect_value("20") + controller.OutputUi(page, "width_txt").expect.to_have_text("20") diff --git a/tests/playwright/shiny/inputs/input_kitchensink/input_password_kitchensink/app.py b/tests/playwright/shiny/inputs/input_kitchensink/input_password_kitchensink/app.py new file mode 100644 index 000000000..f2128bd0f --- /dev/null +++ b/tests/playwright/shiny/inputs/input_kitchensink/input_password_kitchensink/app.py @@ -0,0 +1,36 @@ +from shiny.express import input, render, ui + +ui.page_opts(title="Kitchen Sink: ui.input_password()", fillable=True) + +with ui.layout_columns(): + with ui.card(): + ui.input_password("default", label="Default password input") + + @render.code + def default_txt(): + return str(input.default()) + + with ui.card(): + ui.input_password( + "placeholder", "With placeholder", placeholder="Enter password" + ) + + @render.code + def placeholder_txt(): + return str(input.placeholder()) + + +with ui.layout_columns(): + with ui.card(): + ui.input_password("width", "Custom width", width="200px") + + @render.code + def width_txt(): + return str(input.width()) + + with ui.card(): + ui.input_password("value", "With initial value", value="secret123") + + @render.code + def value_txt(): + return str(input.value()) diff --git a/tests/playwright/shiny/inputs/input_kitchensink/input_password_kitchensink/test_input_password_kitchensink.py b/tests/playwright/shiny/inputs/input_kitchensink/input_password_kitchensink/test_input_password_kitchensink.py new file mode 100644 index 000000000..93038902f --- /dev/null +++ b/tests/playwright/shiny/inputs/input_kitchensink/input_password_kitchensink/test_input_password_kitchensink.py @@ -0,0 +1,27 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.run import ShinyAppProc + + +def test_input_password_kitchen(page: Page, local_app: ShinyAppProc) -> None: + page.goto(local_app.url) + + default = controller.InputPassword(page, "default") + default.expect_label("Default password input") + default.expect_value("") + controller.OutputCode(page, "default_txt").expect_value("") + + placeholder = controller.InputPassword(page, "placeholder") + placeholder.expect_placeholder("Enter password") + placeholder.expect_value("") + + width = controller.InputPassword(page, "width") + width.expect_width("200px") + + value = controller.InputPassword(page, "value") + value.expect_value("secret123") + controller.OutputCode(page, "value_txt").expect_value("secret123") + value.set("secret456") + value.expect_value("secret456") + controller.OutputCode(page, "value_txt").expect_value("secret456") diff --git a/tests/playwright/shiny/inputs/input_kitchensink/input_radio_buttons_kitchensink/app.py b/tests/playwright/shiny/inputs/input_kitchensink/input_radio_buttons_kitchensink/app.py new file mode 100644 index 000000000..cd0aa70be --- /dev/null +++ b/tests/playwright/shiny/inputs/input_kitchensink/input_radio_buttons_kitchensink/app.py @@ -0,0 +1,49 @@ +from shiny.express import input, render, ui + +ui.page_opts(title="Kitchen Sink: ui.input_radio_buttons()", fillable=True) + +options = ["Option A", "Option B", "Option C"] +options_dict = {"a": "Option A", "b": "Option B", "c": "Option C"} + +with ui.layout_columns(): + with ui.card(): + ui.h3("Default Radio Buttons") + ui.input_radio_buttons("default", "Default radio buttons", options) + + @render.code + def default_txt(): + return str(input.default()) + + with ui.card(): + ui.h3("With Selected Value") + ui.input_radio_buttons("selected", "Preset value", options, selected="Option B") + + @render.code + def selected_txt(): + return str(input.selected()) + + with ui.card(): + ui.h3("Inline Layout") + ui.input_radio_buttons("inline", "Inline layout", options, inline=True) + + @render.code + def inline_txt(): + return str(input.inline()) + + +with ui.layout_columns(): + with ui.card(): + ui.h3("Custom Width") + ui.input_radio_buttons("width", "Custom width", options, width="30px") + + @render.code + def width_txt(): + return str(input.width()) + + with ui.card(): + ui.h3("With Named List") + ui.input_radio_buttons("choices_dict", "Named list options", options_dict) + + @render.code + def choices_dict_txt(): + return str(input.choices_dict()) diff --git a/tests/playwright/shiny/inputs/input_kitchensink/input_radio_buttons_kitchensink/test_input_radio_buttons_kitchensink.py b/tests/playwright/shiny/inputs/input_kitchensink/input_radio_buttons_kitchensink/test_input_radio_buttons_kitchensink.py new file mode 100644 index 000000000..3a924d9c3 --- /dev/null +++ b/tests/playwright/shiny/inputs/input_kitchensink/input_radio_buttons_kitchensink/test_input_radio_buttons_kitchensink.py @@ -0,0 +1,31 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.run import ShinyAppProc + + +def test_checkbox_group_kitchen(page: Page, local_app: ShinyAppProc) -> None: + page.goto(local_app.url) + + default = controller.InputRadioButtons(page, "default") + default.expect_label("Default radio buttons") + default.expect_selected("Option A") + controller.OutputCode(page, "default_txt").expect_value("Option A") + + selected = controller.InputRadioButtons(page, "selected") + selected.expect_selected("Option B") + controller.OutputCode(page, "selected_txt").expect_value("Option B") + + width = controller.InputRadioButtons(page, "width") + width.expect_width("30px") + width.expect_inline(False) + + inline = controller.InputRadioButtons(page, "inline") + inline.expect_inline(True) + + choices_dict = controller.InputRadioButtons(page, "choices_dict") + choices_dict.expect_selected("a") + controller.OutputCode(page, "choices_dict_txt").expect_value("a") + choices_dict.set("c") + choices_dict.expect_selected("c") + controller.OutputCode(page, "choices_dict_txt").expect_value("c") diff --git a/tests/playwright/shiny/inputs/input_controls_kitchensink/input_select/app.py b/tests/playwright/shiny/inputs/input_kitchensink/input_select/app.py similarity index 100% rename from tests/playwright/shiny/inputs/input_controls_kitchensink/input_select/app.py rename to tests/playwright/shiny/inputs/input_kitchensink/input_select/app.py diff --git a/tests/playwright/shiny/inputs/input_controls_kitchensink/input_select/test_input_select_kitchensink.py b/tests/playwright/shiny/inputs/input_kitchensink/input_select/test_input_select_kitchensink.py similarity index 100% rename from tests/playwright/shiny/inputs/input_controls_kitchensink/input_select/test_input_select_kitchensink.py rename to tests/playwright/shiny/inputs/input_kitchensink/input_select/test_input_select_kitchensink.py diff --git a/tests/playwright/shiny/inputs/input_controls_kitchensink/input_selectize/app.py b/tests/playwright/shiny/inputs/input_kitchensink/input_selectize/app.py similarity index 100% rename from tests/playwright/shiny/inputs/input_controls_kitchensink/input_selectize/app.py rename to tests/playwright/shiny/inputs/input_kitchensink/input_selectize/app.py diff --git a/tests/playwright/shiny/inputs/input_controls_kitchensink/input_selectize/test_input_selectize_kitchensink.py b/tests/playwright/shiny/inputs/input_kitchensink/input_selectize/test_input_selectize_kitchensink.py similarity index 100% rename from tests/playwright/shiny/inputs/input_controls_kitchensink/input_selectize/test_input_selectize_kitchensink.py rename to tests/playwright/shiny/inputs/input_kitchensink/input_selectize/test_input_selectize_kitchensink.py diff --git a/tests/playwright/shiny/inputs/input_kitchensink/input_switch_kitchensink/app.py b/tests/playwright/shiny/inputs/input_kitchensink/input_switch_kitchensink/app.py new file mode 100644 index 000000000..47363c004 --- /dev/null +++ b/tests/playwright/shiny/inputs/input_kitchensink/input_switch_kitchensink/app.py @@ -0,0 +1,28 @@ +from shiny.express import input, render, ui + +ui.page_opts(title="Kitchen Sink for input_switch()", fillable=True) + +with ui.layout_columns(): + with ui.card(): + ui.h3("Default Switch") + ui.input_switch("default", "Default switch") + + @render.code + def default_txt(): + return str(input.default()) + + with ui.card(): + ui.h3("With Value") + ui.input_switch("value", "Preset value", value=True) + + @render.code + def value_txt(): + return str(input.value()) + + with ui.card(): + ui.h3("Custom Width") + ui.input_switch("width", "Custom width", width="200px") + + @render.code + def width_txt(): + return str(input.width()) diff --git a/tests/playwright/shiny/inputs/input_kitchensink/input_switch_kitchensink/test_input_switch_kitchensink.py b/tests/playwright/shiny/inputs/input_kitchensink/input_switch_kitchensink/test_input_switch_kitchensink.py new file mode 100644 index 000000000..54d811b27 --- /dev/null +++ b/tests/playwright/shiny/inputs/input_kitchensink/input_switch_kitchensink/test_input_switch_kitchensink.py @@ -0,0 +1,26 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.run import ShinyAppProc + + +def test_switch_kitchensink(page: Page, local_app: ShinyAppProc) -> None: + page.goto(local_app.url) + + default = controller.InputSwitch(page, "default") + default.expect_label("Default switch") + default.expect_checked(False) + + default_code = controller.OutputCode(page, "default_txt") + default_code.expect_value("False") + + value = controller.InputSwitch(page, "value") + value.expect_checked(True) + controller.OutputCode(page, "value_txt").expect_value("True") + + width = controller.InputSwitch(page, "width") + width.expect_width("200px") + controller.OutputCode(page, "width_txt").expect_value("False") + width.set(True) + width.expect_checked(True) + controller.OutputCode(page, "width_txt").expect_value("True") diff --git a/tests/playwright/shiny/inputs/input_kitchensink/input_text_area_kitchensink/app.py b/tests/playwright/shiny/inputs/input_kitchensink/input_text_area_kitchensink/app.py new file mode 100644 index 000000000..35c757c22 --- /dev/null +++ b/tests/playwright/shiny/inputs/input_kitchensink/input_text_area_kitchensink/app.py @@ -0,0 +1,90 @@ +from shiny.express import input, render, ui + +ui.page_opts(title="Kitchen Sink: ui.input_text_area()", fillable=True) + +with ui.layout_columns(): + with ui.card(): + ui.input_text_area("default", label="Default text area") + + @render.code + def default_txt(): + return str(input.default()) + + with ui.card(): + ui.input_text_area( + "placeholder", "With placeholder", placeholder="Enter text here" + ) + + @render.code + def placeholder_txt(): + return str(input.placeholder()) + + +with ui.layout_columns(): + with ui.card(): + ui.input_text_area( + "custom_size", + "Custom size", + width="300px", + height="150px", + value="Resized text area", + ) + + @render.code + def custom_size_txt(): + return str(input.custom_size()) + + with ui.card(): + ui.input_text_area( + "rows", + "Custom rows", + rows=5, + value="This text area has 5 rows", + resize="none", + ) + + @render.code + def rows_txt(): + return str(input.rows()) + + with ui.card(): + ui.input_text_area( + "cols", "Custom cols", cols=30, value="This text area has 30 cols" + ) + + @render.code + def cols_txt(): + return str(input.cols()) + + +with ui.layout_columns(): + with ui.card(): + ui.input_text_area( + "autocomplete", "Autocomplete", autocomplete="name", resize="horizontal" + ) + + @render.code + def autocomplete_txt(): + return str(input.autocomplete()) + + with ui.card(): + ui.input_text_area( + "resize", "Resizable", value="You can resize this text area", resize="both" + ) + + @render.code + def resize_txt(): + return str(input.resize()) + + with ui.card(): + ui.input_text_area( + "spellcheck", + "Spellcheck", + value="paticular", + spellcheck="true", + resize="vertical", + ) + + @render.code + def spellcheck_txt(): + return str(input.spellcheck()) diff --git a/tests/playwright/shiny/inputs/input_kitchensink/input_text_area_kitchensink/test_input_text_area_kitchensink.py b/tests/playwright/shiny/inputs/input_kitchensink/input_text_area_kitchensink/test_input_text_area_kitchensink.py new file mode 100644 index 000000000..5796846f3 --- /dev/null +++ b/tests/playwright/shiny/inputs/input_kitchensink/input_text_area_kitchensink/test_input_text_area_kitchensink.py @@ -0,0 +1,42 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.run import ShinyAppProc + + +def test_input_text_area_kitchen(page: Page, local_app: ShinyAppProc) -> None: + page.goto(local_app.url) + + default = controller.InputTextArea(page, "default") + default.expect_label("Default text area") + default.expect_value("") + controller.OutputCode(page, "default_txt").expect.to_have_text("") + + placeholder = controller.InputTextArea(page, "placeholder") + placeholder.expect_placeholder("Enter text here") + placeholder.expect_value("") + + custom_size = controller.InputTextArea(page, "custom_size") + custom_size.expect_height("150px") + custom_size.expect_width("300px") + + custom_cols = controller.InputTextArea(page, "cols") + custom_cols.expect_cols("30") + + custom_rows = controller.InputTextArea(page, "rows") + custom_rows.expect_rows("5") + custom_rows.expect_resize("none") + + autocomplete = controller.InputTextArea(page, "autocomplete") + autocomplete.expect_autocomplete("name") + autocomplete.expect_resize("horizontal") + + resize = controller.InputTextArea(page, "resize") + resize.expect_resize("both") + resize.set("Some text") + resize.expect_value("Some text") + controller.OutputCode(page, "resize_txt").expect.to_have_text("Some text") + + spellcheck = controller.InputTextArea(page, "spellcheck") + spellcheck.expect_spellcheck("true") + spellcheck.expect_resize("vertical") diff --git a/tests/playwright/shiny/inputs/input_kitchensink/input_text_kitchensink/app.py b/tests/playwright/shiny/inputs/input_kitchensink/input_text_kitchensink/app.py new file mode 100644 index 000000000..cd13b5e74 --- /dev/null +++ b/tests/playwright/shiny/inputs/input_kitchensink/input_text_kitchensink/app.py @@ -0,0 +1,47 @@ +from shiny.express import input, render, ui + +ui.page_opts(title="Kitchen Sink: ui.input_text()", fillable=True) + +with ui.layout_columns(): + with ui.card(): + ui.input_text("default", label="Default text input") + + @render.code + def default_txt(): + return str(input.default()) + + with ui.card(): + ui.input_text("placeholder", "With placeholder", placeholder="Enter text here") + + @render.code + def placeholder_txt(): + return str(input.placeholder()) + + +with ui.layout_columns(): + with ui.card(): + ui.input_text( + "width", "Custom width", width="200px", value="Custom width input" + ) + + @render.code + def width_txt(): + return str(input.width()) + + with ui.card(): + ui.input_text("autocomplete", "autocomplete input", autocomplete="on") + + @render.code + def autocomplete_txt(): + return str(input.autocomplete()) + + +with ui.layout_columns(): + with ui.card(): + ui.input_text( + "spellcheck", "spellcheck input", value="paticular", spellcheck="true" + ) + + @render.code + def spellcheck_txt(): + return str(input.spellcheck()) diff --git a/tests/playwright/shiny/inputs/input_kitchensink/input_text_kitchensink/test_input_text_kitchensink.py b/tests/playwright/shiny/inputs/input_kitchensink/input_text_kitchensink/test_input_text_kitchensink.py new file mode 100644 index 000000000..42b320c42 --- /dev/null +++ b/tests/playwright/shiny/inputs/input_kitchensink/input_text_kitchensink/test_input_text_kitchensink.py @@ -0,0 +1,33 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.run import ShinyAppProc + + +def test_input_text_kitchen(page: Page, local_app: ShinyAppProc) -> None: + page.goto(local_app.url) + + default = controller.InputText(page, "default") + default.expect_label("Default text input") + default.expect_value("") + controller.OutputCode(page, "default_txt").expect.to_have_text("") + + placeholder = controller.InputText(page, "placeholder") + placeholder.expect_placeholder("Enter text here") + placeholder.expect_value("") + placeholder.expect_autocomplete("off") + + width = controller.InputText(page, "width") + width.expect_width("200px") + width.expect_value("Custom width input") + + autocomplete = controller.InputText(page, "autocomplete") + autocomplete.expect_autocomplete("on") + + spellcheck = controller.InputText(page, "spellcheck") + spellcheck.expect_spellcheck("true") + spellcheck.expect_value("paticular") + controller.OutputCode(page, "spellcheck_txt").expect.to_have_text("paticular") + spellcheck.set("Some text") + spellcheck.expect_value("Some text") + controller.OutputCode(page, "spellcheck_txt").expect.to_have_text("Some text")