Android Espresso - Web browser - android

I have a question
I want to test whether after button click the web browser is beign launched using espresso.
And the question is: is it even possible to test such thing?
If so any ideas how would I do that?

Although it's an old question but just posting here to help anyone else. I had the same situation where i wanted to verify whether a particular url was launched in browser or not. I got real help from this link
I got it working using this chunk of code:
Intents.init();
Matcher<Intent> expectedIntent = allOf(hasAction(Intent.ACTION_VIEW), hasData(EXPECTED_URL));
intending(expectedIntent).respondWith(new Instrumentation.ActivityResult(0, null));
onView(withId(R.id.someid)).perform(click());
intended(expectedIntent);
Intents.release();
So, it tests when browser is opened with correct url and intending() does the magic here by enabling intent stubbing. Using this, we can intercept it so the intent is never sent to the system.

For convenience I suggest a full example:
Production code:
register_terms.text = Html.fromHtml(getString(R.string.register_terms,
getString(R.string.privacy_policy_url),
getString(R.string.register_terms_privacy_policy),
getString(R.string.general_terms_and_conditions_url),
getString(R.string.register_terms_general_terms_and_conditions)))
Strings XML:
<string name="register_terms">By registering you accept our <a href=\"%1$s\">%2$s</a> and the <a href=\"%3$s\">%4$s</a>.</string>
<string name="register_terms_privacy_policy">Privacy Policy</string>
<string name="register_terms_general_terms_and_conditions">General Terms and Conditions</string>
<string name="privacy_policy_url" translatable="false">https://www.privacypolicy.com</string>
<string name="general_terms_and_conditions_url" translatable="false">https://www.generraltermsandconditions.com</string>
Test code:
#Before
fun setUp() {
Intents.init()
}
#After
fun tearDown() {
Intents.release()
}
#Test
fun when_clickPrivacyLink_then_openPrivacyUrl() {
val expected = allOf(IntentMatchers.hasAction(Intent.ACTION_VIEW), IntentMatchers.hasData(string(privacy_policy_url)))
Intents.intending(expected).respondWith(Instrumentation.ActivityResult(0, null))
onView(ViewMatchers.withId(R.id.register_terms))
.perform(openLinkWithText(string(register_terms_privacy_policy)))
Intents.intended(expected)
}

Actually no. Espresso will allow you to click in the button and once the browser is fired up, the test will end. The alternative you have is having your class that fires the browser Intent mocked, so that you can test the remaining flow (if any).
HAve a look in this answer: Controlling when an automation test ends - Espresso, where I describe how you could achieve that.

Related

Reliable Method to perform web searches using intents

I'm working on an app that will on a fragment page, provide a list of charities to call should they be distressed, initial testing had been fine, then I began noticing that clicking the websites onCLickListeners sometimes wouldn't work.
After a while, I realised that pressing the listener enough would eventually bring up the relevant site. Can anyone tell me how to make this more reliable?
As you can imagine, if you need to get some info then and there when distressed, having to click a link 3 times to bring up a website is poor.
Example of my code:
CharityPageFragment
val char1Web = "gamcare.org.uk"
binding.tvCharityOne.text = char1Name
binding.tvCharityOneWebsite.setOnClickListener {
webIntent(char1Web)
}
...
private fun webIntent(website: String) {
val intent = Intent(Intent.ACTION_WEB_SEARCH)
intent.putExtra(SearchManager.QUERY, website)
startActivity(intent)
}

How to restart Android app within Espresso test?

I am using Espresso with Kotlin for the UI test automation. I am trying to find a proper way to restart the app during the test and start it again, so the test scenario is the following:
start the app, go to login page
force close the app and open it again (basically restart it)
check some stuff etc
The way our UI tests are organized:
there is a test class where I have rules
val intent = Intent(ApplicationProvider.getApplicationContext(), MainActivity::class.java)
.putExtra(UI_TEST_INTENT, true)
#get:Rule
val rule = ActivityScenarioRule<MainActivity>(intent)
there Before/After functions and tests functions in this class
What I want is to have generic restartApp function in separated class, let's say TestUtils and to be able to call it at any point of time, when is needed.
So far I didn't find a solution. There are some similar questions on stackoverflow, but I am not sure I understand how to work with the answers I found, like this:
with(activityRule) {
finishActivity()
launchActivity(null)
}
Since ActivityTestRule is deprecated and documentation asking to use ActivityScenarioRule, I tried this:
#get:Rule
val rule = ActivityScenarioRule<MainActivity>(intent)
private fun restart() {
rule.scenario.close()
rule.scenario.recreate()
}
but it gets java.lang.NullPointerException
another option is
private fun restart() {
pressBackUnconditionally()
Intents.release()
ActivityScenario.launch<MainActivity>(intent)
}
it works, app restarts but I can not interact with the app anymore, because for some reason there are two intents running now
Would be great to get an answer I can work with (I am quite new to Espresso)
Cheers
The solution is found:
private fun restart() {
Intents.release()
rule.scenario.close()
Intents.init()
ActivityScenario.launch<MainActivity>(intent)
}
Seems like the author's answer has some excess code. The following is enough
activityScenarioRule.scenario.close()
ActivityScenario.launch(YourActivity::class.java, null)

