I'm using webdriver.io to write a suite of Appium tests for a hybrid Cordova App.
"appium": "^1.10.0",
"wdio-appium-service": "^0.2.3",
"wdio-jasmine-framework": "^0.3.8",
"webdriverio": "^4.14.1",
In one of my tests I am trying to programmatically accept the Location Permissions native modal presented on App launch on top of the WebView.
I can easily do so in iOS using browser.alertAccept() but the latter does not seem to work for Android.
I also tried to switch to the native context to dismiss it but had no luck.
function dismissLocationPermissions() {
if (browser.isAndroid) {
new WebView().switchToContext(0);
browser.pause(2000);
const ANDROID_ACCEPT_ALERT_SELECTOR = "//*[#class='android.widget.Button'][2]";
// OR '*//android.widget.Button[#text="ALLOW"]' ?
$(ANDROID_ACCEPT_ALERT_SELECTOR).click();
WebView().switchToContext(1);
} else {
browser.alertAccept();
}
}
How can I dismiss the native alert on Android using wdio?
There is autoGrantPermissions DesiredCapability, if you set it to true - Appium will automatically determine which permissions are required by your application and grant them to the application during the installation procedure.
The capability is set to false by default so you need to explicitly set it like:
DesiredCapabilities dc = new DesiredCapabilities();
dc.setCapability(MobileCapabilityType.NO_RESET, false);
dc.setCapability(AndroidMobileCapabilityType.AUTO_GRANT_PERMISSIONS, true);
driver = new AndroidDriver<>(url, dc);
More information:
Appium Desired Capabilities
Application Setup
The permission dialog is native, so you don't need to switch to the webview to dismiss it.
There is an android id associated with the allow/deny permission button, so you can use that instead of the identifier you have used.
The id for allow button is: com.android.packageinstaller:id/permission_allow_button .
The id for deny button is: com.android.packageinstaller:id/permission_deny_button
Related
I am trying to automate android application using appium when I launch the application and try to login the pop up asks me to have access to Contacts by tapping on "Allow". The pop up have a different appPackage and it is not part of the application package.
Locator for the allow button
addLocator(Locators.ALLOW_CONTACTS, "com.android.packageinstaller:id/permission_allow_button");
method to tap on Allow button
Button allowContactsButton() {
return new Button(getLocator(Locators.ALLOW_CONTACTS));
}
public void dismissAllowContact() {
allowContactsButton().tap();
}
The dismiss function does is able to locate the button but does not tap on it
You can turn on autoAcceptAlerts and grant permissions.
desiredCapabilities.setCapability("autoGrantPermissions", "true");
desiredCapabilities.setCapability("autoAcceptAlerts", "true");
For location service you will have to explicity set authorization using following:
desiredCapabilities.setCapability("locationServicesAuthorized", "true");
You can try similar approach for contacts.
Hoping this helps !
I need to initialize Instabug in my Android application but do now want it to show the hint prompt saying "Shake your device etc." when user just opened the app. Instead I want to show that after user has already logged in.
I initialize Instabug with the following code:
new Instabug.Builder(this, instaKey)
.setInvocationEvent(InstabugInvocationEvent.SHAKE)
.setShakingThreshold(1100)
.build();
So is there a way to disable that hint prompt in a first place place?
I tried to set .setPromptOptionsEnabled(false, false, false) but it seems this does not what I need.
I cannot find any documentation about this.
You can disable showing it by setting false to setIntroMessageEnabled(boolean) API example:
new Instabug.Builder(this, instaKey)
.setInvocationEvent(InstabugInvocationEvent.SHAKE)
.setShakingThreshold(1100)
.setIntroMessageEnabled(false)
.build();
and then whenever the User is logged in you can show the intro message by invoking it manually example:
Instabug.showIntroMessage();
And by the way the .setPromptOptionsEnabled(true, true, true) is used for controlling prompt Options visibility [talk to us, report a bug, send a feedback] as described in the official docs here: http://docs.instabug.com/docs/enabled-features
I will start with explaining the use case I am trying to implement. I have two different applications:
Native android application, and
Worklight-based hybrid application
The use case starts with opening native android application. On a particular event I am opening the Hybrid application with some parameters.
In the Hybrid application, I am getting the passed parameters in the native side it, and now I want to use that data in the webview of the application (JavaScript, HTML). How can I achieve that?
For example:
I opened the first android application. Which have one text box and a button. I entered my mobile number in text box and hit the button. On the button click I have code which starts the other hybrid application and passes the mobile number with it.
I am able to extract that mobile number parameter on native side of code. How to pass that to the Web (JavaScript) part of it?
Any help will be appreciated.
If you are using Worklight 6.2, you can achieve this in 2 ways.
Use the Simple Data Sharing API
With this API I don't think you'll even need to try to get the data from the native view and move it back to the webview in your Hybrid app, it will just be available in the webview.
Explaining the concept and execution in this answer will make it too long; I suggest to first review the documentation and see whether it fits your needs.
But I suggest:
Use the Action Sender API
With this API you can easily move data from web to native or native to web.
In your case, you say you already have the data in the native code after opening the Hybrid application, and you only need to move it to the webview, so what that is required is to:
Add a sender in the native code
Add a receiver in the JavaScript code
Unfortunately at this time there is no training module available to demonstrate specifically this feature, but there will be.
This is the basic premise for what you'll need to do:
In the JavaScript you implement a receiver:
function wlCommonInit(){
WL.App.addActionReceiver ("doSomething", actionReceiver);
}
function actionReceiver(received){
// Do something with the received data.
alert (received.data.someProperty);
}
In the main Java class of the Hybrid application (or elsewhere, depending on your application) you implement the following in onInitWebFrameworkComplete after the else closing bracket:
public void onInitWebFrameworkComplete(WLInitWebFrameworkResult result){
...
...
else {
handleWebFrameworkInitFailure(result);
}
JSONObject data = new JSONObject();
try {
data.put("someProperty", 12345);
} catch (JSONException e) {
// handle it...
}
WL.getInstance().sendActionToJS("doSomething", data);
}
The end result would be that once you open the app, you'll be welcomed with an alert showing "12345".
I will describe the solution using code snippets.
First Open the hybrid application from a native application.
Intent intent = getPackageManager().getLaunchIntentForPackage(“URI Of Target Application”);
intent.putExtra("someData", someData);
startActivity(intent);
Now Worklight based hybrid application will start and from native part we will extract that passed data and store it in shared preferences:
Bundle dataBundle = getIntent().getExtras();
String someData = dataBundle.getString("someData");
sharedpreferences = getSharedPreferences(MyPREFERENCES, MODE_PRIVATE);
sharedpreferences.edit().putString("someData", someData);
sharedpreferences.commit();
Now make a plugin which you can call after the web part is ready for use.
SharedPreferences sharedpreferences = cordova.getActivity().getSharedPreferences(MyPREFERENCES,cordova.getActivity().MODE_PRIVATE);
if(sharedpreferences!=null) {
String param = sharedpreferences.getString("someData", "-1");
sharedpreferences.edit().remove("someData").commit();
callbackContext.success(param);
}
Call that plugin on web side of Worklight based hybrid application.
function onSuccessSharedData (param) {
Param is the passed parameter
}
Cordova.exec(onSuccessSharedData, onFailure, "pluginName", "action", []);
How do I handle alerts in an Android web application using Appium server (1.0.1) and the Android SDK?
The below code is not working on android:
driver.switchTo().accept().alert();
Error message:
> -modal window does not get closed
You need to get the Alert before you try and accept it
This is code from some of the Appium Java Client Tests:
wait.until(ExpectedConditions.alertIsPresent());
Alert alert = driver.switchTo().alert();
alert.accept();
This should work most of the time.
If accept() isn't working, replace the driver.switchTo().alert(); and alert.accept(); with code to find the button and then click it.
If it's not finding the button wrap findElementBy(Method) code in a try/retry block, and then click on it.
The best way is to use the appium inspector. Click on the element and copy the resource-id from it. Use this resource id in findElement(By.id()) method.
For me resource-id: android:id/button1
((AndroidDriver) driver).findElement(By.id("android:id/button1")).click();
This is for Android. For regular use you can use
driver.findElement(By.id("android:id/button1")).click();
Some alerts may be native Android's alerts, not generated by a browser. In this case the following code:
Alert alert = driver.switchTo().alert(); alert.accept();
may throw:
WebDriverException: unknown error: unhandled inspector error: {"code":-32603,"message":"No JavaScript dialog to handle"}
To handle such alert, just switch to the native application context, make required actions, and then switch back to the browser:
AppiumDriver<WebElement> appiumDriver = (AppiumDriver<WebElement>) webDriver;
String currentContext = appiumDriver.getContext();
appiumDriver.context("NATIVE_APP");
// actions within the alert
appiumDriver.findElements(By.xpath(OK_BUTTON_LOCATOR)).click(); // put locator instead of OK_BUTTON_LOCATOR
appiumDriver.context(currentContext);
// continue working
WebElement btn = driver.findElement(By.xpath("//android.widget.Button[#content-desc='OK']"));
TouchAction act = new TouchAction(driver);
act.tap(241,320).perform();
(241,320) these are X & Y c ordinates of alert
This work perfectly for me
Appium comes with a default capability to accept, dismiss alerts
capabilities.SetCapability("autoAcceptAlerts", true);
capabilities.SetCapability("autoDismissAlerts", true);
If the alert display on Ui , taking more time to display when we need to wait ..we can use fluent wait instead of this..
So an updated answer on this is this: an AlertDialog is a system level element, so clicking on accept button you should use:
androidDriver.findElementById("android:id/button1").click()
else for cancel do this:
androidDriver.findElementById("android:id/button2").click()
// first check alert is present or not
public boolean isAlertPresent() {
try {
mobDriver.switchTo().alert();
log.info("ALERT IS PRESENT !! ");
return true;
} catch (Exception e) {
log.info("ALERT IS NOT PRESENT !! ");
return false;
}
}
public void mobileAlertHandle() {
if (isAlertPresent()) {
Alert alert = mobDriver.switchTo().alert();
alert.accept();
}
}
if this does not work then inspect your element and try with id or name
ex: mobDriver.findElementById("android:id/button2").click()
Please use the below code, Add some wait before clicking on OK Button.
After that pass the xpath of you OK Button.
synchronized (driver)
{
driver.wait(2000);
}
driver.context(NATIVE_APP);
driver.findElementByXPath{("//android.widget.Button[#resourceid=
‘android:id/button1’]").click();
in agreement with the recent post from Android Developers http://android-developers.blogspot.pt/2013/10/getting-your-sms-apps-ready-for-kitkat.html ,I was trying to prepare my app to the new android version, but encountered a problem with the part they suggest to create a dialog to let the user set the app as the default application to handle SMS's :
Android Developers Post
public class ComposeSmsActivity extends Activity {
#Override
protected void onResume() {
super.onResume();
final String myPackageName = getPackageName();
if (!Telephony.Sms.getDefaultSmsPackage(this).equals(myPackageName)) {
// App is not default.
// Show the "not currently set as the default SMS app" interface
View viewGroup = findViewById(R.id.not_default_app);
viewGroup.setVisibility(View.VISIBLE);
// Set up a button that allows the user to change the default SMS app
Button button = (Button) findViewById(R.id.change_default_app);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent intent =
new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME,
myPackageName);
startActivity(intent);
}
});
} else {
// App is the default.
// Hide the "not currently set as the default SMS app" interface
View viewGroup = findViewById(R.id.not_default_app);
viewGroup.setVisibility(View.GONE);
}
}
}
the code itself in pretty much straightforward, but I'm unable to access to Telephony.Sms.getDefaultSmsPackage because it says that Telephony cannot be resolved, and I can't find any import or declaration that would fix that.
Can anyone please help?
android.provider.Telephony simply doesn't exist yet (as of API 18 - 4.3).
This class will be added in 4.4 (presumably API 19), and that blog post is highlighting the changes that you should make once the new API is released so you aren't surprised when the time comes.
From the end of the post:
To help you make the changes, we'll soon be providing the necessary SDK components for Android 4.4 that allow you to compile and test your changes on Android 4.4.
Don't forget that you should wrap this code in an API version check so you don't run into issues with older versions that don't have this class.
this change will break all the SMS blocking apps.
"Note that—beginning with Android 4.4—any attempt by your app to abort the SMS_RECEIVED_ACTION broadcast will be ignored so all apps interested have the chance to receive it."
Do you think there is a way to go around this?!
Maybe at least on Root?
Apparently there is with root access. The latest version Cerberus app claim to be doing this.
Now, if only I knew how they do it :(