diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index ad355f70b..51c27c69d 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -193,6 +193,17 @@ jobs: working-directory: examples/brownian/shinymediapipe run: | npm ci + - name: Checkout py-shiny-templates repository + uses: actions/checkout@v4 + with: + repository: posit-dev/py-shiny-templates + path: py-shiny-templates + + - name: Install py-shiny-templates dependencies + if: matrix.python-version != '3.9' + # Scikit-learn 1.7.0+ requires Python 3.10+ + run: | + make install-py-shiny-templates-deps - name: Run example app tests timeout-minutes: 60 diff --git a/Makefile b/Makefile index 215ef4cb4..1a8c9338e 100644 --- a/Makefile +++ b/Makefile @@ -220,13 +220,15 @@ ci-install-wheel: dist FORCE uv pip install dist/shiny*.whl install-deps: FORCE ## install dependencies - pip install -e ".[dev,test]" --upgrade + uv pip install -e ".[dev,test]" --upgrade ci-install-deps: FORCE uv pip install -e ".[dev,test]" +install-py-shiny-templates-deps: FORCE + uv pip install -r py-shiny-templates/requirements.txt install-docs: FORCE - pip install -e ".[dev,test,doc]" - pip install https://github.com/posit-dev/py-shinylive/tarball/main + uv pip install -e ".[dev,test,doc]" + uv pip install https://github.com/posit-dev/py-shinylive/tarball/main ci-install-docs: FORCE uv pip install -e ".[dev,test,doc]" \ "shinylive @ git+https://github.com/posit-dev/py-shinylive.git" diff --git a/pyproject.toml b/pyproject.toml index 0a5ef7e76..f69807faa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -91,6 +91,7 @@ test = [ "dask[dataframe]", "pyarrow", "pyarrow-stubs", + ] dev = [ "black>=24.0", diff --git a/tests/playwright/examples/example_apps.py b/tests/playwright/examples/example_apps.py index 942ccc644..fe07988f7 100644 --- a/tests/playwright/examples/example_apps.py +++ b/tests/playwright/examples/example_apps.py @@ -4,7 +4,7 @@ from pathlib import PurePath from typing import Literal -from playwright.sync_api import ConsoleMessage, Page +from playwright.sync_api import ConsoleMessage, Page, expect from shiny.run import ShinyAppProc, run_shiny_app @@ -15,6 +15,9 @@ reruns = 1 if is_interactive else 3 reruns_delay = 0 +SHINY_INIT_TIMEOUT = 30_000 +ERROR_ELEMENT_TIMEOUT = 1_000 + def get_apps(path: str) -> typing.List[str]: full_path = pyshiny_root / path @@ -91,11 +94,20 @@ def get_apps(path: str) -> typing.List[str]: "FutureWarning: use_inf_as_na option is deprecated", "pd.option_context('mode.use_inf_as_na", # continutation of line above, "RuntimeWarning: invalid value encountered in dot", # some groups didn't have enough data points to create a meaningful line + "DatetimeIndex.format is deprecated and will be removed in a future version. Convert using index.astype(str) or index.map(formatter) instead.", # cufflinks package is using this method + "Series.__getitem__ treating keys as positions is deprecated. In a future version, integer keys will always be treated as labels (consistent with DataFrame behavior). To access a value by position, use `ser.iloc[pos]`", # cufflinks package is using this method ] app_allow_js_errors: typing.Dict[str, typing.List[str]] = { "examples/brownian": ["Failed to acquire camera feed:"], } +# Check for Shiny output errors, except for known exception cases +app_allow_output_error = [ + "shiny/api-examples/SafeException/app-express.py", + "shiny/api-examples/SafeException/app-core.py", + "examples/global_pyplot/app.py", +] + # Altered from `shinytest2:::app_wait_for_idle()` # https://github.com/rstudio/shinytest2/blob/b8fdce681597e9610fc078aa6e376134c404f3bd/R/app-driver-wait.R @@ -251,3 +263,8 @@ def on_console_msg(msg: ConsoleMessage) -> None: + " had JavaScript console errors!\n" + "* ".join(console_errors) ) + + if ex_app_path not in app_allow_output_error: + # Ensure there are no output errors present + error_locator = page.locator(".shiny-output-error") + expect(error_locator).to_have_count(0, timeout=ERROR_ELEMENT_TIMEOUT) diff --git a/tests/playwright/examples/test_external_templates.py b/tests/playwright/examples/test_external_templates.py new file mode 100644 index 000000000..2f32ab7f3 --- /dev/null +++ b/tests/playwright/examples/test_external_templates.py @@ -0,0 +1,24 @@ +import sys +from pathlib import Path + +import pytest +from conftest import here_root +from example_apps import get_apps, reruns, reruns_delay, validate_example +from playwright.sync_api import Page + +if not Path(here_root / "py-shiny-templates").exists(): + pytest.skip( + "./py-shiny-templates dir is not available. Skipping test.", + allow_module_level=True, + ) + + +@pytest.mark.skipif( + sys.version_info[:2] == (3, 9), + reason="Skipping test for Python 3.9 since scikit-learn pinned version is only supported on Python 3.10+", +) +@pytest.mark.only_browser("chromium") +@pytest.mark.flaky(reruns=reruns, reruns_delay=reruns_delay) +@pytest.mark.parametrize("ex_app_path", get_apps("py-shiny-templates")) +def test_external_templates(page: Page, ex_app_path: str) -> None: + validate_example(page, ex_app_path) diff --git a/tests/playwright/shiny/components/data_frame/edit/app.py b/tests/playwright/shiny/components/data_frame/edit/app.py index c0b5a9549..34876125e 100644 --- a/tests/playwright/shiny/components/data_frame/edit/app.py +++ b/tests/playwright/shiny/components/data_frame/edit/app.py @@ -263,6 +263,7 @@ def df_styles_fn(data: pd.DataFrame) -> list[StyleInfo]: @render_widget def country_detail_pop(): # pyright: ignore[reportUnknownParameterType] + import plotly.express as px # Create the figure explicitly