Skip to content
Snippets Groups Projects
Commit 8efd0b5c authored by Michael Hamann's avatar Michael Hamann
Browse files

[Misc] Introduce SortableElement for better drag&drop handling

This moves the drag&drop code from ApplicationsPanelAdministrationPage
into a dedicated SortableElement. This code is then used in
StaticListItemsEditor to handle drag&drop for list items in the hope of
resolving flickers related to this.

(cherry picked from commit 1a294a25)
parent 0322c9da
No related branches found
No related tags found
No related merge requests found
......@@ -22,8 +22,8 @@
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.xwiki.test.ui.po.BaseElement;
import org.xwiki.test.ui.po.SortableElement;
/**
* Represents the static list items editor present on the configuration pane of a static list field.
......@@ -53,6 +53,8 @@ public class StaticListItemsEditor extends BaseElement
*/
private WebElement addButton;
private final SortableElement sortable;
/**
* Creates a new instance.
*
......@@ -62,6 +64,8 @@ public StaticListItemsEditor(WebElement container)
{
this.container = container;
this.sortable = new SortableElement(container);
By xpath = By.xpath(".//*[@class = 'xHint' and . = 'ID']/following-sibling::input[@type = 'text']");
valueInput = getDriver().findElementWithoutWaiting(container, xpath);
......@@ -134,16 +138,7 @@ public void setLabel(String value, String newLabel)
*/
public void moveBefore(String valueToMove, String beforeValue)
{
WebElement itemToMove = getItem(valueToMove);
WebElement itemBefore = getItem(beforeValue);
Actions actions = getDriver().createActions().clickAndHold(itemToMove);
// We move the item from the center, so we need to ensure that we actually move it with an offset of half its
// width to be sure it's before the other item element.
int offsetX = - (itemToMove.getSize().getWidth() / 2);
int offsetY = 2;
getDriver().moveToTopLeftCornerOfTargetWithOffset(itemBefore, offsetX, offsetY, actions).release().perform();
this.sortable.moveBefore(getSelectorForItem(valueToMove), getSelectorForItem(beforeValue));
}
/**
......@@ -152,8 +147,12 @@ public void moveBefore(String valueToMove, String beforeValue)
*/
public WebElement getItem(String valueOrLabel)
{
By xpath = By.xpath("ul/li/*[@title = '" + valueOrLabel + "' or . = '" + valueOrLabel + "']");
return getDriver().findElementWithoutWaiting(container, xpath);
return getDriver().findElementWithoutWaiting(this.container, getSelectorForItem(valueOrLabel));
}
private By getSelectorForItem(String valueOrLabel)
{
return By.xpath("ul/li/*[@title = '" + valueOrLabel + "' or . = '" + valueOrLabel + "']");
}
/**
......
......@@ -23,13 +23,10 @@
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.Point;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.xwiki.administration.test.po.AdministrationPage;
import org.xwiki.test.ui.po.SortableElement;
import org.xwiki.test.ui.po.ViewPage;
import static org.junit.Assert.assertTrue;
......@@ -56,6 +53,8 @@ public class ApplicationsPanelAdministrationPage extends ViewPage
@FindBy(id = "bt-save")
private WebElement saveButton;
private final SortableElement sortableDisplayedPanels = new SortableElement(this.displayedPanels);
public static ApplicationsPanelAdministrationPage gotoPage()
{
AdministrationPage administrationPage = AdministrationPage.gotoPage();
......@@ -116,40 +115,7 @@ public void moveAppBefore(String appName, String appBeforeName)
return;
}
WebElement app = displayedPanels.findElement(By.linkText(appName));
WebElement appBefore = displayedPanels.findElement(By.linkText(appBeforeName));
Point target = appBefore.getLocation();
Point source = app.getLocation();
// The drag & drop of the "sortable" plugin of "jquery-ui" is very sensitive so we need to script the
// moves of the mouse precisely if we don't want to have flickers.
Actions actions = new Actions(getDriver().getWrappedDriver());
// First we hold the app
actions.clickAndHold(app);
// Then we move into the position of the targeted app so jquery-ui can register we want to take its place.
actions.moveByOffset(target.getX() - source.getX(), target.getY() - source.getY());
// Now we do a little move on top left so jquery-ui understand we want to be *before* the other app and
// put a blank place instead of the other app.
actions.moveByOffset(-5, -5);
// Do it
actions.perform();
// Before releasing the click, check that jquery-ui has moved the other app to let the place free.
getDriver().waitUntilCondition(new ExpectedCondition<Object>()
{
@Override
public Object apply(WebDriver webDriver)
{
Point newTarget = appBefore.getLocation();
Point newSource = app.getLocation();
return newTarget.getX() > newSource.getX() + 5 || newTarget.getY() > newSource.getY() + 5;
}
});
// Now we can release the selection
actions = new Actions(getDriver().getWrappedDriver());
actions.release();
actions.perform();
this.sortableDisplayedPanels.moveBefore(By.linkText(appName), By.linkText(appBeforeName));
}
public void revert()
......
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.test.ui.po;
import org.openqa.selenium.By;
import org.openqa.selenium.Point;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedCondition;
/**
* Represents a sortable element that is using, e.g., the "sortable" plugin of "jquery-ui".
*
* @version $Id$
* @since 15.6RC1
* @since 15.5.1
* @since 14.10.15
*/
public class SortableElement extends BaseElement
{
private final WebElement container;
/**
* Creates a new instance.
*
* @param container the element that wraps the sortable elements
*/
public SortableElement(WebElement container)
{
super();
this.container = container;
}
/**
* Move a given element before another one.
*
* @param toMove the selector for the element to move inside the sortable
* @param before the selector for the element before which the element to move should be moved
*/
public void moveBefore(By toMove, By before)
{
WebElement elementToMove = this.container.findElement(toMove);
WebElement elementBefore = this.container.findElement(before);
Point target = elementBefore.getLocation();
Point source = elementToMove.getLocation();
// The drag & drop of the "sortable" plugin of "jquery-ui" is very sensitive so we need to script the
// moves of the mouse precisely if we don't want to have flickers.
// First, we click and hold the item we want to move.
Actions actions = getDriver().createActions().clickAndHold(elementToMove);
// Then we move into the position of the targeted item so jquery-ui can register we want to take its place.
actions.moveByOffset(target.getX() - source.getX(), target.getY() - source.getY());
// Now we do a little move on top left so jquery-ui understand we want to be *before* the other item and
// put a blank place instead of the other app.
actions.moveByOffset(-4, -4);
actions.perform();
// Before releasing the click, check that jquery-ui has moved the other item to let the place free.
getDriver().waitUntilCondition((ExpectedCondition<Object>) webDriver -> {
Point newTarget = elementBefore.getLocation();
Point newSource = elementToMove.getLocation();
return newTarget.getX() > newSource.getX() + 5 || newTarget.getY() > newSource.getY() + 5;
});
// Now we can release the selection
actions = new Actions(getDriver().getWrappedDriver());
actions.release();
actions.perform();
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment