Appium : xPath without text not identifying the object - appium

Issue description: Using xpath with text description works fine, but using xpath having id is not working.
[Sensitive parts being masked in picture and code]
I'm trying to perform a mock project on a mobile app with latest appium version.
Having following dependency in my pom file
java-client: 8.1.1 |
selenium-java: 4.2.0
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>8.1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.2.0</version>
</dependency>
Below is the screen shot of the app, where i'm trying to identify text. "Hey Ram,"
Using xpath option in appium studio,i'm finding both the xpath.
Copy Unique Xpath ==> //*[#text='Hey Ram,']
Copy Unique Xpath (skip text) ==> //*[#id='dialog_fr_tv_title']
I'm identifying the the text using appium inspector filter and it clearly identifying uniquely.
Using option Copy Unique Xpath (xpath = //*[#text='Hey Ram,'])
Using option Copy Unique Xpath (skip text) (xpath= //*[#id='dialog_fr_tv_title'])
This clearly shows, both xpath one with text, and another with id is clearly identifying the element uniquely.
I have written the following Java code, where i try to extract the text and print the value.
package basics;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.SupportsLegacyAppManagement;
import io.appium.java_client.android.AndroidDriver;
public class SimpleAppiumClass {
#SuppressWarnings({ "null", "deprecation" })
public static void main(String[] args) {
AppiumDriver driver=null;
//Set the Desired Capabilities
DesiredCapabilities caps = new DesiredCapabilities();
//caps.setCapability("udid", "emulator-5554"); //Give Device ID of your mobile phone
caps.setCapability("udid", "d278263c");
caps.setCapability("platformName", "Android");
caps.setCapability("platformVersion", "11");
caps.setCapability("automationName", "UiAutomator2");
caps.setCapability("appPackage", "com.xx.vxx.xxxxxx");
caps.setCapability("appActivity", "com.xx.vxx.xxxxxx.view.activity.SVSplashScreenActivity");
caps.setCapability("noReset", "true");
//Instantiate Appium Driver
try {
driver = new AndroidDriver(new URL("http://0.0.0.0:4723/wd/hub"), caps);
driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
WebElement elementId = driver.findElement(By.id("dialog_fr_tv_title"));
System.out.println("id text: "+elementId.getText());
WebElement elementTextXpath = driver.findElement(By.xpath("//*[#text='Hey Ram,']"));
System.out.println("using text(xpath)=: "+elementTextXpath.getText());
//*[#text='Hey Ram,']
WebElement elementIdXpath = driver.findElement(By.xpath("//*[#id='dialog_fr_tv_title']"));
System.out.println("using id(xpath)=: "+elementIdXpath.getText());
((SupportsLegacyAppManagement) driver).closeApp();
} catch (MalformedURLException e) {
System.out.println(e.getMessage());
((SupportsLegacyAppManagement) driver).closeApp();
}
System.out.println("---End of execution---");
}
}
First time, i'm using By.id [driver.findElement(By.id("dialog_fr_tv_title"))] print me "Hey Ram," text as expected.
Second time, using By.xpath [driver.findElement(By.xpath("//*[#text='Hey Ram,']"))] print me also 'Hey Ram?," text as expected. This time xpath is written using text option.
Thrid time, using By.xpath [driver.findElement(By.xpath("//*[#id='dialog_fr_tv_title']"))] throws me error "Exception in thread "main" org.openqa.selenium.NoSuchElementException: "
Not only for this element, trying to give xpath for button "Continue with mobile number" throws same error.
For button without text: //*[#id='dialog_fr_mobile_btn'] --> throws error
For buttom with text: //*[#text='Continue with Mobile Number'] --> works fine.
Execution logs
id text: Hey Ram,
using text(xpath)=: Hey Ram,
Exception in thread "main" org.openqa.selenium.NoSuchElementException: An element could not be located on the page using the given search parameters.
For documentation on this error, please visit: https://selenium.dev/exceptions/#no_such_element
Build info: version: '4.3.0', revision: 'a4995e2c09*'
System info: host: 'LAPTOP-0LQSGR9A', ip: '172.31.98.220', os.name: 'Windows 11', os.arch: 'amd64', os.version: '10.0', java.version: '18.0.1.1'
Driver info: io.appium.java_client.android.AndroidDriver
Command: [f30292c6-4f04-4d4f-939c-a8d98c801efb, findElement {using=xpath, value=//*[#id='dialog_fr_tv_title']}]
Capabilities {appium:appActivity: com.tv.v18.viola.view.activ..., appium:appPackage: com.tv.v18.viola, appium:automationName: UiAutomator2, appium:databaseEnabled: false, appium:desired: {appActivity: com.tv.v18.viola.view.activ..., appPackage: com.tv.v18.viola, automationName: UiAutomator2, noReset: true, platformName: android, platformVersion: 11, udid: d278263c}, appium:deviceApiLevel: 28, appium:deviceManufacturer: OnePlus, appium:deviceModel: ONEPLUS A5000, appium:deviceName: d278263c, appium:deviceScreenDensity: 380, appium:deviceScreenSize: 1080x1920, appium:deviceUDID: d278263c, appium:javascriptEnabled: true, appium:locationContextEnabled: false, appium:networkConnectionEnabled: true, appium:noReset: true, appium:pixelRatio: 2.375, appium:platformVersion: 9, appium:statBarHeight: 57, appium:takesScreenshot: true, appium:udid: d278263c, appium:viewportRect: {height: 1863, left: 0, top: 57, width: 1080}, appium:warnings: {}, appium:webStorageEnabled: false, platformName: ANDROID}
Session ID: f30292c6-4f04-4d4f-939c-a8d98c801efb
at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:67)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:483)
at org.openqa.selenium.remote.codec.w3c.W3CHttpResponseCodec.createException(W3CHttpResponseCodec.java:200)
at org.openqa.selenium.remote.codec.w3c.W3CHttpResponseCodec.decode(W3CHttpResponseCodec.java:133)
at org.openqa.selenium.remote.codec.w3c.W3CHttpResponseCodec.decode(W3CHttpResponseCodec.java:53)
at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:184)
at io.appium.java_client.remote.AppiumCommandExecutor.execute(AppiumCommandExecutor.java:180)
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:569)
at org.openqa.selenium.remote.ElementLocation$ElementFinder$2.findElement(ElementLocation.java:162)
at org.openqa.selenium.remote.ElementLocation.findElement(ElementLocation.java:60)
at org.openqa.selenium.remote.RemoteWebDriver.findElement(RemoteWebDriver.java:387)
at org.openqa.selenium.remote.RemoteWebDriver.findElement(RemoteWebDriver.java:379)
at basics.SimpleAppiumClass.main(SimpleAppiumClass.java:40)
Let me know if anything I missed or issue with latest version (known issue), any work around. Using text in some scenario will not be good thing if I try to code.
Regards,
Ramkumar

