diff --git a/setup.cfg b/setup.cfg index d2587a133..8a3f2ca01 100644 --- a/setup.cfg +++ b/setup.cfg @@ -125,9 +125,12 @@ include = shiny, shiny.* shiny = py.typed [options.entry_points] +pytest11 = + shiny-test = shiny.test._pytest console_scripts = shiny = shiny._main:main + [flake8] # E302: Expected 2 blank lines # E501: Line too long diff --git a/shiny/test/__init__.py b/shiny/test/__init__.py new file mode 100644 index 000000000..2ebfe86ca --- /dev/null +++ b/shiny/test/__init__.py @@ -0,0 +1,24 @@ +try: + import pytest_playwright # noqa: F401 # pyright: ignore[reportUnusedImport, reportMissingTypeStubs] +except ImportError: + raise ImportError( + "The shiny.test module requires the pytest-playwright package to be installed." + " Please install it with this command:" + "\n\n pip install pytest-playwright" + ) + + +from ._conftest import ShinyAppProc + +# from ._expect import expect_to_change +from ._playwright import Locator, Page, expect + +__all__ = ( + # TODO-future: Find the proper location for these methods to be returned + # "run_shiny_app", + # "expect_to_change", + "ShinyAppProc", + "Page", + "Locator", + "expect", +) diff --git a/shiny/test/_conftest.py b/shiny/test/_conftest.py new file mode 100644 index 000000000..7a80a5285 --- /dev/null +++ b/shiny/test/_conftest.py @@ -0,0 +1,347 @@ +from __future__ import annotations + +import datetime +import logging +import subprocess +import sys +import threading +from pathlib import PurePath +from types import TracebackType +from typing import IO, Any, Callable, Generator, List, Optional, TextIO, Type, Union + +import shiny._utils + +__all__ = ( + "ShinyAppProc", + "run_shiny_app", + # For internal use only + # "shiny_app_gen", +) + + +class OutputStream: + """ + Designed to wrap an IO[str] and accumulate the output using a bg thread + + Also allows for blocking waits for particular lines. + """ + + def __init__(self, io: IO[str], desc: Optional[str] = None): + self._io = io + self._closed = False + self._lines: List[str] = [] + self._cond = threading.Condition() + self._thread = threading.Thread( + group=None, target=self._run, daemon=True, name=desc + ) + + self._thread.start() + + def _run(self): + """ + Add lines into self._lines in a tight loop. + """ + + try: + while not self._io.closed: + try: + line = self._io.readline() + except ValueError: + # This is raised when the stream is closed + break + if line != "": + with self._cond: + self._lines.append(line) + self._cond.notify_all() + finally: + # If we got here, we're finished reading self._io and need to signal any + # waiters that we're done and they'll never hear from us again. + with self._cond: + self._closed = True + self._cond.notify_all() + + def wait_for(self, predicate: Callable[[str], bool], timeout_secs: float) -> bool: + """ + Wait until the predicate returns True for a line in the output. + + Parameters + ---------- + predicate + A function that takes a line of output and returns True if the line + satisfies the condition. + timeoutSecs + How long to wait for the predicate to return True before raising a + TimeoutError. + """ + timeout_at = datetime.datetime.now() + datetime.timedelta(seconds=timeout_secs) + pos = 0 + with self._cond: + while True: + while pos < len(self._lines): + if predicate(self._lines[pos]): + return True + pos += 1 + if self._closed: + return False + else: + remaining = (timeout_at - datetime.datetime.now()).total_seconds() + if remaining < 0 or not self._cond.wait(timeout=remaining): + # Timed out + raise TimeoutError( + "Timeout while waiting for Shiny app to become ready" + ) + + def __str__(self): + with self._cond: + return "".join(self._lines) + + +def dummyio() -> TextIO: + io = TextIO() + io.close() + return io + + +class ShinyAppProc: + """ + Class that represents a running Shiny app process. + + This class is a context manager that can be used to run a Shiny app in a subprocess. It provides a way to interact + with the app and terminate it when it is no longer needed. + """ + + file: PurePath + """The path to the Shiny app file.""" + proc: subprocess.Popen[str] + """The subprocess object that represents the running Shiny app.""" + port: int + """The port that the Shiny app is running on.""" + url: str + """The URL that the Shiny app is running on.""" + stdout: OutputStream + """The standard output stream of the Shiny app subprocess.""" + stderr: OutputStream + """The standard error stream of the Shiny app subprocess.""" + + def __init__( + self, + proc: subprocess.Popen[str], + port: int, + *, + app_file: PurePath | str, + ): + self.proc = proc + self.port = port + self.url = f"http://127.0.0.1:{port}/" + self.stdout = OutputStream(proc.stdout or dummyio()) + self.stderr = OutputStream(proc.stderr or dummyio()) + threading.Thread(group=None, target=self._run, daemon=True).start() + + self.file = PurePath(app_file) + + def _run(self) -> None: + self.proc.wait() + if self.proc.stdout is not None: + self.proc.stdout.close() + if self.proc.stderr is not None: + self.proc.stderr.close() + + def close(self) -> None: + """ + Closes the connection and terminates the process. + + This method is responsible for closing the connection and terminating the process associated with it. + """ + # from time import sleep + # sleep(0.5) + self.proc.terminate() + + def __enter__(self) -> ShinyAppProc: + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType], + ): + self.close() + + def wait_until_ready(self, timeout_secs: float) -> None: + """ + Waits until the shiny app is ready to serve requests. + + Parameters + ---------- + timeout_secs + The maximum number of seconds to wait for the app to become ready. + + Raises + ------ + ConnectionError + If there is an error while starting the shiny app. + TimeoutError + If the shiny app does not become ready within the specified timeout. + """ + error_lines: List[str] = [] + + def stderr_uvicorn(line: str) -> bool: + error_lines.append(line) + if "error while attempting to bind on address" in line: + raise ConnectionError(f"Error while starting shiny app: `{line}`") + return "Uvicorn running on" in line + + if self.stderr.wait_for(stderr_uvicorn, timeout_secs=timeout_secs): + return + else: + raise TimeoutError( + "Shiny app exited without ever becoming ready. Waiting for 'Uvicorn running on' in stderr. Last 20 lines of stderr:\n" + + "\n".join(error_lines[-20:]) + ) + + +def run_shiny_app( + app_file: Union[str, PurePath], + *, + start_attempts: int = 3, + port: int = 0, + cwd: Optional[str] = None, + wait_for_start: bool = True, + timeout_secs: float = 30, + bufsize: int = 64 * 1024, +) -> ShinyAppProc: + """ + Run a Shiny app in a subprocess. + + Parameters + ---------- + app_file + The path to the Shiny app file. + port + The port to run the app on. If 0, a random port will be chosen. + cwd + The working directory to run the app in. + wait_for_start + If True, wait for the app to become ready before returning. + timeout_secs + The maximum number of seconds to wait for the app to become ready. + bufsize + The buffer size to use for stdout and stderr. + """ + shiny_port = port if port != 0 else shiny._utils.random_port() + + child = subprocess.Popen( + [ + sys.executable, + "-m", + "shiny", + "run", + "--port", + str(shiny_port), + str(app_file), + ], + bufsize=bufsize, + executable=sys.executable, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=cwd, + encoding="utf-8", + ) + + # TODO: Detect early exit + + sa = ShinyAppProc(child, shiny_port, app_file=app_file) + + if wait_for_start: + try: + sa.wait_until_ready(timeout_secs) + except ConnectionError as e: + logging.error(f"Failed to bind to port: {e}") + + # Make sure the current process is closed + sa.close() + + start_attempts -= 1 + if start_attempts < 1: + # Ran out of attempts! + raise e + + # Try again with a new port! + return run_shiny_app( + app_file, + start_attempts=start_attempts, + port=port, + cwd=cwd, + wait_for_start=wait_for_start, + timeout_secs=timeout_secs, + bufsize=bufsize, + ) + + return sa + + +# Internal method to help make fixtures a little easier to write +# Attempt up to 3 times to start the app, with a random port each time +def shiny_app_gen( + app_file: PurePath | str, + *, + start_attempts: int = 3, + port: int = 0, + cwd: Optional[str] = None, + # wait_for_start: bool = False, + timeout_secs: float = 30, + bufsize: int = 64 * 1024, +) -> Generator[ShinyAppProc, Any, None]: + """ + Run a Shiny app in a subprocess. + + This app will be automatically shut down when the Generator is exhausted. A + generator is returned so we can utilize the context manager methods of the + `ShinyAppProc` class (`__enter__` and `__exit__`). This allows for the app to be + automatically shut down when the context manager exists. (This exit method is not + possible when returning a ShinyAppProc directly.) + + Parameters + ---------- + app + The path to the Shiny app file. + start_attempts + Number of attempts to try and start the Shiny app. If the random port is already + in use, a new random port will be chosen and another attempt will be made. If + all attempts have been made, an error will be raised. + port + The port to run the app on. If 0, a random port will be chosen. + cwd + The working directory to run the app in. + timeout_secs + The maximum number of seconds to wait for the app to become ready. + bufsize + The buffer size to use for stdout and stderr. + + Yields + ------ + : + A single Shiny app process + """ + # wait_for_start + # If True, wait for the app to become ready before returning. + + sa = run_shiny_app( + app_file, + wait_for_start=True, + start_attempts=start_attempts, + port=port, + cwd=cwd, + bufsize=bufsize, + timeout_secs=timeout_secs, + ) + had_connection_error: bool = False + try: + with sa: + yield sa + except ConnectionError as e: + had_connection_error = True + raise e + finally: + if not had_connection_error: + logging.warning("Application output:\n" + str(sa.stderr)) diff --git a/tests/playwright/controls.py b/shiny/test/_controls.py similarity index 99% rename from tests/playwright/controls.py rename to shiny/test/_controls.py index c78cfb452..59ff6cefb 100644 --- a/tests/playwright/controls.py +++ b/shiny/test/_controls.py @@ -11,18 +11,15 @@ import typing from typing import Literal, Optional, Protocol -from playwright.sync_api import FilePayload, FloatRect, Locator, Page, Position -from playwright.sync_api import expect as playwright_expect - # Import `shiny`'s typing extentions. # Since this is a private file, tell pyright to ignore the import -from shiny._typing_extensions import ( - TypeGuard, # pyright: ignore[reportPrivateImportUsage] -) -from shiny._typing_extensions import ( +from .._typing_extensions import TypeGuard # pyright: ignore[reportPrivateImportUsage] +from .._typing_extensions import ( assert_type, # pyright: ignore[reportPrivateImportUsage] ) -from shiny.types import MISSING, MISSING_TYPE +from ..types import MISSING, MISSING_TYPE +from ._playwright import FilePayload, FloatRect, Locator, Page, Position +from ._playwright import expect as playwright_expect """ Questions: diff --git a/shiny/test/_expect.py b/shiny/test/_expect.py new file mode 100644 index 000000000..a5800095d --- /dev/null +++ b/shiny/test/_expect.py @@ -0,0 +1,92 @@ +import functools +import time +from contextlib import contextmanager +from typing import Any, Callable, Generator + +__all__ = ("expect_to_change",) + + +@contextmanager +def expect_to_change( + func: Callable[[], Any], timeout_secs: float = 10 +) -> Generator[None, None, None]: + """ + Context manager that yields when the value returned by func() changes. Use this + around code that has a side-effect of changing some state asynchronously (such as + all browser actions), to prevent moving onto the next step of the test until this + one has actually taken effect. + + Parameters + ---------- + func + A function that returns a value. The value returned by this function is + compared to the value returned by subsequent calls to this function. + timeout_secs + How long to wait for the value to change before raising TimeoutError. + + Raises + ------ + TimeoutError + If the value does not change within timeout_secs. + + Example + ------- + + with expect_to_change(lambda: page.locator("#name").value()): + page.keyboard.send_keys("hello") + + """ + + original_value = func() + yield + + @retry_with_timeout(timeout_secs) + def wait_for_change(): + if func() == original_value: + raise AssertionError("Value did not change") + + wait_for_change() + + +def retry_with_timeout(timeout: float = 30): + """ + Decorator that retries a function until 1) it succeeds, 2) fails with a + non-assertion error, or 3) repeatedly fails with an AssertionError for longer than + the timeout. If the timeout elapses, the last AssertionError is raised. + + Parameters + ---------- + timeout + How long to wait for the function to succeed before raising the last + AssertionError. + + Returns + ------- + A decorator that can be applied to a function. + + Example + ------- + + @retry_with_timeout(30) + def try_to_find_element(): + if not page.locator("#name").exists(): + raise AssertionError("Element not found") + + try_to_find_element() + """ + + def decorator(func: Callable[[], None]) -> Callable[[], None]: + @functools.wraps(func) + def wrapper() -> None: + start = time.time() + while True: + try: + return func() + except AssertionError as e: + if time.time() - start > timeout: + raise e + time.sleep(0.1) + + return wrapper + + return decorator diff --git a/shiny/test/_playwright.py b/shiny/test/_playwright.py new file mode 100644 index 000000000..06a561f70 --- /dev/null +++ b/shiny/test/_playwright.py @@ -0,0 +1,17 @@ +from playwright.sync_api import BrowserContext as BrowserContext +from playwright.sync_api import FilePayload as FilePayload +from playwright.sync_api import FloatRect as FloatRect +from playwright.sync_api import Locator as Locator +from playwright.sync_api import Page as Page +from playwright.sync_api import Position as Position +from playwright.sync_api import expect as expect + +__all__ = ( + "expect", + "Page", + "FilePayload", + "Locator", + "BrowserContext", + "FloatRect", + "Position", +) diff --git a/shiny/test/_pytest.py b/shiny/test/_pytest.py new file mode 100644 index 000000000..3acb3133e --- /dev/null +++ b/shiny/test/_pytest.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from pathlib import PurePath +from typing import Generator + +import pytest + +from ._conftest import ShinyAppProc, shiny_app_gen + + +@pytest.fixture(scope="module") +def local_app(request: pytest.FixtureRequest) -> Generator[ShinyAppProc, None, None]: + """ + Create a local Shiny app for testing. + + Parameters: + request (pytest.FixtureRequest): The request object for the fixture. + """ + sa_gen = shiny_app_gen(PurePath(request.path).parent / "app.py") + yield next(sa_gen) diff --git a/shiny/test/fixture/__init__.py b/shiny/test/fixture/__init__.py new file mode 100644 index 000000000..686f4537a --- /dev/null +++ b/shiny/test/fixture/__init__.py @@ -0,0 +1,3 @@ +from ._fixture import create_app_fixture + +__all__ = ("create_app_fixture",) diff --git a/shiny/test/fixture/_fixture.py b/shiny/test/fixture/_fixture.py new file mode 100644 index 000000000..6b1792ecb --- /dev/null +++ b/shiny/test/fixture/_fixture.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +from pathlib import PurePath +from typing import Literal, Union + +import pytest + +from .._conftest import shiny_app_gen + +__all__ = ( + "create_app_fixture", + "ScopeName", +) + + +ScopeName = Literal["session", "package", "module", "class", "function"] + + +def create_app_fixture( + app: Union[PurePath, str], + scope: ScopeName = "module", +): + """ + Create a fixture for a local Shiny app directory. + + Parameters + ---------- + app + The path to the Shiny app file. + scope + The scope of the fixture. + """ + + @pytest.fixture(scope=scope) + def fixture_func(): + sa_gen = shiny_app_gen(app) + yield next(sa_gen) + + return fixture_func diff --git a/tests/playwright/README.md b/tests/playwright/README.md index 164857b94..dd40199cb 100644 --- a/tests/playwright/README.md +++ b/tests/playwright/README.md @@ -49,8 +49,7 @@ as the calling `test_*.py` file. ```python import re -from playwright.sync_api import Page, expect -from conftest import ShinyAppProc +from shiny.test import Page, ShinyAppProc, expect def test_airmass(page: Page, local_app: ShinyAppProc): @@ -68,9 +67,8 @@ use it from test funcs. ```python import re -from playwright.sync_api import Page, expect - -from conftest import ShinyAppProc, create_example_fixture +from shiny.test import Page, ShinyAppProc +from conftest import create_example_fixture airmass_app = create_example_fixture("airmass") diff --git a/tests/playwright/conftest.py b/tests/playwright/conftest.py index 6c38ac0dc..08f17b40c 100644 --- a/tests/playwright/conftest.py +++ b/tests/playwright/conftest.py @@ -1,50 +1,42 @@ +# This file is necessary for pytest to find relative module files +# such as examples/example_apps.py + from __future__ import annotations -import datetime -import functools -import logging -import subprocess -import sys -import threading -import time -from contextlib import contextmanager from pathlib import PurePath -from types import TracebackType -from typing import ( - IO, - Any, - Callable, - Generator, - List, - Literal, - Optional, - TextIO, - Type, - Union, -) import pytest -import shiny._utils +from shiny.test._playwright import BrowserContext, Page +from shiny.test.fixture import create_app_fixture +from shiny.test.fixture._fixture import ScopeName as ScopeName __all__ = ( - "ShinyAppProc", - "create_app_fixture", - "create_doc_example_core_fixture", + "create_doc_example_fixture", "create_example_fixture", - "local_app", - "run_shiny_app", - "expect_to_change", - "retry_with_timeout", + "create_doc_example_core_fixture", + "create_doc_example_express_fixture", ) -from playwright.sync_api import BrowserContext, Page + +here = PurePath(__file__).parent +here_root = here.parent.parent # Make a single page fixture that can be used by all tests @pytest.fixture(scope="session") # By using a single page, the browser is only launched once and all tests run in the same tab / page. def session_page(browser: BrowserContext) -> Page: + """ + Create a new page within the given browser context. + + Parameters: + browser (BrowserContext): The browser context in which to create the new page. + + Returns: + Page: The newly created page. + + """ return browser.new_page() @@ -53,217 +45,22 @@ def session_page(browser: BrowserContext) -> Page: # It is not perfect, but it is faster than making a new page for each test. # This must be done before each test def page(session_page: Page) -> Page: + """ + Reset the given page to a known state before each test. + + The page is built on the session_page, which is maintained over the full session. + The page will visit "about:blank" to reset between apps. + The default viewport size is set to 1920 x 1080 (1080p) for each test function. + + Parameters: + session_page (Page): The page to reset. + """ session_page.goto("about:blank") # Reset screen size to 1080p session_page.set_viewport_size({"width": 1920, "height": 1080}) return session_page -here = PurePath(__file__).parent -here_root = here.parent.parent - - -class OutputStream: - """Designed to wrap an IO[str] and accumulate the output using a bg thread - - Also allows for blocking waits for particular lines.""" - - def __init__(self, io: IO[str], desc: Optional[str] = None): - self._io = io - self._closed = False - self._lines: List[str] = [] - self._cond = threading.Condition() - self._thread = threading.Thread( - group=None, target=self._run, daemon=True, name=desc - ) - - self._thread.start() - - def _run(self): - """Pump lines into self._lines in a tight loop.""" - - try: - while not self._io.closed: - try: - line = self._io.readline() - except ValueError: - # This is raised when the stream is closed - break - if line != "": - with self._cond: - self._lines.append(line) - self._cond.notify_all() - finally: - # If we got here, we're finished reading self._io and need to signal any - # waiters that we're done and they'll never hear from us again. - with self._cond: - self._closed = True - self._cond.notify_all() - - def wait_for(self, predicate: Callable[[str], bool], timeoutSecs: float) -> bool: - timeoutAt = datetime.datetime.now() + datetime.timedelta(seconds=timeoutSecs) - pos = 0 - with self._cond: - while True: - while pos < len(self._lines): - if predicate(self._lines[pos]): - return True - pos += 1 - if self._closed: - return False - else: - remaining = (timeoutAt - datetime.datetime.now()).total_seconds() - if remaining < 0 or not self._cond.wait(timeout=remaining): - # Timed out - raise TimeoutError( - "Timeout while waiting for Shiny app to become ready" - ) - - def __str__(self): - with self._cond: - return "".join(self._lines) - - -def dummyio() -> TextIO: - io = TextIO() - io.close() - return io - - -class ShinyAppProc: - def __init__(self, proc: subprocess.Popen[str], port: int): - self.proc = proc - self.port = port - self.url = f"http://127.0.0.1:{port}/" - self.stdout = OutputStream(proc.stdout or dummyio()) - self.stderr = OutputStream(proc.stderr or dummyio()) - threading.Thread(group=None, target=self._run, daemon=True).start() - - def _run(self) -> None: - self.proc.wait() - if self.proc.stdout is not None: - self.proc.stdout.close() - if self.proc.stderr is not None: - self.proc.stderr.close() - - def close(self) -> None: - # from time import sleep - # sleep(0.5) - self.proc.terminate() - - def __enter__(self) -> ShinyAppProc: - return self - - def __exit__( - self, - exc_type: Optional[Type[BaseException]], - exc_value: Optional[BaseException], - traceback: Optional[TracebackType], - ): - self.close() - - def wait_until_ready(self, timeoutSecs: float) -> None: - error_lines: List[str] = [] - - def stderr_uvicorn(line: str) -> bool: - error_lines.append(line) - if "error while attempting to bind on address" in line: - raise ConnectionError(f"Error while staring shiny app: `{line}`") - return "Uvicorn running on" in line - - if self.stderr.wait_for(stderr_uvicorn, timeoutSecs=timeoutSecs): - return - else: - raise TimeoutError( - "Shiny app exited without ever becoming ready. Waiting for 'Uvicorn running on' in stderr. Last 20 lines of stderr:\n" - + "\n".join(error_lines[-20:]) - ) - - -def run_shiny_app( - app_file: Union[str, PurePath], - *, - port: int = 0, - cwd: Optional[str] = None, - wait_for_start: bool = True, - timeout_secs: float = 10, - bufsize: int = 64 * 1024, -) -> ShinyAppProc: - shiny_port = port if port != 0 else shiny._utils.random_port() - - child = subprocess.Popen( - [ - sys.executable, - "-m", - "shiny", - "run", - "--port", - str(shiny_port), - str(app_file), - ], - bufsize=bufsize, - executable=sys.executable, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - cwd=cwd, - encoding="utf-8", - ) - - # TODO: Detect early exit - - sa = ShinyAppProc(child, shiny_port) - if wait_for_start: - sa.wait_until_ready(timeout_secs) - return sa - - -# Attempt up to 3 times to start the app, with a random port each time -def local_app_fixture_gen(app: PurePath | str): - - has_yielded_app = False - remaining_attempts = 3 - while not has_yielded_app and remaining_attempts > 0: - remaining_attempts -= 1 - - # Make shiny process - sa = run_shiny_app(app, wait_for_start=False, port=0) - try: - # enter / exit shiny context manager; (closes streams on exit) - with sa: - # Wait for shiny app to start - # Could throw a `ConnectionError` if the port is already in use - sa.wait_until_ready(30) - # Run app! - has_yielded_app = True - yield sa - except ConnectionError as e: - if remaining_attempts == 0: - # Ran out of attempts! - raise e - print(f"Failed to bind to port: {e}", file=sys.stderr) - # Try again with a new port! - finally: - if has_yielded_app: - logging.warning("Application output:\n" + str(sa.stderr)) - - -ScopeName = Literal["session", "package", "module", "class", "function"] - - -def create_app_fixture( - app: Union[PurePath, str], - scope: ScopeName = "module", -): - @pytest.fixture(scope=scope) - def fixture_func(): - # Pass through `yield` via `next(...)` call - # (`yield` must be on same line as `next`!) - app_gen = local_app_fixture_gen(app) - yield next(app_gen) - - return fixture_func - - def create_example_fixture( example_name: str, example_file: str = "app.py", @@ -300,99 +97,3 @@ def create_doc_example_express_fixture( ): """Used to create app fixtures from ``app-express.py`` example apps in py-shiny/shiny/api-examples""" return create_doc_example_fixture(example_name, "app-express.py", scope) - - -def x_create_doc_example_fixture(example_name: str, scope: ScopeName = "module"): - """Used to create app fixtures from apps in py-shiny/shiny/examples""" - return create_app_fixture( - here_root / "shiny/experimental/api-examples" / example_name / "app.py", scope - ) - - -@pytest.fixture(scope="module") -def local_app(request: pytest.FixtureRequest) -> Generator[ShinyAppProc, None, None]: - app_gen = local_app_fixture_gen(PurePath(request.path).parent / "app.py") - yield next(app_gen) - - -@contextmanager -def expect_to_change( - func: Callable[[], Any], timeoutSecs: float = 10 -) -> Generator[None, None, None]: - """ - Context manager that yields when the value returned by func() changes. Use this - around code that has a side-effect of changing some state asynchronously (such as - all browser actions), to prevent moving onto the next step of the test until this - one has actually taken effect. - - Raises TimeoutError if the value does not change within timeoutSecs. - - Parameters - ---------- - func - A function that returns a value. The value returned by this function is - compared to the value returned by subsequent calls to this function. - timeoutSecs - How long to wait for the value to change before raising TimeoutError. - - Example - ------- - - with expect_to_change(lambda: page.locator("#name").value()): - page.keyboard.send_keys("hello") - - """ - - original_value = func() - yield - - @retry_with_timeout(timeoutSecs) - def wait_for_change(): - if func() == original_value: - raise AssertionError("Value did not change") - - wait_for_change() - - -def retry_with_timeout(timeout: float = 30): - """ - Decorator that retries a function until 1) it succeeds, 2) fails with a - non-assertion error, or 3) repeatedly fails with an AssertionError for longer than - the timeout. If the timeout elapses, the last AssertionError is raised. - - Parameters - ---------- - timeout - How long to wait for the function to succeed before raising the last - AssertionError. - - Returns - ------- - A decorator that can be applied to a function. - - Example - ------- - - @retry_with_timeout(30) - def try_to_find_element(): - if not page.locator("#name").exists(): - raise AssertionError("Element not found") - - try_to_find_element() - """ - - def decorator(func: Callable[[], None]) -> Callable[[], None]: - @functools.wraps(func) - def wrapper() -> None: - start = time.time() - while True: - try: - return func() - except AssertionError as e: - if time.time() - start > timeout: - raise e - time.sleep(0.1) - - return wrapper - - return decorator diff --git a/tests/playwright/deploys/express-accordion/test_deploys_express_accordion.py b/tests/playwright/deploys/express-accordion/test_deploys_express_accordion.py index b734f3fdc..a8ac5c39e 100644 --- a/tests/playwright/deploys/express-accordion/test_deploys_express_accordion.py +++ b/tests/playwright/deploys/express-accordion/test_deploys_express_accordion.py @@ -1,6 +1,4 @@ import pytest -from controls import Accordion -from playwright.sync_api import Page from utils.deploy_utils import ( create_deploys_app_url_fixture, reruns, @@ -8,6 +6,9 @@ skip_if_not_chrome, ) +from shiny.test import Page +from shiny.test._controls import Accordion + app_url = create_deploys_app_url_fixture("shiny_express_accordion") diff --git a/tests/playwright/deploys/express-dataframe/test_deploys_express_dataframe.py b/tests/playwright/deploys/express-dataframe/test_deploys_express_dataframe.py index b08d62502..d9107aab7 100644 --- a/tests/playwright/deploys/express-dataframe/test_deploys_express_dataframe.py +++ b/tests/playwright/deploys/express-dataframe/test_deploys_express_dataframe.py @@ -1,6 +1,4 @@ import pytest -from controls import OutputDataFrame -from playwright.sync_api import Page from utils.deploy_utils import ( create_deploys_app_url_fixture, reruns, @@ -8,6 +6,9 @@ skip_if_not_chrome, ) +from shiny.test import Page +from shiny.test._controls import OutputDataFrame + app_url = create_deploys_app_url_fixture("shiny-express-dataframe") diff --git a/tests/playwright/deploys/express-folium/test_deploys_express_folium.py b/tests/playwright/deploys/express-folium/test_deploys_express_folium.py index 247fc0b89..49772c4f1 100644 --- a/tests/playwright/deploys/express-folium/test_deploys_express_folium.py +++ b/tests/playwright/deploys/express-folium/test_deploys_express_folium.py @@ -1,5 +1,4 @@ import pytest -from playwright.sync_api import Page, expect from utils.deploy_utils import ( create_deploys_app_url_fixture, reruns, @@ -7,6 +6,8 @@ skip_if_not_chrome, ) +from shiny.test import Page, expect + app_url = create_deploys_app_url_fixture("shiny-express-folium") diff --git a/tests/playwright/deploys/express-page_default/test_deploys_express_page_default.py b/tests/playwright/deploys/express-page_default/test_deploys_express_page_default.py index 264504bb6..b6bedb338 100644 --- a/tests/playwright/deploys/express-page_default/test_deploys_express_page_default.py +++ b/tests/playwright/deploys/express-page_default/test_deploys_express_page_default.py @@ -1,6 +1,4 @@ import pytest -from controls import LayoutNavsetTab -from playwright.sync_api import Page, expect from utils.deploy_utils import ( create_deploys_app_url_fixture, reruns, @@ -8,6 +6,9 @@ skip_if_not_chrome, ) +from shiny.test import Page, expect +from shiny.test._controls import LayoutNavsetTab + TIMEOUT = 2 * 60 * 1000 app_url = create_deploys_app_url_fixture("shiny_express_page_default") diff --git a/tests/playwright/deploys/express-page_fillable/test_deploys_express_page_fillable.py b/tests/playwright/deploys/express-page_fillable/test_deploys_express_page_fillable.py index 58c8809f9..36874787e 100644 --- a/tests/playwright/deploys/express-page_fillable/test_deploys_express_page_fillable.py +++ b/tests/playwright/deploys/express-page_fillable/test_deploys_express_page_fillable.py @@ -1,6 +1,4 @@ import pytest -from controls import Card, OutputTextVerbatim -from playwright.sync_api import Page from utils.deploy_utils import ( create_deploys_app_url_fixture, reruns, @@ -8,6 +6,9 @@ skip_if_not_chrome, ) +from shiny.test import Page +from shiny.test._controls import Card, OutputTextVerbatim + app_url = create_deploys_app_url_fixture("express_page_fillable") diff --git a/tests/playwright/deploys/express-page_fluid/test_deploys_express_page_fluid.py b/tests/playwright/deploys/express-page_fluid/test_deploys_express_page_fluid.py index 2e4c1efaa..a03157621 100644 --- a/tests/playwright/deploys/express-page_fluid/test_deploys_express_page_fluid.py +++ b/tests/playwright/deploys/express-page_fluid/test_deploys_express_page_fluid.py @@ -1,6 +1,4 @@ import pytest -from controls import Card, OutputTextVerbatim -from playwright.sync_api import Page from utils.deploy_utils import ( create_deploys_app_url_fixture, reruns, @@ -8,6 +6,9 @@ skip_if_not_chrome, ) +from shiny.test import Page +from shiny.test._controls import Card, OutputTextVerbatim + app_url = create_deploys_app_url_fixture("express_page_fluid") diff --git a/tests/playwright/deploys/express-page_sidebar/test_deploys_express_page_sidebar.py b/tests/playwright/deploys/express-page_sidebar/test_deploys_express_page_sidebar.py index 4ff92f2b5..10cddd5b0 100644 --- a/tests/playwright/deploys/express-page_sidebar/test_deploys_express_page_sidebar.py +++ b/tests/playwright/deploys/express-page_sidebar/test_deploys_express_page_sidebar.py @@ -1,6 +1,4 @@ import pytest -from controls import OutputTextVerbatim, Sidebar -from playwright.sync_api import Page from utils.deploy_utils import ( create_deploys_app_url_fixture, reruns, @@ -8,6 +6,9 @@ skip_if_not_chrome, ) +from shiny.test import Page +from shiny.test._controls import OutputTextVerbatim, Sidebar + app_url = create_deploys_app_url_fixture("express_page_sidebar") diff --git a/tests/playwright/deploys/plotly/test_plotly_app.py b/tests/playwright/deploys/plotly/test_plotly_app.py index d6b5d29fa..295cfdb82 100644 --- a/tests/playwright/deploys/plotly/test_plotly_app.py +++ b/tests/playwright/deploys/plotly/test_plotly_app.py @@ -1,5 +1,4 @@ import pytest -from playwright.sync_api import Page, expect from utils.deploy_utils import ( create_deploys_app_url_fixture, reruns, @@ -7,6 +6,8 @@ skip_if_not_chrome, ) +from shiny.test import Page, expect + TIMEOUT = 2 * 60 * 1000 app_url = create_deploys_app_url_fixture("example_deploy_app_A") diff --git a/tests/playwright/deploys/shiny-client-console-error/test_shiny_client_error.py b/tests/playwright/deploys/shiny-client-console-error/test_shiny_client_error.py index be697f413..6429d5000 100644 --- a/tests/playwright/deploys/shiny-client-console-error/test_shiny_client_error.py +++ b/tests/playwright/deploys/shiny-client-console-error/test_shiny_client_error.py @@ -1,5 +1,4 @@ import pytest -from playwright.sync_api import Page, expect from utils.deploy_utils import ( create_deploys_app_url_fixture, reruns, @@ -7,6 +6,8 @@ skip_if_not_chrome, ) +from shiny.test import Page, expect + app_url = create_deploys_app_url_fixture("shiny_client_console_error") diff --git a/tests/playwright/examples/example_apps.py b/tests/playwright/examples/example_apps.py index a5f0b54f0..95118ee3d 100644 --- a/tests/playwright/examples/example_apps.py +++ b/tests/playwright/examples/example_apps.py @@ -4,9 +4,11 @@ from pathlib import PurePath from typing import Literal -from conftest import run_shiny_app from playwright.sync_api import ConsoleMessage, Page +from shiny.test import ShinyAppProc +from shiny.test._conftest import run_shiny_app + here_tests_e2e_examples = PurePath(__file__).parent pyshiny_root = here_tests_e2e_examples.parent.parent.parent @@ -154,7 +156,7 @@ def wait_for_idle_app( def validate_example(page: Page, ex_app_path: str) -> None: - app = run_shiny_app(pyshiny_root / ex_app_path, wait_for_start=True) + sa: ShinyAppProc = run_shiny_app(pyshiny_root / ex_app_path, wait_for_start=True) console_errors: typing.List[str] = [] @@ -168,8 +170,8 @@ def on_console_msg(msg: ConsoleMessage) -> None: page.on("console", on_console_msg) # Makes sure the app is closed when exiting the code block - with app: - page.goto(app.url) + with sa: + page.goto(sa.url) app_name = os.path.basename(os.path.dirname(ex_app_path)) short_app_path = f"{os.path.basename(os.path.dirname(os.path.dirname(ex_app_path)))}/{app_name}" @@ -187,7 +189,7 @@ def on_console_msg(msg: ConsoleMessage) -> None: ) # Check for py-shiny errors - error_lines = str(app.stderr).splitlines() + error_lines = str(sa.stderr).splitlines() # Remove any errors that are allowed error_lines = [ diff --git a/tests/playwright/examples/test_api_examples.py b/tests/playwright/examples/test_api_examples.py index 475dda70a..a7bb2e205 100644 --- a/tests/playwright/examples/test_api_examples.py +++ b/tests/playwright/examples/test_api_examples.py @@ -1,6 +1,7 @@ import pytest from example_apps import get_apps, reruns, reruns_delay, validate_example -from playwright.sync_api import Page + +from shiny.test import Page @pytest.mark.flaky(reruns=reruns, reruns_delay=reruns_delay) diff --git a/tests/playwright/examples/test_cpuinfo.py b/tests/playwright/examples/test_cpuinfo.py index d8ba32437..4ab182321 100644 --- a/tests/playwright/examples/test_cpuinfo.py +++ b/tests/playwright/examples/test_cpuinfo.py @@ -2,8 +2,9 @@ import re -from conftest import ShinyAppProc, create_example_fixture -from playwright.sync_api import Page, expect +from conftest import create_example_fixture + +from shiny.test import Page, ShinyAppProc, expect cpuinfo_app = create_example_fixture("cpuinfo") diff --git a/tests/playwright/examples/test_examples.py b/tests/playwright/examples/test_examples.py index 60117d7df..55ebf5f79 100644 --- a/tests/playwright/examples/test_examples.py +++ b/tests/playwright/examples/test_examples.py @@ -1,6 +1,7 @@ import pytest from example_apps import get_apps, reruns, reruns_delay, validate_example -from playwright.sync_api import Page + +from shiny.test import Page @pytest.mark.flaky(reruns=reruns, reruns_delay=reruns_delay) diff --git a/tests/playwright/examples/test_shiny_create.py b/tests/playwright/examples/test_shiny_create.py index 83da63850..84aebdf92 100644 --- a/tests/playwright/examples/test_shiny_create.py +++ b/tests/playwright/examples/test_shiny_create.py @@ -4,9 +4,9 @@ import pytest from example_apps import get_apps, reruns, reruns_delay, validate_example -from playwright.sync_api import Page from shiny._main import app_template_choices +from shiny.test import Page def subprocess_create( diff --git a/tests/playwright/shiny/_internal/test_e2e_regex_matching.py b/tests/playwright/shiny/_internal/test_e2e_regex_matching.py index 9a80cad84..344105c78 100644 --- a/tests/playwright/shiny/_internal/test_e2e_regex_matching.py +++ b/tests/playwright/shiny/_internal/test_e2e_regex_matching.py @@ -1,6 +1,6 @@ import re -from controls import _attr_match_str, _style_match_str, _xpath_match_str +from shiny.test._controls import _attr_match_str, _style_match_str, _xpath_match_str def test_style_match_str() -> None: diff --git a/tests/playwright/shiny/async/test_async.py b/tests/playwright/shiny/async/test_async.py index 4e534ce7f..909bcaf9f 100644 --- a/tests/playwright/shiny/async/test_async.py +++ b/tests/playwright/shiny/async/test_async.py @@ -1,9 +1,8 @@ # See https://github.com/microsoft/playwright-python/issues/1532 # pyright: reportUnknownMemberType=false -from conftest import ShinyAppProc -from controls import InputActionButton, InputTextArea, OutputTextVerbatim -from playwright.sync_api import Page, expect +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputActionButton, InputTextArea, OutputTextVerbatim def test_async_app(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/bugs/0648-update-slider-datetime-value/test_update_slider_datetime_value.py b/tests/playwright/shiny/bugs/0648-update-slider-datetime-value/test_update_slider_datetime_value.py index b569d0de7..bd6b1b09f 100644 --- a/tests/playwright/shiny/bugs/0648-update-slider-datetime-value/test_update_slider_datetime_value.py +++ b/tests/playwright/shiny/bugs/0648-update-slider-datetime-value/test_update_slider_datetime_value.py @@ -2,9 +2,8 @@ from typing import Optional -from conftest import ShinyAppProc -from controls import InputActionButton, InputSlider, OutputTextVerbatim -from playwright.sync_api import Page, expect +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputActionButton, InputSlider, OutputTextVerbatim def test_slider_app(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/bugs/0666-sidebar/test_sidebar_colors.py b/tests/playwright/shiny/bugs/0666-sidebar/test_sidebar_colors.py index e376d16ed..6e29d09e7 100644 --- a/tests/playwright/shiny/bugs/0666-sidebar/test_sidebar_colors.py +++ b/tests/playwright/shiny/bugs/0666-sidebar/test_sidebar_colors.py @@ -1,9 +1,9 @@ from __future__ import annotations from colors import bg_color, fg_color -from conftest import ShinyAppProc -from controls import Sidebar, _expect_class_value -from playwright.sync_api import Page, expect + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import Sidebar, _expect_class_value def test_colors_are_rgb() -> None: diff --git a/tests/playwright/shiny/bugs/0676-row-selection/test_0676_row_selection.py b/tests/playwright/shiny/bugs/0676-row-selection/test_0676_row_selection.py index ef991002f..cb63f335c 100644 --- a/tests/playwright/shiny/bugs/0676-row-selection/test_0676_row_selection.py +++ b/tests/playwright/shiny/bugs/0676-row-selection/test_0676_row_selection.py @@ -1,7 +1,6 @@ from __future__ import annotations -from conftest import ShinyAppProc -from playwright.sync_api import Page, expect +from shiny.test import Page, ShinyAppProc, expect def test_row_selection(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/bugs/0696-resolve-id/mod_state.py b/tests/playwright/shiny/bugs/0696-resolve-id/mod_state.py index 4a0ad4210..e6f8bb53a 100644 --- a/tests/playwright/shiny/bugs/0696-resolve-id/mod_state.py +++ b/tests/playwright/shiny/bugs/0696-resolve-id/mod_state.py @@ -2,8 +2,8 @@ import datetime -from controls import OutputTextVerbatim -from playwright.sync_api import Page +from shiny.test import Page +from shiny.test._controls import OutputTextVerbatim def expect_state( diff --git a/tests/playwright/shiny/bugs/0696-resolve-id/test_0696_resolve_id.py b/tests/playwright/shiny/bugs/0696-resolve-id/test_0696_resolve_id.py index 37bb2907c..39c6059a8 100644 --- a/tests/playwright/shiny/bugs/0696-resolve-id/test_0696_resolve_id.py +++ b/tests/playwright/shiny/bugs/0696-resolve-id/test_0696_resolve_id.py @@ -6,8 +6,12 @@ from pathlib import Path import pytest -from conftest import ShinyAppProc -from controls import ( +from examples.example_apps import reruns, reruns_delay +from mod_state import expect_default_mod_state, expect_mod_state + +from shiny._utils import guess_mime_type +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import ( DownloadButton, DownloadLink, InputActionButton, @@ -32,11 +36,6 @@ OutputTextVerbatim, OutputUi, ) -from examples.example_apps import reruns, reruns_delay -from mod_state import expect_default_mod_state, expect_mod_state -from playwright.sync_api import Page - -from shiny._utils import guess_mime_type img_path = Path(__file__).parent / "imgs" penguin_imgs = [str(img_path / img) for img in os.listdir(img_path)] diff --git a/tests/playwright/shiny/bugs/1345-render-data-frame-input/test_1345_input_selected_rows.py b/tests/playwright/shiny/bugs/1345-render-data-frame-input/test_1345_input_selected_rows.py index 80c2d6b17..822d72420 100644 --- a/tests/playwright/shiny/bugs/1345-render-data-frame-input/test_1345_input_selected_rows.py +++ b/tests/playwright/shiny/bugs/1345-render-data-frame-input/test_1345_input_selected_rows.py @@ -1,8 +1,7 @@ from __future__ import annotations -from conftest import ShinyAppProc -from controls import OutputDataFrame, OutputTextVerbatim -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import OutputDataFrame, OutputTextVerbatim def test_row_selection(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/bugs/1351-render-data-frame-selected/test_1351_selected_row.py b/tests/playwright/shiny/bugs/1351-render-data-frame-selected/test_1351_selected_row.py index f98177f5a..5c2374c76 100644 --- a/tests/playwright/shiny/bugs/1351-render-data-frame-selected/test_1351_selected_row.py +++ b/tests/playwright/shiny/bugs/1351-render-data-frame-selected/test_1351_selected_row.py @@ -1,8 +1,7 @@ from __future__ import annotations -from conftest import ShinyAppProc -from controls import InputActionButton, OutputDataFrame, OutputTextVerbatim -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import InputActionButton, OutputDataFrame, OutputTextVerbatim def test_row_selection(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/bugs/1390-df-selected-row-filtered/test_1390_selected_row_filtered.py b/tests/playwright/shiny/bugs/1390-df-selected-row-filtered/test_1390_selected_row_filtered.py index 170012c5a..90f26fc70 100644 --- a/tests/playwright/shiny/bugs/1390-df-selected-row-filtered/test_1390_selected_row_filtered.py +++ b/tests/playwright/shiny/bugs/1390-df-selected-row-filtered/test_1390_selected_row_filtered.py @@ -2,10 +2,11 @@ import platform -from conftest import ShinyAppProc -from controls import OutputCode, OutputDataFrame from playwright.sync_api import Page, expect +from shiny.test import ShinyAppProc +from shiny.test._controls import OutputCode, OutputDataFrame + def test_row_selection(page: Page, local_app: ShinyAppProc) -> None: page.goto(local_app.url) diff --git a/tests/playwright/shiny/components/accordion/test_accordion.py b/tests/playwright/shiny/components/accordion/test_accordion.py index f47786728..893bdcb03 100644 --- a/tests/playwright/shiny/components/accordion/test_accordion.py +++ b/tests/playwright/shiny/components/accordion/test_accordion.py @@ -1,8 +1,8 @@ import pytest -from conftest import ShinyAppProc -from controls import Accordion, InputActionButton, OutputTextVerbatim from examples.example_apps import reruns, reruns_delay -from playwright.sync_api import Page + +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import Accordion, InputActionButton, OutputTextVerbatim @pytest.mark.flaky(reruns=reruns, reruns_delay=reruns_delay) diff --git a/tests/playwright/shiny/components/busy_indicators/test_busy_indicators.py b/tests/playwright/shiny/components/busy_indicators/test_busy_indicators.py index 8156e719b..8a42d8b2d 100644 --- a/tests/playwright/shiny/components/busy_indicators/test_busy_indicators.py +++ b/tests/playwright/shiny/components/busy_indicators/test_busy_indicators.py @@ -1,9 +1,8 @@ import os from urllib.parse import urlparse -from conftest import ShinyAppProc -from controls import InputRadioButtons, InputTaskButton, OutputTextVerbatim -from playwright.sync_api import Page, expect +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputRadioButtons, InputTaskButton, OutputTextVerbatim def get_spinner_computed_property( diff --git a/tests/playwright/shiny/components/card-input/test_card-input.py b/tests/playwright/shiny/components/card-input/test_card-input.py index 1cc794aff..27466e1e1 100644 --- a/tests/playwright/shiny/components/card-input/test_card-input.py +++ b/tests/playwright/shiny/components/card-input/test_card-input.py @@ -1,9 +1,10 @@ from pathlib import Path import pytest -from conftest import run_shiny_app -from controls import Card, OutputCode, ValueBox -from playwright.sync_api import Page + +from shiny.test import Page, ShinyAppProc +from shiny.test._conftest import run_shiny_app +from shiny.test._controls import Card, OutputCode, ValueBox @pytest.mark.parametrize( @@ -14,9 +15,11 @@ ], ) def test_card_input(page: Page, app_path: str, sel_card: str, sel_vb: str) -> None: - app = run_shiny_app(Path(__file__).parent / app_path) + sa: ShinyAppProc = run_shiny_app( + Path(__file__).parent / app_path, wait_for_start=True + ) - page.goto(app.url) + page.goto(sa.url) card = Card(page, sel_card) vb = ValueBox(page, sel_vb) diff --git a/tests/playwright/shiny/components/data_frame/example/test_data_frame.py b/tests/playwright/shiny/components/data_frame/example/test_data_frame.py index 36bd187d4..3c51171d4 100644 --- a/tests/playwright/shiny/components/data_frame/example/test_data_frame.py +++ b/tests/playwright/shiny/components/data_frame/example/test_data_frame.py @@ -5,10 +5,12 @@ from typing import Any, Callable import pytest -from conftest import ShinyAppProc, create_example_fixture, expect_to_change -from controls import InputSelect, InputSwitch +from conftest import create_example_fixture from examples.example_apps import reruns, reruns_delay -from playwright.sync_api import Locator, Page, expect + +from shiny.test import Locator, Page, ShinyAppProc, expect +from shiny.test._controls import InputSelect, InputSwitch +from shiny.test._expect import expect_to_change data_frame_app = create_example_fixture("dataframe") diff --git a/tests/playwright/shiny/components/data_frame/html_columns_df/df_organization/test_df_organization.py b/tests/playwright/shiny/components/data_frame/html_columns_df/df_organization/test_df_organization.py index f0400a920..45bcc3676 100644 --- a/tests/playwright/shiny/components/data_frame/html_columns_df/df_organization/test_df_organization.py +++ b/tests/playwright/shiny/components/data_frame/html_columns_df/df_organization/test_df_organization.py @@ -1,6 +1,5 @@ -from conftest import ShinyAppProc -from controls import InputActionButton, OutputCode, OutputDataFrame -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import InputActionButton, OutputCode, OutputDataFrame def test_dataframe_organization_methods(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/components/data_frame/html_columns_df/tabbing/app.py b/tests/playwright/shiny/components/data_frame/html_columns_df/tabbing/app.py index 4219896a4..853c23503 100644 --- a/tests/playwright/shiny/components/data_frame/html_columns_df/tabbing/app.py +++ b/tests/playwright/shiny/components/data_frame/html_columns_df/tabbing/app.py @@ -4,9 +4,9 @@ from shiny import App, Inputs, Outputs, Session, render, ui df = pd.DataFrame( - sns.load_dataset( + sns.load_dataset( # pyright: ignore[reportUnknownMemberType, reportUnknownArgumentType] "iris" - ) # pyright: ignore[reportUnknownMemberType, reportUnknownArgumentType] + ) ) app_ui = ui.page_fluid( ui.h2("Iris Dataset"), diff --git a/tests/playwright/shiny/components/data_frame/html_columns_df/tabbing/test_tabbing.py b/tests/playwright/shiny/components/data_frame/html_columns_df/tabbing/test_tabbing.py index 9005b05f3..d193c7bf8 100644 --- a/tests/playwright/shiny/components/data_frame/html_columns_df/tabbing/test_tabbing.py +++ b/tests/playwright/shiny/components/data_frame/html_columns_df/tabbing/test_tabbing.py @@ -1,6 +1,5 @@ -from conftest import ShinyAppProc -from controls import OutputDataFrame -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import OutputDataFrame def test_validate_html_columns(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/components/data_frame/html_columns_df/test_html_columns.py b/tests/playwright/shiny/components/data_frame/html_columns_df/test_html_columns.py index deb9ec6db..c6834979d 100644 --- a/tests/playwright/shiny/components/data_frame/html_columns_df/test_html_columns.py +++ b/tests/playwright/shiny/components/data_frame/html_columns_df/test_html_columns.py @@ -1,6 +1,5 @@ -from conftest import ShinyAppProc -from controls import InputActionButton, OutputDataFrame, OutputTextVerbatim -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import InputActionButton, OutputDataFrame, OutputTextVerbatim def test_validate_html_columns(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/components/data_frame/row_selection/test_row_selection.py b/tests/playwright/shiny/components/data_frame/row_selection/test_row_selection.py index 5addfa2a0..bf8b6ee40 100644 --- a/tests/playwright/shiny/components/data_frame/row_selection/test_row_selection.py +++ b/tests/playwright/shiny/components/data_frame/row_selection/test_row_selection.py @@ -1,6 +1,5 @@ -from conftest import ShinyAppProc -from controls import InputActionButton, OutputTextVerbatim -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import InputActionButton, OutputTextVerbatim def expect_row_selection(page: Page, prefix_main: str, prefix_secondary: str): diff --git a/tests/playwright/shiny/components/data_frame/validate_data_edit_mode/test_validate_data_edit_mode.py b/tests/playwright/shiny/components/data_frame/validate_data_edit_mode/test_validate_data_edit_mode.py index 6d26e966b..b450fcd5d 100644 --- a/tests/playwright/shiny/components/data_frame/validate_data_edit_mode/test_validate_data_edit_mode.py +++ b/tests/playwright/shiny/components/data_frame/validate_data_edit_mode/test_validate_data_edit_mode.py @@ -1,6 +1,5 @@ -from conftest import ShinyAppProc -from controls import OutputDataFrame -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import OutputDataFrame def test_validate_data_edit_mode(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/components/data_frame/validate_data_save/test_validate_data_save.py b/tests/playwright/shiny/components/data_frame/validate_data_save/test_validate_data_save.py index 1a7c19b78..013556d39 100644 --- a/tests/playwright/shiny/components/data_frame/validate_data_save/test_validate_data_save.py +++ b/tests/playwright/shiny/components/data_frame/validate_data_save/test_validate_data_save.py @@ -1,6 +1,5 @@ -from conftest import ShinyAppProc -from controls import OutputDataFrame -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import OutputDataFrame def test_validate_data_edit_mode(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/components/layout_columns/test_layout_columns.py b/tests/playwright/shiny/components/layout_columns/test_layout_columns.py index 38f421c22..652dda2e8 100644 --- a/tests/playwright/shiny/components/layout_columns/test_layout_columns.py +++ b/tests/playwright/shiny/components/layout_columns/test_layout_columns.py @@ -2,8 +2,9 @@ from typing import TypeVar -from conftest import ShinyAppProc, create_doc_example_core_fixture -from playwright.sync_api import Page +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc T = TypeVar("T") diff --git a/tests/playwright/shiny/components/nav/test_nav.py b/tests/playwright/shiny/components/nav/test_nav.py index f32dfe4fb..4e3a01598 100644 --- a/tests/playwright/shiny/components/nav/test_nav.py +++ b/tests/playwright/shiny/components/nav/test_nav.py @@ -2,8 +2,8 @@ from dataclasses import dataclass -from conftest import ShinyAppProc -from controls import ( +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import ( LayoutNavSetBar, LayoutNavSetCardPill, LayoutNavSetCardTab, @@ -13,7 +13,6 @@ LayoutNavsetTab, LayoutNavSetUnderline, ) -from playwright.sync_api import Page def test_nav(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/components/navset_hidden/test_nav_hidden.py b/tests/playwright/shiny/components/navset_hidden/test_nav_hidden.py index 650fd7bba..4d7a05510 100644 --- a/tests/playwright/shiny/components/navset_hidden/test_nav_hidden.py +++ b/tests/playwright/shiny/components/navset_hidden/test_nav_hidden.py @@ -1,7 +1,6 @@ # import pytest -from conftest import ShinyAppProc -from controls import InputRadioButtons, LayoutNavSetHidden -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import InputRadioButtons, LayoutNavSetHidden def test_navset_hidden(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/components/popover/test_popover.py b/tests/playwright/shiny/components/popover/test_popover.py index dc6bd74d0..90115064e 100644 --- a/tests/playwright/shiny/components/popover/test_popover.py +++ b/tests/playwright/shiny/components/popover/test_popover.py @@ -1,6 +1,5 @@ -from conftest import ShinyAppProc -from controls import Popover -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import Popover def test_popover(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/components/test_sidebar.py b/tests/playwright/shiny/components/test_sidebar.py index 5c3371cf1..de308f30c 100644 --- a/tests/playwright/shiny/components/test_sidebar.py +++ b/tests/playwright/shiny/components/test_sidebar.py @@ -1,6 +1,7 @@ -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import OutputTextVerbatim, Sidebar -from playwright.sync_api import Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import OutputTextVerbatim, Sidebar app = create_doc_example_core_fixture("sidebar") diff --git a/tests/playwright/shiny/components/tooltip/test_tooltip.py b/tests/playwright/shiny/components/tooltip/test_tooltip.py index 4ea32e56b..bf9dff4aa 100644 --- a/tests/playwright/shiny/components/tooltip/test_tooltip.py +++ b/tests/playwright/shiny/components/tooltip/test_tooltip.py @@ -1,6 +1,5 @@ -from conftest import ShinyAppProc -from controls import Tooltip -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import Tooltip def test_tooltip(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox_ks.py b/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox_ks.py index 776571cdf..6947ddf0d 100644 --- a/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox_ks.py +++ b/tests/playwright/shiny/components/value_box/kitchensink/test_valuebox_ks.py @@ -1,6 +1,5 @@ -from conftest import ShinyAppProc -from controls import ValueBox, expect_to_have_class -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import ValueBox, expect_to_have_class def get_value_box_bg_color(value_box: ValueBox) -> str: diff --git a/tests/playwright/shiny/components/value_box/smoke/test_valuebox.py b/tests/playwright/shiny/components/value_box/smoke/test_valuebox.py index 0458f1c87..cb4202986 100644 --- a/tests/playwright/shiny/components/value_box/smoke/test_valuebox.py +++ b/tests/playwright/shiny/components/value_box/smoke/test_valuebox.py @@ -1,7 +1,7 @@ import pytest -from conftest import ShinyAppProc -from controls import ValueBox -from playwright.sync_api import Page + +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import ValueBox @pytest.mark.parametrize("value_box_id", ["valuebox1", "valuebox2"]) diff --git a/tests/playwright/shiny/default-render-ui/test_default_render_ui.py b/tests/playwright/shiny/default-render-ui/test_default_render_ui.py index 283557aec..68aec6da5 100644 --- a/tests/playwright/shiny/default-render-ui/test_default_render_ui.py +++ b/tests/playwright/shiny/default-render-ui/test_default_render_ui.py @@ -1,5 +1,4 @@ -from conftest import ShinyAppProc -from playwright.sync_api import Page, expect +from shiny.test import Page, ShinyAppProc, expect def test_implicit_register(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/deprecated/output_transformer/test_output_transformer_example.py b/tests/playwright/shiny/deprecated/output_transformer/test_output_transformer_example.py index ca16acb10..e140dfa82 100644 --- a/tests/playwright/shiny/deprecated/output_transformer/test_output_transformer_example.py +++ b/tests/playwright/shiny/deprecated/output_transformer/test_output_transformer_example.py @@ -1,6 +1,5 @@ -from conftest import ShinyAppProc -from controls import OutputTextVerbatim -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import OutputTextVerbatim def test_output_image_kitchen(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/experimental/card/kitchensink/test_card_ks.py b/tests/playwright/shiny/experimental/card/kitchensink/test_card_ks.py index a11130dca..6a7c24d8e 100644 --- a/tests/playwright/shiny/experimental/card/kitchensink/test_card_ks.py +++ b/tests/playwright/shiny/experimental/card/kitchensink/test_card_ks.py @@ -1,6 +1,5 @@ -from conftest import ShinyAppProc -from controls import Card -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import Card def get_body_tag_name(card: Card) -> str: diff --git a/tests/playwright/shiny/experimental/card/test_card.py b/tests/playwright/shiny/experimental/card/test_card.py index 6c49f4e0b..7a154a5ae 100644 --- a/tests/playwright/shiny/experimental/card/test_card.py +++ b/tests/playwright/shiny/experimental/card/test_card.py @@ -1,6 +1,5 @@ -from conftest import ShinyAppProc -from controls import Card -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import Card def test_card(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/implicit-register/test_implicit_register.py b/tests/playwright/shiny/implicit-register/test_implicit_register.py index 668274dbd..6c1420163 100644 --- a/tests/playwright/shiny/implicit-register/test_implicit_register.py +++ b/tests/playwright/shiny/implicit-register/test_implicit_register.py @@ -1,5 +1,4 @@ -from conftest import ShinyAppProc -from playwright.sync_api import Page, expect +from shiny.test import Page, ShinyAppProc, expect def test_implicit_register(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/inputs/input_file/app.py b/tests/playwright/shiny/inputs/input_file/app.py index 4541df7c0..369d7f90a 100644 --- a/tests/playwright/shiny/inputs/input_file/app.py +++ b/tests/playwright/shiny/inputs/input_file/app.py @@ -40,8 +40,8 @@ def summary(): row_count = df.shape[0] column_count = df.shape[1] names: list[str] = ( - df.columns.tolist() - ) # pyright: ignore[reportUnknownMemberType] + df.columns.tolist() # pyright: ignore[reportUnknownMemberType] + ) column_names = ", ".join(str(name) for name in names) # Create a new DataFrame to display the information diff --git a/tests/playwright/shiny/inputs/input_file/test_input_file.py b/tests/playwright/shiny/inputs/input_file/test_input_file.py index 953c1ea35..0610773c2 100644 --- a/tests/playwright/shiny/inputs/input_file/test_input_file.py +++ b/tests/playwright/shiny/inputs/input_file/test_input_file.py @@ -1,8 +1,8 @@ from __future__ import annotations -from conftest import ShinyAppProc -from controls import InputFile, OutputTable, OutputTextVerbatim -from playwright.sync_api import FilePayload, Page, expect +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputFile, OutputTable, OutputTextVerbatim +from shiny.test._playwright import FilePayload def test_input_file_kitchen(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/inputs/input_radio_checkbox_group/test_input_radio_checkbox_group_app.py b/tests/playwright/shiny/inputs/input_radio_checkbox_group/test_input_radio_checkbox_group_app.py index 24f5b265b..3431bb39d 100644 --- a/tests/playwright/shiny/inputs/input_radio_checkbox_group/test_input_radio_checkbox_group_app.py +++ b/tests/playwright/shiny/inputs/input_radio_checkbox_group/test_input_radio_checkbox_group_app.py @@ -1,8 +1,7 @@ from __future__ import annotations -from conftest import ShinyAppProc -from controls import InputCheckboxGroup, InputRadioButtons, PatternOrStr -from playwright.sync_api import Page, expect +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputCheckboxGroup, InputRadioButtons, PatternOrStr def test_input_checkbox_group_kitchen(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/inputs/input_slider/test_input_slider_app.py b/tests/playwright/shiny/inputs/input_slider/test_input_slider_app.py index bb109e7f1..00bfa0325 100644 --- a/tests/playwright/shiny/inputs/input_slider/test_input_slider_app.py +++ b/tests/playwright/shiny/inputs/input_slider/test_input_slider_app.py @@ -1,10 +1,8 @@ import re import time -from conftest import ShinyAppProc -from controls import InputSlider, InputSliderRange, OutputTextVerbatim -from playwright.sync_api import Page - +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import InputSlider, InputSliderRange, OutputTextVerbatim from shiny.types import MISSING diff --git a/tests/playwright/shiny/inputs/input_task_button/test_input_task_button.py b/tests/playwright/shiny/inputs/input_task_button/test_input_task_button.py index f90ce3d5b..05942c387 100644 --- a/tests/playwright/shiny/inputs/input_task_button/test_input_task_button.py +++ b/tests/playwright/shiny/inputs/input_task_button/test_input_task_button.py @@ -2,9 +2,8 @@ import time -from conftest import ShinyAppProc -from controls import InputNumeric, InputTaskButton, OutputText -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import InputNumeric, InputTaskButton, OutputText def click_extended_task_button( diff --git a/tests/playwright/shiny/inputs/input_task_button2/test_input_task_button2.py b/tests/playwright/shiny/inputs/input_task_button2/test_input_task_button2.py index 85da00177..026c23e10 100644 --- a/tests/playwright/shiny/inputs/input_task_button2/test_input_task_button2.py +++ b/tests/playwright/shiny/inputs/input_task_button2/test_input_task_button2.py @@ -1,8 +1,7 @@ from __future__ import annotations -from conftest import ShinyAppProc -from controls import InputTaskButton, OutputText -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import InputTaskButton, OutputText def click_extended_task_button( diff --git a/tests/playwright/shiny/inputs/test_input_action_button_link.py b/tests/playwright/shiny/inputs/test_input_action_button_link.py index 66edf7682..f51058433 100644 --- a/tests/playwright/shiny/inputs/test_input_action_button_link.py +++ b/tests/playwright/shiny/inputs/test_input_action_button_link.py @@ -1,6 +1,7 @@ -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputActionButton, InputActionLink -from playwright.sync_api import Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputActionButton, InputActionLink app = create_doc_example_core_fixture("update_action_button") diff --git a/tests/playwright/shiny/inputs/test_input_checkbox.py b/tests/playwright/shiny/inputs/test_input_checkbox.py index 687c5aca2..3bf1795b1 100644 --- a/tests/playwright/shiny/inputs/test_input_checkbox.py +++ b/tests/playwright/shiny/inputs/test_input_checkbox.py @@ -1,6 +1,7 @@ -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputCheckbox, OutputUi -from playwright.sync_api import Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputCheckbox, OutputUi app = create_doc_example_core_fixture("input_checkbox") diff --git a/tests/playwright/shiny/inputs/test_input_checkbox_group.py b/tests/playwright/shiny/inputs/test_input_checkbox_group.py index aab1262c2..c1df2fbaf 100644 --- a/tests/playwright/shiny/inputs/test_input_checkbox_group.py +++ b/tests/playwright/shiny/inputs/test_input_checkbox_group.py @@ -1,6 +1,7 @@ -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputCheckboxGroup -from playwright.sync_api import Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputCheckboxGroup app = create_doc_example_core_fixture("input_checkbox_group") diff --git a/tests/playwright/shiny/inputs/test_input_dark_mode.py b/tests/playwright/shiny/inputs/test_input_dark_mode.py index d5c793566..556aaee4d 100644 --- a/tests/playwright/shiny/inputs/test_input_dark_mode.py +++ b/tests/playwright/shiny/inputs/test_input_dark_mode.py @@ -1,8 +1,9 @@ from __future__ import annotations -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputActionButton, InputDarkMode, LayoutNavSetBar -from playwright.sync_api import Page +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import InputActionButton, InputDarkMode, LayoutNavSetBar app = create_doc_example_core_fixture("input_dark_mode") diff --git a/tests/playwright/shiny/inputs/test_input_date.py b/tests/playwright/shiny/inputs/test_input_date.py index ceac65dd1..fbf7887c0 100644 --- a/tests/playwright/shiny/inputs/test_input_date.py +++ b/tests/playwright/shiny/inputs/test_input_date.py @@ -4,9 +4,10 @@ import typing from typing import Literal -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputDate -from playwright.sync_api import Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputDate app = create_doc_example_core_fixture("input_date") diff --git a/tests/playwright/shiny/inputs/test_input_date_range.py b/tests/playwright/shiny/inputs/test_input_date_range.py index 778f6d1d7..dd704b224 100644 --- a/tests/playwright/shiny/inputs/test_input_date_range.py +++ b/tests/playwright/shiny/inputs/test_input_date_range.py @@ -4,9 +4,10 @@ import typing from typing import Literal -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputDateRange -from playwright.sync_api import Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputDateRange app = create_doc_example_core_fixture("input_date_range") diff --git a/tests/playwright/shiny/inputs/test_input_numeric.py b/tests/playwright/shiny/inputs/test_input_numeric.py index 503be64c3..2f7be1146 100644 --- a/tests/playwright/shiny/inputs/test_input_numeric.py +++ b/tests/playwright/shiny/inputs/test_input_numeric.py @@ -1,6 +1,7 @@ -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputNumeric, OutputTextVerbatim -from playwright.sync_api import Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputNumeric, OutputTextVerbatim app = create_doc_example_core_fixture("input_numeric") diff --git a/tests/playwright/shiny/inputs/test_input_password.py b/tests/playwright/shiny/inputs/test_input_password.py index ea83fe17d..1d3f40103 100644 --- a/tests/playwright/shiny/inputs/test_input_password.py +++ b/tests/playwright/shiny/inputs/test_input_password.py @@ -1,6 +1,7 @@ -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputActionButton, InputPassword, OutputTextVerbatim -from playwright.sync_api import Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputActionButton, InputPassword, OutputTextVerbatim app = create_doc_example_core_fixture("input_password") diff --git a/tests/playwright/shiny/inputs/test_input_radio_buttons.py b/tests/playwright/shiny/inputs/test_input_radio_buttons.py index b19d888b0..48199936b 100644 --- a/tests/playwright/shiny/inputs/test_input_radio_buttons.py +++ b/tests/playwright/shiny/inputs/test_input_radio_buttons.py @@ -1,6 +1,7 @@ -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputRadioButtons -from playwright.sync_api import Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputRadioButtons app = create_doc_example_core_fixture("input_radio_buttons") diff --git a/tests/playwright/shiny/inputs/test_input_select.py b/tests/playwright/shiny/inputs/test_input_select.py index 9d4316912..9b3b67afa 100644 --- a/tests/playwright/shiny/inputs/test_input_select.py +++ b/tests/playwright/shiny/inputs/test_input_select.py @@ -1,6 +1,7 @@ -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputSelect -from playwright.sync_api import Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputSelect app = create_doc_example_core_fixture("input_select") diff --git a/tests/playwright/shiny/inputs/test_input_selectize.py b/tests/playwright/shiny/inputs/test_input_selectize.py index f725b3752..546fa2e3e 100644 --- a/tests/playwright/shiny/inputs/test_input_selectize.py +++ b/tests/playwright/shiny/inputs/test_input_selectize.py @@ -1,6 +1,7 @@ -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputSelectize -from playwright.sync_api import Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputSelectize app = create_doc_example_core_fixture("input_selectize") diff --git a/tests/playwright/shiny/inputs/test_input_slider.py b/tests/playwright/shiny/inputs/test_input_slider.py index 5da4cfaac..79fb9e9ec 100644 --- a/tests/playwright/shiny/inputs/test_input_slider.py +++ b/tests/playwright/shiny/inputs/test_input_slider.py @@ -1,6 +1,7 @@ -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputSlider, OutputTextVerbatim -from playwright.sync_api import Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputSlider, OutputTextVerbatim slider_app = create_doc_example_core_fixture("input_slider") template_app = create_doc_example_core_fixture("template") diff --git a/tests/playwright/shiny/inputs/test_input_switch.py b/tests/playwright/shiny/inputs/test_input_switch.py index 0fc16a042..5b6bf823a 100644 --- a/tests/playwright/shiny/inputs/test_input_switch.py +++ b/tests/playwright/shiny/inputs/test_input_switch.py @@ -1,6 +1,7 @@ -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputSwitch, OutputUi -from playwright.sync_api import Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputSwitch, OutputUi app = create_doc_example_core_fixture("input_switch") diff --git a/tests/playwright/shiny/inputs/test_input_text.py b/tests/playwright/shiny/inputs/test_input_text.py index d2b045c69..131c73c50 100644 --- a/tests/playwright/shiny/inputs/test_input_text.py +++ b/tests/playwright/shiny/inputs/test_input_text.py @@ -1,8 +1,9 @@ import re -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputText, OutputTextVerbatim -from playwright.sync_api import Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputText, OutputTextVerbatim app = create_doc_example_core_fixture("input_text") diff --git a/tests/playwright/shiny/inputs/test_input_text_area.py b/tests/playwright/shiny/inputs/test_input_text_area.py index 40ab6ccd0..c4531a79b 100644 --- a/tests/playwright/shiny/inputs/test_input_text_area.py +++ b/tests/playwright/shiny/inputs/test_input_text_area.py @@ -1,8 +1,9 @@ import re -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputTextArea, OutputTextVerbatim -from playwright.sync_api import Locator, Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Locator, Page, ShinyAppProc, expect +from shiny.test._controls import InputTextArea, OutputTextVerbatim app = create_doc_example_core_fixture("input_text_area") diff --git a/tests/playwright/shiny/inputs/test_inputs_update.py b/tests/playwright/shiny/inputs/test_inputs_update.py index bd52d1324..eb927b8f4 100644 --- a/tests/playwright/shiny/inputs/test_inputs_update.py +++ b/tests/playwright/shiny/inputs/test_inputs_update.py @@ -1,7 +1,9 @@ # pyright: reportUnknownMemberType=false -from conftest import ShinyAppProc, create_example_fixture -from controls import ( +from conftest import create_example_fixture + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import ( MISSING, InputCheckbox, InputCheckboxGroup, @@ -14,7 +16,6 @@ InputSliderRange, InputText, ) -from playwright.sync_api import Page, expect inputs_update_app = create_example_fixture("inputs-update") diff --git a/tests/playwright/shiny/module-conditional/test_module_conditional.py b/tests/playwright/shiny/module-conditional/test_module_conditional.py index c83a1fecb..967c386e3 100644 --- a/tests/playwright/shiny/module-conditional/test_module_conditional.py +++ b/tests/playwright/shiny/module-conditional/test_module_conditional.py @@ -1,6 +1,5 @@ -from conftest import ShinyAppProc -from controls import InputCheckbox -from playwright.sync_api import Page, expect +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputCheckbox def test_async_app(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/outputs/test_output_image.py b/tests/playwright/shiny/outputs/test_output_image.py index 76c18a3b9..6a08312d6 100644 --- a/tests/playwright/shiny/outputs/test_output_image.py +++ b/tests/playwright/shiny/outputs/test_output_image.py @@ -1,8 +1,9 @@ import re -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import OutputImage -from playwright.sync_api import Page +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import OutputImage app = create_doc_example_core_fixture("output_image") diff --git a/tests/playwright/shiny/outputs/test_output_plot.py b/tests/playwright/shiny/outputs/test_output_plot.py index 5999badf5..572b557db 100644 --- a/tests/playwright/shiny/outputs/test_output_plot.py +++ b/tests/playwright/shiny/outputs/test_output_plot.py @@ -1,8 +1,9 @@ import re -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import OutputPlot -from playwright.sync_api import Page +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import OutputPlot app = create_doc_example_core_fixture("output_plot") diff --git a/tests/playwright/shiny/outputs/test_output_table.py b/tests/playwright/shiny/outputs/test_output_table.py index 72e55efdb..4f35af1de 100644 --- a/tests/playwright/shiny/outputs/test_output_table.py +++ b/tests/playwright/shiny/outputs/test_output_table.py @@ -1,6 +1,7 @@ -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import OutputTable -from playwright.sync_api import Page +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import OutputTable app = create_doc_example_core_fixture("output_table") diff --git a/tests/playwright/shiny/outputs/test_output_text.py b/tests/playwright/shiny/outputs/test_output_text.py index bca59a0a4..b47d281fb 100644 --- a/tests/playwright/shiny/outputs/test_output_text.py +++ b/tests/playwright/shiny/outputs/test_output_text.py @@ -1,6 +1,7 @@ -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputText, OutputText, OutputTextVerbatim -from playwright.sync_api import Page +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import InputText, OutputText, OutputTextVerbatim app = create_doc_example_core_fixture("output_text") diff --git a/tests/playwright/shiny/outputs/test_output_ui.py b/tests/playwright/shiny/outputs/test_output_ui.py index 44508b6d7..80ce1be7a 100644 --- a/tests/playwright/shiny/outputs/test_output_ui.py +++ b/tests/playwright/shiny/outputs/test_output_ui.py @@ -1,6 +1,7 @@ -from conftest import ShinyAppProc, create_doc_example_core_fixture -from controls import InputActionButton, InputSlider, InputText, OutputUi -from playwright.sync_api import Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import InputActionButton, InputSlider, InputText, OutputUi app = create_doc_example_core_fixture("output_ui") diff --git a/tests/playwright/shiny/plot-sizing/test_plot_sizing.py b/tests/playwright/shiny/plot-sizing/test_plot_sizing.py index aef615d6c..b1dd12625 100644 --- a/tests/playwright/shiny/plot-sizing/test_plot_sizing.py +++ b/tests/playwright/shiny/plot-sizing/test_plot_sizing.py @@ -1,8 +1,7 @@ import re -from conftest import ShinyAppProc -from controls import OutputPlot -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import OutputPlot def test_output_image_kitchen(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/server/output_transformer/test_output_transformer_async.py b/tests/playwright/shiny/server/output_transformer/test_output_transformer_async.py index 11230bff1..fed2f9c0d 100644 --- a/tests/playwright/shiny/server/output_transformer/test_output_transformer_async.py +++ b/tests/playwright/shiny/server/output_transformer/test_output_transformer_async.py @@ -1,6 +1,5 @@ -from conftest import ShinyAppProc -from controls import OutputTextVerbatim -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import OutputTextVerbatim def test_output_transformer(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/server/reactive_event/test_reactive_event.py b/tests/playwright/shiny/server/reactive_event/test_reactive_event.py index 4036bba5d..2996608e4 100644 --- a/tests/playwright/shiny/server/reactive_event/test_reactive_event.py +++ b/tests/playwright/shiny/server/reactive_event/test_reactive_event.py @@ -1,6 +1,5 @@ -from conftest import ShinyAppProc -from controls import InputActionButton, OutputTextVerbatim -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import InputActionButton, OutputTextVerbatim def test_output_image_kitchen(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/session/flush/test_on_flush.py b/tests/playwright/shiny/session/flush/test_on_flush.py index 4d14a3321..90dfd45e8 100644 --- a/tests/playwright/shiny/session/flush/test_on_flush.py +++ b/tests/playwright/shiny/session/flush/test_on_flush.py @@ -1,6 +1,5 @@ -from conftest import ShinyAppProc -from controls import OutputTextVerbatim -from playwright.sync_api import Page +from shiny.test import Page, ShinyAppProc +from shiny.test._controls import OutputTextVerbatim def test_output_image_kitchen(page: Page, local_app: ShinyAppProc) -> None: diff --git a/tests/playwright/shiny/shiny-express/hold/test_hold.py b/tests/playwright/shiny/shiny-express/hold/test_hold.py index 77df73f52..82d6d8b6f 100644 --- a/tests/playwright/shiny/shiny-express/hold/test_hold.py +++ b/tests/playwright/shiny/shiny-express/hold/test_hold.py @@ -1,7 +1,5 @@ -from conftest import ShinyAppProc -from controls import OutputTextVerbatim -from playwright.sync_api import Page -from playwright.sync_api import expect as playwright_expect +from shiny.test import Page, ShinyAppProc, expect +from shiny.test._controls import OutputTextVerbatim def test_express_page_fluid(page: Page, local_app: ShinyAppProc) -> None: @@ -10,5 +8,5 @@ def test_express_page_fluid(page: Page, local_app: ShinyAppProc) -> None: txt = OutputTextVerbatim(page, "visible") txt.expect_value("40") - playwright_expect(page.locator("#visible")).to_have_count(1) - playwright_expect(page.locator("#hidden")).to_have_count(0) + expect(page.locator("#visible")).to_have_count(1) + expect(page.locator("#hidden")).to_have_count(0) diff --git a/tests/playwright/shiny/shiny-express/render_express/test_render_express.py b/tests/playwright/shiny/shiny-express/render_express/test_render_express.py index 5abd75a4e..feaba3dd7 100644 --- a/tests/playwright/shiny/shiny-express/render_express/test_render_express.py +++ b/tests/playwright/shiny/shiny-express/render_express/test_render_express.py @@ -1,5 +1,6 @@ -from conftest import ShinyAppProc, create_doc_example_core_fixture -from playwright.sync_api import Page, expect +from conftest import create_doc_example_core_fixture + +from shiny.test import Page, ShinyAppProc, expect app = create_doc_example_core_fixture("render_express") diff --git a/tests/playwright/utils/deploy_utils.py b/tests/playwright/utils/deploy_utils.py index b41bb340d..84463ba74 100644 --- a/tests/playwright/utils/deploy_utils.py +++ b/tests/playwright/utils/deploy_utils.py @@ -11,7 +11,9 @@ import pytest import requests -from conftest import ScopeName, local_app_fixture_gen +from conftest import ScopeName + +from shiny.test._conftest import shiny_app_gen is_interactive = hasattr(sys, "ps1") reruns = 1 if is_interactive else 3 @@ -181,7 +183,7 @@ def fix_fn(request: pytest.FixtureRequest): deploy_location = request.param if deploy_location == LOCAL_LOCATION: - shinyapp_proc_gen = local_app_fixture_gen(app_file) + shinyapp_proc_gen = shiny_app_gen(app_file) # Return the `url` yield next(shinyapp_proc_gen).url elif deploy_location in deploy_locations: