Index: chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java |
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..76c815607b4afbd76d1f85cb0a2bcb5605e800fb |
--- /dev/null |
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsOpenedFromExternalAppTest.java |
@@ -0,0 +1,439 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser; |
+ |
+import android.content.Intent; |
+import android.net.Uri; |
+import android.provider.Browser; |
+import android.test.FlakyTest; |
+import android.text.TextUtils; |
+import android.view.KeyEvent; |
+ |
+import junit.framework.Assert; |
+ |
+import org.chromium.base.ThreadUtils; |
+import org.chromium.base.test.util.DisabledTest; |
+import org.chromium.base.test.util.Feature; |
+import org.chromium.chrome.test.ChromeTabbedActivityTestBase; |
+import org.chromium.chrome.test.util.ChromeTabUtils; |
+import org.chromium.chrome.test.util.TestHttpServerClient; |
+import org.chromium.content.browser.test.util.Criteria; |
+import org.chromium.content.browser.test.util.CriteriaHelper; |
+import org.chromium.content.browser.test.util.DOMUtils; |
+import org.chromium.content.browser.test.util.JavaScriptUtils; |
+import org.chromium.content.browser.test.util.KeyUtils; |
+ |
+import java.util.concurrent.TimeoutException; |
+ |
+/** |
+ * Test the behavior of tabs when opening a URL from an external app, more specifically how we reuse |
+ * tabs. |
+ */ |
+public class TabsOpenedFromExternalAppTest extends ChromeTabbedActivityTestBase { |
+ |
+ private static final String EXTERNAL_APP_1_ID = "app1"; |
+ private static final String EXTERNAL_APP_2_ID = "app2"; |
+ |
+ static class ElementFocusedCriteria implements Criteria { |
+ private final Tab mTab; |
+ private final String mElementId; |
+ |
+ public ElementFocusedCriteria(Tab tab, String elementId) { |
+ mTab = tab; |
+ // Add quotes to match returned value from JS. |
+ mElementId = "\"" + elementId + "\""; |
+ } |
+ |
+ @Override |
+ public boolean isSatisfied() { |
+ String nodeId; |
+ try { |
+ StringBuilder sb = new StringBuilder(); |
+ sb.append("(function() {"); |
+ sb.append(" if (document.activeElement && document.activeElement.id) {"); |
+ sb.append(" return document.activeElement.id;"); |
+ sb.append(" }"); |
+ sb.append(" return null;"); |
+ sb.append("})();"); |
+ |
+ String jsonText = JavaScriptUtils.executeJavaScriptAndWaitForResult( |
+ mTab.getWebContents(), sb.toString()); |
+ if (jsonText.equalsIgnoreCase("null") || "".equals(jsonText)) { |
+ nodeId = null; |
+ } |
+ nodeId = jsonText; |
+ } catch (InterruptedException e) { |
+ e.printStackTrace(); |
+ Assert.fail("Failed to retrieve focused node: InterruptedException was thrown"); |
+ return false; |
+ } catch (TimeoutException e) { |
+ e.printStackTrace(); |
+ Assert.fail("Failed to retrieve focused node: TimeoutException was thrown"); |
+ return false; |
+ } |
+ return TextUtils.equals(mElementId, nodeId); |
+ } |
+ } |
+ |
+ static class ElementTextIsCriteria implements Criteria { |
+ private final Tab mTab; |
+ private final String mElementId; |
+ private final String mExpectedText; |
+ |
+ public ElementTextIsCriteria(Tab tab, String elementId, String expectedText) { |
+ mTab = tab; |
+ mElementId = elementId; |
+ mExpectedText = expectedText; |
+ } |
+ |
+ @Override |
+ public boolean isSatisfied() { |
+ try { |
+ String text = DOMUtils.getNodeValue(mTab.getWebContents(), mElementId); |
+ return TextUtils.equals(mExpectedText, text); |
+ } catch (InterruptedException e) { |
+ e.printStackTrace(); |
+ return false; |
+ } catch (TimeoutException e) { |
+ e.printStackTrace(); |
+ return false; |
+ } |
+ } |
+ } |
+ |
+ @Override |
+ public void startMainActivity() { |
+ // We'll start the activity explicitly in the tests, as we need to start it with an intent |
+ // in a specific test. |
+ } |
+ |
+ /** |
+ * Launch the specified URL as if it was triggered by an external application with id appId. |
+ * Returns when the URL has been navigated to. |
+ * @throws InterruptedException |
+ */ |
+ private void launchUrlFromExternalApp(String url, String appId, boolean createNewTab) |
+ throws InterruptedException { |
+ final Intent intent = new Intent(Intent.ACTION_VIEW); |
+ if (appId != null) { |
+ intent.putExtra(Browser.EXTRA_APPLICATION_ID, appId); |
+ } |
+ if (createNewTab) { |
+ intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true); |
+ } |
+ intent.setData(Uri.parse(url)); |
+ |
+ final Tab originalTab = getActivity().getActivityTab(); |
+ ThreadUtils.runOnUiThreadBlocking(new Runnable() { |
+ @Override |
+ public void run() { |
+ getActivity().onNewIntent(intent); |
+ } |
+ }); |
+ if (createNewTab) { |
+ assertTrue("Failed to select different tab", |
+ CriteriaHelper.pollForUIThreadCriteria(new Criteria() { |
+ @Override |
+ public boolean isSatisfied() { |
+ return getActivity().getActivityTab() != originalTab; |
+ } |
+ })); |
+ } |
+ ChromeTabUtils.waitForTabPageLoaded(getActivity().getActivityTab(), url); |
+ } |
+ |
+ /** |
+ * Tests that URLs opened from the same external app don't create new tabs. |
+ * @throws InterruptedException |
+ */ |
+ /** |
+ * @LargeTest |
+ * @Feature({"Navigation"}) |
+ * Bug 5856404 |
+ */ |
+ @FlakyTest |
+ public void testNoNewTabForSameApp() throws InterruptedException { |
+ startMainActivityFromLauncher(); |
+ |
+ String url1 = TestHttpServerClient.getUrl("chrome/test/data/android/google.html"); |
+ String url2 = TestHttpServerClient.getUrl("chrome/test/data/android/about.html"); |
+ |
+ int originalTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ |
+ // Launch a first URL from an app. |
+ launchUrlFromExternalApp(url1, EXTERNAL_APP_1_ID, false); |
+ // It should have opened in a new tab. |
+ int newTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount); |
+ assertEquals("Selected tab is not on the right URL.", url1, |
+ getActivity().getActivityTab().getUrl()); |
+ |
+ // Launch a new URL from the same app, it should open in the same tab. |
+ originalTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ launchUrlFromExternalApp(url2, EXTERNAL_APP_1_ID, false); |
+ newTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ assertEquals("Incorrect number of tabs open", originalTabCount, newTabCount); |
+ assertEquals("Selected tab is not on the right URL.", url2, |
+ getActivity().getActivityTab().getUrl()); |
+ |
+ // And pressing back should close Clank. |
+ assertTrue("Window does not have focus before pressing back.", |
+ getActivity().hasWindowFocus()); |
+ KeyUtils.singleKeyEventView(getInstrumentation(), getActivity().getActivityTab().getView(), |
+ KeyEvent.KEYCODE_BACK); |
+ getInstrumentation().waitForIdleSync(); |
+ assertFalse("Window still has focus after pressing back.", getActivity().hasWindowFocus()); |
+ } |
+ |
+ /** |
+ * Tests that URLs opened from an unspecified external app (no Browser.EXTRA_APPLICATION_ID in |
+ * the intent extras) don't create new tabs. |
+ * @throws InterruptedException |
+ * TODO(jcivelli): http:/b/5882419 we disabled this behavior so this test is disabled until we |
+ * figure out what we want to do. |
+ */ |
+ /** |
+ * @LargeTest |
+ * @Feature({"Navigation"}) |
+ * Bug 5856404 |
+ */ |
+ @DisabledTest |
+ public void testNewTabForUnknownApp() throws InterruptedException { |
+ startMainActivityFromLauncher(); |
+ |
+ String url1 = TestHttpServerClient.getUrl("chrome/test/data/android/google.html"); |
+ String url2 = TestHttpServerClient.getUrl("chrome/test/data/android/about.html"); |
+ |
+ |
+ // Launch a first URL with an app. |
+ launchUrlFromExternalApp(url1, EXTERNAL_APP_1_ID, false); |
+ |
+ assertEquals("Selected tab is not on the right URL.", url1, |
+ getActivity().getActivityTab().getUrl()); |
+ int originalTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ |
+ // Launch the same URL without app ID. It should open a new tab. |
+ originalTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ launchUrlFromExternalApp(url1, null, false); |
+ int newTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount); |
+ assertEquals("Selected tab is not on the right URL.", url1, |
+ getActivity().getActivityTab().getUrl()); |
+ |
+ // Launch another URL without app ID. It should open a new tab. |
+ originalTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ launchUrlFromExternalApp(url2, null, false); |
+ newTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount); |
+ assertEquals("Selected tab is not on the right URL.", url2, |
+ getActivity().getActivityTab().getUrl()); |
+ |
+ // And pressing back should close Clank. |
+ assertTrue("Window does not have focus before pressing back.", |
+ getActivity().hasWindowFocus()); |
+ KeyUtils.singleKeyEventView(getInstrumentation(), getActivity().getActivityTab().getView(), |
+ KeyEvent.KEYCODE_BACK); |
+ getInstrumentation().waitForIdleSync(); |
+ assertFalse("Window still has focus after pressing back.", getActivity().hasWindowFocus()); |
+ } |
+ |
+ /** |
+ * Tests that URLs opened with the Browser.EXTRA_CREATE_NEW_TAB extra in |
+ * the intent do create new tabs. |
+ * @throws InterruptedException |
+ */ |
+ /** |
+ * Bug: crbug.com/155004 |
+ * @LargeTest |
+ * @Feature({"Navigation"}) |
+ */ |
+ @DisabledTest |
+ public void testNewTabWithNewTabExtra() throws InterruptedException { |
+ startMainActivityFromLauncher(); |
+ |
+ String url1 = TestHttpServerClient.getUrl("chrome/test/data/android/google.html"); |
+ String url2 = TestHttpServerClient.getUrl("chrome/test/data/android/about.html"); |
+ |
+ int originalTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ |
+ // Launch a first URL from an app. |
+ launchUrlFromExternalApp(url1, EXTERNAL_APP_1_ID, false); |
+ // It should have opened in a new tab. |
+ int newTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount); |
+ assertEquals("Selected tab is not on the right URL.", url1, |
+ getActivity().getActivityTab().getUrl()); |
+ |
+ // Launch a new URL from the same app with the right extra to open in a new tab. |
+ originalTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ launchUrlFromExternalApp(url2, EXTERNAL_APP_1_ID, true); |
+ newTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount); |
+ assertEquals("Selected tab is not on the right URL.", url2, |
+ getActivity().getActivityTab().getUrl()); |
+ |
+ // And pressing back should close Clank. |
+ assertTrue("Window does not have focus before pressing back.", |
+ getActivity().hasWindowFocus()); |
+ KeyUtils.singleKeyEventView(getInstrumentation(), getActivity().getActivityTab().getView(), |
+ KeyEvent.KEYCODE_BACK); |
+ getInstrumentation().waitForIdleSync(); |
+ assertFalse("Window still has focus after pressing back.", getActivity().hasWindowFocus()); |
+ } |
+ |
+ /** |
+ * Similar to testNoNewTabForSameApp but actually starting the application (not just opening a |
+ * tab) from the external app. |
+ * @throws InterruptedException |
+ */ |
+ /** |
+ * @LargeTest |
+ * @Feature({"Navigation", "Main"}) |
+ * Bug 5856404 |
+ */ |
+ @FlakyTest |
+ public void testNoNewTabForSameAppOnStart() throws InterruptedException { |
+ String url1 = TestHttpServerClient.getUrl("chrome/test/data/android/google.html"); |
+ String url2 = TestHttpServerClient.getUrl("chrome/test/data/android/about.html"); |
+ |
+ // Launch Clank from the external app. |
+ startMainActivityFromExternalApp(url1, EXTERNAL_APP_1_ID); |
+ assertEquals("Selected tab is not on the right URL.", url1, |
+ getActivity().getActivityTab().getUrl()); |
+ |
+ // Launch a new URL from the same app, it should open in the same tab. |
+ int originalTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ launchUrlFromExternalApp(url2, EXTERNAL_APP_1_ID, false); |
+ int newTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ assertEquals("Incorrect number of tabs open", originalTabCount, newTabCount); |
+ assertEquals("Selected tab is not on the right URL.", url2, |
+ getActivity().getActivityTab().getUrl()); |
+ |
+ // And pressing back should close Clank. |
+ assertTrue("Window does not have focus before pressing back.", |
+ getActivity().hasWindowFocus()); |
+ KeyUtils.singleKeyEventView(getInstrumentation(), getActivity().getActivityTab().getView(), |
+ KeyEvent.KEYCODE_BACK); |
+ getInstrumentation().waitForIdleSync(); |
+ assertFalse("Window still has focus after pressing back.", getActivity().hasWindowFocus()); |
+ } |
+ |
+ /** |
+ * Test that URLs opened from different external apps do create new tabs. |
+ * @throws InterruptedException |
+ * |
+ * @LargeTest |
+ * crbug.com/157773 |
+ */ |
+ @FlakyTest |
+ @Feature({"Navigation", "Main"}) |
+ public void testNewTabForDifferentApps() throws InterruptedException { |
+ startMainActivityFromLauncher(); |
+ |
+ String url1 = TestHttpServerClient.getUrl("chrome/test/data/android/google.html"); |
+ String url2 = TestHttpServerClient.getUrl("chrome/test/data/android/about.html"); |
+ String url3 = TestHttpServerClient.getUrl("chrome/test/data/android/test.html"); |
+ |
+ // Launch a first URL from an app1. |
+ launchUrlFromExternalApp(url1, EXTERNAL_APP_1_ID, false); |
+ |
+ int originalTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ |
+ // Launch a second URL from an app2, it should open in a new tab. |
+ launchUrlFromExternalApp(url2, EXTERNAL_APP_2_ID, false); |
+ |
+ // It should have opened in a new tab. |
+ int newTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount); |
+ assertEquals("Selected tab is not on the right URL.", url2, |
+ getActivity().getActivityTab().getUrl()); |
+ |
+ // Also try with no app id, it should also open in a new tab. |
+ originalTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ launchUrlFromExternalApp(url3, null, false); |
+ newTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount); |
+ assertEquals("Selected tab is not on the right URL.", url3, |
+ getActivity().getActivityTab().getUrl()); |
+ } |
+ |
+ /** |
+ * Tests that a tab is not reused when launched from the same app as an already opened tab and |
+ * when the user typed in the location bar. |
+ * @throws InterruptedException |
+ */ |
+ /** |
+ * @LargeTest |
+ * @Feature({"Navigation"}) |
+ * Bug 6467101 |
+ */ |
+ @FlakyTest |
+ public void testNewTabWhenLocationBarEdited() throws InterruptedException { |
+ startMainActivityFromLauncher(); |
+ |
+ String url1 = TestHttpServerClient.getUrl("chrome/test/data/android/google.html"); |
+ String url2 = TestHttpServerClient.getUrl("chrome/test/data/android/about.html"); |
+ |
+ // Launch a first URL from an app. |
+ launchUrlFromExternalApp(url1, EXTERNAL_APP_1_ID, false); |
+ |
+ // Now simulate the user typing something in the location bar. |
+ typeInOmnibox("hi", true); |
+ |
+ // Launch a second URL from the same app, it should open in a new tab. |
+ int originalTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ launchUrlFromExternalApp(url2, EXTERNAL_APP_1_ID, false); |
+ int newTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount); |
+ assertEquals("Selected tab is not on the right URL.", url2, |
+ getActivity().getActivityTab().getUrl()); |
+ } |
+ |
+ /** |
+ * Tests that a tab is not reused when launched from the same app as an already opened tab and |
+ * when the user has entered text in the page. |
+ * @throws InterruptedException |
+ */ |
+ /** |
+ * @LargeTest |
+ * @Feature({"Navigation"}) |
+ * Bug 6467101 |
+ */ |
+ @FlakyTest |
+ public void testNewTabWhenPageEdited() throws InterruptedException, TimeoutException { |
+ startMainActivityFromLauncher(); |
+ |
+ String url1 = TestHttpServerClient.getUrl("chrome/test/data/android/google.html"); |
+ String url2 = TestHttpServerClient.getUrl("chrome/test/data/android/about.html"); |
+ |
+ // Launch a first URL from an app. |
+ launchUrlFromExternalApp(url1, EXTERNAL_APP_1_ID, false); |
+ |
+ // Click the text-field and type something. |
+ Tab tab = getActivity().getActivityTab(); |
+ DOMUtils.clickNode(this, tab.getContentViewCore(), "textField"); |
+ |
+ // Some processing needs to happen before the test-field has the focus. |
+ assertTrue("Text-field in page not focused.", CriteriaHelper.pollForCriteria( |
+ new ElementFocusedCriteria( |
+ getActivity().getActivityTab(), "textField"), 2000, 200)); |
+ |
+ // Now type something. |
+ getInstrumentation().sendStringSync("banana"); |
+ |
+ // We also have to wait for the text to happen in the page. |
+ assertTrue("Page does not have the text typed in.", CriteriaHelper.pollForCriteria( |
+ new ElementTextIsCriteria(getActivity().getActivityTab(), "textField", |
+ "banana"), 2000, 200)); |
+ |
+ // Launch a second URL from the same app, it should open in a new tab. |
+ int originalTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ launchUrlFromExternalApp(url2, EXTERNAL_APP_1_ID, false); |
+ int newTabCount = ChromeTabUtils.getNumOpenTabs(getActivity()); |
+ assertEquals("Incorrect number of tabs open", originalTabCount + 1, newTabCount); |
+ assertEquals("Selected tab is not on the right URL.", url2, |
+ getActivity().getActivityTab().getUrl()); |
+ } |
+} |