Related

Dart How to load file in runtime

I'm writing a discord bot using the nyxx library and want use dynamic file import for load command info and handler. But, after 5 hours of searching with Google, I didn't find anything to help me do that.
In Node.js, I can use require() or import() for it: Does the dart have something like that?
A small code snippet, showing what I want do:
this.commands = new Collection();
fs.readdirSync('./src/commands').filter(( f ) => f.endsWith( '.js' )).forEach((file) => {
const command = require(`../commands/${file}`);
this.commands.set( command.info.name, command );
});
Is it possible to do this or not? (I don't like to write many imports for commands and register it in lib.)
You can in theory use Isolate.spawnUri to spawn external Dart programs to run in its own Isolate instances that can then communicate back to the main program using SendPort.
It does, however, come with some limitations. E.g. it is very limited what types of objects you can send though SendPort when using spawnUri since the two programs does not share any type information (compared to Isolate.spawn which does allow you to send your own custom types). The documented types you can send can be found here:
Null
bool
int
double
String
List or Map (whose elements are any of these)
TransferableTypedData
SendPort
Capability
https://api.dart.dev/stable/2.17.6/dart-isolate/SendPort/send.html
But it does allow us to make some kind of protocol and you can create some helper class around this to handle the conversion of a known object structure into e.g. Map<String, Object>.
A small example that works with Dart VM would be:
Your command implemented as: command.dart
import 'dart:isolate';
void main(List<String> arguments, Map<String, Object> message) {
final userName = message['username'] as String;
final sendPort = message['port'] as SendPort;
sendPort.send('Hi $userName. '
'You got a message from my external command program!');
}
Your server that calls your command: server.dart
import 'dart:isolate';
void main() {
final recievePort = ReceivePort();
recievePort.listen((message) {
print('Got the following message: $message');
recievePort.close();
});
Isolate.spawnUri(Uri.file('command.dart'), [], {
'username': 'julemand101',
'port': recievePort.sendPort,
});
}
If running this with: dart server.dart you, hopefully, get:
Got the following message: Hi julemand101. You got a message from my external command program!
If you want to compile your application, you can do so by doing the following. You need to compile the command.dart, since a compiled Dart program does not understand how to read Dart code.
dart compile exe server.dart
dart compile aot-snapshot command.dart
You should here change Uri.file('command.dart') to Uri.file('command.aot') since the file-extension for aot-snapshot are .aot.
If everything works, you should be able to see:
> .\server.exe
Got the following message: Hi julemand101. You got a message from my external command program!

How to get device IP in Dart/Flutter

I am currently writing an app where the user needs to know the IP address of their phone/tablet. Where would I find this information?
I only want to know what the local IP address is, such as, 192.168.x.xxx and NOT the public IP address of the router.
So far, I can only seem to find InternetAddress.anyIPv4 and InternetAddress.loopbackIPv4. The loopback address is not what I want as it is 127.0.0.1.
I guess you mean the local IP of the currently connected Wifi network, right?
EDITED
In this answer, I used to suggest using the NetworkInterface in 'dart:io', however NetworkInterface.list is not supported in all Android devices (as pointed out by Mahesh). The wifi package provides that, but later this was incorporated to the flutter's connectivity plugin. In Oct/2020 the methods for that were moved from the connectivity to the wifi_info_flutter plugin, and in 2021 that package was discontinued in favor of network_info_plus.
So just go for network_info_plus and call await NetworkInfo().getWifiIP().
By the way, you may also want to check if Wifi is available using the connectivity_plus plugin in flutter/plugins. Here's an example of how to check if wifi is available.
This provides the IP addresses of all interfaces
import 'dart:io';
...
Future printIps() async {
for (var interface in await NetworkInterface.list()) {
print('== Interface: ${interface.name} ==');
for (var addr in interface.addresses) {
print(
'${addr.address} ${addr.host} ${addr.isLoopback} ${addr.rawAddress} ${addr.type.name}');
}
}
}
See also
https://api.dartlang.org/stable/2.0.0/dart-io/NetworkInterface-class.html
I was searching for getting IP address in flutter for both the iOS and android platforms.
As answered by Feu and Günter Zöchbauer following works on only iOS platform
NetworkInterface.list(....);
this listing of network interfaces is not supported for android platform.
After too long search and struggling with possible solutions, for getting IP also on android device, I came across a flutter package called wifi, with this package we can get device IP address on both iOS and android platforms.
Simple sample function to get device IP address
Future<InternetAddress> get selfIP async {
String ip = await Wifi.ip;
return InternetAddress(ip);
}
I have tested this on android using wifi and also from mobile network.
And also tested on iOS device.
Though from name it looks only for wifi network, but it has also given me correct IP address on mobile data network [tested on 4G network].
#finally_this_works : I have almost given up searching for getting IP address on android and was thinking of implementing platform channel to fetch IP natively from java code for android platform [as interface list was working for iOS]. This wifi package saved the day and lots of headache.
It seems that Dart doesn't have a solution to get your own ip address. Searching for a solution I came across the rest api https://ipify.org to get my public address. Hope it helps.
Here is another way.
Future<InternetAddress> _retrieveIPAddress() async {
InternetAddress result;
int code = Random().nextInt(255);
var dgSocket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, 0);
dgSocket.readEventsEnabled = true;
dgSocket.broadcastEnabled = true;
Future<InternetAddress> ret =
dgSocket.timeout(Duration(milliseconds: 100), onTimeout: (sink) {
sink.close();
}).expand<InternetAddress>((event) {
if (event == RawSocketEvent.read) {
Datagram dg = dgSocket.receive();
if (dg != null && dg.data.length == 1 && dg.data[0] == code) {
dgSocket.close();
return [dg.address];
}
}
return [];
}).firstWhere((InternetAddress a) => a != null);
dgSocket.send([code], InternetAddress("255.255.255.255"), dgSocket.port);
return ret;
}
here, you can use this package https://pub.dev/packages/dart_ipify available on pub.dev,
import 'package:dart_ipify/dart_ipify.dart';
void main() async {
final ipv4 = await Ipify.ipv4();
print(ipv4); // 98.207.254.136
final ipv6 = await Ipify.ipv64();
print(ipv6); // 98.207.254.136 or 2a00:1450:400f:80d::200e
final ipv4json = await Ipify.ipv64(format: Format.JSON);
print(ipv4json); //{"ip":"98.207.254.136"} or {"ip":"2a00:1450:400f:80d::200e"}
// The response type can be text, json or jsonp
}
To get device connected network/WiFi IP you can use network_info_plus package.
For android add the following permissions ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION in the AndroidManifest.xml file
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
IOS permissions are a little more complicated so please read what is requested in the library.
Add package to pubspec.yaml
dependencies:
network_info_plus: ^1.0.2
Than you can get the current IP by executing
String? wifiIP = await NetworkInfo().getWifiIP();
In my recent app I have a requirement to get user's Ip address and then I found this packgage useful. https://pub.dev/packages/get_ip
Here is How I use it.
_onLoginButtonPressed() async {
String ipAddress = await GetIp.ipAddress;
print(ipAddress); //192.168.232.2
}
You can use the wifi package for getting the local IP Address (for eg. 192.168.x.x). (as The NetworkInterface.list (from dart:io) is no longer supporting Android from 7.0 and above).
Use the wifi package :
import 'package:wifi/wifi.dart';
You can retrieve the IP Address like this:
Future<String> getIp() async {
String ip = await Wifi.ip;
return ip;
}
You can display it on a Text widget using FutureBuilder:
FutureBuilder<String>(
future: getIp(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else {
if (snapshot.hasError)
return Center(child: Text('Error: ${snapshot.error}'));
else
return Center(child: Text('IP Address is : ${snapshot.data}')); //IP Address
}
},
);
Have you tried the device_info package?
There is an example on querying device information in https://pub.dartlang.org/packages/device_info#-example-tab-
You can use the network_info_plus plugin to get various info about the wifi connection
Eg:
import 'package:network_info_plus/network_info_plus.dart';
final NetworkInfo _networkInfo = NetworkInfo();
wifiIPv4Addr = await _networkInfo.getWifiIP();
For further refernce you can refer to the examples given under the official page
You can use the following package: https://pub.dev/packages/network_info_plus
And here is the page giving more detail about how to use the package https://plus.fluttercommunity.dev/docs/network_info_plus/usage/#using-network-info-plus
Essentially...if you have the package installed, you would use something like
import 'package:network_info_plus/network_info_plus.dart';
final info = NetworkInfo();
var wifiIP = await info.getWifiIP();
This package comes with some additional methods that could prove quite useful ;)
NOTE: This package is not supported on flutter web.
The flutter network_info_plus package provides access to Wifi information such as Wifi IP and name, but the problem is that it doesn't work when the phone HotSpot is on and the phone gets its IP from its own HotSpot.
The code below works for any condition and returns local IP whether it comes from phone HotSpot or another router:
import 'package:flutter/services.dart';
MethodChannel _channel = const MethodChannel('get_ip');
String ip = await _channel.invokeMethod('getIpAdress');
Node that in case you got No implementation found for method error, you should add get_ip package.
I hope it helps.
If we want to find the public facing IP address of a browser we can use dart_ipify The example in the documentation worked perfectly for me.
Import the package:
import 'package:dart_ipify/dart_ipify.dart';
Add this code to _incrementCounter():
final ipv4 = await Ipify.ipv4();
print(ipv4);

