From d283eb4d63291d61610046877c568b55b70ac6de Mon Sep 17 00:00:00 2001 From: vamsi Date: Sat, 20 Dec 2025 16:12:44 -0500 Subject: [PATCH] New Keyword: add Drag And Drop To Frame keyword for cross-iframe scenarios --- .../keywords/draganddropframe.robot | 29 ++++++ atest/resources/html/frames/draganddrop.html | 95 +++++++++++++++++++ src/SeleniumLibrary/keywords/element.py | 32 +++++++ utest/test/api/test_plugins.py | 2 +- 4 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 atest/acceptance/keywords/draganddropframe.robot create mode 100644 atest/resources/html/frames/draganddrop.html diff --git a/atest/acceptance/keywords/draganddropframe.robot b/atest/acceptance/keywords/draganddropframe.robot new file mode 100644 index 000000000..6a060deec --- /dev/null +++ b/atest/acceptance/keywords/draganddropframe.robot @@ -0,0 +1,29 @@ +*** Settings *** +Documentation New Keyword for Drag and Drop Frame +Test Setup Go To Page "frames/draganddrop.html" +Test Teardown Close Browser +Resource ../resource.robot +Force Tags dragandDrop + +*** Test Cases *** +Positive Test_Drag And Drop To Frame Local HTML using new Keyword Drag And Drop To Frame + [Documentation] Tests new Keyword created. + Maximize Browser Window + Wait Until Element Is Visible id=source 10s + ${Status}= Run Keyword And Return Status Drag And Drop To Frame id=source id=target id=previewFrame + Capture Page Screenshot + log Returned ${Status}: due to new Keyword switched to iframe and dragged and dropped the element from soruce to target succesfully. + Select Frame id=previewFrame + Element Should Contain id=target Dropped Successfully! + Unselect Frame + Capture Page Screenshot dropped.png + [Teardown] + +Negative Test_Drag And Drop To Frame Local HTML using existing keyword Drag And Drop + [Documentation] Tests existing Keyword keyword. + Maximize Browser Window + Wait Until Element Is Visible id=source 10s + ${Error Message}= Run Keyword And Expect Error * Drag And Drop id=source id=target + Capture Page Screenshot + log Returned ${Error Message} due to target element is inside iframe + [Teardown] Close Browser diff --git a/atest/resources/html/frames/draganddrop.html b/atest/resources/html/frames/draganddrop.html new file mode 100644 index 000000000..b3aeb0520 --- /dev/null +++ b/atest/resources/html/frames/draganddrop.html @@ -0,0 +1,95 @@ + + + + + Custom Mouse-Based Cross-Frame Drag Test (Fixed with Overlay) + + + + +

Click & hold blue box (outside), drag ANYWHERE (including into green area inside iframe), release → drops!

+ + +
Drag Me!
(outside iframe)
+ + +
+ +
+
+ + + + + \ No newline at end of file diff --git a/src/SeleniumLibrary/keywords/element.py b/src/SeleniumLibrary/keywords/element.py index 831ebfaf2..2547b0106 100644 --- a/src/SeleniumLibrary/keywords/element.py +++ b/src/SeleniumLibrary/keywords/element.py @@ -1275,3 +1275,35 @@ def _convert_special_keys(self, keys): def _selenium_keys_has_attr(self, key): return hasattr(Keys, key) + + @keyword('Drag And Drop To Frame') + def drag_and_drop_to_frame( + self, locator: Union[WebElement, str], target: Union[WebElement, str], frame: Union[WebElement, str], + ): + """ + Drags the element identified by ``locator`` from default content and drops it onto the ``target`` element + inside a specified iframe. + + The ``locator`` argument is the locator of the dragged element (in default content) + and the ``target`` is the locator of the target (inside the iframe) + and the ``frame`` is the locator of the iframe containing the target + + See the `Locating elements` section for details about the locator syntax. + + This keyword is designed for cross-frame drag-and-drop scenarios where the standard `Drag And Drop` keyword fails + because it cannot switch contexts mid-action. + + Example: + | Drag And Drop To Frame | css:div#element | css:div.target | id:my-iframe | + + Note: This assumes the source is in the default content and the target is inside the iframe. + """ + element = self.find_element(locator) + action = ActionChains(self.driver, duration=self.ctx.action_chain_delay) + action.click_and_hold(element).perform() + frame_element = self.find_element(frame) + self.driver.switch_to.frame(frame_element) + target_element = self.find_element(target) + action = ActionChains(self.driver, duration=self.ctx.action_chain_delay) + action.move_to_element(target_element).release().perform() + self.driver.switch_to.default_content() diff --git a/utest/test/api/test_plugins.py b/utest/test/api/test_plugins.py index c8241d8ba..16f5bd154 100644 --- a/utest/test/api/test_plugins.py +++ b/utest/test/api/test_plugins.py @@ -22,7 +22,7 @@ def setUpClass(cls): def test_no_libraries(self): for item in [None, "None", ""]: sl = SeleniumLibrary(plugins=item) - self.assertEqual(len(sl.get_keyword_names()), 182) + self.assertEqual(len(sl.get_keyword_names()), 183) def test_parse_library(self): plugin = "path.to.MyLibrary"