Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(playwright): Add playwright workaround within Accordion's .expect_open() and .expect_panels() #1165

Merged
merged 11 commits into from
Oct 16, 2024
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions shiny/playwright/controller/_accordion.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ def expect_open(
arr=value,
key="data-value",
timeout=timeout,
alt_verify=True,
)

def expect_panels(
Expand All @@ -281,6 +282,7 @@ def expect_panels(
arr=value,
key="data-value",
timeout=timeout,
alt_verify=True,
)

def set(
Expand Down
45 changes: 30 additions & 15 deletions shiny/playwright/controller/_expect.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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

Expand All @@ -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
Expand All @@ -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
17 changes: 13 additions & 4 deletions shiny/render/_data_frame_utils/_tbl_data.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -33,6 +33,9 @@
"subset_frame",
)

if TYPE_CHECKING:
import pandas as pd

########################################################################################
# Narwhals
#
Expand Down Expand Up @@ -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.

Expand All @@ -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)}")

Expand Down
7 changes: 2 additions & 5 deletions tests/playwright/shiny/components/accordion/test_accordion.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
[
Expand All @@ -92,7 +89,7 @@ def test_accordion(page: Page, local_app: ShinyAppProc) -> None:
"Section E",
"Section F",
"Section G",
]
],
)
acc.expect_open(
[
Expand All @@ -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(
Expand Down
Loading