Skip to content

Commit bf869a0

Browse files
committed
Add Drag And Drop To Frame keyword Updated implementation and tests
1 parent 57be117 commit bf869a0

File tree

4 files changed

+182
-2
lines changed

4 files changed

+182
-2
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
*** Settings ***
2+
Documentation Tests for the custom Drag And Drop To Frame keyword
3+
... in cross-frame drag-and-drop scenarios.
4+
Test Setup Open Test Browser
5+
Test Teardown Close Browser
6+
Resource ../resource.robot
7+
Force Tags draganddrop
8+
9+
*** Keywords ***
10+
Open Test Browser
11+
Open Browser ${ROOT}/frames/draganddrop.html ${BROWSER}
12+
Maximize Browser Window
13+
Wait Until Page Contains Element id=source timeout=10s
14+
15+
*** Test Cases ***
16+
Drag And Drop To Frame Works With Local HTML
17+
[Documentation] Verifies successful cross-frame drag-and-drop from default content to a target inside an iframe.
18+
Drag And Drop To Frame id=source id=target id=previewFrame
19+
Select Frame id=previewFrame
20+
Element Should Contain id=target Dropped Successfully!
21+
Unselect Frame
22+
23+
Drag And Drop To Frame Returns To Default Content
24+
[Documentation] Verifies that the keyword returns to default content after execution.
25+
Drag And Drop To Frame id=source id=target id=previewFrame
26+
Element Should Be Visible id=previewFrame
27+
28+
Drag And Drop To Frame Hides Source Element
29+
[Documentation] Verifies that the source element becomes hidden after a successful drop.
30+
Drag And Drop To Frame id=source id=target id=previewFrame
31+
Element Should Not Be Visible id=source
32+
33+
Standard Drag And Drop Fails When Target Is Inside Frame
34+
[Documentation] Verifies that the standard Drag And Drop keyword cannot complete this cross-frame scenario.
35+
Run Keyword And Expect Error * Drag And Drop id=source id=target
36+
Select Frame id=previewFrame
37+
Element Should Not Contain id=target Dropped Successfully!
38+
Unselect Frame
39+
40+
Drag And Drop To Frame Fails With Invalid Frame
41+
[Documentation] Verifies that the keyword fails when the frame locator is invalid.
42+
Run Keyword And Expect Error * Drag And Drop To Frame
43+
... id=source id=target id=missingFrame
44+
45+
Drag And Drop To Frame Fails With Invalid Target
46+
[Documentation] Verifies that the keyword fails when the target element is not found inside the iframe.
47+
Run Keyword And Expect Error * Drag And Drop To Frame
48+
... id=source id=missingTarget id=previewFrame
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: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1293,4 +1293,41 @@ def get_css_property_value(
12931293
| ${color}= | `Get CSS Property Value` | css:button.submit | background-color |
12941294
| ${size}= | `Get CSS Property Value` | id:username | font-size |
12951295
"""
1296-
return self.find_element(locator).value_of_css_property(css_property)
1296+
return self.find_element(locator).value_of_css_property(css_property)
1297+
1298+
@keyword('Drag And Drop To Frame')
1299+
def drag_and_drop_to_frame(
1300+
self, locator: Locator, target: Locator, frame: Locator,
1301+
) -> None:
1302+
"""
1303+
Drags the element identified by ``locator`` from default content and drops it onto
1304+
the ``target`` element inside the specified iframe.
1305+
1306+
The ``locator`` argument is the locator of the dragged element in default content,
1307+
the ``target`` is the locator of the drop target inside the iframe, and the
1308+
``frame`` is the locator of the iframe containing the target.
1309+
1310+
See the `Locating elements` section for details about the locator syntax.
1311+
1312+
This keyword is designed for cross-frame drag-and-drop scenarios where the standard
1313+
`Drag And Drop` keyword fails because it cannot switch contexts mid-action.
1314+
1315+
Example:
1316+
| Drag And Drop To Frame | css:div#draggable | css:div.drop-target | id:my-iframe |
1317+
1318+
Note: This assumes the source is in the default content and the target is inside
1319+
the iframe.
1320+
"""
1321+
source_element = self.find_element(locator)
1322+
action = ActionChains(self.driver, duration=self.ctx.action_chain_delay)
1323+
action.click_and_hold(source_element).perform()
1324+
1325+
try:
1326+
frame_element = self.find_element(frame)
1327+
self.driver.switch_to.frame(frame_element)
1328+
target_element = self.find_element(target)
1329+
1330+
action = ActionChains(self.driver, duration=self.ctx.action_chain_delay)
1331+
action.move_to_element(target_element).release().perform()
1332+
finally:
1333+
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()), 183)
25+
self.assertEqual(len(sl.get_keyword_names()), 184)
2626

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

0 commit comments

Comments
 (0)