1 /* 2 * Copyright (C) 2020 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.inline; 18 19 import static android.autofillservice.cts.activities.WebViewActivity.HTML_NAME_PASSWORD; 20 import static android.autofillservice.cts.activities.WebViewActivity.HTML_NAME_USERNAME; 21 import static android.autofillservice.cts.testcore.InstrumentedAutoFillServiceInlineEnabled.SERVICE_NAME; 22 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD; 23 24 import static com.google.common.truth.Truth.assertThat; 25 26 import android.app.assist.AssistStructure.ViewNode; 27 import android.autofillservice.cts.activities.MyWebView; 28 import android.autofillservice.cts.activities.WebViewActivity; 29 import android.autofillservice.cts.commontests.AbstractWebViewTestCase; 30 import android.autofillservice.cts.testcore.AutofillActivityTestRule; 31 import android.autofillservice.cts.testcore.CannedFillResponse; 32 import android.autofillservice.cts.testcore.CannedFillResponse.CannedDataset; 33 import android.autofillservice.cts.testcore.Helper; 34 import android.autofillservice.cts.testcore.InlineUiBot; 35 import android.autofillservice.cts.testcore.InstrumentedAutoFillService.FillRequest; 36 import android.autofillservice.cts.testcore.InstrumentedAutoFillService.SaveRequest; 37 import android.util.Log; 38 import android.view.KeyEvent; 39 import android.view.ViewStructure.HtmlInfo; 40 41 import androidx.test.filters.FlakyTest; 42 import androidx.test.uiautomator.UiObject2; 43 44 import org.junit.Assume; 45 import org.junit.Test; 46 import org.junit.rules.TestRule; 47 48 @FlakyTest(bugId = 162372863) 49 public class InlineWebViewActivityTest extends AbstractWebViewTestCase<WebViewActivity> { 50 51 private static final String TAG = "InlineWebViewActivityTest"; 52 private WebViewActivity mActivity; 53 InlineWebViewActivityTest()54 public InlineWebViewActivityTest() { 55 super(getInlineUiBot()); 56 } 57 58 @Override getActivityRule()59 protected AutofillActivityTestRule<WebViewActivity> getActivityRule() { 60 return new AutofillActivityTestRule<WebViewActivity>(WebViewActivity.class) { 61 @Override 62 protected void beforeActivityLaunched() { 63 super.beforeActivityLaunched(); 64 Log.i(TAG, "Setting service before launching the activity"); 65 enableService(); 66 } 67 68 @Override 69 protected void afterActivityLaunched() { 70 mActivity = getActivity(); 71 } 72 }; 73 } 74 75 @Override 76 protected void enableService() { 77 Helper.enableAutofillService(SERVICE_NAME); 78 } 79 80 @Override 81 protected boolean isInlineMode() { 82 return true; 83 } 84 85 @Override 86 public TestRule getMainTestRule() { 87 return InlineUiBot.annotateRule(super.getMainTestRule()); 88 } 89 90 @Test 91 public void testAutofillNoDatasets() throws Exception { 92 // Set service. 93 enableService(); 94 95 // Load WebView 96 mActivity.loadWebView(mUiBot); 97 98 // Set expectations. 99 sReplier.addResponse(CannedFillResponse.NO_RESPONSE); 100 101 // Trigger autofill. 102 mActivity.getUsernameInput().click(); 103 sReplier.getNextFillRequest(); 104 105 // Assert not shown. 106 mUiBot.assertNoDatasetsEver(); 107 } 108 109 @Test 110 public void testAutofillOneDataset() throws Exception { 111 // TODO(b/226240255) WebView does not inform the autofill service about editText (re-)entry 112 Assume.assumeFalse(isPreventImeStartup()); 113 114 // TODO(b/187664861): Find better solution for small display device. 115 mUiBot.assumeMinimumResolution(500); 116 // Set service. 117 enableService(); 118 119 // Load WebView 120 final MyWebView myWebView = mActivity.loadWebView(mUiBot); 121 mUiBot.waitForIdleSync(); 122 // Initial check to make sure autofill is enabled in the application context 123 Helper.assertAutofillEnabled(myWebView.getContext(), true); 124 125 // Set expectations. 126 myWebView.expectAutofill("dude", "sweet"); 127 sReplier.addResponse(new CannedDataset.Builder() 128 .setField(HTML_NAME_USERNAME, "dude") 129 .setField(HTML_NAME_PASSWORD, "sweet") 130 .setPresentation(createPresentation("The Dude")) 131 .setInlinePresentation(createInlinePresentation("The Dude")) 132 .build()); 133 134 // Trigger autofill. 135 mActivity.getUsernameInput().click(); 136 mUiBot.waitForIdleSync(); 137 final FillRequest fillRequest = sReplier.getNextFillRequest(); 138 mUiBot.assertDatasets("The Dude"); 139 140 // Change focus around 141 mActivity.getUsernameLabel().click(); 142 mUiBot.waitForIdleSync(); 143 mUiBot.assertNoDatasets(); 144 mActivity.getPasswordInput().click(); 145 mUiBot.waitForIdleSync(); 146 final UiObject2 datasetPicker = mUiBot.assertDatasets("The Dude"); 147 148 // Now Autofill it. 149 mUiBot.selectDataset(datasetPicker, "The Dude"); 150 myWebView.assertAutofilled(); 151 mUiBot.assertNoDatasets(); 152 153 try { 154 final ViewNode webViewNode = 155 Helper.findWebViewNodeByFormName(fillRequest.structure, "FORM AM I"); 156 assertThat(webViewNode.getClassName()).isEqualTo("android.webkit.WebView"); 157 assertThat(webViewNode.getWebDomain()).isEqualTo(WebViewActivity.FAKE_DOMAIN); 158 assertThat(webViewNode.getWebScheme()).isEqualTo("https"); 159 160 final ViewNode usernameNode = 161 Helper.findNodeByHtmlName(fillRequest.structure, HTML_NAME_USERNAME); 162 Helper.assertTextIsSanitized(usernameNode); 163 final HtmlInfo usernameHtmlInfo = Helper.assertHasHtmlTag(usernameNode, "input"); 164 Helper.assertHasAttribute(usernameHtmlInfo, "type", "text"); 165 Helper.assertHasAttribute(usernameHtmlInfo, "name", "username"); 166 assertThat(usernameNode.isFocused()).isTrue(); 167 assertThat(usernameNode.getAutofillHints()).asList().containsExactly("username"); 168 assertThat(usernameNode.getHint()).isEqualTo("There's no place like a holder"); 169 170 final ViewNode passwordNode = 171 Helper.findNodeByHtmlName(fillRequest.structure, HTML_NAME_PASSWORD); 172 Helper.assertTextIsSanitized(passwordNode); 173 final HtmlInfo passwordHtmlInfo = Helper.assertHasHtmlTag(passwordNode, "input"); 174 Helper.assertHasAttribute(passwordHtmlInfo, "type", "password"); 175 Helper.assertHasAttribute(passwordHtmlInfo, "name", "password"); 176 assertThat(passwordNode.getAutofillHints()).asList() 177 .containsExactly("current-password"); 178 assertThat(passwordNode.getHint()).isEqualTo("Holder it like it cannnot passer a word"); 179 assertThat(passwordNode.isFocused()).isFalse(); 180 } catch (RuntimeException | Error e) { 181 Helper.dumpStructure("failed on testAutofillOneDataset()", fillRequest.structure); 182 throw e; 183 } 184 } 185 186 @Test 187 public void testAutofillAndSave() throws Exception { 188 // Set service. 189 enableService(); 190 191 // Load WebView 192 final MyWebView myWebView = mActivity.loadWebView(mUiBot); 193 194 // Set expectations. 195 myWebView.expectAutofill("dude", "sweet"); 196 sReplier.addResponse(new CannedFillResponse.Builder() 197 .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, 198 HTML_NAME_USERNAME, HTML_NAME_PASSWORD) 199 .addDataset(new CannedDataset.Builder() 200 .setField(HTML_NAME_USERNAME, "dude") 201 .setField(HTML_NAME_PASSWORD, "sweet") 202 .setPresentation(createPresentation("The Dude")) 203 .setInlinePresentation(createInlinePresentation("The Dude")) 204 .build()) 205 .build()); 206 207 208 // Trigger autofill. 209 mActivity.getUsernameInput().click(); 210 final FillRequest fillRequest = sReplier.getNextFillRequest(); 211 mUiBot.assertDatasets("The Dude"); 212 213 // Assert structure passed to service. 214 final ViewNode usernameNode = Helper.findNodeByHtmlName(fillRequest.structure, 215 HTML_NAME_USERNAME); 216 Helper.assertTextIsSanitized(usernameNode); 217 assertThat(usernameNode.isFocused()).isTrue(); 218 assertThat(usernameNode.getAutofillHints()).asList().containsExactly("username"); 219 final ViewNode passwordNode = Helper.findNodeByHtmlName(fillRequest.structure, 220 HTML_NAME_PASSWORD); 221 Helper.assertTextIsSanitized(passwordNode); 222 assertThat(passwordNode.getAutofillHints()).asList().containsExactly("current-password"); 223 assertThat(passwordNode.isFocused()).isFalse(); 224 225 // Autofill it. 226 mUiBot.selectDataset("The Dude"); 227 myWebView.assertAutofilled(); 228 229 // Now trigger save. 230 mActivity.getUsernameInput().click(); 231 mActivity.dispatchKeyPress(KeyEvent.KEYCODE_U); 232 mActivity.getPasswordInput().click(); 233 mActivity.dispatchKeyPress(KeyEvent.KEYCODE_P); 234 mActivity.getLoginButton().click(); 235 236 // Assert save UI shown. 237 mUiBot.updateForAutofill(true, SAVE_DATA_TYPE_PASSWORD); 238 239 // Assert results 240 final SaveRequest saveRequest = sReplier.getNextSaveRequest(); 241 final ViewNode usernameNode2 = Helper.findNodeByHtmlName(saveRequest.structure, 242 HTML_NAME_USERNAME); 243 final ViewNode passwordNode2 = Helper.findNodeByHtmlName(saveRequest.structure, 244 HTML_NAME_PASSWORD); 245 246 Helper.assertTextAndValue(usernameNode2, "dudeu"); 247 Helper.assertTextAndValue(passwordNode2, "sweetp"); 248 } 249 } 250