Skip to content

Commit 5f9c103

Browse files
authored
Merge branch 'main' into fix-issue-2013
2 parents c717898 + a713ebd commit 5f9c103

File tree

11 files changed

+190
-45
lines changed

11 files changed

+190
-45
lines changed

.github/workflows/pytest.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,17 @@ jobs:
193193
working-directory: examples/brownian/shinymediapipe
194194
run: |
195195
npm ci
196+
- name: Checkout py-shiny-templates repository
197+
uses: actions/checkout@v4
198+
with:
199+
repository: posit-dev/py-shiny-templates
200+
path: py-shiny-templates
201+
202+
- name: Install py-shiny-templates dependencies
203+
if: matrix.python-version != '3.9'
204+
# Scikit-learn 1.7.0+ requires Python 3.10+
205+
run: |
206+
make ci-install-py-shiny-templates-deps
196207
197208
- name: Run example app tests
198209
timeout-minutes: 60

Makefile

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,6 @@ clean-test: FORCE
5656
rm -fr .pytest_cache
5757
rm -rf typings/
5858

59-
typings/appdirs:
60-
@echo "Creating appdirs stubs"
61-
pyright --createstub appdirs
6259
typings/folium:
6360
@echo "Creating folium stubs"
6461
pyright --createstub folium
@@ -75,7 +72,7 @@ typings/matplotlib/__init__.pyi:
7572
mv typings/python-type-stubs/stubs/matplotlib typings/
7673
rm -rf typings/python-type-stubs
7774

78-
pyright-typings: typings/appdirs typings/folium typings/uvicorn typings/seaborn typings/matplotlib/__init__.pyi
75+
pyright-typings: typings/folium typings/uvicorn typings/seaborn typings/matplotlib/__init__.pyi
7976

8077
check: check-format check-lint check-types check-tests ## check code, style, types, and test (basic CI)
8178
check-fix: format check-lint check-types check-tests ## check and format code, style, types, and test
@@ -223,6 +220,8 @@ install-deps: FORCE ## install dependencies
223220
pip install -e ".[dev,test]" --upgrade
224221
ci-install-deps: FORCE
225222
uv pip install -e ".[dev,test]"
223+
ci-install-py-shiny-templates-deps: FORCE
224+
uv pip install -r py-shiny-templates/requirements.txt
226225

227226
install-docs: FORCE
228227
pip install -e ".[dev,test,doc]"

docs/_quartodoc-testing.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ quartodoc:
2626
contents:
2727
- playwright.controller.InputActionButton
2828
- playwright.controller.InputActionLink
29+
- playwright.controller.InputBookmarkButton
2930
- playwright.controller.InputCheckbox
3031
- playwright.controller.InputCheckboxGroup
3132
- playwright.controller.InputDarkMode
@@ -59,6 +60,7 @@ quartodoc:
5960
- playwright.controller.NavsetTab
6061
- playwright.controller.NavsetUnderline
6162
- playwright.controller.NavPanel
63+
- playwright.controller.PageNavbar
6264
- title: Upload and download
6365
desc: Methods for interacting with Shiny app uploading and downloading controller.
6466
contents:

