1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.autofillservice.cts.commontests; 17 18 import static com.google.common.truth.Truth.assertThat; 19 20 import static org.junit.Assume.assumeTrue; 21 22 import android.app.Activity; 23 import android.app.PendingIntent; 24 import android.autofillservice.cts.R; 25 import android.autofillservice.cts.activities.AbstractAutoFillActivity; 26 import android.autofillservice.cts.testcore.Helper; 27 import android.autofillservice.cts.testcore.UiBot; 28 import android.content.Intent; 29 import android.service.autofill.CustomDescription; 30 import android.support.test.uiautomator.By; 31 import android.support.test.uiautomator.UiObject2; 32 import android.widget.RemoteViews; 33 34 import androidx.annotation.NonNull; 35 import androidx.test.filters.FlakyTest; 36 37 import org.junit.Test; 38 39 /** 40 * Template for tests cases that test what happens when a link in the {@link CustomDescription} is 41 * tapped by the user. 42 * 43 * <p>It must be extend by 2 sub-class to provide tests for the 2 distinct scenarios: 44 * <ul> 45 * <li>Save is triggered by 1st activity finishing and launching a 2nd activity. 46 * <li>Save is triggered by explicit {@link android.view.autofill.AutofillManager#commit()} call 47 * and shown in the same activity. 48 * </ul> 49 * 50 * <p>The overall behavior should be the same in both cases, although the implementation of the 51 * tests per se will be sligthly different. 52 */ 53 public abstract class CustomDescriptionWithLinkTestCase<A extends AbstractAutoFillActivity> extends 54 AutoFillServiceTestCase.AutoActivityLaunch<A> { 55 56 private static final String ID_LINK = "link"; 57 58 private final Class<A> mActivityClass; 59 60 protected A mActivity; 61 CustomDescriptionWithLinkTestCase(@onNull Class<A> activityClass)62 protected CustomDescriptionWithLinkTestCase(@NonNull Class<A> activityClass) { 63 mActivityClass = activityClass; 64 } 65 startActivity()66 protected void startActivity() { 67 startActivity(false); 68 } 69 startActivity(boolean remainOnRecents)70 protected void startActivity(boolean remainOnRecents) { 71 final Intent intent = new Intent(mContext, mActivityClass); 72 if (remainOnRecents) { 73 intent.setFlags( 74 Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS | Intent.FLAG_ACTIVITY_NEW_TASK); 75 } 76 mActivity = launchActivity(intent); 77 } 78 79 /** 80 * Tests scenarios when user taps a link in the custom description and then taps back: 81 * the Save UI should have been restored. 82 */ 83 @Test testTapLink_tapBack()84 public final void testTapLink_tapBack() throws Exception { 85 saveUiRestoredAfterTappingLinkTest(PostSaveLinkTappedAction.TAP_BACK_BUTTON); 86 } 87 88 /** 89 * Tests scenarios when user taps a link in the custom description, change the screen 90 * orientation while the new activity is show, then taps back: 91 * the Save UI should have been restored. 92 */ 93 @Test testTapLink_changeOrientationThenTapBack()94 public final void testTapLink_changeOrientationThenTapBack() throws Exception { 95 assumeTrue("Rotation is supported", Helper.isRotationSupported(mContext)); 96 97 mUiBot.assumeMinimumResolution(500); 98 mUiBot.setScreenOrientation(UiBot.PORTRAIT); 99 try { 100 saveUiRestoredAfterTappingLinkTest( 101 PostSaveLinkTappedAction.ROTATE_THEN_TAP_BACK_BUTTON); 102 } finally { 103 try { 104 mUiBot.setScreenOrientation(UiBot.PORTRAIT); 105 cleanUpAfterScreenOrientationIsBackToPortrait(); 106 } catch (Exception e) { 107 mSafeCleanerRule.add(e); 108 } finally { 109 mUiBot.resetScreenResolution(); 110 } 111 } 112 } 113 114 /** 115 * Tests scenarios when user taps a link in the custom description, then the new activity 116 * finishes: 117 * the Save UI should have been restored. 118 */ 119 @Test testTapLink_finishActivity()120 public final void testTapLink_finishActivity() throws Exception { 121 saveUiRestoredAfterTappingLinkTest(PostSaveLinkTappedAction.FINISH_ACTIVITY); 122 } 123 saveUiRestoredAfterTappingLinkTest(PostSaveLinkTappedAction type)124 protected abstract void saveUiRestoredAfterTappingLinkTest(PostSaveLinkTappedAction type) 125 throws Exception; 126 cleanUpAfterScreenOrientationIsBackToPortrait()127 protected void cleanUpAfterScreenOrientationIsBackToPortrait() throws Exception { 128 } 129 130 /** 131 * Tests scenarios when user taps a link in the custom description, taps back to return to the 132 * activity with the Save UI, and touch outside the Save UI to dismiss it. 133 * 134 * <p>Then user starts a new session by focusing in a field. 135 */ 136 @Test testTapLink_tapBack_thenStartOverByTouchOutsideAndFocus()137 public final void testTapLink_tapBack_thenStartOverByTouchOutsideAndFocus() 138 throws Exception { 139 tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TOUCH_OUTSIDE, false); 140 } 141 142 /** 143 * Tests scenarios when user taps a link in the custom description, taps back to return to the 144 * activity with the Save UI, and touch outside the Save UI to dismiss it. 145 * 146 * <p>Then user starts a new session by forcing autofill. 147 */ 148 @Test testTapLink_tapBack_thenStartOverByTouchOutsideAndManualRequest()149 public void testTapLink_tapBack_thenStartOverByTouchOutsideAndManualRequest() 150 throws Exception { 151 tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TOUCH_OUTSIDE, true); 152 } 153 154 /** 155 * Tests scenarios when user taps a link in the custom description, taps back to return to the 156 * activity with the Save UI, and tap the "No" button to dismiss it. 157 * 158 * <p>Then user starts a new session by focusing in a field. 159 */ 160 @Test testTapLink_tapBack_thenStartOverBySayingNoAndFocus()161 public final void testTapLink_tapBack_thenStartOverBySayingNoAndFocus() 162 throws Exception { 163 tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TAP_NO_ON_SAVE_UI, 164 false); 165 } 166 167 /** 168 * Tests scenarios when user taps a link in the custom description, taps back to return to the 169 * activity with the Save UI, and tap the "No" button to dismiss it. 170 * 171 * <p>Then user starts a new session by forcing autofill. 172 */ 173 @Test testTapLink_tapBack_thenStartOverBySayingNoAndManualRequest()174 public final void testTapLink_tapBack_thenStartOverBySayingNoAndManualRequest() 175 throws Exception { 176 tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TAP_NO_ON_SAVE_UI, true); 177 } 178 179 /** 180 * Tests scenarios when user taps a link in the custom description, taps back to return to the 181 * activity with the Save UI, and the "Yes" button to save it. 182 * 183 * <p>Then user starts a new session by focusing in a field. 184 */ 185 @Test testTapLink_tapBack_thenStartOverBySayingYesAndFocus()186 public final void testTapLink_tapBack_thenStartOverBySayingYesAndFocus() 187 throws Exception { 188 tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TAP_YES_ON_SAVE_UI, 189 false); 190 } 191 192 /** 193 * Tests scenarios when user taps a link in the custom description, taps back to return to the 194 * activity with the Save UI, and the "Yes" button to save it. 195 * 196 * <p>Then user starts a new session by forcing autofill. 197 */ 198 @Test testTapLink_tapBack_thenStartOverBySayingYesAndManualRequest()199 public final void testTapLink_tapBack_thenStartOverBySayingYesAndManualRequest() 200 throws Exception { 201 tapLinkThenTapBackThenStartOverTest(PostSaveLinkTappedAction.TAP_YES_ON_SAVE_UI, true); 202 } 203 tapLinkThenTapBackThenStartOverTest( PostSaveLinkTappedAction action, boolean manualRequest)204 protected abstract void tapLinkThenTapBackThenStartOverTest( 205 PostSaveLinkTappedAction action, boolean manualRequest) throws Exception; 206 207 /** 208 * Tests scenarios when user taps a link in the custom description, then re-launches the 209 * original activity: 210 * the Save UI should have been canceled. 211 */ 212 @Test testTapLink_backToPreviousActivityByLaunchingIt()213 public final void testTapLink_backToPreviousActivityByLaunchingIt() 214 throws Exception { 215 saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction.LAUNCH_PREVIOUS_ACTIVITY); 216 } 217 218 /** 219 * Tests scenarios when user taps a link in the custom description, then launches a 3rd 220 * activity: 221 * the Save UI should have been canceled. 222 */ 223 @Test testTapLink_launchNewActivityThenTapBack()224 public final void testTapLink_launchNewActivityThenTapBack() throws Exception { 225 saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction.LAUNCH_NEW_ACTIVITY); 226 } 227 saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction type)228 protected abstract void saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction type) 229 throws Exception; 230 231 @Test 232 @FlakyTest(bugId = 177259617) testTapLink_launchTrampolineActivityThenTapBackAndStartNewSession()233 public final void testTapLink_launchTrampolineActivityThenTapBackAndStartNewSession() 234 throws Exception { 235 // Reset AutofillOptions to avoid cts package was added to augmented autofill allowlist. 236 Helper.resetApplicationAutofillOptions(sContext); 237 238 tapLinkLaunchTrampolineActivityThenTapBackAndStartNewSessionTest(); 239 240 // Clear AutofillOptions. 241 Helper.clearApplicationAutofillOptions(sContext); 242 } 243 tapLinkLaunchTrampolineActivityThenTapBackAndStartNewSessionTest()244 protected abstract void tapLinkLaunchTrampolineActivityThenTapBackAndStartNewSessionTest() 245 throws Exception; 246 247 @Test testTapLinkAfterUpdateAppliedToLinkView()248 public final void testTapLinkAfterUpdateAppliedToLinkView() throws Exception { 249 tapLinkAfterUpdateAppliedTest(true); 250 } 251 252 @Test testTapLinkAfterUpdateAppliedToAnotherView()253 public final void testTapLinkAfterUpdateAppliedToAnotherView() throws Exception { 254 tapLinkAfterUpdateAppliedTest(false); 255 } 256 tapLinkAfterUpdateAppliedTest(boolean updateLinkView)257 protected abstract void tapLinkAfterUpdateAppliedTest(boolean updateLinkView) throws Exception; 258 259 public enum PostSaveLinkTappedAction { 260 TAP_BACK_BUTTON, 261 ROTATE_THEN_TAP_BACK_BUTTON, 262 FINISH_ACTIVITY, 263 LAUNCH_NEW_ACTIVITY, 264 LAUNCH_PREVIOUS_ACTIVITY, 265 TOUCH_OUTSIDE, 266 TAP_NO_ON_SAVE_UI, 267 TAP_YES_ON_SAVE_UI 268 } 269 startActivityOnNewTask(Class<?> clazz)270 protected final void startActivityOnNewTask(Class<?> clazz) { 271 final Intent intent = new Intent(mContext, clazz); 272 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 273 mContext.startActivity(intent); 274 } 275 newTemplate()276 protected RemoteViews newTemplate() { 277 final RemoteViews presentation = new RemoteViews(mPackageName, 278 R.layout.custom_description_with_link); 279 return presentation; 280 } 281 newCustomDescriptionBuilder( Class<? extends Activity> activityClass)282 protected final CustomDescription.Builder newCustomDescriptionBuilder( 283 Class<? extends Activity> activityClass) { 284 final Intent intent = new Intent(mContext, activityClass); 285 intent.setFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS | Intent.FLAG_ACTIVITY_NEW_DOCUMENT); 286 return newCustomDescriptionBuilder(intent); 287 } 288 newCustomDescription( Class<? extends Activity> activityClass)289 protected final CustomDescription newCustomDescription( 290 Class<? extends Activity> activityClass) { 291 return newCustomDescriptionBuilder(activityClass).build(); 292 } 293 newCustomDescriptionBuilder(Intent intent)294 protected final CustomDescription.Builder newCustomDescriptionBuilder(Intent intent) { 295 final RemoteViews presentation = newTemplate(); 296 final PendingIntent pendingIntent = 297 PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_MUTABLE); 298 presentation.setOnClickPendingIntent(R.id.link, pendingIntent); 299 return new CustomDescription.Builder(presentation); 300 } 301 newCustomDescription(Intent intent)302 protected final CustomDescription newCustomDescription(Intent intent) { 303 return newCustomDescriptionBuilder(intent).build(); 304 } 305 assertSaveUiWithLinkIsShown(int saveType)306 protected final UiObject2 assertSaveUiWithLinkIsShown(int saveType) throws Exception { 307 return assertSaveUiWithLinkIsShown(saveType, "DON'T TAP ME!"); 308 } 309 assertSaveUiWithLinkIsShown(int saveType, String expectedText)310 protected final UiObject2 assertSaveUiWithLinkIsShown(int saveType, String expectedText) 311 throws Exception { 312 // First make sure the UI is shown... 313 final UiObject2 saveUi = mUiBot.assertSaveShowing(saveType); 314 // Then make sure it does have the custom view with link on it... 315 final UiObject2 link = getLink(saveUi); 316 assertThat(link.getText()).isEqualTo(expectedText); 317 return saveUi; 318 } 319 getLink(final UiObject2 container)320 protected final UiObject2 getLink(final UiObject2 container) { 321 final UiObject2 link = container.findObject(By.res(mPackageName, ID_LINK)); 322 assertThat(link).isNotNull(); 323 return link; 324 } 325 tapSaveUiLink(UiObject2 saveUi)326 protected final void tapSaveUiLink(UiObject2 saveUi) { 327 getLink(saveUi).click(); 328 } 329 } 330