Skip to content

Commit

Permalink
Implement native scroll actions (#142) +semver: feature
Browse files Browse the repository at this point in the history
- cover with tests
- replace js references with native actions in pre-existing methods
- add localization values for scrolling actions
- refactor MouseActions
- Update to Selenium 4.23.0
  • Loading branch information
mialeska authored Jul 23, 2024
1 parent e9892f6 commit e59afe5
Show file tree
Hide file tree
Showing 15 changed files with 167 additions and 34 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
<dependency>
<groupId>com.github.aquality-automation</groupId>
<artifactId>aquality-selenium-core</artifactId>
<version>4.0.3</version>
<version>4.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/aquality/selenium/browser/Browser.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import org.openqa.selenium.*;
import org.openqa.selenium.WebDriver.Navigation;
import org.openqa.selenium.devtools.HasDevTools;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.interactions.WheelInput.ScrollOrigin;
import org.openqa.selenium.logging.LogEntries;
import org.openqa.selenium.remote.Augmenter;
import org.openqa.selenium.remote.RemoteWebDriver;
Expand Down Expand Up @@ -352,9 +354,32 @@ public void handlePromptAlert(AlertActions alertAction, String text) {
* @param y coordinate y
*/
public void scrollWindowBy(int x, int y) {
localizedLogger.info("loc.scrolling.by", x, y);
new Actions(getDriver()).scrollByAmount(x, y).perform();
}

/**
* Executes scrolling of the page to given coordinates x and y using JavaScript.
*
* @param x coordinate x
* @param y coordinate y
*/
public void scrollWindowByViaJs(int x, int y) {
localizedLogger.info("loc.scrolling.by.js", x, y);
executeScript(JavaScript.SCROLL_WINDOW_BY.getScript(), x, y);
}

/**
* Scrolls portion of screen from specified origin.
*
* @param scrollOrigin Origination point (either viewport or element, with possible offset)
* @param x coordinate x
* @param y coordinate y
*/
public void scrollFromOrigin(ScrollOrigin scrollOrigin, int x, int y) {
new Actions(getDriver()).scrollFromOrigin(scrollOrigin, x, y).perform();
}

/**
* Sets given window size
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public void scrollIntoView() {
* @param y vertical coordinate
*/
public void scrollBy(int x, int y) {
logElementAction("loc.scrolling.js");
logElementAction("loc.scrolling.by.js", x, y);
executeScript(JavaScript.SCROLL_BY, x, y);
}

Expand Down
84 changes: 63 additions & 21 deletions src/main/java/aquality/selenium/elements/actions/MouseActions.java
Original file line number Diff line number Diff line change
@@ -1,81 +1,123 @@
package aquality.selenium.elements.actions;

import aquality.selenium.browser.AqualityServices;
import aquality.selenium.core.localization.ILocalizedLogger;
import aquality.selenium.core.utilities.IElementActionRetrier;
import aquality.selenium.elements.interfaces.IElement;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.interactions.WheelInput.ScrollOrigin;

import java.util.function.UnaryOperator;
import java.util.function.BiFunction;

import static aquality.selenium.browser.AqualityServices.getBrowser;

public class MouseActions {
private final IElement element;
private final String type;
private final String name;
private final ILocalizedLogger logger;
private final IElementActionRetrier elementActionRetrier;

public MouseActions(IElement element, String type) {
this.element = element;
this.type = type;
this.name = element.getName();
this.logger = AqualityServices.getLocalizedLogger();
this.elementActionRetrier = AqualityServices.get(IElementActionRetrier.class);
}

/**
* Click via Action.
*/
public void click() {
infoLoc("loc.clicking");
logElementAction("loc.clicking");
new JsActions(element, type).highlightElement();
performAction(Actions::click);
performActionAfterMove((elem, actions) -> actions.click());
}

/**
* Click Right (calls context menu) on the element
*/
public void rightClick() {
infoLoc("loc.clicking.right");
performAction(actions -> actions.contextClick(element.getElement()));
logElementAction("loc.clicking.right");
performActionAfterMove((elem, actions) -> actions.contextClick(elem));
}

/**
* Scrolling to element
*/
public void scrollToElement() {
logElementAction("loc.scrolling");
performAction((elem, actions) -> actions.scrollToElement(elem));
}

/**
* Scrolling by coordinates
*
* @param x horizontal coordinate
* @param y vertical coordinate
*/
public void scrollFromOrigin(int x, int y) {
scrollFromOrigin(x, y, 0, 0);
}

/**
* Scrolling by coordinates
*
* @param x horizontal coordinate
* @param y vertical coordinate
* @param xOffset horizontal offset
* @param yOffset vertical offset
*/
public void scrollFromOrigin(int x, int y, int xOffset, int yOffset)
{
logElementAction("loc.scrolling.by", x, y);
elementActionRetrier.doWithRetry(() -> {
ScrollOrigin scrollOrigin = ScrollOrigin.fromElement(element.getElement(), xOffset, yOffset);
getBrowser().scrollFromOrigin(scrollOrigin, x, y);
});
}

/**
* Move mouse to this element.
*/
public void moveMouseToElement() {
infoLoc("loc.moving");
performAction(actions -> actions);
logElementAction("loc.moving");
performActionAfterMove((elem, actions) -> actions);
}

/**
* Move mouse from this element.
*/
public void moveMouseFromElement() {
infoLoc("loc.movingFrom");
AqualityServices.get(IElementActionRetrier.class).doWithRetry(() ->
new Actions(getBrowser().getDriver())
.moveToElement(element.getElement(), -element.getElement().getSize().width, -element.getElement().getSize().height)
.build().perform());
logElementAction("loc.movingFrom");
performAction(((elem, actions) -> actions.moveToElement(elem, elem.getSize().width, elem.getSize().height)));
}

/**
* Performs double-click on the element.
*/
public void doubleClick() {
infoLoc("loc.clicking.double");
performAction(actions -> actions.doubleClick(element.getElement()));
logElementAction("loc.clicking.double");
performActionAfterMove((elem, actions) -> actions.doubleClick(elem));
}

private void performActionAfterMove(BiFunction<WebElement, Actions, Actions> function) {
performAction((elem, actions) -> function.apply(elem, actions.moveToElement(elem)));
}

private void performAction(UnaryOperator<Actions> function) {
Actions actions = new Actions(getBrowser().getDriver()).moveToElement(element.getElement());
AqualityServices.get(IElementActionRetrier.class).doWithRetry(() ->
function.apply(actions).build().perform());
private void performAction(BiFunction<WebElement, Actions, Actions> action) {
elementActionRetrier.doWithRetry(() -> action.apply(element.getElement(), new Actions(getBrowser().getDriver())).perform());
}

/**
* The implementation of a method for logging of MouseActions
*
* @param key key in localization resource of message to display in the log.
* @param key key in localization resource of message to display in the log.
* @param args Arguments, which will be provided to template of localized message.
*/
private void infoLoc(String key) {
AqualityServices.getLocalizedLogger().infoElementAction(type, name, key);
private void logElementAction(String key, Object... args) {
logger.infoElementAction(type, name, key, args);
}
}
10 changes: 10 additions & 0 deletions src/main/java/aquality/selenium/forms/Form.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ public IElementStateProvider state() {
* @param y vertical coordinate
*/
public void scrollBy(int x, int y) {
getFormLabel().getMouseActions().scrollFromOrigin(x, y);
}

/**
* Scroll form via JavaScript without scrolling entire page
*
* @param x horizontal coordinate
* @param y vertical coordinate
*/
public void scrollByJs(int x, int y) {
getFormLabel().getJsActions().scrollBy(x, y);
}

Expand Down
5 changes: 4 additions & 1 deletion src/main/resources/localization/be.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@
"loc.movingFrom": "Адводзім курсор мышы ад элемента",
"loc.radio": "Радыёкнопка",
"loc.scrolling.center.js": "Пракручваем старонку да цэнтра элемента праз JavaScript",
"loc.scrolling.js": "Пракручваем старонку праз JavaScript",
"loc.scrolling.js": "Пракручваем старонку да элемента праз JavaScript",
"loc.scrolling": "Пракручваем старонку да элемента",
"loc.scrolling.by": "Пракручваем на (%s,%s)",
"loc.scrolling.by.js": "Пракручваем на (%s,%s) праз JavaScript",
"loc.selecting.value": "Выбіраем значэнне - '%s'",
"loc.deselecting.value": "Адмяняем выбар значэння - '%s'",
"loc.send.text": "Задаем тэкст - '%s'",
Expand Down
5 changes: 4 additions & 1 deletion src/main/resources/localization/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@
"loc.movingFrom": "Moving mouse from element",
"loc.radio": "RadioButton",
"loc.scrolling.center.js": "Scrolling to the center via JavaScript",
"loc.scrolling.js": "Scrolling via JavaScript",
"loc.scrolling.js": "Scrolling to element via JavaScript",
"loc.scrolling": "Scrolling to element",
"loc.scrolling.by": "Scrolling by (%s,%s)",
"loc.scrolling.by.js": "Scrolling by (%s,%s) via JavaScript",
"loc.selecting.value": "Selecting value - '%s'",
"loc.deselecting.value": "Deselecting value - '%s'",
"loc.send.text": "Setting text - '%s'",
Expand Down
5 changes: 4 additions & 1 deletion src/main/resources/localization/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@
"loc.movingFrom": "Przesuwanie myszy z elementu",
"loc.radio": "RadioButton",
"loc.scrolling.center.js": "Przewijanie do centrum przez JavaScript",
"loc.scrolling.js": "Przewijanie przez JavaScript",
"loc.scrolling.js": "Przewijanie do elementu przez JavaScript",
"loc.scrolling": "Przewijanie do elementu",
"loc.scrolling.by": "Przewijanie o (%s,%s)",
"loc.scrolling.by.js": "Przewijanie o (%s,%s) przez JavaScript",
"loc.selecting.value": "Wybieranie wartości - '%s'",
"loc.deselecting.value": "Anulowanie wybierania wartości - '%s'",
"loc.send.text": "Ustawianie tekstu - '%s'",
Expand Down
5 changes: 4 additions & 1 deletion src/main/resources/localization/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@
"loc.movingFrom": "Сдвиг курсора с элемента",
"loc.radio": "Радиокнопка",
"loc.scrolling.center.js": "Скроллинг в центр (посредством JavaScript)",
"loc.scrolling.js": "Скроллинг посредством JavaScript",
"loc.scrolling.js": "Скроллинг к элементу (посредством JavaScript)",
"loc.scrolling": "Скроллинг к элементу",
"loc.scrolling.by": "Скроллинг на (%s,%s)",
"loc.scrolling.by.js": "Скроллинг на (%s,%s) посредством JavaScript",
"loc.selecting.value": "Выбор значения - '%s'",
"loc.deselecting.value": "Отмена выбора значения - '%s'",
"loc.send.text": "Ввод текста - '%s'",
Expand Down
5 changes: 4 additions & 1 deletion src/main/resources/localization/uk.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@
"loc.movingFrom": "Переміщення миші від елемента",
"loc.radio": "Радіокнопка",
"loc.scrolling.center.js": "Прокрутка до центру за допомогою JavaScript",
"loc.scrolling.js": "Прокрутка за допомогою JavaScript",
"loc.scrolling.js": "Прокрутка до елемента за допомогою JavaScript",
"loc.scrolling": "Прокрутка до елемента",
"loc.scrolling.by": "Прокрутка на (%s,%s)",
"loc.scrolling.by.js": "Прокрутка на (%s,%s) за допомогою JavaScript",
"loc.selecting.value": "Вибір значення - '%s'",
"loc.deselecting.value": "Скасування выбору значення - '%s'",
"loc.send.text": "Встановлення тексту - '%s'",
Expand Down
36 changes: 34 additions & 2 deletions src/test/java/tests/integration/ActionTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import theinternet.forms.JQueryMenuForm;
import theinternet.forms.WelcomeForm;

import java.awt.*;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;

Expand All @@ -22,7 +23,7 @@ public class ActionTests extends BaseTest {
@BeforeMethod
@Override
protected void beforeMethod() {
AqualityServices.getBrowser().getDriver().manage().window().maximize();
getBrowser().maximize();
}

@Test
Expand All @@ -35,11 +36,42 @@ public void testScrollToTheCenter() {

Long windowHeight = getScriptResultOrDefault("getWindowSize.js", 10L);
double currentY = getScriptResultOrDefault("getElementYCoordinate.js", 0.0, link.getElement());
double coordinateRelatingWindowCenter = windowHeight.doubleValue() / 2 - currentY;
double coordinateRelatingWindowCenter = windowHeight.doubleValue() / 2 - currentY;
Assert.assertTrue(Math.abs(coordinateRelatingWindowCenter) <= accuracy,
"Upper bound of element should be in the center of the page");
}

@Test
public void testScrollToElement() throws TimeoutException {
InfiniteScrollForm infiniteScrollForm = new InfiniteScrollForm();
getBrowser().goTo(infiniteScrollForm.getUrl());
infiniteScrollForm.waitForMoreExamples();
Dimension size = infiniteScrollForm.getLastExampleLabel().visual().getSize();
getBrowser().scrollWindowBy(size.width, size.height);
getBrowser().setWindowSize(size.width, size.height);
getBrowser().scrollWindowBy(0, 0);
int defaultCount = infiniteScrollForm.getExampleLabels().size();
AtomicReference<ILabel> lastExampleLabel = new AtomicReference<>(infiniteScrollForm.getLastExampleLabel());
AqualityServices.getConditionalWait().waitForTrue(() -> {
lastExampleLabel.set(infiniteScrollForm.getLastExampleLabel());
lastExampleLabel.get().getMouseActions().scrollToElement();
return infiniteScrollForm.getExampleLabels().size() > defaultCount;
}, "Some examples should be added after scroll");
}

@Test
public void testScrollFromOrigin() throws TimeoutException {
InfiniteScrollForm infiniteScrollForm = new InfiniteScrollForm();
getBrowser().goTo(infiniteScrollForm.getUrl());
int defaultCount = infiniteScrollForm.getExampleLabels().size();
AtomicReference<ILabel> lastExampleLabel = new AtomicReference<>(infiniteScrollForm.getLastExampleLabel());
AqualityServices.getConditionalWait().waitForTrue(() -> {
lastExampleLabel.set(infiniteScrollForm.getLastExampleLabel());
lastExampleLabel.get().getMouseActions().scrollFromOrigin(0, lastExampleLabel.get().visual().getSize().height);
return infiniteScrollForm.getExampleLabels().size() > defaultCount;
}, "Some examples should be added after scroll");
}

@Test
public void testScrollIntoView() throws TimeoutException {
InfiniteScrollForm infiniteScrollForm = new InfiniteScrollForm();
Expand Down
9 changes: 9 additions & 0 deletions src/test/java/tests/integration/BrowserTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,15 @@ public void testShouldBePossibleToScrollWindowBy(){
getBrowser().scrollWindowBy(0, formHeight);
Assert.assertEquals(initialY - scrollForm.getFormPointInViewPort().getY(), formHeight);
}
@Test
public void testShouldBePossibleToScrollWindowByViaJavaScript(){
WelcomeForm scrollForm = new WelcomeForm();
getBrowser().goTo(scrollForm.getUrl());
int initialY = scrollForm.getFormPointInViewPort().getY();
int formHeight = (int) scrollForm.getSize().getHeight();
getBrowser().scrollWindowByViaJs(0, formHeight);
Assert.assertEquals(initialY - scrollForm.getFormPointInViewPort().getY(), formHeight);
}

@Test
public void testShouldBePossibleToGetBrowserName() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import aquality.selenium.browser.AqualityServices;
import aquality.selenium.browser.devtools.EmulationHandling;
import com.google.common.collect.ImmutableMap;
import org.openqa.selenium.devtools.v124.emulation.Emulation;
import org.openqa.selenium.devtools.v124.emulation.model.DisplayFeature;
import org.openqa.selenium.devtools.v127.emulation.Emulation;
import org.openqa.selenium.devtools.v127.emulation.model.DisplayFeature;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import aquality.selenium.browser.AqualityServices;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.devtools.v124.network.model.ConnectionType;
import org.openqa.selenium.devtools.v127.network.model.ConnectionType;
import org.testng.Assert;
import org.testng.annotations.Test;
import tests.BaseTest;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import manytools.BrowserLanguageForm;
import manytools.UserAgentForm;
import org.openqa.selenium.devtools.idealized.Network;
import org.openqa.selenium.devtools.v124.emulation.Emulation;
import org.openqa.selenium.devtools.v127.emulation.Emulation;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
Expand Down

0 comments on commit e59afe5

Please sign in to comment.