From 4775dff7069a3b029107cbee04ef96f5bd6f1c46 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Mon, 9 Feb 2026 17:09:09 -0500 Subject: [PATCH] migrate US-003: test_tutorial_mode.py - remove @selenium_only from test_activate_tutorial_mode - fix switch_to_frame to match iframes by id (not just name), matching Selenium's behavior - fix stale element detection for Playwright's "not attached to the DOM" - add switch_to_default_content after frame assertions for axe compat Co-Authored-By: Claude Opus 4.6 --- lib/galaxy/selenium/has_driver.py | 3 ++- lib/galaxy/selenium/has_playwright_driver.py | 23 ++++++++++++------- .../selenium/test_tutorial_mode.py | 3 +-- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/galaxy/selenium/has_driver.py b/lib/galaxy/selenium/has_driver.py index 66658bcbfb9..ae8f51332a7 100644 --- a/lib/galaxy/selenium/has_driver.py +++ b/lib/galaxy/selenium/has_driver.py @@ -666,7 +666,8 @@ def exception_indicates_not_clickable(exception): def exception_indicates_stale_element(exception): - return "stale" in str(exception) + exception_str = str(exception) + return "stale" in exception_str or "not attached to the DOM" in exception_str __all__ = ( diff --git a/lib/galaxy/selenium/has_playwright_driver.py b/lib/galaxy/selenium/has_playwright_driver.py index 665894d52b6..e03b9faade6 100644 --- a/lib/galaxy/selenium/has_playwright_driver.py +++ b/lib/galaxy/selenium/has_playwright_driver.py @@ -849,16 +849,23 @@ class HasPlaywrightDriver(TimeoutMessageMixin, WaitMethodsMixin, Generic[WaitTyp state to track the current frame. """ if isinstance(frame_reference, str): - # Find frame by name or id - # Try as name attribute first + # Find frame by name or id (matching Selenium's switch_to.frame behavior) frame = self.page.frame(name=frame_reference) - if frame is None: - # Try as frame locator by name attribute selector - frame_locator = self.page.frame_locator(f"[name='{frame_reference}']") - # Store the frame locator for future use - self._current_frame = frame_locator - else: + if frame is not None: self._current_frame = frame + else: + # Selenium also matches by id — locate the iframe element and + # resolve its content Frame so downstream code (axe_eval, etc.) + # gets a proper Frame object rather than a FrameLocator. + selector = f"iframe[name='{frame_reference}'], iframe[id='{frame_reference}']" + iframe_el = self.page.query_selector(selector) + if iframe_el is not None: + self._current_frame = iframe_el.content_frame() + else: + # Last resort: use a FrameLocator (limited API) + self._current_frame = self.page.frame_locator( + f"[name='{frame_reference}'], [id='{frame_reference}']" + ) elif isinstance(frame_reference, int): # Get frame by index frames = self.page.frames diff --git a/lib/galaxy_test/selenium/test_tutorial_mode.py b/lib/galaxy_test/selenium/test_tutorial_mode.py index 6654d63b18c..86ada2c5800 100644 --- a/lib/galaxy_test/selenium/test_tutorial_mode.py +++ b/lib/galaxy_test/selenium/test_tutorial_mode.py @@ -3,14 +3,12 @@ from unittest import SkipTest import pytest from .framework import ( - selenium_only, selenium_test, SeleniumTestCase, ) class TestTutorialMode(SeleniumTestCase): - @selenium_only("Not yet migrated to support Playwright backend") @selenium_test @pytest.mark.gtn_screenshot def test_activate_tutorial_mode(self): @@ -24,6 +22,7 @@ class TestTutorialMode(SeleniumTestCase): self.switch_to_frame("gtn-embed") self.wait_for_selector_visible("#top-navbar") self.screenshot("tutorial_mode_0_2") + self.switch_to_default_content() def _ensure_tutorial_mode_available(self): """Skip a test if the webhook GTN doesn't appear."""