Automate launching Safari from a iOS mobile app using appium - ios

I am doing some automation with Appium on a iOS mobile app.
I need to:
open the app
do some tasks
open safari
I looked around how to do it but I've been reading that it's impossible due to a limitation in apple's framework, it doesn't allow you to sent commands to more than one app per session.
Does anyone know a way around this? Or if what I read is just not 100% true.

it doesn't allow you to sent commands to more than one app per session
Thats true, but you can run 2 sessions in a single test:
create instance of appium driver with app-based capabilities
do what you need in the app
quit driver
create instance of appium driver with browser-based capabilities
do what you need in the safari
quit driver
In a quick way it may look like:
#Test
public void testBothAppAndSafari() throws MalformedURLException {
URL appiumServerUrl = new URL("<your appium server host>");
DesiredCapabilities appCaps = new DesiredCapabilities();
// put required native app capabilities in appCaps
DesiredCapabilities safariCaps = new DesiredCapabilities();
// put required safari capabilities in safariCaps
IOSDriver driver = new IOSDriver(appiumServerUrl, appCaps);
driver.findElement(<locator for element in native app>).click();
// do whatever you want with mobile app
driver.quit();
driver = new IOSDriver(appiumServerUrl, safariCaps);
driver.findElement(<locator for element in web>).click();
// do whatever you want in safari
driver.quit();
}

You can use following approach,
Created two setup one for app and other for safari.
First launch application and perform task
clear first session
Created again new Appium object for safari ( call second setup )
Perform browser activity
Close safari appium session

