Robolectric, how to check visibility of FragmentDialog? - android

I have a button in my activity. Once clicked, it checks user registration status. If registered then shows a dialog otherwise starts registration activity.
I set required flags that says user has registered. I perform click and expect the dialog must be created but after debug I see this is null :(
This is my code:
#Test
public void testSignupButton()
{
PreferenceUtils.setSessionId(activity, "sessionId");
assertTrue(PreferenceUtils.isActivated(activity));
btnSignUp.performClick();
Dialog dialog = ShadowDialog.getLatestDialog(); // << dialog is null
ShadowDialog loginDialogFragment = Shadows.shadowOf(dialog); // Test fails here since dialog is null
assertThat(loginDialogFragment.getTitle().toString(), equalTo("TestDialogFragment"));
}
Any idea, would be appreciated, thanks.

omg, I found where was my problem. I had activity = Robolectric.buildActivity(HitchWelcomeActivity.class).create().get(); in my setupView method which is wrong. In order to get real activity I should have visible() method too.
So, I changed above code to following and my problem fixed.
#Before
public void setUp()
{
activity = Robolectric.buildActivity(WelcomeActivity.class)
.create()
.start()
.resume()
.visible()
.get();
btnSignUp = (Button) activity.findViewById(R.id.dialog_welcome_sign_up);
btnSkip = (TextView) activity.findViewById(R.id.dialog_welcome_next_time);
}

Related

Android Mockito Test on Activity's method which shows alert dialog

I am new to the Mockito Android Test framework. I have written one method in the activity and which shows the AlertDialog and I am writing test cases for that method.
Here are the functions of the activity which shows the alert dialog box:
fun showDialogBox(
shortMessage: String?,
longMessage: String?,
progress: Int,
status: String?
) {
runOnUiThread {
if (cdfwExtractionStatusAlertDialog == null) {
val mDialogView = LayoutInflater.from(this).inflate(
R.layout.cdfw_extraction_status_layout,
null
)
val mBuilder = AlertDialog.Builder(this)
.setView(mDialogView)
.setCancelable(false)
shortMessageTv = mDialogView.findViewById(R.id.short_message_tv)
longMessageTv = mDialogView.findViewById(R.id.long_message_tv)
closeButton = mDialogView.findViewById(R.id.close_button)
cdfwProcessBar = mDialogView.findViewById(R.id.progress_bar)
cdfwProcessBar.progress = progress
shortMessageTv.text = shortMessage
longMessageTv.text = longMessage
closeButton.setOnClickListener {
cdfwExtractionStatusAlertDialog?.dismiss()
}
checkStatus(status)
cdfwExtractionStatusAlertDialog = mBuilder.show()
} else {
shortMessageTv.text = longMessage
longMessageTv.text = longMessage
cdfwProcessBar.progress = progress
checkStatus(status)
}
}
}
Here are my mockito test cases for the same function:
#Test
public void testCDFWExtractionStatusNoZipFound(){
MainActivity activity = Mockito.mock(MainActivity.class);
activity.showDialogBox("","CDFW Required.",0,"FAILED_NO_ZIP_FOUND");
Mockito.matches("CDFW Extraction Status");
Mockito.matches("CDFW Required.");
Mockito.matches("Close");
}
#Test
public void testCDFWExtractionStatusInProgress(){
MainActivity activity = Mockito.mock(MainActivity.class);
activity.showDialogBox("","CDFW extraction ongoing",0,"IN_PROGRESS");
Mockito.matches("CDFW Extraction Status");
Mockito.matches("CDFW extraction ongoing");
}
It shows the test passed successfully but I am not sure whether this is the right way to write test cases for the same function if this is the wrong method then how do I write a test case for the same function.
When you are testing your UI it is better to use instrumented testing.
Instrumentation testing is testing that runs on Physical devices, so, in your case, the test will run in a real device or emulator to check if the flow is right.
For that, you can use Espresso, which is a framework provided by AndroidX.
For more detail, please check this out:
Test UI for a single app
Now, why not use Mockito for UI testing?
When you are testing the UI, you have to ensure the right flows to the user and good interaction. You can mock the scenario, but you can't test the whole flow because you don't know how it will behave when, for example, a click is made in a different place than the one you are testing on.
If you want to test for example that your Alert Dialog is shown, you can use:
#Test
fun testCDFWExtractionStatusNoZipFound() {
//..
onView(withId(R.id.dialogId)).check(matches(allOf(withText("CDFW Required"), isDisplayed()));
//..
}

AlertDialog appears only late (weird behaviour) Android

I am uploading data to a webserver. On my fragment I have a button to start the upload. There are two phases what I am trying to have the user notification done via a none-cancellable AlertDialog solution.
When I am pressing the upload button, preparation for the upload is starting I am setting up the AlertDialog and presenting it. Once the physical upload is starting, I am using the same AlertDialog, but changing the message in it to show the progress of the upload.
***** Now the issue is the following ******
When I setup the AlertDialog and call the Show method, it does not display the AlertDialog. But once the upload is started and the progress is updated I just call the setMessage method and at this point the AlertDialog appears.
The relevant codes are the followings:
The submitbutton.setOnClickLictener is in the onViewCreated()
submitbutton.setOnClickListener {
requireActivity().runOnUiThread {
SubmitAd()
}
}
I have tried here the run the SubmitAd() on the UIThread, if it helps, but it is the same without it.
SubmitAd is showing the Dialog. (Actually at this point nothing is shown.
fun SubmitAd() {
var addInApp: Boolean = false
ToBePurchased = 0
if (CheckCanUpload()) {
var AlertView = AlertDialog.Builder(requireActivity())
AlertView.setTitle("Hirdetés feltöltés")
AlertView.setMessage("A feltöltés előkészítése hosszabb ideig is eltarhat, kérjük várjon!")
AlertView.setCancelable(false)
DialogToShow = AlertView.create()
DialogToShow!!.show()
purchaseLoop = 0
UploadWithPurchase()
} else {
var AlertView = AlertDialog.Builder(requireActivity())
AlertView.setTitle("Hirdetés hiba")
AlertView.setMessage("A hirdetése hiányos. Kérjük töltse ki az összes mezőt és csatoljon fotót a hirdetéséhez!")
AlertView.setPositiveButton("Ok") { dialog, which ->
dialog.dismiss()
}
DialogToShow = AlertView.create()
DialogToShow!!.show()
}
}
In UploadWithPurchase() the Playstore purchase handling is done, but if there is no purchase at all, it is just going through a loop, which calls UploadWithPurchase() recursively until all possible purchases are checked, then it goes to the real Upload() which calls an Http request to upload the data and reports back via an interface the progress of the upload process.
The Webhelper returns the progress like this:
override fun WebHelperProgress(id: String, progress: Float) {
if (DialogToShow != null) {
DialogToShow!!.setMessage("Feltöltés folyamatban. Kérem várjon! ... ${progress.toInt()}%")
}
}
When this method is called, the AlertDialog appears.
Whatever I have tried, does not help. AlertDialog does not show up at the first call, but no clue why.
EDIT later: I have figured out that the AlertDialog is actually appears once it comes out from the recursive loop, but I do not know how to force it to be displayed before it starts the loop. That would be my aim to notify the user that a longer process is starting. It meaningless to start the process and the user does not know what is happening.
Finally I could solve it by putting the purchaseLoop to a separate Thread like this.
fun SubmitAd() {
var addInApp: Boolean = false
ToBePurchased = 0
if (CheckCanUpload()) {
var AlertView = AlertDialog.Builder(requireActivity())
AlertView.setTitle("Hirdetés feltöltés")
AlertView.setMessage("A feltöltés előkészítése hosszabb ideig is eltarhat, kérjük várjon!")
AlertView.setCancelable(false)
DialogToShow = AlertView.create()
DialogToShow!!.show()
purchaseLoop = 0
******** SOLUTION HERE ********
Thread {
UploadWithPurchase()
}.start()
*******************************
} else {
var AlertView = AlertDialog.Builder(requireActivity())
AlertView.setTitle("Hirdetés hiba")
AlertView.setMessage("A hirdetése hiányos. Kérjük töltse ki az összes mezőt és csatoljon fotót a hirdetéséhez!")
AlertView.setPositiveButton("Ok") { dialog, which ->
dialog.dismiss()
}
DialogToShow = AlertView.create()
DialogToShow!!.show()
}
}

Kotlin - Clear Button listener for google's Place Autocomplete API onClick not called

I'm trying to create a listener for the clear button that comes from google's Place Autocomplete API. i called my clearButton() method in my fragment's onViewCreated method
clearButton()
placeAutocompleteFragment?.view?.findViewById<View>(R.id.place_autocomplete_clear_button)
?.setOnClickListener {
View.OnClickListener {
Log.d(TAG, "Cleared")
it?.findViewById<EditText>(R.id.place_autocomplete_search_input)?.setText("")
it?.visibility = View.GONE
}
}
now when i click on the clear button icon, the text doesn't get erased, nothing happens. I can still type in a new location though, but i can't clear it. my Log.d isn't getting displayed either.
I don't have android studio on this machine now to try, but I guess you can do something like
place_autocomplete_clear_button.onClick { place_autocomplete_search_input.text = "" }
where place_autocomplete_clear_button can be static import and onClick might be from anko
Figured it out. I had the method calls set up all wrong.
Here's how it should look like:
private fun clearButton() {
placeAutocompleteFragment?.view?.findViewById<View>(R.id.place_autocomplete_clear_button)?.setOnClickListener {
Log.d(TAG, "Cleared Button Clicked")
it.visibility = View.GONE
//do something
}
}

How can I prevent UI thread from going on until password input is right

I am writing an Android file viewer to provided for the host app, our viewer is a fragment , and host app use newinstance to use our fragment. Some codes are as below:
newInstance () {
......
openfile();
return fragment;
}
openfile() {
......
if(ispasswordfile) {
showPasswordInputDialog;
mpassword = .;
mpagecount = ..;
......;
return errorcode;
} else {
.......
}
}
showPasswordDialog() {
......
dialog.show();
EditText edittext = dialog.findViewById(...);
edittext.addListener(){
override
public boolean action....{
.......
if(password is right) {
.....
}
}
};
My question is when dialog input password is right and then execute the sentences after showPasswordInputDialog. These codes are all running in UI thread, and listener need to change the UI if password is wrong, so I cannot pause the UI thread. So are there any ways to set a barrier between showPasswordInputDialog; and mpassword = .; And when the password is right I will remove the barrier and let the UI thread going down.
I try to use a while loop it does not work. And I cannot add actions in the listener when password is right, because I want to return the fragment by newInstance.
This has bothered me a long time, and help is appreciated!
You have to think that as an event based execution. So don't place the
mpassword = .;
below the show dialog method. Now in the text change listener, on each and every change just check whether it is the right password or not. Then if the password is correct, then dismiss the dialog and set the password
if(password is right) {
mpassword = .; //Then continue your work from here or call other method.
}
There is no need to block main thread.
Let me know if I misunderstood the question.
So are there any ways to set a barrier between showPasswordInputDialog; and mpassword = .;
No, sorry.
Put whatever work that needs to be done in if(password is right) block or use some sort of flag to know if the password is true and use it in openfile().

Digits android authcallback not called on success/failure otp verification

I am creating a digitsauthconfig like this:
private DigitsAuthConfig createDigitsAuthConfig() {
return new DigitsAuthConfig.Builder()
.withAuthCallBack(createAuthCallback())
.withPhoneNumber("+91")
.withThemeResId(R.style.CustomDigitsTheme)
.build();
}
Where authcallback is returned by:
private AuthCallback createAuthCallback() {
return new AuthCallback() {
#Override
public void success(DigitsSession session, String phoneNumber) {
doIfSuccessfulOtpVerification();
}
#Override
public void failure(DigitsException exception) {
doIfNotSuccessfulOtpVerification();
}
};
}
I initiate the process using a button with event listener:
digitsButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Digits.authenticate(createDigitsAuthConfig());
}
});
The problem is, once my phone number is verified, it goes back to the activity where the button is displayed and does nothing. Technically, the authcallback is never called, doesn't matter successful or not. But if I click the button again, the authcallback is called without repeating the verification step. So right now I am required to click the button twice.
What is the way around it?
Finally i got solution for that issue, May it will help you too also.
You need to remove the ActiveSession, before calling the setCallback(authCallback) like mentioned as below.It will remove the existing session(if you already entered your phone number and got an OTP) from digits. This session will will not allows you to make another session to generate an OTP. So, we have to remove it. And it will work if there is no any previous sessions.
DigitsAuthButton digitsButton = (DigitsAuthButton) findViewById(R.id.auth_button);
Digits.getSessionManager().clearActiveSession();
digitsButton.setCallback(((WyzConnectApp) getApplication()).getAuthCallback());
Digits changed the way it reference the AuthCallback passed in the Digits#authenticate call.
Now Digits holds a weak reference (to avoid a memory leak), unless you hold a strong reference, that AuthCallback will be garbage collected and the result of the flow will be never propagated.
You need to define the AuthCallback in the Application context and then use this callback in your activity and it should work.
Please check the documentation on how to do this
I know its late but may be helpful for a beginner.To verify that the session is already active, please put this code in your onCreate of that activity in which Digits phone number verification button is initialised:
TwitterAuthConfig authConfig = new TwitterAuthConfig(TWITTER_KEY, TWITTER_SECRET);
Fabric.with(this, new TwitterCore(authConfig), new Digits.Builder().build());
if (Digits.getActiveSession() != null) {
Intent ss = new Intent(CurrentActivity.this, SecondActivity.class);
startActivity(ss);
finish();
}

Categories

Resources