Skip to content
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

[plugin-mobile-app] Add possibility to swipe the context #1939

Merged
merged 1 commit into from
Sep 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/modules/plugins/pages/plugin-mobile-app.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,9 @@ When I clear field located `accessibilityId(username)`

Swipes to an element in either UP, DOWN, LEFT, or RIGHT direction with the specified swipe duration

[INFO]
The step takes into account current context. If you need to perform swipe on the element, you need to switch the context to this element.

==== *_Wording_*

[source,gherkin]
Expand Down Expand Up @@ -467,6 +470,16 @@ When I swipe UP to element located `accessibilityId(end-of-screen)` with duratio
Then number of elements found by `accessibilityId(end-of-screen)` is equal to `1`
----

.Swipe context element.story
[source,gherkin]
----
Scenario: Switch slides
When I change context to element located `accessibilityId(carousel)`
Then number of elements found by `accessibilityId(slide 2)` is equal to `0`
When I swipe LEFT to element located `accessibilityId(slide 2)` with duration PT1S
Then number of elements found by `accessibilityId(slide 2)` is equal to `1`
----

=== Upload a file to a device

==== *_Info_*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,67 +17,77 @@
package org.vividus.bdd.mobileapp.model;

import org.openqa.selenium.Dimension;
import org.openqa.selenium.Point;
import org.openqa.selenium.Rectangle;
import org.vividus.bdd.mobileapp.configuration.MobileApplicationConfiguration;

