Skip to content

Commit

Permalink
1899-feature-request-add-new-progress-bar-when-uploading-app-files-to…
Browse files Browse the repository at this point in the history
…-browserstack-or-lambdatest (#1905)

* implement progressbarloader for bs and lt

* adding assertions to tests

* optimize w3cCompliantScrolling
  • Loading branch information
MohabMohie authored Feb 23, 2025
1 parent 23ae64b commit 8e3ff6f
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.shaft.driver.SHAFT;
import com.shaft.tools.io.ReportManager;
import com.shaft.tools.io.internal.FailureReporter;
import com.shaft.tools.io.internal.ProgressBarLogger;
import com.shaft.tools.io.internal.ReportManagerHelper;
import org.openqa.selenium.MutableCapabilities;

Expand Down Expand Up @@ -90,7 +91,8 @@ private static MutableCapabilities setupNativeAppExecution(String username, Stri
parameters.add(apkFile);
parameters.add(customID);
var appUrl = "";
try {

try (ProgressBarLogger ignored = new ProgressBarLogger("Uploading app to BrowserStack...")) {
appUrl = Objects.requireNonNull(RestActions.getResponseJSONValue(new RestActions(serviceUri).buildNewRequest(appUploadServiceName, RestActions.RequestType.POST)
.setParameters(parameters, RestActions.ParametersType.FORM)
.setAuthentication(username, password, RequestBuilder.AuthenticationType.BASIC)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -644,12 +644,11 @@ public void initializeDriver(@NonNull DriverType driverType, MutableCapabilities
}
}

if (SHAFT.Properties.web.headlessExecution()) {
driver.manage().window().setSize(new Dimension(TARGET_WINDOW_SIZE.getWidth(), TARGET_WINDOW_SIZE.getHeight()));
} else {
Dimension browserWindowSize = new Dimension(SHAFT.Properties.web.browserWindowWidth(), SHAFT.Properties.web.browserWindowHeight());
if (!isMobileExecution && !SHAFT.Properties.flags.autoMaximizeBrowserWindow()) {
driver.manage().window().setSize(browserWindowSize);
if (!isMobileExecution && SHAFT.Properties.web.headlessExecution()) {
if (SHAFT.Properties.flags.autoMaximizeBrowserWindow()) {
driver.manage().window().setSize(new Dimension(TARGET_WINDOW_SIZE.getWidth(), TARGET_WINDOW_SIZE.getHeight()));
} else {
driver.manage().window().setSize(new Dimension(SHAFT.Properties.web.browserWindowWidth(), SHAFT.Properties.web.browserWindowHeight()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.shaft.driver.SHAFT;
import com.shaft.tools.io.ReportManager;
import com.shaft.tools.io.internal.FailureReporter;
import com.shaft.tools.io.internal.ProgressBarLogger;
import com.shaft.tools.io.internal.ReportManagerHelper;
import org.openqa.selenium.MutableCapabilities;
import org.openqa.selenium.Platform;
Expand Down Expand Up @@ -86,7 +87,7 @@ private static MutableCapabilities setupNativeAppExecution(String username, Stri
parameters.add(apkFile);
parameters.add(customID);
var appUrl = "";
try {
try (ProgressBarLogger ignored = new ProgressBarLogger("Uploading app to LambdaTest...")) {
appUrl = Objects.requireNonNull(RestActions.getResponseJSONValue(new SHAFT.API(serviceUri).post(appUploadServiceName).setContentType("multipart/form-data").setParameters(parameters, RestActions.ParametersType.FORM).setAuthentication(username, password, RequestBuilder.AuthenticationType.BASIC).perform(), "app_url"));
ReportManager.logDiscrete("LambdaTest app_url: " + appUrl);
} catch (NullPointerException exception) {
Expand Down
124 changes: 42 additions & 82 deletions src/main/java/com/shaft/gui/element/TouchActions.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
import com.google.common.collect.ImmutableMap;
import com.shaft.driver.SHAFT;
import com.shaft.driver.internal.DriverFactory.DriverFactoryHelper;
import com.shaft.driver.internal.DriverFactory.SynchronizationManager;
import com.shaft.driver.internal.FluentWebDriverAction;
import com.shaft.driver.internal.WizardHelpers;
import com.shaft.gui.internal.image.ScreenshotManager;
import com.shaft.tools.io.ReportManager;
import com.shaft.tools.io.internal.ReportManagerHelper;
import com.shaft.validation.internal.WebDriverElementValidationsBuilder;
import io.appium.java_client.AppiumBy;
import io.appium.java_client.AppiumDriver;
Expand All @@ -23,6 +23,7 @@
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import static java.util.Arrays.asList;

Expand Down Expand Up @@ -533,7 +534,7 @@ public TouchActions swipeElementIntoView(By scrollableElementLocator, By targetE
try {
if (driverFactoryHelper.getDriver() instanceof AppiumDriver appiumDriver) {
// appium native application
boolean isElementFound = attemptToSwipeElementIntoViewInNativeApp(scrollableElementLocator, targetElementLocator, swipeDirection);
boolean isElementFound = attemptW3cCompliantActionsScroll(swipeDirection, scrollableElementLocator, targetElementLocator);
if (Boolean.FALSE.equals(isElementFound)) {
elementActionsHelper.failAction(appiumDriver, targetElementLocator);
}
Expand Down Expand Up @@ -604,76 +605,21 @@ public TouchActions rotate(ScreenOrientation orientation) {

@SuppressWarnings("unchecked")
private List<Object> attemptToSwipeElementIntoViewInNativeApp(By scrollableElementLocator, String targetElementImage, SwipeDirection swipeDirection) {
boolean isElementFound = false;
boolean canStillScroll = true;
var isDiscrete = ReportManagerHelper.getDiscreteLogging();
ReportManagerHelper.setDiscreteLogging(true);
List<Object> visualIdentificationObjects;
// appium native device
// Wait for element presence and get the needed data
visualIdentificationObjects = elementActionsHelper.waitForElementPresence(driverFactoryHelper.getDriver(), targetElementImage);
List<Integer> coordinates = (List<Integer>) visualIdentificationObjects.get(2);

// force SHAFT back into the loop even if canStillScroll is false, or ignore it completely for the first 5 scroll attempts
int blindScrollingAttempts = 0;

do {
// appium native device
// Wait for element presence and get the needed data
visualIdentificationObjects = elementActionsHelper.waitForElementPresence(driverFactoryHelper.getDriver(), targetElementImage);
List<Integer> coordinates = (List<Integer>) visualIdentificationObjects.get(2);

if (!Collections.emptyList().equals(coordinates)) {
// element is already on screen
isElementFound = true;
ReportManager.logDiscrete("Element found on screen.");
} else {
// for the animated GIF:
elementActionsHelper.takeScreenshot(driverFactoryHelper.getDriver(), null, "swipeElementIntoView", null, true);
canStillScroll = attemptW3cCompliantActionsScroll(swipeDirection, scrollableElementLocator, null);
if (!canStillScroll) {
// check if element can be found after scrolling to the end of the page
visualIdentificationObjects = elementActionsHelper.waitForElementPresence(driverFactoryHelper.getDriver(), targetElementImage);
coordinates = (List<Integer>) visualIdentificationObjects.get(2);
if (!Collections.emptyList().equals(coordinates)) {
isElementFound = true;
ReportManager.logDiscrete("Element found on screen.");
}
}
}
blindScrollingAttempts++;
} while (Boolean.FALSE.equals(isElementFound) && (blindScrollingAttempts < DEFAULT_NUMBER_OF_ATTEMPTS_TO_SCROLL_TO_ELEMENT || Boolean.TRUE.equals(canStillScroll)));
ReportManagerHelper.setDiscreteLogging(isDiscrete);
if (!Collections.emptyList().equals(coordinates)) {
// element is already on screen
ReportManager.logDiscrete("Element found on screen.");
} else {
attemptW3cCompliantActionsScroll(swipeDirection, scrollableElementLocator, null);
}
return visualIdentificationObjects;
}

private boolean attemptToSwipeElementIntoViewInNativeApp(By scrollableElementLocator, By targetElementLocator, SwipeDirection swipeDirection) {
boolean isElementFound = false;
boolean canStillScroll = true;
var isDiscrete = ReportManagerHelper.getDiscreteLogging();
ReportManagerHelper.setDiscreteLogging(true);

// force SHAFT back into the loop even if canStillScroll is false, or ignore it completely for the first 5 scroll attempts
int blindScrollingAttempts = 0;

do {
// appium native device
if (elementActionsHelper.waitForElementPresenceWithReducedTimeout(driverFactoryHelper.getDriver(), targetElementLocator) > 0) {
// element is already on screen
isElementFound = true;
ReportManager.logDiscrete("Element found on screen.");
} else {
// for the animated GIF:
elementActionsHelper.takeScreenshot(driverFactoryHelper.getDriver(), null, "swipeElementIntoView", null, true);
canStillScroll = attemptW3cCompliantActionsScroll(swipeDirection, scrollableElementLocator, targetElementLocator);
if (!canStillScroll && elementActionsHelper.waitForElementPresenceWithReducedTimeout(driverFactoryHelper.getDriver(), targetElementLocator) > 0) {
// element was found after scrolling to the end of the page
isElementFound = true;
ReportManager.logDiscrete("Element found on screen.");
}
}
blindScrollingAttempts++;
} while (Boolean.FALSE.equals(isElementFound) && (blindScrollingAttempts < DEFAULT_NUMBER_OF_ATTEMPTS_TO_SCROLL_TO_ELEMENT || Boolean.TRUE.equals(canStillScroll)));
ReportManagerHelper.setDiscreteLogging(isDiscrete);
return isElementFound;
}

private void attemptUISelectorScroll(SwipeDirection swipeDirection, int scrollableElementInstanceNumber) {
ReportManager.logDiscrete("Swiping to find Element using UiSelector.");
int scrollingSpeed = 100;
Expand All @@ -685,7 +631,7 @@ private void attemptUISelectorScroll(SwipeDirection swipeDirection, int scrollab
elementActionsHelper.getElementsCount(driverFactoryHelper.getDriver(), androidUIAutomator);
}

private boolean attemptW3cCompliantActionsScroll(SwipeDirection swipeDirection, By scrollableElementLocator, By targetElementLocator) {
private HashMap<Object, Object> prepareParameters(SwipeDirection swipeDirection, By scrollableElementLocator, By targetElementLocator) {
var logMessage = "Swiping to find Element using W3C Compliant Actions. SwipeDirection \"" + swipeDirection + "\"";
if (targetElementLocator != null) {
logMessage += ", TargetElementLocator \"" + targetElementLocator + "\"";
Expand All @@ -697,7 +643,6 @@ private boolean attemptW3cCompliantActionsScroll(SwipeDirection swipeDirection,
ReportManager.logDiscrete(logMessage);

Dimension screenSize = driverFactoryHelper.getDriver().manage().window().getSize();
boolean canScrollMore = true;

var scrollParameters = new HashMap<>();

Expand Down Expand Up @@ -732,30 +677,45 @@ private boolean attemptW3cCompliantActionsScroll(SwipeDirection swipeDirection,
case LEFT -> scrollParameters.putAll(ImmutableMap.of("left", screenSize.getWidth() - 100, "top", 0));
}
}
scrollParameters.putAll(ImmutableMap.of(
"direction", swipeDirection.toString()
));
return scrollParameters;
}

private boolean performW3cCompliantScroll(HashMap<Object, Object> scrollParameters) {
boolean canScrollMore = true;
if (driverFactoryHelper.getDriver() instanceof AndroidDriver androidDriver) {
scrollParameters.putAll(ImmutableMap.of(
"direction", swipeDirection.toString()
));
canScrollMore = (Boolean) androidDriver.executeScript("mobile:scrollGesture", scrollParameters);
} else if (driverFactoryHelper.getDriver() instanceof IOSDriver iosDriver) {
scrollParameters.putAll(ImmutableMap.of(
"direction", swipeDirection.toString()
));
//http://appium.github.io/appium-xcuitest-driver/4.16/execute-methods/#mobile-scroll
var ret = iosDriver.executeScript("mobile:scroll", scrollParameters);
canScrollMore = ret == null || (Boolean) ret;
}
var logMessageAfter = "Attempted to scroll using these parameters: \"" + scrollParameters + "\"";
if (canScrollMore) {
logMessageAfter += ", there is still more room to keep scrolling.";
} else {
logMessageAfter += ", there is no more room to keep scrolling.";
}
ReportManager.logDiscrete(logMessageAfter);
return canScrollMore;
}

private boolean attemptW3cCompliantActionsScroll(SwipeDirection swipeDirection, By scrollableElementLocator, By targetElementLocator) {
var scrollParameters = prepareParameters(swipeDirection, scrollableElementLocator, targetElementLocator);
AtomicBoolean canScrollMore = new AtomicBoolean(true);

new SynchronizationManager(driver).fluentWait().until(f -> {
// for the animated GIF:
elementActionsHelper.takeScreenshot(driverFactoryHelper.getDriver(), null, "swipeElementIntoView", null, true);

var elementExistsOnViewPort = !driver.findElements(targetElementLocator).isEmpty();
if (elementExistsOnViewPort)
return true;
canScrollMore.set(performW3cCompliantScroll(scrollParameters));
elementExistsOnViewPort = !driver.findElements(targetElementLocator).isEmpty();
if (!canScrollMore.get() && !elementExistsOnViewPort)
throw new RuntimeException("Element not found after scrolling to the end of the page.");
return elementExistsOnViewPort;
});
ReportManager.logDiscrete("Element found on screen.");
return true;
}


private void attemptPinchToZoomIn() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ protected void internalPerform() {
}
this.validationCategoryString = validationCategory.equals(ValidationEnums.ValidationCategory.HARD_ASSERT) ? "Assert" : "Verify";
ReportManager.logDiscrete(this.validationCategoryString + " that " + this.customReportMessage);
try (ProgressBarLogger pblogger = new ProgressBarLogger(this.validationCategoryString.equals("Assert") ? "Asserting..." : "Verifying...")) {
try (ProgressBarLogger ignored = new ProgressBarLogger(this.validationCategoryString.equals("Assert") ? "Asserting..." : "Verifying...")) {
// perform validation
performValidation();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import java.time.Duration;

public class androidBasicInteractionsTests {
public class AndroidBasicInteractionsTests {
private static final ThreadLocal<SHAFT.GUI.WebDriver> driver = new ThreadLocal<>();
private final String PACKAGE = "io.appium.android.apis";

Expand All @@ -35,7 +35,8 @@ public void wizard_scrollInExpandableLists_verticalScrolling_insideScreen() {
.tap(By.xpath("//android.widget.TextView[@text='Group 18']"))
.swipeElementIntoView(By.xpath("//android.widget.TextView[@text='Child 13']"), TouchActions.SwipeDirection.DOWN)
.swipeElementIntoView(By.xpath("//android.widget.TextView[@text='Group 1']"), TouchActions.SwipeDirection.UP)
.sendAppToBackground(1);
.sendAppToBackground(1)
.assertThat(By.xpath("//android.widget.TextView[@text='Group 1']")).exists();
}

@Test
Expand Down Expand Up @@ -64,7 +65,9 @@ public void scrollInExpandableLists_verticalScrolling_insideElement(){
.swipeElementIntoView(By.id("io.appium.android.apis:id/list2"), By.xpath("//android.widget.ListView[2]/android.widget.TextView[@text='Blue']"), TouchActions.SwipeDirection.DOWN)
.tap(By.xpath("//android.widget.ListView[2]/android.widget.TextView[@text='Blue']"))
.swipeElementIntoView(By.id("io.appium.android.apis:id/list2"), By.xpath("//android.widget.ListView[2]/android.widget.TextView[@text='Abbaye de Belloc']"), TouchActions.SwipeDirection.UP)
.tap(By.xpath("//android.widget.ListView[2]/android.widget.TextView[@text='Abbaye de Belloc']"));
.tap(By.xpath("//android.widget.ListView[2]/android.widget.TextView[@text='Abbaye de Belloc']"))
.assertThat(By.xpath("//android.widget.ListView[1]/android.widget.TextView[@text='Abbaye de Belloc']")).exists();

}

@Test
Expand All @@ -77,7 +80,7 @@ public void scrollInExpandableLists_verticalScrolling_insideElement2(){
.swipeElementIntoView(By.id("io.appium.android.apis:id/list1"), By.xpath("//android.widget.ListView[1]/android.widget.TextView[@text='Blue']"), TouchActions.SwipeDirection.DOWN)
.tap(By.xpath("//android.widget.ListView[1]/android.widget.TextView[@text='Blue']"))
.swipeElementIntoView(By.id("io.appium.android.apis:id/list1"), By.xpath("//android.widget.ListView[1]/android.widget.TextView[@text='Abbaye de Belloc']"), TouchActions.SwipeDirection.UP)
.tap(By.xpath("//android.widget.ListView[1]/android.widget.TextView[@text='Abbaye de Belloc']"));
.assertThat(By.xpath("//android.widget.ListView[1]/android.widget.TextView[@text='Abbaye de Belloc']")).exists();
}

@Test
Expand All @@ -92,7 +95,7 @@ public void scrollInExpandableLists_horizontalScrolling_insideElement(){
.swipeElementIntoView(By.xpath("//android.widget.HorizontalScrollView"), By.xpath("//android.widget.HorizontalScrollView//android.widget.TextView[@text='TAB 12']"), TouchActions.SwipeDirection.RIGHT)
.tap(By.xpath("//android.widget.HorizontalScrollView//android.widget.TextView[@text='TAB 12']"))
.swipeElementIntoView(By.xpath("//android.widget.HorizontalScrollView"), By.xpath("//android.widget.HorizontalScrollView//android.widget.TextView[@text='TAB 1']"), TouchActions.SwipeDirection.LEFT)
.tap(By.xpath("//android.widget.HorizontalScrollView//android.widget.TextView[@text='TAB 1']"));
.assertThat(By.xpath("//android.widget.HorizontalScrollView//android.widget.TextView[@text='TAB 1']")).exists();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public void setup() {
// SHAFT.Properties.mobile.set().selfManaged(true);

// local appium server (for local and GitHub actions execution)
SHAFT.Properties.platform.set().executionAddress("localhost:4723");
SHAFT.Properties.mobile.set().app(SHAFT.Properties.paths.testData() + "apps/BStackSampleApp.ipa");
// SHAFT.Properties.platform.set().executionAddress("localhost:4723");
// SHAFT.Properties.mobile.set().app(SHAFT.Properties.paths.testData() + "apps/BStackSampleApp.ipa");

// remote browserstack server (new app version)
// SHAFT.Properties.platform.set().executionAddress("browserstack");
Expand All @@ -58,6 +58,7 @@ public void setup() {

@AfterClass(alwaysRun = true)
public void teardown() {
driver.get().quit();
if (driver.get() != null)
driver.get().quit();
}
}

0 comments on commit 8e3ff6f

Please sign in to comment.