I have popup notification. My first test checks if that notification is displayed and it passes succesfully. I use next method for this test:
fun viewInNotificationPopupIsDisplayed(viewMatcher: Matcher<View>) {
onView(viewMatcher)
.inRoot(RootMatchers.isPlatformPopup())
.check(ViewAssertions.matches(isDisplayed()))
}
I have a problem with second test case where i have to check that my popup notification has already gone (means it's not displayed anymore).
So i'm trying to use next method:
fun viewInNotificationPopupIsNotDisplayed(viewMatcher: Matcher<View>) {
Espresso.onView(viewMatcher)
.inRoot(RootMatchers.isPlatformPopup())
.check(matches(not(isDisplayed())))
//.check(ViewAssertions.doesNotExist()) // doesn't work as well
}
I get next exception:
android.support.test.espresso.NoMatchingRootException:
Matcher 'with decor view of type PopupWindow$PopupViewContainer'
did not match any of the following roots:
[Root{application-window-token=android.view.ViewRootImpl$W#bb8371e,
window-token=android.view.ViewRootImpl$W#bb8371e, has-window-focus=true,
Please, can anybody help with this?
Seems that such simple check is impossible with espresso if you have a PopupWindow because of the way the NoMatchingRootException is thrown
You may just catch the exception and complete the test but that's not a good option since throwing and catching NoMatchingRootException consumes a lot of time in a comparison with the default test case. Seems that Espresso is waiting for the Root for a while
For this case is suggest just to give up with espresso here and use UiAutomator for this assertion
val device: UiDevice
get() = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
fun assertPopupIsNotDisplayed() {
device.waitForIdle()
assertFalse(device.hasObject(By.text(yourText))))
}
fun assertPopupIsDisplayed() {
device.waitForIdle()
assertTrue(device.hasObject(By.text(yourText))))
}
Related
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)
I have a few tests that only seem to fail the first time i run them, here is an example of one of them;
class OptionDialogTest {
#get:Rule
val activityScenarioRule = activityScenarioRule<MainActivity>()
#Test
fun optionDialogTest() {
val selected = R.string.manage_categories
var dialog: OptionDialog? = null
activityScenarioRule.scenario.onActivity { activity: MainActivity? ->
dialog = OptionDialog("test", "test", arrayOf(R.string.drawer_categories,selected), activity as Context)
dialog!!.setConfirm { _, _ -> }
dialog!!.show()
}
onView(withId(R.id.custom)).perform(click()) //this line sometimes doesn't execute causing the test to fail, presumably it doesn't execute in time.
onView(withText(R.string.manage_categories)).inRoot(isPlatformPopup()).perform(click())
onView(withText(R.string.Yes)).inRoot(isDialog()).perform(click())
assertEquals(dialog!!.getSelected(), selected)
}
}
the error android studio gives is; Waited for the root of the view hierarchy to have window focus and not request layout for 10 seconds. If you specified a non default root matcher, it may be picking a root that never takes focus.
my only guess is that some of the code isn't executing in an completely asyncronous fashion. I don't know if other developers experiance random test failures, or how i would go about fixing this.
any help or ideas are appreciated.
Occasionally, the instrumentation tests (Espresso) are failing on Google's Firebase Test Lab due to a keyboard on-boarding popup (screenshot) that blocks the screen and prevents tap/type events.
This only happens on the Samsung Galaxy S9+
Here is the exception:
android.support.test.espresso.PerformException: Error performing 'type text(666666)' on view '(is descendant of a: (with id: XXX) and an instance of android.widget.EditText)'.
Caused by: android.support.test.espresso.InjectEventSecurityException: java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission
at android.support.test.espresso.base.InputManagerEventInjectionStrategy.injectKeyEvent(InputManagerEventInjectionStrategy.java:113)
Any suggestions?
Maybe you can try to play around with "ime" command from ADB to enable another keyboard (you can log for example the return of ime list in order to get the IDs) before running your tests (e.g. #Before):
getInstrumentation().getUiAutomation().executeShellCommand("ime enable ID");
Thread.sleep(1000);
getInstrumentation().getUiAutomation().executeShellCommand("ime set ID");
Else you can workaround with uiautomator and implement a check before using the keyboard with this kind of piece of code :
...
onView(withId(R.id.inputField)).perform(click());
if (Build.VERSION.SDK_INT >= 23) {
UiDevice device = UiDevice.getInstance(getInstrumentation());
UiObject skipButton = device.findObject(new UiSelector().text("SKIP"));
if (skipButton.exists()) {
try {
skipButton.click();
Timber.e(e, "Dismissed popup on Keyboard");
} catch (UiObjectNotFoundException e) {
Timber.e(e, "There is no popup displayed on Keyboard");
}
}
}
onView(withId(R.id.inputField)).perform(typeText("some"), pressImeActionButton());
...
Hope this help !
I also faced this problem. It's on Firebase Test Lab side, and you shouldn't try to find a workaround. Sometimes there are problems with devices you are not responsible for. Instead you should report about it to Firebase team directly if you want it to be fixed as soon as possible.
The fastest way to do it is to go to #test-lab channel of the Firebase Slack community and report them about an issue like that. They will ask you to provide your matrix ID where you experienced something wrong.
As for layout popup, it was fixed the next day it was reported, so you shouldn't see it now.
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")
)
)
}
I'm just trying to test a submit button at the end of a form.
FormAndroidTest.java:
#Test
public void testSubmitButton() throws Exception {
// Execute
onView(withId(R.id.btnSaveFeedback))
.perform(click());
}
The click() is called, I can see it performed the click in the app, but the call never comes back (until it times out).
I've narrowed it down to being caused by the startActivity call in the click handler:
FormActivity.java:
public void onSubmitClicked(View view) {
...
startActivity(new Intent(this, NextActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME));
...
}
I've traced it down to the call sendUp() call in Espresso's Tap.java failing:
private static Tapper.Status sendSingleTap(...) {
...
DownResultHolder res = MotionEvents.sendDown(uiController, coordinates, precision);
try {
if (!MotionEvents.sendUp(uiController, res.down)) { <-- THIS TIMES OUT
Log.d(TAG, "Injection of up event as part of the click failed. Send cancel event.");
MotionEvents.sendCancel(uiController, res.down);
return Tapper.Status.FAILURE;
...
}
Within that it's the uiController.injectMotionEvent(motionEvent); which loops until the injection has completed, which in this case it never does and times out.
I'm assuming it must have something to do with my threads not settling, but I don't see why or how to resolve it. I've seen a few related threads, but the given answers don't seem to solve my exact problem.
Espresso test stuck/inactive after perform(click()) on button in
ViewAnimator - I've already disabled all animations, and manually trying to manipulate the screen doesn't help.
Android Espresso Test gets stuck at perform(click()); - Same answer of manually swiping given, doesn't help.
Thanks