You also can follow my approach without quit driver.
Go terminate application. (I've used javascript to run terminateApp cause native method not work for me.)
Find Safari on Home screen and then click on it
Use drive.get to open website as you expected.
In there you can change to WEBVIEW_*** to inspect web element.
Back to native context by NATIVE_APP keyword
Sample code:
System.out.println("Run application");
Map<String, Object> params = new HashMap<>();
params.put("bundleId", "com.example");
boolean terminalApp = (boolean) driver.executeScript("mobile: terminateApp", params);
System.out.println("terminateApp: " + terminateApp);
driver.findElementById("Safari").click();
Set<String> contextNames = appDriver.getContextHandles();
// Change context to WEBVIEW_***
appDriver.context(String.valueOf(contextNames.toArray()[1]));
driver.get("https://www.google.com.vn");
Thread.sleep(20000);
// Do something.
// ...
// If you want to communicate with NATIVE context just change to NATIVE_APP.
appDriver.context("NATIVE_APP");

you can activate system apps via driver.activateApp(BUNDLE_ID);
there is no need to kill the app driver and start browser driver to access browser, just switch between apps.
safari
driver.activateApp("com.apple.mobilesafari");

Here is how I resolved the issue:
driver2.activateApp("com.apple.mobilesafari");
Thread.sleep(5000);
boolean openSafariTab =
driver2.findElements(By.xpath("//XCUIElementTypeButton[#name=\"AddTabButton\"]")).size() > 0;
if (openSafariTab) {
driver2.findElement(By.xpath("//XCUIElementTypeButton[#name=\"AddTabButton\"]")).click();
} else { }
Thread.sleep(3000);
driver2.findElement(By.xpath("//XCUIElementTypeTextField[#name=\"TabBarItemTitle\"]")).click();
Thread.sleep(3000);
driver2.findElement(By.xpath("//XCUIElementTypeOther[#name=\"CapsuleViewController" +
"\"]/XCUIElementTypeOther[3]/XCUIElementTypeOther[1]/XCUIElementTypeOther[1]")).sendKeys("https://www.golfbpm.com");
Thread.sleep(3000);
driver2.findElement(By.xpath("//XCUIElementTypeButton[#name=\"Go\"]")).click();

Related

Java client PressKey Method works fine, but the key is not pressed in Mobile?

I have this below snippet of code in my automation framework
public void settings() {
try {
//((AndroidDriver) Driver).pressKeyCode(AndroidKeyCode.SETTINGS);
((AndroidDriver) Driver).pressKey(new KeyEvent(AndroidKey.SETTINGS));
updateTestLog(Action, "settings pressed", Status.PASS);
} catch (Exception ex) {
updateTestLog(Action, ex.getMessage(), Status.DEBUG);
Logger.getLogger(KeyActions.class.getName()).log(Level.SEVERE, null, ex);
}
}
I have connected with the mobile and see if it's working fine. In Appium server got log traces that Key code 176 (Settings) worked fine. But no action in mobile screen
I have tried it for different keys like "Menu","home","search" and "enter". I see it is executed and working fine in mobile.
If you need to open Settings Menu I believe the best option would be calling driver.startActivity() function like:
((AndroidDriver) driver).startActivity(new io.appium.java_client.android.Activity("com.android.settings",".Settings"));
If you're looking for a cross-platform solution it might be easier to go for Launch command available via SeeTest Appium Extension

Sendkeys not working in iOS real device using Appium

I am using appium (v1.10.0) for automating iOS native app on macOS(10.13.6) using Xcode 10.1 on a real device (iPhone 6s) of platform version 12.1.3. When I start appium server and start session, the app will open in the device. Once I run the code in eclipse to send the Username to login page of the app, mobile keyboard is not getting opened and hence sendkeys() is not working.
Tried getKeyboard() before sendkeys(). Still the error exists. Below is the code which I tried.
DesiredCapabilities cap = new DesiredCapabilities();
cap.setCapability("device", "iPhone");
cap.setCapability("deviceName", "iPhone 6s");
cap.setCapability("platformVersion", "12.1.3");
cap.setCapability("platformName", "iOS");
cap.setCapability("app","/Users/TP/Desktop/SampleApp.ipa" );
cap.setCapability("udid", "xxxxxxxxxxxxxxxxxxx");
cap.setCapability("automationName", "XCUITest");
cap.setCapability("xcodeOrgId", "xxxxxxxx");
cap.setCapability("xcodeSigningId", "xxxxxxxx");
driver = new IOSDriver(new URL("http://127.0.0.1:4723/wd/hub"), cap);
driver.findElement(By.xpath("//XCUIElementTypeApplication[#name=\"TBI\"]")).click();
driver.getKeyboard().sendKeys("abc");
Mobile keyboard is not getting opened and hence throwing the following error.
org.openqa.selenium.WebDriverException: An unknown server-side error occurred while processing the command.
Original error: Error Domain=com.facebook.WebDriverAgent Code=1 "Only actions of 'pointer' type are supported. 'key' is given instead for action with id 'keyboard'" UserInfo={NSLocalizedDescription=Only actions of 'pointer' type are supported. 'key' is given instead for action with id 'keyboard'}
you don't need to use getKeyboard() you can directly send the value to the fields
Send Keys sends a sequence of key strokes to an element.
Can you replace the following two lines
driver.findElement(By.xpath("//XCUIElementTypeApplication[#name=\"TBI\"]")).click();
driver.getKeyboard().sendKeys("abc");
with
MobileElement mobileElement = driver.findElement(By.xpath("//XCUIElementTypeApplication[#name=\"TBI\"]"));
mobileElement.sendKeys("abc");
Try this. Worked for me:
IWebElement currentElement = driver.SwitchTo().ActiveElement();
currentElement.SendKeys("any text");

Testing React Native with Appium: how to get past "Permit drawing over other apps"?

In my initial proof of concept for testing a React Native mobile app using Appium, I noticed that when I load the APK to start my test, I am presented with an Android prompt to "Permit drawing over other apps" as I am creating my AndroidDriver driver. If I move the slider manually, then click the back button, all is good -- the app loads fully, and my test proceeds. However, I don't see how to do this with automation using my Appium script because it looks like the driver instantiation will not complete until the slider is moved.
Most people don't see this in Appium testing as it appears to be specific to React Native in dev mode, as seen here...
Here's my code where I've put in conditional code to click the slider, but I never get there because it waits at the "driver = ..." line:
AndroidDriver driver = null;
#Before
public void setUp() throws Exception {
// < defining capabilities (emulator6p) up here...>
// initialize driver object
try {
driver = new AndroidDriver<WebElement>(new URL("http://127.0.0.1:4723/wd/hub"), emulator6p);
} catch (MalformedURLException e) {
System.out.println(e.getMessage());
}
}
#Test
public void test1() throws Exception {
By BY_slider_permitDrawing = By.id("android:id/switchWidget");
boolean present = (driver.findElements(BY_slider_permitDrawing).size() == 1);
if (present) {
driver.findElement(BY_slider_permitDrawing).click();
driver.navigate().back();
}
WebElement button_begin = driver.findElementByAccessibilityId("button-lets-begin");
button_begin.click();
}
I definitely hear plenty of people say that Appium is a viable solution for React Native testing, and really need to get over this hump.
Thanks in advance for any suggestions!
jph
p.s. In case it wasn't really clear, the test does NOT hang at the "driver = " line if I am not loading the APK from scratch, but I will need to do that for CI testing in the future.
Setting right capabilities helped me to run Appium tests in Debug mode on Android:
{
...
"appWaitPackage": "com.android.settings, com.yourPackage",
"appWaitActivity": "com.android.settings.Settings$AppDrawOverlaySettingsActivity, com.yourPackage.MainActivity",
}
Replace yourPackage with your real package name used in Android app. Some docs here: https://appium.io/docs/en/writing-running-appium/caps/#android-only

How to locate element in UIautomator if native keyboard come before your application?

As per project requirement i am working on Mobile App automation. Not problem arises when i executed same code which worked fine on emulator but when it comes to real device the same code were getting failed.the problem is UiAutomator is not able to locate element because of native keyboard come before an application during simulation. I executed this entire thing into Galaxy nexus which works on ANDROID API 18.hence no point to execute whole automation suites in Selendroid mode. in below code after filling value in first editbox,control should have reached to second editbox to fill value and so on. But it does not fill value there because native keyboard appear before application.
SwipeableWebDriver driver = new SwipeableWebDriver(
new URL("http://127.0.0.1:4723/wd/hub"), capabilities);
List<WebElement> editTextList = driver.findElements(By
.className("android.widget.EditText"));
editTextList.get(0).sendKeys(c + "Bob");
editTextList.get(1).sendKeys("123");
editTextList.get(2).sendKeys("456");
el = driver.findElement(By.className("android.widget.Button"));
el.click();
Please anyone who have idea to resolve this issue?
Thanks in advance.
Priyank Shah
First of all you should realize whether the soft keyboard is active or not - Use the following command from your code to check "mInputShown" parameter - If "true" - Active Soft Keyboard.
adb shell dumpsys input_method | grep mInputShown
Use this code for hiding the native keyboard in Java-appium running older versions of appium.
driver.navigate().back()
P.S - The adb command is useless for emulators as the flag whose value is being checked is always set to true, whether your keyboard is active or not.
I don't think you can, and and it is not a appium limitation.
From what I observed even the UIAutomator can not find the elements hidden by the keyboard.
I know 2 solutions for this:
Dismiss the keyboard. (I didn't find any elegant ways of doing that so I'm not using this.
Swipe/scroll on the view until the element is exposed, and then you can action it. This works fine for me.
you should be able to dismiss the keyboard by sending
driver.findElement(By.name("Return")).click();
adding new line character works too
editTextList.get(2).sendKeys("456\n");
If you can detect that the keyboard is open, I would suggest calling UiDevice.pressBack() to dismiss the keyboard.
You could be able to dismiss the keyboard by using the following code
driver.hideKeyboard();
Put the following two lines:
driver.getKeyboard();
driver.hideKeyboard();
Here is a uiautomator ready fully functional method that will respond with true if the keyboard is Open and false if it is closed:
public static boolean isKeyboardDisplayed() {
String checkKeyboardCommand = "dumpsys input_method | grep mInputShown";
try {
Process process = Runtime.getRuntime().exec(checkKeyboardCommand);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
int read;
char[] buffer = new char[4096];
StringBuffer output = new StringBuffer();
while ((read = reader.read(buffer)) > 0) {
output.append(buffer, 0, read);
}
reader.close();
process.waitFor();
if (output.toString().contains("mInputShown=true")) {
return true;
} else {
return false;
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
You may need to enable unicodeKeyboard: true in your android capabilities and use the keyboard button Return to hide the keyboard if shown (this work for me on iOS and Android)
For example, I am using ruby:
element = $appium.find_element(id: field_id)
element.clear
element.send_keys(data)
element.send_keys(:return) if driver.is_keyboard_shown

Phone arguments on Blackberry OS 4.5

When I invoke code like this on blackberry using (compiling) JDE 4.5.
public void invokeCall() {
UiApplication.getUiApplication().invokeAndWait(new Runnable() {
public void run() {
PhoneArguments phoneArgs = new PhoneArguments(
PhoneArguments.ARG_CALL, "12345");
Invoke.invokeApplication(Invoke.APP_TYPE_PHONE, phoneArgs);
}
});
}
The result was that a pop up screen showed up and told that the phone number used to access extensions within my corporation has not been set.
In order to disable it I set the smart dialing country code to unknown.
(This problem did not occur when compiling on JDE 5.0)
Is there any blackberry code to set this automatically?
First of all, thanks for pointing out that smart dialing matters when making calls to extensions and injecting DTMF tones, because I have a similar project where this may cause problems.
In 4.5, you can configure smart dialing aswell, but the menu is not shown in main options menu, only in the phone app options. From the "Desktop", press a numeric key to bring the phone app to foreground, then press menu -> Options -> Smart Dialing -> Country Code.
Now, to set it programmatically, use this version of PhoneArguments constructor. So in your code, replace your arguments with this line to disable smart dialing:
PhoneArguments phoneArgs = new PhoneArguments(PhoneArguments.ARG_CALL, "12345", false);

Resources