Appium - Horizontal Scroll view - appium

I am using an android app that has horizontal scroll view, how to traverse through each of these items inside the horizontal scroll view using appium . The items change dynamically, so using textview won't work. Is there any method to get the length of the horizontal scroll view and traverse using loop?

Here is how i deal with a similar situation:
assuming you want to scroll down to a specific textView element.
while(!driver.findElement(By.id("textViewId").isdisplayed){
scrollDownManually();
}
public void scrollDownManually() {
Dimension size = Android.driver.manage().window().getSize();
int starty = (int) (size.height * 0.80);
int endy = (int) (size.height * 0.20);
int startx = size.width / 2;
Android.driver.swipe(startx, starty, startx, endy, 1000);
}

You can always perform swipe actions
TouchAction t=new TouchAction(driver);
//long press, for atleast 1 sec first and move the object
WebElement First=driver.findElementByXPath("//*[**First Element**]");
WebElement second=driver.findElementByXPath("//*[**Second Element**]");
t.longPress(longPressOptions().withElement(element(First)).withDuration(ofSeconds(3))).moveTo(element(second)).release().perform();

Related

Appium 6.1.0 TouchActions vs TouchAction

I was searching for the "right", or the "latest" way to create Tap/Swipe/Drag etc. events using the latest (at this point) Appium Java-client 6.1.0.
I saw different documentations in the Appium site (Tap using TouchActions, Touch using TouchAction), and there is no reference as which should i use (and which is going to be deprecated?).
new TouchAction(driver)
.tap(tapOptions()
.withElement(element(myElement)))
.perform();
new TouchActions(driver)
.singleTap(myElement)
.perform();
It seems that TouchActions is a part of the Selenium project and TouchAction is a part of the Appium, but it does not mean that the Appium is the correct way.
p.s I am using at the moment Chrome/Safari browsers for Android/iOS the testing, but that does not mean that i don't need native apps support for the code.
Thank you for your time
The latest approach to using TouchAction class is to use AndroidTouchAction class instead of TouchAction class as the same is now made generic. That's why you see #SuppressWarnings("rawtypes") being used in the last answer.
This is how you will tap on an element in 6.1.0
For Android:
AndroidTouchAction touch = new AndroidTouchAction (driver);
touch.tap (TapOptions.tapOptions ()
.withElement (ElementOption.element (e)))
.perform ();
For iOS:
IOSTouchAction touch = new IOSTouchAction (driver);
touch.tap (TapOptions.tapOptions ()
.withElement (ElementOption.element (e)))
.perform ();
You want to use TouchAction (Appium).
Below is a section of my code. The first is the general scroll function which takes coordinates as parameters. You generally would not call that function directly, it was meant to be called by other functions, like the scrollDown function I've included below it, that calculates the coordinates and calls the general scroll function.
Hope this helps.
/**
* This method scrolls based upon the passed parameters
* #author Bill Hileman
* #param int startx - the starting x position
* #param int starty - the starting y position
* #param int endx - the ending x position
* #param int endy - the ending y position
*/
#SuppressWarnings("rawtypes")
public void scroll(int startx, int starty, int endx, int endy) {
TouchAction touchAction = new TouchAction(driver);
touchAction.longPress(PointOption.point(startx, starty))
.moveTo(PointOption.point(endx, endy))
.release()
.perform();
}
/**
* This method does a swipe upwards
* #author Bill Hileman
*/
public void scrollDown() {
//The viewing size of the device
Dimension size = driver.manage().window().getSize();
//Starting y location set to 80% of the height (near bottom)
int starty = (int) (size.height * 0.80);
//Ending y location set to 20% of the height (near top)
int endy = (int) (size.height * 0.20);
//x position set to mid-screen horizontally
int startx = (int) size.width / 2;
scroll(startx, starty, startx, endy);
}

React-Native iOS accurate active input position

I have tried to use some the existing solution for iOS keyboard avoiding but none of the existing solutions seems to be working properly with some of my rather complicated views and I am now having to implement my own system. Thus far I have managed to come up with the following which works quite good:
_keyboardDidShow(e) {
const keyboardHeight = e.endCoordinates.height;
const windowHeight = Dimensions.get('window').height;
const keyboardTop = windowHeight - keyboardHeight;
const margin = 10;
const currentlyFocusedField = TextInput.State.currentlyFocusedField();
UIManager.measureInWindow(currentlyFocusedField, (x, y, width, height) => {
const fieldBottom = y + height;
const offset = fieldBottom - keyboardTop + 10;
if (offset > 0) {
this.setState({ offsetY: -1 * offset, offsettingKeyboard: true });
}
});
}
I should mention that these inputs are inside ScrollViews which are themselves in a bunch of other stuff.
The problem is that when the handler is called for a TextInput that has not been scrolled up inside its view, the proper "fieldBottom" position is obtained without adding the height while the height addition is needed for the real position of a field inside a view that has been scrolled.
I cannot decide if this is a bug or if I am missing something. Is there perhaps a way I can check if the input's parent has been scrolled?

Xamarin Pinch To Zoom and Pan Containers leaving their bounds

I have a label with a lot of text that I want to enable pinch-to-zoom and panning gesture recognizers in. I used the recipes from here and then nested them within each other.
https://developer.xamarin.com/guides/xamarin-forms/user-interface/gestures/pinch/
https://developer.xamarin.com/guides/xamarin-forms/user-interface/gestures/pan/
Problem is, both container objects allow you to move the label completely outside of it's normal bounds anywhere within the top level page view (demonstrated in the pictures below).
Any thoughts on how to implement some limits on these? I'm sure it's just placing some limits on the math in the container code, but I haven't found the right thing to change yet.
As you can see in these images, both the pinch-to-zoom container (without panning) and the pan container (without zooming) allow you to alter the control so it goes outside it's bounds.
Initial Layout:
Pinch-To-Zoom only
Panning only
Pinch and Pan
The links above have the container code, but here it is:
PinchToZoomContainer.cs
public class PinchToZoomContainer : ContentView
{
// Pinch Gesture variables
double currentScale = 1;
double startScale = 1;
double xOffset = 0;
double yOffset = 0;
public PinchToZoomContainer ()
{
var pinchGesture = new PinchGestureRecognizer ();
pinchGesture.PinchUpdated += OnPinchUpdated;
GestureRecognizers.Add (pinchGesture);
}
void OnPinchUpdated (object sender, PinchGestureUpdatedEventArgs e)
{
if (e.Status == GestureStatus.Started) {
// Store the current scale factor applied to the wrapped user interface element,
// and zero the components for the center point of the translate transform.
startScale = Content.Scale;
Content.AnchorX = 0;
Content.AnchorY = 0;
}
if (e.Status == GestureStatus.Running) {
// Calculate the scale factor to be applied.
currentScale += (e.Scale - 1) * startScale;
currentScale = Math.Max (1, currentScale);
// The ScaleOrigin is in relative coordinates to the wrapped user interface element,
// so get the X pixel coordinate.
double renderedX = Content.X + xOffset;
double deltaX = renderedX / Width;
double deltaWidth = Width / (Content.Width * startScale);
double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;
// The ScaleOrigin is in relative coordinates to the wrapped user interface element,
// so get the Y pixel coordinate.
double renderedY = Content.Y + yOffset;
double deltaY = renderedY / Height;
double deltaHeight = Height / (Content.Height * startScale);
double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;
// Calculate the transformed element pixel coordinates.
double targetX = xOffset - (originX * Content.Width) * (currentScale - startScale);
double targetY = yOffset - (originY * Content.Height) * (currentScale - startScale);
// Apply translation based on the change in origin.
Content.TranslationX = targetX.Clamp (-Content.Width * (currentScale - 1), 0);
Content.TranslationY = targetY.Clamp (-Content.Height * (currentScale - 1), 0);
// Apply scale factor
Content.Scale = currentScale;
}
if (e.Status == GestureStatus.Completed) {
// Store the translation delta's of the wrapped user interface element.
xOffset = Content.TranslationX;
yOffset = Content.TranslationY;
}
}
PanContainer.cs
public class PanContainer : ContentView
{
double startX, startY;
double x, y;
public PanContainer ()
{
// Set PanGestureRecognizer.TouchPoints to control the
// number of touch points needed to pan
var panGesture = new PanGestureRecognizer ();
panGesture.PanUpdated += OnPanUpdated;
GestureRecognizers.Add (panGesture);
}
void OnPanUpdated (object sender, PanUpdatedEventArgs e)
{
switch (e.StatusType) {
case GestureStatus.Started:
startX = Content.TranslationX;
startY = Content.TranslationY;
break;
case GestureStatus.Running:
// Translate and ensure we don't pan beyond the wrapped user interface element bounds.
//Content.TranslationX = Math.Max (Math.Min (0, x + e.TotalX), -Math.Abs (Content.Width - App.ScreenWidth));// App.ScreenWidth));
//Content.TranslationY = Math.Max (Math.Min (0, y + e.TotalY), -Math.Abs (Content.Height - App.ScreenHeight)); //App.ScreenHeight));
Content.TranslationX = startX + e.TotalX;
Content.TranslationY = startY + e.TotalY;
break;
case GestureStatus.Completed:
// Store the translation applied during the pan
x = Content.TranslationX;
y = Content.TranslationY;
break;
}
}
}
I imagine, on the PanContainer, my issue is in these lines that I had to comment out:
//Content.TranslationX = Math.Max (Math.Min (0, x + e.TotalX), -Math.Abs (Content.Width - App.ScreenWidth));// App.ScreenWidth));
//Content.TranslationY = Math.Max (Math.Min (0, y + e.TotalY), -Math.Abs (Content.Height - App.ScreenHeight)); //App.ScreenHeight));
I changed these to a more simple version because I can't find App.ScreenWidth or .ScreenHeight properties.
The pinch container, however, is just as it was originally in the recipe and still goes outside the bounds.
There is an IsClippedToBounds property that helped me with this issue.
For example:
<PanContainer IsClippedToBounds="true">
<PanContainer.Content>
<Image x:Name="SomeImage" />
</PanContainer.Content>
</PanContainer>
To get pinch and pan, you can either wrap a pinch element in a pan element or vice versa, or you can create a single class with the functions from both the pinch and pan classes. The latter is probably better.
That alone will probably not work exactly as you expect though because the calculations in the pinch and pan functionality are not aware of each other, so if for example you pinch to zoom in then the pan functionality doesn't know that it can now pan further.
This answer is mostly likely very late for your needs, Chet... but, you can simply wrap the whole thing in a ScrollView (which you will appropriately locate and/or size to your needs). That should work as expected.
<ScrollView Grid.Column="2" VerticalOptions="Start">
<PanContainer>
<PanContainer.Content>
<Image x:Name="SomeImage" Aspect="AspectFit" />
</PanContainer.Content>
</PanContainer>
</ScrollView>
Cheers!
Mike

Swipe is not working in ios devices

I have a app that want to allow contacts from a device event. I used
(autoAlertAccept,true) but that won't work for me. I am using appium 1.5.2 and
even I want to swipe that particular contact to chat or call with
that particular contact. When I used a:
driver.manage().timeouts().implicitlyWait(50, TimeUnit.SECONDS);
driver.findElement(By.className("android.widget.ImageButton")).click();
size1 = driver.manage().window().getSize();
System.out.println(size1); int x1 = (int) (size1.width * 0.70);
int x2 = (int) (size1.width * 0.30);
int starty =size1.height / 2;
System.out.println(x1 + x2 + starty);
driver.findElement(By.name("Demo Usr"));
driver.swipe(x1,starty,x2,starty,3000);
I found some exception
ur provided code will swipe the screen from left to right. But to move an element from one position to another, use the below code:
WebElement elem = driver.findElement(By.xpath("ur xpath"));//get element locator as ur wish by accesibility id or xpath.
int startX = elem.getLocation().getX();
int startY = elem.getLocation().getY();
//this will swipe from right to left horizontally
driver.swipe(startX,startY,startX-90,startY,2000);
// use 90 or more according to ur screen position
//this will swipe from left to right horizontally
driver.swipe(startX,startY,startX+90,startY,2000);
//u must have to be sure that this location is within the screen, otherwise u will get an error
If there is any issue let me know.

Access iOS Control Center using appium

I am trying to open the Control Center using appium and the following code:
int halfWidth = driver.manage().window().getSize().width / 2;
int screenHeight = driver.manage().window().getSize().height;
driver.swipe(halfWidth, screenHeight-5, halfWidth, screenHeight-300, 500); // driver is instance of IOSDriver
Instead of opening control centre the app simply draws on the screen upwards from the bottom (using coordinates input). Anyone know how to open Control Center using appium and swipe (or any other way)?
Thanks, Charlie
We can do this. I tried in Appium 1.4.13 and I am able to change settings.
I used below code to change the settings in my iPadAir2.
int height = driver.findElementByClassName("UIAWindow").getSize().getHeight();
int width = driver.findElementByClassName("UIAWindow").getSize().getWidth();
driver.swipe(width-100, height, width-100, height-200, 500);
driver.findElementByAccessibilityId("Wi-Fi").click();
Appium 1.6.5, You can use swipe method, bellow my Python code:
window_size = self.driver.get_window_size() # this returns dictionary
el = self.driver.find_element(*self.configuration.CommonScreen.WEB_VIEW)
action = TouchAction(self.driver)
start_x = window_size["width"] * 0.5
start_y = window_size["height"]
end_x = window_size["width"] * 0.5
end_y = window_size["height"] * 0.5
action.press(el, start_x, start_y).wait(100).move_to(el, end_x, end_y).release().perform()
I am able to toggle the Wifi OFF or turn Airplane mode ON using Appium 1.6.4-beta for iOS
Swipe up from the bottom of the screen
Click continue link
Click the Wifi or Airplane button
Swipe down from middle of screen
But this doesn't appear to be doing anything in the simulator. I have to actually turn off my computers internet connection to disable the internet on the simulator.
#iOSFindBy(xpath = "//XCUIElementTypeSwitch[#name='Wi-Fi']")
private MobileElement WIFI_MODE_BUTTON;
public void disableWifi() {
openToolBarMenu();
//if wifi is on/true then turn it off
if (WIFI_MODE_BUTTON.getAttribute("value") == "true" ) {
Autoscope.tap(WIFI_MODE_BUTTON);
}
closeToolBarMenu();
}
#iOSFindBy(xpath = "//XCUIElementTypeButton[#name='Continue']")
private MobileElement CONTINUE_BUTTON; //continue button on control center
public void openToolBarMenu() {
Autoscope.scrollFromBottomOfScreen();
if (Autoscope.isElementDisplayed(CONTINUE_BUTTON)) {
Autoscope.tap(CONTINUE_BUTTON);
}
}
static public void scrollFromBottomOfScreen() {
TouchAction touchAction = new TouchAction(autoscopeDriver);
int xStartPoint = Math.round(pixelWidth() / 2);
int yStartPoint = pixelHeight();
int yEndPoint = 0 - yStartPoint;
touchAction.press(xStartPoint, yStartPoint).moveTo(0, yEndPoint).release().perform();
}
This code will help in bringing up the Control center, while you are in your app, you can perform all the operations which are available in the Control Center
new TouchAction(DriverConfig.getInstance().getDriver()).press(point(250, 735)).waitAction(waitOptions(Duration.ofSeconds(3))).moveTo(point(250, -460)).release()
.perform();
C#: iOS 13.x
//Opening control center
var size = Driver.Manage().Window.Size;
var height = size.Height;
var width = size.Width;
var touchAction = new TouchAction(Driver);
touchAction.Press(width - 100, height).Wait(1000).MoveTo(width - 100, height - 200).Release().Perform();
//Clicking the WiFi button
Driver.FindElementByAccessibilityId("wifi-button").Click();
//Checking if WiFi enabled or not
var myElement = Driver.FindElementByAccessibilityId("wifi-button");
var result = myElement.GetAttribute("label");
if(!result.Contains("Wi-Fi, Not Connected") && !result.Equals("Wi-Fi"))
{
// WiFi connected
}
else
{
// WiFi Not connected
}
The idea is to simulate the swipe action you use to open Control Center on the corresponding iOS device. My device is iPhone 11 so it is swipe from the top right(to the right of the notch) down. My code is to swipe from position(x,y) (80% width, 0) to (80% width, 50% height)
Dimension size = getScreenSize();
int x = (size.getWidth() / 5) * 4;
int startY = 0;
int endY = size.getHeight() / 2;
new TouchAction(driver).press(PointOption.point(x, startY))
.waitAction(WaitOptions.waitOptions(Duration.ofSeconds(1)))
.moveTo(PointOption.point(x, endY))
.release().perform();
Ok so after a fair amount of investigation it seems to me that this is not possible. If you really need this functionality then I think a tool like eggplant might be appropriate.

Resources