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