mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
migrate US-011: test_visualizations.py + shadow DOM support
Add PlaywrightShadowRoot class and shadow_root property to PlaywrightElement, matching Selenium WebElement.shadow_root API. Remove @selenium_only from 3 visualization tests. All tests skip due to missing viz plugins on external server, not Playwright issues. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
7a4208f893
commit
d705d47293
@@ -10,12 +10,34 @@ from typing import (
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
from playwright.sync_api import ElementHandle
|
||||
from playwright.sync_api import (
|
||||
ElementHandle,
|
||||
JSHandle,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .has_playwright_driver import HasPlaywrightDriver
|
||||
from .web_element_protocol import WebElementProtocol
|
||||
|
||||
|
||||
class PlaywrightShadowRoot:
|
||||
"""Wrapper around a shadow root JSHandle that provides find_element support."""
|
||||
|
||||
def __init__(self, shadow_root_handle: JSHandle, driver: "HasPlaywrightDriver"):
|
||||
self._shadow_root = shadow_root_handle
|
||||
self._driver = driver
|
||||
|
||||
def find_element(self, by: str = "id", value: Optional[str] = None) -> "WebElementProtocol":
|
||||
if value is None:
|
||||
raise ValueError("value parameter is required")
|
||||
selector = self._driver._selenium_locator_to_playwright_selector(by, value)
|
||||
result_handle = self._shadow_root.evaluate_handle(f"root => root.querySelector('{selector}')")
|
||||
element_handle = result_handle.as_element()
|
||||
if element_handle:
|
||||
return PlaywrightElement(element_handle, self._driver)
|
||||
raise Exception(f"No element found in shadow root with {by}='{value}'")
|
||||
|
||||
|
||||
class PlaywrightElement:
|
||||
"""
|
||||
Wrapper around Playwright ElementHandle that implements WebElementProtocol.
|
||||
@@ -24,14 +46,7 @@ class PlaywrightElement:
|
||||
WebElement API, allowing the same code to work with both backends.
|
||||
"""
|
||||
|
||||
def __init__(self, element_handle: ElementHandle, driver):
|
||||
"""
|
||||
Initialize PlaywrightElement wrapper.
|
||||
|
||||
Args:
|
||||
element_handle: The Playwright ElementHandle to wrap
|
||||
driver: The HasPlaywrightDriver instance (for find_element operations)
|
||||
"""
|
||||
def __init__(self, element_handle: ElementHandle, driver: "HasPlaywrightDriver"):
|
||||
self._element = element_handle
|
||||
self._driver = driver
|
||||
|
||||
@@ -119,57 +134,29 @@ class PlaywrightElement:
|
||||
"""
|
||||
self._element.evaluate("(el) => el.form ? el.form.submit() : el.submit()")
|
||||
|
||||
@property
|
||||
def shadow_root(self) -> PlaywrightShadowRoot:
|
||||
"""Access the shadow root of this element, matching Selenium's WebElement.shadow_root."""
|
||||
handle = self._element.evaluate_handle("el => el.shadowRoot")
|
||||
return PlaywrightShadowRoot(handle, self._driver)
|
||||
|
||||
def find_element(self, by: str = "id", value: Optional[str] = None) -> "WebElementProtocol":
|
||||
"""
|
||||
Find a child element within this element.
|
||||
|
||||
Delegates to the driver's element finding logic.
|
||||
|
||||
Args:
|
||||
by: The locator strategy (e.g., "id", "css selector")
|
||||
value: The locator value
|
||||
|
||||
Returns:
|
||||
A PlaywrightElement wrapping the found ElementHandle
|
||||
"""
|
||||
# Convert the locator to Playwright selector
|
||||
from .has_playwright_driver import HasPlaywrightDriver
|
||||
|
||||
if isinstance(self._driver, HasPlaywrightDriver):
|
||||
if value is None:
|
||||
raise ValueError("value parameter is required")
|
||||
selector = self._driver._selenium_locator_to_playwright_selector(by, value)
|
||||
# Use ElementHandle's query_selector to find within this element
|
||||
found_element = self._element.query_selector(selector)
|
||||
if found_element:
|
||||
return PlaywrightElement(found_element, self._driver)
|
||||
raise Exception(f"No element found with {by}='{value}'")
|
||||
|
||||
raise NotImplementedError("find_element within element not yet fully implemented")
|
||||
"""Find a child element within this element."""
|
||||
if value is None:
|
||||
raise ValueError("value parameter is required")
|
||||
selector = self._driver._selenium_locator_to_playwright_selector(by, value)
|
||||
found_element = self._element.query_selector(selector)
|
||||
if found_element:
|
||||
return PlaywrightElement(found_element, self._driver)
|
||||
raise Exception(f"No element found with {by}='{value}'")
|
||||
|
||||
def find_elements(self, by: str = "id", value: Optional[str] = None) -> list["WebElementProtocol"]:
|
||||
"""
|
||||
Find all child elements matching the locator within this element.
|
||||
|
||||
Delegates to the driver's element finding logic.
|
||||
|
||||
Args:
|
||||
by: The locator strategy (e.g., "css selector", "xpath")
|
||||
value: The locator value
|
||||
|
||||
Returns:
|
||||
List of PlaywrightElement instances
|
||||
"""
|
||||
from .has_playwright_driver import HasPlaywrightDriver
|
||||
|
||||
if isinstance(self._driver, HasPlaywrightDriver):
|
||||
if value is None:
|
||||
raise ValueError("value parameter is required")
|
||||
selector = self._driver._selenium_locator_to_playwright_selector(by, value)
|
||||
found_elements = self._element.query_selector_all(selector)
|
||||
return [PlaywrightElement(elem, self._driver) for elem in found_elements]
|
||||
|
||||
raise NotImplementedError("find_elements within element not yet fully implemented")
|
||||
"""Find all child elements matching the locator within this element."""
|
||||
if value is None:
|
||||
raise ValueError("value parameter is required")
|
||||
selector = self._driver._selenium_locator_to_playwright_selector(by, value)
|
||||
found_elements = self._element.query_selector_all(selector)
|
||||
return [PlaywrightElement(elem, self._driver) for elem in found_elements]
|
||||
|
||||
def content_frame(self):
|
||||
"""
|
||||
|
||||
@@ -4,7 +4,6 @@ from galaxy_test.base.populators import (
|
||||
)
|
||||
from .framework import (
|
||||
managed_history,
|
||||
selenium_only,
|
||||
selenium_test,
|
||||
SeleniumTestCase,
|
||||
)
|
||||
@@ -32,7 +31,6 @@ class TestVisualizationsAnonymous(SeleniumTestCase):
|
||||
self.wait_for_selector("#image-annotate")
|
||||
self.screenshot("visualization_plugin_charts_image_annotate")
|
||||
|
||||
@selenium_only("Not yet migrated to support Playwright backend - need to implement shadow DOM absractions")
|
||||
@selenium_test
|
||||
@skip_without_visualization_plugin("tabulator")
|
||||
def test_charts_tabulator(self):
|
||||
@@ -47,7 +45,6 @@ class TestVisualizationsAnonymous(SeleniumTestCase):
|
||||
self.wait_for_selector(".tabulator-table")
|
||||
self.screenshot("visualization_plugin_tabulator_landing")
|
||||
|
||||
@selenium_only("Not yet migrated to support Playwright backend")
|
||||
@selenium_test
|
||||
@skip_without_visualization_plugin("h5web")
|
||||
def test_charts_h5web(self):
|
||||
@@ -65,7 +62,6 @@ class TestVisualizationsAnonymous(SeleniumTestCase):
|
||||
class TestVisualizations(SeleniumTestCase):
|
||||
ensure_registered = True
|
||||
|
||||
@selenium_only("Not yet migrated to support Playwright backend - need to implement shadow DOM absractions")
|
||||
@selenium_test
|
||||
@managed_history
|
||||
@skip_without_visualization_plugin("igv")
|
||||
|
||||
Reference in New Issue
Block a user