Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Selenium dragAndDrop fails with setCanDrag and setUseNativeDrag

    I am using setCanDrag and setUseNativeDrag (to true) to get the effect of the cursor changing from a crossed-out circle to a plus sign while a canvas is being dragged.

    Here is my example code:
    Code:
    [B]public[/B] [B]class[/B] TestLayout [B]extends[/B] VStack {
        [B]public[/B] TestLayout() {
            setBorder("3px solid black");
            setCanAcceptDrop([B]true[/B]);
            setHeight(1);
            setMembersMargin(10);
            setPadding(10);
            setWidth(1);
     
            [B]for[/B] (String color : [B]new[/B] String[] { "red", "blue", "green" }) {
                addMember([B]new[/B] MovableLayout(color));
            }
        }
     
        [B]private[/B] [B]static[/B] [B]final[/B] [B]class[/B] MovableLayout [B]extends[/B] Canvas {
            [B]public[/B] MovableLayout(String color) {
                setID(color);
     
                setBackgroundColor(color);
                setCanDragReposition([B]true[/B]);
                setCanDrop([B]true[/B]);
                setDragAppearance(DragAppearance.[B][I]TARGET[/I][/B]);
                setHeight(100);
                setWidth(100);
     
                /**
                 * The two lines below cause the [U]Selenium[/U] dragAndDrop method to
                 * fail. If either of these lines are removed, the [U]Selenium[/U]
                 * dragAndDrop method will succeed.
                 */
                setCanDrag([B]true[/B]);
                setUseNativeDrag([B]true[/B]);
            }
        }
    }
    This code successfully produces an application where the canvases can be dragged and dropped resulting in their change in position in the VStack.

    However, when I use automated Selenium tests to test the drag and drop functionality, Selenium cannot perform the drag and drop.

    Here is the Selenium test code that will perform the drag and drop test.
    Code:
    [B]import[/B] org.openqa.selenium.By;
    [B]import[/B] org.openqa.selenium.WebElement;
    [B]import[/B] org.openqa.selenium.interactions.Actions;
    [B]import[/B] org.openqa.selenium.support.ui.ExpectedConditions;
    [B]import[/B] org.openqa.selenium.support.ui.WebDriverWait;
    [B]import[/B] org.testng.Assert;
    [B]import[/B] org.testng.annotations.Test;
     
    [B]import[/B] com.isomorphic.webdriver.ByScLocator;
    [B]import[/B] com.isomorphic.webdriver.SmartClientFirefoxDriver;
     
    [B]public[/B] [B]class[/B] DragAndDropTest {
     
        @Test()
        [B]public[/B] [B]void[/B] testDragAndDrop() {
            SmartClientFirefoxDriver driver = [B]new[/B] SmartClientFirefoxDriver();
            driver.setBaseUrl("");
            driver.get("http://localhost:8080/Classifier.html");
     
            ([B]new[/B] WebDriverWait(driver, 10)).until(ExpectedConditions.[I]elementToBeClickable[/I](getLocatorByColor("red")));
     
            System.[B][I]out[/I][/B].println("layout is ready");
     
            Assert.[I]assertEquals[/I](getHorizontalPostion(driver, "red"), 13);
            Assert.[I]assertEquals[/I](getHorizontalPostion(driver, "blue"), 123);
            Assert.[I]assertEquals[/I](getHorizontalPostion(driver, "green"), 233);
     
            System.[B][I]out[/I][/B].println("initilaize color ordering is correct");
     
            WebElement redWebElement = getColor(driver, "red");
            WebElement greenWebElement = getColor(driver, "green");
     
            [B]new[/B] Actions(driver).dragAndDrop(redWebElement, greenWebElement)
                               .build()
                               .perform();
     
            Assert.[I]assertEquals[/I](getHorizontalPostion(driver, "red"), 123);
            Assert.[I]assertEquals[/I](getHorizontalPostion(driver, "blue"), 13);
            Assert.[I]assertEquals[/I](getHorizontalPostion(driver, "green"), 233);
     
            System.[B][I]out[/I][/B].println("drag and drop color ordering is correct");
     
        }
     
        [B]private[/B] [B]int[/B] getHorizontalPostion(SmartClientFirefoxDriver driver, String color) {
            WebElement element = getColor(driver, color);
            [B]int[/B] getVerticalPosition = element.getLocation()
                                             .getY();
            [B]return[/B] getVerticalPosition;
        }
     
        [B]private[/B] WebElement getColor(SmartClientFirefoxDriver driver, String color) {
            By redLocator = getLocatorByColor(color);
            [B]return[/B] driver.findElement(redLocator);
        }
     
        [B]private[/B] By getLocatorByColor(String color) {
            [B]return[/B] ByScLocator.[I]scLocator[/I]("//Canvas[ID=\"" + color + "\"]/");
        }
     
    }
    If I remove either or both of the two lines indicated in the first code example, then Selenium can successfully perform the drag and drop and the test passes.

    Removing either of the two lines of code also removes the desired cursor appearance while the canvas is being dragged.

    #2
    We see the problem and are looking into why Selenium playback is not working with native browser drag. This may prove to be a limitation of Selenium or we may be able to enable support for it with some configuration changes. We'll let you know what we find.

    However it sounds from your description like you may be using a sledgehammer to crack a nut here. The 'useNativeDrag' setting turns on the new native HTML5 support for dragging in the browser, which in turn enables SmartGWT applications to support cross frame / cross window data transfers via drag - not possible any other way.

    It sounds like you simply want to change the cursor / drag appearance which should be doable without using this feature.
    Have you looked at the setCursor() method on canvas? You can also customize the dragAppearance (an attribute on Canavs) and dragTracker (see setDragTracker on EventHandler) to further customize the drag appearance.

    If you can't figure out the correct combination of settings to get the appearance you're after, please let us know and we'll point you in the right direction.

    Regards
    Isomorphic Software

    Comment


      #3
      Oh - one small follow up. In your code, the 2nd block of Assert.AssertEquals() must be:
      Code:
                 Assert.assertEquals(getHorizontalPostion(driver, "red"), 233);
                Assert.assertEquals(getHorizontalPostion(driver, "blue"), 13);
                Assert.assertEquals(getHorizontalPostion(driver, "green"), 123);
      otherwise, the test will fail.

      This is unrelated to the native drag/drop stuff - probably just a copy/paste error, but is something we thought we'd mention.

      Comment


        #4
        Originally posted by Isomorphic View Post
        It sounds like you simply want to change the cursor / drag appearance which should be doable without using this feature.
        Have you looked at the setCursor() method on canvas? You can also customize the dragAppearance (an attribute on Canavs) and dragTracker (see setDragTracker on EventHandler) to further customize the drag appearance.
        I知 interested in using the cursor style I saw in a SmartGWT Showcase example. In this example the cursor changed automatically from NOT_ALLOWED to CROSSHAIR depending on whether drop was not allowed or allowed. useNativeDrag does this automatically.

        I知 assuming that to achieve this without useNativeDrag I would have to set up event handlers to detect the location of the drag and depending on the location I would need to set the cursor . What combination of even handlers, setCursor, and setDragTacker do I need to achieve this?

        I was able to find a couple of code examples in the SmartGWT Showcase or setDragTracker, but these examples don稚 tell me how to change the cursor between NOT_ALLOWED to CROSSHAIR. Additionally, there are five versions of setDragTracker. How do I know which one to use to achieve the desired effect?

        Alternatively, is there a way to get the automatic cursor change as described without using useNativeDrag?

        Comment


          #5
          I知 interested in using the cursor style I saw in a SmartGWT Showcase example. In this example the cursor changed automatically from NOT_ALLOWED to CROSSHAIR depending on whether drop was not allowed or allowed. useNativeDrag does this automatically.

          I知 assuming that to achieve this without useNativeDrag I would have to set up event handlers to detect the location of the drag and depending on the location I would need to set the cursor . What combination of even handlers, setCursor, and setDragTacker do I need to achieve this?
          You can use one of the drop events - typically addDropMoveHandler() - to react to the user dragging a droppable item over the widget, and call setCursor(Cursor.NOT_ALLOWED) if the drop will not be supported (for example if willAcceptDrop() returns false). You'll also want to call setCursor(Cursor.CROSSHAIR) to clear this if drop *is* allowed.

          ...Additionally, there are five versions of setDragTracker. How do I know which one to use to achieve the desired effect?
          There are 2 versions of 'EventHandler.setDragTracker()' - one of them takes just a chunk of HTML to display, the other also takes arguments to control sizing and offset (from the mouse) for the tracker HTML.
          You can use either one of these (depending on how much control over the appearance you need) during normal SmartGWT drag [useNativeDrag false]. Has no effect during native HTML5 drag.
          There are 3 versions of 'EventHandler.setDragTrackerImage()' - this method allows you to specify an image to display during native drag [useNativeDrag true]. The overloaded signature gives you control over the offset from the mouse cursor. Has no effect during non-native SmartGWT drag.

          Regards
          Isomorphic Software

          Comment


            #6
            Originally posted by Isomorphic View Post
            You can use one of the drop events - typically addDropMoveHandler() - to react to the user dragging a droppable item over the widget, and call setCursor(Cursor.NOT_ALLOWED) if the drop will not be supported (for example if willAcceptDrop() returns false). You'll also want to call setCursor(Cursor.CROSSHAIR) to clear this if drop *is* allowed.
            When I use setUseNativeDrag(true) the canvas being dragged will have the NOT_ALLOWED cursor for anywhere it can’t be dropped. This doesn’t require handlers on any destination cavases.

            If I understand your suggestion I would have to put addDropMoveHandler on every canvas that it can’t be dropped to, to change the cursor to NOT_ALLOWED. Perhaps, I’m not understanding this, but this seems extremely onerous.

            Is there a way that the canvas being dragged can detect through a handler on the canvas being dragged where it is over a droppable canvas or not and therefore be able to change its own cursor?

            I tried changing the cursor in DragRepositionMoveHandler. I知 doing this on IconButton. Even though DragRepositionMoveHandler is called for drag/mouse movements setting the cursor has no effect on IconButton.

            Here is my test code:
            Code:
            [B]public[/B] [B]class[/B] DragButtonCursorChange [B]extends[/B] VStack {
             
                [B]public[/B] DragButtonCursorChange() {
                    setMembersMargin(20);
             
                    RibbonBar ribbonBar = [B]new[/B] RibbonBar();
                    ribbonBar.setWidth(1);
                    addMember(ribbonBar);
             
                    RibbonGroup ribbonGroup = [B]new[/B] RibbonGroup();
                    ribbonGroup.setTitle("Add");
                    [B]final[/B] IconButton button = [B]new[/B] IconButton();
                    button.setShowButtonTitle([B]false[/B]);
                    button.setIcon("ConversationGuide/Edit/TopicGroup.png");
                    button.setCanDragReposition([B]true[/B]);
                    button.setCanDrop([B]true[/B]);
                    button.setCanDrag([B]true[/B]);
             
                    /**
                     * This cursor is never displayed.
                     */
                    button.setCursor(Cursor.[B][I]NOT_ALLOWED[/I][/B]);
             
                    button.addDragRepositionMoveHandler([B]new[/B] DragRepositionMoveHandler() {
             
                        @Override
                        [B]public[/B] [B]void[/B] onDragRepositionMove(DragRepositionMoveEvent event) {
                            /**
                             * Even though this event handler is called the cursor never
                             * changes.
                             */
                            button.setCursor(Cursor.[B][I]CROSSHAIR[/I][/B]);
                        }
                    });
             
                    ribbonGroup.addControl(button);
             
                    ribbonBar.addGroup(ribbonGroup);
             
                }
            }
            Last edited by dbscott525; 12 Jan 2016, 14:32.

            Comment


              #7
              There is not currently an out-of-the-box way to provide a no-drop indicator in a non-native drag in SmartGWT for *all* undroppable widgets other than doing what you suggest (setting the cursor dynamically on all potential target widgets).
              That obviously doesn't make sense. The API as it stands was designed for the use case where a dev wants a no-drop cursor to indicate that drop is currently disallowed for *some specific widget due to current application state*, or similar. We do something like this when a user is doing drag/drop interactions within TreeGrids to indicate which folders are valid drop targets for the current drag data, and which are not.

              Having said that, support for a "general" no-drop indicator for all widgets that won't accept drop during a drag interaction does make sense to us and we are currently looking at whether we can provide a simple API to achieve this.

              Your original approach of using native HTML5 drag and relying on the browser's native behavior of showing a no-drop indicator in your app is an option but the Selenium playback issue is likely to be a bigger barrier to overcome since we're dealing with a third party tool's handling of native browser events. If you do want to take this approach, or have other reasons for using native drag (such as a cross frame drag requirement), let us know and we will prioritize further investigation into Selenium's options in this area -- and in the mean time you could possibly do something like modify your selenium playback script to execute custom JS to mimic this particular step of the user interaction.

              Anyway - we'll follow up when we have more information for you on the simplified API for showing a general "no drop" indicator during standard SmartGWT dragging.

              Regards
              Isomorphic Software

              Comment


                #8
                We've added a new API to EventHandler - a boolean flag EventHandler.showNoDropIndicator.
                This is false by default - if set to true, when the user drags a droppable widget over anything that isn't a valid drop target (determined by looking at canAcceptDrop and running the 'willAcceptDrop()' method), the "not-allowed" cursor will show up automatically.

                You can pick this up in the next nightly build, dated Jan 14 or above (5.1p and 6.0d branches)

                This seems like it should give you the interface you need. Please let us know if it doesn't work as expected.

                Regards
                Isomorphic Software

                Comment

                Working...
                X