diff --git a/atest/acceptance/keywords/draganddropframe.robot b/atest/acceptance/keywords/draganddropframe.robot new file mode 100644 index 000000000..32cc14866 --- /dev/null +++ b/atest/acceptance/keywords/draganddropframe.robot @@ -0,0 +1,77 @@ +*** Settings *** +Documentation Tests for the custom Drag And Drop Across Frames keyword +... in cross-frame drag-and-drop scenarios. +Resource ../resource.robot +Test Setup Go To Page "frames/draganddrop.html" + +*** Test Cases *** +Drag And Drop Across Frames Works From Default Content + [Documentation] Verifies drag-and-drop from default content to a target inside an iframe. + Wait Until Page Contains Element id=defaultSource 10s + Drag And Drop Across Frames id=defaultSource id=target id=targetFrame + Select Frame id=targetFrame + Element Should Contain id=target Dropped Successfully! + Unselect Frame + +Drag And Drop Across Frames Works From Source Frame + [Documentation] Verifies drag-and-drop from a source iframe to a target iframe. + Wait Until Page Contains Element id=sourceFrame 10s + Select Frame id=sourceFrame + Wait Until Page Contains Element id=frameSource 10s + Unselect Frame + Drag And Drop Across Frames id=frameSource id=target id=targetFrame id=sourceFrame + Select Frame id=targetFrame + Element Should Contain id=target Dropped Successfully! + Unselect Frame + +Drag And Drop Across Frames Returns To Default Content + [Documentation] Verifies that the keyword returns to default content after execution. + Wait Until Page Contains Element id=defaultSource 10s + Drag And Drop Across Frames id=defaultSource id=target id=targetFrame + Page Should Not Contain Element id=target + +Drag And Drop Across Frames Hides Default Source Element + [Documentation] Verifies that the default source element becomes hidden after a successful drop. + Wait Until Page Contains Element id=defaultSource 10s + Drag And Drop Across Frames id=defaultSource id=target id=targetFrame + Element Should Not Be Visible id=defaultSource + +Drag And Drop Across Frames Hides Frame Source Element + [Documentation] Verifies that the frame source element becomes hidden after a successful drop. + Wait Until Page Contains Element id=sourceFrame 10s + Drag And Drop Across Frames id=frameSource id=target id=targetFrame id=sourceFrame + Select Frame id=sourceFrame + Element Should Not Be Visible id=frameSource + Unselect Frame + +Drag And Drop Across Frames Fails With Invalid Target Frame + [Documentation] Verifies that the keyword fails when the target frame locator is invalid. + Wait Until Page Contains Element id=defaultSource 10s + Run Keyword And Expect Error + ... Element with locator 'id=missingFrame' not found. + ... Drag And Drop Across Frames + ... id=defaultSource id=target id=missingFrame + +Drag And Drop Across Frames Fails With Invalid Target + [Documentation] Verifies that the keyword fails when the target element is not found inside the target iframe. + Wait Until Page Contains Element id=defaultSource 10s + Run Keyword And Expect Error + ... Element with locator 'id=missingTarget' not found. + ... Drag And Drop Across Frames + ... id=defaultSource id=missingTarget id=targetFrame + +Drag And Drop Across Frames Fails With Invalid Source Frame + [Documentation] Verifies that the keyword fails when the source frame locator is invalid. + Wait Until Page Contains Element id=defaultSource 10s + Run Keyword And Expect Error + ... Element with locator 'id=missingSourceFrame' not found. + ... Drag And Drop Across Frames + ... id=frameSource id=target id=targetFrame id=missingSourceFrame + +Drag And Drop Across Frames Fails With Invalid Source + [Documentation] Verifies that the keyword fails when the source element is not found. + Wait Until Page Contains Element id=defaultSource 10s + Run Keyword And Expect Error + ... Element with locator 'id=missingSource' not found. + ... Drag And Drop Across Frames + ... id=missingSource id=target id=targetFrame \ No newline at end of file diff --git a/atest/resources/html/frames/draganddrop.html b/atest/resources/html/frames/draganddrop.html new file mode 100644 index 000000000..98421dd5f --- /dev/null +++ b/atest/resources/html/frames/draganddrop.html @@ -0,0 +1,260 @@ + + + + + Drag And Drop Across Frames Test Page + + + + +

Drag And Drop Across Frames Test Page

+ +
Default Source
+ +
+ + +
+ +
+ + + + + \ No newline at end of file diff --git a/src/SeleniumLibrary/keywords/element.py b/src/SeleniumLibrary/keywords/element.py index f385b938f..e08187646 100644 --- a/src/SeleniumLibrary/keywords/element.py +++ b/src/SeleniumLibrary/keywords/element.py @@ -1292,3 +1292,59 @@ def get_css_property_value(self, locator: Locator, css_property: str) -> str: | ${size}= | `Get CSS Property Value` | id:username | font-size | """ return self.find_element(locator).value_of_css_property(css_property) + + + @keyword("Drag And Drop Across Frames") + def drag_and_drop_across_frames( + self, + locator: Locator, + target: Locator, + target_frame: Locator, + source_frame: Locator | None = None, + ) -> None: + """ + Drags an element and drops it onto a target element across frame boundaries. + + The ``locator`` argument is the locator of the element to drag. The ``target`` + argument is the locator of the drop target. The ``target_frame`` argument is + the locator of the iframe containing the target. The optional ``source_frame`` + argument is the locator of the iframe containing the source. If + ``source_frame`` is not provided, the source element is located in the default content. + + After this keyword runs, the browser context is always reset to default content. + + See the `Locating elements` section for details about the locator syntax. + + Examples: + | Drag And Drop Across Frames | id:source | id:target | id:target-frame | + | Drag And Drop Across Frames | id:source | id:target | id:target-frame | id:source-frame | + """ + released = False + + try: + if source_frame is not None: + source_frame_element = self.find_element(source_frame) + self.driver.switch_to.frame(source_frame_element) + + source_element = self.find_element(locator) + ActionChains( + self.driver, duration=self.ctx.action_chain_delay + ).click_and_hold(source_element).perform() + + self.driver.switch_to.default_content() + + target_frame_element = self.find_element(target_frame) + self.driver.switch_to.frame(target_frame_element) + + target_element = self.find_element(target) + ActionChains( + self.driver, duration=self.ctx.action_chain_delay + ).move_to_element(target_element).release().perform() + released = True + + finally: + if not released: + ActionChains( + self.driver, duration=self.ctx.action_chain_delay + ).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 317cc6dd5..d7fba9665 100644 --- a/utest/test/api/test_plugins.py +++ b/utest/test/api/test_plugins.py @@ -28,7 +28,7 @@ class Plugin(NamedTuple): def test_no_libraries(self): for item in [None, "None", ""]: sl = SeleniumLibrary(plugins=item) - assert len(sl.get_keyword_names()) == 183 + assert len(sl.get_keyword_names()) == 184 def test_parse_library(self): plugin = "path.to.MyLibrary"