Skip to content

Commit d283eb4

Browse files
committed
New Keyword: add Drag And Drop To Frame keyword for cross-iframe scenarios
1 parent c5c21ea commit d283eb4

File tree

4 files changed

+157
-1
lines changed

4 files changed

+157
-1
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
*** Settings ***
2+
Documentation New Keyword for Drag and Drop Frame
3+
Test Setup Go To Page "frames/draganddrop.html"
4+
Test Teardown Close Browser
5+
Resource ../resource.robot
6+
Force Tags dragandDrop
7+
8+
*** Test Cases ***
9+
Positive Test_Drag And Drop To Frame Local HTML using new Keyword Drag And Drop To Frame
10+
[Documentation] Tests new Keyword created.
11+
Maximize Browser Window
12+
Wait Until Element Is Visible id=source 10s
13+
${Status}= Run Keyword And Return Status Drag And Drop To Frame id=source id=target id=previewFrame
14+
Capture Page Screenshot
15+
log Returned ${Status}: due to new Keyword switched to iframe and dragged and dropped the element from soruce to target succesfully.
16+
Select Frame id=previewFrame
17+
Element Should Contain id=target Dropped Successfully!
18+
Unselect Frame
19+
Capture Page Screenshot dropped.png
20+
[Teardown]
21+
22+
Negative Test_Drag And Drop To Frame Local HTML using existing keyword Drag And Drop
23+
[Documentation] Tests existing Keyword keyword.
24+
Maximize Browser Window
25+
Wait Until Element Is Visible id=source 10s
26+
${Error Message}= Run Keyword And Expect Error * Drag And Drop id=source id=target
27+
Capture Page Screenshot
28+
log Returned ${Error Message} due to target element is inside iframe
29+
[Teardown] Close Browser
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Custom Mouse-Based Cross-Frame Drag Test (Fixed with Overlay)</title>
6+
<style>
7+
body { font-family: Arial, sans-serif; margin: 40px; background: #f0f0f0; position: relative; }
8+
#source {
9+
width: 150px; height: 150px; background: lightblue;
10+
text-align: center; line-height: 150px; font-size: 20px;
11+
cursor: pointer; user-select: none; position: absolute; top: 50px; left: 50px;
12+
z-index: 10;
13+
}
14+
#iframe-container { position: relative; display: inline-block; margin-top: 250px; }
15+
iframe { width: 800px; height: 500px; border: 3px solid #333; background: white; }
16+
#overlay {
17+
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
18+
background: transparent; z-index: 20; display: none; cursor: default;
19+
}
20+
#target {
21+
width: 400px; height: 300px; background: lightgreen;
22+
margin: 100px auto; text-align: center; line-height: 300px; font-size: 28px;
23+
border: 4px dashed #000;
24+
}
25+
</style>
26+
</head>
27+
<body>
28+
29+
<h2>Click & hold blue box (outside), drag ANYWHERE (including into green area inside iframe), release → drops!</h2>
30+
31+
<!-- Source outside iframe -->
32+
<div id="source">Drag Me!<br>(outside iframe)</div>
33+
34+
<!-- Iframe wrapper with overlay -->
35+
<div id="iframe-container">
36+
<iframe id="previewFrame" srcdoc="
37+
<html>
38+
<head>
39+
<style>
40+
body { margin: 0; background: #fff; }
41+
#target { width: 400px; height: 300px; background: lightgreen; margin: 100px auto; text-align: center; line-height: 300px; font-size: 28px; border: 4px dashed #000; }
42+
</style>
43+
<script>
44+
window.addEventListener('message', function(e) {
45+
if (e.data.action === 'drop') {
46+
const target = document.getElementById('target');
47+
const droppedBox = document.createElement('div');
48+
droppedBox.innerHTML = 'Dropped Successfully!<br><br>Box from outside!';
49+
droppedBox.style.cssText = 'width: 150px; height: 150px; background: lightblue; margin: 20px auto; line-height: 150px; font-size: 20px; text-align: center;';
50+
target.innerHTML = '';
51+
target.appendChild(droppedBox);
52+
}
53+
});
54+
</script>
55+
</head>
56+
<body>
57+
<div id='target'>Drop Here<br>(inside iframe)</div>
58+
</body>
59+
</html>
60+
"></iframe>
61+
<div id="overlay"></div>
62+
</div>
63+
64+
<script>
65+
let dragging = false;
66+
const source = document.getElementById('source');
67+
const overlay = document.getElementById('overlay');
68+
const iframe = document.getElementById('previewFrame');
69+
70+
// Start drag: show overlay to capture events over iframe
71+
source.addEventListener('mousedown', (e) => {
72+
dragging = true;
73+
overlay.style.display = 'block';
74+
e.preventDefault();
75+
});
76+
77+
// End drag: hide overlay, hide source, send drop to iframe
78+
const endDrag = () => {
79+
if (dragging) {
80+
dragging = false;
81+
overlay.style.display = 'none';
82+
source.style.display = 'none';
83+
84+
iframe.contentWindow.postMessage({
85+
action: 'drop'
86+
}, '*');
87+
}
88+
};
89+
90+
// Mouseup anywhere in parent (including overlay)
91+
document.addEventListener('mouseup', endDrag);
92+
</script>
93+
94+
</body>
95+
</html>

src/SeleniumLibrary/keywords/element.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,3 +1275,35 @@ def _convert_special_keys(self, keys):
12751275

12761276
def _selenium_keys_has_attr(self, key):
12771277
return hasattr(Keys, key)
1278+
1279+
@keyword('Drag And Drop To Frame')
1280+
def drag_and_drop_to_frame(
1281+
self, locator: Union[WebElement, str], target: Union[WebElement, str], frame: Union[WebElement, str],
1282+
):
1283+
"""
1284+
Drags the element identified by ``locator`` from default content and drops it onto the ``target`` element
1285+
inside a specified iframe.
1286+
1287+
The ``locator`` argument is the locator of the dragged element (in default content)
1288+
and the ``target`` is the locator of the target (inside the iframe)
1289+
and the ``frame`` is the locator of the iframe containing the target
1290+
1291+
See the `Locating elements` section for details about the locator syntax.
1292+
1293+
This keyword is designed for cross-frame drag-and-drop scenarios where the standard `Drag And Drop` keyword fails
1294+
because it cannot switch contexts mid-action.
1295+
1296+
Example:
1297+
| Drag And Drop To Frame | css:div#element | css:div.target | id:my-iframe |
1298+
1299+
Note: This assumes the source is in the default content and the target is inside the iframe.
1300+
"""
1301+
element = self.find_element(locator)
1302+
action = ActionChains(self.driver, duration=self.ctx.action_chain_delay)
1303+
action.click_and_hold(element).perform()
1304+
frame_element = self.find_element(frame)
1305+
self.driver.switch_to.frame(frame_element)
1306+
target_element = self.find_element(target)
1307+
action = ActionChains(self.driver, duration=self.ctx.action_chain_delay)
1308+
action.move_to_element(target_element).release().perform()
1309+
self.driver.switch_to.default_content()

utest/test/api/test_plugins.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def setUpClass(cls):
2222
def test_no_libraries(self):
2323
for item in [None, "None", ""]:
2424
sl = SeleniumLibrary(plugins=item)
25-
self.assertEqual(len(sl.get_keyword_names()), 182)
25+
self.assertEqual(len(sl.get_keyword_names()), 183)
2626

2727
def test_parse_library(self):
2828
plugin = "path.to.MyLibrary"

0 commit comments

Comments
 (0)