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 
     17 package android.autofillservice.cts;
     18 
     19 import static android.autofillservice.cts.Helper.findNodeByResourceId;
     20 
     21 import static com.google.common.truth.Truth.assertThat;
     22 
     23 import android.app.assist.AssistStructure;
     24 import android.platform.test.annotations.AppModeFull;
     25 import android.support.test.runner.AndroidJUnit4;
     26 import android.view.View;
     27 import android.view.autofill.AutofillValue;
     28 import android.widget.EditText;
     29 
     30 import androidx.annotation.IdRes;
     31 import androidx.annotation.NonNull;
     32 import androidx.annotation.Nullable;
     33 
     34 import org.junit.Before;
     35 import org.junit.Rule;
     36 import org.junit.Test;
     37 import org.junit.runner.RunWith;
     38 
     39 import java.util.function.Consumer;
     40 
     41 @RunWith(AndroidJUnit4.class)
     42 @AppModeFull // Unit test
     43 public class ViewAttributesTest extends AutoFillServiceTestCase {
     44     @Rule
     45     public final AutofillActivityTestRule<ViewAttributesTestActivity> mActivityRule =
     46             new AutofillActivityTestRule<>(ViewAttributesTestActivity.class);
     47 
     48     private ViewAttributesTestActivity mActivity;
     49 
     50     @Before
     51     public void setActivity() {
     52         mActivity = mActivityRule.getActivity();
     53     }
     54 
     55     @Nullable private String[] getHintsFromView(@IdRes int resId) {
     56         return mActivity.findViewById(resId).getAutofillHints();
     57     }
     58 
     59     private void checkEditTextNoHint(@Nullable String[] hints) {
     60         assertThat(hints).isNull();
     61     }
     62 
     63     private void checkEditTextHintCustom(@Nullable String[] hints) {
     64         assertThat(hints).isEqualTo(
     65                 new String[] {mActivity.getString(R.string.new_password_label)});
     66     }
     67 
     68     private void checkEditTextPassword(@Nullable String[] hints) {
     69         assertThat(hints).isEqualTo(new String[] {View.AUTOFILL_HINT_PASSWORD});
     70     }
     71 
     72     private void checkEditTextPhoneName(@Nullable String[] hints) {
     73         assertThat(hints).isEqualTo(
     74                 new String[] {View.AUTOFILL_HINT_PHONE, View.AUTOFILL_HINT_USERNAME});
     75     }
     76 
     77     private void checkEditTextHintsFromArray(@Nullable String[] hints) {
     78         assertThat(hints).isEqualTo(new String[] {"yesterday", "today", "tomorrow", "never"});
     79     }
     80 
     81     @Test
     82     public void checkViewHints() {
     83         checkEditTextNoHint(getHintsFromView(R.id.editTextNoHint));
     84         checkEditTextHintCustom(getHintsFromView(R.id.editTextHintCustom));
     85         checkEditTextPassword(getHintsFromView(R.id.editTextPassword));
     86         checkEditTextPhoneName(getHintsFromView(R.id.editTextPhoneName));
     87         checkEditTextHintsFromArray(getHintsFromView(R.id.editTextHintsFromArray));
     88     }
     89 
     90     @Test
     91     public void checkSetAutoFill() {
     92         View v = mActivity.findViewById(R.id.editTextNoHint);
     93 
     94         v.setAutofillHints((String[]) null);
     95         assertThat(v.getAutofillHints()).isNull();
     96 
     97         v.setAutofillHints(new String[0]);
     98         assertThat(v.getAutofillHints()).isNull();
     99 
    100         v.setAutofillHints(new String[]{View.AUTOFILL_HINT_PASSWORD});
    101         assertThat(v.getAutofillHints()).isEqualTo(new String[]{View.AUTOFILL_HINT_PASSWORD});
    102 
    103         v.setAutofillHints(new String[]{"custom", "value"});
    104         assertThat(v.getAutofillHints()).isEqualTo(new String[]{"custom", "value"});
    105 
    106         v.setAutofillHints("more", "values");
    107         assertThat(v.getAutofillHints()).isEqualTo(new String[]{"more", "values"});
    108 
    109         v.setAutofillHints(
    110                 new String[]{View.AUTOFILL_HINT_PASSWORD, View.AUTOFILL_HINT_EMAIL_ADDRESS});
    111         assertThat(v.getAutofillHints()).isEqualTo(new String[]{View.AUTOFILL_HINT_PASSWORD,
    112                 View.AUTOFILL_HINT_EMAIL_ADDRESS});
    113     }
    114 
    115     /**
    116      * Wait for autofill to be initialized and trigger autofill on a view.
    117      *
    118      * @param view The view to trigger the autofill on
    119      *
    120      * @return The {@link InstrumentedAutoFillService.FillRequest triggered}
    121      */
    122     private InstrumentedAutoFillService.FillRequest startAutoFill(boolean forceAutofill,
    123             @NonNull View view) throws Exception {
    124         if (forceAutofill) {
    125             mActivity.getAutofillManager().requestAutofill(view);
    126         } else {
    127             mActivity.syncRunOnUiThread(() -> {
    128                 view.clearFocus();
    129                 view.requestFocus();
    130             });
    131         }
    132 
    133         InstrumentedAutoFillService.waitUntilConnected();
    134 
    135         return sReplier.getNextFillRequest();
    136     }
    137 
    138     @Nullable private String[] getHintsFromStructure(@NonNull AssistStructure structure,
    139             @NonNull String resName) {
    140         return findNodeByResourceId(structure, resName).getAutofillHints();
    141     }
    142 
    143     private void onAssistStructure(boolean forceAutofill, @NonNull Consumer<AssistStructure> test)
    144             throws Exception {
    145         EditText editTextNoHint = mActivity.findViewById(R.id.editTextNoHint);
    146         mActivity.syncRunOnUiThread(() -> editTextNoHint.setVisibility(View.VISIBLE));
    147 
    148         // Set service.
    149         enableService();
    150 
    151         // Set expectations.
    152         sReplier.addResponse(new CannedFillResponse.CannedDataset.Builder()
    153                 .setField("editTextNoHint", AutofillValue.forText("filled"))
    154                 .setPresentation(createPresentation("dataset"))
    155                 .build());
    156 
    157         // Trigger autofill.
    158         InstrumentedAutoFillService.FillRequest request = startAutoFill(forceAutofill,
    159                 editTextNoHint);
    160 
    161         assertThat(request.contexts.size()).isEqualTo(1);
    162         test.accept(request.contexts.get(0).getStructure());
    163     }
    164 
    165     @Test
    166     public void checkAssistStructureHints() throws Exception {
    167         onAssistStructure(false, (structure) -> {
    168                     // Check autofill hints analogue to #checkViewHints
    169                     checkEditTextNoHint(getHintsFromStructure(structure, "editTextNoHint"));
    170                     checkEditTextHintCustom(getHintsFromStructure(structure, "editTextHintCustom"));
    171                     checkEditTextPassword(getHintsFromStructure(structure, "editTextPassword"));
    172                     checkEditTextPhoneName(getHintsFromStructure(structure, "editTextPhoneName"));
    173                     checkEditTextHintsFromArray(getHintsFromStructure(structure,
    174                             "editTextHintsFromArray"));
    175                 }
    176         );
    177     }
    178 
    179     @Test
    180     public void checkViewLocationInAssistStructure() throws Exception {
    181         onAssistStructure(false, (structure) -> {
    182                     // check size of outerView
    183                     AssistStructure.ViewNode outerView = findNodeByResourceId(structure,
    184                             "outerView");
    185 
    186                     // The size of the view should include all paddings and size of all children
    187                     assertThat(outerView.getHeight()).isEqualTo(
    188                             2             // outerView.top
    189                                     + 11  // nestedView.top
    190                                     + 23  // doubleNestedView.top
    191                                     + 41  // tripleNestedView.height
    192                                     + 47  // secondDoubleNestedView.height
    193                                     + 31  // doubleNestedView.bottom
    194                                     + 17  // nestedView.bottom
    195                                     + 5); // outerView.bottom
    196                     assertThat(outerView.getWidth()).isEqualTo(
    197                             7                     // outerView.left
    198                                     + 19          // nestedView.left
    199                                     + Math.max(37 // doubleNestedView.left
    200                                             + 43  // tripleNestedView.width
    201                                             + 29, // doubleNestedView.right
    202                                     53)           // secondDoubleNestedView.width
    203                                     + 13          // nestedView.right
    204                                     + 3);         // outerView.right
    205 
    206 
    207                     // The nestedView is suppressed, hence the structure should be
    208                     //
    209                     // outerView
    210                     //  doubleNestedView left=26 top=13
    211                     //   tripleNestedView left=37 top=23
    212                     //  secondDoubleNestedView left=26 top=108
    213 
    214                     assertThat(outerView.getChildCount()).isEqualTo(2);
    215                     AssistStructure.ViewNode doubleNestedView;
    216                     AssistStructure.ViewNode secondDoubleNestedView;
    217                     if (outerView.getChildAt(0).getIdEntry().equals("doubleNestedView")) {
    218                         doubleNestedView = outerView.getChildAt(0);
    219                         secondDoubleNestedView = outerView.getChildAt(1);
    220                     } else {
    221                         secondDoubleNestedView = outerView.getChildAt(0);
    222                         doubleNestedView = outerView.getChildAt(1);
    223                     }
    224                     assertThat(doubleNestedView.getIdEntry()).isEqualTo("doubleNestedView");
    225                     assertThat(secondDoubleNestedView.getIdEntry()).isEqualTo
    226                             ("secondDoubleNestedView");
    227 
    228                     // The location of the doubleNestedView should include all suppressed parent's
    229                     // offset
    230                     assertThat(doubleNestedView.getLeft()).isEqualTo(
    231                             7              // outerView.left
    232                                     + 19); // nestedView.left
    233                     assertThat(doubleNestedView.getTop()).isEqualTo(
    234                             2              // outerView.top
    235                                     + 11); // nestedView.top
    236 
    237                     // The location of the tripleNestedView should be relative to it's parent
    238                     assertThat(doubleNestedView.getChildCount()).isEqualTo(1);
    239                     AssistStructure.ViewNode tripleNestedView = doubleNestedView.getChildAt(0);
    240                     assertThat(doubleNestedView.getIdEntry()).isEqualTo("doubleNestedView");
    241 
    242                     assertThat(tripleNestedView.getLeft()).isEqualTo(37); // doubleNestedView.left
    243                     assertThat(tripleNestedView.getTop()).isEqualTo(23); // doubleNestedView.top
    244                 }
    245         );
    246     }
    247 
    248     @Test
    249     public void checkViewLocationInAssistStructureAfterForceAutofill() throws Exception {
    250         onAssistStructure(true, (structure) -> {
    251                     AssistStructure.ViewNode outerView = findNodeByResourceId(structure,
    252                             "outerView");
    253 
    254                     // The structure should be
    255                     //
    256                     // outerView
    257                     //  nestedView left=7 top=2
    258                     //   doubleNestedView left=19 top=11
    259                     //    tripleNestedView left=37 top=23
    260                     //   secondDoubleNestedView left=19 top=106
    261 
    262                     // Test only what is different from #checkViewLocationInAssistStructure
    263                     assertThat(outerView.getChildCount()).isEqualTo(1);
    264 
    265                     AssistStructure.ViewNode nestedView = outerView.getChildAt(0);
    266                     assertThat(nestedView.getIdEntry()).isEqualTo("nestedView");
    267                     assertThat(nestedView.getLeft()).isEqualTo(7); // outerView.left
    268                     assertThat(nestedView.getTop()).isEqualTo(2); // outerView.top
    269 
    270                     assertThat(nestedView.getChildCount()).isEqualTo(2);
    271                     AssistStructure.ViewNode doubleNestedView;
    272                     if (nestedView.getChildAt(0).getIdEntry().equals("doubleNestedView")) {
    273                         doubleNestedView = nestedView.getChildAt(0);
    274                     } else {
    275                         doubleNestedView = nestedView.getChildAt(1);
    276                     }
    277                     assertThat(doubleNestedView.getIdEntry()).isEqualTo("doubleNestedView");
    278 
    279                     assertThat(doubleNestedView.getLeft()).isEqualTo(19); // nestedView.left
    280                     assertThat(doubleNestedView.getTop()).isEqualTo(11); // nestedView.top
    281                 }
    282         );
    283     }
    284 }
    285