Pepper Robot - How to launch tablet applications through DialogFlow?

I'm trying to incorporate Pepper's built in Android tablet more in DialogFlow interactions. Particularly, my goal is to open applications installed on the tablet itself for people to use while they're talking with Pepper. I'm aware there is a 'j-tablet-browser' app installed on Pepper's end that can let a person browse the tablet like an ordinary Android device, but I would like to take it one step further and directly launch an Android app, like Amazon's Alexa.
The best solution I can up with is:
User says specific utterance (e.g. "Pepper, open Alexa please")
DialogFlow launches the j-tablet-browser behavior
{
"speak": "Sure, just a second",
"action": "startApp",
"action_parameters": {
"appId": "j-tablet-browser/."
}
}
User navigates the Android menu manually to tap the Alexa icon
My ideal goal is to make the process seamless:
User says specific utterance (e.g. "Pepper, open Alexa please")
DialogFlow launches the Alexa app installed on the Android tablet
Does anyone have an idea how this could be done?
This is quite a broad question so I'll try and focus on the specifics for launching an app with a Dialogflow chatbot. If you don't already have a QiSDK Dialogflow chatbot running on Pepper, there is a good tutorial here which details the full process. If you already have a chatbot implemented I hope the below steps are general enough for you to apply to your project.
This chatbot only returns text results for Pepper to say, so you'll need to make some modifications to allow particular actions to be launched.
Modifying DialogflowDataSource
Step 2 on this page of the tutorial details how to send a text query to Dialogflow and get a text response. You'll want to modify it to return the full reponse object (including actions), not just the text. Define a new function called detectIntentFullResponse for example.
// Change this
return response.queryResult.fulfillmentText
// to this
return response.queryResult
Modifying DialogflowChatbot
Step 2 on this page shows how to implement a QiSDK Chatbot. Add some logic to check for actions in the replyTo function.
var response: DetectIntentResponse? = null
// ...
response = dataSource.detectIntentFullResponse(input, dialogflowSessionId, language)
// ...
return if (reponse.action != null) {
StandardReplyReaction(
ActionReaction(qiContext, response), ReplyPriority.NORMAL
)
} else if (reponse.answer != null) {
StandardReplyReaction(
SimpleSayReaction(qiContext, reponse.answer), ReplyPriority.NORMAL
)
} else {
StandardReplyReaction(
EmptyChatbotReaction(qiContext), ReplyPriority.FALLBACK
)
}
Now make a new Class, ActionReaction. Note that the below is incomplete, but should serve as an example of how you can determine which action to run (if you want others). Look at SimpleSayReaction for more implementation details.
class ActionReaction internal constructor(context: QiContext, private val response: DetectIntentResponse) :
BaseChatbotReaction(context) {
override fun runWith(speechEngine: SpeechEngine) {
if (response.action == "launch-app") {
var appID = response.parameters.app.toString()
// launch app at appID
}
}
}
As for launching the app, various approaches are detailed in other questions, such as here. It is possible to extend this approach to do other actions, such as running or retrieving online data.

How to open Android Outlook application from an external one

