-
Notifications
You must be signed in to change notification settings - Fork 784
Add "Drag And Drop To Frame" keyword for cross-frame drag-and-drop #1953
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| *** Settings *** | ||
| Documentation Tests for the custom Drag And Drop To Frame keyword | ||
| ... in cross-frame drag-and-drop scenarios. | ||
| Resource ../resource.robot | ||
| Test Setup Go To Page "frames/draganddrop.html" | ||
| Force Tags draganddrop | ||
|
|
||
| *** Test Cases *** | ||
| Drag And Drop To Frame Works With Local HTML | ||
| [Documentation] Verifies successful cross-frame drag-and-drop from default content to a target inside an iframe. | ||
| Wait Until Page Contains Element id=source timeout=10s | ||
| Drag And Drop To Frame id=source id=target id=previewFrame | ||
| Select Frame id=previewFrame | ||
| Element Should Contain id=target Dropped Successfully! | ||
| Unselect Frame | ||
|
|
||
| Drag And Drop To Frame Returns To Default Content | ||
| [Documentation] Verifies that the keyword returns to default content after execution. | ||
| Wait Until Page Contains Element id=source timeout=10s | ||
| Drag And Drop To Frame id=source id=target id=previewFrame | ||
| Element Should Be Visible id=previewFrame | ||
|
|
||
| Drag And Drop To Frame Hides Source Element | ||
| [Documentation] Verifies that the source element becomes hidden after a successful drop. | ||
| Wait Until Page Contains Element id=source timeout=10s | ||
| Drag And Drop To Frame id=source id=target id=previewFrame | ||
| Element Should Not Be Visible id=source | ||
|
|
||
| Standard Drag And Drop Fails When Target Is Inside Frame | ||
| [Documentation] Verifies that the standard Drag And Drop keyword cannot complete this cross-frame scenario. | ||
| Wait Until Page Contains Element id=source timeout=10s | ||
| Run Keyword And Expect Error * Drag And Drop id=source id=target | ||
| Select Frame id=previewFrame | ||
| Element Should Not Contain id=target Dropped Successfully! | ||
| Unselect Frame | ||
|
|
||
| Drag And Drop To Frame Fails With Invalid Frame | ||
| [Documentation] Verifies that the keyword fails when the frame locator is invalid. | ||
| Wait Until Page Contains Element id=source timeout=10s | ||
| Run Keyword And Expect Error * Drag And Drop To Frame | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would not wildcard the error that is expected. This can cause the test to "pass" when any error occurs. |
||
| ... id=source id=target id=missingFrame | ||
|
|
||
| Drag And Drop To Frame Fails With Invalid Target | ||
| [Documentation] Verifies that the keyword fails when the target element is not found inside the iframe. | ||
| Wait Until Page Contains Element id=source timeout=10s | ||
| Run Keyword And Expect Error * Drag And Drop To Frame | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would not wildcard the error that is expected. This can cause the test to "pass" when any error occurs. |
||
| ... id=source id=missingTarget id=previewFrame | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test fixture does not seem to work as intended. The test would pass even if move_to_element(target_element) did nothing. The fixture needs to validate that the drop actually lands on the target inside the frame, e.g. using real HTML5 DnD events or at least checking the mouseup coordinates hit the target's bounding box. As written, these tests give false confidence
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @yuriverweij, thanks for the detailed feedback. I agree with the suggestions and I’m working on the updates. I’ll push the changes shortly. |
| 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> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1293,4 +1293,41 @@ def get_css_property_value( | |
| | ${color}= | `Get CSS Property Value` | css:button.submit | background-color | | ||
| | ${size}= | `Get CSS Property Value` | id:username | font-size | | ||
| """ | ||
| return self.find_element(locator).value_of_css_property(css_property) | ||
| return self.find_element(locator).value_of_css_property(css_property) | ||
|
|
||
| @keyword('Drag And Drop To Frame') | ||
| def drag_and_drop_to_frame( | ||
| self, locator: Locator, target: Locator, frame: Locator, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In order to make the keyword more generic it might be worth changing some parts:
|
||
| ) -> None: | ||
| """ | ||
| Drags the element identified by ``locator`` from default content and drops it onto | ||
| the ``target`` element inside the specified iframe. | ||
|
|
||
| The ``locator`` argument is the locator of the dragged element in default content, | ||
| the ``target`` is the locator of the drop 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#draggable | css:div.drop-target | id:my-iframe | | ||
|
|
||
| Note: This assumes the source is in the default content and the target is inside | ||
| the iframe. | ||
| """ | ||
| source_element = self.find_element(locator) | ||
| action = ActionChains(self.driver, duration=self.ctx.action_chain_delay) | ||
| action.click_and_hold(source_element).perform() | ||
|
|
||
| try: | ||
| 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() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the |
||
| finally: | ||
| self.driver.switch_to.default_content() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not wildcard the error that is expected. This can cause the test to "pass" when any error occurs.