public enum SwipeDirection
{
UP
{
@Override
public SwipeCoordinates calculateCoordinates(Dimension dimension,
public SwipeCoordinates calculateCoordinates(Rectangle swipeArea,
MobileApplicationConfiguration mobileApplicationConfiguration)
{
int indent = dimension.getHeight() / VERTICAL_INDENT_COEFFICIENT;
return createCoordinates(dimension.getHeight() - indent, indent, dimension.getWidth(),
mobileApplicationConfiguration.getSwipeVerticalXPosition());
int indent = swipeArea.getHeight() / VERTICAL_INDENT_COEFFICIENT;
return createCoordinates(swipeArea.getHeight() - indent, indent, swipeArea.getWidth(),
mobileApplicationConfiguration.getSwipeVerticalXPosition(), swipeArea.getPoint());
}
},
DOWN
{
@Override
public SwipeCoordinates calculateCoordinates(Dimension dimension,
public SwipeCoordinates calculateCoordinates(Rectangle swipeArea,
MobileApplicationConfiguration mobileApplicationConfiguration)
{
int indent = dimension.getHeight() / VERTICAL_INDENT_COEFFICIENT;
return createCoordinates(indent, dimension.getHeight() - indent, dimension.getWidth(),
mobileApplicationConfiguration.getSwipeVerticalXPosition());
int indent = swipeArea.getHeight() / VERTICAL_INDENT_COEFFICIENT;
return createCoordinates(indent, swipeArea.getHeight() - indent, swipeArea.getWidth(),
mobileApplicationConfiguration.getSwipeVerticalXPosition(), swipeArea.getPoint());
}
},
LEFT
{
@Override
public SwipeCoordinates calculateCoordinates(Dimension dimension,
public SwipeCoordinates calculateCoordinates(Rectangle swipeArea,
MobileApplicationConfiguration mobileApplicationConfiguration)
{
int indent = dimension.getWidth() / HORIZONTAL_INDENT_COEFFICIENT;
int y = calculateCoordinate(dimension.getHeight(),
int indent = swipeArea.getWidth() / HORIZONTAL_INDENT_COEFFICIENT;
int y = calculateCoordinate(swipeArea.getHeight(),
mobileApplicationConfiguration.getSwipeHorizontalYPosition());
return new SwipeCoordinates(dimension.getWidth() - indent, y, indent, y);
return createAdjustedCoordinates(swipeArea.getWidth() - indent, y, indent, y, swipeArea.getPoint());
}
},
RIGHT
{
@Override
public SwipeCoordinates calculateCoordinates(Dimension dimension,
public SwipeCoordinates calculateCoordinates(Rectangle swipeArea,
MobileApplicationConfiguration mobileApplicationConfiguration)
{
Dimension dimension = swipeArea.getDimension();
Point point = swipeArea.getPoint();
int indent = dimension.getWidth() / HORIZONTAL_INDENT_COEFFICIENT;
int y = calculateCoordinate(dimension.getHeight(),
mobileApplicationConfiguration.getSwipeHorizontalYPosition());
return new SwipeCoordinates(indent, y, dimension.getWidth() - indent, y);
return createAdjustedCoordinates(indent, y, dimension.getWidth() - indent, y, point);
}
};

private static final int VERTICAL_INDENT_COEFFICIENT = 5;
private static final int HORIZONTAL_INDENT_COEFFICIENT = 8;

public abstract SwipeCoordinates calculateCoordinates(Dimension dimension,
public abstract SwipeCoordinates calculateCoordinates(Rectangle swipeArea,
MobileApplicationConfiguration mobileApplicationConfiguration);

public static SwipeCoordinates createCoordinates(int startY, int endY, int width, int xOffsetPercentage)
public static SwipeCoordinates createCoordinates(int startY, int endY, int width, int xOffsetPercentage,
Point point)
{
int x = calculateCoordinate(width, xOffsetPercentage);
return new SwipeCoordinates(x, startY, x, endY);
return createAdjustedCoordinates(x, startY, x, endY, point);
}

private static SwipeCoordinates createAdjustedCoordinates(int startX, int startY, int endX, int endY, Point point)
{
return new SwipeCoordinates(startX + point.x, startY + point.y, endX + point.getX(), endY + point.getY());
}

@SuppressWarnings("MagicNumber")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;

import org.jbehave.core.annotations.When;
import org.openqa.selenium.Point;
import org.openqa.selenium.Rectangle;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebElement;
import org.vividus.bdd.mobileapp.model.SwipeDirection;
import org.vividus.bdd.monitor.TakeScreenshotOnFailure;
Expand All @@ -31,6 +35,7 @@
import org.vividus.selenium.manager.GenericWebDriverManager;
import org.vividus.ui.action.ISearchActions;
import org.vividus.ui.action.search.Locator;
import org.vividus.ui.context.IUiContext;

@TakeScreenshotOnFailure
public class TouchSteps
Expand All @@ -42,14 +47,16 @@ public class TouchSteps
private final IBaseValidations baseValidations;
private final ISearchActions searchActions;
private final GenericWebDriverManager genericWebDriverManager;
private final IUiContext uiContext;

public TouchSteps(TouchActions touchActions, IBaseValidations baseValidations,
ISearchActions searchActions, GenericWebDriverManager genericWebDriverManager)
ISearchActions searchActions, GenericWebDriverManager genericWebDriverManager, IUiContext uiContext)
{
this.touchActions = touchActions;
this.baseValidations = baseValidations;
this.searchActions = searchActions;
this.genericWebDriverManager = genericWebDriverManager;
this.uiContext = uiContext;
}

/**
Expand Down Expand Up @@ -89,6 +96,8 @@ public void tapByLocator(Locator locator)

/**
* Swipes to element in <b>direction</b> direction with duration <b>duration</b>
* The step takes into account current context. If you need to perform swipe on the element,
* you need to switch the context to this element.
* @param direction direction to swipe, either <b>UP</b> or <b>DOWN</b>
* @param locator locator to find an element
* @param swipeDuration swipe duration in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a> format
Expand All @@ -97,44 +106,54 @@ public void tapByLocator(Locator locator)
public void swipeToElement(SwipeDirection direction, Locator locator, Duration swipeDuration)
valfirst marked this conversation as resolved.
Show resolved Hide resolved
{
locator.getSearchParameters().setWaitForElement(false);

Supplier<Rectangle> swipeArea = createSwipeArea();
List<WebElement> elements = new ArrayList<>(searchActions.findElements(locator));
if (elements.isEmpty())
{
touchActions.swipeUntil(direction, swipeDuration, () ->
touchActions.swipeUntil(direction, swipeDuration, swipeArea.get(), () ->
{
elements.addAll(searchActions.findElements(locator));
return !elements.isEmpty();
});
}

if (baseValidations.assertElementsNumber(String.format("The element by locator %s exists", locator), elements,
ComparisonRule.EQUAL_TO, 1) && (SwipeDirection.UP == direction || SwipeDirection.DOWN == direction))
{
adjustVerticalPosition(elements.get(0), swipeDuration);
adjustVerticalPosition(elements.get(0), swipeArea.get(), swipeDuration);
}
}

private Supplier<Rectangle> createSwipeArea()
{
SearchContext searchContext = uiContext.getSearchContext();
if (searchContext instanceof WebElement)
{
WebElement contextElement = (WebElement) searchContext;
return contextElement::getRect;
}
return () -> new Rectangle(new Point(0, 0), genericWebDriverManager.getSize());
}

private void adjustVerticalPosition(WebElement element, Duration swipeDuration)
private void adjustVerticalPosition(WebElement element, Rectangle swipeArea, Duration swipeDuration)
{
int windowSizeHeight = genericWebDriverManager.getSize().getHeight();
int windowCenterY = windowSizeHeight / 2;
int swipeAreaSizeHeight = swipeArea.getHeight();
int swipeAreaCenterY = swipeAreaSizeHeight / 2;
int elementTopCoordinateY = element.getLocation().getY();

int bottomVisibilityIndent = (int) (VISIBILITY_BOTTOM_INDENT_COEFFICIENT * windowSizeHeight);
int visibilityY = windowSizeHeight - bottomVisibilityIndent;
int bottomVisibilityIndent = (int) (VISIBILITY_BOTTOM_INDENT_COEFFICIENT * swipeAreaSizeHeight);
int visibilityY = swipeAreaSizeHeight - bottomVisibilityIndent;
if (elementTopCoordinateY > visibilityY)
{
touchActions.performVerticalSwipe(windowCenterY, windowCenterY - (elementTopCoordinateY - visibilityY),
swipeDuration);
touchActions.performVerticalSwipe(swipeAreaCenterY,
swipeAreaCenterY - (elementTopCoordinateY - visibilityY), swipeArea, swipeDuration);
return;
}

int topVisibilityIndent = (int) (VISIBILITY_TOP_INDENT_COEFFICIENT * windowSizeHeight);
int topVisibilityIndent = (int) (VISIBILITY_TOP_INDENT_COEFFICIENT * swipeAreaSizeHeight);
if (elementTopCoordinateY < topVisibilityIndent)
{
touchActions.performVerticalSwipe(windowCenterY,
windowCenterY + topVisibilityIndent - elementTopCoordinateY, swipeDuration);
touchActions.performVerticalSwipe(swipeAreaCenterY,
swipeAreaCenterY + topVisibilityIndent - elementTopCoordinateY, swipeArea, swipeDuration);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

import org.openqa.selenium.Dimension;
import org.openqa.selenium.Point;
import org.openqa.selenium.Rectangle;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.RemoteWebElement;
import org.vividus.bdd.mobileapp.configuration.MobileApplicationConfiguration;
Expand Down Expand Up @@ -131,11 +132,13 @@ private TouchAction<?> buildTapAction(WebElement element, BiConsumer<TouchAction
* <li>the end of mobile scroll view is reached</li>
* <li>the swipe limit is exceeded</li>
* </ul>
* @param direction direction to swipe, either <b>UP</b> or <b>DOWN</b>
* @param direction direction to swipe, either <b>UP</b> or <b>DOWN</b>
* @param swipeDuration duration between a pointer moves from the start to the end of the swipe coordinates
* @param swipeArea the area to execute the swipe
* @param stopCondition condition to stop swiping
*/
public void swipeUntil(SwipeDirection direction, Duration swipeDuration, BooleanSupplier stopCondition)
public void swipeUntil(SwipeDirection direction, Duration swipeDuration, Rectangle swipeArea,
BooleanSupplier stopCondition)
{
/*
* mobile:scroll
Expand All @@ -156,8 +159,7 @@ public void swipeUntil(SwipeDirection direction, Duration swipeDuration, Boolean
Duration stabilizationDuration = mobileApplicationConfiguration.getSwipeStabilizationDuration();
int swipeLimit = mobileApplicationConfiguration.getSwipeLimit();
BufferedImage previousFrame = null;
SwipeCoordinates swipeCoordinates = direction.calculateCoordinates(genericWebDriverManager.getSize(),
mobileApplicationConfiguration);
SwipeCoordinates swipeCoordinates = direction.calculateCoordinates(swipeArea, mobileApplicationConfiguration);
for (int count = 0; count <= swipeLimit; count++)
{
swipe(swipeCoordinates, swipeDuration);
Expand Down Expand Up @@ -185,14 +187,15 @@ public void swipeUntil(SwipeDirection direction, Duration swipeDuration, Boolean
/**
* Performs vertical swipe from <b>startY</b> to <b>endY</b> with <b>swipeDuration</b>
*
* @param startY start Y coordinate
* @param endY end Y coordinate
* @param startY start Y coordinate
* @param endY end Y coordinate
* @param swipeArea the area to execute the swipe
* @param swipeDuration swipe duration in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a> format
*/
public void performVerticalSwipe(int startY, int endY, Duration swipeDuration)
public void performVerticalSwipe(int startY, int endY, Rectangle swipeArea, Duration swipeDuration)
{
swipe(SwipeDirection.createCoordinates(startY, endY, genericWebDriverManager.getSize().getWidth(),
mobileApplicationConfiguration.getSwipeVerticalXPosition()), swipeDuration);
swipe(SwipeDirection.createCoordinates(startY, endY, swipeArea.getWidth(),
mobileApplicationConfiguration.getSwipeVerticalXPosition(), swipeArea.getPoint()), swipeDuration);
}

private BufferedImage takeScreenshot()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.mockito.junit.jupiter.MockitoExtension;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.Point;
import org.openqa.selenium.Rectangle;
import org.openqa.selenium.remote.RemoteWebElement;
import org.vividus.bdd.mobileapp.configuration.MobileApplicationConfiguration;
import org.vividus.bdd.mobileapp.model.SwipeDirection;
Expand Down Expand Up @@ -78,6 +79,7 @@ class TouchActionsTests
private static final String WHITE_IMAGE = "white.png";
private static final String ELEMENT_ID = "elementId";
private static final Dimension DIMENSION = new Dimension(600, 800);
private static final Rectangle SWIPE_AREA = new Rectangle(new Point(0, 0), DIMENSION);

@Spy private final MobileApplicationConfiguration mobileApplicationConfiguration =
new MobileApplicationConfiguration(Duration.ZERO, 5, 50, 0);
Expand Down Expand Up @@ -181,7 +183,7 @@ void shouldSwipeUntilConditionIsTrue() throws IOException
when(screenshotTaker.takeViewportScreenshot()).thenReturn(getImage(BLACK_IMAGE))
.thenReturn(getImage(WHITE_IMAGE));

touchActions.swipeUntil(SwipeDirection.UP, DURATION, stopCondition);
touchActions.swipeUntil(SwipeDirection.UP, DURATION, SWIPE_AREA, stopCondition);

verifySwipe(3);
verifyConfiguration();
Expand All @@ -200,7 +202,7 @@ void shouldNotExceedSwipeLimit() throws IOException
.thenReturn(getImage(WHITE_IMAGE));

IllegalStateException exception = assertThrows(IllegalStateException.class,
() -> touchActions.swipeUntil(SwipeDirection.UP, DURATION, stopCondition));
() -> touchActions.swipeUntil(SwipeDirection.UP, DURATION, SWIPE_AREA, stopCondition));

assertEquals("Swiping is stopped due to exceeded swipe limit '5'", exception.getMessage());
verifySwipe(6);
Expand All @@ -217,7 +219,7 @@ void shouldStopSwipeOnceEndOfPageIsReached() throws IOException
.thenReturn(getImage(BLACK_IMAGE))
.thenReturn(getImage(BLACK_IMAGE));

touchActions.swipeUntil(SwipeDirection.UP, DURATION, stopCondition);
touchActions.swipeUntil(SwipeDirection.UP, DURATION, SWIPE_AREA, stopCondition);

verifySwipe(4);
verifyConfiguration();
Expand All @@ -233,7 +235,7 @@ void shouldWrapIOException() throws IOException
doThrow(exception).when(screenshotTaker).takeViewportScreenshot();

UncheckedIOException wrapper = assertThrows(UncheckedIOException.class,
() -> touchActions.swipeUntil(SwipeDirection.UP, DURATION, stopCondition));
() -> touchActions.swipeUntil(SwipeDirection.UP, DURATION, SWIPE_AREA, stopCondition));

assertEquals(exception, wrapper.getCause());
verifySwipe(1);
Expand All @@ -243,8 +245,7 @@ void shouldWrapIOException() throws IOException
@Test
void shouldPerformVerticalSwipe()
{
when(genericWebDriverManager.getSize()).thenReturn(DIMENSION);
touchActions.performVerticalSwipe(640, 160, DURATION);
touchActions.performVerticalSwipe(640, 160, SWIPE_AREA, DURATION);
verifySwipe(1);
}

Expand Down
Loading