I'm currently developing an Android application in order to display home screen widgets. Those ones are related to Microsoft Outlook (Events + Messages) in order to show incoming events and unread new messages in a kind of dynamic tiles.
The Msal graph library helps me a lot to authenticate and retrieve in formations which contains an identifier for each event / message results
But now I want to know if the outlook application is installed on the user device and if there is a way to open Outlook when the user click on the widget. Moreover if the user can open the corresponding clicked event or message with the identifier.
For example the Event widget currently displaying a birthday event. The user click on it. Then it opens Outlook and display directly that birthday event.
Regards
I don't think this is officially documented somewhere. But here's what you can do to find out about it.
You can list all Microsoft applications installed on your device...
val packages = context.packageManager
.getInstalledApplications(PackageManager.GET_META_DATA)
for (info in packages) {
if(info.packageName.startsWith("com.microsoft", true)){
Log.d("package name:" + info.packageName)
Log.d("Launch Activity: " + context.packageManager.getLaunchIntentForPackage(info.packageName))
}
}
Take a note of the "launch intent" displayed in the LogCat. You can use that to launch Outlook. Just make sure you don't hard-code those values because Microsoft can change those values at any point, for example the activity class can change. So, instead of doing this...
context.startActivity(
Intent().apply {
action = Intent.ACTION_MAIN
addCategory(Intent.CATEGORY_LAUNCHER)
setPackage("com.microsoft.office.outlook")
component = ComponentName("com.microsoft.office.outlook", "com.microsoft.office.outlook.MainActivity")
}
)
Do this...
context.startActivity(
Intent().apply {
action = Intent.ACTION_MAIN
addCategory(Intent.CATEGORY_LAUNCHER)
component = ComponentName(
outlookLaunchIntent?.component?.packageName,
outlookLaunchIntent?.component?.className
)
setPackage(outlookLaunchIntent.package)
}
)
Also, remember that getLaunchIntentForPackage and component can return null, so make sure you check for null values properly
I am relaying a suggestion from a couple of internal folks:
Please try to open the event using one of the following URLs:
ms-outlook://events/open?restid=%s&account=test#om.com (if you have a regular REST id)
ms-outlook://events/open?immutableid=%s&account=test#om.com (if you are using an immutable id)
Since immutable IDs are still in preview stage in Microsoft Graph, and customers should not use preview APIs in their production apps, I think option #1 applies to your case.
Please reply here if the URL works, or not, and if you have other related questions. I requested the couple of folks to keep an eye on this thread as well.
Well, i managed to open the outlook android application with the help of your code #Leo. As im not developping with Kotlin, ill post the JAVA code below :
Intent outlookLaunchIntent = context.getPackageManager().getLaunchIntentForPackage("com.microsoft.office.outlook");
if (outlookLaunchIntent != null) {
context.startActivity(outlookLaunchIntent );
}
Below code to open event/message in a web browser provided by webLink property of the graph API. (I only test for event and the url provided not working. Ill post a new issue on StackOverFlow for that but you already see the issue over there : https://github.com/microsoftgraph/microsoft-graph-docs/issues/4203
try {
Intent webIntent = new Intent(Intent.ACTION_VIEW).setData(Uri.parse(calendarWebLink));
webIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(webIntent);
} catch (RuntimeException e) {
// The url is invalid, maybe missing http://
e.printStackTrace();
}
However im still stuck on the decicive goal of my widget item click which is to open the relative event/email in the Microsoft Outlook Android application.
Microsoft Outlook Android app contains widgets which can achieve what im looking for. So i wonder if it is possible to list its broadcast receivers.
The best thing i found is an old manifest for that app but it doesnt help me.
https://gist.github.com/RyPope/df0e61f477af4b73865cd72bdaa7d8c2
Hi may you try to open the event using one of the url:
ms-outlook://events/open?restid=%s&account=test#om.com (If the
user is having rest id)
ms-outlook://events/open?immutableid=%s&account=test#om.com (If
the user is having immutable id)

Is there a way to test Chrome Custom Tabs with Espresso?

Here is the stub of the code.
Click data item on ListView . Works as designed and opens Chrome Custom Tab :
onData(anything()).inAdapterView(withId(R.id.listView))
.atPosition(0).perform(click());
Pause(5000);
Espresso.pressBack();
Cannot seem to evaluate anything in the tab or even hit device back button.
Getting this error
Error : android.support.test.espresso.NoActivityResumedException: No
activities in stage RESUMED.
Did you forget to launch the activity. (test.getActivity() or similar)?
You can use UIAutomator (https://developer.android.com/training/testing/ui-automator.html). You can actually use both Espresso and UIAutomator at the same time. See the accepted answer on the following post for more information:
How to access elements on external website using Espresso
You can prevent opening Custom Tabs and then just assert whether the intent you are launching is correct:
fun stubWebView(uri: String) {
Intents.intending(allOf(IntentMatchers.hasAction(Intent.ACTION_VIEW), IntentMatchers.hasData(uri)))
.respondWith(Instrumentation.ActivityResult(Activity.RESULT_OK, null))
}
fun isNavigatedToWebView(uri: String) {
Intents.intended(allOf(IntentMatchers.hasAction(Intent.ACTION_VIEW), IntentMatchers.hasData(uri)))
}
This way you can avoid Espresso.pressBack() in your test.
Note that since these are using Espresso Intents, you need to either use IntentsTestRule or wrap these with Intents.init and release like this
fun intents(func: () -> Unit) {
Intents.init()
try {
func()
} finally {
Intents.release()
}
}
intents {
stubWebView(uri = "https://www.example.com")
doSomethingSuchAsClickingAButton()
isNavigatedToWebView(uri = "https://www.example.com")
}
A suggestion for improved readability following Mustafa answer:
intents{
Intents.intended(
CoreMatchers.allOf(
IntentMatchers.hasAction(Intent.ACTION_VIEW),
IntentMatchers.hasPackage("com.android.chrome")
)
)
}

Categories

Resources