diff --git a/shiny/bookmark/_restore_state.py b/shiny/bookmark/_restore_state.py index 0cdc2f85e..f5e7d2c0c 100644 --- a/shiny/bookmark/_restore_state.py +++ b/shiny/bookmark/_restore_state.py @@ -4,7 +4,7 @@ from contextlib import contextmanager from contextvars import ContextVar, Token from pathlib import Path -from typing import TYPE_CHECKING, Any, Literal, Optional +from typing import TYPE_CHECKING, Any, Literal, Optional, TypeVar, overload from urllib.parse import parse_qs, parse_qsl from .._docstring import add_example @@ -387,8 +387,15 @@ def get_current_restore_context() -> RestoreContext | None: return ctx +T = TypeVar("T") + + +@overload +def restore_input(resolved_id: ResolvedId, default: Any) -> Any: ... +@overload +def restore_input(resolved_id: None, default: T) -> T: ... @add_example() -def restore_input(resolved_id: ResolvedId, default: Any) -> Any: +def restore_input(resolved_id: ResolvedId | None, default: Any) -> Any: """ Restore an input value @@ -402,6 +409,9 @@ def restore_input(resolved_id: ResolvedId, default: Any) -> Any: default A default value to use, if there's no value to restore. """ + if resolved_id is None: + return default + if not isinstance(resolved_id, ResolvedId): raise TypeError( "Expected `resolved_id` to be of type `ResolvedId` which is returned from `shiny.module.resolve_id(id)`." diff --git a/shiny/playwright/controller/__init__.py b/shiny/playwright/controller/__init__.py index 494fd27bf..05d529e69 100644 --- a/shiny/playwright/controller/__init__.py +++ b/shiny/playwright/controller/__init__.py @@ -1,6 +1,7 @@ from ._input_buttons import ( InputActionButton, InputActionLink, + InputBookmarkButton, InputDarkMode, InputFile, InputTaskButton, @@ -77,6 +78,7 @@ __all__ = [ "InputActionButton", "InputActionLink", + "InputBookmarkButton", "InputCheckbox", "InputCheckboxGroup", "InputDarkMode", diff --git a/shiny/playwright/controller/_input_buttons.py b/shiny/playwright/controller/_input_buttons.py index 287053185..81860674a 100644 --- a/shiny/playwright/controller/_input_buttons.py +++ b/shiny/playwright/controller/_input_buttons.py @@ -47,7 +47,7 @@ def __init__( super().__init__( page, id=id, - loc=f"button#{id}.action-button.shiny-bound-input", + loc=f'button[id="{id}"].action-button.shiny-bound-input', ) def expect_disabled(self, value: bool, *, timeout: Timeout = None): @@ -66,6 +66,45 @@ def expect_disabled(self, value: bool, *, timeout: Timeout = None): ) +class InputBookmarkButton( + InputActionButton, +): + """Controller for :func:`shiny.ui.input_bookmark_button`.""" + + def __init__( + self, + page: Page, + id: str = "._bookmark_", + ) -> None: + """ + Initializes the input bookmark button. + + Parameters + ---------- + page + The page where the input bookmark button is located. + id + The id of the input bookmark button. Defaults to "._bookmark_". + """ + super().__init__( + page, + id=id, + ) + + def expect_disabled(self, value: bool, *, timeout: Timeout = None): + """ + Expect the input bookmark 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`. + """ + super().expect_disabled(value, timeout=timeout) + + class InputDarkMode(UiBase): """Controller for :func:`shiny.ui.input_dark_mode`.""" diff --git a/shiny/playwright/controller/_input_fields.py b/shiny/playwright/controller/_input_fields.py index a22a173e1..77a1b0ae6 100644 --- a/shiny/playwright/controller/_input_fields.py +++ b/shiny/playwright/controller/_input_fields.py @@ -647,6 +647,20 @@ def expect_daysofweekdisabled( timeout=timeout, ) + def set(self, 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) + self.loc.press("Enter", timeout=timeout) + class InputDate(_DateBase): def __init__(self, page: Page, id: str) -> None: diff --git a/shiny/ui/_input_check_radio.py b/shiny/ui/_input_check_radio.py index a4ee620bf..754eb475f 100644 --- a/shiny/ui/_input_check_radio.py +++ b/shiny/ui/_input_check_radio.py @@ -66,12 +66,13 @@ def input_checkbox( * :func:`~shiny.ui.input_checkbox_group` * :func:`~shiny.ui.input_radio_buttons` """ - + resolved_id = resolve_id(id) + value = restore_input(resolved_id, value) return div( div( tags.label( tags.input( - id=resolve_id(id), + id=resolved_id, type="checkbox", checked="checked" if value else None, class_="shiny-input-checkbox", @@ -142,11 +143,13 @@ def _bslib_input_checkbox( *, width: Optional[str] = None, ) -> Tag: + resolved_id = resolve_id(id) + value = restore_input(resolved_id, value) return div( div( {"class": "form-check"}, tags.input( - id=resolve_id(id), + id=resolved_id, class_="form-check-input", type="checkbox", role="switch", @@ -157,7 +160,7 @@ def _bslib_input_checkbox( # Must be wrapped in `span` for update_switch(label=) method to work tags.span(label), class_="form-check-label", - for_=resolve_id(id), + for_=resolved_id, ), class_=class_, ), @@ -218,11 +221,12 @@ def input_checkbox_group( resolved_id = resolve_id(id) input_label = shiny_input_label(resolved_id, label) + options = _generate_options( id=resolved_id, type="checkbox", choices=choices, - selected=selected, + selected=restore_input(resolved_id, selected), inline=inline, ) return div( diff --git a/shiny/ui/_input_dark_mode.py b/shiny/ui/_input_dark_mode.py index a758114b3..95c2ef6ac 100644 --- a/shiny/ui/_input_dark_mode.py +++ b/shiny/ui/_input_dark_mode.py @@ -1,16 +1,17 @@ from __future__ import annotations -__all__ = ("input_dark_mode", "update_dark_mode") - from typing import Literal, Optional from htmltools import Tag, TagAttrValue, css from .._docstring import add_example, no_example -from ..module import resolve_id +from .._namespaces import resolve_id_or_none +from ..bookmark import restore_input from ..session import Session, require_active_session from ._web_component import web_component +__all__ = ("input_dark_mode", "update_dark_mode") + BootstrapColorMode = Literal["light", "dark"] @@ -46,13 +47,11 @@ def input_dark_mode( ---------- * """ + resolved_id = resolve_id_or_none(id) if mode is not None: mode = validate_dark_mode_option(mode) - if id is not None: - id = resolve_id(id) - return web_component( "bslib-input-dark-mode", { @@ -65,9 +64,9 @@ def input_dark_mode( }, ) }, - id=id, + id=resolved_id, attribute="data-bs-theme", - mode=mode, + mode=restore_input(resolved_id, mode), **kwargs, ) diff --git a/shiny/ui/_input_date.py b/shiny/ui/_input_date.py index d88b69d38..1399bdbad 100644 --- a/shiny/ui/_input_date.py +++ b/shiny/ui/_input_date.py @@ -1,7 +1,5 @@ from __future__ import annotations -__all__ = ("input_date", "input_date_range") - import json from datetime import date from typing import Optional @@ -9,10 +7,13 @@ from htmltools import Tag, TagAttrValue, TagChild, css, div, span, tags from .._docstring import add_example +from ..bookmark import restore_input from ..module import resolve_id from ._html_deps_external import datepicker_deps from ._utils import shiny_input_label +__all__ = ("input_date", "input_date_range") + @add_example() def input_date( @@ -111,11 +112,13 @@ def input_date( """ resolved_id = resolve_id(id) + default_value = value if value is not None else date.today() + return div( shiny_input_label(resolved_id, label), _date_input_tag( id=resolved_id, - value=value, + value=restore_input(resolved_id, default_value), min=min, max=max, format=format, @@ -230,12 +233,15 @@ def input_date_range( """ resolved_id = resolve_id(id) + default_start = start if start is not None else date.today() + default_end = end if end is not None else date.today() + restored_date_range = restore_input(resolved_id, [default_start, default_end]) return div( shiny_input_label(resolved_id, label), div( _date_input_tag( id=resolved_id, - value=start, + value=restored_date_range[0], min=min, max=max, format=format, @@ -251,7 +257,7 @@ def input_date_range( ), _date_input_tag( id=resolved_id, - value=end, + value=restored_date_range[1], min=min, max=max, format=format, diff --git a/shiny/ui/_input_numeric.py b/shiny/ui/_input_numeric.py index aa27a02f7..1be180782 100644 --- a/shiny/ui/_input_numeric.py +++ b/shiny/ui/_input_numeric.py @@ -5,6 +5,7 @@ from htmltools import Tag, TagChild, css, div, tags from .._docstring import add_example +from ..bookmark import restore_input from ..module import resolve_id from ._utils import shiny_input_label @@ -69,7 +70,7 @@ def input_numeric( id=resolved_id, type="number", class_="shiny-input-number form-control", - value=value, + value=restore_input(resolved_id, value), min=min, max=max, step=step, diff --git a/shiny/ui/_input_select.py b/shiny/ui/_input_select.py index c12ffd93c..3a0ec75ba 100644 --- a/shiny/ui/_input_select.py +++ b/shiny/ui/_input_select.py @@ -8,7 +8,6 @@ "input_select", "input_selectize", ) - import copy from json import dumps from typing import Any, Mapping, Optional, Union, cast @@ -16,6 +15,7 @@ from htmltools import Tag, TagChild, TagList, css, div, tags from .._docstring import add_example +from ..bookmark import restore_input from ..module import resolve_id from ._html_deps_external import selectize_deps from ._utils import JSEval, extract_js_keys, shiny_input_label @@ -111,11 +111,12 @@ def input_selectize( * :func:`~shiny.ui.input_radio_buttons` * :func:`~shiny.ui.input_checkbox_group` """ + resolved_id = resolve_id(id) x = input_select( - id, - label, - choices, + id=resolved_id, + label=label, + choices=restore_input(resolved_id, choices), selected=selected, multiple=multiple, selectize=True, @@ -196,7 +197,11 @@ def input_select( remove_button = _resolve_remove_button(remove_button, multiple) + resolved_id = resolve_id(id) + choices_ = _normalize_choices(choices) + + selected = restore_input(resolved_id, selected) if selected is None and not multiple: selected = _find_first_option(choices_) @@ -207,8 +212,6 @@ def input_select( choices_tags = _render_choices(choices_, selected) - resolved_id = resolve_id(id) - return div( shiny_input_label(resolved_id, label), div( diff --git a/shiny/ui/_input_slider.py b/shiny/ui/_input_slider.py index c2210a680..a92380007 100644 --- a/shiny/ui/_input_slider.py +++ b/shiny/ui/_input_slider.py @@ -1,5 +1,7 @@ from __future__ import annotations +from ..bookmark import restore_input + __all__ = ( "input_slider", "SliderValueArg", @@ -140,9 +142,10 @@ def input_slider( * :func:`~shiny.ui.update_slider` """ + resolved_id = resolve_id(id) + value = restore_input(resolved_id, value) # Thanks to generic typing, max, value, etc. should be of the same type data_type = _slider_type(min) - # Make sure min, max, value, and step are all numeric # (converts dates/datetimes to milliseconds since epoch...this is the value JS wants) min_num = _as_numeric(min) @@ -201,7 +204,6 @@ def input_slider( # ionRangeSlider wants attr = 'true'/'false' props = {k: str(v).lower() if isinstance(v, bool) else v for k, v in props.items()} - resolved_id = resolve_id(id) slider_tag = div( shiny_input_label(resolved_id, label), tags.input(**props), diff --git a/shiny/ui/_input_text.py b/shiny/ui/_input_text.py index 46b3e95a5..d2a68b9ae 100644 --- a/shiny/ui/_input_text.py +++ b/shiny/ui/_input_text.py @@ -5,6 +5,7 @@ from htmltools import Tag, TagChild, css, div, tags from .._docstring import add_example +from ..bookmark import restore_input from ..module import resolve_id from ._html_deps_py_shiny import autoresize_dependency from ._utils import shiny_input_label @@ -76,7 +77,7 @@ def input_text( id=resolved_id, type="text", class_="shiny-input-text form-control", - value=value, + value=restore_input(resolved_id, value), placeholder=placeholder, autocomplete=autocomplete, spellcheck=spellcheck, @@ -180,7 +181,7 @@ def input_text_area( resolved_id = resolve_id(id) area = tags.textarea( - value, + restore_input(resolved_id, value), id=resolved_id, class_=" ".join(classes), style=css(width=None if width else "100%", height=height, resize=resize), diff --git a/tests/playwright/ai_generated_apps/bookmark/bookmark_exclude/app-core.py b/tests/playwright/ai_generated_apps/bookmark/bookmark_exclude/app-core.py new file mode 100644 index 000000000..dabbf9603 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/bookmark_exclude/app-core.py @@ -0,0 +1,56 @@ +from starlette.requests import Request + +from shiny import App, Inputs, Outputs, Session, module, reactive, render, ui + + +@module.ui +def mod_ui(): + return ui.div( + ui.input_text("text_in", "Initial Module Text"), + ui.output_text("included_module_text"), + ui.input_numeric("num_in", "Enter number", 1), + ui.output_text("included_module_num"), + ui.input_text("text_excl", "Module Excluded"), + ui.output_text("excluded_module_text"), + ) + + +@module.server +def mod_server(input: Inputs, output: Outputs, session: Session): + # 1. Fix: Correct input ID for exclusion + session.bookmark.exclude.append("text_excl") # Changed from "excluded" + + @render.text + def included_module_text(): + return f"Included text: {input.text_in()}" + + @render.text + def included_module_num(): + return f"Included num: {input.num_in()}" + + @render.text + def excluded_module_text(): + return f"Excluded text: {input.text_excl()}" + + # 2. Add: Trigger bookmarking when inputs change + @reactive.effect + @reactive.event(input.text_in, input.num_in, ignore_init=True) + async def _(): + await session.bookmark() + + +def app_ui(request: Request): + return ui.page_fluid( + mod_ui("mod1"), + ) + + +def server(input: Inputs, output: Outputs, session: Session): + mod_server("mod1") + + @session.bookmark.on_bookmarked + async def _(url: str): + await session.bookmark.update_query_string(url) + + +app = App(app_ui, server, bookmark_store="url") diff --git a/tests/playwright/ai_generated_apps/bookmark/bookmark_exclude/test_bookmark_exclusion.py b/tests/playwright/ai_generated_apps/bookmark/bookmark_exclude/test_bookmark_exclusion.py new file mode 100644 index 000000000..8bf5d53e5 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/bookmark_exclude/test_bookmark_exclusion.py @@ -0,0 +1,37 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.pytest import create_app_fixture +from shiny.run import ShinyAppProc + +app = create_app_fixture(["app-core.py"]) + + +def test_bookmark_exclusion(page: Page, app: ShinyAppProc) -> None: + page.goto(app.url) + + mod1_text_box = controller.InputText(page, "mod1-text_in") + mod1_txt = controller.OutputText(page, "mod1-included_module_text") + mod1_txt.expect_value("Included text:") + + mod1_num = controller.InputNumeric(page, "mod1-num_in") + mod1_num_txt = controller.OutputText(page, "mod1-included_module_num") + mod1_num_txt.expect_value("Included num: 1") + + mod1_excluded = controller.InputText(page, "mod1-text_excl") + mod1_excluded_txt = controller.OutputText(page, "mod1-excluded_module_text") + mod1_excluded_txt.expect_value("Excluded text:") + + mod1_text_box.set("Hello world") + mod1_txt.expect_value("Included text: Hello world") + mod1_num.set("10") + mod1_num_txt.expect_value("Included num: 10") + mod1_excluded.set("Hello excluded") + mod1_excluded_txt.expect_value("Excluded text: Hello excluded") + + # reload page + page.reload() + + mod1_txt.expect_value("Included text: Hello world") + mod1_num_txt.expect_value("Included num: 10") + mod1_excluded_txt.expect_value("Excluded text:") diff --git a/tests/playwright/ai_generated_apps/bookmark/input_checkbox/app-express.py b/tests/playwright/ai_generated_apps/bookmark/input_checkbox/app-express.py new file mode 100644 index 000000000..03f510ab8 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_checkbox/app-express.py @@ -0,0 +1,38 @@ +from shiny.express import app_opts, input, module, render, session, ui + +app_opts(bookmark_store="url", debug=True) + +with ui.card(): + ui.card_header("Bookmarking Checkbox Demo") + + # Non-modular section + ui.h3("Non-Module Section") + + # Basic checkbox with default value (False) + ui.input_checkbox(id="basic", label="Basic checkbox") + + @render.text + def basic_text(): + return f"Checkbox value: {input.basic()}" + + # module section + + @module + def checkbox_module(input, output, session): + ui.h3("Checkbox Module") + + # Basic checkbox with default value (True) + ui.input_checkbox(id="module_checkbox", label="Basic module checkbox") + + @render.text + def checkbox_text(): + return f"Checkbox value: {input.module_checkbox()}" + + checkbox_module("first") + +ui.input_bookmark_button() + + +@session.bookmark.on_bookmarked +async def _(url: str): + await session.bookmark.update_query_string(url) diff --git a/tests/playwright/ai_generated_apps/bookmark/input_checkbox/test_input_checkbox_express_bookmarking.py b/tests/playwright/ai_generated_apps/bookmark/input_checkbox/test_input_checkbox_express_bookmarking.py new file mode 100644 index 000000000..832b4f5c3 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_checkbox/test_input_checkbox_express_bookmarking.py @@ -0,0 +1,47 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.pytest import create_app_fixture +from shiny.run import ShinyAppProc + +app = create_app_fixture(["app-express.py"]) + + +def test_checkbox_demo(page: Page, app: ShinyAppProc) -> None: + page.goto(app.url) + + # Test basic checkbox + basic_checkbox = controller.InputCheckbox(page, "basic") + basic_checkbox.expect_label("Basic checkbox") + basic_checkbox.expect_checked(False) + + basic_text = controller.OutputText(page, "basic_text") + basic_text.expect_value("Checkbox value: False") + + # Test module checkbox + module_checkbox = controller.InputCheckbox(page, "first-module_checkbox") + module_checkbox.expect_label("Basic module checkbox") + module_checkbox.expect_checked(False) + + module_text = controller.OutputText(page, "first-checkbox_text") + module_text.expect_value("Checkbox value: False") + + basic_checkbox.set(True) + basic_checkbox.expect_checked(True) + basic_text.expect_value("Checkbox value: True") + module_checkbox.set(True) + module_checkbox.expect_checked(True) + module_text.expect_value("Checkbox value: True") + + bookmark_button = controller.InputBookmarkButton(page) + bookmark_button.click() + + # reload the page to test bookmark + page.reload() + + # Check if the basic checkbox is checked + basic_checkbox.expect_checked(True) + basic_text.expect_value("Checkbox value: True") + # Check if the module checkbox is checked + module_checkbox.expect_checked(True) + module_text.expect_value("Checkbox value: True") diff --git a/tests/playwright/ai_generated_apps/bookmark/input_checkbox_group/app-express.py b/tests/playwright/ai_generated_apps/bookmark/input_checkbox_group/app-express.py new file mode 100644 index 000000000..03f314854 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_checkbox_group/app-express.py @@ -0,0 +1,46 @@ +from shiny.express import app_opts, input, module, render, session, ui + +app_opts(bookmark_store="url") + +with ui.card(): + ui.card_header("Bookmarking Checkbox Group Demo") + + # Non-modular section + ui.h3("Non-Module Section") + + # Basic checkbox group with choices + ui.input_checkbox_group( + id="basic", + label="Basic checkbox group", + choices=["Option 1", "Option 2", "Option 3"], + ) + + @render.text + def basic_text(): + return f"Checkbox group values: {input.basic()}" + + # module section + + @module + def checkbox_module(input, output, session): + ui.h3("Checkbox Group Module") + + # Module checkbox group with choices + ui.input_checkbox_group( + id="module_checkbox", + label="Module checkbox group", + choices=["Choice A", "Choice B", "Choice C"], + ) + + @render.text + def checkbox_text(): + return f"Checkbox group values: {input.module_checkbox()}" + + checkbox_module("first") + +ui.input_bookmark_button() + + +@session.bookmark.on_bookmarked +async def _(url: str): + await session.bookmark.update_query_string(url) diff --git a/tests/playwright/ai_generated_apps/bookmark/input_checkbox_group/test_input_checkbox_group_express_bookmarking.py b/tests/playwright/ai_generated_apps/bookmark/input_checkbox_group/test_input_checkbox_group_express_bookmarking.py new file mode 100644 index 000000000..57b0b2a7e --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_checkbox_group/test_input_checkbox_group_express_bookmarking.py @@ -0,0 +1,50 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.pytest import create_app_fixture +from shiny.run import ShinyAppProc + +app = create_app_fixture(["app-express.py"]) + + +def test_checkbox_group_demo(page: Page, app: ShinyAppProc) -> None: + page.goto(app.url) + + # Test basic checkbox group + basic_group = controller.InputCheckboxGroup(page, "basic") + basic_group.expect_label("Basic checkbox group") + basic_group.expect_selected([]) + + basic_text = controller.OutputText(page, "basic_text") + basic_text.expect_value("Checkbox group values: ()") + + # Test module checkbox group + module_group = controller.InputCheckboxGroup(page, "first-module_checkbox") + module_group.expect_label("Module checkbox group") + module_group.expect_selected([]) + + module_text = controller.OutputText(page, "first-checkbox_text") + module_text.expect_value("Checkbox group values: ()") + + # Select values + basic_group.set(["Option 1", "Option 3"]) + basic_group.expect_selected(["Option 1", "Option 3"]) + basic_text.expect_value("Checkbox group values: ('Option 1', 'Option 3')") + + module_group.set(["Choice A", "Choice C"]) + module_group.expect_selected(["Choice A", "Choice C"]) + module_text.expect_value("Checkbox group values: ('Choice A', 'Choice C')") + + # Bookmark the state + bookmark_button = controller.InputBookmarkButton(page) + bookmark_button.click() + + # Reload the page to test bookmark + page.reload() + + # Check if selections are preserved + basic_group.expect_selected(["Option 1", "Option 3"]) + basic_text.expect_value("Checkbox group values: ('Option 1', 'Option 3')") + + module_group.expect_selected(["Choice A", "Choice C"]) + module_text.expect_value("Checkbox group values: ('Choice A', 'Choice C')") diff --git a/tests/playwright/ai_generated_apps/bookmark/input_dark_mode/app-express.py b/tests/playwright/ai_generated_apps/bookmark/input_dark_mode/app-express.py new file mode 100644 index 000000000..8e1d7d218 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_dark_mode/app-express.py @@ -0,0 +1,40 @@ +from shiny.express import app_opts, input, module, render, session, ui + +app_opts(bookmark_store="url") + +with ui.card(): + ui.card_header("Bookmarking Dark Mode Demo") + + # Non-modular section + ui.h3("Non-Module Section") + + # Basic dark mode toggle + ui.input_dark_mode(id="basic", label="Basic dark mode toggle", mode="dark") + + @render.text + def basic_text(): + return f"Dark mode value: {input.basic()}" + + # module section + + @module + def dark_mode_module(input, output, session): + ui.h3("Dark Mode Module") + + # Module dark mode toggle + ui.input_dark_mode( + id="module_dark_mode", label="Module dark mode toggle", mode="dark" + ) + + @render.text + def dark_mode_text(): + return f"Dark mode value: {input.module_dark_mode()}" + + dark_mode_module("first") + +ui.input_bookmark_button() + + +@session.bookmark.on_bookmarked +async def _(url: str): + await session.bookmark.update_query_string(url) diff --git a/tests/playwright/ai_generated_apps/bookmark/input_dark_mode/test_input_dark_mode_express_bookmarking.py b/tests/playwright/ai_generated_apps/bookmark/input_dark_mode/test_input_dark_mode_express_bookmarking.py new file mode 100644 index 000000000..64db71b89 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_dark_mode/test_input_dark_mode_express_bookmarking.py @@ -0,0 +1,39 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.pytest import create_app_fixture +from shiny.run import ShinyAppProc + +app = create_app_fixture(["app-express.py"]) + + +def test_dark_mode_demo(page: Page, app: ShinyAppProc) -> None: + page.goto(app.url) + + # Test basic checkbox group + basic_dark_mode = controller.InputDarkMode(page, "basic") + basic_text = controller.OutputText(page, "basic_text") + + basic_text.expect_value("Dark mode value: dark") + + # Test module dark mode + module_group = controller.InputDarkMode(page, "first-module_dark_mode") + module_text = controller.OutputText(page, "first-dark_mode_text") + + module_text.expect_value("Dark mode value: dark") + + module_group.click() + basic_text.expect_value("Dark mode value: light") + + # Bookmark the state + bookmark_button = controller.InputBookmarkButton(page) + bookmark_button.click() + + basic_dark_mode.click() + module_text.expect_value("Dark mode value: dark") + + # Reload the page to test bookmark + page.reload() + + basic_text.expect_value("Dark mode value: light") + module_text.expect_value("Dark mode value: light") diff --git a/tests/playwright/ai_generated_apps/bookmark/input_date/app-express.py b/tests/playwright/ai_generated_apps/bookmark/input_date/app-express.py new file mode 100644 index 000000000..d83a3ca28 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_date/app-express.py @@ -0,0 +1,46 @@ +from shiny.express import app_opts, input, module, render, session, ui + +app_opts(bookmark_store="url") + +with ui.card(): + ui.card_header("Bookmarking Date Input Demo") + + # Non-modular section + ui.h3("Non-Module Section") + + # Basic date input + ui.input_date( + id="basic", + label="Basic date input", + value="2024-01-01", + ) + + @render.text + def basic_text(): + return f"Date value: {input.basic()}" + + # module section + + @module + def date_module(input, output, session): + ui.h3("Date Input Module") + + # Module date input + ui.input_date( + id="module_date", + label="Module date input", + value="2024-01-01", + ) + + @render.text + def date_text(): + return f"Date value: {input.module_date()}" + + date_module("first") + +ui.input_bookmark_button() + + +@session.bookmark.on_bookmarked +async def _(url: str): + await session.bookmark.update_query_string(url) diff --git a/tests/playwright/ai_generated_apps/bookmark/input_date/test_bookmark_input_date_express_bookmarking.py b/tests/playwright/ai_generated_apps/bookmark/input_date/test_bookmark_input_date_express_bookmarking.py new file mode 100644 index 000000000..f02f81af4 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_date/test_bookmark_input_date_express_bookmarking.py @@ -0,0 +1,46 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.pytest import create_app_fixture +from shiny.run import ShinyAppProc + +app = create_app_fixture(["app-express.py"]) + + +def test_bookmark_date_inputs(page: Page, app: ShinyAppProc) -> None: + page.goto(app.url) + + # Test basic date input + date1 = controller.InputDate(page, "basic") + date1.expect_label("Basic date input") + date1.expect_value("2024-01-01") + + text1 = controller.OutputText(page, "basic_text") + text1.expect_value("Date value: 2024-01-01") + + module_date = controller.InputDate(page, "first-module_date") + module_date.expect_label("Module date input") + module_date.expect_value("2024-01-01") + module_date.set("2024-02-02") + + text2 = controller.OutputText(page, "first-date_text") + text2.expect_value("Date value: 2024-02-02") + text1.expect_value("Date value: 2024-01-01") + + # Bookmark the state + bookmark_button = controller.InputBookmarkButton(page) + bookmark_button.click() + + date1.set("2024-03-03") + text1.expect_value("Date value: 2024-03-03") + + module_date.set("2024-04-04") + text2.expect_value("Date value: 2024-04-04") + + # Reload the page to test bookmark + page.reload() + + date1.expect_value("2024-01-01") + text1.expect_value("Date value: 2024-01-01") + module_date.expect_value("2024-02-02") + text2.expect_value("Date value: 2024-02-02") diff --git a/tests/playwright/ai_generated_apps/bookmark/input_date_range/app-express.py b/tests/playwright/ai_generated_apps/bookmark/input_date_range/app-express.py new file mode 100644 index 000000000..84bd4c23b --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_date_range/app-express.py @@ -0,0 +1,45 @@ +from shiny.express import app_opts, input, module, render, session, ui + +app_opts(bookmark_store="url") + +with ui.card(): + ui.card_header("Bookmarking Date Range Demo") + + # Non-modular section + ui.h3("Non-Module Section") + + # Basic date range with default values + ui.input_date_range( + id="basic", label="Basic date range", start="2023-01-01", end="2023-12-31" + ) + + @render.text + def basic_text(): + return f"Date range values: {input.basic()}" + + # module section + + @module + def date_module(input, output, session): + ui.h3("Date Range Module") + + # Module date range + ui.input_date_range( + id="module_date_range", + label="Module date range", + start="2023-06-01", + end="2023-06-30", + ) + + @render.text + def date_text(): + return f"Date range values: {input.module_date_range()}" + + date_module("first") + +ui.input_bookmark_button() + + +@session.bookmark.on_bookmarked +async def _(url: str): + await session.bookmark.update_query_string(url) diff --git a/tests/playwright/ai_generated_apps/bookmark/input_date_range/test_input_date_range_express_bookmarking.py b/tests/playwright/ai_generated_apps/bookmark/input_date_range/test_input_date_range_express_bookmarking.py new file mode 100644 index 000000000..5db5343dd --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_date_range/test_input_date_range_express_bookmarking.py @@ -0,0 +1,62 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.pytest import create_app_fixture +from shiny.run import ShinyAppProc + +app = create_app_fixture(["app-express.py"]) + + +def test_date_range_input(page: Page, app: ShinyAppProc) -> None: + page.goto(app.url) + + # Initialize the date range input controller + date_range = controller.InputDateRange(page, "basic") + + # Check initial state + date_range.expect_label("Basic date range") + + basic_text = controller.OutputText(page, "basic_text") + basic_text.expect_value( + "Date range values: (datetime.date(2023, 1, 1), datetime.date(2023, 12, 31))" + ) + + mod_date_range = controller.InputDateRange(page, "first-module_date_range") + mod_date_range.expect_label("Module date range") + mod_date_range.set(("2023-06-01", "2023-06-30")) + + mod_date_range_txt = controller.OutputText(page, "first-date_text") + mod_date_range_txt.expect_value( + "Date range values: (datetime.date(2023, 6, 1), datetime.date(2023, 6, 30))" + ) + + # Change the date range values + date_range.set(("2024-01-01", "2024-01-31")) + basic_text.expect_value( + "Date range values: (datetime.date(2024, 1, 1), datetime.date(2024, 1, 31))" + ) + + mod_date_range.set(("2024-02-01", "2024-02-28")) + mod_date_range_txt.expect_value( + "Date range values: (datetime.date(2024, 2, 1), datetime.date(2024, 2, 28))" + ) + + # click bookmark button + bookmark_button = controller.InputBookmarkButton(page) + bookmark_button.click() + + mod_date_range.set(("2024-03-01", "2024-03-31")) + mod_date_range_txt.expect_value( + "Date range values: (datetime.date(2024, 3, 1), datetime.date(2024, 3, 31))" + ) + + # Reload the page to test bookmark + page.reload() + + basic_text.expect_value( + "Date range values: (datetime.date(2024, 1, 1), datetime.date(2024, 1, 31))" + ) + + mod_date_range_txt.expect_value( + "Date range values: (datetime.date(2024, 2, 1), datetime.date(2024, 2, 28))" + ) diff --git a/tests/playwright/ai_generated_apps/bookmark/input_file/app-express.py b/tests/playwright/ai_generated_apps/bookmark/input_file/app-express.py new file mode 100644 index 000000000..83fb894eb --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_file/app-express.py @@ -0,0 +1,54 @@ +from shiny.express import app_opts, input, module, render, session, ui + +app_opts(bookmark_store="url") + +with ui.card(): + ui.card_header("Bookmarking File Input Demo") + + # Non-modular section + ui.h3("Non-Module Section") + + # Basic file input + ui.input_file(id="basic", label="Basic file input") + + @render.text + def basic_text(): + files = input.basic() + if files is None or len(files) == 0: + return "No files selected" + + # Extract file names + file_names = [f["name"] for f in files] + return f"File name(s): {', '.join(file_names)}" + + # module section + + @module + def date_module(input, output, session): + ui.h3("File Input Module") + + # Module file input + ui.input_file( + id="module_file", + label="Module file input", + multiple=True, + ) + + @render.text + def mod_text(): + files = input.module_file() + if files is None or len(files) == 0: + return "No files selected" + + # Extract file names + file_names = [f["name"] for f in files] + return f"File name(s): {', '.join(file_names)}" + + date_module("first") + +ui.input_bookmark_button() + + +@session.bookmark.on_bookmarked +async def _(url: str): + await session.bookmark.update_query_string(url) diff --git a/tests/playwright/ai_generated_apps/bookmark/input_file/test_input_file_express_bookmarking.py b/tests/playwright/ai_generated_apps/bookmark/input_file/test_input_file_express_bookmarking.py new file mode 100644 index 000000000..e4a1011d8 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_file/test_input_file_express_bookmarking.py @@ -0,0 +1,57 @@ +import pytest +from playwright.sync_api import FilePayload, Page + +from shiny.playwright import controller +from shiny.pytest import create_app_fixture +from shiny.run import ShinyAppProc + +app = create_app_fixture(["app-express.py"]) + + +@pytest.mark.skip("Broken test! TODO: Barret") +def test_file_input_bookmarking(page: Page, app: ShinyAppProc) -> None: + page.goto(app.url) + + # Test file input component + file_input = controller.InputFile(page, "basic") + mod_file_input = controller.InputFile(page, "first-module_file") + + file_output_txt = controller.OutputText(page, "basic_text") + mod_file_output_txt = controller.OutputText(page, "first-mod_text") + + mod_file_output_txt.expect_value("No files selected") + file_output_txt.expect_value("No files selected") + + # Note: The file content is a CSV with a header and some data + # Simulate uploading a CSV file + file_info: FilePayload = { + "name": "users.csv", + "mimeType": "text/csv", + "buffer": b',user_id,name,email\n1,Alice,alice@example.com\n2,"Bob, Los Angeles", bob\n', + } + + file_input.set(file_info) + file_input.expect_complete() + + file_output_txt.expect_value("File name(s): users.csv") + + # simulate uploading multiple files + file_info2 = file_info.copy() + file_info2["name"] = "users2.csv" + file_info2["buffer"] = ( + b",user_id,name,email\n3,Charlie,charlie@example.com\n4,Dave,dave@example.com\n" + ) + mod_file_input.set([file_info, file_info2]) + file_input.expect_complete() + + mod_file_output_txt.expect_value("File name(s): users.csv, users2.csv") + + # click bookmark button + bookmark_button = controller.InputBookmarkButton(page) + bookmark_button.click() + + page.reload() + + # Check if the values are retained after reloading the page + file_output_txt.expect_value("File name(s): users.csv") + mod_file_output_txt.expect_value("File name(s): users.csv, users2.csv") diff --git a/tests/playwright/ai_generated_apps/bookmark/input_password/app-express.py b/tests/playwright/ai_generated_apps/bookmark/input_password/app-express.py new file mode 100644 index 000000000..111631d0e --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_password/app-express.py @@ -0,0 +1,52 @@ +from shiny.express import app_opts, input, module, render, session, ui + +app_opts(bookmark_store="url", debug=True) + +with ui.card(): + ui.card_header("Bookmarking Password Demo") + + # Non-modular section + ui.h3("Non-Module Section") + + # Changed from checkbox to password input + ui.input_password(id="basic", label="Basic password") + + # Added text input for non-module section + ui.input_text(id="basic_text", label="Basic text input") + + @render.text + def basic_password_value(): + return f"Password value: {input.basic()}" + + @render.text + def basic_text_value(): + return f"Text input value: {input.basic_text()}" + + # module section + + @module + def checkbox_module(input, output, session): + ui.h3("Password Module") + + # password input + ui.input_password(id="module_password", label="Basic module password") + + # Added text input for module section + ui.input_text(id="module_text", label="Module text input") + + @render.text + def password_text(): + return f"Password value: {input.module_password()}" + + @render.text + def text_value(): + return f"Text input value: {input.module_text()}" + + checkbox_module("first") + +ui.input_bookmark_button() + + +@session.bookmark.on_bookmarked +async def _(url: str): + await session.bookmark.update_query_string(url) diff --git a/tests/playwright/ai_generated_apps/bookmark/input_password/test_input_password_express_bookmarking.py b/tests/playwright/ai_generated_apps/bookmark/input_password/test_input_password_express_bookmarking.py new file mode 100644 index 000000000..6630e7b96 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_password/test_input_password_express_bookmarking.py @@ -0,0 +1,57 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.pytest import create_app_fixture +from shiny.run import ShinyAppProc + +app = create_app_fixture(["app-express.py"]) + + +def test_text_input_demo(page: Page, app: ShinyAppProc) -> None: + page.goto(app.url) + + # Test text input + text_input = controller.InputText(page, "basic_text") + text_output = controller.OutputText(page, "basic_text_value") + + mod_input_txt = controller.InputText(page, "first-module_text") + mod_output_txt = controller.OutputText(page, "first-text_value") + + # Test password input + password_input = controller.InputPassword(page, "basic") + password_output = controller.OutputText(page, "basic_password_value") + + mod_input_password = controller.InputPassword(page, "first-module_password") + mod_output_password = controller.OutputText(page, "first-password_text") + + # Check initial values + text_output.expect_value("Text input value:") + mod_output_txt.expect_value("Text input value:") + password_output.expect_value("Password value:") + mod_output_password.expect_value("Password value:") + + # Set text input values + text_input.set("Hello world") + text_output.expect_value("Text input value: Hello world") + + mod_input_txt.set("Hello Miami") + mod_output_txt.expect_value("Text input value: Hello Miami") + + # Set password input values + password_input.set("password") + password_output.expect_value("Password value: password") + + mod_input_password.set("password") + mod_output_password.expect_value("Password value: password") + + # click bookmark button + bookmark_button = controller.InputBookmarkButton(page) + bookmark_button.click() + + page.reload() + + # Check if the values are retained after reloading the page + text_output.expect_value("Text input value: Hello world") + mod_output_txt.expect_value("Text input value: Hello Miami") + password_output.expect_value("Password value:") + mod_output_password.expect_value("Password value:") diff --git a/tests/playwright/ai_generated_apps/bookmark/input_radio_buttons/app-express.py b/tests/playwright/ai_generated_apps/bookmark/input_radio_buttons/app-express.py new file mode 100644 index 000000000..a584d1b30 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_radio_buttons/app-express.py @@ -0,0 +1,46 @@ +from shiny.express import app_opts, input, module, render, session, ui + +app_opts(bookmark_store="url") + +with ui.card(): + ui.card_header("Bookmarking Radio Buttons Demo") + + # Non-modular section + ui.h3("Non-Module Section") + + # Basic radio buttons with choices + ui.input_radio_buttons( + id="basic", + label="Basic radio buttons", + choices=["Option 1", "Option 2", "Option 3"], + ) + + @render.text + def basic_text(): + return f"Radio button value: {input.basic()}" + + # module section + + @module + def radio_module(input, output, session): + ui.h3("Radio Buttons Module") + + # Module radio buttons with choices + ui.input_radio_buttons( + id="module_radio", + label="Module radio buttons", + choices=["Choice A", "Choice B", "Choice C"], + ) + + @render.text + def radio_text(): + return f"Radio button value: {input.module_radio()}" + + radio_module("first") + +ui.input_bookmark_button() + + +@session.bookmark.on_bookmarked +async def _(url: str): + await session.bookmark.update_query_string(url) diff --git a/tests/playwright/ai_generated_apps/bookmark/input_radio_buttons/test_input_radio_buttons_express_bookmarking.py b/tests/playwright/ai_generated_apps/bookmark/input_radio_buttons/test_input_radio_buttons_express_bookmarking.py new file mode 100644 index 000000000..2dfff8ba1 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_radio_buttons/test_input_radio_buttons_express_bookmarking.py @@ -0,0 +1,45 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.pytest import create_app_fixture +from shiny.run import ShinyAppProc + +app = create_app_fixture(["app-express.py"]) + + +def test_radio_buttons_demo(page: Page, app: ShinyAppProc) -> None: + page.goto(app.url) + + # Test radio buttons input + radio_buttons = controller.InputRadioButtons(page, "basic") + selection_output = controller.OutputText(page, "basic_text") + + mod_radio_button = controller.InputRadioButtons(page, "first-module_radio") + mod_selection_output = controller.OutputText(page, "first-radio_text") + + radio_buttons.set("Option 2") + radio_buttons.expect_selected("Option 2") + selection_output.expect_value("Radio button value: Option 2") + + mod_radio_button.set("Choice B") + mod_radio_button.expect_selected("Choice B") + mod_selection_output.expect_value("Radio button value: Choice B") + + # click bookmark button + bookmark_button = controller.InputBookmarkButton(page) + bookmark_button.click() + + mod_radio_button.set("Choice C") + mod_selection_output.expect_value("Radio button value: Choice C") + + radio_buttons.set("Option 3") + selection_output.expect_value("Radio button value: Option 3") + + # Reload the page to test bookmark + page.reload() + + radio_buttons.expect_selected("Option 2") + selection_output.expect_value("Radio button value: Option 2") + + mod_radio_button.expect_selected("Choice B") + mod_selection_output.expect_value("Radio button value: Choice B") diff --git a/tests/playwright/ai_generated_apps/bookmark/input_select/app-express.py b/tests/playwright/ai_generated_apps/bookmark/input_select/app-express.py new file mode 100644 index 000000000..54ff84f06 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_select/app-express.py @@ -0,0 +1,50 @@ +from shiny.express import app_opts, input, module, render, session, ui + +app_opts(bookmark_store="url") + +with ui.card(): + ui.card_header("Bookmarking Select Demo") + + # Non-modular section + ui.h3("Non-Module Section") + + # Basic select with choices + ui.input_select( + id="basic", + label="Basic select", + choices={"option1": "Option 1", "option2": "Option 2", "option3": "Option 3"}, + ) + + @render.text + def basic_text(): + return f"Select value: {input.basic()}" + + # module section + + @module + def select_module(input, output, session): + ui.h3("Select Module") + + # Module select with choices + ui.input_select( + id="module_select", + label="Module select", + choices={ + "choiceA": "Choice A", + "choiceB": "Choice B", + "choiceC": "Choice C", + }, + ) + + @render.text + def select_text(): + return f"Select value: {input.module_select()}" + + select_module("first") + +ui.input_bookmark_button() + + +@session.bookmark.on_bookmarked +async def _(url: str): + await session.bookmark.update_query_string(url) diff --git a/tests/playwright/ai_generated_apps/bookmark/input_select/test_input_select_express_bookmarking.py b/tests/playwright/ai_generated_apps/bookmark/input_select/test_input_select_express_bookmarking.py new file mode 100644 index 000000000..72db2c452 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_select/test_input_select_express_bookmarking.py @@ -0,0 +1,50 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.pytest import create_app_fixture +from shiny.run import ShinyAppProc + +app = create_app_fixture(["app-express.py"]) + + +def test_input_select_demo(page: Page, app: ShinyAppProc) -> None: + page.goto(app.url) + + # Test basic select with simple choices + select1 = controller.InputSelect(page, "basic") + select1.expect_label("Basic select") + + basic_select_txt = controller.OutputText(page, "basic_text") + basic_select_txt.expect_value("Select value: option1") + + mod_select1 = controller.InputSelect(page, "first-module_select") + mod_select1.expect_label("Module select") + mod_select_txt = controller.OutputText(page, "first-select_text") + mod_select_txt.expect_value("Select value: choiceA") + + # Change the basic select value + select1.set("option2") + basic_select_txt.expect_value("Select value: option2") + + # Change the module select value + mod_select1.set("choiceB") + mod_select_txt.expect_value("Select value: choiceB") + + # click bookmark button + bookmark_button = controller.InputBookmarkButton(page) + bookmark_button.click() + + # Change the basic select value again + select1.set("option3") + basic_select_txt.expect_value("Select value: option3") + + # Change the module select value again + mod_select1.set("choiceC") + mod_select_txt.expect_value("Select value: choiceC") + + # Reload the page to test bookmark + page.reload() + + basic_select_txt.expect_value("Select value: option2") + + mod_select_txt.expect_value("Select value: choiceB") diff --git a/tests/playwright/ai_generated_apps/bookmark/input_slider/app-express.py b/tests/playwright/ai_generated_apps/bookmark/input_slider/app-express.py new file mode 100644 index 000000000..55909438b --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_slider/app-express.py @@ -0,0 +1,40 @@ +from shiny.express import app_opts, input, module, render, session, ui + +app_opts(bookmark_store="url") + +with ui.card(): + ui.card_header("Bookmarking Slider Demo") + + # Non-modular section + ui.h3("Non-Module Section") + + # Basic slider + ui.input_slider(id="basic", label="Basic slider", min=0, max=100, value=50) + + @render.text + def basic_text(): + return f"Slider value: {input.basic()}" + + # module section + + @module + def slider_module(input, output, session): + ui.h3("Slider Module") + + # Module slider + ui.input_slider( + id="module_slider", label="Module slider", min=0, max=100, value=50 + ) + + @render.text + def slider_text(): + return f"Slider value: {input.module_slider()}" + + slider_module("first") + +ui.input_bookmark_button() + + +@session.bookmark.on_bookmarked +async def _(url: str): + await session.bookmark.update_query_string(url) diff --git a/tests/playwright/ai_generated_apps/bookmark/input_slider/test_input_slider_express_bookmarking.py b/tests/playwright/ai_generated_apps/bookmark/input_slider/test_input_slider_express_bookmarking.py new file mode 100644 index 000000000..db87bff7d --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_slider/test_input_slider_express_bookmarking.py @@ -0,0 +1,50 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.pytest import create_app_fixture +from shiny.run import ShinyAppProc + +app = create_app_fixture(["app-express.py"]) + + +def test_slider_parameters(page: Page, app: ShinyAppProc) -> None: + page.goto(app.url) + + # Test basic numeric slider + slider1 = controller.InputSlider(page, "basic") + value1 = controller.OutputText(page, "basic_text") + + slider1.expect_value("50") + value1.expect_value("Slider value: 50") + + mod_slider1 = controller.InputSlider(page, "first-module_slider") + mod_value1 = controller.OutputText(page, "first-slider_text") + + mod_slider1.expect_value("50") + mod_value1.expect_value("Slider value: 50") + + # Change the basic slider value + slider1.set("75") + value1.expect_value("Slider value: 75") + + # Change the module slider value + mod_slider1.set("25") + mod_value1.expect_value("Slider value: 25") + + # click bookmark button + bookmark_button = controller.InputBookmarkButton(page) + bookmark_button.click() + + # Change the basic slider value again + slider1.set("100") + value1.expect_value("Slider value: 100") + + # Change the module slider value again + mod_slider1.set("0") + mod_value1.expect_value("Slider value: 0") + + # Reload the page to test bookmark + page.reload() + + value1.expect_value("Slider value: 75") + mod_value1.expect_value("Slider value: 25") diff --git a/tests/playwright/ai_generated_apps/bookmark/input_switch/app-express.py b/tests/playwright/ai_generated_apps/bookmark/input_switch/app-express.py new file mode 100644 index 000000000..a2605bf2a --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_switch/app-express.py @@ -0,0 +1,38 @@ +from shiny.express import app_opts, input, module, render, session, ui + +app_opts(bookmark_store="url") + +with ui.card(): + ui.card_header("Bookmarking Switch Demo") + + # Non-modular section + ui.h3("Non-Module Section") + + # Basic switch with choices + ui.input_switch(id="basic", label="Basic switch", value=False) + + @render.text + def basic_text(): + return f"Switch value: {input.basic()}" + + # module section + + @module + def switch_module(input, output, session): + ui.h3("Switch Module") + + # Module switch + ui.input_switch(id="module_switch", label="Module switch", value=False) + + @render.text + def switch_text(): + return f"Switch value: {input.module_switch()}" + + switch_module("first") + +ui.input_bookmark_button() + + +@session.bookmark.on_bookmarked +async def _(url: str): + await session.bookmark.update_query_string(url) diff --git a/tests/playwright/ai_generated_apps/bookmark/input_switch/test_input_switch_express_bookmarking.py b/tests/playwright/ai_generated_apps/bookmark/input_switch/test_input_switch_express_bookmarking.py new file mode 100644 index 000000000..3f55db807 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_switch/test_input_switch_express_bookmarking.py @@ -0,0 +1,47 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.pytest import create_app_fixture +from shiny.run import ShinyAppProc + +app = create_app_fixture(["app-express.py"]) + + +def test_switch_demo(page: Page, app: ShinyAppProc) -> None: + page.goto(app.url) + + switch1 = controller.InputSwitch(page, "basic") + basic_txt = controller.OutputText(page, "basic_text") + switch2 = controller.InputSwitch(page, "first-module_switch") + module_txt = controller.OutputText(page, "first-switch_text") + + # Check initial values + switch1.expect_checked(False) + basic_txt.expect_value("Switch value: False") + switch2.expect_checked(False) + module_txt.expect_value("Switch value: False") + + # Toggle switches + switch1.set(True) + switch1.expect_checked(True) + + switch2.set(True) + switch2.expect_checked(True) + + basic_txt.expect_value("Switch value: True") + module_txt.expect_value("Switch value: True") + + # click bookmark button + bookmark_button = controller.InputBookmarkButton(page) + bookmark_button.click() + + switch1.set(False) + switch1.expect_checked(False) + switch2.set(False) + switch2.expect_checked(False) + + page.reload() + basic_txt.expect_value("Switch value: True") + module_txt.expect_value("Switch value: True") + switch1.expect_checked(True) + switch2.expect_checked(True) diff --git a/tests/playwright/ai_generated_apps/bookmark/input_text/app-express.py b/tests/playwright/ai_generated_apps/bookmark/input_text/app-express.py new file mode 100644 index 000000000..cdd2ee1f7 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_text/app-express.py @@ -0,0 +1,40 @@ +from shiny.express import app_opts, input, module, render, session, ui + +app_opts(bookmark_store="url") + +with ui.card(): + ui.card_header("Bookmarking Text Input Demo") + + # Non-modular section + ui.h3("Non-Module Section") + + # Basic text input + ui.input_text(id="basic", label="Basic text input", value="Type something here") + + @render.text + def basic_text(): + return f"Text input value: {input.basic()}" + + # module section + + @module + def text_module(input, output, session): + ui.h3("Text Input Module") + + # Module text input + ui.input_text( + id="module_text", label="Module text input", value="Type something here" + ) + + @render.text + def text_text(): + return f"Text input value: {input.module_text()}" + + text_module("first") + +ui.input_bookmark_button() + + +@session.bookmark.on_bookmarked +async def _(url: str): + await session.bookmark.update_query_string(url) diff --git a/tests/playwright/ai_generated_apps/bookmark/input_text/test_input_text_express_bookmarking.py b/tests/playwright/ai_generated_apps/bookmark/input_text/test_input_text_express_bookmarking.py new file mode 100644 index 000000000..1bc2f8b74 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_text/test_input_text_express_bookmarking.py @@ -0,0 +1,42 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.pytest import create_app_fixture +from shiny.run import ShinyAppProc + +app = create_app_fixture(["app-express.py"]) + + +def test_text_input_demo(page: Page, app: ShinyAppProc) -> None: + page.goto(app.url) + + # Test text input + text_input = controller.InputText(page, "basic") + text_output = controller.OutputText(page, "basic_text") + + mod_input_txt = controller.InputText(page, "first-module_text") + mod_output_txt = controller.OutputText(page, "first-text_text") + + text_output.expect_value("Text input value: Type something here") + mod_output_txt.expect_value("Text input value: Type something here") + + text_input.set("Hello world") + text_output.expect_value("Text input value: Hello world") + + mod_input_txt.set("Hello Miami") + mod_output_txt.expect_value("Text input value: Hello Miami") + + # click bookmark button + bookmark_button = controller.InputBookmarkButton(page) + bookmark_button.click() + + text_input.set("Hello again") + text_output.expect_value("Text input value: Hello again") + mod_input_txt.set("Hello again Miami") + mod_output_txt.expect_value("Text input value: Hello again Miami") + + # reload pagw + page.reload() + + text_output.expect_value("Text input value: Hello world") + mod_output_txt.expect_value("Text input value: Hello Miami") diff --git a/tests/playwright/ai_generated_apps/bookmark/input_text_area/app-express.py b/tests/playwright/ai_generated_apps/bookmark/input_text_area/app-express.py new file mode 100644 index 000000000..c4e9b8ac1 --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_text_area/app-express.py @@ -0,0 +1,42 @@ +from shiny.express import app_opts, input, module, render, session, ui + +app_opts(bookmark_store="url") + +with ui.card(): + ui.card_header("Bookmarking Text Area Demo") + + # Non-modular section + ui.h3("Non-Module Section") + + # Basic text area with value + ui.input_text_area(id="basic", label="Basic text area", value="Enter text here") + + @render.text + def basic_text(): + return f"Text area value: {input.basic()}" + + # module section + + @module + def text_area_module(input, output, session): + ui.h3("Text Area Module") + + # Module text area with value + ui.input_text_area( + id="module_text_area", + label="Module text area", + value="Enter module text here", + ) + + @render.text + def text_area_text(): + return f"Text area value: {input.module_text_area()}" + + text_area_module("first") + +ui.input_bookmark_button() + + +@session.bookmark.on_bookmarked +async def _(url: str): + await session.bookmark.update_query_string(url) diff --git a/tests/playwright/ai_generated_apps/bookmark/input_text_area/test_input_text_area_express_bookmarking.py b/tests/playwright/ai_generated_apps/bookmark/input_text_area/test_input_text_area_express_bookmarking.py new file mode 100644 index 000000000..1cf10a86a --- /dev/null +++ b/tests/playwright/ai_generated_apps/bookmark/input_text_area/test_input_text_area_express_bookmarking.py @@ -0,0 +1,42 @@ +from playwright.sync_api import Page + +from shiny.playwright import controller +from shiny.pytest import create_app_fixture +from shiny.run import ShinyAppProc + +app = create_app_fixture(["app-express.py"]) + + +def test_text_area_demo(page: Page, app: ShinyAppProc) -> None: + page.goto(app.url) + + # Test text input + text_input = controller.InputTextArea(page, "basic") + text_output = controller.OutputText(page, "basic_text") + + mod_input_txt = controller.InputTextArea(page, "first-module_text_area") + mod_output_txt = controller.OutputText(page, "first-text_area_text") + + text_output.expect_value("Text area value: Enter text here") + mod_output_txt.expect_value("Text area value: Enter module text here") + + text_input.set("Hello world") + text_output.expect_value("Text area value: Hello world") + + mod_input_txt.set("Hello Miami") + mod_output_txt.expect_value("Text area value: Hello Miami") + + # click bookmark button + bookmark_button = controller.InputBookmarkButton(page) + bookmark_button.click() + + text_input.set("Hello again") + text_output.expect_value("Text area value: Hello again") + mod_input_txt.set("Hello again Miami") + mod_output_txt.expect_value("Text area value: Hello again Miami") + + # reload page + page.reload() + + text_output.expect_value("Text area value: Hello world") + mod_output_txt.expect_value("Text area value: Hello Miami") diff --git a/tests/playwright/ai_generated_apps/accordion/app-core.py b/tests/playwright/ai_generated_apps/core_express/accordion/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/accordion/app-core.py rename to tests/playwright/ai_generated_apps/core_express/accordion/app-core.py diff --git a/tests/playwright/ai_generated_apps/accordion/app-express.py b/tests/playwright/ai_generated_apps/core_express/accordion/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/accordion/app-express.py rename to tests/playwright/ai_generated_apps/core_express/accordion/app-express.py diff --git a/tests/playwright/ai_generated_apps/accordion/test_accordion_core_express.py b/tests/playwright/ai_generated_apps/core_express/accordion/test_accordion_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/accordion/test_accordion_core_express.py rename to tests/playwright/ai_generated_apps/core_express/accordion/test_accordion_core_express.py diff --git a/tests/playwright/ai_generated_apps/accordion_panel/app-core.py b/tests/playwright/ai_generated_apps/core_express/accordion_panel/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/accordion_panel/app-core.py rename to tests/playwright/ai_generated_apps/core_express/accordion_panel/app-core.py diff --git a/tests/playwright/ai_generated_apps/accordion_panel/app-express.py b/tests/playwright/ai_generated_apps/core_express/accordion_panel/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/accordion_panel/app-express.py rename to tests/playwright/ai_generated_apps/core_express/accordion_panel/app-express.py diff --git a/tests/playwright/ai_generated_apps/accordion_panel/test_accordion_panel_core_express.py b/tests/playwright/ai_generated_apps/core_express/accordion_panel/test_accordion_panel_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/accordion_panel/test_accordion_panel_core_express.py rename to tests/playwright/ai_generated_apps/core_express/accordion_panel/test_accordion_panel_core_express.py diff --git a/tests/playwright/ai_generated_apps/card/app-core.py b/tests/playwright/ai_generated_apps/core_express/card/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/card/app-core.py rename to tests/playwright/ai_generated_apps/core_express/card/app-core.py diff --git a/tests/playwright/ai_generated_apps/card/app-express.py b/tests/playwright/ai_generated_apps/core_express/card/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/card/app-express.py rename to tests/playwright/ai_generated_apps/core_express/card/app-express.py diff --git a/tests/playwright/ai_generated_apps/card/test_card_core_express.py b/tests/playwright/ai_generated_apps/core_express/card/test_card_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/card/test_card_core_express.py rename to tests/playwright/ai_generated_apps/core_express/card/test_card_core_express.py diff --git a/tests/playwright/ai_generated_apps/card_footer/app-core.py b/tests/playwright/ai_generated_apps/core_express/card_footer/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/card_footer/app-core.py rename to tests/playwright/ai_generated_apps/core_express/card_footer/app-core.py diff --git a/tests/playwright/ai_generated_apps/card_footer/app-express.py b/tests/playwright/ai_generated_apps/core_express/card_footer/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/card_footer/app-express.py rename to tests/playwright/ai_generated_apps/core_express/card_footer/app-express.py diff --git a/tests/playwright/ai_generated_apps/card_footer/test_card_footer_core_express.py b/tests/playwright/ai_generated_apps/core_express/card_footer/test_card_footer_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/card_footer/test_card_footer_core_express.py rename to tests/playwright/ai_generated_apps/core_express/card_footer/test_card_footer_core_express.py diff --git a/tests/playwright/ai_generated_apps/input_action_button/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_action_button/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_action_button/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_action_button/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_action_button/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_action_button/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_action_button/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_action_button/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_action_button/test_input_action_button_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_action_button/test_input_action_button_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_action_button/test_input_action_button_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_action_button/test_input_action_button_core_express.py diff --git a/tests/playwright/ai_generated_apps/input_action_link/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_action_link/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_action_link/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_action_link/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_action_link/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_action_link/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_action_link/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_action_link/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_action_link/test_input_action_link_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_action_link/test_input_action_link_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_action_link/test_input_action_link_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_action_link/test_input_action_link_core_express.py diff --git a/tests/playwright/ai_generated_apps/input_checkbox/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_checkbox/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_checkbox/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_checkbox/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_checkbox/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_checkbox/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_checkbox/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_checkbox/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_checkbox/test_input_checkbox_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_checkbox/test_input_checkbox_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_checkbox/test_input_checkbox_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_checkbox/test_input_checkbox_core_express.py diff --git a/tests/playwright/ai_generated_apps/input_checkbox_group/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_checkbox_group/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_checkbox_group/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_checkbox_group/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_checkbox_group/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_checkbox_group/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_checkbox_group/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_checkbox_group/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_checkbox_group/test_input_checkbox_group_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_checkbox_group/test_input_checkbox_group_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_checkbox_group/test_input_checkbox_group_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_checkbox_group/test_input_checkbox_group_core_express.py diff --git a/tests/playwright/ai_generated_apps/input_dark_mode/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_dark_mode/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_dark_mode/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_dark_mode/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_dark_mode/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_dark_mode/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_dark_mode/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_dark_mode/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_dark_mode/test_input_dark_mode_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_dark_mode/test_input_dark_mode_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_dark_mode/test_input_dark_mode_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_dark_mode/test_input_dark_mode_core_express.py diff --git a/tests/playwright/ai_generated_apps/input_date/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_date/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_date/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_date/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_date/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_date/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_date/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_date/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_date/test_input_date_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_date/test_input_date_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_date/test_input_date_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_date/test_input_date_core_express.py diff --git a/tests/playwright/ai_generated_apps/input_date_range/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_date_range/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_date_range/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_date_range/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_date_range/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_date_range/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_date_range/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_date_range/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_date_range/test_input_date_range_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_date_range/test_input_date_range_core_express.py similarity index 77% rename from tests/playwright/ai_generated_apps/input_date_range/test_input_date_range_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_date_range/test_input_date_range_core_express.py index dbf081bf3..117da1e64 100644 --- a/tests/playwright/ai_generated_apps/input_date_range/test_input_date_range_core_express.py +++ b/tests/playwright/ai_generated_apps/core_express/input_date_range/test_input_date_range_core_express.py @@ -31,17 +31,17 @@ def test_date_range_input(page: Page, app: ShinyAppProc) -> None: date_range.expect_autoclose("true") # Test autoclose # Test setting new values - date_range.set(("2023-06-01", "2023-06-30")) - date_range.expect_value(("2023-06-01", "2023-06-30")) + date_range.set(("06/01/2023", "06/30/2023")) + date_range.expect_value(("06/01/2023", "06/30/2023")) # Test setting only start date - date_range.set(("2023-07-01", None)) - date_range.expect_value(("2023-07-01", "2023-06-30")) + date_range.set(("07/01/2023", None)) + date_range.expect_value(("07/01/2023", "06/30/2023")) # Test setting only end date - date_range.set((None, "2023-07-31")) - date_range.expect_value(("2023-07-01", "2023-07-31")) + date_range.set((None, "07/31/2023")) + date_range.expect_value(("07/01/2023", "07/31/2023")) # Test setting dates at the boundaries - date_range.set(("2020-01-01", "2025-12-31")) # Min and max dates - date_range.expect_value(("2020-01-01", "2025-12-31")) + date_range.set(("01/01/2020", "12/31/2025")) # Min and max dates + date_range.expect_value(("01/01/2020", "12/31/2025")) diff --git a/tests/playwright/ai_generated_apps/input_file/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_file/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_file/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_file/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_file/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_file/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_file/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_file/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_file/test_input_file_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_file/test_input_file_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_file/test_input_file_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_file/test_input_file_core_express.py diff --git a/tests/playwright/ai_generated_apps/input_numeric/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_numeric/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_numeric/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_numeric/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_numeric/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_numeric/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_numeric/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_numeric/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_numeric/test_input_numeric_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_numeric/test_input_numeric_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_numeric/test_input_numeric_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_numeric/test_input_numeric_core_express.py diff --git a/tests/playwright/ai_generated_apps/input_password/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_password/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_password/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_password/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_password/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_password/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_password/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_password/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_password/test_input_password_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_password/test_input_password_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_password/test_input_password_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_password/test_input_password_core_express.py diff --git a/tests/playwright/ai_generated_apps/input_radio_buttons/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_radio_buttons/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_radio_buttons/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_radio_buttons/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_radio_buttons/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_radio_buttons/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_radio_buttons/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_radio_buttons/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_radio_buttons/test_input_radio_buttons_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_radio_buttons/test_input_radio_buttons_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_radio_buttons/test_input_radio_buttons_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_radio_buttons/test_input_radio_buttons_core_express.py diff --git a/tests/playwright/ai_generated_apps/input_select/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_select/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_select/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_select/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_select/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_select/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_select/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_select/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_select/test_input_select_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_select/test_input_select_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_select/test_input_select_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_select/test_input_select_core_express.py diff --git a/tests/playwright/ai_generated_apps/input_slider/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_slider/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_slider/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_slider/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_slider/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_slider/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_slider/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_slider/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_slider/test_input_slider_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_slider/test_input_slider_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_slider/test_input_slider_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_slider/test_input_slider_core_express.py diff --git a/tests/playwright/ai_generated_apps/input_switch/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_switch/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_switch/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_switch/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_switch/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_switch/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_switch/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_switch/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_switch/test_input_switch_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_switch/test_input_switch_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_switch/test_input_switch_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_switch/test_input_switch_core_express.py diff --git a/tests/playwright/ai_generated_apps/input_task_button/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_task_button/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_task_button/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_task_button/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_task_button/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_task_button/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_task_button/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_task_button/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_task_button/test_input_task_button_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_task_button/test_input_task_button_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_task_button/test_input_task_button_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_task_button/test_input_task_button_core_express.py diff --git a/tests/playwright/ai_generated_apps/input_text/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_text/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_text/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_text/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_text/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_text/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_text/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_text/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_text/test_input_text_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_text/test_input_text_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_text/test_input_text_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_text/test_input_text_core_express.py diff --git a/tests/playwright/ai_generated_apps/input_text_area/app-core.py b/tests/playwright/ai_generated_apps/core_express/input_text_area/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_text_area/app-core.py rename to tests/playwright/ai_generated_apps/core_express/input_text_area/app-core.py diff --git a/tests/playwright/ai_generated_apps/input_text_area/app-express.py b/tests/playwright/ai_generated_apps/core_express/input_text_area/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_text_area/app-express.py rename to tests/playwright/ai_generated_apps/core_express/input_text_area/app-express.py diff --git a/tests/playwright/ai_generated_apps/input_text_area/test_input_text_area_core_express.py b/tests/playwright/ai_generated_apps/core_express/input_text_area/test_input_text_area_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/input_text_area/test_input_text_area_core_express.py rename to tests/playwright/ai_generated_apps/core_express/input_text_area/test_input_text_area_core_express.py diff --git a/tests/playwright/ai_generated_apps/navset_pill/app-core.py b/tests/playwright/ai_generated_apps/core_express/navset_pill/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/navset_pill/app-core.py rename to tests/playwright/ai_generated_apps/core_express/navset_pill/app-core.py diff --git a/tests/playwright/ai_generated_apps/navset_pill/app-express.py b/tests/playwright/ai_generated_apps/core_express/navset_pill/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/navset_pill/app-express.py rename to tests/playwright/ai_generated_apps/core_express/navset_pill/app-express.py diff --git a/tests/playwright/ai_generated_apps/navset_pill/test_navset_pill_core_express.py b/tests/playwright/ai_generated_apps/core_express/navset_pill/test_navset_pill_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/navset_pill/test_navset_pill_core_express.py rename to tests/playwright/ai_generated_apps/core_express/navset_pill/test_navset_pill_core_express.py diff --git a/tests/playwright/ai_generated_apps/update_text/app-core.py b/tests/playwright/ai_generated_apps/core_express/update_text/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/update_text/app-core.py rename to tests/playwright/ai_generated_apps/core_express/update_text/app-core.py diff --git a/tests/playwright/ai_generated_apps/update_text/app-express.py b/tests/playwright/ai_generated_apps/core_express/update_text/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/update_text/app-express.py rename to tests/playwright/ai_generated_apps/core_express/update_text/app-express.py diff --git a/tests/playwright/ai_generated_apps/update_text/test_update_text_core_express.py b/tests/playwright/ai_generated_apps/core_express/update_text/test_update_text_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/update_text/test_update_text_core_express.py rename to tests/playwright/ai_generated_apps/core_express/update_text/test_update_text_core_express.py diff --git a/tests/playwright/ai_generated_apps/update_text_area/app-core.py b/tests/playwright/ai_generated_apps/core_express/update_text_area/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/update_text_area/app-core.py rename to tests/playwright/ai_generated_apps/core_express/update_text_area/app-core.py diff --git a/tests/playwright/ai_generated_apps/update_text_area/app-express.py b/tests/playwright/ai_generated_apps/core_express/update_text_area/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/update_text_area/app-express.py rename to tests/playwright/ai_generated_apps/core_express/update_text_area/app-express.py diff --git a/tests/playwright/ai_generated_apps/update_text_area/test_update_text_area_core_express.py b/tests/playwright/ai_generated_apps/core_express/update_text_area/test_update_text_area_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/update_text_area/test_update_text_area_core_express.py rename to tests/playwright/ai_generated_apps/core_express/update_text_area/test_update_text_area_core_express.py diff --git a/tests/playwright/ai_generated_apps/update_tooltip/app-core.py b/tests/playwright/ai_generated_apps/core_express/update_tooltip/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/update_tooltip/app-core.py rename to tests/playwright/ai_generated_apps/core_express/update_tooltip/app-core.py diff --git a/tests/playwright/ai_generated_apps/update_tooltip/app-express.py b/tests/playwright/ai_generated_apps/core_express/update_tooltip/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/update_tooltip/app-express.py rename to tests/playwright/ai_generated_apps/core_express/update_tooltip/app-express.py diff --git a/tests/playwright/ai_generated_apps/update_tooltip/test_update_tooltip_core_express.py b/tests/playwright/ai_generated_apps/core_express/update_tooltip/test_update_tooltip_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/update_tooltip/test_update_tooltip_core_express.py rename to tests/playwright/ai_generated_apps/core_express/update_tooltip/test_update_tooltip_core_express.py diff --git a/tests/playwright/ai_generated_apps/value_box/app-core.py b/tests/playwright/ai_generated_apps/core_express/value_box/app-core.py similarity index 100% rename from tests/playwright/ai_generated_apps/value_box/app-core.py rename to tests/playwright/ai_generated_apps/core_express/value_box/app-core.py diff --git a/tests/playwright/ai_generated_apps/value_box/app-express.py b/tests/playwright/ai_generated_apps/core_express/value_box/app-express.py similarity index 100% rename from tests/playwright/ai_generated_apps/value_box/app-express.py rename to tests/playwright/ai_generated_apps/core_express/value_box/app-express.py diff --git a/tests/playwright/ai_generated_apps/value_box/test_value_box_core_express.py b/tests/playwright/ai_generated_apps/core_express/value_box/test_value_box_core_express.py similarity index 100% rename from tests/playwright/ai_generated_apps/value_box/test_value_box_core_express.py rename to tests/playwright/ai_generated_apps/core_express/value_box/test_value_box_core_express.py