what are the prerequisites to perform mobile testing on sauce labs?

i am new to Appium. I want to perform mobile testing on android and i OS app using appium tool on sauce labs.
I want to know what are the pre-requisites, and how to write the scripts(in java) and how exactly the flow goes.
can anybody help me out??
Thanks a lot in advance.. :-)
package com.saucelabs;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.openqa.selenium.Platform;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import java.net.URL;
import static junit.framework.Assert.assertEquals;
/**
* Simple {#link RemoteWebDriver} test that demonstrates how to run your Selenium tests with Sauce OnDemand.
* *
* #author Ross Rowe
*/
public class DemoScript {
private WebDriver driver;
#Before
public void setUp() throws Exception {
DesiredCapabilities caps = DesiredCapabilities.android();
caps.setCapability("browserName", "");
caps.setCapability("platformVersion", "4.4");
caps.setCapability("appiumVersion", "");
caps.setCapability("platformName", "Android");
caps.setCapability("deviceName", "Android Emulator");
caps.setCapability("device-orientation", "portrait");
/* DesiredCapabilities capabillities = DesiredCapabilities.iphone();
capabillities.setCapability("version", "5.0");
capabillities.setCapability("platform", Platform.MAC);*/
this.driver = new RemoteWebDriver(
new URL("http://**********************"),caps);
}
#Test
public void basic() throws Exception {
driver.get("http://www.amazon.com/");
assertEquals("Amazon.com: Online Shopping for Electronics, Apparel, Computers, Books, DVDs & more", driver.getTitle());
}
#After
public void tearDown() throws Exception {
driver.quit();
}
}
i am new to Appium. I want to perform mobile testing on android and i OS app using appium tool on sauce labs.
I want to know what are the pre-requisites, and how to write the scripts(in java) and how exactly the flow goes.
can anybody help me out??
Thanks a lot in advance.. :-)
Are there any changes required in the code?
Is appium server should be started before running the script?

