Appium on App Center can't switch between iOS and Android (Java) - java

When I write appium tests specifically to run on App Center (and therefore must use the custom 'Enhanced Driver'), it looks like I can only declare driver as either an EnhancedAndroidDriver type, or an EnhancedIOSDriver type.
public EnhancedAndroidDriver<MobileElement> driver;
// public EnhancedIOSDriver<MobileElement> driver; <----- can't declare same variable twice
public AppiumDriver<MobileElement> getDriver() throws IOException {
String PLATFORM_NAME = System.getenv("XTC_PLATFORM");
if (PLATFORM_NAME.equals("Android")) {
EnhancedAndroidDriver<MobileElement> androiddriver = Factory.createAndroidDriver(new URL("http://localhost:4723/wd/hub"), caps);
driver = androiddriver;
} else if (PLATFORM_NAME.equals("iOS")) {
EnhancedIOSDriver<MobileElement> iosdriver = Factory.createIOSDriver(new URL("http://localhost:4723/wd/hub"), caps);
driver = iosdriver; <---- compiler error, wrong type
}
return driver;
}
I want to run a simple test in a single file that will run on both platforms, but it seems I must choose either android or ios for that file to run on. How do I avoid duplicating all my test files? I am using react native and my app is basically identical on both platforms. I have done something similar with the regular AppiumDriver.
Any suggestions to programmatically switch which type 'EnhancedIOS/AndroidDriver' the variable driver refers to in Java??
With the regular AppiumDriver, I can do this:
private static AppiumDriver<MobileElement> driver;
public AppiumDriver<MobileElement> getDriver() throws IOException {
if (PLATFORM_NAME.equals("Android")) {
driver = new AndroidDriver<MobileElement>(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
} else if (PLATFORM_NAME.equals("iOS")) {
driver = new IOSDriver<MobileElement>(new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
}
return driver;
}
And then use the driver in the tests generically. But it seems impossible to take this approach with the App Center Enhanced drivers because they don't share a common type (or I don't know enough about Java to figure it out). Is there any way to work around this??

I ended up writing an Interface that wrapped the driver, which instantiated and operated on the platform specific driver for each driver method I use, and allowed me to use the common name driver to refer to it throughout the rest of the test framework.
I passed in the platform name to the Interface when I instantiated the interface. The platform name was read as an environment variable I set in the App Center branch build settings.

Related

Is there a non-remote way to specify geckodriver location when you cannot specify it by System property or Path?

In my application I cannot set geckodriver executable location using System.setProperty and I cannot set it in the path.
Why? Because my app is multi-tenant... and each tenant has their own directory where Firefox and Geckodriver is copied and ran. This is due to bugs in the Firefox + Geckodriver, where infinite javascript loops and several other situations cause Firefox to hang until manual kill. Sometimes quit fails to kill things completely as well. So we need to supply a custom geckodriver location within the JVM per-tenant. Thus the problem.
So I am instead using:
driverService = new GeckoDriverService.Builder()
.usingDriverExecutable(new File(geckoDriverBinaryPath))
.build();
driverService.start();
RemoteWebDriver driver = new RemoteWebDriver(driverServiceUrl, capabilities);
But that is making me use a RemoteWebDriver when I am not remote.
Is there a better way to do this?
Rather than calling start() on the FirefoxDriverService object, why not simply use the FirefoxDriver constructor that takes the service?
driverService = new GeckoDriverService.Builder()
.usingDriverExecutable(new File(geckoDriverBinaryPath))
.build();
WebDriver driver = new FirefoxDriver(driverService);
As the questions stands it is still too broad. There are some unknowns: How are you running this? JUnit?, Maven?, Jenkins? And I am still not clear where this per-tenat geckoDriverBinaryPath comes from and how it is passed around.
What is wrong with just using:
System.setProperty("webdriver.gecko.driver", geckoDriverBinaryPath);
You could set an environment variable in your OS. Something like export geckoDriverBinary=/some/path and then in your code read it back using:
String geckoDriverBinaryPath = System.getenv("geckoDriverBinary");
System.setProperty("webdriver.gecko.driver", geckoDriverBinaryPath);
...
If you are running it from command line, either straight up or using Maven, you could pass the variable in like -DgeckoDriverBinaryPath=/some/path and then in your code read it back using:
String geckoDriverBinaryPath = System.getProperty("geckoDriverBinary");
System.setProperty("webdriver.gecko.driver", geckoDriverBinaryPath);
...
If the different tenants have the path fixed, you could write a utility function that would detect which tenant it is being run on, and set the property accordingly.
This answer is probably going to get closed as not-answer, but more of a discussion. :(

Compile java code at runtime

I have a java class that is used to perform login action using selenium. There are currently 10+ different login types and as such there is a lot of if else involved which looks bad and is not efficient.
Eg:
if (logintype == 1 )
{
driver.findElement(By.id("username")).clear();
driver.findElement(By.id("username")).sendKeys(username);
driver.findElement(By.id("password")).clear();
driver.findElement(By.id("password")).sendKeys(password);
driver.findElement(By.id("signin")).click();
}
else if (logintype ==2 )
{
driver.findElement(By.id("username")).clear();
driver.findElement(By.id("username")).sendKeys(username);
driver.findElement(By.id("password")).clear();
driver.findElement(By.id("password")).sendKeys(password);
driver.findElement(By.id("signin")).click();
}
...........
...........
Other than code not being efficient the new code needs to be written, pushed and the server needs to be restarted every time a new login module is added.
I wanted to see if i can get the logic for login can be stored in db and if it can be compiled at runtime. I found groovy shell but i dont know how to get the results back to my class file. Also running groovy shell would require a lot of code changes. Is it possible in java
public class ExecuteAuth implements Runnable{
private WebDriver driver;
driver = new FirefoxDriver(firefoxBinary, profile, cap);
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
driver.manage().window().maximize();
//MongoDB code
DBCursor dbObjects = loginCollection.find();
while (dbObjects.hasNext()) {
DBObject dbObject = dbObjects.next();
loginModule.add(new LoginModule((BasicDBObject) dbObject));
String loginType = (String) dbObject.get("loginType")
String script;
if (loginType.equals("1")) {
script = (String) dbObject.get("script")
}
}
GroovyShell shell = new GroovyShell ();
shell.evaluate(script);
RUN REST OF THE LOGIN LOGIC AFTER THE CODE IS EVALUATED
}
I strongly advise against that approach. You are opening a door to bad code be injected in your application. Another way could be upload to your server your new jars and take advantage of class loader to load classes at runtime:
How should I load Jars dynamically at runtime?
Also, you have alternatives to avoid if-else's: usage of interfaces and factory methods are the way to go, imho. And put your login's implementations on different classes implementing a Login interface, for example.
Factory method design pattern:
http://www.oodesign.com/factory-method-pattern.html
http://www.javaworld.com/article/2077386/learn-java/factory-methods.html

How to locate iOS Device model?

My question is as follows: How can I locate the device model name, the reason is as follows. I am writing Java code for Appium, and would like to create a certain logic gate that deals with different model types that has test cases suited for each particular device's dimensions.
I tried reading different solutions, some involving Android figuring maybe they could offer some helpful pointers. However, this was not the case. System.getProperties().list(System.out); for example, which I hoped could provide me the solution needed, was not the answer either. Though I have expected as much.
I would use capabilities.getPlatform(); However, this is not valid since capabilities by default are null, and thus getPlatform() would simply return to me the name that I had to pass in manually. At this point, I am suspecting there may not be a way to do this in Java(?) I did find a code that was done in JavaScript, but it was for checking the type of phone that connected to the website (link: http://www.abeautifulsite.net/detecting-mobile-devices-with-javascript/)
Help is much appreciated. Below is my #BeforeTest method:
#BeforeTest
public void setUp() throws FileNotFoundException, IOException, IllegalMonitorStateException
{
//File app = new File("/Users/first.last/Library/Developer/Xcode/DerivedData/iPhone-aowtfpozolcnpigbxzxxxxkyiubq/Build/Products/Debug-iphonesimulator/TheWeather.app");
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, "iOS");
//8.4
capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, "8.4");
//Iphone 6-(storytelling)-igor
capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Iphone 6-(storytelling)-name");
//capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath());
capabilities.setCapability("bundleID", "com.weather.TWC");
//70cb6f4fd5e313b16742c083ccbc1897d82b094d
capabilities.setCapability("udid", "70cb6f4fd5e313b16742c083ccbc1897d82b094d");
driver = new IOSDriver(new URL("http://127.0.0.1:4723/wd/hub"),capabilities);
driver = (IOSDriver) new Augmenter().augment(driver);
System.out.println(capabilities.getCapability(MobileCapabilityType.DEVICE_NAME));
logger.info("Environment: iOS 8.4 and iPhone 6 plus device");
}
I would use the parameters that get passed to desired capabilities.
But as I understand in your case this is not an option so I would suggest to launch instruments -s devices command from the code and parse its output matching the particular UDID. I know this is a "dirty" approach but I have not came across better options. Good luck!

Determine Type of Selenium Driver in Java

I'm working on building out a testing framework for some sites using selenium webdriver, and my goal is to have a number of drivers running the same tests concurrently (aka a firefoxdriver, an internetexplorerdriver, and a chromedriver all running at the same time with some shared resources). However, I'm having trouble with logging which driver is doing what. I'm passing the drivers through a lot of my code, but as far as I can tell a webdriver has no knowledge of what specific type of driver it is. Is there any way to ask a webdriver element what it was instantiated as?
You can use instanceof like
if( driver instanceof FirefoxDriver) {
System.out.println("Firefox it is!!");
}
else if( driver instanceof ChromeDriver) {
System.out.println("Chrome it is!!");
}
// and so on
For more details : What is the 'instanceof' operator used for?
/******************************************************************************************
* Name: getBrowserDetails | Description: Gets Browser Name and Version
******************************************************************************************/
public String getBrowserDetails() throws Exception {
Capabilities caps = ((RemoteWebDriver)BaseTest.driver).getCapabilities();
String browserName = caps.getBrowserName();
String browserVersion = caps.getVersion();
String browser = (browserName + " " + browserVersion).toUpperCase();
return browser;
}
If using instanceof, be sure to also consider org.openqa.selenium.WrapsDriver so as to handle EventFiringWebDriver.

Make use of a java object in Perl

Scenario :
there is a java class as given below :
public class TestSelenium {
public void googleTest() throws Exception {
WebDriver driver = new InternetExplorerDriver();
driver.get("http://www.google.com/webhp?complete=1&hl=en");
}
}
There is a perl program which makes use of Inline::Java module to call the googleTest of java class written above. Perl program looks like.
use warnings;
use Selenium::Remote::Driver;
use Inline Java => 'STUDY',
CLASSPATH => 'C:\selenium\selenium-java-2.37.0\selenium-2.37.0\libs\selenium-java-2.37.0.jar;C:\selenium\SeleniumTestPoc\bin\MyJar.jar;C:\selenium\selenium-java-2.37.0\selenium-2.37.0\libs\selenium-server-standalone-2.37.0.jar',
STUDY => ['TestSelenium'];
$test= TestSelenium->new;
$test->googleTest;
Now the above Perl code will open IExplorer and go to google.com page. In my Perl program further to $test->googleTest; I want to make use of same browser that was opened by java(WebDriver driver = new InternetExplorerDriver();) and perform a search for text "Cheese".
Question is, can the object of WebDriver class("driver" in this case) be further used in my Perl program so that I can use same browser and perform different UI operations on it in Perl?
I'm assuming that if you bring the WebDriver variable outside of the googleTest() method (making it available for all methods in the class) then you can write other methods in TestSelenium that reference the same WebDriver which you can then call in perl.
Modify TestSelenium to something like this:
public class TestSelenium {
private WebDriver driver = new InternetExplorerDriver();
public void googleTest() throws Exception {
driver.get("http://www.google.com/webhp?complete=1&hl=en");
}
public void searchForCheese() throws Exception {
// Your actions here can still reference the driver
}
}
And then after you call $test= TestSelenium->new;
Performing $test->googleTest; and $test->searchForCheese; should theoretically reference the same driver and in turn the same browser window.
EDIT :
If you want to pass the driver object back to perl try something like this.
Change the googleTest() java method to return the driver once it's done with the method (I will be using your original method but both should work):
public WebDriver googleTest() throws Exception {
WebDriver driver = new InternetExplorerDriver();
driver.get("http://www.google.com/webhp?complete=1&hl=en");
return driver;
}
And then I think you can hook onto the driver handle in perl via $mydriver= $test->goggleTest;
I assume you'll then be able to access the WebDriver hooks via $mydriver

Categories