1 // Copyright 2020 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.android_webview.test.devui; 6 7 import static androidx.test.espresso.Espresso.onData; 8 import static androidx.test.espresso.Espresso.onView; 9 import static androidx.test.espresso.action.ViewActions.click; 10 import static androidx.test.espresso.action.ViewActions.longClick; 11 import static androidx.test.espresso.assertion.ViewAssertions.matches; 12 import static androidx.test.espresso.intent.Intents.assertNoUnverifiedIntents; 13 import static androidx.test.espresso.intent.Intents.intended; 14 import static androidx.test.espresso.intent.Intents.intending; 15 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; 16 import static androidx.test.espresso.matcher.ViewMatchers.withId; 17 import static androidx.test.espresso.matcher.ViewMatchers.withText; 18 19 import static org.hamcrest.MatcherAssert.assertThat; 20 import static org.hamcrest.Matchers.anything; 21 import static org.hamcrest.Matchers.equalTo; 22 import static org.hamcrest.Matchers.is; 23 import static org.hamcrest.Matchers.not; 24 25 import static org.chromium.android_webview.test.devui.DeveloperUiTestUtils.getClipBoardTextOnUiThread; 26 import static org.chromium.android_webview.test.devui.DeveloperUiTestUtils.withCount; 27 28 import android.app.Activity; 29 import android.app.Instrumentation.ActivityResult; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.pm.PackageInfo; 33 import android.os.Build; 34 import android.provider.Settings; 35 import android.support.test.InstrumentationRegistry; 36 37 import androidx.test.espresso.intent.matcher.IntentMatchers; 38 import androidx.test.espresso.intent.rule.IntentsTestRule; 39 import androidx.test.filters.MediumTest; 40 41 import org.junit.After; 42 import org.junit.Assume; 43 import org.junit.Rule; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 47 import org.chromium.android_webview.devui.MainActivity; 48 import org.chromium.android_webview.devui.R; 49 import org.chromium.android_webview.devui.WebViewPackageError; 50 import org.chromium.android_webview.devui.util.WebViewPackageHelper; 51 import org.chromium.android_webview.test.AwJUnit4ClassRunner; 52 import org.chromium.base.test.util.Batch; 53 import org.chromium.base.test.util.Feature; 54 55 import java.util.Locale; 56 57 /** 58 * UI tests for the developer UI's HomeFragment. 59 */ 60 @RunWith(AwJUnit4ClassRunner.class) 61 @Batch(Batch.PER_CLASS) 62 public class HomeFragmentTest { 63 public static final PackageInfo FAKE_WEBVIEW_PACKAGE = new PackageInfo(); 64 static { 65 FAKE_WEBVIEW_PACKAGE.packageName = "org.chromium.fake_webview"; 66 FAKE_WEBVIEW_PACKAGE.versionCode = 123456789; 67 FAKE_WEBVIEW_PACKAGE.versionName = "999.888.777.666"; 68 } 69 70 @Rule 71 public IntentsTestRule mRule = 72 new IntentsTestRule<MainActivity>(MainActivity.class, false, false); 73 74 @After tearDown()75 public void tearDown() { 76 // Activity is launched, i.e the test is not skipped. 77 if (mRule.getActivity() != null) { 78 // Tests are responsible for verifying every Intent they trigger. 79 assertNoUnverifiedIntents(); 80 } 81 } 82 launchHomeFragment()83 private void launchHomeFragment() { 84 Intent intent = new Intent(); 85 intent.putExtra(MainActivity.FRAGMENT_ID_INTENT_EXTRA, MainActivity.FRAGMENT_ID_HOME); 86 mRule.launchActivity(intent); 87 88 // Stub all external intents, to avoid launching other apps (ex. system browser), has to be 89 // done after launching the activity. 90 intending(not(IntentMatchers.isInternal())) 91 .respondWith(new ActivityResult(Activity.RESULT_OK, null)); 92 } 93 94 @Test 95 @MediumTest 96 @Feature({"AndroidWebView"}) 97 // Test when the system WebView provider is the same package from which the developer UI is 98 // launched. testSameWebViewPackage()99 public void testSameWebViewPackage() throws Throwable { 100 Context context = InstrumentationRegistry.getTargetContext(); 101 // Inject test app package as the current WebView package. 102 WebViewPackageHelper.setCurrentWebViewPackageForTesting( 103 WebViewPackageHelper.getContextPackageInfo(context)); 104 launchHomeFragment(); 105 106 // No error messages is displayed. 107 onView(withId(R.id.main_error_view)).check(matches(not(isDisplayed()))); 108 109 onView(withId(R.id.main_info_list)).check(matches(withCount(2))); 110 111 PackageInfo currentWebViewPackage = WebViewPackageHelper.getCurrentWebViewPackage(context); 112 String expectedWebViewPackageInfo = 113 String.format(Locale.US, "%s (%s/%s)", currentWebViewPackage.packageName, 114 currentWebViewPackage.versionName, currentWebViewPackage.versionCode); 115 onData(anything()) 116 .atPosition(0) 117 .onChildView(withId(android.R.id.text1)) 118 .check(matches(withText("WebView package"))); 119 onData(anything()) 120 .atPosition(0) 121 .onChildView(withId(android.R.id.text2)) 122 .check(matches(withText(expectedWebViewPackageInfo))); 123 124 String expectedDeviceInfo = 125 String.format(Locale.US, "%s - %s", Build.MODEL, Build.FINGERPRINT); 126 onData(anything()) 127 .atPosition(1) 128 .onChildView(withId(android.R.id.text1)) 129 .check(matches(withText("Device info"))); 130 onData(anything()) 131 .atPosition(1) 132 .onChildView(withId(android.R.id.text2)) 133 .check(matches(withText(expectedDeviceInfo))); 134 } 135 136 @Test 137 @MediumTest 138 @Feature({"AndroidWebView"}) 139 // Test when the system WebView provider is different from the package from which the developer 140 // UI is launched. testDifferentWebViewPackage()141 public void testDifferentWebViewPackage() throws Throwable { 142 Context context = InstrumentationRegistry.getTargetContext(); 143 // Inject a dummy PackageInfo as the current WebView package to make sure it will always be 144 // different from the test's app package. 145 WebViewPackageHelper.setCurrentWebViewPackageForTesting(FAKE_WEBVIEW_PACKAGE); 146 launchHomeFragment(); 147 148 onView(withId(R.id.main_info_list)).check(matches(withCount(3))); 149 150 String expectedWebViewPackageInfo = 151 String.format(Locale.US, "%s (%s/%s)", FAKE_WEBVIEW_PACKAGE.packageName, 152 FAKE_WEBVIEW_PACKAGE.versionName, FAKE_WEBVIEW_PACKAGE.versionCode); 153 onData(anything()) 154 .atPosition(0) 155 .onChildView(withId(android.R.id.text1)) 156 .check(matches(withText("WebView package"))); 157 onData(anything()) 158 .atPosition(0) 159 .onChildView(withId(android.R.id.text2)) 160 .check(matches(withText(expectedWebViewPackageInfo))); 161 162 PackageInfo devUiPackage = WebViewPackageHelper.getContextPackageInfo(context); 163 String expectedDevUiInfo = String.format(Locale.US, "%s (%s/%s)", devUiPackage.packageName, 164 devUiPackage.versionName, devUiPackage.versionCode); 165 onData(anything()) 166 .atPosition(1) 167 .onChildView(withId(android.R.id.text1)) 168 .check(matches(withText("DevTools package"))); 169 onData(anything()) 170 .atPosition(1) 171 .onChildView(withId(android.R.id.text2)) 172 .check(matches(withText(expectedDevUiInfo))); 173 174 String expectedDeviceInfo = 175 String.format(Locale.US, "%s - %s", Build.MODEL, Build.FINGERPRINT); 176 onData(anything()) 177 .atPosition(2) 178 .onChildView(withId(android.R.id.text1)) 179 .check(matches(withText("Device info"))); 180 onData(anything()) 181 .atPosition(2) 182 .onChildView(withId(android.R.id.text2)) 183 .check(matches(withText(expectedDeviceInfo))); 184 } 185 186 @Test 187 @MediumTest 188 @Feature({"AndroidWebView"}) testLongPressCopy()189 public void testLongPressCopy() throws Throwable { 190 Context context = InstrumentationRegistry.getTargetContext(); 191 // Inject a dummy PackageInfo as the current WebView package to make sure it will always be 192 // different from the test's app package. 193 WebViewPackageHelper.setCurrentWebViewPackageForTesting(FAKE_WEBVIEW_PACKAGE); 194 launchHomeFragment(); 195 196 onView(withText("WebView package")).perform(longClick()); 197 String expectedWebViewInfo = 198 String.format(Locale.US, "%s (%s/%s)", FAKE_WEBVIEW_PACKAGE.packageName, 199 FAKE_WEBVIEW_PACKAGE.versionName, FAKE_WEBVIEW_PACKAGE.versionCode); 200 assertThat(getClipBoardTextOnUiThread(context), is(equalTo(expectedWebViewInfo))); 201 202 onView(withText("DevTools package")).perform(longClick()); 203 PackageInfo devUiPackage = WebViewPackageHelper.getContextPackageInfo(context); 204 String expectedDevUiInfo = String.format(Locale.US, "%s (%s/%s)", devUiPackage.packageName, 205 devUiPackage.versionName, devUiPackage.versionCode); 206 assertThat(getClipBoardTextOnUiThread(context), is(equalTo(expectedDevUiInfo))); 207 208 onView(withText("Device info")).perform(longClick()); 209 String expectedDeviceInfo = 210 String.format(Locale.US, "%s - %s", Build.MODEL, Build.FINGERPRINT); 211 assertThat(getClipBoardTextOnUiThread(context), is(equalTo(expectedDeviceInfo))); 212 } 213 214 @Test 215 @MediumTest 216 @Feature({"AndroidWebView"}) testDifferentWebViewPackageError_bannerMessage_postNougat()217 public void testDifferentWebViewPackageError_bannerMessage_postNougat() throws Throwable { 218 Assume.assumeTrue("This test verifies behavior introduced in Nougat and above", 219 Build.VERSION.SDK_INT >= Build.VERSION_CODES.N); 220 221 Context context = InstrumentationRegistry.getTargetContext(); 222 // Inject a dummy PackageInfo as the current WebView package to make sure it will always be 223 // different from the test's app package. 224 WebViewPackageHelper.setCurrentWebViewPackageForTesting(FAKE_WEBVIEW_PACKAGE); 225 launchHomeFragment(); 226 227 String expectedErrorMessage = String.format(Locale.US, 228 WebViewPackageError.DIFFERENT_WEBVIEW_PROVIDER_ERROR_MESSAGE, 229 WebViewPackageHelper.loadLabel(context)); 230 onView(withId(R.id.main_error_view)).check(matches(isDisplayed())); 231 onView(withId(R.id.error_text)).check(matches(withText(expectedErrorMessage))); 232 // Since the current provider is set to a fake package not an actual installed WebView 233 // provider, the UI should only offer to change the system WebView provider and should not 234 // offer to open the current WebView provider dev UI. 235 onView(withId(R.id.action_button)) 236 .check(matches(withText(WebViewPackageError.CHANGE_WEBVIEW_PROVIDER_BUTTON_TEXT))) 237 .perform(click()); 238 intended(IntentMatchers.hasAction(Settings.ACTION_WEBVIEW_SETTINGS)); 239 } 240 241 @Test 242 @MediumTest 243 @Feature({"AndroidWebView"}) 244 // Test the dialog shown when the WebView package error message is clicked. testDifferentWebViewPackageError_dialog_postNougat()245 public void testDifferentWebViewPackageError_dialog_postNougat() throws Throwable { 246 Assume.assumeTrue("This test verifies behavior introduced in Nougat and above", 247 Build.VERSION.SDK_INT >= Build.VERSION_CODES.N); 248 249 Context context = InstrumentationRegistry.getTargetContext(); 250 // Inject a dummy PackageInfo as the current WebView package to make sure it will always be 251 // different from the test's app package. 252 WebViewPackageHelper.setCurrentWebViewPackageForTesting(FAKE_WEBVIEW_PACKAGE); 253 launchHomeFragment(); 254 255 String dialogExpectedMessage = String.format(Locale.US, 256 WebViewPackageError.DIFFERENT_WEBVIEW_PROVIDER_DIALOG_MESSAGE, 257 WebViewPackageHelper.loadLabel(context)); 258 onView(withId(R.id.main_error_view)).perform(click()); 259 onView(withText(dialogExpectedMessage)).check(matches(isDisplayed())); 260 // Since the current provider is set to a fake package not an actual installed WebView 261 // provider, the UI should only offer to change the system WebView provider and should not 262 // offer to open the current WebView provider dev UI. 263 onView(withId(android.R.id.button1)).check(matches(not(isDisplayed()))); // positive button 264 onView(withId(android.R.id.button2)).check(matches(not(isDisplayed()))); // negative button 265 // botton3 is dialog neutral button 266 onView(withId(android.R.id.button3)) 267 .check(matches(withText(WebViewPackageError.CHANGE_WEBVIEW_PROVIDER_BUTTON_TEXT))) 268 .perform(click()); 269 intended(IntentMatchers.hasAction(Settings.ACTION_WEBVIEW_SETTINGS)); 270 } 271 272 @Test 273 @MediumTest 274 @Feature({"AndroidWebView"}) 275 // Test that error message is shown when system's WebView provider package is different from dev 276 // UI's on a preNougat android versions (where WebView provider can't be changed). testDifferentWebViewPackageError_bannerMessage_preNougat()277 public void testDifferentWebViewPackageError_bannerMessage_preNougat() throws Throwable { 278 Assume.assumeTrue("This test verifies pre-Nougat behavior", 279 Build.VERSION.SDK_INT < Build.VERSION_CODES.N); 280 281 Context context = InstrumentationRegistry.getTargetContext(); 282 // Inject a dummy PackageInfo as the current WebView package to make sure it will always be 283 // different from the test's app package. 284 WebViewPackageHelper.setCurrentWebViewPackageForTesting(FAKE_WEBVIEW_PACKAGE); 285 launchHomeFragment(); 286 287 String expectedErrorMessage = String.format(Locale.US, 288 WebViewPackageError.DIFFERENT_WEBVIEW_PROVIDER_ERROR_MESSAGE, 289 WebViewPackageHelper.loadLabel(context)); 290 onView(withId(R.id.main_error_view)).check(matches(isDisplayed())); 291 onView(withId(R.id.error_text)).check(matches(withText(expectedErrorMessage))); 292 // Since the current provider is set to a fake package not an actual installed WebView 293 // provider, the UI shouldn't offer opening current WebView provider dev UI. It should not 294 // offer to change system WebView provider because this is not supported on pre-Nougat 295 // android versions. 296 onView(withId(R.id.action_button)).check(matches(not(isDisplayed()))); 297 } 298 299 @Test 300 @MediumTest 301 @Feature({"AndroidWebView"}) 302 // Test the dialog shown when the WebView package error message is clicked (where WebView 303 // provider can't be changed). testDifferentWebViewPackageError_dialog_preNougat()304 public void testDifferentWebViewPackageError_dialog_preNougat() throws Throwable { 305 Assume.assumeTrue("This test verifies pre-Nougat behavior", 306 Build.VERSION.SDK_INT < Build.VERSION_CODES.N); 307 308 Context context = InstrumentationRegistry.getTargetContext(); 309 // Inject a dummy PackageInfo as the current WebView package to make sure it will always be 310 // different from the test's app package. 311 WebViewPackageHelper.setCurrentWebViewPackageForTesting(FAKE_WEBVIEW_PACKAGE); 312 launchHomeFragment(); 313 314 String dialogExpectedMessage = String.format(Locale.US, 315 WebViewPackageError.DIFFERENT_WEBVIEW_PROVIDER_DIALOG_MESSAGE, 316 WebViewPackageHelper.loadLabel(context)); 317 onView(withId(R.id.main_error_view)).perform(click()); 318 onView(withText(dialogExpectedMessage)).check(matches(isDisplayed())); 319 // Since the current provider is set to a fake package not an actual installed WebView 320 // provider, the UI shouldn't offer opening current WebView provider dev UI. It should not 321 // offer to change system WebView provider because this is not supported on pre-Nougat 322 // android versions. 323 // 324 // There should be no buttons in the Dialog. 325 onView(withId(android.R.id.button1)).check(matches(not(isDisplayed()))); 326 onView(withId(android.R.id.button2)).check(matches(not(isDisplayed()))); 327 onView(withId(android.R.id.button3)).check(matches(not(isDisplayed()))); 328 } 329 } 330