Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions atest/acceptance/keywords/draganddropframe.robot
Original file line number Diff line number Diff line change
@@ -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
95 changes: 95 additions & 0 deletions atest/resources/html/frames/draganddrop.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom Mouse-Based Cross-Frame Drag Test (Fixed with Overlay)</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; background: #f0f0f0; position: relative; }
#source {
width: 150px; height: 150px; background: lightblue;
text-align: center; line-height: 150px; font-size: 20px;
cursor: pointer; user-select: none; position: absolute; top: 50px; left: 50px;
z-index: 10;
}
#iframe-container { position: relative; display: inline-block; margin-top: 250px; }
iframe { width: 800px; height: 500px; border: 3px solid #333; background: white; }
#overlay {
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
background: transparent; z-index: 20; display: none; cursor: default;
}
#target {
width: 400px; height: 300px; background: lightgreen;
margin: 100px auto; text-align: center; line-height: 300px; font-size: 28px;
border: 4px dashed #000;
}
</style>
</head>
<body>

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

<!-- Source outside iframe -->
<div id="source">Drag Me!<br>(outside iframe)</div>

<!-- Iframe wrapper with overlay -->
<div id="iframe-container">
<iframe id="previewFrame" srcdoc="
<html>
<head>
<style>
body { margin: 0; background: #fff; }
#target { width: 400px; height: 300px; background: lightgreen; margin: 100px auto; text-align: center; line-height: 300px; font-size: 28px; border: 4px dashed #000; }
</style>
<script>
window.addEventListener('message', function(e) {
if (e.data.action === 'drop') {
const target = document.getElementById('target');
const droppedBox = document.createElement('div');
droppedBox.innerHTML = 'Dropped Successfully!<br><br>Box from outside!';
droppedBox.style.cssText = 'width: 150px; height: 150px; background: lightblue; margin: 20px auto; line-height: 150px; font-size: 20px; text-align: center;';
target.innerHTML = '';
target.appendChild(droppedBox);
}
});
</script>
</head>
<body>
<div id='target'>Drop Here<br>(inside iframe)</div>
</body>
</html>
"></iframe>
<div id="overlay"></div>
</div>

<script>
let dragging = false;
const source = document.getElementById('source');
const overlay = document.getElementById('overlay');
const iframe = document.getElementById('previewFrame');

// Start drag: show overlay to capture events over iframe
source.addEventListener('mousedown', (e) => {
dragging = true;
overlay.style.display = 'block';
e.preventDefault();
});

// End drag: hide overlay, hide source, send drop to iframe
const endDrag = () => {
if (dragging) {
dragging = false;
overlay.style.display = 'none';
source.style.display = 'none';

iframe.contentWindow.postMessage({
action: 'drop'
}, '*');
}
};

// Mouseup anywhere in parent (including overlay)
document.addEventListener('mouseup', endDrag);
</script>

</body>
</html>
32 changes: 32 additions & 0 deletions src/SeleniumLibrary/keywords/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
2 changes: 1 addition & 1 deletion utest/test/api/test_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down