1 package org.robolectric.integrationtests.axt; 2 3 import static androidx.test.core.app.ApplicationProvider.getApplicationContext; 4 import static androidx.test.espresso.intent.Intents.getIntents; 5 import static androidx.test.espresso.intent.Intents.intending; 6 import static androidx.test.espresso.intent.matcher.ComponentNameMatchers.hasClassName; 7 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; 8 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent; 9 import static androidx.test.ext.truth.content.IntentCorrespondences.action; 10 import static androidx.test.ext.truth.content.IntentCorrespondences.all; 11 import static androidx.test.ext.truth.content.IntentCorrespondences.data; 12 import static androidx.test.ext.truth.content.IntentSubject.assertThat; 13 import static com.google.common.truth.Truth.assertThat; 14 import static org.junit.Assert.assertThrows; 15 16 import android.app.Activity; 17 import android.app.Instrumentation.ActivityResult; 18 import android.content.ActivityNotFoundException; 19 import android.content.Intent; 20 import android.net.Uri; 21 import android.os.Bundle; 22 import androidx.test.core.app.ActivityScenario; 23 import androidx.test.espresso.intent.Intents; 24 import androidx.test.ext.junit.runners.AndroidJUnit4; 25 import com.google.common.collect.Iterables; 26 import org.junit.After; 27 import org.junit.Before; 28 import org.junit.Test; 29 import org.junit.runner.RunWith; 30 31 /** Integration tests for using androidx.test's espresso intents API on Robolectric. */ 32 @RunWith(AndroidJUnit4.class) 33 public class IntentsTest { 34 35 @Before setUp()36 public void setUp() { 37 Intents.init(); 38 } 39 40 @After tearDown()41 public void tearDown() { 42 Intents.release(); 43 } 44 45 @Test testNoIntents()46 public void testNoIntents() { 47 Intents.assertNoUnverifiedIntents(); 48 } 49 50 @Test testIntendedFailEmpty()51 public void testIntendedFailEmpty() { 52 assertThrows( 53 AssertionError.class, () -> Intents.intended(org.hamcrest.Matchers.any(Intent.class))); 54 } 55 56 @Test testIntendedSuccess()57 public void testIntendedSuccess() { 58 Intent i = new Intent(); 59 i.setAction(Intent.ACTION_VIEW); 60 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 61 getApplicationContext().startActivity(i); 62 Intents.intended(hasAction(Intent.ACTION_VIEW)); 63 } 64 65 @Test testIntendedNotMatching()66 public void testIntendedNotMatching() { 67 Intent i = new Intent(); 68 i.setAction(Intent.ACTION_VIEW); 69 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 70 getApplicationContext().startActivity(i); 71 assertThrows( 72 AssertionError.class, 73 () -> Intents.intended(hasAction(Intent.ACTION_AIRPLANE_MODE_CHANGED))); 74 } 75 76 /** 77 * Variant of testIntendedSuccess that uses truth APIs. 78 * 79 * <p>In this form the test verifies that only a single intent was sent. 80 */ 81 @Test testIntendedSuccess_truth()82 public void testIntendedSuccess_truth() { 83 Intent i = new Intent(); 84 i.setAction(Intent.ACTION_VIEW); 85 i.putExtra("ignoreextra", ""); 86 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 87 getApplicationContext().startActivity(i); 88 assertThat(Iterables.getOnlyElement(getIntents())).hasAction(Intent.ACTION_VIEW); 89 } 90 91 /** 92 * Variant of testIntendedSuccess that uses truth APIs. 93 * 94 * <p>This is a more flexible/lenient variant of {@link #testIntendedSuccess_truth} that handles 95 * cases where other intents might have been sent. 96 */ 97 @Test testIntendedSuccess_truthCorrespondence()98 public void testIntendedSuccess_truthCorrespondence() { 99 Intent i = new Intent(); 100 i.setAction(Intent.ACTION_VIEW); 101 i.putExtra("ignoreextra", ""); 102 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 103 getApplicationContext().startActivity(i); 104 Intent alsoSentIntentButDontCare = new Intent(Intent.ACTION_MAIN); 105 alsoSentIntentButDontCare.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 106 107 getApplicationContext().startActivity(alsoSentIntentButDontCare); 108 assertThat(getIntents()) 109 .comparingElementsUsing(action()) 110 .contains(new Intent(Intent.ACTION_VIEW)); 111 } 112 113 /** Variant of testIntendedSuccess_truthCorrespondence that uses chained Correspondences. */ 114 @Test testIntendedSuccess_truthChainedCorrespondence()115 public void testIntendedSuccess_truthChainedCorrespondence() { 116 Intent i = new Intent(); 117 i.setAction(Intent.ACTION_VIEW); 118 i.putExtra("ignoreextra", ""); 119 i.setData(Uri.parse("http://robolectric.org")); 120 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 121 getApplicationContext().startActivity(i); 122 Intent alsoSentIntentButNotMatching = new Intent(Intent.ACTION_VIEW); 123 alsoSentIntentButNotMatching.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 124 getApplicationContext().startActivity(alsoSentIntentButNotMatching); 125 126 Intent expectedIntent = 127 new Intent(Intent.ACTION_VIEW).setData(Uri.parse("http://robolectric.org")); 128 assertThat(getIntents()).comparingElementsUsing(all(action(), data())).contains(expectedIntent); 129 } 130 131 /** 132 * Activity that captures calls to {@link Activity#onActivityResult(int, int, Intent)} 133 * 134 * @noinspection NewClassNamingConvention 135 */ 136 public static class ResultCapturingActivity extends Activity { 137 138 private ActivityResult activityResult; 139 140 @Override onCreate(Bundle savedInstanceState)141 protected void onCreate(Bundle savedInstanceState) { 142 super.onCreate(savedInstanceState); 143 } 144 145 @Override onStart()146 protected void onStart() { 147 super.onStart(); 148 } 149 150 @Override onResume()151 protected void onResume() { 152 super.onResume(); 153 } 154 155 @Override onDestroy()156 protected void onDestroy() { 157 super.onDestroy(); 158 } 159 160 @Override onPause()161 protected void onPause() { 162 super.onPause(); 163 } 164 165 @Override onActivityResult(int requestCode, int resultCode, Intent data)166 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 167 super.onActivityResult(requestCode, resultCode, data); 168 activityResult = new ActivityResult(resultCode, data); 169 } 170 } 171 172 /** 173 * Dummy activity whose calls we intent to we're stubbing out. 174 * 175 * @noinspection NewClassNamingConvention 176 */ 177 public static class DummyActivity extends Activity {} 178 179 @Test intending_callsOnActivityResult()180 public void intending_callsOnActivityResult() { 181 intending(hasComponent(hasClassName(DummyActivity.class.getName()))) 182 .respondWith(new ActivityResult(Activity.RESULT_OK, new Intent().putExtra("key", 123))); 183 184 try (ActivityScenario<ResultCapturingActivity> activityScenario = 185 ActivityScenario.launch(ResultCapturingActivity.class)) { 186 activityScenario.onActivity( 187 activity -> 188 activity.startActivityForResult(new Intent(activity, DummyActivity.class), 0)); 189 190 activityScenario.onActivity( 191 activity -> { 192 assertThat(activity.activityResult.getResultCode()).isEqualTo(Activity.RESULT_OK); 193 assertThat(activity.activityResult.getResultData()).extras().containsKey("key"); 194 }); 195 } 196 } 197 198 @Test browserIntentNotResolved()199 public void browserIntentNotResolved() { 200 Intent browserIntent = new Intent(Intent.ACTION_VIEW); 201 browserIntent.setData(Uri.parse("http://www.robolectric.org")); 202 browserIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 203 204 Intents.intending(hasAction(Intent.ACTION_VIEW)) 205 .respondWithFunction( 206 intent -> { 207 throw new ActivityNotFoundException(); 208 }); 209 210 assertThrows( 211 ActivityNotFoundException.class, 212 () -> getApplicationContext().startActivity(browserIntent)); 213 } 214 } 215