diff --git a/CHANGELOG.md b/CHANGELOG.md index d12eb15c2..55afe614b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,7 +76,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Fixed output controller `OutputDataFrame` in `shiny.playwright.controller` to correctly assert the number of rows in `.expect_nrow()` as the total number of virtual rows, not the number of currently displaying rows. (#1719) -* Fixed issue where `@render.download` did not respect the module namespacing. (Thanks, @nsiicm0) (#1732) +* Fixed issue where `@render.download` did not respect the module namespacing. (Thanks, @nsiicm0!) (#1732) + +* Added workaround in `Accordion` in `shiny.playwright.controller` where `.expect_open()` and `.expect_panels()` would hang while resolving a playwright locator. (Thanks, @joesho112358!) (#1165) ## [1.1.0] - 2024-09-03 diff --git a/shiny/playwright/controller/_accordion.py b/shiny/playwright/controller/_accordion.py index 1e541d479..5995a28d2 100644 --- a/shiny/playwright/controller/_accordion.py +++ b/shiny/playwright/controller/_accordion.py @@ -255,6 +255,7 @@ def expect_open( arr=value, key="data-value", timeout=timeout, + alt_verify=True, ) def expect_panels( @@ -281,6 +282,7 @@ def expect_panels( arr=value, key="data-value", timeout=timeout, + alt_verify=True, ) def set( diff --git a/shiny/playwright/controller/_expect.py b/shiny/playwright/controller/_expect.py index 48fef0645..2e4c81082 100644 --- a/shiny/playwright/controller/_expect.py +++ b/shiny/playwright/controller/_expect.py @@ -146,6 +146,7 @@ def expect_locator_values_in_list( is_checked: bool | MISSING_TYPE = MISSING, timeout: Timeout = None, key: str = "value", + alt_verify: bool = False, ) -> None: """ Expect the locator to contain the values in the list. @@ -171,6 +172,11 @@ def expect_locator_values_in_list( The timeout for the expectation. Defaults to `None`. key The key. Defaults to `"value"`. + alt_verify + Determines if multiple expectations should be performed. + Defaults to `False`, a single (and very complicated) locator is asserted. + `True` will perform multiple assertions, which have the possibility of being invalid. + Use in playwright bug situations only. """ # Make sure the locator has exactly `arr` values @@ -195,6 +201,28 @@ def expect_locator_values_in_list( return loc_container_orig = loc_container + def perform_multiple_assertions(): + # Expecting container to exist (count = 1) + playwright_expect(loc_container_orig).to_have_count(1, timeout=timeout) + + # Expecting the container to contain {len(arr)} items + playwright_expect(loc_container_orig.locator(loc_item)).to_have_count( + len(arr), timeout=timeout + ) + + for item, i in zip(arr, range(len(arr))): + # Expecting item `{i}` to be `{item}` + playwright_expect( + loc_container_orig.locator(loc_item).nth(i) + ).to_have_attribute(key, item, timeout=timeout) + return + + if alt_verify: + # Accordion has issues where the single locator assertion waits forever within playwright. + # Perform multiple assertions until playwright fixes bug. + perform_multiple_assertions() + return + # Find all items in set for item, i in zip(arr, range(len(arr))): # Get all elements of type @@ -220,21 +248,8 @@ def expect_locator_values_in_list( try: playwright_expect(loc_inputs).to_have_count(len(arr), timeout=timeout) except AssertionError as e: - # Debug expections - - # Expecting container to exist (count = 1) - playwright_expect(loc_container_orig).to_have_count(1, timeout=timeout) - - # Expecting the container to contain {len(arr)} items - playwright_expect(loc_container_orig.locator(loc_item)).to_have_count( - len(arr), timeout=timeout - ) - - for item, i in zip(arr, range(len(arr))): - # Expecting item `{i}` to be `{item}` - playwright_expect( - loc_container_orig.locator(loc_item).nth(i) - ).to_have_attribute(key, item, timeout=timeout) + # Debug expectations + perform_multiple_assertions() # Could not find the reason why. Raising the original error. raise e diff --git a/shiny/render/_data_frame_utils/_tbl_data.py b/shiny/render/_data_frame_utils/_tbl_data.py index 957331965..399891ff7 100644 --- a/shiny/render/_data_frame_utils/_tbl_data.py +++ b/shiny/render/_data_frame_utils/_tbl_data.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, List, TypedDict, cast +from typing import TYPE_CHECKING, Any, List, TypedDict, cast import narwhals.stable.v1 as nw import orjson @@ -33,6 +33,9 @@ "subset_frame", ) +if TYPE_CHECKING: + import pandas as pd + ######################################################################################## # Narwhals # @@ -81,15 +84,20 @@ def as_data_frame( except TypeError as e: try: compatible_data = compatible_to_pandas(data) - return nw.from_native(compatible_data, eager_only=True) + ret: DataFrame[pd.DataFrame] = nw.from_native( + compatible_data, eager_only=True + ) + # Cast internal data as `IntoDataFrameT` type. + # A warning has already been given to the user, so this is tolerable. + return cast(DataFrame[IntoDataFrameT], ret) except TypeError: # Couldn't convert to pandas, so raise the original error raise e def compatible_to_pandas( - data: IntoDataFrameT, -) -> IntoDataFrameT: + data: IntoDataFrame, +) -> pd.DataFrame: """ Convert data to pandas, if possible. @@ -108,6 +116,7 @@ def compatible_to_pandas( stacklevel=3, ) return data.to_pandas() + # pyright: ignore[reportReturnType] raise TypeError(f"Unsupported data type: {type(data)}") diff --git a/tests/playwright/shiny/components/accordion/test_accordion.py b/tests/playwright/shiny/components/accordion/test_accordion.py index 174e18d7a..f2d366c4b 100644 --- a/tests/playwright/shiny/components/accordion/test_accordion.py +++ b/tests/playwright/shiny/components/accordion/test_accordion.py @@ -79,9 +79,6 @@ def test_accordion(page: Page, local_app: ShinyAppProc) -> None: "input.acc(): ('updated_section_a', 'Section C', 'Section D')" ) - # TODO-karan-future; Remove return when test is able to pass. Currently it hangs indefinitely and no notification as to why. - return - toggle_efg_button.click() acc.expect_panels( [ @@ -92,7 +89,7 @@ def test_accordion(page: Page, local_app: ShinyAppProc) -> None: "Section E", "Section F", "Section G", - ] + ], ) acc.expect_open( [ @@ -102,7 +99,7 @@ def test_accordion(page: Page, local_app: ShinyAppProc) -> None: "Section E", "Section F", "Section G", - ] + ], ) # Should be uncommented once https://github.com/rstudio/bslib/issues/565 is fixed # output_txt_verbatim.expect_value(