How to switch from one app to another app at run time

Is there any possibility to switch from one application to another application at run time using Appium.
Thanks
Finally I found accurate answer, May it will be usefull for some one
source https://www.linkedin.com/grp/post/6669152-6027319885992841219?trk=groups-post-b-title
// App1 capabilities
String calculatorAppPackageName="com.android.calculator2";
String calculatorAppActivityName="com.android.calculator2.Calculator";
// App2 capabilities
String settingsAppPackageName="com.android.settings";
String settingsAppActivityName="com.android.settings.Settings";
#Before
public void setUp() throws MalformedURLException
{
DesiredCapabilities capabilities = DesiredCapabilities.android();
capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "Appium");
capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "192.168.215.101:5555");
capabilities.setCapability(MobileCapabilityType.APP_PACKAGE, calculatorAppPackageName);
capabilities.setCapability(MobileCapabilityType.APP_ACTIVITY, calculatorAppActivityName);
driver = new AndroidDriver(new URL("http://localhost:4723/wd/hub"), capabilities);
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
#Test
public void testApp() throws InterruptedException, MalformedURLException
{
//Perform calculation in calculator
driver.findElement(By.name("4")).click();
driver.findElement(By.name("×")).click();
driver.findElement(By.name("3")).click();
driver.findElement(By.name("=")).click();
//launch settings App
driver.startActivity(settingsAppPackageName, settingsAppActivityName);
//Switch OFF WIFI
driver.findElement(By.id("com.android.settings:id/switchWidget")).click();
//Re launch calculator App
driver.startActivity(calculatorAppPackageName, calculatorAppActivityName);
//Validate results
String result = driver.findElement(By.className("android.widget.EditText")).getText();
System.out.println("Result : " + result);
Assert.assertEquals("Incorrect Result", "12", result);
}
You can change applications by re-instantiating the webdriver with the new application's attributes.
driver = webdriver.Remote(appiumUrl,dcapabilityApp1)
[Your tests]
driver = webdriver.Remote(appiumUrl,dcapabilityApp2)
[New app tests]
As long as you don't close/disconnect the emulator/simulator/device then your user data will be maintained.
You can use:
driver.startActivity(settingsAppPackageName, settingsAppActivityName);
to invoke another app withing the same code.
Going through question , i have an assumption that it might break your driver current session.and if the driver command failed there is no fall back for it.
Can't it been done with adb command .
One can use above solution or might use abd command
adb shell am start -d <YOUR_ACTIVITY_NAME>
And this will open directly appActivity without fail.
driver.startActivity() method can be used to switch between apps. For more details how it works you can check below video.
Watch "Appium Tutorial- Switching between apps (Contact and SMS)" on YouTube
https://youtu.be/sH1bHeDDj8U