pyproject.toml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ dependencies = [
3737
"markdown-it-py>=1.1.0",
3838
"mdit-py-plugins>=0.3.0",
3939
"linkify-it-py>=1.0",
40-
"appdirs>=1.4.4",
40+
"platformdirs>=2.1.0",
4141
"asgiref>=3.5.2",
4242
"packaging>=20.9",
4343
"watchfiles>=0.18.0;platform_system!='Emscripten'",
@@ -113,11 +113,7 @@ dev = [
113113
"anthropic",
114114
"google-generativeai;python_version>='3.9'",
115115
"langchain_core",
116-
# langsmith (needed for langchain_core) versions >= 0.3
117-
# (up to at least 0.3.2 as of 2025-01-29)
118-
# cause an `argparse.ArgumentError` when running `pytest`.
119-
# https://github.com/posit-dev/py-shiny/issues/1829
120-
"langsmith<0.3",
116+
"langsmith>=0.3.4",
121117
"openai",
122118
"ollama",
123119
"chatlas>=0.6.1",

shiny/_main_add_test.py

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -73,28 +73,10 @@ def path_does_not_exist(x: Path) -> bool | str:
7373
if not test_file.name.startswith("test_"):
7474
return "Test file must start with 'test_'"
7575

76-
# if app path directory is the same as the test file directory, use `local_app`
77-
# otherwise, use `create_app_fixture`
78-
is_same_dir = app_file.parent == test_file.parent
79-
8076
test_name = test_file.name.replace(".py", "")
8177
rel_path = os.path.relpath(app_file, test_file.parent)
8278

83-
template = (
84-
f"""\
85-
from playwright.sync_api import Page
86-
87-
from shiny.playwright import controller
88-
from shiny.run import ShinyAppProc
89-
90-
91-
def {test_name}(page: Page, local_app: ShinyAppProc):
92-
93-
page.goto(local_app.url)
94-
# Add test code here
95-
"""
96-
if is_same_dir
97-
else f"""\
79+
template = f"""\
9880
from playwright.sync_api import Page
9981
10082
from shiny.playwright import controller
@@ -109,7 +91,6 @@ def {test_name}(page: Page, app: ShinyAppProc):
10991
page.goto(app.url)
11092
# Add test code here
11193
"""
112-
)
11394
# Make sure test file directory exists
11495
test_file.parent.mkdir(parents=True, exist_ok=True)
11596

shiny/_static.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,10 @@ def remove_shinylive_local(
3636

3737

3838
def get_default_shinylive_dir() -> Path:
39-
import appdirs
40-
41-
return (
42-
Path(
43-
appdirs.user_cache_dir( # pyright: ignore[reportUnknownMemberType, reportUnknownArgumentType]
44-
"shiny"
45-
)
46-
)
47-
/ "shinylive"
48-
)
39+
import platformdirs
40+
41+
user_cache_dir = platformdirs.user_cache_dir("shinylive")
42+
return Path(user_cache_dir) / "shinylive"
4943

5044

5145
def _installed_shinylive_versions(shinylive_dir: Optional[Path] = None) -> list[str]:

shiny/playwright/controller/_output.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,13 +1058,33 @@ def expect_class_state(
10581058
"cell-edit-editing", timeout=timeout
10591059
)
10601060
elif value == "editing":
1061-
self.expect_cell_class("cell-edit-editing", row=row, col=col)
1061+
self.expect_cell_class(
1062+
"cell-edit-editing",
1063+
row=row,
1064+
col=col,
1065+
timeout=timeout,
1066+
)
10621067
elif value == "saving":
1063-
self.expect_cell_class("cell-edit-saving", row=row, col=col)
1068+
self.expect_cell_class(
1069+
"cell-edit-saving",
1070+
row=row,
1071+
col=col,
1072+
timeout=timeout,
1073+
)
10641074
elif value == "failure":
1065-
self.expect_cell_class("cell-edit-failure", row=row, col=col)
1075+
self.expect_cell_class(
1076+
"cell-edit-failure",
1077+
row=row,
1078+
col=col,
1079+
timeout=timeout,
1080+
)
10661081
elif value == "success":
1067-
self.expect_cell_class("cell-edit-success", row=row, col=col)
1082+
self.expect_cell_class(
1083+
"cell-edit-success",
1084+
row=row,
1085+
col=col,
1086+
timeout=timeout,
1087+
)
10681088
else:
10691089
raise ValueError(
10701090
"Invalid state. Select one of 'success', 'failure', 'saving', 'editing', 'ready'"

tests/playwright/examples/example_apps.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from pathlib import PurePath
55
from typing import Literal
66

7-
from playwright.sync_api import ConsoleMessage, Page
7+
from playwright.sync_api import ConsoleMessage, Page, expect
88

99
from shiny.run import ShinyAppProc, run_shiny_app
1010

@@ -91,11 +91,20 @@ def get_apps(path: str) -> typing.List[str]:
9191
"FutureWarning: use_inf_as_na option is deprecated",
9292
"pd.option_context('mode.use_inf_as_na", # continutation of line above,
9393
"RuntimeWarning: invalid value encountered in dot", # some groups didn't have enough data points to create a meaningful line
94+
"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
95+
"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
9496
]
9597
app_allow_js_errors: typing.Dict[str, typing.List[str]] = {
9698
"examples/brownian": ["Failed to acquire camera feed:"],
9799
}
98100

101+
# Check for Shiny output errors, except for known exception cases
102+
app_allow_output_error = [
103+
"shiny/api-examples/SafeException/app-express.py",
104+
"shiny/api-examples/SafeException/app-core.py",
105+
"examples/global_pyplot/app.py",
106+
]
107+
99108

100109
# Altered from `shinytest2:::app_wait_for_idle()`
101110
# https://github.com/rstudio/shinytest2/blob/b8fdce681597e9610fc078aa6e376134c404f3bd/R/app-driver-wait.R
@@ -251,3 +260,8 @@ def on_console_msg(msg: ConsoleMessage) -> None:
251260
+ " had JavaScript console errors!\n"
252261
+ "* ".join(console_errors)
253262
)
263+
264+
if ex_app_path not in app_allow_output_error:
265+
# Ensure there are no output errors present
266+
error_locator = page.locator(".shiny-output-error")
267+
expect(error_locator).to_have_count(0)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import sys
2+
from pathlib import Path
3+
4+
import pytest
5+
from conftest import here_root
6+
from example_apps import get_apps, reruns, reruns_delay, validate_example
7+
from playwright.sync_api import Page
8+
9+
if not Path(here_root / "py-shiny-templates").exists():
10+
pytest.skip(
11+
"./py-shiny-templates dir is not available. Skipping test.",
12+
allow_module_level=True,
13+
)
14+
15+
16+
@pytest.mark.skipif(
17+
sys.version_info[:2] == (3, 9),
18+
reason="Skipping test for Python 3.9 since scikit-learn pinned version is only supported on Python 3.10+",
19+
)
20+
@pytest.mark.only_browser("chromium")
21+
@pytest.mark.flaky(reruns=reruns, reruns_delay=reruns_delay)
22+
@pytest.mark.parametrize("ex_app_path", get_apps("py-shiny-templates"))
23+
def test_external_templates(page: Page, ex_app_path: str) -> None:
24+
validate_example(page, ex_app_path)

tests/playwright/shiny/components/data_frame/validate_row_selection_edit_mode/test_validate_row_selection_edit_mode.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ def test_validate_row_selection_in_edit_mode(
6060
page.keyboard.press("Escape")
6161
data_frame._edit_cell_no_save("Temp value", row=1, col=16)
6262
page.keyboard.press("Escape")
63+
# Wait for the row to be focused again after escaping edit mode
64+
data_frame._expect_row_focus_state(True, row=1)
65+
# Also ensure the cell is no longer in editing state
66+
data_frame.expect_class_state("ready", row=1, col=16)
6367
page.keyboard.press("Enter")
6468
data_frame.expect_class_state(
6569
"editing",

0 commit comments

Comments
 (0)