The class JsObject does not have a constructor jsify

I am trying to execute a map example using Dart. But I am getting an error
The class JsObject does not have a constructor jsify
The dart code that I am using is
library google_maps;
import 'dart:html' show query;
import 'dart:js' show context, JsObject;
void main() {
// The top-level getter context provides a JsObject that represents the global
// object in JavaScript.
final google_maps = context['google']['maps'];
// new JsObject() constructs a new JavaScript object and returns a proxy
// to it.
var center = new JsObject(google_maps['LatLng'], [-34.397, 150.644]);
var mapTypeId = google_maps['MapTypeId']['ROADMAP'];
// new JsObject.jsify() recursively converts a collection of Dart objects
// to a collection of JavaScript objects and returns a proxy to it.
var mapOptions = new JsObject.jsify({
"center": center,
"zoom": 8,
"mapTypeId": mapTypeId
});
// Nodes are passed though, or transferred, not proxied.
new JsObject(google_maps['Map'], [query('#map-canvas'), mapOptions]);
}
The pubspec.yaml is
name: google_maps_api_with_dart_js
description: An app that displays a location using the JavaScript
Google Maps API that is called using the dart:js library.
dependencies:
browser: ">=0.9.0 <0.10.0"
environment:
sdk: ">=0.8.10+6 <2.0.0"
I got this resolved by shifting to new version
Dart Editor version 1.0.0_r30188 (STABLE)
Dart SDK version 1.0.0.3_r30188
Now it's all working fine.
Thank you!

Resources